Completed
Branch db-repair-tool (5be9a7)
by
unknown
13:43 queued 07:31
created
core/db_models/fields/EE_Float_Field.php 1 patch
Indentation   +68 added lines, -68 removed lines patch added patch discarded remove patch
@@ -6,80 +6,80 @@
 block discarded – undo
6 6
  */
7 7
 class EE_Float_Field extends EE_Model_Field_Base
8 8
 {
9
-    /**
10
-     * @var EE_Currency_Config
11
-     */
12
-    protected $currency;
9
+	/**
10
+	 * @var EE_Currency_Config
11
+	 */
12
+	protected $currency;
13 13
 
14 14
 
15
-    /**
16
-     * @param string $table_column
17
-     * @param string $nicename
18
-     * @param bool   $nullable
19
-     * @param null   $default_value
20
-     */
21
-    public function __construct($table_column, $nicename, $nullable, $default_value = null)
22
-    {
23
-        $this->currency = EE_Config::instance()->currency instanceof EE_Currency_Config
15
+	/**
16
+	 * @param string $table_column
17
+	 * @param string $nicename
18
+	 * @param bool   $nullable
19
+	 * @param null   $default_value
20
+	 */
21
+	public function __construct($table_column, $nicename, $nullable, $default_value = null)
22
+	{
23
+		$this->currency = EE_Config::instance()->currency instanceof EE_Currency_Config
24 24
 			? EE_Config::instance()->currency
25 25
 			: new EE_Currency_Config();
26
-        parent::__construct($table_column, $nicename, $nullable, $default_value);
27
-        $this->setSchemaType('number');
28
-    }
26
+		parent::__construct($table_column, $nicename, $nullable, $default_value);
27
+		$this->setSchemaType('number');
28
+	}
29 29
 
30 30
 
31
-    /**
32
-     * If provided a string, strips out number-related formatting, like commas, periods, spaces, other junk, etc.
33
-     * However, treats commas and periods as thousand-separators ro decimal marks, as indicate by the config's currency.
34
-     * So if you want to pass in a string that NEEDS to interpret periods as decimal marks, call floatval() on it first.
35
-     * Returns a float
36
-     *
37
-     * @param float|string $value_inputted_for_field_on_model_object
38
-     * @return float
39
-     */
40
-    public function prepare_for_set($value_inputted_for_field_on_model_object)
41
-    {
42
-        // remove whitespaces and thousands separators
43
-        if (is_string($value_inputted_for_field_on_model_object)) {
44
-            $value_inputted_for_field_on_model_object = str_replace(
45
-                array(" ", $this->currency->thsnds),
46
-                "",
47
-                $value_inputted_for_field_on_model_object
48
-            );
49
-            // normalize it so periods are decimal marks (we don't care where you're from: we're talking PHP now)
50
-            $value_inputted_for_field_on_model_object = str_replace(
51
-                $this->currency->dec_mrk,
52
-                ".",
53
-                $value_inputted_for_field_on_model_object
54
-            );
55
-            // double-check there's absolutely nothing left on this string besides numbers
56
-            $value_inputted_for_field_on_model_object = preg_replace(
57
-                "/[^0-9,.]/",
58
-                "",
59
-                $value_inputted_for_field_on_model_object
60
-            );
61
-        }
62
-        return floatval($value_inputted_for_field_on_model_object);
63
-    }
31
+	/**
32
+	 * If provided a string, strips out number-related formatting, like commas, periods, spaces, other junk, etc.
33
+	 * However, treats commas and periods as thousand-separators ro decimal marks, as indicate by the config's currency.
34
+	 * So if you want to pass in a string that NEEDS to interpret periods as decimal marks, call floatval() on it first.
35
+	 * Returns a float
36
+	 *
37
+	 * @param float|string $value_inputted_for_field_on_model_object
38
+	 * @return float
39
+	 */
40
+	public function prepare_for_set($value_inputted_for_field_on_model_object)
41
+	{
42
+		// remove whitespaces and thousands separators
43
+		if (is_string($value_inputted_for_field_on_model_object)) {
44
+			$value_inputted_for_field_on_model_object = str_replace(
45
+				array(" ", $this->currency->thsnds),
46
+				"",
47
+				$value_inputted_for_field_on_model_object
48
+			);
49
+			// normalize it so periods are decimal marks (we don't care where you're from: we're talking PHP now)
50
+			$value_inputted_for_field_on_model_object = str_replace(
51
+				$this->currency->dec_mrk,
52
+				".",
53
+				$value_inputted_for_field_on_model_object
54
+			);
55
+			// double-check there's absolutely nothing left on this string besides numbers
56
+			$value_inputted_for_field_on_model_object = preg_replace(
57
+				"/[^0-9,.]/",
58
+				"",
59
+				$value_inputted_for_field_on_model_object
60
+			);
61
+		}
62
+		return floatval($value_inputted_for_field_on_model_object);
63
+	}
64 64
 
65
-    /**
66
-     * Returns the number formatted according to local custom (set by the country of the blog).
67
-     *
68
-     * @param float $value_on_field_to_be_outputted
69
-     * @return string
70
-     */
71
-    public function prepare_for_pretty_echoing($value_on_field_to_be_outputted, $schema = null)
72
-    {
73
-        return number_format(
74
-            $value_on_field_to_be_outputted,
75
-            $this->currency->dec_plc,
76
-            $this->currency->dec_mrk,
77
-            $this->currency->thsnds
78
-        );
79
-    }
65
+	/**
66
+	 * Returns the number formatted according to local custom (set by the country of the blog).
67
+	 *
68
+	 * @param float $value_on_field_to_be_outputted
69
+	 * @return string
70
+	 */
71
+	public function prepare_for_pretty_echoing($value_on_field_to_be_outputted, $schema = null)
72
+	{
73
+		return number_format(
74
+			$value_on_field_to_be_outputted,
75
+			$this->currency->dec_plc,
76
+			$this->currency->dec_mrk,
77
+			$this->currency->thsnds
78
+		);
79
+	}
80 80
 
81
-    public function prepare_for_set_from_db($value_found_in_db_for_model_object)
82
-    {
83
-        return floatval($value_found_in_db_for_model_object);
84
-    }
81
+	public function prepare_for_set_from_db($value_found_in_db_for_model_object)
82
+	{
83
+		return floatval($value_found_in_db_for_model_object);
84
+	}
85 85
 }
Please login to merge, or discard this patch.
core/libraries/form_sections/form_handlers/FormHandler.php 2 patches
Indentation   +637 added lines, -637 removed lines patch added patch discarded remove patch
@@ -29,643 +29,643 @@
 block discarded – undo
29 29
  */
30 30
 abstract class FormHandler implements FormHandlerInterface
31 31
 {
32
-    /**
33
-     * will add opening and closing HTML form tags as well as a submit button
34
-     */
35
-    const ADD_FORM_TAGS_AND_SUBMIT = 'add_form_tags_and_submit';
36
-
37
-    /**
38
-     * will add opening and closing HTML form tags but NOT a submit button
39
-     */
40
-    const ADD_FORM_TAGS_ONLY = 'add_form_tags_only';
41
-
42
-    /**
43
-     * will NOT add opening and closing HTML form tags but will add a submit button
44
-     */
45
-    const ADD_FORM_SUBMIT_ONLY = 'add_form_submit_only';
46
-
47
-    /**
48
-     * will NOT add opening and closing HTML form tags NOR a submit button
49
-     */
50
-    const DO_NOT_SETUP_FORM = 'do_not_setup_form';
51
-
52
-    /**
53
-     * if set to false, then this form has no displayable content,
54
-     * and will only be used for processing data sent passed via GET or POST
55
-     * defaults to true ( ie: form has displayable content )
56
-     *
57
-     * @var boolean $displayable
58
-     */
59
-    private $displayable = true;
60
-
61
-    /**
62
-     * @var string $form_name
63
-     */
64
-    private $form_name;
65
-
66
-    /**
67
-     * @var string $admin_name
68
-     */
69
-    private $admin_name;
70
-
71
-    /**
72
-     * @var string $slug
73
-     */
74
-    private $slug;
75
-
76
-    /**
77
-     * @var string $submit_btn_text
78
-     */
79
-    private $submit_btn_text;
80
-
81
-    /**
82
-     * @var string $form_action
83
-     */
84
-    private $form_action;
85
-
86
-    /**
87
-     * form params in key value pairs
88
-     * can be added to form action URL or as hidden inputs
89
-     *
90
-     * @var array $form_args
91
-     */
92
-    private $form_args = array();
93
-
94
-    /**
95
-     * value of one of the string constant above
96
-     *
97
-     * @var string $form_config
98
-     */
99
-    private $form_config;
100
-
101
-    /**
102
-     * whether or not the form was determined to be invalid
103
-     *
104
-     * @var boolean $form_has_errors
105
-     */
106
-    private $form_has_errors;
107
-
108
-    /**
109
-     * the absolute top level form section being used on the page
110
-     *
111
-     * @var EE_Form_Section_Proper $form
112
-     */
113
-    private $form;
114
-
115
-    /**
116
-     * @var EE_Registry $registry
117
-     */
118
-    protected $registry;
119
-
120
-    // phpcs:disable PEAR.Functions.ValidDefaultValue.NotAtEnd
121
-    /**
122
-     * Form constructor.
123
-     *
124
-     * @param string      $form_name
125
-     * @param string      $admin_name
126
-     * @param string      $slug
127
-     * @param string      $form_action
128
-     * @param string      $form_config
129
-     * @param EE_Registry $registry
130
-     * @throws InvalidDataTypeException
131
-     * @throws DomainException
132
-     * @throws InvalidArgumentException
133
-     */
134
-    public function __construct(
135
-        $form_name,
136
-        $admin_name,
137
-        $slug,
138
-        $form_action = '',
139
-        $form_config = FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
140
-        EE_Registry $registry
141
-    ) {
142
-        $this->setFormName($form_name);
143
-        $this->setAdminName($admin_name);
144
-        $this->setSlug($slug);
145
-        $this->setFormAction($form_action);
146
-        $this->setFormConfig($form_config);
147
-        $this->setSubmitBtnText(esc_html__('Submit', 'event_espresso'));
148
-        $this->registry = $registry;
149
-    }
150
-
151
-
152
-    /**
153
-     * @return array
154
-     */
155
-    public static function getFormConfigConstants()
156
-    {
157
-        return array(
158
-            FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
159
-            FormHandler::ADD_FORM_TAGS_ONLY,
160
-            FormHandler::ADD_FORM_SUBMIT_ONLY,
161
-            FormHandler::DO_NOT_SETUP_FORM,
162
-        );
163
-    }
164
-
165
-
166
-    /**
167
-     * @param bool $for_display
168
-     * @return EE_Form_Section_Proper
169
-     * @throws EE_Error
170
-     * @throws LogicException
171
-     */
172
-    public function form($for_display = false)
173
-    {
174
-        if (! $this->formIsValid()) {
175
-            return null;
176
-        }
177
-        if ($for_display) {
178
-            $form_config = $this->formConfig();
179
-            if (
180
-                $form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
181
-                || $form_config === FormHandler::ADD_FORM_SUBMIT_ONLY
182
-            ) {
183
-                $this->appendSubmitButton();
184
-                $this->clearFormButtonFloats();
185
-            }
186
-        }
187
-        return $this->form;
188
-    }
189
-
190
-
191
-    /**
192
-     * @return boolean
193
-     * @throws LogicException
194
-     */
195
-    public function formIsValid()
196
-    {
197
-        if ($this->form instanceof EE_Form_Section_Proper) {
198
-            return true;
199
-        }
200
-        $form = apply_filters(
201
-            'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__formIsValid__generated_form_object',
202
-            $this->generate(),
203
-            $this
204
-        );
205
-        if ($this->verifyForm($form)) {
206
-            $this->setForm($form);
207
-        }
208
-        return true;
209
-    }
210
-
211
-
212
-    /**
213
-     * @param EE_Form_Section_Proper|null $form
214
-     * @return bool
215
-     * @throws LogicException
216
-     */
217
-    public function verifyForm(EE_Form_Section_Proper $form = null)
218
-    {
219
-        $form = $form !== null ? $form : $this->form;
220
-        if ($form instanceof EE_Form_Section_Proper) {
221
-            return true;
222
-        }
223
-        throw new LogicException(
224
-            sprintf(
225
-                esc_html__('The "%1$s" form is invalid or missing. %2$s', 'event_espresso'),
226
-                $this->form_name,
227
-                var_export($form, true)
228
-            )
229
-        );
230
-    }
231
-
232
-
233
-    /**
234
-     * @param EE_Form_Section_Proper $form
235
-     */
236
-    public function setForm(EE_Form_Section_Proper $form)
237
-    {
238
-        $this->form = $form;
239
-    }
240
-
241
-
242
-    /**
243
-     * @return boolean
244
-     */
245
-    public function displayable()
246
-    {
247
-        return $this->displayable;
248
-    }
249
-
250
-
251
-    /**
252
-     * @param boolean $displayable
253
-     */
254
-    public function setDisplayable($displayable = false)
255
-    {
256
-        $this->displayable = filter_var($displayable, FILTER_VALIDATE_BOOLEAN);
257
-    }
258
-
259
-
260
-    /**
261
-     * a public name for the form that can be displayed on the frontend of a site
262
-     *
263
-     * @return string
264
-     */
265
-    public function formName()
266
-    {
267
-        return $this->form_name;
268
-    }
269
-
270
-
271
-    /**
272
-     * @param string $form_name
273
-     * @throws InvalidDataTypeException
274
-     */
275
-    public function setFormName($form_name)
276
-    {
277
-        if (! is_string($form_name)) {
278
-            throw new InvalidDataTypeException('$form_name', $form_name, 'string');
279
-        }
280
-        $this->form_name = $form_name;
281
-    }
282
-
283
-
284
-    /**
285
-     * a public name for the form that can be displayed, but only in the admin
286
-     *
287
-     * @return string
288
-     */
289
-    public function adminName()
290
-    {
291
-        return $this->admin_name;
292
-    }
293
-
294
-
295
-    /**
296
-     * @param string $admin_name
297
-     * @throws InvalidDataTypeException
298
-     */
299
-    public function setAdminName($admin_name)
300
-    {
301
-        if (! is_string($admin_name)) {
302
-            throw new InvalidDataTypeException('$admin_name', $admin_name, 'string');
303
-        }
304
-        $this->admin_name = $admin_name;
305
-    }
306
-
307
-
308
-    /**
309
-     * a URL friendly string that can be used for identifying the form
310
-     *
311
-     * @return string
312
-     */
313
-    public function slug()
314
-    {
315
-        return $this->slug;
316
-    }
317
-
318
-
319
-    /**
320
-     * @param string $slug
321
-     * @throws InvalidDataTypeException
322
-     */
323
-    public function setSlug($slug)
324
-    {
325
-        if (! is_string($slug)) {
326
-            throw new InvalidDataTypeException('$slug', $slug, 'string');
327
-        }
328
-        $this->slug = $slug;
329
-    }
330
-
331
-
332
-    /**
333
-     * @return string
334
-     */
335
-    public function submitBtnText()
336
-    {
337
-        return $this->submit_btn_text;
338
-    }
339
-
340
-
341
-    /**
342
-     * @param string $submit_btn_text
343
-     * @throws InvalidDataTypeException
344
-     * @throws InvalidArgumentException
345
-     */
346
-    public function setSubmitBtnText($submit_btn_text)
347
-    {
348
-        if (! is_string($submit_btn_text)) {
349
-            throw new InvalidDataTypeException('$submit_btn_text', $submit_btn_text, 'string');
350
-        }
351
-        if (empty($submit_btn_text)) {
352
-            throw new InvalidArgumentException(
353
-                esc_html__('Can not set Submit button text because an empty string was provided.', 'event_espresso')
354
-            );
355
-        }
356
-        $this->submit_btn_text = $submit_btn_text;
357
-    }
358
-
359
-
360
-    /**
361
-     * @return string
362
-     */
363
-    public function formAction()
364
-    {
365
-        return ! empty($this->form_args)
366
-            ? add_query_arg($this->form_args, $this->form_action)
367
-            : $this->form_action;
368
-    }
369
-
370
-
371
-    /**
372
-     * @param string $form_action
373
-     * @throws InvalidDataTypeException
374
-     */
375
-    public function setFormAction($form_action)
376
-    {
377
-        if (! is_string($form_action)) {
378
-            throw new InvalidDataTypeException('$form_action', $form_action, 'string');
379
-        }
380
-        $this->form_action = $form_action;
381
-    }
382
-
383
-
384
-    /**
385
-     * @param array $form_args
386
-     * @throws InvalidDataTypeException
387
-     * @throws InvalidArgumentException
388
-     */
389
-    public function addFormActionArgs($form_args = array())
390
-    {
391
-        if (is_object($form_args)) {
392
-            throw new InvalidDataTypeException(
393
-                '$form_args',
394
-                $form_args,
395
-                'anything other than an object was expected.'
396
-            );
397
-        }
398
-        if (empty($form_args)) {
399
-            throw new InvalidArgumentException(
400
-                esc_html__('The redirect arguments can not be an empty array.', 'event_espresso')
401
-            );
402
-        }
403
-        $this->form_args = array_merge($this->form_args, $form_args);
404
-    }
405
-
406
-
407
-    /**
408
-     * @return string
409
-     */
410
-    public function formConfig()
411
-    {
412
-        return $this->form_config;
413
-    }
414
-
415
-
416
-    /**
417
-     * @param string $form_config
418
-     * @throws DomainException
419
-     */
420
-    public function setFormConfig($form_config)
421
-    {
422
-        if (
423
-            ! in_array(
424
-                $form_config,
425
-                array(
426
-                FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
427
-                FormHandler::ADD_FORM_TAGS_ONLY,
428
-                FormHandler::ADD_FORM_SUBMIT_ONLY,
429
-                FormHandler::DO_NOT_SETUP_FORM,
430
-                ),
431
-                true
432
-            )
433
-        ) {
434
-            throw new DomainException(
435
-                sprintf(
436
-                    esc_html__(
437
-                        '"%1$s" is not a valid value for the form config. Please use one of the class constants on \EventEspresso\core\libraries\form_sections\form_handlers\Form',
438
-                        'event_espresso'
439
-                    ),
440
-                    $form_config
441
-                )
442
-            );
443
-        }
444
-        $this->form_config = $form_config;
445
-    }
446
-
447
-
448
-    /**
449
-     * called after the form is instantiated
450
-     * and used for performing any logic that needs to occur early
451
-     * before any of the other methods are called.
452
-     * returns true if everything is ok to proceed,
453
-     * and false if no further form logic should be implemented
454
-     *
455
-     * @return boolean
456
-     */
457
-    public function initialize()
458
-    {
459
-        $this->form_has_errors = EE_Error::has_error(true);
460
-        return true;
461
-    }
462
-
463
-
464
-    /**
465
-     * used for setting up css and js
466
-     *
467
-     * @return void
468
-     * @throws LogicException
469
-     * @throws EE_Error
470
-     */
471
-    public function enqueueStylesAndScripts()
472
-    {
473
-        $this->form()->enqueue_js();
474
-    }
475
-
476
-
477
-    /**
478
-     * creates and returns the actual form
479
-     *
480
-     * @return EE_Form_Section_Proper
481
-     */
482
-    abstract public function generate();
483
-
484
-
485
-    /**
486
-     * creates and returns an EE_Submit_Input labeled "Submit"
487
-     *
488
-     * @param string $text
489
-     * @return EE_Submit_Input
490
-     */
491
-    public function generateSubmitButton($text = '')
492
-    {
493
-        $text = ! empty($text) ? $text : $this->submitBtnText();
494
-        return new EE_Submit_Input(
495
-            array(
496
-                'html_name'             => 'ee-form-submit-' . $this->slug(),
497
-                'html_id'               => 'ee-form-submit-' . $this->slug(),
498
-                'html_class'            => 'ee-form-submit',
499
-                'html_label'            => ' ',
500
-                'other_html_attributes' => ' rel="' . $this->slug() . '"',
501
-                'default'               => $text,
502
-            )
503
-        );
504
-    }
505
-
506
-
507
-    /**
508
-     * calls generateSubmitButton() and appends it onto the form along with a float clearing div
509
-     *
510
-     * @param string $text
511
-     * @return void
512
-     * @throws EE_Error
513
-     */
514
-    public function appendSubmitButton($text = '')
515
-    {
516
-        if ($this->form->subsection_exists($this->slug() . '-submit-btn')) {
517
-            return;
518
-        }
519
-        $this->form->add_subsections(
520
-            array($this->slug() . '-submit-btn' => $this->generateSubmitButton($text)),
521
-            null,
522
-            false
523
-        );
524
-    }
525
-
526
-
527
-    /**
528
-     * creates and returns an EE_Submit_Input labeled "Cancel"
529
-     *
530
-     * @param string $text
531
-     * @return EE_Submit_Input
532
-     */
533
-    public function generateCancelButton($text = '')
534
-    {
535
-        $cancel_button = new EE_Submit_Input(
536
-            array(
537
-                'html_name'             => 'ee-form-submit-' . $this->slug(), // YES! Same name as submit !!!
538
-                'html_id'               => 'ee-cancel-form-' . $this->slug(),
539
-                'html_class'            => 'ee-cancel-form',
540
-                'html_label'            => ' ',
541
-                'other_html_attributes' => ' rel="' . $this->slug() . '"',
542
-                'default'               => ! empty($text) ? $text : esc_html__('Cancel', 'event_espresso'),
543
-            )
544
-        );
545
-        $cancel_button->set_button_css_attributes(false);
546
-        return $cancel_button;
547
-    }
548
-
549
-
550
-    /**
551
-     * appends a float clearing div onto end of form
552
-     *
553
-     * @return void
554
-     * @throws EE_Error
555
-     */
556
-    public function clearFormButtonFloats()
557
-    {
558
-        $this->form->add_subsections(
559
-            array(
560
-                'clear-submit-btn-float' => new EE_Form_Section_HTML(
561
-                    EEH_HTML::div('', '', 'clear-float') . EEH_HTML::divx()
562
-                ),
563
-            ),
564
-            null,
565
-            false
566
-        );
567
-    }
568
-
569
-
570
-    /**
571
-     * takes the generated form and displays it along with ony other non-form HTML that may be required
572
-     * returns a string of HTML that can be directly echoed in a template
573
-     *
574
-     * @return string
575
-     * @throws InvalidArgumentException
576
-     * @throws InvalidInterfaceException
577
-     * @throws InvalidDataTypeException
578
-     * @throws LogicException
579
-     * @throws EE_Error
580
-     */
581
-    public function display()
582
-    {
583
-        $form_html = apply_filters(
584
-            'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__display__before_form',
585
-            ''
586
-        );
587
-        $form_config = $this->formConfig();
588
-        if (
589
-            $form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
590
-            || $form_config === FormHandler::ADD_FORM_TAGS_ONLY
591
-        ) {
592
-            $additional_props = $this->requiresMultipartEnctype()
593
-                ? 'enctype="multipart/form-data"'
594
-                : '';
595
-            $form_html .= $this->form()->form_open(
596
-                $this->formAction(),
597
-                'POST',
598
-                $additional_props
599
-            );
600
-        }
601
-        $form_html .= $this->form(true)->get_html();
602
-        if (
603
-            $form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
604
-            || $form_config === FormHandler::ADD_FORM_TAGS_ONLY
605
-        ) {
606
-            $form_html .= $this->form()->form_close();
607
-        }
608
-        $form_html .= apply_filters(
609
-            'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__display__after_form',
610
-            ''
611
-        );
612
-        return $form_html;
613
-    }
614
-
615
-    /**
616
-     * Determines if this form needs "enctype='multipart/form-data'" or not.
617
-     * @since 4.9.80.p
618
-     * @return bool
619
-     * @throws EE_Error
620
-     */
621
-    public function requiresMultipartEnctype()
622
-    {
623
-        foreach ($this->form()->inputs_in_subsections() as $input) {
624
-            if ($input instanceof EE_File_Input) {
625
-                return true;
626
-            }
627
-        }
628
-        return false;
629
-    }
630
-
631
-
632
-    /**
633
-     * handles processing the form submission
634
-     * returns true or false depending on whether the form was processed successfully or not
635
-     *
636
-     * @param array $submitted_form_data
637
-     * @return array
638
-     * @throws InvalidArgumentException
639
-     * @throws InvalidInterfaceException
640
-     * @throws InvalidDataTypeException
641
-     * @throws EE_Error
642
-     * @throws LogicException
643
-     * @throws InvalidFormSubmissionException
644
-     */
645
-    public function process($submitted_form_data = array())
646
-    {
647
-        if (! $this->form()->was_submitted($submitted_form_data)) {
648
-            throw new InvalidFormSubmissionException($this->form_name);
649
-        }
650
-        $this->form(true)->receive_form_submission($submitted_form_data);
32
+	/**
33
+	 * will add opening and closing HTML form tags as well as a submit button
34
+	 */
35
+	const ADD_FORM_TAGS_AND_SUBMIT = 'add_form_tags_and_submit';
36
+
37
+	/**
38
+	 * will add opening and closing HTML form tags but NOT a submit button
39
+	 */
40
+	const ADD_FORM_TAGS_ONLY = 'add_form_tags_only';
41
+
42
+	/**
43
+	 * will NOT add opening and closing HTML form tags but will add a submit button
44
+	 */
45
+	const ADD_FORM_SUBMIT_ONLY = 'add_form_submit_only';
46
+
47
+	/**
48
+	 * will NOT add opening and closing HTML form tags NOR a submit button
49
+	 */
50
+	const DO_NOT_SETUP_FORM = 'do_not_setup_form';
51
+
52
+	/**
53
+	 * if set to false, then this form has no displayable content,
54
+	 * and will only be used for processing data sent passed via GET or POST
55
+	 * defaults to true ( ie: form has displayable content )
56
+	 *
57
+	 * @var boolean $displayable
58
+	 */
59
+	private $displayable = true;
60
+
61
+	/**
62
+	 * @var string $form_name
63
+	 */
64
+	private $form_name;
65
+
66
+	/**
67
+	 * @var string $admin_name
68
+	 */
69
+	private $admin_name;
70
+
71
+	/**
72
+	 * @var string $slug
73
+	 */
74
+	private $slug;
75
+
76
+	/**
77
+	 * @var string $submit_btn_text
78
+	 */
79
+	private $submit_btn_text;
80
+
81
+	/**
82
+	 * @var string $form_action
83
+	 */
84
+	private $form_action;
85
+
86
+	/**
87
+	 * form params in key value pairs
88
+	 * can be added to form action URL or as hidden inputs
89
+	 *
90
+	 * @var array $form_args
91
+	 */
92
+	private $form_args = array();
93
+
94
+	/**
95
+	 * value of one of the string constant above
96
+	 *
97
+	 * @var string $form_config
98
+	 */
99
+	private $form_config;
100
+
101
+	/**
102
+	 * whether or not the form was determined to be invalid
103
+	 *
104
+	 * @var boolean $form_has_errors
105
+	 */
106
+	private $form_has_errors;
107
+
108
+	/**
109
+	 * the absolute top level form section being used on the page
110
+	 *
111
+	 * @var EE_Form_Section_Proper $form
112
+	 */
113
+	private $form;
114
+
115
+	/**
116
+	 * @var EE_Registry $registry
117
+	 */
118
+	protected $registry;
119
+
120
+	// phpcs:disable PEAR.Functions.ValidDefaultValue.NotAtEnd
121
+	/**
122
+	 * Form constructor.
123
+	 *
124
+	 * @param string      $form_name
125
+	 * @param string      $admin_name
126
+	 * @param string      $slug
127
+	 * @param string      $form_action
128
+	 * @param string      $form_config
129
+	 * @param EE_Registry $registry
130
+	 * @throws InvalidDataTypeException
131
+	 * @throws DomainException
132
+	 * @throws InvalidArgumentException
133
+	 */
134
+	public function __construct(
135
+		$form_name,
136
+		$admin_name,
137
+		$slug,
138
+		$form_action = '',
139
+		$form_config = FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
140
+		EE_Registry $registry
141
+	) {
142
+		$this->setFormName($form_name);
143
+		$this->setAdminName($admin_name);
144
+		$this->setSlug($slug);
145
+		$this->setFormAction($form_action);
146
+		$this->setFormConfig($form_config);
147
+		$this->setSubmitBtnText(esc_html__('Submit', 'event_espresso'));
148
+		$this->registry = $registry;
149
+	}
150
+
151
+
152
+	/**
153
+	 * @return array
154
+	 */
155
+	public static function getFormConfigConstants()
156
+	{
157
+		return array(
158
+			FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
159
+			FormHandler::ADD_FORM_TAGS_ONLY,
160
+			FormHandler::ADD_FORM_SUBMIT_ONLY,
161
+			FormHandler::DO_NOT_SETUP_FORM,
162
+		);
163
+	}
164
+
165
+
166
+	/**
167
+	 * @param bool $for_display
168
+	 * @return EE_Form_Section_Proper
169
+	 * @throws EE_Error
170
+	 * @throws LogicException
171
+	 */
172
+	public function form($for_display = false)
173
+	{
174
+		if (! $this->formIsValid()) {
175
+			return null;
176
+		}
177
+		if ($for_display) {
178
+			$form_config = $this->formConfig();
179
+			if (
180
+				$form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
181
+				|| $form_config === FormHandler::ADD_FORM_SUBMIT_ONLY
182
+			) {
183
+				$this->appendSubmitButton();
184
+				$this->clearFormButtonFloats();
185
+			}
186
+		}
187
+		return $this->form;
188
+	}
189
+
190
+
191
+	/**
192
+	 * @return boolean
193
+	 * @throws LogicException
194
+	 */
195
+	public function formIsValid()
196
+	{
197
+		if ($this->form instanceof EE_Form_Section_Proper) {
198
+			return true;
199
+		}
200
+		$form = apply_filters(
201
+			'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__formIsValid__generated_form_object',
202
+			$this->generate(),
203
+			$this
204
+		);
205
+		if ($this->verifyForm($form)) {
206
+			$this->setForm($form);
207
+		}
208
+		return true;
209
+	}
210
+
211
+
212
+	/**
213
+	 * @param EE_Form_Section_Proper|null $form
214
+	 * @return bool
215
+	 * @throws LogicException
216
+	 */
217
+	public function verifyForm(EE_Form_Section_Proper $form = null)
218
+	{
219
+		$form = $form !== null ? $form : $this->form;
220
+		if ($form instanceof EE_Form_Section_Proper) {
221
+			return true;
222
+		}
223
+		throw new LogicException(
224
+			sprintf(
225
+				esc_html__('The "%1$s" form is invalid or missing. %2$s', 'event_espresso'),
226
+				$this->form_name,
227
+				var_export($form, true)
228
+			)
229
+		);
230
+	}
231
+
232
+
233
+	/**
234
+	 * @param EE_Form_Section_Proper $form
235
+	 */
236
+	public function setForm(EE_Form_Section_Proper $form)
237
+	{
238
+		$this->form = $form;
239
+	}
240
+
241
+
242
+	/**
243
+	 * @return boolean
244
+	 */
245
+	public function displayable()
246
+	{
247
+		return $this->displayable;
248
+	}
249
+
250
+
251
+	/**
252
+	 * @param boolean $displayable
253
+	 */
254
+	public function setDisplayable($displayable = false)
255
+	{
256
+		$this->displayable = filter_var($displayable, FILTER_VALIDATE_BOOLEAN);
257
+	}
258
+
259
+
260
+	/**
261
+	 * a public name for the form that can be displayed on the frontend of a site
262
+	 *
263
+	 * @return string
264
+	 */
265
+	public function formName()
266
+	{
267
+		return $this->form_name;
268
+	}
269
+
270
+
271
+	/**
272
+	 * @param string $form_name
273
+	 * @throws InvalidDataTypeException
274
+	 */
275
+	public function setFormName($form_name)
276
+	{
277
+		if (! is_string($form_name)) {
278
+			throw new InvalidDataTypeException('$form_name', $form_name, 'string');
279
+		}
280
+		$this->form_name = $form_name;
281
+	}
282
+
283
+
284
+	/**
285
+	 * a public name for the form that can be displayed, but only in the admin
286
+	 *
287
+	 * @return string
288
+	 */
289
+	public function adminName()
290
+	{
291
+		return $this->admin_name;
292
+	}
293
+
294
+
295
+	/**
296
+	 * @param string $admin_name
297
+	 * @throws InvalidDataTypeException
298
+	 */
299
+	public function setAdminName($admin_name)
300
+	{
301
+		if (! is_string($admin_name)) {
302
+			throw new InvalidDataTypeException('$admin_name', $admin_name, 'string');
303
+		}
304
+		$this->admin_name = $admin_name;
305
+	}
306
+
307
+
308
+	/**
309
+	 * a URL friendly string that can be used for identifying the form
310
+	 *
311
+	 * @return string
312
+	 */
313
+	public function slug()
314
+	{
315
+		return $this->slug;
316
+	}
317
+
318
+
319
+	/**
320
+	 * @param string $slug
321
+	 * @throws InvalidDataTypeException
322
+	 */
323
+	public function setSlug($slug)
324
+	{
325
+		if (! is_string($slug)) {
326
+			throw new InvalidDataTypeException('$slug', $slug, 'string');
327
+		}
328
+		$this->slug = $slug;
329
+	}
330
+
331
+
332
+	/**
333
+	 * @return string
334
+	 */
335
+	public function submitBtnText()
336
+	{
337
+		return $this->submit_btn_text;
338
+	}
339
+
340
+
341
+	/**
342
+	 * @param string $submit_btn_text
343
+	 * @throws InvalidDataTypeException
344
+	 * @throws InvalidArgumentException
345
+	 */
346
+	public function setSubmitBtnText($submit_btn_text)
347
+	{
348
+		if (! is_string($submit_btn_text)) {
349
+			throw new InvalidDataTypeException('$submit_btn_text', $submit_btn_text, 'string');
350
+		}
351
+		if (empty($submit_btn_text)) {
352
+			throw new InvalidArgumentException(
353
+				esc_html__('Can not set Submit button text because an empty string was provided.', 'event_espresso')
354
+			);
355
+		}
356
+		$this->submit_btn_text = $submit_btn_text;
357
+	}
358
+
359
+
360
+	/**
361
+	 * @return string
362
+	 */
363
+	public function formAction()
364
+	{
365
+		return ! empty($this->form_args)
366
+			? add_query_arg($this->form_args, $this->form_action)
367
+			: $this->form_action;
368
+	}
369
+
370
+
371
+	/**
372
+	 * @param string $form_action
373
+	 * @throws InvalidDataTypeException
374
+	 */
375
+	public function setFormAction($form_action)
376
+	{
377
+		if (! is_string($form_action)) {
378
+			throw new InvalidDataTypeException('$form_action', $form_action, 'string');
379
+		}
380
+		$this->form_action = $form_action;
381
+	}
382
+
383
+
384
+	/**
385
+	 * @param array $form_args
386
+	 * @throws InvalidDataTypeException
387
+	 * @throws InvalidArgumentException
388
+	 */
389
+	public function addFormActionArgs($form_args = array())
390
+	{
391
+		if (is_object($form_args)) {
392
+			throw new InvalidDataTypeException(
393
+				'$form_args',
394
+				$form_args,
395
+				'anything other than an object was expected.'
396
+			);
397
+		}
398
+		if (empty($form_args)) {
399
+			throw new InvalidArgumentException(
400
+				esc_html__('The redirect arguments can not be an empty array.', 'event_espresso')
401
+			);
402
+		}
403
+		$this->form_args = array_merge($this->form_args, $form_args);
404
+	}
405
+
406
+
407
+	/**
408
+	 * @return string
409
+	 */
410
+	public function formConfig()
411
+	{
412
+		return $this->form_config;
413
+	}
414
+
415
+
416
+	/**
417
+	 * @param string $form_config
418
+	 * @throws DomainException
419
+	 */
420
+	public function setFormConfig($form_config)
421
+	{
422
+		if (
423
+			! in_array(
424
+				$form_config,
425
+				array(
426
+				FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
427
+				FormHandler::ADD_FORM_TAGS_ONLY,
428
+				FormHandler::ADD_FORM_SUBMIT_ONLY,
429
+				FormHandler::DO_NOT_SETUP_FORM,
430
+				),
431
+				true
432
+			)
433
+		) {
434
+			throw new DomainException(
435
+				sprintf(
436
+					esc_html__(
437
+						'"%1$s" is not a valid value for the form config. Please use one of the class constants on \EventEspresso\core\libraries\form_sections\form_handlers\Form',
438
+						'event_espresso'
439
+					),
440
+					$form_config
441
+				)
442
+			);
443
+		}
444
+		$this->form_config = $form_config;
445
+	}
446
+
447
+
448
+	/**
449
+	 * called after the form is instantiated
450
+	 * and used for performing any logic that needs to occur early
451
+	 * before any of the other methods are called.
452
+	 * returns true if everything is ok to proceed,
453
+	 * and false if no further form logic should be implemented
454
+	 *
455
+	 * @return boolean
456
+	 */
457
+	public function initialize()
458
+	{
459
+		$this->form_has_errors = EE_Error::has_error(true);
460
+		return true;
461
+	}
462
+
463
+
464
+	/**
465
+	 * used for setting up css and js
466
+	 *
467
+	 * @return void
468
+	 * @throws LogicException
469
+	 * @throws EE_Error
470
+	 */
471
+	public function enqueueStylesAndScripts()
472
+	{
473
+		$this->form()->enqueue_js();
474
+	}
475
+
476
+
477
+	/**
478
+	 * creates and returns the actual form
479
+	 *
480
+	 * @return EE_Form_Section_Proper
481
+	 */
482
+	abstract public function generate();
483
+
484
+
485
+	/**
486
+	 * creates and returns an EE_Submit_Input labeled "Submit"
487
+	 *
488
+	 * @param string $text
489
+	 * @return EE_Submit_Input
490
+	 */
491
+	public function generateSubmitButton($text = '')
492
+	{
493
+		$text = ! empty($text) ? $text : $this->submitBtnText();
494
+		return new EE_Submit_Input(
495
+			array(
496
+				'html_name'             => 'ee-form-submit-' . $this->slug(),
497
+				'html_id'               => 'ee-form-submit-' . $this->slug(),
498
+				'html_class'            => 'ee-form-submit',
499
+				'html_label'            => ' ',
500
+				'other_html_attributes' => ' rel="' . $this->slug() . '"',
501
+				'default'               => $text,
502
+			)
503
+		);
504
+	}
505
+
506
+
507
+	/**
508
+	 * calls generateSubmitButton() and appends it onto the form along with a float clearing div
509
+	 *
510
+	 * @param string $text
511
+	 * @return void
512
+	 * @throws EE_Error
513
+	 */
514
+	public function appendSubmitButton($text = '')
515
+	{
516
+		if ($this->form->subsection_exists($this->slug() . '-submit-btn')) {
517
+			return;
518
+		}
519
+		$this->form->add_subsections(
520
+			array($this->slug() . '-submit-btn' => $this->generateSubmitButton($text)),
521
+			null,
522
+			false
523
+		);
524
+	}
525
+
526
+
527
+	/**
528
+	 * creates and returns an EE_Submit_Input labeled "Cancel"
529
+	 *
530
+	 * @param string $text
531
+	 * @return EE_Submit_Input
532
+	 */
533
+	public function generateCancelButton($text = '')
534
+	{
535
+		$cancel_button = new EE_Submit_Input(
536
+			array(
537
+				'html_name'             => 'ee-form-submit-' . $this->slug(), // YES! Same name as submit !!!
538
+				'html_id'               => 'ee-cancel-form-' . $this->slug(),
539
+				'html_class'            => 'ee-cancel-form',
540
+				'html_label'            => ' ',
541
+				'other_html_attributes' => ' rel="' . $this->slug() . '"',
542
+				'default'               => ! empty($text) ? $text : esc_html__('Cancel', 'event_espresso'),
543
+			)
544
+		);
545
+		$cancel_button->set_button_css_attributes(false);
546
+		return $cancel_button;
547
+	}
548
+
549
+
550
+	/**
551
+	 * appends a float clearing div onto end of form
552
+	 *
553
+	 * @return void
554
+	 * @throws EE_Error
555
+	 */
556
+	public function clearFormButtonFloats()
557
+	{
558
+		$this->form->add_subsections(
559
+			array(
560
+				'clear-submit-btn-float' => new EE_Form_Section_HTML(
561
+					EEH_HTML::div('', '', 'clear-float') . EEH_HTML::divx()
562
+				),
563
+			),
564
+			null,
565
+			false
566
+		);
567
+	}
568
+
569
+
570
+	/**
571
+	 * takes the generated form and displays it along with ony other non-form HTML that may be required
572
+	 * returns a string of HTML that can be directly echoed in a template
573
+	 *
574
+	 * @return string
575
+	 * @throws InvalidArgumentException
576
+	 * @throws InvalidInterfaceException
577
+	 * @throws InvalidDataTypeException
578
+	 * @throws LogicException
579
+	 * @throws EE_Error
580
+	 */
581
+	public function display()
582
+	{
583
+		$form_html = apply_filters(
584
+			'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__display__before_form',
585
+			''
586
+		);
587
+		$form_config = $this->formConfig();
588
+		if (
589
+			$form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
590
+			|| $form_config === FormHandler::ADD_FORM_TAGS_ONLY
591
+		) {
592
+			$additional_props = $this->requiresMultipartEnctype()
593
+				? 'enctype="multipart/form-data"'
594
+				: '';
595
+			$form_html .= $this->form()->form_open(
596
+				$this->formAction(),
597
+				'POST',
598
+				$additional_props
599
+			);
600
+		}
601
+		$form_html .= $this->form(true)->get_html();
602
+		if (
603
+			$form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
604
+			|| $form_config === FormHandler::ADD_FORM_TAGS_ONLY
605
+		) {
606
+			$form_html .= $this->form()->form_close();
607
+		}
608
+		$form_html .= apply_filters(
609
+			'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__display__after_form',
610
+			''
611
+		);
612
+		return $form_html;
613
+	}
614
+
615
+	/**
616
+	 * Determines if this form needs "enctype='multipart/form-data'" or not.
617
+	 * @since 4.9.80.p
618
+	 * @return bool
619
+	 * @throws EE_Error
620
+	 */
621
+	public function requiresMultipartEnctype()
622
+	{
623
+		foreach ($this->form()->inputs_in_subsections() as $input) {
624
+			if ($input instanceof EE_File_Input) {
625
+				return true;
626
+			}
627
+		}
628
+		return false;
629
+	}
630
+
631
+
632
+	/**
633
+	 * handles processing the form submission
634
+	 * returns true or false depending on whether the form was processed successfully or not
635
+	 *
636
+	 * @param array $submitted_form_data
637
+	 * @return array
638
+	 * @throws InvalidArgumentException
639
+	 * @throws InvalidInterfaceException
640
+	 * @throws InvalidDataTypeException
641
+	 * @throws EE_Error
642
+	 * @throws LogicException
643
+	 * @throws InvalidFormSubmissionException
644
+	 */
645
+	public function process($submitted_form_data = array())
646
+	{
647
+		if (! $this->form()->was_submitted($submitted_form_data)) {
648
+			throw new InvalidFormSubmissionException($this->form_name);
649
+		}
650
+		$this->form(true)->receive_form_submission($submitted_form_data);
651 651
 		if (! $this->form()->is_valid()) {
652 652
 			throw new InvalidFormSubmissionException(
653
-                $this->form_name,
654
-                sprintf(
655
-                    esc_html__(
656
-                        'The "%1$s" form is invalid. Please correct the following errors and resubmit: %2$s %3$s',
657
-                        'event_espresso'
658
-                    ),
659
-                    $this->form_name,
660
-                    '<br />',
661
-                    implode('<br />', $this->form()->get_validation_errors_accumulated())
662
-                )
663
-            );
664
-        }
665
-        return apply_filters(
666
-            'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__process__valid_data',
667
-            $this->form()->valid_data(),
668
-            $this
669
-        );
670
-    }
653
+				$this->form_name,
654
+				sprintf(
655
+					esc_html__(
656
+						'The "%1$s" form is invalid. Please correct the following errors and resubmit: %2$s %3$s',
657
+						'event_espresso'
658
+					),
659
+					$this->form_name,
660
+					'<br />',
661
+					implode('<br />', $this->form()->get_validation_errors_accumulated())
662
+				)
663
+			);
664
+		}
665
+		return apply_filters(
666
+			'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__process__valid_data',
667
+			$this->form()->valid_data(),
668
+			$this
669
+		);
670
+	}
671 671
 }
Please login to merge, or discard this patch.
Spacing   +17 added lines, -17 removed lines patch added patch discarded remove patch
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
      */
172 172
     public function form($for_display = false)
173 173
     {
174
-        if (! $this->formIsValid()) {
174
+        if ( ! $this->formIsValid()) {
175 175
             return null;
176 176
         }
177 177
         if ($for_display) {
@@ -274,7 +274,7 @@  discard block
 block discarded – undo
274 274
      */
275 275
     public function setFormName($form_name)
276 276
     {
277
-        if (! is_string($form_name)) {
277
+        if ( ! is_string($form_name)) {
278 278
             throw new InvalidDataTypeException('$form_name', $form_name, 'string');
279 279
         }
280 280
         $this->form_name = $form_name;
@@ -298,7 +298,7 @@  discard block
 block discarded – undo
298 298
      */
299 299
     public function setAdminName($admin_name)
300 300
     {
301
-        if (! is_string($admin_name)) {
301
+        if ( ! is_string($admin_name)) {
302 302
             throw new InvalidDataTypeException('$admin_name', $admin_name, 'string');
303 303
         }
304 304
         $this->admin_name = $admin_name;
@@ -322,7 +322,7 @@  discard block
 block discarded – undo
322 322
      */
323 323
     public function setSlug($slug)
324 324
     {
325
-        if (! is_string($slug)) {
325
+        if ( ! is_string($slug)) {
326 326
             throw new InvalidDataTypeException('$slug', $slug, 'string');
327 327
         }
328 328
         $this->slug = $slug;
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
      */
346 346
     public function setSubmitBtnText($submit_btn_text)
347 347
     {
348
-        if (! is_string($submit_btn_text)) {
348
+        if ( ! is_string($submit_btn_text)) {
349 349
             throw new InvalidDataTypeException('$submit_btn_text', $submit_btn_text, 'string');
350 350
         }
351 351
         if (empty($submit_btn_text)) {
@@ -374,7 +374,7 @@  discard block
 block discarded – undo
374 374
      */
375 375
     public function setFormAction($form_action)
376 376
     {
377
-        if (! is_string($form_action)) {
377
+        if ( ! is_string($form_action)) {
378 378
             throw new InvalidDataTypeException('$form_action', $form_action, 'string');
379 379
         }
380 380
         $this->form_action = $form_action;
@@ -493,11 +493,11 @@  discard block
 block discarded – undo
493 493
         $text = ! empty($text) ? $text : $this->submitBtnText();
494 494
         return new EE_Submit_Input(
495 495
             array(
496
-                'html_name'             => 'ee-form-submit-' . $this->slug(),
497
-                'html_id'               => 'ee-form-submit-' . $this->slug(),
496
+                'html_name'             => 'ee-form-submit-'.$this->slug(),
497
+                'html_id'               => 'ee-form-submit-'.$this->slug(),
498 498
                 'html_class'            => 'ee-form-submit',
499 499
                 'html_label'            => '&nbsp;',
500
-                'other_html_attributes' => ' rel="' . $this->slug() . '"',
500
+                'other_html_attributes' => ' rel="'.$this->slug().'"',
501 501
                 'default'               => $text,
502 502
             )
503 503
         );
@@ -513,11 +513,11 @@  discard block
 block discarded – undo
513 513
      */
514 514
     public function appendSubmitButton($text = '')
515 515
     {
516
-        if ($this->form->subsection_exists($this->slug() . '-submit-btn')) {
516
+        if ($this->form->subsection_exists($this->slug().'-submit-btn')) {
517 517
             return;
518 518
         }
519 519
         $this->form->add_subsections(
520
-            array($this->slug() . '-submit-btn' => $this->generateSubmitButton($text)),
520
+            array($this->slug().'-submit-btn' => $this->generateSubmitButton($text)),
521 521
             null,
522 522
             false
523 523
         );
@@ -534,11 +534,11 @@  discard block
 block discarded – undo
534 534
     {
535 535
         $cancel_button = new EE_Submit_Input(
536 536
             array(
537
-                'html_name'             => 'ee-form-submit-' . $this->slug(), // YES! Same name as submit !!!
538
-                'html_id'               => 'ee-cancel-form-' . $this->slug(),
537
+                'html_name'             => 'ee-form-submit-'.$this->slug(), // YES! Same name as submit !!!
538
+                'html_id'               => 'ee-cancel-form-'.$this->slug(),
539 539
                 'html_class'            => 'ee-cancel-form',
540 540
                 'html_label'            => '&nbsp;',
541
-                'other_html_attributes' => ' rel="' . $this->slug() . '"',
541
+                'other_html_attributes' => ' rel="'.$this->slug().'"',
542 542
                 'default'               => ! empty($text) ? $text : esc_html__('Cancel', 'event_espresso'),
543 543
             )
544 544
         );
@@ -558,7 +558,7 @@  discard block
 block discarded – undo
558 558
         $this->form->add_subsections(
559 559
             array(
560 560
                 'clear-submit-btn-float' => new EE_Form_Section_HTML(
561
-                    EEH_HTML::div('', '', 'clear-float') . EEH_HTML::divx()
561
+                    EEH_HTML::div('', '', 'clear-float').EEH_HTML::divx()
562 562
                 ),
563 563
             ),
564 564
             null,
@@ -644,11 +644,11 @@  discard block
 block discarded – undo
644 644
      */
645 645
     public function process($submitted_form_data = array())
646 646
     {
647
-        if (! $this->form()->was_submitted($submitted_form_data)) {
647
+        if ( ! $this->form()->was_submitted($submitted_form_data)) {
648 648
             throw new InvalidFormSubmissionException($this->form_name);
649 649
         }
650 650
         $this->form(true)->receive_form_submission($submitted_form_data);
651
-		if (! $this->form()->is_valid()) {
651
+		if ( ! $this->form()->is_valid()) {
652 652
 			throw new InvalidFormSubmissionException(
653 653
                 $this->form_name,
654 654
                 sprintf(
Please login to merge, or discard this patch.
core/libraries/form_sections/inputs/EE_Switch_Input.input.php 1 patch
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -24,9 +24,9 @@
 block discarded – undo
24 24
 				]
25 25
 			)
26 26
 		);
27
-		if (! is_array($answer_options) || empty($answer_options)) {
27
+		if ( ! is_array($answer_options) || empty($answer_options)) {
28 28
 			$answer_options = [
29
-				EE_Switch_Input::OPTION_ON  => esc_html__( 'enabled', 'event_espresso'),
29
+				EE_Switch_Input::OPTION_ON  => esc_html__('enabled', 'event_espresso'),
30 30
 				EE_Switch_Input::OPTION_OFF => esc_html__('disabled', 'event_espresso'),
31 31
 			];
32 32
 		}
Please login to merge, or discard this patch.
core/libraries/form_sections/inputs/EE_Select_Input.input.php 1 patch
Indentation   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -13,16 +13,16 @@
 block discarded – undo
13 13
  */
14 14
 class EE_Select_Input extends EE_Form_Input_With_Options_Base
15 15
 {
16
-    /**
17
-     * @param array $answer_options
18
-     * @param array $input_settings
19
-     */
20
-    public function __construct($answer_options, $input_settings = array())
21
-    {
22
-        $this->_set_display_strategy(new EE_Select_Display_Strategy($answer_options));
23
-        $this->_add_validation_strategy(
24
-            new EE_Enum_Validation_Strategy($input_settings['validation_error_message'] ?? null)
25
-        );
26
-        parent::__construct($answer_options, $input_settings);
27
-    }
16
+	/**
17
+	 * @param array $answer_options
18
+	 * @param array $input_settings
19
+	 */
20
+	public function __construct($answer_options, $input_settings = array())
21
+	{
22
+		$this->_set_display_strategy(new EE_Select_Display_Strategy($answer_options));
23
+		$this->_add_validation_strategy(
24
+			new EE_Enum_Validation_Strategy($input_settings['validation_error_message'] ?? null)
25
+		);
26
+		parent::__construct($answer_options, $input_settings);
27
+	}
28 28
 }
Please login to merge, or discard this patch.
core/libraries/form_sections/inputs/EE_Radio_Button_Input.input.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -11,17 +11,17 @@
 block discarded – undo
11 11
  */
12 12
 class EE_Radio_Button_Input extends EE_Form_Input_With_Options_Base
13 13
 {
14
-    /**
15
-     * @param array $answer_options
16
-     * @param array $input_settings
17
-     */
18
-    public function __construct($answer_options, $input_settings = array())
19
-    {
20
-        $this->_set_display_strategy(new EE_Radio_Button_Display_Strategy());
21
-        $this->_add_validation_strategy(
22
-            new EE_Enum_Validation_Strategy($input_settings['validation_error_message'] ?? null)
23
-        );
24
-        $this->_multiple_selections = false;
25
-        parent::__construct($answer_options, $input_settings);
26
-    }
14
+	/**
15
+	 * @param array $answer_options
16
+	 * @param array $input_settings
17
+	 */
18
+	public function __construct($answer_options, $input_settings = array())
19
+	{
20
+		$this->_set_display_strategy(new EE_Radio_Button_Display_Strategy());
21
+		$this->_add_validation_strategy(
22
+			new EE_Enum_Validation_Strategy($input_settings['validation_error_message'] ?? null)
23
+		);
24
+		$this->_multiple_selections = false;
25
+		parent::__construct($answer_options, $input_settings);
26
+	}
27 27
 }
Please login to merge, or discard this patch.
core/libraries/form_sections/base/EE_Form_Section_Proper.form.php 1 patch
Indentation   +1520 added lines, -1520 removed lines patch added patch discarded remove patch
@@ -15,336 +15,336 @@  discard block
 block discarded – undo
15 15
  */
16 16
 class EE_Form_Section_Proper extends EE_Form_Section_Validatable
17 17
 {
18
-    const SUBMITTED_FORM_DATA_SSN_KEY = 'submitted_form_data';
19
-
20
-    /**
21
-     * Subsections
22
-     *
23
-     * @var EE_Form_Section_Validatable[]
24
-     */
25
-    protected $_subsections = array();
26
-
27
-    /**
28
-     * Strategy for laying out the form
29
-     *
30
-     * @var EE_Form_Section_Layout_Base
31
-     */
32
-    protected $_layout_strategy;
33
-
34
-    /**
35
-     * Whether or not this form has received and validated a form submission yet
36
-     *
37
-     * @var boolean
38
-     */
39
-    protected $_received_submission = false;
40
-
41
-    /**
42
-     * message displayed to users upon successful form submission
43
-     *
44
-     * @var string
45
-     */
46
-    protected $_form_submission_success_message = '';
47
-
48
-    /**
49
-     * message displayed to users upon unsuccessful form submission
50
-     *
51
-     * @var string
52
-     */
53
-    protected $_form_submission_error_message = '';
54
-
55
-    /**
56
-     * @var array like post / request
57
-     */
58
-    protected $cached_request_data;
59
-
60
-    /**
61
-     * Stores whether this form (and its sub-sections) were found to be valid or not.
62
-     * Starts off as null, but once the form is validated, it set to either true or false
63
-     * @var boolean|null
64
-     */
65
-    protected $is_valid;
66
-
67
-    /**
68
-     * Stores all the data that will localized for form validation
69
-     *
70
-     * @var array
71
-     */
72
-    protected static $_js_localization = array();
73
-
74
-    /**
75
-     * whether or not the form's localized validation JS vars have been set
76
-     *
77
-     * @type boolean
78
-     */
79
-    protected static $_scripts_localized = false;
80
-
81
-
82
-    /**
83
-     * when constructing a proper form section, calls _construct_finalize on children
84
-     * so that they know who their parent is, and what name they've been given.
85
-     *
86
-     * @param array[] $options_array   {
87
-     * @type          $subsections     EE_Form_Section_Validatable[] where keys are the section's name
88
-     * @type          $include         string[] numerically-indexed where values are section names to be included,
89
-     *                                 and in that order. This is handy if you want
90
-     *                                 the subsections to be ordered differently than the default, and if you override
91
-     *                                 which fields are shown
92
-     * @type          $exclude         string[] values are subsections to be excluded. This is handy if you want
93
-     *                                 to remove certain default subsections (note: if you specify BOTH 'include' AND
94
-     *                                 'exclude', the inclusions will be applied first, and the exclusions will exclude
95
-     *                                 items from that list of inclusions)
96
-     * @type          $layout_strategy EE_Form_Section_Layout_Base strategy for laying out the form
97
-     *                                 } @see EE_Form_Section_Validatable::__construct()
98
-     * @throws EE_Error
99
-     */
100
-    public function __construct($options_array = array())
101
-    {
102
-        $options_array = (array) apply_filters(
103
-            'FHEE__EE_Form_Section_Proper___construct__options_array',
104
-            $options_array,
105
-            $this
106
-        );
107
-        // call parent first, as it may be setting the name
108
-        parent::__construct($options_array);
109
-        // if they've included subsections in the constructor, add them now
110
-        if (isset($options_array['include'])) {
111
-            // we are going to make sure we ONLY have those subsections to include
112
-            // AND we are going to make sure they're in that specified order
113
-            $reordered_subsections = array();
114
-            foreach ($options_array['include'] as $input_name) {
115
-                if (isset($this->_subsections[ $input_name ])) {
116
-                    $reordered_subsections[ $input_name ] = $this->_subsections[ $input_name ];
117
-                }
118
-            }
119
-            $this->_subsections = $reordered_subsections;
120
-        }
121
-        if (isset($options_array['exclude'])) {
122
-            $exclude            = $options_array['exclude'];
123
-            $this->_subsections = array_diff_key($this->_subsections, array_flip($exclude));
124
-        }
125
-        if (isset($options_array['layout_strategy'])) {
126
-            $this->_layout_strategy = $options_array['layout_strategy'];
127
-        }
128
-        if (! $this->_layout_strategy) {
129
-            $this->_layout_strategy = is_admin() ? new EE_Admin_Two_Column_Layout() : new EE_Two_Column_Layout();
130
-        }
131
-        $this->_layout_strategy->_construct_finalize($this);
132
-        // ok so we are definitely going to want the forms JS,
133
-        // so enqueue it or remember to enqueue it during wp_enqueue_scripts
134
-        if (did_action('wp_enqueue_scripts') || did_action('admin_enqueue_scripts')) {
135
-            // ok so they've constructed this object after when they should have.
136
-            // just enqueue the generic form scripts and initialize the form immediately in the JS
137
-            EE_Form_Section_Proper::wp_enqueue_scripts(true);
138
-        } else {
139
-            add_action('wp_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
140
-            add_action('admin_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
141
-        }
142
-        add_action('wp_footer', array($this, 'ensure_scripts_localized'), 1);
143
-        /**
144
-         * Gives other plugins a chance to hook in before construct finalize is called.
145
-         * The form probably doesn't yet have a parent form section.
146
-         * Since 4.9.32, when this action was introduced, this is the best place to add a subsection onto a form,
147
-         * assuming you don't care what the form section's name, HTML ID, or HTML name etc are.
148
-         * Also see AHEE__EE_Form_Section_Proper___construct_finalize__end
149
-         *
150
-         * @since 4.9.32
151
-         * @param EE_Form_Section_Proper $this          before __construct is done, but all of its logic,
152
-         *                                              except maybe calling _construct_finalize has been done
153
-         * @param array                  $options_array options passed into the constructor
154
-         */
155
-        do_action(
156
-            'AHEE__EE_Form_Input_Base___construct__before_construct_finalize_called',
157
-            $this,
158
-            $options_array
159
-        );
160
-        if (isset($options_array['name'])) {
161
-            $this->_construct_finalize(null, $options_array['name']);
162
-        }
163
-    }
164
-
165
-
166
-    /**
167
-     * Finishes construction given the parent form section and this form section's name
168
-     *
169
-     * @param EE_Form_Section_Proper $parent_form_section
170
-     * @param string                 $name
171
-     * @throws EE_Error
172
-     */
173
-    public function _construct_finalize($parent_form_section, $name)
174
-    {
175
-        parent::_construct_finalize($parent_form_section, $name);
176
-        $this->_set_default_name_if_empty();
177
-        $this->_set_default_html_id_if_empty();
178
-        foreach ($this->_subsections as $subsection_name => $subsection) {
179
-            if ($subsection instanceof EE_Form_Section_Base) {
180
-                $subsection->_construct_finalize($this, $subsection_name);
181
-            } else {
182
-                throw new EE_Error(
183
-                    sprintf(
184
-                        esc_html__(
185
-                            'Subsection "%s" is not an instanceof EE_Form_Section_Base on form "%s". It is a "%s"',
186
-                            'event_espresso'
187
-                        ),
188
-                        $subsection_name,
189
-                        get_class($this),
190
-                        $subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
191
-                    )
192
-                );
193
-            }
194
-        }
195
-        /**
196
-         * Action performed just after form has been given a name (and HTML ID etc) and is fully constructed.
197
-         * If you have code that should modify the form and needs it and its subsections to have a name, HTML ID
198
-         * (or other attributes derived from the name like the HTML label id, etc), this is where it should be done.
199
-         * This might only happen just before displaying the form, or just before it receives form submission data.
200
-         * If you need to modify the form or its subsections before _construct_finalize is called on it (and we've
201
-         * ensured it has a name, HTML IDs, etc
202
-         *
203
-         * @param EE_Form_Section_Proper      $this
204
-         * @param EE_Form_Section_Proper|null $parent_form_section
205
-         * @param string                      $name
206
-         */
207
-        do_action(
208
-            'AHEE__EE_Form_Section_Proper___construct_finalize__end',
209
-            $this,
210
-            $parent_form_section,
211
-            $name
212
-        );
213
-    }
214
-
215
-
216
-    /**
217
-     * Gets the layout strategy for this form section
218
-     *
219
-     * @return EE_Form_Section_Layout_Base
220
-     */
221
-    public function get_layout_strategy()
222
-    {
223
-        return $this->_layout_strategy;
224
-    }
225
-
226
-
227
-    /**
228
-     * Gets the HTML for a single input for this form section according
229
-     * to the layout strategy
230
-     *
231
-     * @param EE_Form_Input_Base $input
232
-     * @return string
233
-     */
234
-    public function get_html_for_input($input)
235
-    {
236
-        return $this->_layout_strategy->layout_input($input);
237
-    }
238
-
239
-
240
-    /**
241
-     * was_submitted - checks if form inputs are present in request data
242
-     * Basically an alias for form_data_present_in() (which is used by both
243
-     * proper form sections and form inputs)
244
-     *
245
-     * @param null $form_data
246
-     * @return boolean
247
-     * @throws EE_Error
248
-     */
249
-    public function was_submitted($form_data = null)
250
-    {
251
-        return $this->form_data_present_in($form_data);
252
-    }
253
-
254
-    /**
255
-     * Gets the cached request data; but if there is none, or $req_data was set with
256
-     * something different, refresh the cache, and then return it
257
-     * @param null $req_data
258
-     * @return array
259
-     */
260
-    protected function getCachedRequest($req_data = null)
261
-    {
262
-        if (
263
-            $this->cached_request_data === null
264
-            || (
265
-                $req_data !== null
266
-                && $req_data !== $this->cached_request_data
267
-            )
268
-        ) {
269
-            $req_data = apply_filters(
270
-                'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
271
-                $req_data,
272
-                $this
273
-            );
274
-            if ($req_data === null) {
275
-                /** @var RequestInterface $request */
276
-                $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
277
-                $req_data = $request->requestParams();
278
-            }
279
-            $req_data = apply_filters(
280
-                'FHEE__EE_Form_Section_Proper__receive_form_submission__request_data',
281
-                $req_data,
282
-                $this
283
-            );
284
-            $this->cached_request_data = (array) $req_data;
285
-        }
286
-        return $this->cached_request_data;
287
-    }
288
-
289
-
290
-    /**
291
-     * After the form section is initially created, call this to sanitize the data in the submission
292
-     * which relates to this form section, validate it, and set it as properties on the form.
293
-     *
294
-     * @param array|null $req_data should usually be post data (the default).
295
-     *                             However, you CAN supply a different array.
296
-     *                             Consider using set_defaults() instead however.
297
-     *                             (If you rendered the form in the page using echo $form_x->get_html()
298
-     *                             the inputs will have the correct name in the request data for this function
299
-     *                             to find them and populate the form with them.
300
-     *                             If you have a flat form (with only input subsections),
301
-     *                             you can supply a flat array where keys
302
-     *                             are the form input names and values are their values)
303
-     * @param boolean    $validate whether or not to perform validation on this data. Default is,
304
-     *                             of course, to validate that data, and set errors on the invalid values.
305
-     *                             But if the data has already been validated
306
-     *                             (eg you validated the data then stored it in the DB)
307
-     *                             you may want to skip this step.
308
-     * @throws InvalidArgumentException
309
-     * @throws InvalidInterfaceException
310
-     * @throws InvalidDataTypeException
311
-     * @throws EE_Error
312
-     */
313
-    public function receive_form_submission($req_data = null, $validate = true)
314
-    {
315
-        $req_data = $this->getCachedRequest($req_data);
316
-        $this->_normalize($req_data);
317
-        if ($validate) {
318
-            $this->_validate();
319
-            // if it's invalid, we're going to want to re-display so remember what they submitted
320
-            if (! $this->is_valid()) {
321
-                $this->store_submitted_form_data_in_session();
322
-            }
323
-        }
324
-        if ($this->submission_error_message() === '' && ! $this->is_valid()) {
325
-            $this->set_submission_error_message();
326
-        }
327
-        do_action(
328
-            'AHEE__EE_Form_Section_Proper__receive_form_submission__end',
329
-            $req_data,
330
-            $this,
331
-            $validate
332
-        );
333
-    }
334
-
335
-
336
-    /**
337
-     * caches the originally submitted input values in the session
338
-     * so that they can be used to repopulate the form if it failed validation
339
-     *
340
-     * @return boolean whether or not the data was successfully stored in the session
341
-     * @throws InvalidArgumentException
342
-     * @throws InvalidInterfaceException
343
-     * @throws InvalidDataTypeException
344
-     * @throws EE_Error
345
-     */
346
-    protected function store_submitted_form_data_in_session()
347
-    {
18
+	const SUBMITTED_FORM_DATA_SSN_KEY = 'submitted_form_data';
19
+
20
+	/**
21
+	 * Subsections
22
+	 *
23
+	 * @var EE_Form_Section_Validatable[]
24
+	 */
25
+	protected $_subsections = array();
26
+
27
+	/**
28
+	 * Strategy for laying out the form
29
+	 *
30
+	 * @var EE_Form_Section_Layout_Base
31
+	 */
32
+	protected $_layout_strategy;
33
+
34
+	/**
35
+	 * Whether or not this form has received and validated a form submission yet
36
+	 *
37
+	 * @var boolean
38
+	 */
39
+	protected $_received_submission = false;
40
+
41
+	/**
42
+	 * message displayed to users upon successful form submission
43
+	 *
44
+	 * @var string
45
+	 */
46
+	protected $_form_submission_success_message = '';
47
+
48
+	/**
49
+	 * message displayed to users upon unsuccessful form submission
50
+	 *
51
+	 * @var string
52
+	 */
53
+	protected $_form_submission_error_message = '';
54
+
55
+	/**
56
+	 * @var array like post / request
57
+	 */
58
+	protected $cached_request_data;
59
+
60
+	/**
61
+	 * Stores whether this form (and its sub-sections) were found to be valid or not.
62
+	 * Starts off as null, but once the form is validated, it set to either true or false
63
+	 * @var boolean|null
64
+	 */
65
+	protected $is_valid;
66
+
67
+	/**
68
+	 * Stores all the data that will localized for form validation
69
+	 *
70
+	 * @var array
71
+	 */
72
+	protected static $_js_localization = array();
73
+
74
+	/**
75
+	 * whether or not the form's localized validation JS vars have been set
76
+	 *
77
+	 * @type boolean
78
+	 */
79
+	protected static $_scripts_localized = false;
80
+
81
+
82
+	/**
83
+	 * when constructing a proper form section, calls _construct_finalize on children
84
+	 * so that they know who their parent is, and what name they've been given.
85
+	 *
86
+	 * @param array[] $options_array   {
87
+	 * @type          $subsections     EE_Form_Section_Validatable[] where keys are the section's name
88
+	 * @type          $include         string[] numerically-indexed where values are section names to be included,
89
+	 *                                 and in that order. This is handy if you want
90
+	 *                                 the subsections to be ordered differently than the default, and if you override
91
+	 *                                 which fields are shown
92
+	 * @type          $exclude         string[] values are subsections to be excluded. This is handy if you want
93
+	 *                                 to remove certain default subsections (note: if you specify BOTH 'include' AND
94
+	 *                                 'exclude', the inclusions will be applied first, and the exclusions will exclude
95
+	 *                                 items from that list of inclusions)
96
+	 * @type          $layout_strategy EE_Form_Section_Layout_Base strategy for laying out the form
97
+	 *                                 } @see EE_Form_Section_Validatable::__construct()
98
+	 * @throws EE_Error
99
+	 */
100
+	public function __construct($options_array = array())
101
+	{
102
+		$options_array = (array) apply_filters(
103
+			'FHEE__EE_Form_Section_Proper___construct__options_array',
104
+			$options_array,
105
+			$this
106
+		);
107
+		// call parent first, as it may be setting the name
108
+		parent::__construct($options_array);
109
+		// if they've included subsections in the constructor, add them now
110
+		if (isset($options_array['include'])) {
111
+			// we are going to make sure we ONLY have those subsections to include
112
+			// AND we are going to make sure they're in that specified order
113
+			$reordered_subsections = array();
114
+			foreach ($options_array['include'] as $input_name) {
115
+				if (isset($this->_subsections[ $input_name ])) {
116
+					$reordered_subsections[ $input_name ] = $this->_subsections[ $input_name ];
117
+				}
118
+			}
119
+			$this->_subsections = $reordered_subsections;
120
+		}
121
+		if (isset($options_array['exclude'])) {
122
+			$exclude            = $options_array['exclude'];
123
+			$this->_subsections = array_diff_key($this->_subsections, array_flip($exclude));
124
+		}
125
+		if (isset($options_array['layout_strategy'])) {
126
+			$this->_layout_strategy = $options_array['layout_strategy'];
127
+		}
128
+		if (! $this->_layout_strategy) {
129
+			$this->_layout_strategy = is_admin() ? new EE_Admin_Two_Column_Layout() : new EE_Two_Column_Layout();
130
+		}
131
+		$this->_layout_strategy->_construct_finalize($this);
132
+		// ok so we are definitely going to want the forms JS,
133
+		// so enqueue it or remember to enqueue it during wp_enqueue_scripts
134
+		if (did_action('wp_enqueue_scripts') || did_action('admin_enqueue_scripts')) {
135
+			// ok so they've constructed this object after when they should have.
136
+			// just enqueue the generic form scripts and initialize the form immediately in the JS
137
+			EE_Form_Section_Proper::wp_enqueue_scripts(true);
138
+		} else {
139
+			add_action('wp_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
140
+			add_action('admin_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
141
+		}
142
+		add_action('wp_footer', array($this, 'ensure_scripts_localized'), 1);
143
+		/**
144
+		 * Gives other plugins a chance to hook in before construct finalize is called.
145
+		 * The form probably doesn't yet have a parent form section.
146
+		 * Since 4.9.32, when this action was introduced, this is the best place to add a subsection onto a form,
147
+		 * assuming you don't care what the form section's name, HTML ID, or HTML name etc are.
148
+		 * Also see AHEE__EE_Form_Section_Proper___construct_finalize__end
149
+		 *
150
+		 * @since 4.9.32
151
+		 * @param EE_Form_Section_Proper $this          before __construct is done, but all of its logic,
152
+		 *                                              except maybe calling _construct_finalize has been done
153
+		 * @param array                  $options_array options passed into the constructor
154
+		 */
155
+		do_action(
156
+			'AHEE__EE_Form_Input_Base___construct__before_construct_finalize_called',
157
+			$this,
158
+			$options_array
159
+		);
160
+		if (isset($options_array['name'])) {
161
+			$this->_construct_finalize(null, $options_array['name']);
162
+		}
163
+	}
164
+
165
+
166
+	/**
167
+	 * Finishes construction given the parent form section and this form section's name
168
+	 *
169
+	 * @param EE_Form_Section_Proper $parent_form_section
170
+	 * @param string                 $name
171
+	 * @throws EE_Error
172
+	 */
173
+	public function _construct_finalize($parent_form_section, $name)
174
+	{
175
+		parent::_construct_finalize($parent_form_section, $name);
176
+		$this->_set_default_name_if_empty();
177
+		$this->_set_default_html_id_if_empty();
178
+		foreach ($this->_subsections as $subsection_name => $subsection) {
179
+			if ($subsection instanceof EE_Form_Section_Base) {
180
+				$subsection->_construct_finalize($this, $subsection_name);
181
+			} else {
182
+				throw new EE_Error(
183
+					sprintf(
184
+						esc_html__(
185
+							'Subsection "%s" is not an instanceof EE_Form_Section_Base on form "%s". It is a "%s"',
186
+							'event_espresso'
187
+						),
188
+						$subsection_name,
189
+						get_class($this),
190
+						$subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
191
+					)
192
+				);
193
+			}
194
+		}
195
+		/**
196
+		 * Action performed just after form has been given a name (and HTML ID etc) and is fully constructed.
197
+		 * If you have code that should modify the form and needs it and its subsections to have a name, HTML ID
198
+		 * (or other attributes derived from the name like the HTML label id, etc), this is where it should be done.
199
+		 * This might only happen just before displaying the form, or just before it receives form submission data.
200
+		 * If you need to modify the form or its subsections before _construct_finalize is called on it (and we've
201
+		 * ensured it has a name, HTML IDs, etc
202
+		 *
203
+		 * @param EE_Form_Section_Proper      $this
204
+		 * @param EE_Form_Section_Proper|null $parent_form_section
205
+		 * @param string                      $name
206
+		 */
207
+		do_action(
208
+			'AHEE__EE_Form_Section_Proper___construct_finalize__end',
209
+			$this,
210
+			$parent_form_section,
211
+			$name
212
+		);
213
+	}
214
+
215
+
216
+	/**
217
+	 * Gets the layout strategy for this form section
218
+	 *
219
+	 * @return EE_Form_Section_Layout_Base
220
+	 */
221
+	public function get_layout_strategy()
222
+	{
223
+		return $this->_layout_strategy;
224
+	}
225
+
226
+
227
+	/**
228
+	 * Gets the HTML for a single input for this form section according
229
+	 * to the layout strategy
230
+	 *
231
+	 * @param EE_Form_Input_Base $input
232
+	 * @return string
233
+	 */
234
+	public function get_html_for_input($input)
235
+	{
236
+		return $this->_layout_strategy->layout_input($input);
237
+	}
238
+
239
+
240
+	/**
241
+	 * was_submitted - checks if form inputs are present in request data
242
+	 * Basically an alias for form_data_present_in() (which is used by both
243
+	 * proper form sections and form inputs)
244
+	 *
245
+	 * @param null $form_data
246
+	 * @return boolean
247
+	 * @throws EE_Error
248
+	 */
249
+	public function was_submitted($form_data = null)
250
+	{
251
+		return $this->form_data_present_in($form_data);
252
+	}
253
+
254
+	/**
255
+	 * Gets the cached request data; but if there is none, or $req_data was set with
256
+	 * something different, refresh the cache, and then return it
257
+	 * @param null $req_data
258
+	 * @return array
259
+	 */
260
+	protected function getCachedRequest($req_data = null)
261
+	{
262
+		if (
263
+			$this->cached_request_data === null
264
+			|| (
265
+				$req_data !== null
266
+				&& $req_data !== $this->cached_request_data
267
+			)
268
+		) {
269
+			$req_data = apply_filters(
270
+				'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
271
+				$req_data,
272
+				$this
273
+			);
274
+			if ($req_data === null) {
275
+				/** @var RequestInterface $request */
276
+				$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
277
+				$req_data = $request->requestParams();
278
+			}
279
+			$req_data = apply_filters(
280
+				'FHEE__EE_Form_Section_Proper__receive_form_submission__request_data',
281
+				$req_data,
282
+				$this
283
+			);
284
+			$this->cached_request_data = (array) $req_data;
285
+		}
286
+		return $this->cached_request_data;
287
+	}
288
+
289
+
290
+	/**
291
+	 * After the form section is initially created, call this to sanitize the data in the submission
292
+	 * which relates to this form section, validate it, and set it as properties on the form.
293
+	 *
294
+	 * @param array|null $req_data should usually be post data (the default).
295
+	 *                             However, you CAN supply a different array.
296
+	 *                             Consider using set_defaults() instead however.
297
+	 *                             (If you rendered the form in the page using echo $form_x->get_html()
298
+	 *                             the inputs will have the correct name in the request data for this function
299
+	 *                             to find them and populate the form with them.
300
+	 *                             If you have a flat form (with only input subsections),
301
+	 *                             you can supply a flat array where keys
302
+	 *                             are the form input names and values are their values)
303
+	 * @param boolean    $validate whether or not to perform validation on this data. Default is,
304
+	 *                             of course, to validate that data, and set errors on the invalid values.
305
+	 *                             But if the data has already been validated
306
+	 *                             (eg you validated the data then stored it in the DB)
307
+	 *                             you may want to skip this step.
308
+	 * @throws InvalidArgumentException
309
+	 * @throws InvalidInterfaceException
310
+	 * @throws InvalidDataTypeException
311
+	 * @throws EE_Error
312
+	 */
313
+	public function receive_form_submission($req_data = null, $validate = true)
314
+	{
315
+		$req_data = $this->getCachedRequest($req_data);
316
+		$this->_normalize($req_data);
317
+		if ($validate) {
318
+			$this->_validate();
319
+			// if it's invalid, we're going to want to re-display so remember what they submitted
320
+			if (! $this->is_valid()) {
321
+				$this->store_submitted_form_data_in_session();
322
+			}
323
+		}
324
+		if ($this->submission_error_message() === '' && ! $this->is_valid()) {
325
+			$this->set_submission_error_message();
326
+		}
327
+		do_action(
328
+			'AHEE__EE_Form_Section_Proper__receive_form_submission__end',
329
+			$req_data,
330
+			$this,
331
+			$validate
332
+		);
333
+	}
334
+
335
+
336
+	/**
337
+	 * caches the originally submitted input values in the session
338
+	 * so that they can be used to repopulate the form if it failed validation
339
+	 *
340
+	 * @return boolean whether or not the data was successfully stored in the session
341
+	 * @throws InvalidArgumentException
342
+	 * @throws InvalidInterfaceException
343
+	 * @throws InvalidDataTypeException
344
+	 * @throws EE_Error
345
+	 */
346
+	protected function store_submitted_form_data_in_session()
347
+	{
348 348
 		$session = EE_Registry::instance()->SSN;
349 349
 		if ($session instanceof EE_Session) {
350 350
 			return EE_Registry::instance()->SSN->set_session_data(
@@ -357,1194 +357,1194 @@  discard block
 block discarded – undo
357 357
 	}
358 358
 
359 359
 
360
-    /**
361
-     * retrieves the originally submitted input values in the session
362
-     * so that they can be used to repopulate the form if it failed validation
363
-     *
364
-     * @return array
365
-     * @throws InvalidArgumentException
366
-     * @throws InvalidInterfaceException
367
-     * @throws InvalidDataTypeException
368
-     */
369
-    protected function get_submitted_form_data_from_session()
370
-    {
371
-        $session = EE_Registry::instance()->SSN;
372
-        if ($session instanceof EE_Session) {
373
-            return $session->get_session_data(
374
-                EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY
375
-            );
376
-        }
377
-        return array();
378
-    }
379
-
380
-
381
-    /**
382
-     * flushed the originally submitted input values from the session
383
-     *
384
-     * @return boolean whether or not the data was successfully removed from the session
385
-     * @throws InvalidArgumentException
386
-     * @throws InvalidInterfaceException
387
-     * @throws InvalidDataTypeException
388
-     */
389
-    public static function flush_submitted_form_data_from_session()
390
-    {
391
-        return EE_Registry::instance()->SSN->reset_data(
392
-            array(EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY)
393
-        );
394
-    }
395
-
396
-
397
-    /**
398
-     * Populates this form and its subsections with data from the session.
399
-     * (Wrapper for EE_Form_Section_Proper::receive_form_submission, so it shows
400
-     * validation errors when displaying too)
401
-     * Returns true if the form was populated from the session, false otherwise
402
-     *
403
-     * @return boolean
404
-     * @throws InvalidArgumentException
405
-     * @throws InvalidInterfaceException
406
-     * @throws InvalidDataTypeException
407
-     * @throws EE_Error
408
-     */
409
-    public function populate_from_session()
410
-    {
411
-        $form_data_in_session = $this->get_submitted_form_data_from_session();
412
-        if (empty($form_data_in_session)) {
413
-            return false;
414
-        }
415
-        $this->receive_form_submission($form_data_in_session);
416
-        add_action('shutdown', array('EE_Form_Section_Proper', 'flush_submitted_form_data_from_session'));
417
-        if ($this->form_data_present_in($form_data_in_session)) {
418
-            return true;
419
-        }
420
-        return false;
421
-    }
422
-
423
-
424
-    /**
425
-     * Populates the default data for the form, given an array where keys are
426
-     * the input names, and values are their values (preferably normalized to be their
427
-     * proper PHP types, not all strings... although that should be ok too).
428
-     * Proper subsections are sub-arrays, the key being the subsection's name, and
429
-     * the value being an array formatted in teh same way
430
-     *
431
-     * @param array $default_data
432
-     * @throws EE_Error
433
-     */
434
-    public function populate_defaults($default_data)
435
-    {
436
-        foreach ($this->subsections(false) as $subsection_name => $subsection) {
437
-            if (isset($default_data[ $subsection_name ])) {
438
-                if ($subsection instanceof EE_Form_Input_Base) {
439
-                    $subsection->set_default($default_data[ $subsection_name ]);
440
-                } elseif ($subsection instanceof EE_Form_Section_Proper) {
441
-                    $subsection->populate_defaults($default_data[ $subsection_name ]);
442
-                }
443
-            }
444
-        }
445
-    }
446
-
447
-
448
-    /**
449
-     * returns true if subsection exists
450
-     *
451
-     * @param string $name
452
-     * @return boolean
453
-     */
454
-    public function subsection_exists($name)
455
-    {
456
-        return isset($this->_subsections[ $name ]) ? true : false;
457
-    }
458
-
459
-
460
-    /**
461
-     * Gets the subsection specified by its name
462
-     *
463
-     * @param string  $name
464
-     * @param boolean $require_construction_to_be_finalized most client code should leave this as TRUE
465
-     *                                                      so that the inputs will be properly configured.
466
-     *                                                      However, some client code may be ok
467
-     *                                                      with construction finalize being called later
468
-     *                                                      (realizing that the subsections' html names
469
-     *                                                      might not be set yet, etc.)
470
-     * @return EE_Form_Section_Base
471
-     * @throws EE_Error
472
-     */
473
-    public function get_subsection($name, $require_construction_to_be_finalized = true)
474
-    {
475
-        if ($require_construction_to_be_finalized) {
476
-            $this->ensure_construct_finalized_called();
477
-        }
478
-        return $this->subsection_exists($name) ? $this->_subsections[ $name ] : null;
479
-    }
480
-
481
-
482
-    /**
483
-     * Gets all the validatable subsections of this form section
484
-     *
485
-     * @return EE_Form_Section_Validatable[]
486
-     * @throws EE_Error
487
-     */
488
-    public function get_validatable_subsections()
489
-    {
490
-        $validatable_subsections = array();
491
-        foreach ($this->subsections() as $name => $obj) {
492
-            if ($obj instanceof EE_Form_Section_Validatable) {
493
-                $validatable_subsections[ $name ] = $obj;
494
-            }
495
-        }
496
-        return $validatable_subsections;
497
-    }
498
-
499
-
500
-    /**
501
-     * Gets an input by the given name. If not found, or if its not an EE_FOrm_Input_Base child,
502
-     * throw an EE_Error.
503
-     *
504
-     * @param string  $name
505
-     * @param boolean $require_construction_to_be_finalized most client code should
506
-     *                                                      leave this as TRUE so that the inputs will be properly
507
-     *                                                      configured. However, some client code may be ok with
508
-     *                                                      construction finalize being called later
509
-     *                                                      (realizing that the subsections' html names might not be
510
-     *                                                      set yet, etc.)
511
-     * @return EE_Form_Input_Base
512
-     * @throws EE_Error
513
-     */
514
-    public function get_input($name, $require_construction_to_be_finalized = true)
515
-    {
516
-        $subsection = $this->get_subsection(
517
-            $name,
518
-            $require_construction_to_be_finalized
519
-        );
520
-        if (! $subsection instanceof EE_Form_Input_Base) {
521
-            throw new EE_Error(
522
-                sprintf(
523
-                    esc_html__(
524
-                        "Subsection '%s' is not an instanceof EE_Form_Input_Base on form '%s'. It is a '%s'",
525
-                        'event_espresso'
526
-                    ),
527
-                    $name,
528
-                    get_class($this),
529
-                    $subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
530
-                )
531
-            );
532
-        }
533
-        return $subsection;
534
-    }
535
-
536
-
537
-    /**
538
-     * Like get_input(), gets the proper subsection of the form given the name,
539
-     * otherwise throws an EE_Error
540
-     *
541
-     * @param string  $name
542
-     * @param boolean $require_construction_to_be_finalized most client code should
543
-     *                                                      leave this as TRUE so that the inputs will be properly
544
-     *                                                      configured. However, some client code may be ok with
545
-     *                                                      construction finalize being called later
546
-     *                                                      (realizing that the subsections' html names might not be
547
-     *                                                      set yet, etc.)
548
-     * @return EE_Form_Section_Proper
549
-     * @throws EE_Error
550
-     */
551
-    public function get_proper_subsection($name, $require_construction_to_be_finalized = true)
552
-    {
553
-        $subsection = $this->get_subsection(
554
-            $name,
555
-            $require_construction_to_be_finalized
556
-        );
557
-        if (! $subsection instanceof EE_Form_Section_Proper) {
558
-            throw new EE_Error(
559
-                sprintf(
560
-                    esc_html__(
561
-                        "Subsection '%'s is not an instanceof EE_Form_Section_Proper on form '%s'",
562
-                        'event_espresso'
563
-                    ),
564
-                    $name,
565
-                    get_class($this)
566
-                )
567
-            );
568
-        }
569
-        return $subsection;
570
-    }
571
-
572
-
573
-    /**
574
-     * Gets the value of the specified input. Should be called after receive_form_submission()
575
-     * or populate_defaults() on the form, where the normalized value on the input is set.
576
-     *
577
-     * @param string $name
578
-     * @return mixed depending on the input's type and its normalization strategy
579
-     * @throws EE_Error
580
-     */
581
-    public function get_input_value($name)
582
-    {
583
-        $input = $this->get_input($name);
584
-        return $input->normalized_value();
585
-    }
586
-
587
-
588
-    /**
589
-     * Checks if this form section itself is valid, and then checks its subsections
590
-     *
591
-     * @throws EE_Error
592
-     * @return boolean
593
-     */
594
-    public function is_valid()
595
-    {
596
-        if ($this->is_valid === null) {
597
-            if (! $this->has_received_submission()) {
598
-                throw new EE_Error(
599
-                    sprintf(
600
-                        esc_html__(
601
-                            'You cannot check if a form is valid before receiving the form submission using receive_form_submission',
602
-                            'event_espresso'
603
-                        )
604
-                    )
605
-                );
606
-            }
607
-            if (! parent::is_valid()) {
608
-                $this->is_valid = false;
609
-            } else {
610
-                // ok so no general errors to this entire form section.
611
-                // so let's check the subsections, but only set errors if that hasn't been done yet
612
-                $this->is_valid = true;
613
-                foreach ($this->get_validatable_subsections() as $subsection) {
614
-                    if (! $subsection->is_valid()) {
615
-                        $this->is_valid = false;
616
-                    }
617
-                }
618
-            }
619
-        }
620
-        return $this->is_valid;
621
-    }
622
-
623
-
624
-    /**
625
-     * gets the default name of this form section if none is specified
626
-     *
627
-     * @return void
628
-     */
629
-    protected function _set_default_name_if_empty()
630
-    {
631
-        if (! $this->_name) {
632
-            $classname    = get_class($this);
633
-            $default_name = str_replace('EE_', '', $classname);
634
-            $this->_name  = $default_name;
635
-        }
636
-    }
637
-
638
-
639
-    /**
640
-     * Returns the HTML for the form, except for the form opening and closing tags
641
-     * (as the form section doesn't know where you necessarily want to send the information to),
642
-     * and except for a submit button. Enqueues JS and CSS; if called early enough we will
643
-     * try to enqueue them in the header, otherwise they'll be enqueued in the footer.
644
-     * Not doing_it_wrong because theoretically this CAN be used properly,
645
-     * provided its used during "wp_enqueue_scripts", or it doesn't need to enqueue
646
-     * any CSS.
647
-     *
648
-     * @throws InvalidArgumentException
649
-     * @throws InvalidInterfaceException
650
-     * @throws InvalidDataTypeException
651
-     * @throws EE_Error
652
-     */
653
-    public function get_html_and_js()
654
-    {
655
-        $this->enqueue_js();
656
-        return $this->get_html();
657
-    }
658
-
659
-
660
-    /**
661
-     * returns HTML for displaying this form section. recursively calls display_section() on all subsections
662
-     *
663
-     * @param bool $display_previously_submitted_data
664
-     * @return string
665
-     * @throws InvalidArgumentException
666
-     * @throws InvalidInterfaceException
667
-     * @throws InvalidDataTypeException
668
-     * @throws EE_Error
669
-     * @throws EE_Error
670
-     * @throws EE_Error
671
-     */
672
-    public function get_html($display_previously_submitted_data = true)
673
-    {
674
-        $this->ensure_construct_finalized_called();
675
-        if ($display_previously_submitted_data) {
676
-            $this->populate_from_session();
677
-        }
678
-        return $this->_form_html_filter
679
-            ? $this->_form_html_filter->filterHtml($this->_layout_strategy->layout_form(), $this)
680
-            : $this->_layout_strategy->layout_form();
681
-    }
682
-
683
-
684
-    /**
685
-     * enqueues JS and CSS for the form.
686
-     * It is preferred to call this before wp_enqueue_scripts so the
687
-     * scripts and styles can be put in the header, but if called later
688
-     * they will be put in the footer (which is OK for JS, but in HTML4 CSS should
689
-     * only be in the header; but in HTML5 its ok in the body.
690
-     * See http://stackoverflow.com/questions/4957446/load-external-css-file-in-body-tag.
691
-     * So if your form enqueues CSS, it's preferred to call this before wp_enqueue_scripts.)
692
-     *
693
-     * @return void
694
-     * @throws EE_Error
695
-     */
696
-    public function enqueue_js()
697
-    {
698
-        $this->_enqueue_and_localize_form_js();
699
-        foreach ($this->subsections() as $subsection) {
700
-            $subsection->enqueue_js();
701
-        }
702
-    }
703
-
704
-
705
-    /**
706
-     * adds a filter so that jquery validate gets enqueued in EE_System::wp_enqueue_scripts().
707
-     * This must be done BEFORE wp_enqueue_scripts() gets called, which is on
708
-     * the wp_enqueue_scripts hook.
709
-     * However, registering the form js and localizing it can happen when we
710
-     * actually output the form (which is preferred, seeing how teh form's fields
711
-     * could change until it's actually outputted)
712
-     *
713
-     * @param boolean $init_form_validation_automatically whether or not we want the form validation
714
-     *                                                    to be triggered automatically or not
715
-     * @return void
716
-     */
717
-    public static function wp_enqueue_scripts($init_form_validation_automatically = true)
718
-    {
719
-        wp_register_script(
720
-            'ee_form_section_validation',
721
-            EE_GLOBAL_ASSETS_URL . 'scripts' . '/form_section_validation.js',
722
-            array('jquery-validate', 'jquery-ui-datepicker', 'jquery-validate-extra-methods'),
723
-            EVENT_ESPRESSO_VERSION,
724
-            true
725
-        );
726
-        wp_localize_script(
727
-            'ee_form_section_validation',
728
-            'ee_form_section_validation_init',
729
-            array('init' => $init_form_validation_automatically ? '1' : '0')
730
-        );
731
-    }
732
-
733
-
734
-    /**
735
-     * gets the variables used by form_section_validation.js.
736
-     * This needs to be called AFTER we've called $this->_enqueue_jquery_validate_script,
737
-     * but before the wordpress hook wp_loaded
738
-     *
739
-     * @throws EE_Error
740
-     */
741
-    public function _enqueue_and_localize_form_js()
742
-    {
743
-        $this->ensure_construct_finalized_called();
744
-        // actually, we don't want to localize just yet. There may be other forms on the page.
745
-        // so we need to add our form section data to a static variable accessible by all form sections
746
-        // and localize it just before the footer
747
-        $this->localize_validation_rules();
748
-        add_action('wp_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'), 2);
749
-        add_action('admin_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'));
750
-    }
751
-
752
-
753
-    /**
754
-     * add our form section data to a static variable accessible by all form sections
755
-     *
756
-     * @param bool $return_for_subsection
757
-     * @return void
758
-     * @throws EE_Error
759
-     */
760
-    public function localize_validation_rules($return_for_subsection = false)
761
-    {
762
-        // we only want to localize vars ONCE for the entire form,
763
-        // so if the form section doesn't have a parent, then it must be the top dog
764
-        if ($return_for_subsection || ! $this->parent_section()) {
765
-            EE_Form_Section_Proper::$_js_localization['form_data'][ $this->html_id() ] = array(
766
-                'form_section_id'  => $this->html_id(true),
767
-                'validation_rules' => $this->get_jquery_validation_rules(),
768
-                'other_data'       => $this->get_other_js_data(),
769
-                'errors'           => $this->subsection_validation_errors_by_html_name(),
770
-            );
771
-            EE_Form_Section_Proper::$_scripts_localized                                = true;
772
-        }
773
-    }
774
-
775
-
776
-    /**
777
-     * Gets an array of extra data that will be useful for client-side javascript.
778
-     * This is primarily data added by inputs and forms in addition to any
779
-     * scripts they might enqueue
780
-     *
781
-     * @param array $form_other_js_data
782
-     * @return array
783
-     * @throws EE_Error
784
-     */
785
-    public function get_other_js_data($form_other_js_data = array())
786
-    {
787
-        foreach ($this->subsections() as $subsection) {
788
-            $form_other_js_data = $subsection->get_other_js_data($form_other_js_data);
789
-        }
790
-        return $form_other_js_data;
791
-    }
792
-
793
-
794
-    /**
795
-     * Gets a flat array of inputs for this form section and its subsections.
796
-     * Keys are their form names, and values are the inputs themselves
797
-     *
798
-     * @return EE_Form_Input_Base
799
-     * @throws EE_Error
800
-     */
801
-    public function inputs_in_subsections()
802
-    {
803
-        $inputs = array();
804
-        foreach ($this->subsections() as $subsection) {
805
-            if ($subsection instanceof EE_Form_Input_Base) {
806
-                $inputs[ $subsection->html_name() ] = $subsection;
807
-            } elseif ($subsection instanceof EE_Form_Section_Proper) {
808
-                $inputs += $subsection->inputs_in_subsections();
809
-            }
810
-        }
811
-        return $inputs;
812
-    }
813
-
814
-
815
-    /**
816
-     * Gets a flat array of all the validation errors.
817
-     * Keys are html names (because those should be unique)
818
-     * and values are a string of all their validation errors
819
-     *
820
-     * @return string[]
821
-     * @throws EE_Error
822
-     */
823
-    public function subsection_validation_errors_by_html_name()
824
-    {
825
-        $inputs = $this->inputs();
826
-        $errors = array();
827
-        foreach ($inputs as $form_input) {
828
-            if ($form_input instanceof EE_Form_Input_Base && $form_input->get_validation_errors()) {
829
-                $errors[ $form_input->html_name() ] = $form_input->get_validation_error_string();
830
-            }
831
-        }
832
-        return $errors;
833
-    }
834
-
835
-
836
-    /**
837
-     * passes all the form data required by the JS to the JS, and enqueues the few required JS files.
838
-     * Should be setup by each form during the _enqueues_and_localize_form_js
839
-     *
840
-     * @throws InvalidArgumentException
841
-     * @throws InvalidInterfaceException
842
-     * @throws InvalidDataTypeException
843
-     */
844
-    public static function localize_script_for_all_forms()
845
-    {
846
-        // allow inputs and stuff to hook in their JS and stuff here
847
-        do_action('AHEE__EE_Form_Section_Proper__localize_script_for_all_forms__begin');
848
-        EE_Form_Section_Proper::$_js_localization['localized_error_messages'] = EE_Form_Section_Proper::_get_localized_error_messages();
849
-        $email_validation_level = isset(EE_Registry::instance()->CFG->registration->email_validation_level)
850
-            ? EE_Registry::instance()->CFG->registration->email_validation_level
851
-            : 'wp_default';
852
-        EE_Form_Section_Proper::$_js_localization['email_validation_level']   = $email_validation_level;
853
-        wp_enqueue_script('ee_form_section_validation');
854
-        wp_localize_script(
855
-            'ee_form_section_validation',
856
-            'ee_form_section_vars',
857
-            EE_Form_Section_Proper::$_js_localization
858
-        );
859
-    }
860
-
861
-
862
-    /**
863
-     * ensure_scripts_localized
864
-     *
865
-     * @throws EE_Error
866
-     */
867
-    public function ensure_scripts_localized()
868
-    {
869
-        if (! EE_Form_Section_Proper::$_scripts_localized) {
870
-            $this->_enqueue_and_localize_form_js();
871
-        }
872
-    }
873
-
874
-
875
-    /**
876
-     * Gets the hard-coded validation error messages to be used in the JS. The convention
877
-     * is that the key here should be the same as the custom validation rule put in the JS file
878
-     *
879
-     * @return array keys are custom validation rules, and values are internationalized strings
880
-     */
881
-    private static function _get_localized_error_messages()
882
-    {
883
-        return array(
884
-            'validUrl' => wp_strip_all_tags(__('This is not a valid absolute URL. Eg, http://domain.com/monkey.jpg', 'event_espresso')),
885
-            'regex'    => wp_strip_all_tags(__('Please check your input', 'event_espresso'))
886
-        );
887
-    }
888
-
889
-
890
-    /**
891
-     * @return array
892
-     */
893
-    public static function js_localization()
894
-    {
895
-        return self::$_js_localization;
896
-    }
897
-
898
-
899
-    /**
900
-     * @return void
901
-     */
902
-    public static function reset_js_localization()
903
-    {
904
-        self::$_js_localization = array();
905
-    }
906
-
907
-
908
-    /**
909
-     * Gets the JS to put inside the jquery validation rules for subsection of this form section.
910
-     * See parent function for more...
911
-     *
912
-     * @return array
913
-     * @throws EE_Error
914
-     */
915
-    public function get_jquery_validation_rules()
916
-    {
917
-        $jquery_validation_rules = array();
918
-        foreach ($this->get_validatable_subsections() as $subsection) {
919
-            $jquery_validation_rules = array_merge(
920
-                $jquery_validation_rules,
921
-                $subsection->get_jquery_validation_rules()
922
-            );
923
-        }
924
-        return $jquery_validation_rules;
925
-    }
926
-
927
-
928
-    /**
929
-     * Sanitizes all the data and sets the sanitized value of each field
930
-     *
931
-     * @param array $req_data
932
-     * @return void
933
-     * @throws EE_Error
934
-     */
935
-    protected function _normalize($req_data)
936
-    {
937
-        $this->_received_submission = true;
938
-        $this->_validation_errors   = array();
939
-        foreach ($this->get_validatable_subsections() as $subsection) {
940
-            try {
941
-                $subsection->_normalize($req_data);
942
-            } catch (EE_Validation_Error $e) {
943
-                $subsection->add_validation_error($e);
944
-            }
945
-        }
946
-    }
947
-
948
-
949
-    /**
950
-     * Performs validation on this form section and its subsections.
951
-     * For each subsection,
952
-     * calls _validate_{subsection_name} on THIS form (if the function exists)
953
-     * and passes it the subsection, then calls _validate on that subsection.
954
-     * If you need to perform validation on the form as a whole (considering multiple)
955
-     * you would be best to override this _validate method,
956
-     * calling parent::_validate() first.
957
-     *
958
-     * @throws EE_Error
959
-     */
960
-    protected function _validate()
961
-    {
962
-        // reset the cache of whether this form is valid or not- we're re-validating it now
963
-        $this->is_valid = null;
964
-        foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
965
-            if (method_exists($this, '_validate_' . $subsection_name)) {
966
-                call_user_func_array(array($this, '_validate_' . $subsection_name), array($subsection));
967
-            }
968
-            $subsection->_validate();
969
-        }
970
-    }
971
-
972
-
973
-    /**
974
-     * Gets all the validated inputs for the form section
975
-     *
976
-     * @return array
977
-     * @throws EE_Error
978
-     */
979
-    public function valid_data()
980
-    {
981
-        $inputs = array();
982
-        foreach ($this->subsections() as $subsection_name => $subsection) {
983
-            if ($subsection instanceof EE_Form_Section_Proper) {
984
-                $inputs[ $subsection_name ] = $subsection->valid_data();
985
-            } elseif ($subsection instanceof EE_Form_Input_Base) {
986
-                $inputs[ $subsection_name ] = $subsection->normalized_value();
987
-            }
988
-        }
989
-        return $inputs;
990
-    }
991
-
992
-
993
-    /**
994
-     * Gets all the inputs on this form section
995
-     *
996
-     * @return EE_Form_Input_Base[]
997
-     * @throws EE_Error
998
-     */
999
-    public function inputs()
1000
-    {
1001
-        $inputs = array();
1002
-        foreach ($this->subsections() as $subsection_name => $subsection) {
1003
-            if ($subsection instanceof EE_Form_Input_Base) {
1004
-                $inputs[ $subsection_name ] = $subsection;
1005
-            }
1006
-        }
1007
-        return $inputs;
1008
-    }
1009
-
1010
-
1011
-    /**
1012
-     * Gets all the subsections which are a proper form
1013
-     *
1014
-     * @return EE_Form_Section_Proper[]
1015
-     * @throws EE_Error
1016
-     */
1017
-    public function subforms()
1018
-    {
1019
-        $form_sections = array();
1020
-        foreach ($this->subsections() as $name => $obj) {
1021
-            if ($obj instanceof EE_Form_Section_Proper) {
1022
-                $form_sections[ $name ] = $obj;
1023
-            }
1024
-        }
1025
-        return $form_sections;
1026
-    }
1027
-
1028
-
1029
-    /**
1030
-     * Gets all the subsections (inputs, proper subsections, or html-only sections).
1031
-     * Consider using inputs() or subforms()
1032
-     * if you only want form inputs or proper form sections.
1033
-     *
1034
-     * @param boolean $require_construction_to_be_finalized most client code should
1035
-     *                                                      leave this as TRUE so that the inputs will be properly
1036
-     *                                                      configured. However, some client code may be ok with
1037
-     *                                                      construction finalize being called later
1038
-     *                                                      (realizing that the subsections' html names might not be
1039
-     *                                                      set yet, etc.)
1040
-     * @return EE_Form_Section_Proper[]
1041
-     * @throws EE_Error
1042
-     */
1043
-    public function subsections($require_construction_to_be_finalized = true)
1044
-    {
1045
-        if ($require_construction_to_be_finalized) {
1046
-            $this->ensure_construct_finalized_called();
1047
-        }
1048
-        return $this->_subsections;
1049
-    }
1050
-
1051
-
1052
-    /**
1053
-     * Returns whether this form has any subforms or inputs
1054
-     * @return bool
1055
-     */
1056
-    public function hasSubsections()
1057
-    {
1058
-        return ! empty($this->_subsections);
1059
-    }
1060
-
1061
-
1062
-    /**
1063
-     * Returns a simple array where keys are input names, and values are their normalized
1064
-     * values. (Similar to calling get_input_value on inputs)
1065
-     *
1066
-     * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1067
-     *                                        or just this forms' direct children inputs
1068
-     * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1069
-     *                                        or allow multidimensional array
1070
-     * @return array if $flatten is TRUE it will always be a 1-dimensional array
1071
-     *                                        with array keys being input names
1072
-     *                                        (regardless of whether they are from a subsection or not),
1073
-     *                                        and if $flatten is FALSE it can be a multidimensional array
1074
-     *                                        where keys are always subsection names and values are either
1075
-     *                                        the input's normalized value, or an array like the top-level array
1076
-     * @throws EE_Error
1077
-     */
1078
-    public function input_values($include_subform_inputs = false, $flatten = false)
1079
-    {
1080
-        return $this->_input_values(false, $include_subform_inputs, $flatten);
1081
-    }
1082
-
1083
-
1084
-    /**
1085
-     * Similar to EE_Form_Section_Proper::input_values(), except this returns the 'display_value'
1086
-     * of each input. On some inputs (especially radio boxes or checkboxes), the value stored
1087
-     * is not necessarily the value we want to display to users. This creates an array
1088
-     * where keys are the input names, and values are their display values
1089
-     *
1090
-     * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1091
-     *                                        or just this forms' direct children inputs
1092
-     * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1093
-     *                                        or allow multidimensional array
1094
-     * @return array if $flatten is TRUE it will always be a 1-dimensional array
1095
-     *                                        with array keys being input names
1096
-     *                                        (regardless of whether they are from a subsection or not),
1097
-     *                                        and if $flatten is FALSE it can be a multidimensional array
1098
-     *                                        where keys are always subsection names and values are either
1099
-     *                                        the input's normalized value, or an array like the top-level array
1100
-     * @throws EE_Error
1101
-     */
1102
-    public function input_pretty_values($include_subform_inputs = false, $flatten = false)
1103
-    {
1104
-        return $this->_input_values(true, $include_subform_inputs, $flatten);
1105
-    }
1106
-
1107
-
1108
-    /**
1109
-     * Gets the input values from the form
1110
-     *
1111
-     * @param boolean $pretty                 Whether to retrieve the pretty value,
1112
-     *                                        or just the normalized value
1113
-     * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1114
-     *                                        or just this forms' direct children inputs
1115
-     * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1116
-     *                                        or allow multidimensional array
1117
-     * @return array if $flatten is TRUE it will always be a 1-dimensional array with array keys being
1118
-     *                                        input names (regardless of whether they are from a subsection or not),
1119
-     *                                        and if $flatten is FALSE it can be a multidimensional array
1120
-     *                                        where keys are always subsection names and values are either
1121
-     *                                        the input's normalized value, or an array like the top-level array
1122
-     * @throws EE_Error
1123
-     */
1124
-    public function _input_values($pretty = false, $include_subform_inputs = false, $flatten = false)
1125
-    {
1126
-        $input_values = array();
1127
-        foreach ($this->subsections() as $subsection_name => $subsection) {
1128
-            if ($subsection instanceof EE_Form_Input_Base) {
1129
-                $input_values[ $subsection_name ] = $pretty
1130
-                    ? $subsection->pretty_value()
1131
-                    : $subsection->normalized_value();
1132
-            } elseif ($subsection instanceof EE_Form_Section_Proper && $include_subform_inputs) {
1133
-                $subform_input_values = $subsection->_input_values(
1134
-                    $pretty,
1135
-                    $include_subform_inputs,
1136
-                    $flatten
1137
-                );
1138
-                if ($flatten) {
1139
-                    $input_values = array_merge($input_values, $subform_input_values);
1140
-                } else {
1141
-                    $input_values[ $subsection_name ] = $subform_input_values;
1142
-                }
1143
-            }
1144
-        }
1145
-        return $input_values;
1146
-    }
1147
-
1148
-
1149
-    /**
1150
-     * Gets the originally submitted input values from the form
1151
-     *
1152
-     * @param boolean $include_subforms  Whether to include inputs from subforms,
1153
-     *                                   or just this forms' direct children inputs
1154
-     * @return array                     if $flatten is TRUE it will always be a 1-dimensional array
1155
-     *                                   with array keys being input names
1156
-     *                                   (regardless of whether they are from a subsection or not),
1157
-     *                                   and if $flatten is FALSE it can be a multidimensional array
1158
-     *                                   where keys are always subsection names and values are either
1159
-     *                                   the input's normalized value, or an array like the top-level array
1160
-     * @throws EE_Error
1161
-     */
1162
-    public function submitted_values($include_subforms = false)
1163
-    {
1164
-        $submitted_values = array();
1165
-        foreach ($this->subsections() as $subsection) {
1166
-            if ($subsection instanceof EE_Form_Input_Base) {
1167
-                // is this input part of an array of inputs?
1168
-                if (strpos($subsection->html_name(), '[') !== false) {
1169
-                    $full_input_name  = EEH_Array::convert_array_values_to_keys(
1170
-                        explode(
1171
-                            '[',
1172
-                            str_replace(']', '', $subsection->html_name())
1173
-                        ),
1174
-                        $subsection->raw_value()
1175
-                    );
1176
-                    $submitted_values = array_replace_recursive($submitted_values, $full_input_name);
1177
-                } else {
1178
-                    $submitted_values[ $subsection->html_name() ] = $subsection->raw_value();
1179
-                }
1180
-            } elseif ($subsection instanceof EE_Form_Section_Proper && $include_subforms) {
1181
-                $subform_input_values = $subsection->submitted_values($include_subforms);
1182
-                $submitted_values     = array_replace_recursive($submitted_values, $subform_input_values);
1183
-            }
1184
-        }
1185
-        return $submitted_values;
1186
-    }
1187
-
1188
-
1189
-    /**
1190
-     * Indicates whether or not this form has received a submission yet
1191
-     * (ie, had receive_form_submission called on it yet)
1192
-     *
1193
-     * @return boolean
1194
-     * @throws EE_Error
1195
-     */
1196
-    public function has_received_submission()
1197
-    {
1198
-        $this->ensure_construct_finalized_called();
1199
-        return $this->_received_submission;
1200
-    }
1201
-
1202
-
1203
-    /**
1204
-     * Equivalent to passing 'exclude' in the constructor's options array.
1205
-     * Removes the listed inputs from the form
1206
-     *
1207
-     * @param array $inputs_to_exclude values are the input names
1208
-     * @return void
1209
-     */
1210
-    public function exclude(array $inputs_to_exclude = array())
1211
-    {
1212
-        foreach ($inputs_to_exclude as $input_to_exclude_name) {
1213
-            unset($this->_subsections[ $input_to_exclude_name ]);
1214
-        }
1215
-    }
1216
-
1217
-
1218
-    /**
1219
-     * Changes these inputs' display strategy to be EE_Hidden_Display_Strategy.
1220
-     * @param array $inputs_to_hide
1221
-     * @throws EE_Error
1222
-     */
1223
-    public function hide(array $inputs_to_hide = array())
1224
-    {
1225
-        foreach ($inputs_to_hide as $input_to_hide) {
1226
-            $input = $this->get_input($input_to_hide);
1227
-            $input->set_display_strategy(new EE_Hidden_Display_Strategy());
1228
-        }
1229
-    }
1230
-
1231
-
1232
-    /**
1233
-     * add_subsections
1234
-     * Adds the listed subsections to the form section.
1235
-     * If $subsection_name_to_target is provided,
1236
-     * then new subsections are added before or after that subsection,
1237
-     * otherwise to the start or end of the entire subsections array.
1238
-     *
1239
-     * @param EE_Form_Section_Base[] $new_subsections           array of new form subsections
1240
-     *                                                          where keys are their names
1241
-     * @param string                 $subsection_name_to_target an existing for section that $new_subsections
1242
-     *                                                          should be added before or after
1243
-     *                                                          IF $subsection_name_to_target is null,
1244
-     *                                                          then $new_subsections will be added to
1245
-     *                                                          the beginning or end of the entire subsections array
1246
-     * @param boolean                $add_before                whether to add $new_subsections, before or after
1247
-     *                                                          $subsection_name_to_target,
1248
-     *                                                          or if $subsection_name_to_target is null,
1249
-     *                                                          before or after entire subsections array
1250
-     * @return void
1251
-     * @throws EE_Error
1252
-     */
1253
-    public function add_subsections($new_subsections, $subsection_name_to_target = null, $add_before = true)
1254
-    {
1255
-        foreach ($new_subsections as $subsection_name => $subsection) {
1256
-            if (! $subsection instanceof EE_Form_Section_Base) {
1257
-                EE_Error::add_error(
1258
-                    sprintf(
1259
-                        esc_html__(
1260
-                            "Trying to add a %s as a subsection (it was named '%s') to the form section '%s'. It was removed.",
1261
-                            'event_espresso'
1262
-                        ),
1263
-                        get_class($subsection),
1264
-                        $subsection_name,
1265
-                        $this->name()
1266
-                    )
1267
-                );
1268
-                unset($new_subsections[ $subsection_name ]);
1269
-            }
1270
-        }
1271
-        $this->_subsections = EEH_Array::insert_into_array(
1272
-            $this->_subsections,
1273
-            $new_subsections,
1274
-            $subsection_name_to_target,
1275
-            $add_before
1276
-        );
1277
-        if ($this->_construction_finalized) {
1278
-            foreach ($this->_subsections as $name => $subsection) {
1279
-                $subsection->_construct_finalize($this, $name);
1280
-            }
1281
-        }
1282
-    }
1283
-
1284
-
1285
-    /**
1286
-     * @param string $subsection_name
1287
-     * @param bool   $recursive
1288
-     * @return bool
1289
-     */
1290
-    public function has_subsection($subsection_name, $recursive = false)
1291
-    {
1292
-        foreach ($this->_subsections as $name => $subsection) {
1293
-            if (
1294
-                $name === $subsection_name
1295
-                || (
1296
-                    $recursive
1297
-                    && $subsection instanceof EE_Form_Section_Proper
1298
-                    && $subsection->has_subsection($subsection_name, $recursive)
1299
-                )
1300
-            ) {
1301
-                return true;
1302
-            }
1303
-        }
1304
-        return false;
1305
-    }
1306
-
1307
-
1308
-
1309
-    /**
1310
-     * Just gets all validatable subsections to clean their sensitive data
1311
-     *
1312
-     * @throws EE_Error
1313
-     */
1314
-    public function clean_sensitive_data()
1315
-    {
1316
-        foreach ($this->get_validatable_subsections() as $subsection) {
1317
-            $subsection->clean_sensitive_data();
1318
-        }
1319
-    }
1320
-
1321
-
1322
-    /**
1323
-     * Sets the submission error message (aka validation error message for this form section and all sub-sections)
1324
-     * @param string                           $form_submission_error_message
1325
-     * @param EE_Form_Section_Validatable $form_section unused
1326
-     * @throws EE_Error
1327
-     */
1328
-    public function set_submission_error_message(
1329
-        $form_submission_error_message = ''
1330
-    ) {
1331
-        $this->_form_submission_error_message = ! empty($form_submission_error_message)
1332
-            ? $form_submission_error_message
1333
-            : $this->getAllValidationErrorsString();
1334
-    }
1335
-
1336
-
1337
-    /**
1338
-     * Returns the cached error message. A default value is set for this during _validate(),
1339
-     * (called during receive_form_submission) but it can be explicitly set using
1340
-     * set_submission_error_message
1341
-     *
1342
-     * @return string
1343
-     */
1344
-    public function submission_error_message()
1345
-    {
1346
-        return $this->_form_submission_error_message;
1347
-    }
1348
-
1349
-
1350
-    /**
1351
-     * Sets a message to display if the data submitted to the form was valid.
1352
-     * @param string $form_submission_success_message
1353
-     */
1354
-    public function set_submission_success_message($form_submission_success_message = '')
1355
-    {
1356
-        $this->_form_submission_success_message = ! empty($form_submission_success_message)
1357
-            ? $form_submission_success_message
1358
-            : esc_html__('Form submitted successfully', 'event_espresso');
1359
-    }
1360
-
1361
-
1362
-    /**
1363
-     * Gets a message appropriate for display when the form is correctly submitted
1364
-     * @return string
1365
-     */
1366
-    public function submission_success_message()
1367
-    {
1368
-        return $this->_form_submission_success_message;
1369
-    }
1370
-
1371
-
1372
-    /**
1373
-     * Returns the prefix that should be used on child of this form section for
1374
-     * their html names. If this form section itself has a parent, prepends ITS
1375
-     * prefix onto this form section's prefix. Used primarily by
1376
-     * EE_Form_Input_Base::_set_default_html_name_if_empty
1377
-     *
1378
-     * @return string
1379
-     * @throws EE_Error
1380
-     */
1381
-    public function html_name_prefix()
1382
-    {
1383
-        if ($this->parent_section() instanceof EE_Form_Section_Proper) {
1384
-            return $this->parent_section()->html_name_prefix() . '[' . $this->name() . ']';
1385
-        }
1386
-        return $this->name();
1387
-    }
1388
-
1389
-
1390
-    /**
1391
-     * Gets the name, but first checks _construct_finalize has been called. If not,
1392
-     * calls it (assumes there is no parent and that we want the name to be whatever
1393
-     * was set, which is probably nothing, or the classname)
1394
-     *
1395
-     * @return string
1396
-     * @throws EE_Error
1397
-     */
1398
-    public function name()
1399
-    {
1400
-        $this->ensure_construct_finalized_called();
1401
-        return parent::name();
1402
-    }
1403
-
1404
-
1405
-    /**
1406
-     * @return EE_Form_Section_Proper
1407
-     * @throws EE_Error
1408
-     */
1409
-    public function parent_section()
1410
-    {
1411
-        $this->ensure_construct_finalized_called();
1412
-        return parent::parent_section();
1413
-    }
1414
-
1415
-
1416
-    /**
1417
-     * make sure construction finalized was called, otherwise children might not be ready
1418
-     *
1419
-     * @return void
1420
-     * @throws EE_Error
1421
-     */
1422
-    public function ensure_construct_finalized_called()
1423
-    {
1424
-        if (! $this->_construction_finalized) {
1425
-            $this->_construct_finalize($this->_parent_section, $this->_name);
1426
-        }
1427
-    }
1428
-
1429
-
1430
-    /**
1431
-     * Checks if any of this form section's inputs, or any of its children's inputs,
1432
-     * are in teh form data. If any are found, returns true. Else false
1433
-     *
1434
-     * @param array $req_data
1435
-     * @return boolean
1436
-     * @throws EE_Error
1437
-     */
1438
-    public function form_data_present_in($req_data = null)
1439
-    {
1440
-        $req_data = $this->getCachedRequest($req_data);
1441
-        foreach ($this->subsections() as $subsection) {
1442
-            if ($subsection instanceof EE_Form_Input_Base) {
1443
-                if ($subsection->form_data_present_in($req_data)) {
1444
-                    return true;
1445
-                }
1446
-            } elseif ($subsection instanceof EE_Form_Section_Proper) {
1447
-                if ($subsection->form_data_present_in($req_data)) {
1448
-                    return true;
1449
-                }
1450
-            }
1451
-        }
1452
-        return false;
1453
-    }
1454
-
1455
-
1456
-    /**
1457
-     * Gets validation errors for this form section and subsections
1458
-     * Similar to EE_Form_Section_Validatable::get_validation_errors() except this
1459
-     * gets the validation errors for ALL subsection
1460
-     *
1461
-     * @return EE_Validation_Error[]
1462
-     * @throws EE_Error
1463
-     */
1464
-    public function get_validation_errors_accumulated()
1465
-    {
1466
-        $validation_errors = $this->get_validation_errors();
1467
-        foreach ($this->get_validatable_subsections() as $subsection) {
1468
-            if ($subsection instanceof EE_Form_Section_Proper) {
1469
-                $validation_errors_on_this_subsection = $subsection->get_validation_errors_accumulated();
1470
-            } else {
1471
-                $validation_errors_on_this_subsection = $subsection->get_validation_errors();
1472
-            }
1473
-            if ($validation_errors_on_this_subsection) {
1474
-                $validation_errors = array_merge($validation_errors, $validation_errors_on_this_subsection);
1475
-            }
1476
-        }
1477
-        return $validation_errors;
1478
-    }
1479
-
1480
-    /**
1481
-     * Fetch validation errors from children and grandchildren and puts them in a single string.
1482
-     * This traverses the form section tree to generate this, but you probably want to instead use
1483
-     * get_form_submission_error_message() which is usually this message cached (or a custom validation error message)
1484
-     *
1485
-     * @return string
1486
-     * @since 4.9.59.p
1487
-     */
1488
-    protected function getAllValidationErrorsString()
1489
-    {
1490
-        $submission_error_messages = array();
1491
-        // bad, bad, bad registrant
1492
-        foreach ($this->get_validation_errors_accumulated() as $validation_error) {
1493
-            if ($validation_error instanceof EE_Validation_Error) {
1494
-                $form_section = $validation_error->get_form_section();
1495
-                if ($form_section instanceof EE_Form_Input_Base) {
1496
-                    $label = $validation_error->get_form_section()->html_label_text();
1497
-                } elseif ($form_section instanceof EE_Form_Section_Validatable) {
1498
-                    $label = $validation_error->get_form_section()->name();
1499
-                } else {
1500
-                    $label = esc_html__('Unknown', 'event_espresso');
1501
-                }
1502
-                $submission_error_messages[] = sprintf(
1503
-                    esc_html__('%s : %s', 'event_espresso'),
1504
-                    $label,
1505
-                    $validation_error->getMessage()
1506
-                );
1507
-            }
1508
-        }
1509
-        return implode('<br>', $submission_error_messages);
1510
-    }
1511
-
1512
-
1513
-    /**
1514
-     * This isn't just the name of an input, it's a path pointing to an input. The
1515
-     * path is similar to a folder path: slash (/) means to descend into a subsection,
1516
-     * dot-dot-slash (../) means to ascend into the parent section.
1517
-     * After a series of slashes and dot-dot-slashes, there should be the name of an input,
1518
-     * which will be returned.
1519
-     * Eg, if you want the related input to be conditional on a sibling input name 'foobar'
1520
-     * just use 'foobar'. If you want it to be conditional on an aunt/uncle input name
1521
-     * 'baz', use '../baz'. If you want it to be conditional on a cousin input,
1522
-     * the child of 'baz_section' named 'baz_child', use '../baz_section/baz_child'.
1523
-     * Etc
1524
-     *
1525
-     * @param string|false $form_section_path we accept false also because substr( '../', '../' ) = false
1526
-     * @return EE_Form_Section_Base
1527
-     * @throws EE_Error
1528
-     */
1529
-    public function find_section_from_path($form_section_path)
1530
-    {
1531
-        // check if we can find the input from purely going straight up the tree
1532
-        $input = parent::find_section_from_path($form_section_path);
1533
-        if ($input instanceof EE_Form_Section_Base) {
1534
-            return $input;
1535
-        }
1536
-        $next_slash_pos = strpos($form_section_path, '/');
1537
-        if ($next_slash_pos !== false) {
1538
-            $child_section_name = substr($form_section_path, 0, $next_slash_pos);
1539
-            $subpath            = substr($form_section_path, $next_slash_pos + 1);
1540
-        } else {
1541
-            $child_section_name = $form_section_path;
1542
-            $subpath            = '';
1543
-        }
1544
-        $child_section = $this->get_subsection($child_section_name);
1545
-        if ($child_section instanceof EE_Form_Section_Base) {
1546
-            return $child_section->find_section_from_path($subpath);
1547
-        }
1548
-        return null;
1549
-    }
360
+	/**
361
+	 * retrieves the originally submitted input values in the session
362
+	 * so that they can be used to repopulate the form if it failed validation
363
+	 *
364
+	 * @return array
365
+	 * @throws InvalidArgumentException
366
+	 * @throws InvalidInterfaceException
367
+	 * @throws InvalidDataTypeException
368
+	 */
369
+	protected function get_submitted_form_data_from_session()
370
+	{
371
+		$session = EE_Registry::instance()->SSN;
372
+		if ($session instanceof EE_Session) {
373
+			return $session->get_session_data(
374
+				EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY
375
+			);
376
+		}
377
+		return array();
378
+	}
379
+
380
+
381
+	/**
382
+	 * flushed the originally submitted input values from the session
383
+	 *
384
+	 * @return boolean whether or not the data was successfully removed from the session
385
+	 * @throws InvalidArgumentException
386
+	 * @throws InvalidInterfaceException
387
+	 * @throws InvalidDataTypeException
388
+	 */
389
+	public static function flush_submitted_form_data_from_session()
390
+	{
391
+		return EE_Registry::instance()->SSN->reset_data(
392
+			array(EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY)
393
+		);
394
+	}
395
+
396
+
397
+	/**
398
+	 * Populates this form and its subsections with data from the session.
399
+	 * (Wrapper for EE_Form_Section_Proper::receive_form_submission, so it shows
400
+	 * validation errors when displaying too)
401
+	 * Returns true if the form was populated from the session, false otherwise
402
+	 *
403
+	 * @return boolean
404
+	 * @throws InvalidArgumentException
405
+	 * @throws InvalidInterfaceException
406
+	 * @throws InvalidDataTypeException
407
+	 * @throws EE_Error
408
+	 */
409
+	public function populate_from_session()
410
+	{
411
+		$form_data_in_session = $this->get_submitted_form_data_from_session();
412
+		if (empty($form_data_in_session)) {
413
+			return false;
414
+		}
415
+		$this->receive_form_submission($form_data_in_session);
416
+		add_action('shutdown', array('EE_Form_Section_Proper', 'flush_submitted_form_data_from_session'));
417
+		if ($this->form_data_present_in($form_data_in_session)) {
418
+			return true;
419
+		}
420
+		return false;
421
+	}
422
+
423
+
424
+	/**
425
+	 * Populates the default data for the form, given an array where keys are
426
+	 * the input names, and values are their values (preferably normalized to be their
427
+	 * proper PHP types, not all strings... although that should be ok too).
428
+	 * Proper subsections are sub-arrays, the key being the subsection's name, and
429
+	 * the value being an array formatted in teh same way
430
+	 *
431
+	 * @param array $default_data
432
+	 * @throws EE_Error
433
+	 */
434
+	public function populate_defaults($default_data)
435
+	{
436
+		foreach ($this->subsections(false) as $subsection_name => $subsection) {
437
+			if (isset($default_data[ $subsection_name ])) {
438
+				if ($subsection instanceof EE_Form_Input_Base) {
439
+					$subsection->set_default($default_data[ $subsection_name ]);
440
+				} elseif ($subsection instanceof EE_Form_Section_Proper) {
441
+					$subsection->populate_defaults($default_data[ $subsection_name ]);
442
+				}
443
+			}
444
+		}
445
+	}
446
+
447
+
448
+	/**
449
+	 * returns true if subsection exists
450
+	 *
451
+	 * @param string $name
452
+	 * @return boolean
453
+	 */
454
+	public function subsection_exists($name)
455
+	{
456
+		return isset($this->_subsections[ $name ]) ? true : false;
457
+	}
458
+
459
+
460
+	/**
461
+	 * Gets the subsection specified by its name
462
+	 *
463
+	 * @param string  $name
464
+	 * @param boolean $require_construction_to_be_finalized most client code should leave this as TRUE
465
+	 *                                                      so that the inputs will be properly configured.
466
+	 *                                                      However, some client code may be ok
467
+	 *                                                      with construction finalize being called later
468
+	 *                                                      (realizing that the subsections' html names
469
+	 *                                                      might not be set yet, etc.)
470
+	 * @return EE_Form_Section_Base
471
+	 * @throws EE_Error
472
+	 */
473
+	public function get_subsection($name, $require_construction_to_be_finalized = true)
474
+	{
475
+		if ($require_construction_to_be_finalized) {
476
+			$this->ensure_construct_finalized_called();
477
+		}
478
+		return $this->subsection_exists($name) ? $this->_subsections[ $name ] : null;
479
+	}
480
+
481
+
482
+	/**
483
+	 * Gets all the validatable subsections of this form section
484
+	 *
485
+	 * @return EE_Form_Section_Validatable[]
486
+	 * @throws EE_Error
487
+	 */
488
+	public function get_validatable_subsections()
489
+	{
490
+		$validatable_subsections = array();
491
+		foreach ($this->subsections() as $name => $obj) {
492
+			if ($obj instanceof EE_Form_Section_Validatable) {
493
+				$validatable_subsections[ $name ] = $obj;
494
+			}
495
+		}
496
+		return $validatable_subsections;
497
+	}
498
+
499
+
500
+	/**
501
+	 * Gets an input by the given name. If not found, or if its not an EE_FOrm_Input_Base child,
502
+	 * throw an EE_Error.
503
+	 *
504
+	 * @param string  $name
505
+	 * @param boolean $require_construction_to_be_finalized most client code should
506
+	 *                                                      leave this as TRUE so that the inputs will be properly
507
+	 *                                                      configured. However, some client code may be ok with
508
+	 *                                                      construction finalize being called later
509
+	 *                                                      (realizing that the subsections' html names might not be
510
+	 *                                                      set yet, etc.)
511
+	 * @return EE_Form_Input_Base
512
+	 * @throws EE_Error
513
+	 */
514
+	public function get_input($name, $require_construction_to_be_finalized = true)
515
+	{
516
+		$subsection = $this->get_subsection(
517
+			$name,
518
+			$require_construction_to_be_finalized
519
+		);
520
+		if (! $subsection instanceof EE_Form_Input_Base) {
521
+			throw new EE_Error(
522
+				sprintf(
523
+					esc_html__(
524
+						"Subsection '%s' is not an instanceof EE_Form_Input_Base on form '%s'. It is a '%s'",
525
+						'event_espresso'
526
+					),
527
+					$name,
528
+					get_class($this),
529
+					$subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
530
+				)
531
+			);
532
+		}
533
+		return $subsection;
534
+	}
535
+
536
+
537
+	/**
538
+	 * Like get_input(), gets the proper subsection of the form given the name,
539
+	 * otherwise throws an EE_Error
540
+	 *
541
+	 * @param string  $name
542
+	 * @param boolean $require_construction_to_be_finalized most client code should
543
+	 *                                                      leave this as TRUE so that the inputs will be properly
544
+	 *                                                      configured. However, some client code may be ok with
545
+	 *                                                      construction finalize being called later
546
+	 *                                                      (realizing that the subsections' html names might not be
547
+	 *                                                      set yet, etc.)
548
+	 * @return EE_Form_Section_Proper
549
+	 * @throws EE_Error
550
+	 */
551
+	public function get_proper_subsection($name, $require_construction_to_be_finalized = true)
552
+	{
553
+		$subsection = $this->get_subsection(
554
+			$name,
555
+			$require_construction_to_be_finalized
556
+		);
557
+		if (! $subsection instanceof EE_Form_Section_Proper) {
558
+			throw new EE_Error(
559
+				sprintf(
560
+					esc_html__(
561
+						"Subsection '%'s is not an instanceof EE_Form_Section_Proper on form '%s'",
562
+						'event_espresso'
563
+					),
564
+					$name,
565
+					get_class($this)
566
+				)
567
+			);
568
+		}
569
+		return $subsection;
570
+	}
571
+
572
+
573
+	/**
574
+	 * Gets the value of the specified input. Should be called after receive_form_submission()
575
+	 * or populate_defaults() on the form, where the normalized value on the input is set.
576
+	 *
577
+	 * @param string $name
578
+	 * @return mixed depending on the input's type and its normalization strategy
579
+	 * @throws EE_Error
580
+	 */
581
+	public function get_input_value($name)
582
+	{
583
+		$input = $this->get_input($name);
584
+		return $input->normalized_value();
585
+	}
586
+
587
+
588
+	/**
589
+	 * Checks if this form section itself is valid, and then checks its subsections
590
+	 *
591
+	 * @throws EE_Error
592
+	 * @return boolean
593
+	 */
594
+	public function is_valid()
595
+	{
596
+		if ($this->is_valid === null) {
597
+			if (! $this->has_received_submission()) {
598
+				throw new EE_Error(
599
+					sprintf(
600
+						esc_html__(
601
+							'You cannot check if a form is valid before receiving the form submission using receive_form_submission',
602
+							'event_espresso'
603
+						)
604
+					)
605
+				);
606
+			}
607
+			if (! parent::is_valid()) {
608
+				$this->is_valid = false;
609
+			} else {
610
+				// ok so no general errors to this entire form section.
611
+				// so let's check the subsections, but only set errors if that hasn't been done yet
612
+				$this->is_valid = true;
613
+				foreach ($this->get_validatable_subsections() as $subsection) {
614
+					if (! $subsection->is_valid()) {
615
+						$this->is_valid = false;
616
+					}
617
+				}
618
+			}
619
+		}
620
+		return $this->is_valid;
621
+	}
622
+
623
+
624
+	/**
625
+	 * gets the default name of this form section if none is specified
626
+	 *
627
+	 * @return void
628
+	 */
629
+	protected function _set_default_name_if_empty()
630
+	{
631
+		if (! $this->_name) {
632
+			$classname    = get_class($this);
633
+			$default_name = str_replace('EE_', '', $classname);
634
+			$this->_name  = $default_name;
635
+		}
636
+	}
637
+
638
+
639
+	/**
640
+	 * Returns the HTML for the form, except for the form opening and closing tags
641
+	 * (as the form section doesn't know where you necessarily want to send the information to),
642
+	 * and except for a submit button. Enqueues JS and CSS; if called early enough we will
643
+	 * try to enqueue them in the header, otherwise they'll be enqueued in the footer.
644
+	 * Not doing_it_wrong because theoretically this CAN be used properly,
645
+	 * provided its used during "wp_enqueue_scripts", or it doesn't need to enqueue
646
+	 * any CSS.
647
+	 *
648
+	 * @throws InvalidArgumentException
649
+	 * @throws InvalidInterfaceException
650
+	 * @throws InvalidDataTypeException
651
+	 * @throws EE_Error
652
+	 */
653
+	public function get_html_and_js()
654
+	{
655
+		$this->enqueue_js();
656
+		return $this->get_html();
657
+	}
658
+
659
+
660
+	/**
661
+	 * returns HTML for displaying this form section. recursively calls display_section() on all subsections
662
+	 *
663
+	 * @param bool $display_previously_submitted_data
664
+	 * @return string
665
+	 * @throws InvalidArgumentException
666
+	 * @throws InvalidInterfaceException
667
+	 * @throws InvalidDataTypeException
668
+	 * @throws EE_Error
669
+	 * @throws EE_Error
670
+	 * @throws EE_Error
671
+	 */
672
+	public function get_html($display_previously_submitted_data = true)
673
+	{
674
+		$this->ensure_construct_finalized_called();
675
+		if ($display_previously_submitted_data) {
676
+			$this->populate_from_session();
677
+		}
678
+		return $this->_form_html_filter
679
+			? $this->_form_html_filter->filterHtml($this->_layout_strategy->layout_form(), $this)
680
+			: $this->_layout_strategy->layout_form();
681
+	}
682
+
683
+
684
+	/**
685
+	 * enqueues JS and CSS for the form.
686
+	 * It is preferred to call this before wp_enqueue_scripts so the
687
+	 * scripts and styles can be put in the header, but if called later
688
+	 * they will be put in the footer (which is OK for JS, but in HTML4 CSS should
689
+	 * only be in the header; but in HTML5 its ok in the body.
690
+	 * See http://stackoverflow.com/questions/4957446/load-external-css-file-in-body-tag.
691
+	 * So if your form enqueues CSS, it's preferred to call this before wp_enqueue_scripts.)
692
+	 *
693
+	 * @return void
694
+	 * @throws EE_Error
695
+	 */
696
+	public function enqueue_js()
697
+	{
698
+		$this->_enqueue_and_localize_form_js();
699
+		foreach ($this->subsections() as $subsection) {
700
+			$subsection->enqueue_js();
701
+		}
702
+	}
703
+
704
+
705
+	/**
706
+	 * adds a filter so that jquery validate gets enqueued in EE_System::wp_enqueue_scripts().
707
+	 * This must be done BEFORE wp_enqueue_scripts() gets called, which is on
708
+	 * the wp_enqueue_scripts hook.
709
+	 * However, registering the form js and localizing it can happen when we
710
+	 * actually output the form (which is preferred, seeing how teh form's fields
711
+	 * could change until it's actually outputted)
712
+	 *
713
+	 * @param boolean $init_form_validation_automatically whether or not we want the form validation
714
+	 *                                                    to be triggered automatically or not
715
+	 * @return void
716
+	 */
717
+	public static function wp_enqueue_scripts($init_form_validation_automatically = true)
718
+	{
719
+		wp_register_script(
720
+			'ee_form_section_validation',
721
+			EE_GLOBAL_ASSETS_URL . 'scripts' . '/form_section_validation.js',
722
+			array('jquery-validate', 'jquery-ui-datepicker', 'jquery-validate-extra-methods'),
723
+			EVENT_ESPRESSO_VERSION,
724
+			true
725
+		);
726
+		wp_localize_script(
727
+			'ee_form_section_validation',
728
+			'ee_form_section_validation_init',
729
+			array('init' => $init_form_validation_automatically ? '1' : '0')
730
+		);
731
+	}
732
+
733
+
734
+	/**
735
+	 * gets the variables used by form_section_validation.js.
736
+	 * This needs to be called AFTER we've called $this->_enqueue_jquery_validate_script,
737
+	 * but before the wordpress hook wp_loaded
738
+	 *
739
+	 * @throws EE_Error
740
+	 */
741
+	public function _enqueue_and_localize_form_js()
742
+	{
743
+		$this->ensure_construct_finalized_called();
744
+		// actually, we don't want to localize just yet. There may be other forms on the page.
745
+		// so we need to add our form section data to a static variable accessible by all form sections
746
+		// and localize it just before the footer
747
+		$this->localize_validation_rules();
748
+		add_action('wp_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'), 2);
749
+		add_action('admin_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'));
750
+	}
751
+
752
+
753
+	/**
754
+	 * add our form section data to a static variable accessible by all form sections
755
+	 *
756
+	 * @param bool $return_for_subsection
757
+	 * @return void
758
+	 * @throws EE_Error
759
+	 */
760
+	public function localize_validation_rules($return_for_subsection = false)
761
+	{
762
+		// we only want to localize vars ONCE for the entire form,
763
+		// so if the form section doesn't have a parent, then it must be the top dog
764
+		if ($return_for_subsection || ! $this->parent_section()) {
765
+			EE_Form_Section_Proper::$_js_localization['form_data'][ $this->html_id() ] = array(
766
+				'form_section_id'  => $this->html_id(true),
767
+				'validation_rules' => $this->get_jquery_validation_rules(),
768
+				'other_data'       => $this->get_other_js_data(),
769
+				'errors'           => $this->subsection_validation_errors_by_html_name(),
770
+			);
771
+			EE_Form_Section_Proper::$_scripts_localized                                = true;
772
+		}
773
+	}
774
+
775
+
776
+	/**
777
+	 * Gets an array of extra data that will be useful for client-side javascript.
778
+	 * This is primarily data added by inputs and forms in addition to any
779
+	 * scripts they might enqueue
780
+	 *
781
+	 * @param array $form_other_js_data
782
+	 * @return array
783
+	 * @throws EE_Error
784
+	 */
785
+	public function get_other_js_data($form_other_js_data = array())
786
+	{
787
+		foreach ($this->subsections() as $subsection) {
788
+			$form_other_js_data = $subsection->get_other_js_data($form_other_js_data);
789
+		}
790
+		return $form_other_js_data;
791
+	}
792
+
793
+
794
+	/**
795
+	 * Gets a flat array of inputs for this form section and its subsections.
796
+	 * Keys are their form names, and values are the inputs themselves
797
+	 *
798
+	 * @return EE_Form_Input_Base
799
+	 * @throws EE_Error
800
+	 */
801
+	public function inputs_in_subsections()
802
+	{
803
+		$inputs = array();
804
+		foreach ($this->subsections() as $subsection) {
805
+			if ($subsection instanceof EE_Form_Input_Base) {
806
+				$inputs[ $subsection->html_name() ] = $subsection;
807
+			} elseif ($subsection instanceof EE_Form_Section_Proper) {
808
+				$inputs += $subsection->inputs_in_subsections();
809
+			}
810
+		}
811
+		return $inputs;
812
+	}
813
+
814
+
815
+	/**
816
+	 * Gets a flat array of all the validation errors.
817
+	 * Keys are html names (because those should be unique)
818
+	 * and values are a string of all their validation errors
819
+	 *
820
+	 * @return string[]
821
+	 * @throws EE_Error
822
+	 */
823
+	public function subsection_validation_errors_by_html_name()
824
+	{
825
+		$inputs = $this->inputs();
826
+		$errors = array();
827
+		foreach ($inputs as $form_input) {
828
+			if ($form_input instanceof EE_Form_Input_Base && $form_input->get_validation_errors()) {
829
+				$errors[ $form_input->html_name() ] = $form_input->get_validation_error_string();
830
+			}
831
+		}
832
+		return $errors;
833
+	}
834
+
835
+
836
+	/**
837
+	 * passes all the form data required by the JS to the JS, and enqueues the few required JS files.
838
+	 * Should be setup by each form during the _enqueues_and_localize_form_js
839
+	 *
840
+	 * @throws InvalidArgumentException
841
+	 * @throws InvalidInterfaceException
842
+	 * @throws InvalidDataTypeException
843
+	 */
844
+	public static function localize_script_for_all_forms()
845
+	{
846
+		// allow inputs and stuff to hook in their JS and stuff here
847
+		do_action('AHEE__EE_Form_Section_Proper__localize_script_for_all_forms__begin');
848
+		EE_Form_Section_Proper::$_js_localization['localized_error_messages'] = EE_Form_Section_Proper::_get_localized_error_messages();
849
+		$email_validation_level = isset(EE_Registry::instance()->CFG->registration->email_validation_level)
850
+			? EE_Registry::instance()->CFG->registration->email_validation_level
851
+			: 'wp_default';
852
+		EE_Form_Section_Proper::$_js_localization['email_validation_level']   = $email_validation_level;
853
+		wp_enqueue_script('ee_form_section_validation');
854
+		wp_localize_script(
855
+			'ee_form_section_validation',
856
+			'ee_form_section_vars',
857
+			EE_Form_Section_Proper::$_js_localization
858
+		);
859
+	}
860
+
861
+
862
+	/**
863
+	 * ensure_scripts_localized
864
+	 *
865
+	 * @throws EE_Error
866
+	 */
867
+	public function ensure_scripts_localized()
868
+	{
869
+		if (! EE_Form_Section_Proper::$_scripts_localized) {
870
+			$this->_enqueue_and_localize_form_js();
871
+		}
872
+	}
873
+
874
+
875
+	/**
876
+	 * Gets the hard-coded validation error messages to be used in the JS. The convention
877
+	 * is that the key here should be the same as the custom validation rule put in the JS file
878
+	 *
879
+	 * @return array keys are custom validation rules, and values are internationalized strings
880
+	 */
881
+	private static function _get_localized_error_messages()
882
+	{
883
+		return array(
884
+			'validUrl' => wp_strip_all_tags(__('This is not a valid absolute URL. Eg, http://domain.com/monkey.jpg', 'event_espresso')),
885
+			'regex'    => wp_strip_all_tags(__('Please check your input', 'event_espresso'))
886
+		);
887
+	}
888
+
889
+
890
+	/**
891
+	 * @return array
892
+	 */
893
+	public static function js_localization()
894
+	{
895
+		return self::$_js_localization;
896
+	}
897
+
898
+
899
+	/**
900
+	 * @return void
901
+	 */
902
+	public static function reset_js_localization()
903
+	{
904
+		self::$_js_localization = array();
905
+	}
906
+
907
+
908
+	/**
909
+	 * Gets the JS to put inside the jquery validation rules for subsection of this form section.
910
+	 * See parent function for more...
911
+	 *
912
+	 * @return array
913
+	 * @throws EE_Error
914
+	 */
915
+	public function get_jquery_validation_rules()
916
+	{
917
+		$jquery_validation_rules = array();
918
+		foreach ($this->get_validatable_subsections() as $subsection) {
919
+			$jquery_validation_rules = array_merge(
920
+				$jquery_validation_rules,
921
+				$subsection->get_jquery_validation_rules()
922
+			);
923
+		}
924
+		return $jquery_validation_rules;
925
+	}
926
+
927
+
928
+	/**
929
+	 * Sanitizes all the data and sets the sanitized value of each field
930
+	 *
931
+	 * @param array $req_data
932
+	 * @return void
933
+	 * @throws EE_Error
934
+	 */
935
+	protected function _normalize($req_data)
936
+	{
937
+		$this->_received_submission = true;
938
+		$this->_validation_errors   = array();
939
+		foreach ($this->get_validatable_subsections() as $subsection) {
940
+			try {
941
+				$subsection->_normalize($req_data);
942
+			} catch (EE_Validation_Error $e) {
943
+				$subsection->add_validation_error($e);
944
+			}
945
+		}
946
+	}
947
+
948
+
949
+	/**
950
+	 * Performs validation on this form section and its subsections.
951
+	 * For each subsection,
952
+	 * calls _validate_{subsection_name} on THIS form (if the function exists)
953
+	 * and passes it the subsection, then calls _validate on that subsection.
954
+	 * If you need to perform validation on the form as a whole (considering multiple)
955
+	 * you would be best to override this _validate method,
956
+	 * calling parent::_validate() first.
957
+	 *
958
+	 * @throws EE_Error
959
+	 */
960
+	protected function _validate()
961
+	{
962
+		// reset the cache of whether this form is valid or not- we're re-validating it now
963
+		$this->is_valid = null;
964
+		foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
965
+			if (method_exists($this, '_validate_' . $subsection_name)) {
966
+				call_user_func_array(array($this, '_validate_' . $subsection_name), array($subsection));
967
+			}
968
+			$subsection->_validate();
969
+		}
970
+	}
971
+
972
+
973
+	/**
974
+	 * Gets all the validated inputs for the form section
975
+	 *
976
+	 * @return array
977
+	 * @throws EE_Error
978
+	 */
979
+	public function valid_data()
980
+	{
981
+		$inputs = array();
982
+		foreach ($this->subsections() as $subsection_name => $subsection) {
983
+			if ($subsection instanceof EE_Form_Section_Proper) {
984
+				$inputs[ $subsection_name ] = $subsection->valid_data();
985
+			} elseif ($subsection instanceof EE_Form_Input_Base) {
986
+				$inputs[ $subsection_name ] = $subsection->normalized_value();
987
+			}
988
+		}
989
+		return $inputs;
990
+	}
991
+
992
+
993
+	/**
994
+	 * Gets all the inputs on this form section
995
+	 *
996
+	 * @return EE_Form_Input_Base[]
997
+	 * @throws EE_Error
998
+	 */
999
+	public function inputs()
1000
+	{
1001
+		$inputs = array();
1002
+		foreach ($this->subsections() as $subsection_name => $subsection) {
1003
+			if ($subsection instanceof EE_Form_Input_Base) {
1004
+				$inputs[ $subsection_name ] = $subsection;
1005
+			}
1006
+		}
1007
+		return $inputs;
1008
+	}
1009
+
1010
+
1011
+	/**
1012
+	 * Gets all the subsections which are a proper form
1013
+	 *
1014
+	 * @return EE_Form_Section_Proper[]
1015
+	 * @throws EE_Error
1016
+	 */
1017
+	public function subforms()
1018
+	{
1019
+		$form_sections = array();
1020
+		foreach ($this->subsections() as $name => $obj) {
1021
+			if ($obj instanceof EE_Form_Section_Proper) {
1022
+				$form_sections[ $name ] = $obj;
1023
+			}
1024
+		}
1025
+		return $form_sections;
1026
+	}
1027
+
1028
+
1029
+	/**
1030
+	 * Gets all the subsections (inputs, proper subsections, or html-only sections).
1031
+	 * Consider using inputs() or subforms()
1032
+	 * if you only want form inputs or proper form sections.
1033
+	 *
1034
+	 * @param boolean $require_construction_to_be_finalized most client code should
1035
+	 *                                                      leave this as TRUE so that the inputs will be properly
1036
+	 *                                                      configured. However, some client code may be ok with
1037
+	 *                                                      construction finalize being called later
1038
+	 *                                                      (realizing that the subsections' html names might not be
1039
+	 *                                                      set yet, etc.)
1040
+	 * @return EE_Form_Section_Proper[]
1041
+	 * @throws EE_Error
1042
+	 */
1043
+	public function subsections($require_construction_to_be_finalized = true)
1044
+	{
1045
+		if ($require_construction_to_be_finalized) {
1046
+			$this->ensure_construct_finalized_called();
1047
+		}
1048
+		return $this->_subsections;
1049
+	}
1050
+
1051
+
1052
+	/**
1053
+	 * Returns whether this form has any subforms or inputs
1054
+	 * @return bool
1055
+	 */
1056
+	public function hasSubsections()
1057
+	{
1058
+		return ! empty($this->_subsections);
1059
+	}
1060
+
1061
+
1062
+	/**
1063
+	 * Returns a simple array where keys are input names, and values are their normalized
1064
+	 * values. (Similar to calling get_input_value on inputs)
1065
+	 *
1066
+	 * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1067
+	 *                                        or just this forms' direct children inputs
1068
+	 * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1069
+	 *                                        or allow multidimensional array
1070
+	 * @return array if $flatten is TRUE it will always be a 1-dimensional array
1071
+	 *                                        with array keys being input names
1072
+	 *                                        (regardless of whether they are from a subsection or not),
1073
+	 *                                        and if $flatten is FALSE it can be a multidimensional array
1074
+	 *                                        where keys are always subsection names and values are either
1075
+	 *                                        the input's normalized value, or an array like the top-level array
1076
+	 * @throws EE_Error
1077
+	 */
1078
+	public function input_values($include_subform_inputs = false, $flatten = false)
1079
+	{
1080
+		return $this->_input_values(false, $include_subform_inputs, $flatten);
1081
+	}
1082
+
1083
+
1084
+	/**
1085
+	 * Similar to EE_Form_Section_Proper::input_values(), except this returns the 'display_value'
1086
+	 * of each input. On some inputs (especially radio boxes or checkboxes), the value stored
1087
+	 * is not necessarily the value we want to display to users. This creates an array
1088
+	 * where keys are the input names, and values are their display values
1089
+	 *
1090
+	 * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1091
+	 *                                        or just this forms' direct children inputs
1092
+	 * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1093
+	 *                                        or allow multidimensional array
1094
+	 * @return array if $flatten is TRUE it will always be a 1-dimensional array
1095
+	 *                                        with array keys being input names
1096
+	 *                                        (regardless of whether they are from a subsection or not),
1097
+	 *                                        and if $flatten is FALSE it can be a multidimensional array
1098
+	 *                                        where keys are always subsection names and values are either
1099
+	 *                                        the input's normalized value, or an array like the top-level array
1100
+	 * @throws EE_Error
1101
+	 */
1102
+	public function input_pretty_values($include_subform_inputs = false, $flatten = false)
1103
+	{
1104
+		return $this->_input_values(true, $include_subform_inputs, $flatten);
1105
+	}
1106
+
1107
+
1108
+	/**
1109
+	 * Gets the input values from the form
1110
+	 *
1111
+	 * @param boolean $pretty                 Whether to retrieve the pretty value,
1112
+	 *                                        or just the normalized value
1113
+	 * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1114
+	 *                                        or just this forms' direct children inputs
1115
+	 * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1116
+	 *                                        or allow multidimensional array
1117
+	 * @return array if $flatten is TRUE it will always be a 1-dimensional array with array keys being
1118
+	 *                                        input names (regardless of whether they are from a subsection or not),
1119
+	 *                                        and if $flatten is FALSE it can be a multidimensional array
1120
+	 *                                        where keys are always subsection names and values are either
1121
+	 *                                        the input's normalized value, or an array like the top-level array
1122
+	 * @throws EE_Error
1123
+	 */
1124
+	public function _input_values($pretty = false, $include_subform_inputs = false, $flatten = false)
1125
+	{
1126
+		$input_values = array();
1127
+		foreach ($this->subsections() as $subsection_name => $subsection) {
1128
+			if ($subsection instanceof EE_Form_Input_Base) {
1129
+				$input_values[ $subsection_name ] = $pretty
1130
+					? $subsection->pretty_value()
1131
+					: $subsection->normalized_value();
1132
+			} elseif ($subsection instanceof EE_Form_Section_Proper && $include_subform_inputs) {
1133
+				$subform_input_values = $subsection->_input_values(
1134
+					$pretty,
1135
+					$include_subform_inputs,
1136
+					$flatten
1137
+				);
1138
+				if ($flatten) {
1139
+					$input_values = array_merge($input_values, $subform_input_values);
1140
+				} else {
1141
+					$input_values[ $subsection_name ] = $subform_input_values;
1142
+				}
1143
+			}
1144
+		}
1145
+		return $input_values;
1146
+	}
1147
+
1148
+
1149
+	/**
1150
+	 * Gets the originally submitted input values from the form
1151
+	 *
1152
+	 * @param boolean $include_subforms  Whether to include inputs from subforms,
1153
+	 *                                   or just this forms' direct children inputs
1154
+	 * @return array                     if $flatten is TRUE it will always be a 1-dimensional array
1155
+	 *                                   with array keys being input names
1156
+	 *                                   (regardless of whether they are from a subsection or not),
1157
+	 *                                   and if $flatten is FALSE it can be a multidimensional array
1158
+	 *                                   where keys are always subsection names and values are either
1159
+	 *                                   the input's normalized value, or an array like the top-level array
1160
+	 * @throws EE_Error
1161
+	 */
1162
+	public function submitted_values($include_subforms = false)
1163
+	{
1164
+		$submitted_values = array();
1165
+		foreach ($this->subsections() as $subsection) {
1166
+			if ($subsection instanceof EE_Form_Input_Base) {
1167
+				// is this input part of an array of inputs?
1168
+				if (strpos($subsection->html_name(), '[') !== false) {
1169
+					$full_input_name  = EEH_Array::convert_array_values_to_keys(
1170
+						explode(
1171
+							'[',
1172
+							str_replace(']', '', $subsection->html_name())
1173
+						),
1174
+						$subsection->raw_value()
1175
+					);
1176
+					$submitted_values = array_replace_recursive($submitted_values, $full_input_name);
1177
+				} else {
1178
+					$submitted_values[ $subsection->html_name() ] = $subsection->raw_value();
1179
+				}
1180
+			} elseif ($subsection instanceof EE_Form_Section_Proper && $include_subforms) {
1181
+				$subform_input_values = $subsection->submitted_values($include_subforms);
1182
+				$submitted_values     = array_replace_recursive($submitted_values, $subform_input_values);
1183
+			}
1184
+		}
1185
+		return $submitted_values;
1186
+	}
1187
+
1188
+
1189
+	/**
1190
+	 * Indicates whether or not this form has received a submission yet
1191
+	 * (ie, had receive_form_submission called on it yet)
1192
+	 *
1193
+	 * @return boolean
1194
+	 * @throws EE_Error
1195
+	 */
1196
+	public function has_received_submission()
1197
+	{
1198
+		$this->ensure_construct_finalized_called();
1199
+		return $this->_received_submission;
1200
+	}
1201
+
1202
+
1203
+	/**
1204
+	 * Equivalent to passing 'exclude' in the constructor's options array.
1205
+	 * Removes the listed inputs from the form
1206
+	 *
1207
+	 * @param array $inputs_to_exclude values are the input names
1208
+	 * @return void
1209
+	 */
1210
+	public function exclude(array $inputs_to_exclude = array())
1211
+	{
1212
+		foreach ($inputs_to_exclude as $input_to_exclude_name) {
1213
+			unset($this->_subsections[ $input_to_exclude_name ]);
1214
+		}
1215
+	}
1216
+
1217
+
1218
+	/**
1219
+	 * Changes these inputs' display strategy to be EE_Hidden_Display_Strategy.
1220
+	 * @param array $inputs_to_hide
1221
+	 * @throws EE_Error
1222
+	 */
1223
+	public function hide(array $inputs_to_hide = array())
1224
+	{
1225
+		foreach ($inputs_to_hide as $input_to_hide) {
1226
+			$input = $this->get_input($input_to_hide);
1227
+			$input->set_display_strategy(new EE_Hidden_Display_Strategy());
1228
+		}
1229
+	}
1230
+
1231
+
1232
+	/**
1233
+	 * add_subsections
1234
+	 * Adds the listed subsections to the form section.
1235
+	 * If $subsection_name_to_target is provided,
1236
+	 * then new subsections are added before or after that subsection,
1237
+	 * otherwise to the start or end of the entire subsections array.
1238
+	 *
1239
+	 * @param EE_Form_Section_Base[] $new_subsections           array of new form subsections
1240
+	 *                                                          where keys are their names
1241
+	 * @param string                 $subsection_name_to_target an existing for section that $new_subsections
1242
+	 *                                                          should be added before or after
1243
+	 *                                                          IF $subsection_name_to_target is null,
1244
+	 *                                                          then $new_subsections will be added to
1245
+	 *                                                          the beginning or end of the entire subsections array
1246
+	 * @param boolean                $add_before                whether to add $new_subsections, before or after
1247
+	 *                                                          $subsection_name_to_target,
1248
+	 *                                                          or if $subsection_name_to_target is null,
1249
+	 *                                                          before or after entire subsections array
1250
+	 * @return void
1251
+	 * @throws EE_Error
1252
+	 */
1253
+	public function add_subsections($new_subsections, $subsection_name_to_target = null, $add_before = true)
1254
+	{
1255
+		foreach ($new_subsections as $subsection_name => $subsection) {
1256
+			if (! $subsection instanceof EE_Form_Section_Base) {
1257
+				EE_Error::add_error(
1258
+					sprintf(
1259
+						esc_html__(
1260
+							"Trying to add a %s as a subsection (it was named '%s') to the form section '%s'. It was removed.",
1261
+							'event_espresso'
1262
+						),
1263
+						get_class($subsection),
1264
+						$subsection_name,
1265
+						$this->name()
1266
+					)
1267
+				);
1268
+				unset($new_subsections[ $subsection_name ]);
1269
+			}
1270
+		}
1271
+		$this->_subsections = EEH_Array::insert_into_array(
1272
+			$this->_subsections,
1273
+			$new_subsections,
1274
+			$subsection_name_to_target,
1275
+			$add_before
1276
+		);
1277
+		if ($this->_construction_finalized) {
1278
+			foreach ($this->_subsections as $name => $subsection) {
1279
+				$subsection->_construct_finalize($this, $name);
1280
+			}
1281
+		}
1282
+	}
1283
+
1284
+
1285
+	/**
1286
+	 * @param string $subsection_name
1287
+	 * @param bool   $recursive
1288
+	 * @return bool
1289
+	 */
1290
+	public function has_subsection($subsection_name, $recursive = false)
1291
+	{
1292
+		foreach ($this->_subsections as $name => $subsection) {
1293
+			if (
1294
+				$name === $subsection_name
1295
+				|| (
1296
+					$recursive
1297
+					&& $subsection instanceof EE_Form_Section_Proper
1298
+					&& $subsection->has_subsection($subsection_name, $recursive)
1299
+				)
1300
+			) {
1301
+				return true;
1302
+			}
1303
+		}
1304
+		return false;
1305
+	}
1306
+
1307
+
1308
+
1309
+	/**
1310
+	 * Just gets all validatable subsections to clean their sensitive data
1311
+	 *
1312
+	 * @throws EE_Error
1313
+	 */
1314
+	public function clean_sensitive_data()
1315
+	{
1316
+		foreach ($this->get_validatable_subsections() as $subsection) {
1317
+			$subsection->clean_sensitive_data();
1318
+		}
1319
+	}
1320
+
1321
+
1322
+	/**
1323
+	 * Sets the submission error message (aka validation error message for this form section and all sub-sections)
1324
+	 * @param string                           $form_submission_error_message
1325
+	 * @param EE_Form_Section_Validatable $form_section unused
1326
+	 * @throws EE_Error
1327
+	 */
1328
+	public function set_submission_error_message(
1329
+		$form_submission_error_message = ''
1330
+	) {
1331
+		$this->_form_submission_error_message = ! empty($form_submission_error_message)
1332
+			? $form_submission_error_message
1333
+			: $this->getAllValidationErrorsString();
1334
+	}
1335
+
1336
+
1337
+	/**
1338
+	 * Returns the cached error message. A default value is set for this during _validate(),
1339
+	 * (called during receive_form_submission) but it can be explicitly set using
1340
+	 * set_submission_error_message
1341
+	 *
1342
+	 * @return string
1343
+	 */
1344
+	public function submission_error_message()
1345
+	{
1346
+		return $this->_form_submission_error_message;
1347
+	}
1348
+
1349
+
1350
+	/**
1351
+	 * Sets a message to display if the data submitted to the form was valid.
1352
+	 * @param string $form_submission_success_message
1353
+	 */
1354
+	public function set_submission_success_message($form_submission_success_message = '')
1355
+	{
1356
+		$this->_form_submission_success_message = ! empty($form_submission_success_message)
1357
+			? $form_submission_success_message
1358
+			: esc_html__('Form submitted successfully', 'event_espresso');
1359
+	}
1360
+
1361
+
1362
+	/**
1363
+	 * Gets a message appropriate for display when the form is correctly submitted
1364
+	 * @return string
1365
+	 */
1366
+	public function submission_success_message()
1367
+	{
1368
+		return $this->_form_submission_success_message;
1369
+	}
1370
+
1371
+
1372
+	/**
1373
+	 * Returns the prefix that should be used on child of this form section for
1374
+	 * their html names. If this form section itself has a parent, prepends ITS
1375
+	 * prefix onto this form section's prefix. Used primarily by
1376
+	 * EE_Form_Input_Base::_set_default_html_name_if_empty
1377
+	 *
1378
+	 * @return string
1379
+	 * @throws EE_Error
1380
+	 */
1381
+	public function html_name_prefix()
1382
+	{
1383
+		if ($this->parent_section() instanceof EE_Form_Section_Proper) {
1384
+			return $this->parent_section()->html_name_prefix() . '[' . $this->name() . ']';
1385
+		}
1386
+		return $this->name();
1387
+	}
1388
+
1389
+
1390
+	/**
1391
+	 * Gets the name, but first checks _construct_finalize has been called. If not,
1392
+	 * calls it (assumes there is no parent and that we want the name to be whatever
1393
+	 * was set, which is probably nothing, or the classname)
1394
+	 *
1395
+	 * @return string
1396
+	 * @throws EE_Error
1397
+	 */
1398
+	public function name()
1399
+	{
1400
+		$this->ensure_construct_finalized_called();
1401
+		return parent::name();
1402
+	}
1403
+
1404
+
1405
+	/**
1406
+	 * @return EE_Form_Section_Proper
1407
+	 * @throws EE_Error
1408
+	 */
1409
+	public function parent_section()
1410
+	{
1411
+		$this->ensure_construct_finalized_called();
1412
+		return parent::parent_section();
1413
+	}
1414
+
1415
+
1416
+	/**
1417
+	 * make sure construction finalized was called, otherwise children might not be ready
1418
+	 *
1419
+	 * @return void
1420
+	 * @throws EE_Error
1421
+	 */
1422
+	public function ensure_construct_finalized_called()
1423
+	{
1424
+		if (! $this->_construction_finalized) {
1425
+			$this->_construct_finalize($this->_parent_section, $this->_name);
1426
+		}
1427
+	}
1428
+
1429
+
1430
+	/**
1431
+	 * Checks if any of this form section's inputs, or any of its children's inputs,
1432
+	 * are in teh form data. If any are found, returns true. Else false
1433
+	 *
1434
+	 * @param array $req_data
1435
+	 * @return boolean
1436
+	 * @throws EE_Error
1437
+	 */
1438
+	public function form_data_present_in($req_data = null)
1439
+	{
1440
+		$req_data = $this->getCachedRequest($req_data);
1441
+		foreach ($this->subsections() as $subsection) {
1442
+			if ($subsection instanceof EE_Form_Input_Base) {
1443
+				if ($subsection->form_data_present_in($req_data)) {
1444
+					return true;
1445
+				}
1446
+			} elseif ($subsection instanceof EE_Form_Section_Proper) {
1447
+				if ($subsection->form_data_present_in($req_data)) {
1448
+					return true;
1449
+				}
1450
+			}
1451
+		}
1452
+		return false;
1453
+	}
1454
+
1455
+
1456
+	/**
1457
+	 * Gets validation errors for this form section and subsections
1458
+	 * Similar to EE_Form_Section_Validatable::get_validation_errors() except this
1459
+	 * gets the validation errors for ALL subsection
1460
+	 *
1461
+	 * @return EE_Validation_Error[]
1462
+	 * @throws EE_Error
1463
+	 */
1464
+	public function get_validation_errors_accumulated()
1465
+	{
1466
+		$validation_errors = $this->get_validation_errors();
1467
+		foreach ($this->get_validatable_subsections() as $subsection) {
1468
+			if ($subsection instanceof EE_Form_Section_Proper) {
1469
+				$validation_errors_on_this_subsection = $subsection->get_validation_errors_accumulated();
1470
+			} else {
1471
+				$validation_errors_on_this_subsection = $subsection->get_validation_errors();
1472
+			}
1473
+			if ($validation_errors_on_this_subsection) {
1474
+				$validation_errors = array_merge($validation_errors, $validation_errors_on_this_subsection);
1475
+			}
1476
+		}
1477
+		return $validation_errors;
1478
+	}
1479
+
1480
+	/**
1481
+	 * Fetch validation errors from children and grandchildren and puts them in a single string.
1482
+	 * This traverses the form section tree to generate this, but you probably want to instead use
1483
+	 * get_form_submission_error_message() which is usually this message cached (or a custom validation error message)
1484
+	 *
1485
+	 * @return string
1486
+	 * @since 4.9.59.p
1487
+	 */
1488
+	protected function getAllValidationErrorsString()
1489
+	{
1490
+		$submission_error_messages = array();
1491
+		// bad, bad, bad registrant
1492
+		foreach ($this->get_validation_errors_accumulated() as $validation_error) {
1493
+			if ($validation_error instanceof EE_Validation_Error) {
1494
+				$form_section = $validation_error->get_form_section();
1495
+				if ($form_section instanceof EE_Form_Input_Base) {
1496
+					$label = $validation_error->get_form_section()->html_label_text();
1497
+				} elseif ($form_section instanceof EE_Form_Section_Validatable) {
1498
+					$label = $validation_error->get_form_section()->name();
1499
+				} else {
1500
+					$label = esc_html__('Unknown', 'event_espresso');
1501
+				}
1502
+				$submission_error_messages[] = sprintf(
1503
+					esc_html__('%s : %s', 'event_espresso'),
1504
+					$label,
1505
+					$validation_error->getMessage()
1506
+				);
1507
+			}
1508
+		}
1509
+		return implode('<br>', $submission_error_messages);
1510
+	}
1511
+
1512
+
1513
+	/**
1514
+	 * This isn't just the name of an input, it's a path pointing to an input. The
1515
+	 * path is similar to a folder path: slash (/) means to descend into a subsection,
1516
+	 * dot-dot-slash (../) means to ascend into the parent section.
1517
+	 * After a series of slashes and dot-dot-slashes, there should be the name of an input,
1518
+	 * which will be returned.
1519
+	 * Eg, if you want the related input to be conditional on a sibling input name 'foobar'
1520
+	 * just use 'foobar'. If you want it to be conditional on an aunt/uncle input name
1521
+	 * 'baz', use '../baz'. If you want it to be conditional on a cousin input,
1522
+	 * the child of 'baz_section' named 'baz_child', use '../baz_section/baz_child'.
1523
+	 * Etc
1524
+	 *
1525
+	 * @param string|false $form_section_path we accept false also because substr( '../', '../' ) = false
1526
+	 * @return EE_Form_Section_Base
1527
+	 * @throws EE_Error
1528
+	 */
1529
+	public function find_section_from_path($form_section_path)
1530
+	{
1531
+		// check if we can find the input from purely going straight up the tree
1532
+		$input = parent::find_section_from_path($form_section_path);
1533
+		if ($input instanceof EE_Form_Section_Base) {
1534
+			return $input;
1535
+		}
1536
+		$next_slash_pos = strpos($form_section_path, '/');
1537
+		if ($next_slash_pos !== false) {
1538
+			$child_section_name = substr($form_section_path, 0, $next_slash_pos);
1539
+			$subpath            = substr($form_section_path, $next_slash_pos + 1);
1540
+		} else {
1541
+			$child_section_name = $form_section_path;
1542
+			$subpath            = '';
1543
+		}
1544
+		$child_section = $this->get_subsection($child_section_name);
1545
+		if ($child_section instanceof EE_Form_Section_Base) {
1546
+			return $child_section->find_section_from_path($subpath);
1547
+		}
1548
+		return null;
1549
+	}
1550 1550
 }
Please login to merge, or discard this patch.
core/libraries/batch/BatchRequestProcessor.php 1 patch
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -117,7 +117,7 @@  discard block
 block discarded – undo
117 117
 			$job_response = $job_handler->createJob($this->_job_parameters);
118 118
 			$this->validateResponse('createJob', $this->_job_id, $job_response);
119 119
 			$success = $this->_job_parameters->save();
120
-			if (! $success) {
120
+			if ( ! $success) {
121 121
 				throw new BatchRequestException(
122 122
 					sprintf(
123 123
 						esc_html__(
@@ -215,7 +215,7 @@  discard block
 block discarded – undo
215 215
 		array $request_data = []
216 216
 	): JobHandlerInterface {
217 217
 
218
-		if (! class_exists($classname)) {
218
+		if ( ! class_exists($classname)) {
219 219
 			throw new BatchRequestException(
220 220
 				sprintf(
221 221
 					esc_html__(
@@ -227,7 +227,7 @@  discard block
 block discarded – undo
227 227
 			);
228 228
 		}
229 229
 		$job_handler = $this->loader->getNew($classname);
230
-		if (! $job_handler instanceof JobHandlerInterface) {
230
+		if ( ! $job_handler instanceof JobHandlerInterface) {
231 231
 			throw new BatchRequestException(
232 232
 				sprintf(
233 233
 					esc_html__(
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
 	 */
256 256
 	protected function _get_error_response(Exception $exception, string $method_name): JobStepResponse
257 257
 	{
258
-		if (! $this->_job_parameters instanceof JobParameters) {
258
+		if ( ! $this->_job_parameters instanceof JobParameters) {
259 259
 			$this->_job_parameters = new JobParameters($this->_job_id, esc_html__('__Unknown__', 'event_espresso'), []);
260 260
 		}
261 261
 		$this->_job_parameters->set_status(JobParameters::status_error);
@@ -267,9 +267,9 @@  discard block
 block discarded – undo
267 267
 			),
268 268
 			'<h4>',
269 269
 			get_class($exception),
270
-			'<code>' . 'BatchRunner::' . $method_name . '()</code>',
271
-			'</h4><p>' . $exception->getMessage() . '</p>',
272
-			'<pre>' . $exception->getTraceAsString() . '</pre>'
270
+			'<code>'.'BatchRunner::'.$method_name.'()</code>',
271
+			'</h4><p>'.$exception->getMessage().'</p>',
272
+			'<pre>'.$exception->getTraceAsString().'</pre>'
273 273
 		);
274 274
 		return new JobStepResponse(
275 275
 			$this->_job_parameters,
@@ -287,7 +287,7 @@  discard block
 block discarded – undo
287 287
 	 */
288 288
 	private function validateResponse(string $function, string $job_id, $job_response)
289 289
 	{
290
-		if (! $job_response instanceof JobStepResponse) {
290
+		if ( ! $job_response instanceof JobStepResponse) {
291 291
 			throw new BatchRequestException(
292 292
 				sprintf(
293 293
 					esc_html__(
Please login to merge, or discard this patch.
core/libraries/batch/Helpers/JobParameters.php 1 patch
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -191,7 +191,7 @@  discard block
 block discarded – undo
191 191
 	 */
192 192
 	public static function load(string $job_id): JobParameters
193 193
 	{
194
-		$job_record         = new JobParametersWordPressOption(JobParameters::wp_option_prefix . $job_id);
194
+		$job_record         = new JobParametersWordPressOption(JobParameters::wp_option_prefix.$job_id);
195 195
 		$job_parameter_vars = $job_record->loadOption();
196 196
 		if (
197 197
 			! is_array($job_parameter_vars)
@@ -205,7 +205,7 @@  discard block
 block discarded – undo
205 205
 						'event_espresso'
206 206
 					),
207 207
 					$job_id,
208
-					JobParameters::wp_option_prefix . $job_id
208
+					JobParameters::wp_option_prefix.$job_id
209 209
 				)
210 210
 			);
211 211
 		}
@@ -265,8 +265,8 @@  discard block
 block discarded – undo
265 265
 	 */
266 266
 	public function request_datum(string $key, $default = '')
267 267
 	{
268
-		if (isset($this->_request_data[ $key ])) {
269
-			return $this->_request_data[ $key ];
268
+		if (isset($this->_request_data[$key])) {
269
+			return $this->_request_data[$key];
270 270
 		}
271 271
 		return $default;
272 272
 	}
@@ -281,8 +281,8 @@  discard block
 block discarded – undo
281 281
 	 */
282 282
 	public function extra_datum(string $key, $default = '')
283 283
 	{
284
-		if (isset($this->_extra_data[ $key ])) {
285
-			return $this->_extra_data[ $key ];
284
+		if (isset($this->_extra_data[$key])) {
285
+			return $this->_extra_data[$key];
286 286
 		}
287 287
 		return $default;
288 288
 	}
@@ -296,7 +296,7 @@  discard block
 block discarded – undo
296 296
 	 */
297 297
 	public function add_extra_data(string $key, $value)
298 298
 	{
299
-		$this->_extra_data[ $key ] = $value;
299
+		$this->_extra_data[$key] = $value;
300 300
 	}
301 301
 
302 302
 
@@ -419,7 +419,7 @@  discard block
 block discarded – undo
419 419
 	 */
420 420
 	public function option_name(): string
421 421
 	{
422
-		return JobParameters::wp_option_prefix . $this->job_id();
422
+		return JobParameters::wp_option_prefix.$this->job_id();
423 423
 	}
424 424
 
425 425
 
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/DatetimeOffsetFix.php 2 patches
Indentation   +467 added lines, -467 removed lines patch added patch discarded remove patch
@@ -23,471 +23,471 @@
 block discarded – undo
23 23
 
24 24
 class DatetimeOffsetFix extends JobHandler
25 25
 {
26
-    /**
27
-     * Key for the option used to track which models have been processed when doing the batches.
28
-     */
29
-    const MODELS_TO_PROCESS_OPTION_KEY = 'ee_models_processed_for_datetime_offset_fix';
30
-
31
-
32
-    const COUNT_OF_MODELS_PROCESSED = 'ee_count_of_ee_models_processed_for_datetime_offset_fixed';
33
-
34
-    /**
35
-     * Key for the option used to track what the current offset is that will be applied when this tool is executed.
36
-     */
37
-    const OFFSET_TO_APPLY_OPTION_KEY = 'ee_datetime_offset_fix_offset_to_apply';
38
-
39
-
40
-    const OPTION_KEY_OFFSET_RANGE_START_DATE = 'ee_datetime_offset_start_date_range';
41
-
42
-
43
-    const OPTION_KEY_OFFSET_RANGE_END_DATE = 'ee_datetime_offset_end_date_range';
44
-
45
-
46
-    /**
47
-     * String labelling the datetime offset fix type for change-log entries.
48
-     */
49
-    const DATETIME_OFFSET_FIX_CHANGELOG_TYPE = 'datetime_offset_fix';
50
-
51
-
52
-    /**
53
-     * String labelling a datetime offset fix error for change-log entries.
54
-     */
55
-    const DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE = 'datetime_offset_fix_error';
56
-
57
-    /**
58
-     * @var EEM_Base[]
59
-     */
60
-    protected $models_with_datetime_fields = array();
61
-
62
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
63
-
64
-    /**
65
-     * Performs any necessary setup for starting the job. This is also a good
66
-     * place to setup the $job_arguments which will be used for subsequent HTTP requests
67
-     * when continue_job will be called
68
-     *
69
-     * @param JobParameters $job_parameters
70
-     * @return JobStepResponse
71
-     * @throws EE_Error
72
-     * @throws InvalidArgumentException
73
-     * @throws InvalidDataTypeException
74
-     * @throws InvalidInterfaceException
75
-     */
76
-    public function create_job(JobParameters $job_parameters)
77
-    {
78
-        $models_with_datetime_fields = $this->getModelsWithDatetimeFields();
79
-        // we'll be doing each model as a batch.
80
-        $job_parameters->set_job_size(count($models_with_datetime_fields));
81
-        return new JobStepResponse(
82
-            $job_parameters,
83
-            esc_html__('Starting Datetime Offset Fix', 'event_espresso')
84
-        );
85
-    }
86
-
87
-    /**
88
-     * Performs another step of the job
89
-     *
90
-     * @param JobParameters $job_parameters
91
-     * @param int           $batch_size
92
-     * @return JobStepResponse
93
-     * @throws EE_Error
94
-     * @throws InvalidArgumentException
95
-     * @throws InvalidDataTypeException
96
-     * @throws InvalidInterfaceException
97
-     */
98
-    public function continue_job(JobParameters $job_parameters, $batch_size = 50)
99
-    {
100
-        $models_to_process = $this->getModelsWithDatetimeFields();
101
-        // let's pop off the a model and do the query to apply the offset.
102
-        $model_to_process = array_pop($models_to_process);
103
-        // update our record
104
-        $this->setModelsToProcess($models_to_process);
105
-        $this->processModel($model_to_process);
106
-        $this->updateCountOfModelsProcessed();
107
-        $job_parameters->set_units_processed($this->getCountOfModelsProcessed());
108
-        if (count($models_to_process) > 0) {
109
-            $job_parameters->set_status(JobParameters::status_continue);
110
-        } else {
111
-            $job_parameters->set_status(JobParameters::status_complete);
112
-        }
113
-        return new JobStepResponse(
114
-            $job_parameters,
115
-            sprintf(
116
-                esc_html__('Updated the offset for all datetime fields on the %s model.', 'event_espresso'),
117
-                $model_to_process
118
-            )
119
-        );
120
-    }
121
-
122
-    /**
123
-     * Performs any clean-up logic when we know the job is completed
124
-     *
125
-     * @param JobParameters $job_parameters
126
-     * @return JobStepResponse
127
-     * @throws BatchRequestException
128
-     */
129
-    public function cleanup_job(JobParameters $job_parameters)
130
-    {
131
-        // delete important saved options.
132
-        delete_option(self::MODELS_TO_PROCESS_OPTION_KEY);
133
-        delete_option(self::COUNT_OF_MODELS_PROCESSED);
134
-        delete_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE);
135
-        delete_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE);
136
-        return new JobStepResponse($job_parameters, esc_html__(
137
-            'Offset has been applied to all affected fields.',
138
-            'event_espresso'
139
-        ));
140
-    }
141
-
142
-
143
-    /**
144
-     * Contains the logic for processing a model and applying the datetime offset to affected fields on that model.
145
-     *
146
-     * @param string $model_class_name
147
-     * @throws EE_Error
148
-     */
149
-    protected function processModel($model_class_name)
150
-    {
151
-        global $wpdb;
152
-        /** @var EEM_Base $model */
153
-        $model = $model_class_name::instance();
154
-        $original_offset = self::getOffset();
155
-        $start_date_range = self::getStartDateRange();
156
-        $end_date_range = self::getEndDateRange();
157
-        $sql_date_function = $original_offset > 0 ? 'DATE_ADD' : 'DATE_SUB';
158
-        $offset = abs($original_offset) * 60;
159
-        $date_ranges = array();
160
-        // since some affected models might have two tables, we have to get our tables and set up a query for each table.
161
-        foreach ($model->get_tables() as $table) {
162
-            $query = 'UPDATE ' . $table->get_table_name();
163
-            $fields_affected = array();
164
-            $inner_query = array();
165
-            foreach ($model->_get_fields_for_table($table->get_table_alias()) as $model_field) {
166
-                if ($model_field instanceof EE_Datetime_Field) {
167
-                    $inner_query[ $model_field->get_table_column() ] = $model_field->get_table_column() . ' = '
168
-                                                                       . $sql_date_function . '('
169
-                                                                       . $model_field->get_table_column()
170
-                                                                       . ", INTERVAL {$offset} MINUTE)";
171
-                    $fields_affected[] = $model_field;
172
-                }
173
-            }
174
-            if (! $fields_affected) {
175
-                continue;
176
-            }
177
-            // do we do one query per column/field or one query for all fields on the model? It all depends on whether
178
-            // there is a date range applied or not.
179
-            if ($start_date_range instanceof DbSafeDateTime || $end_date_range instanceof DbSafeDateTime) {
180
-                $result = $this->doQueryForEachField($query, $inner_query, $start_date_range, $end_date_range);
181
-            } else {
182
-                $result = $this->doQueryForAllFields($query, $inner_query);
183
-            }
184
-
185
-            // record appropriate logs for the query
186
-            switch (true) {
187
-                case $result === false:
188
-                    // record error.
189
-                    $error_message = $wpdb->last_error;
190
-                    // handle the edge cases where last_error might be empty.
191
-                    if (! $error_message) {
192
-                        $error_message = esc_html__('Unknown mysql error occurred.', 'event_espresso');
193
-                    }
194
-                    $this->recordChangeLog($model, $original_offset, $table, $fields_affected, $error_message);
195
-                    break;
196
-                case is_array($result) && ! empty($result):
197
-                    foreach ($result as $field_name => $error_message) {
198
-                        $this->recordChangeLog($model, $original_offset, $table, array($field_name), $error_message);
199
-                    }
200
-                    break;
201
-                default:
202
-                    $this->recordChangeLog($model, $original_offset, $table, $fields_affected);
203
-            }
204
-        }
205
-    }
206
-
207
-
208
-    /**
209
-     * Does the query on each $inner_query individually.
210
-     *
211
-     * @param string              $query
212
-     * @param array               $inner_query
213
-     * @param DbSafeDateTime|null $start_date_range
214
-     * @param DbSafeDateTime|null $end_date_range
215
-     * @return array  An array of any errors encountered and the fields they were for.
216
-     */
217
-    private function doQueryForEachField($query, array $inner_query, $start_date_range, $end_date_range)
218
-    {
219
-        global $wpdb;
220
-        $errors = array();
221
-        foreach ($inner_query as $field_name => $field_query) {
222
-            $query_to_run = $query;
223
-            $where_conditions = array();
224
-            $query_to_run .= ' SET ' . $field_query;
225
-            if ($start_date_range instanceof DbSafeDateTime) {
226
-                $start_date = $start_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
227
-                $where_conditions[] = "{$field_name} > '{$start_date}'";
228
-            }
229
-            if ($end_date_range instanceof DbSafeDateTime) {
230
-                $end_date = $end_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
231
-                $where_conditions[] = "{$field_name} < '{$end_date}'";
232
-            }
233
-            if ($where_conditions) {
234
-                $query_to_run .= ' WHERE ' . implode(' AND ', $where_conditions);
235
-            }
236
-            $result = $wpdb->query($query_to_run);
237
-            if ($result === false) {
238
-                // record error.
239
-                $error_message = $wpdb->last_error;
240
-                // handle the edgecases where last_error might be empty.
241
-                if (! $error_message) {
242
-                    $error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
243
-                }
244
-                $errors[ $field_name ] = $error_message;
245
-            }
246
-        }
247
-        return $errors;
248
-    }
249
-
250
-
251
-    /**
252
-     * Performs the query for all fields within the inner_query
253
-     *
254
-     * @param string $query
255
-     * @param array  $inner_query
256
-     * @return false|int
257
-     */
258
-    private function doQueryForAllFields($query, array $inner_query)
259
-    {
260
-        global $wpdb;
261
-        $query .= ' SET ' . implode(',', $inner_query);
262
-        return $wpdb->query($query);
263
-    }
264
-
265
-
266
-    /**
267
-     * Records a changelog entry using the given information.
268
-     *
269
-     * @param EEM_Base              $model
270
-     * @param float                 $offset
271
-     * @param EE_Table_Base         $table
272
-     * @param EE_Model_Field_Base[] $model_fields_affected
273
-     * @param string                $error_message If present then there was an error so let's record that instead.
274
-     * @throws EE_Error
275
-     */
276
-    private function recordChangeLog(
277
-        EEM_Base $model,
278
-        $offset,
279
-        EE_Table_Base $table,
280
-        $model_fields_affected,
281
-        $error_message = ''
282
-    ) {
283
-        // setup $fields list.
284
-        $fields = array();
285
-        /** @var EE_Datetime_Field $model_field */
286
-        foreach ($model_fields_affected as $model_field) {
287
-            if (! $model_field instanceof EE_Datetime_Field) {
288
-                continue;
289
-            }
290
-            $fields[] = $model_field->get_name();
291
-        }
292
-        // setup the message for the changelog entry.
293
-        $message = $error_message
294
-            ? sprintf(
295
-                esc_html__(
296
-                    'The %1$s table for the %2$s model did not have the offset of %3$f applied to its fields (%4$s), because of the following error:%5$s',
297
-                    'event_espresso'
298
-                ),
299
-                $table->get_table_name(),
300
-                $model->get_this_model_name(),
301
-                $offset,
302
-                implode(',', $fields),
303
-                $error_message
304
-            )
305
-            : sprintf(
306
-                esc_html__(
307
-                    'The %1$s table for the %2$s model has had the offset of %3$f applied to its following fields: %4$s',
308
-                    'event_espresso'
309
-                ),
310
-                $table->get_table_name(),
311
-                $model->get_this_model_name(),
312
-                $offset,
313
-                implode(',', $fields)
314
-            );
315
-        // write to the log
316
-        $changelog = EE_Change_Log::new_instance(array(
317
-            'LOG_type'    => $error_message
318
-                ? self::DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE
319
-                : self::DATETIME_OFFSET_FIX_CHANGELOG_TYPE,
320
-            'LOG_message' => $message,
321
-        ));
322
-        $changelog->save();
323
-    }
324
-
325
-
326
-    /**
327
-     * Returns an array of models that have datetime fields.
328
-     * This array is added to a short lived transient cache to keep having to build this list to a minimum.
329
-     *
330
-     * @return array an array of model class names.
331
-     * @throws EE_Error
332
-     * @throws InvalidDataTypeException
333
-     * @throws InvalidInterfaceException
334
-     * @throws InvalidArgumentException
335
-     */
336
-    private function getModelsWithDatetimeFields()
337
-    {
338
-        $this->getModelsToProcess();
339
-        if (! empty($this->models_with_datetime_fields)) {
340
-            return $this->models_with_datetime_fields;
341
-        }
342
-
343
-        $all_non_abstract_models = EE_Registry::instance()->non_abstract_db_models;
344
-        foreach ($all_non_abstract_models as $non_abstract_model) {
345
-            // get model instance
346
-            /** @var EEM_Base $non_abstract_model */
347
-            $non_abstract_model = $non_abstract_model::instance();
348
-            if ($non_abstract_model->get_a_field_of_type('EE_Datetime_Field') instanceof EE_Datetime_Field) {
349
-                $this->models_with_datetime_fields[] = get_class($non_abstract_model);
350
-            }
351
-        }
352
-        $this->setModelsToProcess($this->models_with_datetime_fields);
353
-        return $this->models_with_datetime_fields;
354
-    }
355
-
356
-
357
-    /**
358
-     * This simply records the models that have been processed with our tracking option.
359
-     *
360
-     * @param array $models_to_set array of model class names.
361
-     */
362
-    private function setModelsToProcess($models_to_set)
363
-    {
364
-        update_option(self::MODELS_TO_PROCESS_OPTION_KEY, $models_to_set);
365
-    }
366
-
367
-
368
-    /**
369
-     * Used to keep track of how many models have been processed for the batch
370
-     *
371
-     * @param $count
372
-     */
373
-    private function updateCountOfModelsProcessed($count = 1)
374
-    {
375
-        $count = $this->getCountOfModelsProcessed() + (int) $count;
376
-        update_option(self::COUNT_OF_MODELS_PROCESSED, $count);
377
-    }
378
-
379
-
380
-    /**
381
-     * Retrieve the tracked number of models processed between requests.
382
-     *
383
-     * @return int
384
-     */
385
-    private function getCountOfModelsProcessed()
386
-    {
387
-        return (int) get_option(self::COUNT_OF_MODELS_PROCESSED, 0);
388
-    }
389
-
390
-
391
-    /**
392
-     * Returns the models that are left to process.
393
-     *
394
-     * @return array  an array of model class names.
395
-     */
396
-    private function getModelsToProcess()
397
-    {
398
-        if (empty($this->models_with_datetime_fields)) {
399
-            $this->models_with_datetime_fields = get_option(self::MODELS_TO_PROCESS_OPTION_KEY, array());
400
-        }
401
-        return $this->models_with_datetime_fields;
402
-    }
403
-
404
-
405
-    /**
406
-     * Used to record the offset that will be applied to dates and times for EE_Datetime_Field columns.
407
-     *
408
-     * @param float $offset
409
-     */
410
-    public static function updateOffset($offset)
411
-    {
412
-        update_option(self::OFFSET_TO_APPLY_OPTION_KEY, $offset);
413
-    }
414
-
415
-
416
-    /**
417
-     * Used to retrieve the saved offset that will be applied to dates and times for EE_Datetime_Field columns.
418
-     *
419
-     * @return float
420
-     */
421
-    public static function getOffset()
422
-    {
423
-        return (float) get_option(self::OFFSET_TO_APPLY_OPTION_KEY, 0);
424
-    }
425
-
426
-
427
-    /**
428
-     * Used to set the saved offset range start date.
429
-     *
430
-     * @param DbSafeDateTime|null $start_date
431
-     */
432
-    public static function updateStartDateRange(DbSafeDateTime $start_date = null)
433
-    {
434
-        $date_to_save = $start_date instanceof DbSafeDateTime
435
-            ? $start_date->format('U')
436
-            : '';
437
-        update_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, $date_to_save);
438
-    }
439
-
440
-
441
-    /**
442
-     * Used to get the saved offset range start date.
443
-     *
444
-     * @return DbSafeDateTime|null
445
-     */
446
-    public static function getStartDateRange()
447
-    {
448
-        $start_date = get_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, null);
449
-        try {
450
-            $datetime = DateTime::createFromFormat('U', $start_date, new DateTimeZone('UTC'));
451
-            $start_date = $datetime instanceof DateTime
452
-                ? DbSafeDateTime::createFromDateTime($datetime)
453
-                : null;
454
-        } catch (Exception $e) {
455
-            $start_date = null;
456
-        }
457
-        return $start_date;
458
-    }
459
-
460
-
461
-    /**
462
-     * Used to set the saved offset range end date.
463
-     *
464
-     * @param DbSafeDateTime|null $end_date
465
-     */
466
-    public static function updateEndDateRange(DbSafeDateTime $end_date = null)
467
-    {
468
-        $date_to_save = $end_date instanceof DbSafeDateTime
469
-            ? $end_date->format('U')
470
-            : '';
471
-        update_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, $date_to_save);
472
-    }
473
-
474
-
475
-    /**
476
-     * Used to get the saved offset range end date.
477
-     *
478
-     * @return DbSafeDateTime|null
479
-     */
480
-    public static function getEndDateRange()
481
-    {
482
-        $end_date = get_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, null);
483
-        try {
484
-            $datetime = DateTime::createFromFormat('U', $end_date, new DateTimeZone('UTC'));
485
-            $end_date = $datetime instanceof Datetime
486
-                ? DbSafeDateTime::createFromDateTime($datetime)
487
-                : null;
488
-        } catch (Exception $e) {
489
-            $end_date = null;
490
-        }
491
-        return $end_date;
492
-    }
26
+	/**
27
+	 * Key for the option used to track which models have been processed when doing the batches.
28
+	 */
29
+	const MODELS_TO_PROCESS_OPTION_KEY = 'ee_models_processed_for_datetime_offset_fix';
30
+
31
+
32
+	const COUNT_OF_MODELS_PROCESSED = 'ee_count_of_ee_models_processed_for_datetime_offset_fixed';
33
+
34
+	/**
35
+	 * Key for the option used to track what the current offset is that will be applied when this tool is executed.
36
+	 */
37
+	const OFFSET_TO_APPLY_OPTION_KEY = 'ee_datetime_offset_fix_offset_to_apply';
38
+
39
+
40
+	const OPTION_KEY_OFFSET_RANGE_START_DATE = 'ee_datetime_offset_start_date_range';
41
+
42
+
43
+	const OPTION_KEY_OFFSET_RANGE_END_DATE = 'ee_datetime_offset_end_date_range';
44
+
45
+
46
+	/**
47
+	 * String labelling the datetime offset fix type for change-log entries.
48
+	 */
49
+	const DATETIME_OFFSET_FIX_CHANGELOG_TYPE = 'datetime_offset_fix';
50
+
51
+
52
+	/**
53
+	 * String labelling a datetime offset fix error for change-log entries.
54
+	 */
55
+	const DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE = 'datetime_offset_fix_error';
56
+
57
+	/**
58
+	 * @var EEM_Base[]
59
+	 */
60
+	protected $models_with_datetime_fields = array();
61
+
62
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
63
+
64
+	/**
65
+	 * Performs any necessary setup for starting the job. This is also a good
66
+	 * place to setup the $job_arguments which will be used for subsequent HTTP requests
67
+	 * when continue_job will be called
68
+	 *
69
+	 * @param JobParameters $job_parameters
70
+	 * @return JobStepResponse
71
+	 * @throws EE_Error
72
+	 * @throws InvalidArgumentException
73
+	 * @throws InvalidDataTypeException
74
+	 * @throws InvalidInterfaceException
75
+	 */
76
+	public function create_job(JobParameters $job_parameters)
77
+	{
78
+		$models_with_datetime_fields = $this->getModelsWithDatetimeFields();
79
+		// we'll be doing each model as a batch.
80
+		$job_parameters->set_job_size(count($models_with_datetime_fields));
81
+		return new JobStepResponse(
82
+			$job_parameters,
83
+			esc_html__('Starting Datetime Offset Fix', 'event_espresso')
84
+		);
85
+	}
86
+
87
+	/**
88
+	 * Performs another step of the job
89
+	 *
90
+	 * @param JobParameters $job_parameters
91
+	 * @param int           $batch_size
92
+	 * @return JobStepResponse
93
+	 * @throws EE_Error
94
+	 * @throws InvalidArgumentException
95
+	 * @throws InvalidDataTypeException
96
+	 * @throws InvalidInterfaceException
97
+	 */
98
+	public function continue_job(JobParameters $job_parameters, $batch_size = 50)
99
+	{
100
+		$models_to_process = $this->getModelsWithDatetimeFields();
101
+		// let's pop off the a model and do the query to apply the offset.
102
+		$model_to_process = array_pop($models_to_process);
103
+		// update our record
104
+		$this->setModelsToProcess($models_to_process);
105
+		$this->processModel($model_to_process);
106
+		$this->updateCountOfModelsProcessed();
107
+		$job_parameters->set_units_processed($this->getCountOfModelsProcessed());
108
+		if (count($models_to_process) > 0) {
109
+			$job_parameters->set_status(JobParameters::status_continue);
110
+		} else {
111
+			$job_parameters->set_status(JobParameters::status_complete);
112
+		}
113
+		return new JobStepResponse(
114
+			$job_parameters,
115
+			sprintf(
116
+				esc_html__('Updated the offset for all datetime fields on the %s model.', 'event_espresso'),
117
+				$model_to_process
118
+			)
119
+		);
120
+	}
121
+
122
+	/**
123
+	 * Performs any clean-up logic when we know the job is completed
124
+	 *
125
+	 * @param JobParameters $job_parameters
126
+	 * @return JobStepResponse
127
+	 * @throws BatchRequestException
128
+	 */
129
+	public function cleanup_job(JobParameters $job_parameters)
130
+	{
131
+		// delete important saved options.
132
+		delete_option(self::MODELS_TO_PROCESS_OPTION_KEY);
133
+		delete_option(self::COUNT_OF_MODELS_PROCESSED);
134
+		delete_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE);
135
+		delete_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE);
136
+		return new JobStepResponse($job_parameters, esc_html__(
137
+			'Offset has been applied to all affected fields.',
138
+			'event_espresso'
139
+		));
140
+	}
141
+
142
+
143
+	/**
144
+	 * Contains the logic for processing a model and applying the datetime offset to affected fields on that model.
145
+	 *
146
+	 * @param string $model_class_name
147
+	 * @throws EE_Error
148
+	 */
149
+	protected function processModel($model_class_name)
150
+	{
151
+		global $wpdb;
152
+		/** @var EEM_Base $model */
153
+		$model = $model_class_name::instance();
154
+		$original_offset = self::getOffset();
155
+		$start_date_range = self::getStartDateRange();
156
+		$end_date_range = self::getEndDateRange();
157
+		$sql_date_function = $original_offset > 0 ? 'DATE_ADD' : 'DATE_SUB';
158
+		$offset = abs($original_offset) * 60;
159
+		$date_ranges = array();
160
+		// since some affected models might have two tables, we have to get our tables and set up a query for each table.
161
+		foreach ($model->get_tables() as $table) {
162
+			$query = 'UPDATE ' . $table->get_table_name();
163
+			$fields_affected = array();
164
+			$inner_query = array();
165
+			foreach ($model->_get_fields_for_table($table->get_table_alias()) as $model_field) {
166
+				if ($model_field instanceof EE_Datetime_Field) {
167
+					$inner_query[ $model_field->get_table_column() ] = $model_field->get_table_column() . ' = '
168
+																	   . $sql_date_function . '('
169
+																	   . $model_field->get_table_column()
170
+																	   . ", INTERVAL {$offset} MINUTE)";
171
+					$fields_affected[] = $model_field;
172
+				}
173
+			}
174
+			if (! $fields_affected) {
175
+				continue;
176
+			}
177
+			// do we do one query per column/field or one query for all fields on the model? It all depends on whether
178
+			// there is a date range applied or not.
179
+			if ($start_date_range instanceof DbSafeDateTime || $end_date_range instanceof DbSafeDateTime) {
180
+				$result = $this->doQueryForEachField($query, $inner_query, $start_date_range, $end_date_range);
181
+			} else {
182
+				$result = $this->doQueryForAllFields($query, $inner_query);
183
+			}
184
+
185
+			// record appropriate logs for the query
186
+			switch (true) {
187
+				case $result === false:
188
+					// record error.
189
+					$error_message = $wpdb->last_error;
190
+					// handle the edge cases where last_error might be empty.
191
+					if (! $error_message) {
192
+						$error_message = esc_html__('Unknown mysql error occurred.', 'event_espresso');
193
+					}
194
+					$this->recordChangeLog($model, $original_offset, $table, $fields_affected, $error_message);
195
+					break;
196
+				case is_array($result) && ! empty($result):
197
+					foreach ($result as $field_name => $error_message) {
198
+						$this->recordChangeLog($model, $original_offset, $table, array($field_name), $error_message);
199
+					}
200
+					break;
201
+				default:
202
+					$this->recordChangeLog($model, $original_offset, $table, $fields_affected);
203
+			}
204
+		}
205
+	}
206
+
207
+
208
+	/**
209
+	 * Does the query on each $inner_query individually.
210
+	 *
211
+	 * @param string              $query
212
+	 * @param array               $inner_query
213
+	 * @param DbSafeDateTime|null $start_date_range
214
+	 * @param DbSafeDateTime|null $end_date_range
215
+	 * @return array  An array of any errors encountered and the fields they were for.
216
+	 */
217
+	private function doQueryForEachField($query, array $inner_query, $start_date_range, $end_date_range)
218
+	{
219
+		global $wpdb;
220
+		$errors = array();
221
+		foreach ($inner_query as $field_name => $field_query) {
222
+			$query_to_run = $query;
223
+			$where_conditions = array();
224
+			$query_to_run .= ' SET ' . $field_query;
225
+			if ($start_date_range instanceof DbSafeDateTime) {
226
+				$start_date = $start_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
227
+				$where_conditions[] = "{$field_name} > '{$start_date}'";
228
+			}
229
+			if ($end_date_range instanceof DbSafeDateTime) {
230
+				$end_date = $end_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
231
+				$where_conditions[] = "{$field_name} < '{$end_date}'";
232
+			}
233
+			if ($where_conditions) {
234
+				$query_to_run .= ' WHERE ' . implode(' AND ', $where_conditions);
235
+			}
236
+			$result = $wpdb->query($query_to_run);
237
+			if ($result === false) {
238
+				// record error.
239
+				$error_message = $wpdb->last_error;
240
+				// handle the edgecases where last_error might be empty.
241
+				if (! $error_message) {
242
+					$error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
243
+				}
244
+				$errors[ $field_name ] = $error_message;
245
+			}
246
+		}
247
+		return $errors;
248
+	}
249
+
250
+
251
+	/**
252
+	 * Performs the query for all fields within the inner_query
253
+	 *
254
+	 * @param string $query
255
+	 * @param array  $inner_query
256
+	 * @return false|int
257
+	 */
258
+	private function doQueryForAllFields($query, array $inner_query)
259
+	{
260
+		global $wpdb;
261
+		$query .= ' SET ' . implode(',', $inner_query);
262
+		return $wpdb->query($query);
263
+	}
264
+
265
+
266
+	/**
267
+	 * Records a changelog entry using the given information.
268
+	 *
269
+	 * @param EEM_Base              $model
270
+	 * @param float                 $offset
271
+	 * @param EE_Table_Base         $table
272
+	 * @param EE_Model_Field_Base[] $model_fields_affected
273
+	 * @param string                $error_message If present then there was an error so let's record that instead.
274
+	 * @throws EE_Error
275
+	 */
276
+	private function recordChangeLog(
277
+		EEM_Base $model,
278
+		$offset,
279
+		EE_Table_Base $table,
280
+		$model_fields_affected,
281
+		$error_message = ''
282
+	) {
283
+		// setup $fields list.
284
+		$fields = array();
285
+		/** @var EE_Datetime_Field $model_field */
286
+		foreach ($model_fields_affected as $model_field) {
287
+			if (! $model_field instanceof EE_Datetime_Field) {
288
+				continue;
289
+			}
290
+			$fields[] = $model_field->get_name();
291
+		}
292
+		// setup the message for the changelog entry.
293
+		$message = $error_message
294
+			? sprintf(
295
+				esc_html__(
296
+					'The %1$s table for the %2$s model did not have the offset of %3$f applied to its fields (%4$s), because of the following error:%5$s',
297
+					'event_espresso'
298
+				),
299
+				$table->get_table_name(),
300
+				$model->get_this_model_name(),
301
+				$offset,
302
+				implode(',', $fields),
303
+				$error_message
304
+			)
305
+			: sprintf(
306
+				esc_html__(
307
+					'The %1$s table for the %2$s model has had the offset of %3$f applied to its following fields: %4$s',
308
+					'event_espresso'
309
+				),
310
+				$table->get_table_name(),
311
+				$model->get_this_model_name(),
312
+				$offset,
313
+				implode(',', $fields)
314
+			);
315
+		// write to the log
316
+		$changelog = EE_Change_Log::new_instance(array(
317
+			'LOG_type'    => $error_message
318
+				? self::DATETIME_OFFSET_FIX_CHANGELOG_ERROR_TYPE
319
+				: self::DATETIME_OFFSET_FIX_CHANGELOG_TYPE,
320
+			'LOG_message' => $message,
321
+		));
322
+		$changelog->save();
323
+	}
324
+
325
+
326
+	/**
327
+	 * Returns an array of models that have datetime fields.
328
+	 * This array is added to a short lived transient cache to keep having to build this list to a minimum.
329
+	 *
330
+	 * @return array an array of model class names.
331
+	 * @throws EE_Error
332
+	 * @throws InvalidDataTypeException
333
+	 * @throws InvalidInterfaceException
334
+	 * @throws InvalidArgumentException
335
+	 */
336
+	private function getModelsWithDatetimeFields()
337
+	{
338
+		$this->getModelsToProcess();
339
+		if (! empty($this->models_with_datetime_fields)) {
340
+			return $this->models_with_datetime_fields;
341
+		}
342
+
343
+		$all_non_abstract_models = EE_Registry::instance()->non_abstract_db_models;
344
+		foreach ($all_non_abstract_models as $non_abstract_model) {
345
+			// get model instance
346
+			/** @var EEM_Base $non_abstract_model */
347
+			$non_abstract_model = $non_abstract_model::instance();
348
+			if ($non_abstract_model->get_a_field_of_type('EE_Datetime_Field') instanceof EE_Datetime_Field) {
349
+				$this->models_with_datetime_fields[] = get_class($non_abstract_model);
350
+			}
351
+		}
352
+		$this->setModelsToProcess($this->models_with_datetime_fields);
353
+		return $this->models_with_datetime_fields;
354
+	}
355
+
356
+
357
+	/**
358
+	 * This simply records the models that have been processed with our tracking option.
359
+	 *
360
+	 * @param array $models_to_set array of model class names.
361
+	 */
362
+	private function setModelsToProcess($models_to_set)
363
+	{
364
+		update_option(self::MODELS_TO_PROCESS_OPTION_KEY, $models_to_set);
365
+	}
366
+
367
+
368
+	/**
369
+	 * Used to keep track of how many models have been processed for the batch
370
+	 *
371
+	 * @param $count
372
+	 */
373
+	private function updateCountOfModelsProcessed($count = 1)
374
+	{
375
+		$count = $this->getCountOfModelsProcessed() + (int) $count;
376
+		update_option(self::COUNT_OF_MODELS_PROCESSED, $count);
377
+	}
378
+
379
+
380
+	/**
381
+	 * Retrieve the tracked number of models processed between requests.
382
+	 *
383
+	 * @return int
384
+	 */
385
+	private function getCountOfModelsProcessed()
386
+	{
387
+		return (int) get_option(self::COUNT_OF_MODELS_PROCESSED, 0);
388
+	}
389
+
390
+
391
+	/**
392
+	 * Returns the models that are left to process.
393
+	 *
394
+	 * @return array  an array of model class names.
395
+	 */
396
+	private function getModelsToProcess()
397
+	{
398
+		if (empty($this->models_with_datetime_fields)) {
399
+			$this->models_with_datetime_fields = get_option(self::MODELS_TO_PROCESS_OPTION_KEY, array());
400
+		}
401
+		return $this->models_with_datetime_fields;
402
+	}
403
+
404
+
405
+	/**
406
+	 * Used to record the offset that will be applied to dates and times for EE_Datetime_Field columns.
407
+	 *
408
+	 * @param float $offset
409
+	 */
410
+	public static function updateOffset($offset)
411
+	{
412
+		update_option(self::OFFSET_TO_APPLY_OPTION_KEY, $offset);
413
+	}
414
+
415
+
416
+	/**
417
+	 * Used to retrieve the saved offset that will be applied to dates and times for EE_Datetime_Field columns.
418
+	 *
419
+	 * @return float
420
+	 */
421
+	public static function getOffset()
422
+	{
423
+		return (float) get_option(self::OFFSET_TO_APPLY_OPTION_KEY, 0);
424
+	}
425
+
426
+
427
+	/**
428
+	 * Used to set the saved offset range start date.
429
+	 *
430
+	 * @param DbSafeDateTime|null $start_date
431
+	 */
432
+	public static function updateStartDateRange(DbSafeDateTime $start_date = null)
433
+	{
434
+		$date_to_save = $start_date instanceof DbSafeDateTime
435
+			? $start_date->format('U')
436
+			: '';
437
+		update_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, $date_to_save);
438
+	}
439
+
440
+
441
+	/**
442
+	 * Used to get the saved offset range start date.
443
+	 *
444
+	 * @return DbSafeDateTime|null
445
+	 */
446
+	public static function getStartDateRange()
447
+	{
448
+		$start_date = get_option(self::OPTION_KEY_OFFSET_RANGE_START_DATE, null);
449
+		try {
450
+			$datetime = DateTime::createFromFormat('U', $start_date, new DateTimeZone('UTC'));
451
+			$start_date = $datetime instanceof DateTime
452
+				? DbSafeDateTime::createFromDateTime($datetime)
453
+				: null;
454
+		} catch (Exception $e) {
455
+			$start_date = null;
456
+		}
457
+		return $start_date;
458
+	}
459
+
460
+
461
+	/**
462
+	 * Used to set the saved offset range end date.
463
+	 *
464
+	 * @param DbSafeDateTime|null $end_date
465
+	 */
466
+	public static function updateEndDateRange(DbSafeDateTime $end_date = null)
467
+	{
468
+		$date_to_save = $end_date instanceof DbSafeDateTime
469
+			? $end_date->format('U')
470
+			: '';
471
+		update_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, $date_to_save);
472
+	}
473
+
474
+
475
+	/**
476
+	 * Used to get the saved offset range end date.
477
+	 *
478
+	 * @return DbSafeDateTime|null
479
+	 */
480
+	public static function getEndDateRange()
481
+	{
482
+		$end_date = get_option(self::OPTION_KEY_OFFSET_RANGE_END_DATE, null);
483
+		try {
484
+			$datetime = DateTime::createFromFormat('U', $end_date, new DateTimeZone('UTC'));
485
+			$end_date = $datetime instanceof Datetime
486
+				? DbSafeDateTime::createFromDateTime($datetime)
487
+				: null;
488
+		} catch (Exception $e) {
489
+			$end_date = null;
490
+		}
491
+		return $end_date;
492
+	}
493 493
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -159,19 +159,19 @@  discard block
 block discarded – undo
159 159
         $date_ranges = array();
160 160
         // since some affected models might have two tables, we have to get our tables and set up a query for each table.
161 161
         foreach ($model->get_tables() as $table) {
162
-            $query = 'UPDATE ' . $table->get_table_name();
162
+            $query = 'UPDATE '.$table->get_table_name();
163 163
             $fields_affected = array();
164 164
             $inner_query = array();
165 165
             foreach ($model->_get_fields_for_table($table->get_table_alias()) as $model_field) {
166 166
                 if ($model_field instanceof EE_Datetime_Field) {
167
-                    $inner_query[ $model_field->get_table_column() ] = $model_field->get_table_column() . ' = '
168
-                                                                       . $sql_date_function . '('
167
+                    $inner_query[$model_field->get_table_column()] = $model_field->get_table_column().' = '
168
+                                                                       . $sql_date_function.'('
169 169
                                                                        . $model_field->get_table_column()
170 170
                                                                        . ", INTERVAL {$offset} MINUTE)";
171 171
                     $fields_affected[] = $model_field;
172 172
                 }
173 173
             }
174
-            if (! $fields_affected) {
174
+            if ( ! $fields_affected) {
175 175
                 continue;
176 176
             }
177 177
             // do we do one query per column/field or one query for all fields on the model? It all depends on whether
@@ -188,7 +188,7 @@  discard block
 block discarded – undo
188 188
                     // record error.
189 189
                     $error_message = $wpdb->last_error;
190 190
                     // handle the edge cases where last_error might be empty.
191
-                    if (! $error_message) {
191
+                    if ( ! $error_message) {
192 192
                         $error_message = esc_html__('Unknown mysql error occurred.', 'event_espresso');
193 193
                     }
194 194
                     $this->recordChangeLog($model, $original_offset, $table, $fields_affected, $error_message);
@@ -221,7 +221,7 @@  discard block
 block discarded – undo
221 221
         foreach ($inner_query as $field_name => $field_query) {
222 222
             $query_to_run = $query;
223 223
             $where_conditions = array();
224
-            $query_to_run .= ' SET ' . $field_query;
224
+            $query_to_run .= ' SET '.$field_query;
225 225
             if ($start_date_range instanceof DbSafeDateTime) {
226 226
                 $start_date = $start_date_range->format(EE_Datetime_Field::mysql_timestamp_format);
227 227
                 $where_conditions[] = "{$field_name} > '{$start_date}'";
@@ -231,17 +231,17 @@  discard block
 block discarded – undo
231 231
                 $where_conditions[] = "{$field_name} < '{$end_date}'";
232 232
             }
233 233
             if ($where_conditions) {
234
-                $query_to_run .= ' WHERE ' . implode(' AND ', $where_conditions);
234
+                $query_to_run .= ' WHERE '.implode(' AND ', $where_conditions);
235 235
             }
236 236
             $result = $wpdb->query($query_to_run);
237 237
             if ($result === false) {
238 238
                 // record error.
239 239
                 $error_message = $wpdb->last_error;
240 240
                 // handle the edgecases where last_error might be empty.
241
-                if (! $error_message) {
241
+                if ( ! $error_message) {
242 242
                     $error_message = esc_html__('Unknown mysql error occured.', 'event_espresso');
243 243
                 }
244
-                $errors[ $field_name ] = $error_message;
244
+                $errors[$field_name] = $error_message;
245 245
             }
246 246
         }
247 247
         return $errors;
@@ -258,7 +258,7 @@  discard block
 block discarded – undo
258 258
     private function doQueryForAllFields($query, array $inner_query)
259 259
     {
260 260
         global $wpdb;
261
-        $query .= ' SET ' . implode(',', $inner_query);
261
+        $query .= ' SET '.implode(',', $inner_query);
262 262
         return $wpdb->query($query);
263 263
     }
264 264
 
@@ -284,7 +284,7 @@  discard block
 block discarded – undo
284 284
         $fields = array();
285 285
         /** @var EE_Datetime_Field $model_field */
286 286
         foreach ($model_fields_affected as $model_field) {
287
-            if (! $model_field instanceof EE_Datetime_Field) {
287
+            if ( ! $model_field instanceof EE_Datetime_Field) {
288 288
                 continue;
289 289
             }
290 290
             $fields[] = $model_field->get_name();
@@ -336,7 +336,7 @@  discard block
 block discarded – undo
336 336
     private function getModelsWithDatetimeFields()
337 337
     {
338 338
         $this->getModelsToProcess();
339
-        if (! empty($this->models_with_datetime_fields)) {
339
+        if ( ! empty($this->models_with_datetime_fields)) {
340 340
             return $this->models_with_datetime_fields;
341 341
         }
342 342
 
Please login to merge, or discard this patch.