Completed
Branch BUG/11361/fix-log-calls-using-... (7ad8bc)
by
unknown
27:20 queued 14:31
created
form_sections/strategies/layout/EE_Form_Section_Layout_Base.strategy.php 2 patches
Indentation   +284 added lines, -284 removed lines patch added patch discarded remove patch
@@ -15,288 +15,288 @@
 block discarded – undo
15 15
 abstract class EE_Form_Section_Layout_Base
16 16
 {
17 17
 
18
-    /**
19
-     * Form form section to lay out
20
-     *
21
-     * @var EE_Form_Section_Proper
22
-     */
23
-    protected $_form_section;
24
-
25
-
26
-
27
-    /**
28
-     *  __construct
29
-     */
30
-    public function __construct()
31
-    {
32
-    }
33
-
34
-
35
-
36
-    /**
37
-     * The form section on which this strategy is to perform
38
-     *
39
-     * @param EE_Form_Section_Proper $form
40
-     */
41
-    public function _construct_finalize(EE_Form_Section_Proper $form)
42
-    {
43
-        $this->_form_section = $form;
44
-    }
45
-
46
-
47
-
48
-    /**
49
-     * @return EE_Form_Section_Proper
50
-     */
51
-    public function form_section()
52
-    {
53
-        return $this->_form_section;
54
-    }
55
-
56
-
57
-
58
-    /**
59
-     * Also has teh side effect of enqueuing any needed JS and CSS for
60
-     * this form.
61
-     * Creates all the HTML necessary for displaying this form, its inputs, and
62
-     * proper subsections.
63
-     * Returns the HTML
64
-     *
65
-     * @return string HTML for displaying
66
-     * @throws EE_Error
67
-     */
68
-    public function layout_form()
69
-    {
70
-        $html = '';
71
-        // layout_form_begin
72
-        $html .= apply_filters(
73
-            'FHEE__EE_Form_Section_Layout_Base__layout_form__start__for_' . $this->_form_section->name(),
74
-            $this->layout_form_begin(),
75
-            $this->_form_section
76
-        );
77
-        // layout_form_loop
78
-        $html .= apply_filters(
79
-            'FHEE__EE_Form_Section_Layout_Base__layout_form__loop__for_' . $this->_form_section->name(),
80
-            $this->layout_form_loop(),
81
-            $this->_form_section
82
-        );
83
-        // layout_form_end
84
-        $html .= apply_filters(
85
-            'FHEE__EE_Form_Section_Layout_Base__layout_form__end__for_' . $this->_form_section->name(),
86
-            $this->layout_form_end(),
87
-            $this->_form_section
88
-        );
89
-        $html = $this->add_form_section_hooks_and_filters($html);
90
-        return $html;
91
-    }
92
-
93
-
94
-
95
-    /**
96
-     * @return string
97
-     * @throws EE_Error
98
-     */
99
-    public function layout_form_loop()
100
-    {
101
-        $html = '';
102
-        foreach ($this->_form_section->subsections() as $name => $subsection) {
103
-            if ($subsection instanceof EE_Form_Input_Base) {
104
-                $html .= apply_filters(
105
-                    'FHEE__EE_Form_Section_Layout_Base__layout_form__loop_for_input_'
106
-                    . $name . '__in_' . $this->_form_section->name(),
107
-                    $this->layout_input($subsection),
108
-                    $this->_form_section,
109
-                    $subsection
110
-                );
111
-            } elseif ($subsection instanceof EE_Form_Section_Base) {
112
-                $html .= apply_filters(
113
-                    'FHEE__EE_Form_Section_Layout_Base__layout_form__loop_for_non_input_'
114
-                    . $name . '__in_' . $this->_form_section->name(),
115
-                    $this->layout_subsection($subsection),
116
-                    $this->_form_section,
117
-                    $subsection
118
-                );
119
-            }
120
-        }
121
-        return $html;
122
-    }
123
-
124
-
125
-
126
-    /**
127
-     * Should be used to start teh form section (Eg a table tag, or a div tag, etc.)
128
-     *
129
-     * @return string
130
-     */
131
-    abstract public function layout_form_begin();
132
-
133
-
134
-
135
-    /**
136
-     * Should be used to end the form section (eg a /table tag, or a /div tag, etc)
137
-     *
138
-     * @return string
139
-     */
140
-    abstract public function layout_form_end();
141
-
142
-
143
-
144
-    /**
145
-     * Should be used internally by layout_form() to layout each input (eg, if this layout
146
-     * is putting each input in a row of its own, this should probably be called by a
147
-     *  foreach loop in layout_form() (WITHOUT adding any content directly within layout_form()'s foreach loop.
148
-     * Eg, this method should add the tr and td tags). This method is exposed in case you want to completely
149
-     * customize the form's layout, but would like to make use of it for laying out
150
-     * 'easy-to-layout' inputs
151
-     *
152
-     * @param EE_Form_Input_Base $input
153
-     * @return string html
154
-     */
155
-    abstract public function layout_input($input);
156
-
157
-
158
-
159
-    /**
160
-     * Similar to layout_input(), should be used internally by layout_form() within a
161
-     * loop to layout each proper subsection. Unlike layout_input(), however, it is assumed
162
-     * that the proper subsection will layout its container, label, etc on its own.
163
-     *
164
-     * @param EE_Form_Section_Base $subsection
165
-     * @return string html
166
-     */
167
-    abstract public function layout_subsection($subsection);
168
-
169
-
170
-
171
-    /**
172
-     * Gets the HTML for the label tag and its contents for the input
173
-     *
174
-     * @param EE_Form_Input_Base $input
175
-     * @return string
176
-     */
177
-    public function display_label($input)
178
-    {
179
-        if($input->get_display_strategy() instanceof EE_Hidden_Display_Strategy){
180
-            return '';
181
-        }
182
-        $class = $input->required()
183
-            ? 'ee-required-label ' . $input->html_label_class()
184
-            : $input->html_label_class();
185
-        $label_text = $input->required()
186
-            ? $input->html_label_text() . '<span class="ee-asterisk">*</span>'
187
-            : $input->html_label_text();
188
-        return '<label id="'
189
-               . $input->html_label_id()
190
-               . '" class="'
191
-               . $class
192
-               . '" style="'
193
-               . $input->html_label_style()
194
-               . '" for="' . $input->html_name()
195
-               . '">'
196
-               . $label_text
197
-               . '</label>';
198
-    }
199
-
200
-
201
-
202
-    /**
203
-     * Gets the HTML for all the form's form-wide errors (ie, errors which
204
-     * are not for specific inputs. E.g., if two inputs somehow disagree,
205
-     * those errors would probably be on the form section, not one of its inputs)
206
-     * @return string
207
-     */
208
-    public function display_form_wide_errors()
209
-    {
210
-        $html = '';
211
-        if ($this->_form_section->get_validation_errors()) {
212
-            $html .= "<div class='ee-form-wide-errors'>";
213
-            //get all the errors on THIS form section (errors which aren't
214
-            //for specific inputs, but instead for the entire form section)
215
-            foreach ($this->_form_section->get_validation_errors() as $error) {
216
-                $html .= $error->getMessage() . '<br>';
217
-            }
218
-            $html .= '</div>';
219
-        }
220
-        return apply_filters(
221
-            'FHEE__EE_Form_Section_Layout_Base__display_form_wide_errors',
222
-            $html,
223
-            $this
224
-        );
225
-    }
226
-
227
-
228
-
229
-    /**
230
-     * returns the HTML for the server-side validation errors for the specified input
231
-     * Note that if JS is enabled, it should remove these and instead
232
-     * populate the form's errors in the jquery validate fashion
233
-     * using the localized data provided to the JS
234
-     *
235
-     * @param EE_Form_Input_Base $input
236
-     * @return string
237
-     */
238
-    public function display_errors($input)
239
-    {
240
-        if ($input->get_validation_errors()) {
241
-            return "<label  id='"
242
-                   . $input->html_id()
243
-                   . "-error' class='error' for='{$input->html_name()}'>"
244
-                   . $input->get_validation_error_string()
245
-                   . '</label>';
246
-        }
247
-        return '';
248
-    }
249
-
250
-
251
-
252
-    /**
253
-     * Displays the help span for the specified input
254
-     *
255
-     * @param EE_Form_Input_Base $input
256
-     * @return string
257
-     */
258
-    public function display_help_text($input)
259
-    {
260
-        $help_text  = $input->html_help_text();
261
-        if ($help_text !== '' && $help_text !== null) {
262
-            $tag = is_admin() ? 'p' : 'span';
263
-            return '<'
264
-                   . $tag
265
-                   . ' id="'
266
-                   . $input->html_id()
267
-                   . '-help" class="'
268
-                   . $input->html_help_class()
269
-                   . '" style="'
270
-                   . $input->html_help_style()
271
-                   . '">'
272
-                   . $help_text
273
-                   . '</'
274
-                   . $tag
275
-                   . '>';
276
-        }
277
-        return '';
278
-    }
279
-
280
-
281
-
282
-    /**
283
-     * Does an action and hook onto the end of teh form
284
-     *
285
-     * @param string $html
286
-     * @return string
287
-     */
288
-    public function add_form_section_hooks_and_filters($html)
289
-    {
290
-        // replace dashes and spaces with underscores
291
-        $hook_name = str_replace(array('-', ' '), '_', $this->_form_section->html_id());
292
-        do_action('AHEE__Form_Section_Layout__' . $hook_name, $this->_form_section);
293
-        $html = (string) apply_filters(
294
-            'AFEE__Form_Section_Layout__' . $hook_name . '__html',
295
-            $html,
296
-            $this->_form_section
297
-        );
298
-        $html .= EEH_HTML::nl() . '<!-- AHEE__Form_Section_Layout__' . $hook_name . '__html -->';
299
-        $html .= EEH_HTML::nl() . '<!-- AFEE__Form_Section_Layout__' . $hook_name . ' -->';
300
-        return $html;
301
-    }
18
+	/**
19
+	 * Form form section to lay out
20
+	 *
21
+	 * @var EE_Form_Section_Proper
22
+	 */
23
+	protected $_form_section;
24
+
25
+
26
+
27
+	/**
28
+	 *  __construct
29
+	 */
30
+	public function __construct()
31
+	{
32
+	}
33
+
34
+
35
+
36
+	/**
37
+	 * The form section on which this strategy is to perform
38
+	 *
39
+	 * @param EE_Form_Section_Proper $form
40
+	 */
41
+	public function _construct_finalize(EE_Form_Section_Proper $form)
42
+	{
43
+		$this->_form_section = $form;
44
+	}
45
+
46
+
47
+
48
+	/**
49
+	 * @return EE_Form_Section_Proper
50
+	 */
51
+	public function form_section()
52
+	{
53
+		return $this->_form_section;
54
+	}
55
+
56
+
57
+
58
+	/**
59
+	 * Also has teh side effect of enqueuing any needed JS and CSS for
60
+	 * this form.
61
+	 * Creates all the HTML necessary for displaying this form, its inputs, and
62
+	 * proper subsections.
63
+	 * Returns the HTML
64
+	 *
65
+	 * @return string HTML for displaying
66
+	 * @throws EE_Error
67
+	 */
68
+	public function layout_form()
69
+	{
70
+		$html = '';
71
+		// layout_form_begin
72
+		$html .= apply_filters(
73
+			'FHEE__EE_Form_Section_Layout_Base__layout_form__start__for_' . $this->_form_section->name(),
74
+			$this->layout_form_begin(),
75
+			$this->_form_section
76
+		);
77
+		// layout_form_loop
78
+		$html .= apply_filters(
79
+			'FHEE__EE_Form_Section_Layout_Base__layout_form__loop__for_' . $this->_form_section->name(),
80
+			$this->layout_form_loop(),
81
+			$this->_form_section
82
+		);
83
+		// layout_form_end
84
+		$html .= apply_filters(
85
+			'FHEE__EE_Form_Section_Layout_Base__layout_form__end__for_' . $this->_form_section->name(),
86
+			$this->layout_form_end(),
87
+			$this->_form_section
88
+		);
89
+		$html = $this->add_form_section_hooks_and_filters($html);
90
+		return $html;
91
+	}
92
+
93
+
94
+
95
+	/**
96
+	 * @return string
97
+	 * @throws EE_Error
98
+	 */
99
+	public function layout_form_loop()
100
+	{
101
+		$html = '';
102
+		foreach ($this->_form_section->subsections() as $name => $subsection) {
103
+			if ($subsection instanceof EE_Form_Input_Base) {
104
+				$html .= apply_filters(
105
+					'FHEE__EE_Form_Section_Layout_Base__layout_form__loop_for_input_'
106
+					. $name . '__in_' . $this->_form_section->name(),
107
+					$this->layout_input($subsection),
108
+					$this->_form_section,
109
+					$subsection
110
+				);
111
+			} elseif ($subsection instanceof EE_Form_Section_Base) {
112
+				$html .= apply_filters(
113
+					'FHEE__EE_Form_Section_Layout_Base__layout_form__loop_for_non_input_'
114
+					. $name . '__in_' . $this->_form_section->name(),
115
+					$this->layout_subsection($subsection),
116
+					$this->_form_section,
117
+					$subsection
118
+				);
119
+			}
120
+		}
121
+		return $html;
122
+	}
123
+
124
+
125
+
126
+	/**
127
+	 * Should be used to start teh form section (Eg a table tag, or a div tag, etc.)
128
+	 *
129
+	 * @return string
130
+	 */
131
+	abstract public function layout_form_begin();
132
+
133
+
134
+
135
+	/**
136
+	 * Should be used to end the form section (eg a /table tag, or a /div tag, etc)
137
+	 *
138
+	 * @return string
139
+	 */
140
+	abstract public function layout_form_end();
141
+
142
+
143
+
144
+	/**
145
+	 * Should be used internally by layout_form() to layout each input (eg, if this layout
146
+	 * is putting each input in a row of its own, this should probably be called by a
147
+	 *  foreach loop in layout_form() (WITHOUT adding any content directly within layout_form()'s foreach loop.
148
+	 * Eg, this method should add the tr and td tags). This method is exposed in case you want to completely
149
+	 * customize the form's layout, but would like to make use of it for laying out
150
+	 * 'easy-to-layout' inputs
151
+	 *
152
+	 * @param EE_Form_Input_Base $input
153
+	 * @return string html
154
+	 */
155
+	abstract public function layout_input($input);
156
+
157
+
158
+
159
+	/**
160
+	 * Similar to layout_input(), should be used internally by layout_form() within a
161
+	 * loop to layout each proper subsection. Unlike layout_input(), however, it is assumed
162
+	 * that the proper subsection will layout its container, label, etc on its own.
163
+	 *
164
+	 * @param EE_Form_Section_Base $subsection
165
+	 * @return string html
166
+	 */
167
+	abstract public function layout_subsection($subsection);
168
+
169
+
170
+
171
+	/**
172
+	 * Gets the HTML for the label tag and its contents for the input
173
+	 *
174
+	 * @param EE_Form_Input_Base $input
175
+	 * @return string
176
+	 */
177
+	public function display_label($input)
178
+	{
179
+		if($input->get_display_strategy() instanceof EE_Hidden_Display_Strategy){
180
+			return '';
181
+		}
182
+		$class = $input->required()
183
+			? 'ee-required-label ' . $input->html_label_class()
184
+			: $input->html_label_class();
185
+		$label_text = $input->required()
186
+			? $input->html_label_text() . '<span class="ee-asterisk">*</span>'
187
+			: $input->html_label_text();
188
+		return '<label id="'
189
+			   . $input->html_label_id()
190
+			   . '" class="'
191
+			   . $class
192
+			   . '" style="'
193
+			   . $input->html_label_style()
194
+			   . '" for="' . $input->html_name()
195
+			   . '">'
196
+			   . $label_text
197
+			   . '</label>';
198
+	}
199
+
200
+
201
+
202
+	/**
203
+	 * Gets the HTML for all the form's form-wide errors (ie, errors which
204
+	 * are not for specific inputs. E.g., if two inputs somehow disagree,
205
+	 * those errors would probably be on the form section, not one of its inputs)
206
+	 * @return string
207
+	 */
208
+	public function display_form_wide_errors()
209
+	{
210
+		$html = '';
211
+		if ($this->_form_section->get_validation_errors()) {
212
+			$html .= "<div class='ee-form-wide-errors'>";
213
+			//get all the errors on THIS form section (errors which aren't
214
+			//for specific inputs, but instead for the entire form section)
215
+			foreach ($this->_form_section->get_validation_errors() as $error) {
216
+				$html .= $error->getMessage() . '<br>';
217
+			}
218
+			$html .= '</div>';
219
+		}
220
+		return apply_filters(
221
+			'FHEE__EE_Form_Section_Layout_Base__display_form_wide_errors',
222
+			$html,
223
+			$this
224
+		);
225
+	}
226
+
227
+
228
+
229
+	/**
230
+	 * returns the HTML for the server-side validation errors for the specified input
231
+	 * Note that if JS is enabled, it should remove these and instead
232
+	 * populate the form's errors in the jquery validate fashion
233
+	 * using the localized data provided to the JS
234
+	 *
235
+	 * @param EE_Form_Input_Base $input
236
+	 * @return string
237
+	 */
238
+	public function display_errors($input)
239
+	{
240
+		if ($input->get_validation_errors()) {
241
+			return "<label  id='"
242
+				   . $input->html_id()
243
+				   . "-error' class='error' for='{$input->html_name()}'>"
244
+				   . $input->get_validation_error_string()
245
+				   . '</label>';
246
+		}
247
+		return '';
248
+	}
249
+
250
+
251
+
252
+	/**
253
+	 * Displays the help span for the specified input
254
+	 *
255
+	 * @param EE_Form_Input_Base $input
256
+	 * @return string
257
+	 */
258
+	public function display_help_text($input)
259
+	{
260
+		$help_text  = $input->html_help_text();
261
+		if ($help_text !== '' && $help_text !== null) {
262
+			$tag = is_admin() ? 'p' : 'span';
263
+			return '<'
264
+				   . $tag
265
+				   . ' id="'
266
+				   . $input->html_id()
267
+				   . '-help" class="'
268
+				   . $input->html_help_class()
269
+				   . '" style="'
270
+				   . $input->html_help_style()
271
+				   . '">'
272
+				   . $help_text
273
+				   . '</'
274
+				   . $tag
275
+				   . '>';
276
+		}
277
+		return '';
278
+	}
279
+
280
+
281
+
282
+	/**
283
+	 * Does an action and hook onto the end of teh form
284
+	 *
285
+	 * @param string $html
286
+	 * @return string
287
+	 */
288
+	public function add_form_section_hooks_and_filters($html)
289
+	{
290
+		// replace dashes and spaces with underscores
291
+		$hook_name = str_replace(array('-', ' '), '_', $this->_form_section->html_id());
292
+		do_action('AHEE__Form_Section_Layout__' . $hook_name, $this->_form_section);
293
+		$html = (string) apply_filters(
294
+			'AFEE__Form_Section_Layout__' . $hook_name . '__html',
295
+			$html,
296
+			$this->_form_section
297
+		);
298
+		$html .= EEH_HTML::nl() . '<!-- AHEE__Form_Section_Layout__' . $hook_name . '__html -->';
299
+		$html .= EEH_HTML::nl() . '<!-- AFEE__Form_Section_Layout__' . $hook_name . ' -->';
300
+		return $html;
301
+	}
302 302
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -70,19 +70,19 @@  discard block
 block discarded – undo
70 70
         $html = '';
71 71
         // layout_form_begin
72 72
         $html .= apply_filters(
73
-            'FHEE__EE_Form_Section_Layout_Base__layout_form__start__for_' . $this->_form_section->name(),
73
+            'FHEE__EE_Form_Section_Layout_Base__layout_form__start__for_'.$this->_form_section->name(),
74 74
             $this->layout_form_begin(),
75 75
             $this->_form_section
76 76
         );
77 77
         // layout_form_loop
78 78
         $html .= apply_filters(
79
-            'FHEE__EE_Form_Section_Layout_Base__layout_form__loop__for_' . $this->_form_section->name(),
79
+            'FHEE__EE_Form_Section_Layout_Base__layout_form__loop__for_'.$this->_form_section->name(),
80 80
             $this->layout_form_loop(),
81 81
             $this->_form_section
82 82
         );
83 83
         // layout_form_end
84 84
         $html .= apply_filters(
85
-            'FHEE__EE_Form_Section_Layout_Base__layout_form__end__for_' . $this->_form_section->name(),
85
+            'FHEE__EE_Form_Section_Layout_Base__layout_form__end__for_'.$this->_form_section->name(),
86 86
             $this->layout_form_end(),
87 87
             $this->_form_section
88 88
         );
@@ -103,7 +103,7 @@  discard block
 block discarded – undo
103 103
             if ($subsection instanceof EE_Form_Input_Base) {
104 104
                 $html .= apply_filters(
105 105
                     'FHEE__EE_Form_Section_Layout_Base__layout_form__loop_for_input_'
106
-                    . $name . '__in_' . $this->_form_section->name(),
106
+                    . $name.'__in_'.$this->_form_section->name(),
107 107
                     $this->layout_input($subsection),
108 108
                     $this->_form_section,
109 109
                     $subsection
@@ -111,7 +111,7 @@  discard block
 block discarded – undo
111 111
             } elseif ($subsection instanceof EE_Form_Section_Base) {
112 112
                 $html .= apply_filters(
113 113
                     'FHEE__EE_Form_Section_Layout_Base__layout_form__loop_for_non_input_'
114
-                    . $name . '__in_' . $this->_form_section->name(),
114
+                    . $name.'__in_'.$this->_form_section->name(),
115 115
                     $this->layout_subsection($subsection),
116 116
                     $this->_form_section,
117 117
                     $subsection
@@ -176,14 +176,14 @@  discard block
 block discarded – undo
176 176
      */
177 177
     public function display_label($input)
178 178
     {
179
-        if($input->get_display_strategy() instanceof EE_Hidden_Display_Strategy){
179
+        if ($input->get_display_strategy() instanceof EE_Hidden_Display_Strategy) {
180 180
             return '';
181 181
         }
182 182
         $class = $input->required()
183
-            ? 'ee-required-label ' . $input->html_label_class()
183
+            ? 'ee-required-label '.$input->html_label_class()
184 184
             : $input->html_label_class();
185 185
         $label_text = $input->required()
186
-            ? $input->html_label_text() . '<span class="ee-asterisk">*</span>'
186
+            ? $input->html_label_text().'<span class="ee-asterisk">*</span>'
187 187
             : $input->html_label_text();
188 188
         return '<label id="'
189 189
                . $input->html_label_id()
@@ -191,7 +191,7 @@  discard block
 block discarded – undo
191 191
                . $class
192 192
                . '" style="'
193 193
                . $input->html_label_style()
194
-               . '" for="' . $input->html_name()
194
+               . '" for="'.$input->html_name()
195 195
                . '">'
196 196
                . $label_text
197 197
                . '</label>';
@@ -213,7 +213,7 @@  discard block
 block discarded – undo
213 213
             //get all the errors on THIS form section (errors which aren't
214 214
             //for specific inputs, but instead for the entire form section)
215 215
             foreach ($this->_form_section->get_validation_errors() as $error) {
216
-                $html .= $error->getMessage() . '<br>';
216
+                $html .= $error->getMessage().'<br>';
217 217
             }
218 218
             $html .= '</div>';
219 219
         }
@@ -257,7 +257,7 @@  discard block
 block discarded – undo
257 257
      */
258 258
     public function display_help_text($input)
259 259
     {
260
-        $help_text  = $input->html_help_text();
260
+        $help_text = $input->html_help_text();
261 261
         if ($help_text !== '' && $help_text !== null) {
262 262
             $tag = is_admin() ? 'p' : 'span';
263 263
             return '<'
@@ -289,14 +289,14 @@  discard block
 block discarded – undo
289 289
     {
290 290
         // replace dashes and spaces with underscores
291 291
         $hook_name = str_replace(array('-', ' '), '_', $this->_form_section->html_id());
292
-        do_action('AHEE__Form_Section_Layout__' . $hook_name, $this->_form_section);
292
+        do_action('AHEE__Form_Section_Layout__'.$hook_name, $this->_form_section);
293 293
         $html = (string) apply_filters(
294
-            'AFEE__Form_Section_Layout__' . $hook_name . '__html',
294
+            'AFEE__Form_Section_Layout__'.$hook_name.'__html',
295 295
             $html,
296 296
             $this->_form_section
297 297
         );
298
-        $html .= EEH_HTML::nl() . '<!-- AHEE__Form_Section_Layout__' . $hook_name . '__html -->';
299
-        $html .= EEH_HTML::nl() . '<!-- AFEE__Form_Section_Layout__' . $hook_name . ' -->';
298
+        $html .= EEH_HTML::nl().'<!-- AHEE__Form_Section_Layout__'.$hook_name.'__html -->';
299
+        $html .= EEH_HTML::nl().'<!-- AFEE__Form_Section_Layout__'.$hook_name.' -->';
300 300
         return $html;
301 301
     }
302 302
 }
Please login to merge, or discard this patch.
core/admin/EE_Admin_Page_CPT.core.php 2 patches
Indentation   +1432 added lines, -1432 removed lines patch added patch discarded remove patch
@@ -3,7 +3,7 @@  discard block
 block discarded – undo
3 3
 use EventEspresso\core\services\request\middleware\RecommendedVersions;
4 4
 
5 5
 if ( ! defined('EVENT_ESPRESSO_VERSION')) {
6
-    exit('No direct script access allowed');
6
+	exit('No direct script access allowed');
7 7
 }
8 8
 
9 9
 /**
@@ -28,470 +28,470 @@  discard block
 block discarded – undo
28 28
 {
29 29
 
30 30
 
31
-    /**
32
-     * This gets set in _setup_cpt
33
-     * It will contain the object for the custom post type.
34
-     *
35
-     * @var EE_CPT_Base
36
-     */
37
-    protected $_cpt_object;
38
-
39
-
40
-
41
-    /**
42
-     * a boolean flag to set whether the current route is a cpt route or not.
43
-     *
44
-     * @var bool
45
-     */
46
-    protected $_cpt_route = false;
47
-
48
-
49
-
50
-    /**
51
-     * This property allows cpt classes to define multiple routes as cpt routes.
52
-     * //in this array we define what the custom post type for this route is.
53
-     * array(
54
-     * 'route_name' => 'custom_post_type_slug'
55
-     * )
56
-     *
57
-     * @var array
58
-     */
59
-    protected $_cpt_routes = array();
60
-
31
+	/**
32
+	 * This gets set in _setup_cpt
33
+	 * It will contain the object for the custom post type.
34
+	 *
35
+	 * @var EE_CPT_Base
36
+	 */
37
+	protected $_cpt_object;
38
+
39
+
40
+
41
+	/**
42
+	 * a boolean flag to set whether the current route is a cpt route or not.
43
+	 *
44
+	 * @var bool
45
+	 */
46
+	protected $_cpt_route = false;
47
+
48
+
49
+
50
+	/**
51
+	 * This property allows cpt classes to define multiple routes as cpt routes.
52
+	 * //in this array we define what the custom post type for this route is.
53
+	 * array(
54
+	 * 'route_name' => 'custom_post_type_slug'
55
+	 * )
56
+	 *
57
+	 * @var array
58
+	 */
59
+	protected $_cpt_routes = array();
60
+
61 61
 
62 62
 
63
-    /**
64
-     * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
65
-     * in this format:
66
-     * array(
67
-     * 'post_type_slug' => 'edit_route'
68
-     * )
69
-     *
70
-     * @var array
71
-     */
72
-    protected $_cpt_edit_routes = array();
73
-
74
-
75
-
76
-    /**
77
-     * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
78
-     * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
79
-     * _cpt_model_names property should be in the following format: array(
80
-     * 'route_defined_by_action_param' => 'Model_Name')
81
-     *
82
-     * @var array $_cpt_model_names
83
-     */
84
-    protected $_cpt_model_names = array();
85
-
86
-
87
-    /**
88
-     * @var EE_CPT_Base
89
-     */
90
-    protected $_cpt_model_obj = false;
91
-
92
-
93
-
94
-    /**
95
-     * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
96
-     * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
97
-     * the _register_autosave_containers() method so that we don't override any other containers already registered.
98
-     * Registration of containers should be done before load_page_dependencies() is run.
99
-     *
100
-     * @var array()
101
-     */
102
-    protected $_autosave_containers = array();
103
-    protected $_autosave_fields = array();
104
-
105
-    /**
106
-     * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
107
-     * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
108
-     *
109
-     * @var array
110
-     */
111
-    protected $_pagenow_map;
112
-
113
-
114
-
115
-    /**
116
-     * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
117
-     * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
118
-     * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
119
-     * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
120
-     * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
121
-     *
122
-     * @access protected
123
-     * @abstract
124
-     * @param  string $post_id The ID of the cpt that was saved (so you can link relationally)
125
-     * @param  EE_CPT_Base $post    The post object of the cpt that was saved.
126
-     * @return void
127
-     */
128
-    abstract protected function _insert_update_cpt_item($post_id, $post);
129
-
130
-
131
-
132
-    /**
133
-     * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
134
-     *
135
-     * @abstract
136
-     * @access public
137
-     * @param  string $post_id The ID of the cpt that was trashed
138
-     * @return void
139
-     */
140
-    abstract public function trash_cpt_item($post_id);
141
-
142
-
143
-
144
-    /**
145
-     * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
146
-     *
147
-     * @param  string $post_id theID of the cpt that was untrashed
148
-     * @return void
149
-     */
150
-    abstract public function restore_cpt_item($post_id);
151
-
152
-
153
-
154
-    /**
155
-     * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
156
-     * from the db
157
-     *
158
-     * @param  string $post_id the ID of the cpt that was deleted
159
-     * @return void
160
-     */
161
-    abstract public function delete_cpt_item($post_id);
162
-
163
-
164
-
165
-    /**
166
-     * Just utilizing the method EE_Admin exposes for doing things before page setup.
167
-     *
168
-     * @access protected
169
-     * @return void
170
-     */
171
-    protected function _before_page_setup()
172
-    {
173
-        $page = isset($this->_req_data['page']) ? $this->_req_data['page'] : $this->page_slug;
174
-        $this->_cpt_routes = array_merge(array(
175
-            'create_new' => $this->page_slug,
176
-            'edit'       => $this->page_slug,
177
-            'trash'      => $this->page_slug,
178
-        ), $this->_cpt_routes);
179
-        //let's see if the current route has a value for cpt_object_slug if it does we use that instead of the page
180
-        $this->_cpt_object = isset($this->_req_data['action']) && isset($this->_cpt_routes[$this->_req_data['action']])
181
-            ? get_post_type_object($this->_cpt_routes[$this->_req_data['action']])
182
-            : get_post_type_object($page);
183
-        //tweak pagenow for page loading.
184
-        if ( ! $this->_pagenow_map) {
185
-            $this->_pagenow_map = array(
186
-                'create_new' => 'post-new.php',
187
-                'edit'       => 'post.php',
188
-                'trash'      => 'post.php',
189
-            );
190
-        }
191
-        add_action('current_screen', array($this, 'modify_pagenow'));
192
-        //TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
193
-        //get current page from autosave
194
-        $current_page = isset($this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page'])
195
-            ? $this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page']
196
-            : null;
197
-        $this->_current_page = isset($this->_req_data['current_page'])
198
-            ? $this->_req_data['current_page']
199
-            : $current_page;
200
-        //autosave... make sure its only for the correct page
201
-        //if ( ! empty($this->_current_page) && $this->_current_page == $this->page_slug) {
202
-            //setup autosave ajax hook
203
-            //add_action('wp_ajax_ee-autosave', array( $this, 'do_extra_autosave_stuff' ), 10 ); //TODO reactivate when 4.2 autosave is implemented
204
-        //}
205
-    }
206
-
207
-
208
-
209
-    /**
210
-     * Simply ensure that we simulate the correct post route for cpt screens
211
-     *
212
-     * @param WP_Screen $current_screen
213
-     * @return void
214
-     */
215
-    public function modify_pagenow($current_screen)
216
-    {
217
-        global $pagenow, $hook_suffix;
218
-        //possibly reset pagenow.
219
-        if ( ! empty($this->_req_data['page'])
220
-             && $this->_req_data['page'] == $this->page_slug
221
-             && ! empty($this->_req_data['action'])
222
-             && isset($this->_pagenow_map[$this->_req_data['action']])
223
-        ) {
224
-            $pagenow = $this->_pagenow_map[$this->_req_data['action']];
225
-            $hook_suffix = $pagenow;
226
-        }
227
-    }
228
-
229
-
230
-
231
-    /**
232
-     * This method is used to register additional autosave containers to the _autosave_containers property.
233
-     *
234
-     * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
235
-     *       automatically register the id for the post metabox as a container.
236
-     * @param  array $ids an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
237
-     *                    you would send along the id of a metabox container.
238
-     * @return void
239
-     */
240
-    protected function _register_autosave_containers($ids)
241
-    {
242
-        $this->_autosave_containers = array_merge($this->_autosave_fields, (array)$ids);
243
-    }
244
-
245
-
246
-
247
-    /**
248
-     * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
249
-     * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
250
-     */
251
-    protected function _set_autosave_containers()
252
-    {
253
-        global $wp_meta_boxes;
254
-        $containers = array();
255
-        if (empty($wp_meta_boxes)) {
256
-            return;
257
-        }
258
-        $current_metaboxes = isset($wp_meta_boxes[$this->page_slug]) ? $wp_meta_boxes[$this->page_slug] : array();
259
-        foreach ($current_metaboxes as $box_context) {
260
-            foreach ($box_context as $box_details) {
261
-                foreach ($box_details as $box) {
262
-                    if (
263
-                        is_array($box['callback'])
264
-                        && (
265
-                            $box['callback'][0] instanceof EE_Admin_Page
266
-                            || $box['callback'][0] instanceof EE_Admin_Hooks
267
-                        )
268
-                    ) {
269
-                        $containers[] = $box['id'];
270
-                    }
271
-                }
272
-            }
273
-        }
274
-        $this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
275
-        //add hidden inputs container
276
-        $this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
277
-    }
278
-
279
-
280
-
281
-    protected function _load_autosave_scripts_styles()
282
-    {
283
-        /*wp_register_script('cpt-autosave', EE_ADMIN_URL . 'assets/ee-cpt-autosave.js', array('ee-serialize-full-array', 'event_editor_js'), EVENT_ESPRESSO_VERSION, TRUE );
63
+	/**
64
+	 * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
65
+	 * in this format:
66
+	 * array(
67
+	 * 'post_type_slug' => 'edit_route'
68
+	 * )
69
+	 *
70
+	 * @var array
71
+	 */
72
+	protected $_cpt_edit_routes = array();
73
+
74
+
75
+
76
+	/**
77
+	 * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
78
+	 * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
79
+	 * _cpt_model_names property should be in the following format: array(
80
+	 * 'route_defined_by_action_param' => 'Model_Name')
81
+	 *
82
+	 * @var array $_cpt_model_names
83
+	 */
84
+	protected $_cpt_model_names = array();
85
+
86
+
87
+	/**
88
+	 * @var EE_CPT_Base
89
+	 */
90
+	protected $_cpt_model_obj = false;
91
+
92
+
93
+
94
+	/**
95
+	 * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
96
+	 * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
97
+	 * the _register_autosave_containers() method so that we don't override any other containers already registered.
98
+	 * Registration of containers should be done before load_page_dependencies() is run.
99
+	 *
100
+	 * @var array()
101
+	 */
102
+	protected $_autosave_containers = array();
103
+	protected $_autosave_fields = array();
104
+
105
+	/**
106
+	 * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
107
+	 * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
108
+	 *
109
+	 * @var array
110
+	 */
111
+	protected $_pagenow_map;
112
+
113
+
114
+
115
+	/**
116
+	 * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
117
+	 * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
118
+	 * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
119
+	 * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
120
+	 * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
121
+	 *
122
+	 * @access protected
123
+	 * @abstract
124
+	 * @param  string $post_id The ID of the cpt that was saved (so you can link relationally)
125
+	 * @param  EE_CPT_Base $post    The post object of the cpt that was saved.
126
+	 * @return void
127
+	 */
128
+	abstract protected function _insert_update_cpt_item($post_id, $post);
129
+
130
+
131
+
132
+	/**
133
+	 * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
134
+	 *
135
+	 * @abstract
136
+	 * @access public
137
+	 * @param  string $post_id The ID of the cpt that was trashed
138
+	 * @return void
139
+	 */
140
+	abstract public function trash_cpt_item($post_id);
141
+
142
+
143
+
144
+	/**
145
+	 * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
146
+	 *
147
+	 * @param  string $post_id theID of the cpt that was untrashed
148
+	 * @return void
149
+	 */
150
+	abstract public function restore_cpt_item($post_id);
151
+
152
+
153
+
154
+	/**
155
+	 * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
156
+	 * from the db
157
+	 *
158
+	 * @param  string $post_id the ID of the cpt that was deleted
159
+	 * @return void
160
+	 */
161
+	abstract public function delete_cpt_item($post_id);
162
+
163
+
164
+
165
+	/**
166
+	 * Just utilizing the method EE_Admin exposes for doing things before page setup.
167
+	 *
168
+	 * @access protected
169
+	 * @return void
170
+	 */
171
+	protected function _before_page_setup()
172
+	{
173
+		$page = isset($this->_req_data['page']) ? $this->_req_data['page'] : $this->page_slug;
174
+		$this->_cpt_routes = array_merge(array(
175
+			'create_new' => $this->page_slug,
176
+			'edit'       => $this->page_slug,
177
+			'trash'      => $this->page_slug,
178
+		), $this->_cpt_routes);
179
+		//let's see if the current route has a value for cpt_object_slug if it does we use that instead of the page
180
+		$this->_cpt_object = isset($this->_req_data['action']) && isset($this->_cpt_routes[$this->_req_data['action']])
181
+			? get_post_type_object($this->_cpt_routes[$this->_req_data['action']])
182
+			: get_post_type_object($page);
183
+		//tweak pagenow for page loading.
184
+		if ( ! $this->_pagenow_map) {
185
+			$this->_pagenow_map = array(
186
+				'create_new' => 'post-new.php',
187
+				'edit'       => 'post.php',
188
+				'trash'      => 'post.php',
189
+			);
190
+		}
191
+		add_action('current_screen', array($this, 'modify_pagenow'));
192
+		//TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
193
+		//get current page from autosave
194
+		$current_page = isset($this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page'])
195
+			? $this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page']
196
+			: null;
197
+		$this->_current_page = isset($this->_req_data['current_page'])
198
+			? $this->_req_data['current_page']
199
+			: $current_page;
200
+		//autosave... make sure its only for the correct page
201
+		//if ( ! empty($this->_current_page) && $this->_current_page == $this->page_slug) {
202
+			//setup autosave ajax hook
203
+			//add_action('wp_ajax_ee-autosave', array( $this, 'do_extra_autosave_stuff' ), 10 ); //TODO reactivate when 4.2 autosave is implemented
204
+		//}
205
+	}
206
+
207
+
208
+
209
+	/**
210
+	 * Simply ensure that we simulate the correct post route for cpt screens
211
+	 *
212
+	 * @param WP_Screen $current_screen
213
+	 * @return void
214
+	 */
215
+	public function modify_pagenow($current_screen)
216
+	{
217
+		global $pagenow, $hook_suffix;
218
+		//possibly reset pagenow.
219
+		if ( ! empty($this->_req_data['page'])
220
+			 && $this->_req_data['page'] == $this->page_slug
221
+			 && ! empty($this->_req_data['action'])
222
+			 && isset($this->_pagenow_map[$this->_req_data['action']])
223
+		) {
224
+			$pagenow = $this->_pagenow_map[$this->_req_data['action']];
225
+			$hook_suffix = $pagenow;
226
+		}
227
+	}
228
+
229
+
230
+
231
+	/**
232
+	 * This method is used to register additional autosave containers to the _autosave_containers property.
233
+	 *
234
+	 * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
235
+	 *       automatically register the id for the post metabox as a container.
236
+	 * @param  array $ids an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
237
+	 *                    you would send along the id of a metabox container.
238
+	 * @return void
239
+	 */
240
+	protected function _register_autosave_containers($ids)
241
+	{
242
+		$this->_autosave_containers = array_merge($this->_autosave_fields, (array)$ids);
243
+	}
244
+
245
+
246
+
247
+	/**
248
+	 * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
249
+	 * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
250
+	 */
251
+	protected function _set_autosave_containers()
252
+	{
253
+		global $wp_meta_boxes;
254
+		$containers = array();
255
+		if (empty($wp_meta_boxes)) {
256
+			return;
257
+		}
258
+		$current_metaboxes = isset($wp_meta_boxes[$this->page_slug]) ? $wp_meta_boxes[$this->page_slug] : array();
259
+		foreach ($current_metaboxes as $box_context) {
260
+			foreach ($box_context as $box_details) {
261
+				foreach ($box_details as $box) {
262
+					if (
263
+						is_array($box['callback'])
264
+						&& (
265
+							$box['callback'][0] instanceof EE_Admin_Page
266
+							|| $box['callback'][0] instanceof EE_Admin_Hooks
267
+						)
268
+					) {
269
+						$containers[] = $box['id'];
270
+					}
271
+				}
272
+			}
273
+		}
274
+		$this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
275
+		//add hidden inputs container
276
+		$this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
277
+	}
278
+
279
+
280
+
281
+	protected function _load_autosave_scripts_styles()
282
+	{
283
+		/*wp_register_script('cpt-autosave', EE_ADMIN_URL . 'assets/ee-cpt-autosave.js', array('ee-serialize-full-array', 'event_editor_js'), EVENT_ESPRESSO_VERSION, TRUE );
284 284
         wp_enqueue_script('cpt-autosave');/**/ //todo re-enable when we start doing autosave again in 4.2
285 285
 
286
-        //filter _autosave_containers
287
-        $containers = apply_filters('FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
288
-            $this->_autosave_containers, $this);
289
-        $containers = apply_filters('FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
290
-            $containers, $this);
291
-
292
-        wp_localize_script('event_editor_js', 'EE_AUTOSAVE_IDS',
293
-            $containers); //todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
294
-
295
-        $unsaved_data_msg = array(
296
-            'eventmsg'     => sprintf(__("The changes you made to this %s will be lost if you navigate away from this page.",
297
-                'event_espresso'), $this->_cpt_object->labels->singular_name),
298
-            'inputChanged' => 0,
299
-        );
300
-        wp_localize_script('event_editor_js', 'UNSAVED_DATA_MSG', $unsaved_data_msg);
301
-    }
302
-
303
-
304
-
305
-    public function load_page_dependencies()
306
-    {
307
-        try {
308
-            $this->_load_page_dependencies();
309
-        } catch (EE_Error $e) {
310
-            $e->get_error();
311
-        }
312
-    }
313
-
314
-
315
-
316
-    /**
317
-     * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
318
-     *
319
-     * @access protected
320
-     * @return void
321
-     */
322
-    protected function _load_page_dependencies()
323
-    {
324
-        //we only add stuff if this is a cpt_route!
325
-        if ( ! $this->_cpt_route) {
326
-            parent::_load_page_dependencies();
327
-            return;
328
-        }
329
-        // now let's do some automatic filters into the wp_system
330
-        // and we'll check to make sure the CHILD class
331
-        // automatically has the required methods in place.
332
-        // the following filters are for setting all the redirects
333
-        // on DEFAULT WP custom post type actions
334
-        // let's add a hidden input to the post-edit form
335
-        // so we know when we have to trigger our custom redirects!
336
-        // Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
337
-        add_action('edit_form_after_title', array($this, 'cpt_post_form_hidden_input'));
338
-        // inject our Admin page nav tabs...
339
-        // let's make sure the nav tabs are set if they aren't already
340
-        // if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
341
-        add_action('post_edit_form_tag', array($this, 'inject_nav_tabs'));
342
-        // modify the post_updated messages array
343
-        add_action('post_updated_messages', array($this, 'post_update_messages'), 10);
344
-        // add shortlink button to cpt edit screens.  We can do this as a universal thing BECAUSE,
345
-        // cpts use the same format for shortlinks as posts!
346
-        add_filter('pre_get_shortlink', array($this, 'add_shortlink_button_to_editor'), 10, 4);
347
-        // This basically allows us to change the title of the "publish" metabox area
348
-        // on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
349
-        if ( ! empty($this->_labels['publishbox'])) {
350
-            $box_label = is_array($this->_labels['publishbox'])
351
-                         && isset($this->_labels['publishbox'][$this->_req_action])
352
-                    ? $this->_labels['publishbox'][$this->_req_action]
353
-                    : $this->_labels['publishbox'];
354
-            add_meta_box(
355
-                'submitdiv',
356
-                $box_label,
357
-                'post_submit_meta_box',
358
-                $this->_cpt_routes[$this->_req_action],
359
-                'side',
360
-                'core'
361
-            );
362
-        }
363
-        //let's add page_templates metabox if this cpt added support for it.
364
-        if ($this->_supports_page_templates($this->_cpt_object->name)) {
365
-            add_meta_box(
366
-                'page_templates',
367
-                __('Page Template', 'event_espresso'),
368
-                array($this, 'page_template_meta_box'),
369
-                $this->_cpt_routes[$this->_req_action],
370
-                'side',
371
-                'default'
372
-            );
373
-        }
374
-        //this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
375
-        if (method_exists($this, 'extra_permalink_field_buttons')) {
376
-            add_filter('get_sample_permalink_html', array($this, 'extra_permalink_field_buttons'), 10, 4);
377
-        }
378
-        //add preview button
379
-        add_filter('get_sample_permalink_html', array($this, 'preview_button_html'), 5, 4);
380
-        //insert our own post_stati dropdown
381
-        add_action('post_submitbox_misc_actions', array($this, 'custom_post_stati_dropdown'), 10);
382
-        //This allows adding additional information to the publish post submitbox on the wp post edit form
383
-        if (method_exists($this, 'extra_misc_actions_publish_box')) {
384
-            add_action('post_submitbox_misc_actions', array($this, 'extra_misc_actions_publish_box'), 10);
385
-        }
386
-        // This allows for adding additional stuff after the title field on the wp post edit form.
387
-        // This is also before the wp_editor for post description field.
388
-        if (method_exists($this, 'edit_form_after_title')) {
389
-            add_action('edit_form_after_title', array($this, 'edit_form_after_title'), 10);
390
-        }
391
-        /**
392
-         * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
393
-         */
394
-        add_filter('clean_url', array($this, 'switch_core_wp_urls_with_ours'), 10, 3);
395
-        parent::_load_page_dependencies();
396
-        // notice we are ALSO going to load the pagenow hook set for this route
397
-        // (see _before_page_setup for the reset of the pagenow global ).
398
-        // This is for any plugins that are doing things properly
399
-        // and hooking into the load page hook for core wp cpt routes.
400
-        global $pagenow;
401
-        do_action('load-' . $pagenow);
402
-        $this->modify_current_screen();
403
-        add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
404
-        //we route REALLY early.
405
-        try {
406
-            $this->_route_admin_request();
407
-        } catch (EE_Error $e) {
408
-            $e->get_error();
409
-        }
410
-    }
411
-
412
-
413
-
414
-    /**
415
-     * Since we don't want users going to default core wp routes, this will check any wp urls run through the
416
-     * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
417
-     * route instead.
418
-     *
419
-     * @param string $good_protocol_url The escaped url.
420
-     * @param string $original_url      The original url.
421
-     * @param string $_context          The context sent to the esc_url method.
422
-     * @return string possibly a new url for our route.
423
-     */
424
-    public function switch_core_wp_urls_with_ours($good_protocol_url, $original_url, $_context)
425
-    {
426
-        $routes_to_match = array(
427
-            0 => array(
428
-                'edit.php?post_type=espresso_attendees',
429
-                'admin.php?page=espresso_registrations&action=contact_list',
430
-            ),
431
-            1 => array(
432
-                'edit.php?post_type=' . $this->_cpt_object->name,
433
-                'admin.php?page=' . $this->_cpt_object->name,
434
-            ),
435
-        );
436
-        foreach ($routes_to_match as $route_matches) {
437
-            if (strpos($good_protocol_url, $route_matches[0]) !== false) {
438
-                return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
439
-            }
440
-        }
441
-        return $good_protocol_url;
442
-    }
443
-
444
-
445
-
446
-    /**
447
-     * Determine whether the current cpt supports page templates or not.
448
-     *
449
-     * @since %VER%
450
-     * @param string $cpt_name The cpt slug we're checking on.
451
-     * @return bool True supported, false not.
452
-     */
453
-    private function _supports_page_templates($cpt_name)
454
-    {
455
-
456
-        $cpt_args = EE_Register_CPTs::get_CPTs();
457
-        $cpt_args = isset($cpt_args[$cpt_name]) ? $cpt_args[$cpt_name]['args'] : array();
458
-        $cpt_has_support = ! empty($cpt_args['page_templates']);
459
-
460
-        //if the installed version of WP is > 4.7 we do some additional checks.
461
-        if (RecommendedVersions::compareWordPressVersion('4.7','>=')) {
462
-            $post_templates = wp_get_theme()->get_post_templates();
463
-            //if there are $post_templates for this cpt, then we return false for this method because
464
-            //that means we aren't going to load our page template manager and leave that up to the native
465
-            //cpt template manager.
466
-            $cpt_has_support = ! isset($post_templates[$cpt_name]) ? $cpt_has_support : false;
467
-        }
468
-
469
-        return $cpt_has_support;
470
-    }
471
-
472
-
473
-    /**
474
-     * Callback for the page_templates metabox selector.
475
-     *
476
-     * @since %VER%
477
-     * @return void
478
-     */
479
-    public function page_template_meta_box()
480
-    {
481
-        global $post;
482
-        $template = '';
483
-
484
-        if (RecommendedVersions::compareWordPressVersion('4.7','>=')) {
485
-            $page_template_count = count(get_page_templates());
486
-        } else {
487
-            $page_template_count = count(get_page_templates($post));
488
-        };
489
-
490
-        if ($page_template_count) {
491
-            $page_template = get_post_meta($post->ID, '_wp_page_template', true);
492
-            $template      = ! empty($page_template) ? $page_template : '';
493
-        }
494
-        ?>
286
+		//filter _autosave_containers
287
+		$containers = apply_filters('FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
288
+			$this->_autosave_containers, $this);
289
+		$containers = apply_filters('FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
290
+			$containers, $this);
291
+
292
+		wp_localize_script('event_editor_js', 'EE_AUTOSAVE_IDS',
293
+			$containers); //todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
294
+
295
+		$unsaved_data_msg = array(
296
+			'eventmsg'     => sprintf(__("The changes you made to this %s will be lost if you navigate away from this page.",
297
+				'event_espresso'), $this->_cpt_object->labels->singular_name),
298
+			'inputChanged' => 0,
299
+		);
300
+		wp_localize_script('event_editor_js', 'UNSAVED_DATA_MSG', $unsaved_data_msg);
301
+	}
302
+
303
+
304
+
305
+	public function load_page_dependencies()
306
+	{
307
+		try {
308
+			$this->_load_page_dependencies();
309
+		} catch (EE_Error $e) {
310
+			$e->get_error();
311
+		}
312
+	}
313
+
314
+
315
+
316
+	/**
317
+	 * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
318
+	 *
319
+	 * @access protected
320
+	 * @return void
321
+	 */
322
+	protected function _load_page_dependencies()
323
+	{
324
+		//we only add stuff if this is a cpt_route!
325
+		if ( ! $this->_cpt_route) {
326
+			parent::_load_page_dependencies();
327
+			return;
328
+		}
329
+		// now let's do some automatic filters into the wp_system
330
+		// and we'll check to make sure the CHILD class
331
+		// automatically has the required methods in place.
332
+		// the following filters are for setting all the redirects
333
+		// on DEFAULT WP custom post type actions
334
+		// let's add a hidden input to the post-edit form
335
+		// so we know when we have to trigger our custom redirects!
336
+		// Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
337
+		add_action('edit_form_after_title', array($this, 'cpt_post_form_hidden_input'));
338
+		// inject our Admin page nav tabs...
339
+		// let's make sure the nav tabs are set if they aren't already
340
+		// if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
341
+		add_action('post_edit_form_tag', array($this, 'inject_nav_tabs'));
342
+		// modify the post_updated messages array
343
+		add_action('post_updated_messages', array($this, 'post_update_messages'), 10);
344
+		// add shortlink button to cpt edit screens.  We can do this as a universal thing BECAUSE,
345
+		// cpts use the same format for shortlinks as posts!
346
+		add_filter('pre_get_shortlink', array($this, 'add_shortlink_button_to_editor'), 10, 4);
347
+		// This basically allows us to change the title of the "publish" metabox area
348
+		// on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
349
+		if ( ! empty($this->_labels['publishbox'])) {
350
+			$box_label = is_array($this->_labels['publishbox'])
351
+						 && isset($this->_labels['publishbox'][$this->_req_action])
352
+					? $this->_labels['publishbox'][$this->_req_action]
353
+					: $this->_labels['publishbox'];
354
+			add_meta_box(
355
+				'submitdiv',
356
+				$box_label,
357
+				'post_submit_meta_box',
358
+				$this->_cpt_routes[$this->_req_action],
359
+				'side',
360
+				'core'
361
+			);
362
+		}
363
+		//let's add page_templates metabox if this cpt added support for it.
364
+		if ($this->_supports_page_templates($this->_cpt_object->name)) {
365
+			add_meta_box(
366
+				'page_templates',
367
+				__('Page Template', 'event_espresso'),
368
+				array($this, 'page_template_meta_box'),
369
+				$this->_cpt_routes[$this->_req_action],
370
+				'side',
371
+				'default'
372
+			);
373
+		}
374
+		//this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
375
+		if (method_exists($this, 'extra_permalink_field_buttons')) {
376
+			add_filter('get_sample_permalink_html', array($this, 'extra_permalink_field_buttons'), 10, 4);
377
+		}
378
+		//add preview button
379
+		add_filter('get_sample_permalink_html', array($this, 'preview_button_html'), 5, 4);
380
+		//insert our own post_stati dropdown
381
+		add_action('post_submitbox_misc_actions', array($this, 'custom_post_stati_dropdown'), 10);
382
+		//This allows adding additional information to the publish post submitbox on the wp post edit form
383
+		if (method_exists($this, 'extra_misc_actions_publish_box')) {
384
+			add_action('post_submitbox_misc_actions', array($this, 'extra_misc_actions_publish_box'), 10);
385
+		}
386
+		// This allows for adding additional stuff after the title field on the wp post edit form.
387
+		// This is also before the wp_editor for post description field.
388
+		if (method_exists($this, 'edit_form_after_title')) {
389
+			add_action('edit_form_after_title', array($this, 'edit_form_after_title'), 10);
390
+		}
391
+		/**
392
+		 * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
393
+		 */
394
+		add_filter('clean_url', array($this, 'switch_core_wp_urls_with_ours'), 10, 3);
395
+		parent::_load_page_dependencies();
396
+		// notice we are ALSO going to load the pagenow hook set for this route
397
+		// (see _before_page_setup for the reset of the pagenow global ).
398
+		// This is for any plugins that are doing things properly
399
+		// and hooking into the load page hook for core wp cpt routes.
400
+		global $pagenow;
401
+		do_action('load-' . $pagenow);
402
+		$this->modify_current_screen();
403
+		add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
404
+		//we route REALLY early.
405
+		try {
406
+			$this->_route_admin_request();
407
+		} catch (EE_Error $e) {
408
+			$e->get_error();
409
+		}
410
+	}
411
+
412
+
413
+
414
+	/**
415
+	 * Since we don't want users going to default core wp routes, this will check any wp urls run through the
416
+	 * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
417
+	 * route instead.
418
+	 *
419
+	 * @param string $good_protocol_url The escaped url.
420
+	 * @param string $original_url      The original url.
421
+	 * @param string $_context          The context sent to the esc_url method.
422
+	 * @return string possibly a new url for our route.
423
+	 */
424
+	public function switch_core_wp_urls_with_ours($good_protocol_url, $original_url, $_context)
425
+	{
426
+		$routes_to_match = array(
427
+			0 => array(
428
+				'edit.php?post_type=espresso_attendees',
429
+				'admin.php?page=espresso_registrations&action=contact_list',
430
+			),
431
+			1 => array(
432
+				'edit.php?post_type=' . $this->_cpt_object->name,
433
+				'admin.php?page=' . $this->_cpt_object->name,
434
+			),
435
+		);
436
+		foreach ($routes_to_match as $route_matches) {
437
+			if (strpos($good_protocol_url, $route_matches[0]) !== false) {
438
+				return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
439
+			}
440
+		}
441
+		return $good_protocol_url;
442
+	}
443
+
444
+
445
+
446
+	/**
447
+	 * Determine whether the current cpt supports page templates or not.
448
+	 *
449
+	 * @since %VER%
450
+	 * @param string $cpt_name The cpt slug we're checking on.
451
+	 * @return bool True supported, false not.
452
+	 */
453
+	private function _supports_page_templates($cpt_name)
454
+	{
455
+
456
+		$cpt_args = EE_Register_CPTs::get_CPTs();
457
+		$cpt_args = isset($cpt_args[$cpt_name]) ? $cpt_args[$cpt_name]['args'] : array();
458
+		$cpt_has_support = ! empty($cpt_args['page_templates']);
459
+
460
+		//if the installed version of WP is > 4.7 we do some additional checks.
461
+		if (RecommendedVersions::compareWordPressVersion('4.7','>=')) {
462
+			$post_templates = wp_get_theme()->get_post_templates();
463
+			//if there are $post_templates for this cpt, then we return false for this method because
464
+			//that means we aren't going to load our page template manager and leave that up to the native
465
+			//cpt template manager.
466
+			$cpt_has_support = ! isset($post_templates[$cpt_name]) ? $cpt_has_support : false;
467
+		}
468
+
469
+		return $cpt_has_support;
470
+	}
471
+
472
+
473
+	/**
474
+	 * Callback for the page_templates metabox selector.
475
+	 *
476
+	 * @since %VER%
477
+	 * @return void
478
+	 */
479
+	public function page_template_meta_box()
480
+	{
481
+		global $post;
482
+		$template = '';
483
+
484
+		if (RecommendedVersions::compareWordPressVersion('4.7','>=')) {
485
+			$page_template_count = count(get_page_templates());
486
+		} else {
487
+			$page_template_count = count(get_page_templates($post));
488
+		};
489
+
490
+		if ($page_template_count) {
491
+			$page_template = get_post_meta($post->ID, '_wp_page_template', true);
492
+			$template      = ! empty($page_template) ? $page_template : '';
493
+		}
494
+		?>
495 495
         <p><strong><?php _e('Template') ?></strong></p>
496 496
         <label class="screen-reader-text" for="page_template"><?php _e('Page Template') ?></label><select
497 497
             name="page_template" id="page_template">
@@ -499,450 +499,450 @@  discard block
 block discarded – undo
499 499
         <?php page_template_dropdown($template); ?>
500 500
     </select>
501 501
         <?php
502
-    }
503
-
504
-
505
-
506
-    /**
507
-     * if this post is a draft or scheduled post then we provide a preview button for user to click
508
-     * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
509
-     *
510
-     * @param  string $return    the current html
511
-     * @param  int    $id        the post id for the page
512
-     * @param  string $new_title What the title is
513
-     * @param  string $new_slug  what the slug is
514
-     * @return string            The new html string for the permalink area
515
-     */
516
-    public function preview_button_html($return, $id, $new_title, $new_slug)
517
-    {
518
-        $post = get_post($id);
519
-        if ('publish' !== get_post_status($post)) {
520
-            //include shims for the `get_preview_post_link` function
521
-            require_once( EE_CORE . 'wordpress-shims.php' );
522
-            $return .= '<span_id="view-post-btn"><a target="_blank" href="'
523
-                       . get_preview_post_link($id)
524
-                       . '" class="button button-small">'
525
-                       . __('Preview', 'event_espresso')
526
-                       . '</a></span>'
527
-                       . "\n";
528
-        }
529
-        return $return;
530
-    }
531
-
532
-
533
-
534
-    /**
535
-     * add our custom post stati dropdown on the wp post page for this cpt
536
-     *
537
-     * @return void
538
-     */
539
-    public function custom_post_stati_dropdown()
540
-    {
541
-
542
-        $statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
543
-        $cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
544
-            ? $statuses[$this->_cpt_model_obj->status()]
545
-            : '';
546
-        $template_args    = array(
547
-            'cur_status'            => $this->_cpt_model_obj->status(),
548
-            'statuses'              => $statuses,
549
-            'cur_status_label'      => $cur_status_label,
550
-            'localized_status_save' => sprintf(__('Save %s', 'event_espresso'), $cur_status_label),
551
-        );
552
-        //we'll add a trash post status (WP doesn't add one for some reason)
553
-        if ($this->_cpt_model_obj->status() === 'trash') {
554
-            $template_args['cur_status_label'] = __('Trashed', 'event_espresso');
555
-            $statuses['trash']                 = __('Trashed', 'event_espresso');
556
-            $template_args['statuses']         = $statuses;
557
-        }
558
-
559
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
560
-        EEH_Template::display_template($template, $template_args);
561
-    }
562
-
563
-
564
-
565
-    public function setup_autosave_hooks()
566
-    {
567
-        $this->_set_autosave_containers();
568
-        $this->_load_autosave_scripts_styles();
569
-    }
570
-
571
-
572
-
573
-    /**
574
-     * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a $_POST object (available
575
-     * in $this->_req_data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
576
-     * for the nonce in here, but then this method looks for two things:
577
-     * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
578
-     * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
579
-     * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
580
-     * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
581
-     * template args.
582
-     *    1. $template_args['error'] = IF there is an error you can add the message in here.
583
-     *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
584
-     *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
585
-     *    $this->_template_args['data']['items'] = array(
586
-     *        'event-datetime-ids' => '1,2,3';
587
-     *    );
588
-     *    Keep in mind the following things:
589
-     *    - "where" index is for the input with the id as that string.
590
-     *    - "what" index is what will be used for the value of that input.
591
-     *
592
-     * @return void
593
-     */
594
-    public function do_extra_autosave_stuff()
595
-    {
596
-        //next let's check for the autosave nonce (we'll use _verify_nonce )
597
-        $nonce = isset($this->_req_data['autosavenonce'])
598
-                ? $this->_req_data['autosavenonce']
599
-                : null;
600
-        $this->_verify_nonce($nonce, 'autosave');
601
-        //make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
602
-        if ( ! defined('DOING_AUTOSAVE')) {
603
-            define('DOING_AUTOSAVE', true);
604
-        }
605
-        //if we made it here then the nonce checked out.  Let's run our methods and actions
606
-        $autosave = "_ee_autosave_{$this->_current_view}";
607
-        if (method_exists($this, $autosave)) {
608
-            $this->$autosave();
609
-        } else {
610
-            $this->_template_args['success'] = true;
611
-        }
612
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
613
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
614
-        //now let's return json
615
-        $this->_return_json();
616
-    }
617
-
618
-
619
-
620
-    /**
621
-     * This takes care of setting up default routes and pages that utilize the core WP admin pages.
622
-     * Child classes can override the defaults (in cases for adding metaboxes etc.)
623
-     * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
624
-     *
625
-     * @access protected
626
-     * @throws EE_Error
627
-     * @return void
628
-     */
629
-    protected function _extend_page_config_for_cpt()
630
-    {
631
-        //before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
632
-        if (isset($this->_req_data['page']) && $this->_req_data['page'] !== $this->page_slug) {
633
-            return;
634
-        }
635
-        //set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
636
-        if ( ! empty($this->_cpt_object)) {
637
-            $this->_page_routes = array_merge(array(
638
-                'create_new' => '_create_new_cpt_item',
639
-                'edit'       => '_edit_cpt_item',
640
-            ), $this->_page_routes);
641
-            $this->_page_config = array_merge(array(
642
-                'create_new' => array(
643
-                    'nav'           => array(
644
-                        'label' => $this->_cpt_object->labels->add_new_item,
645
-                        'order' => 5,
646
-                    ),
647
-                    'require_nonce' => false,
648
-                ),
649
-                'edit'       => array(
650
-                    'nav'           => array(
651
-                        'label'      => $this->_cpt_object->labels->edit_item,
652
-                        'order'      => 5,
653
-                        'persistent' => false,
654
-                        'url'        => '',
655
-                    ),
656
-                    'require_nonce' => false,
657
-                ),
658
-            ),
659
-                $this->_page_config
660
-            );
661
-        }
662
-        //load the next section only if this is a matching cpt route as set in the cpt routes array.
663
-        if ( ! isset($this->_cpt_routes[$this->_req_action])) {
664
-            return;
665
-        }
666
-        $this->_cpt_route = isset($this->_cpt_routes[$this->_req_action]) ? true : false;
667
-        //add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
668
-        if (empty($this->_cpt_object)) {
669
-            $msg = sprintf(__('This page has been set as being related to a registered custom post type, however, the custom post type object could not be retrieved. There are two possible reasons for this:  1. The "%s" does not match a registered post type. or 2. The custom post type is not registered for the "%s" action as indexed in the "$_cpt_routes" property on this class (%s).'),
670
-                $this->page_slug, $this->_req_action, get_class($this));
671
-            throw new EE_Error($msg);
672
-        }
673
-        if ($this->_cpt_route) {
674
-            $id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
675
-            $this->_set_model_object($id);
676
-        }
677
-    }
678
-
679
-
680
-
681
-    /**
682
-     * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
683
-     *
684
-     * @access protected
685
-     * @param int  $id The id to retrieve the model object for. If empty we set a default object.
686
-     * @param bool $ignore_route_check
687
-     * @param string $req_type whether the current route is for inserting, updating, or deleting the CPT
688
-     * @throws EE_Error
689
-     */
690
-    protected function _set_model_object($id = null, $ignore_route_check = false, $req_type = '')
691
-    {
692
-        $model = null;
693
-        if (
694
-            empty($this->_cpt_model_names)
695
-            || (
696
-                ! $ignore_route_check
697
-                && ! isset($this->_cpt_routes[$this->_req_action])
698
-            ) || (
699
-                $this->_cpt_model_obj instanceof EE_CPT_Base
700
-                && $this->_cpt_model_obj->ID() === $id
701
-            )
702
-        ) {
703
-            //get out cuz we either don't have a model name OR the object has already been set and it has the same id as what has been sent.
704
-            return;
705
-        }
706
-        //if ignore_route_check is true, then get the model name via EE_Register_CPTs
707
-        if ($ignore_route_check) {
708
-            $model_names = EE_Register_CPTs::get_cpt_model_names();
709
-            $post_type   = get_post_type($id);
710
-            if (isset($model_names[$post_type])) {
711
-                $model = EE_Registry::instance()->load_model($model_names[$post_type]);
712
-            }
713
-        } else {
714
-            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[$this->_req_action]);
715
-        }
716
-        if ($model instanceof EEM_Base) {
717
-            $this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
718
-        }
719
-        do_action(
720
-            'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
721
-            $this->_cpt_model_obj,
722
-            $req_type
723
-        );
724
-    }
725
-
726
-
727
-
728
-    /**
729
-     * admin_init_global
730
-     * This runs all the code that we want executed within the WP admin_init hook.
731
-     * This method executes for ALL EE Admin pages.
732
-     *
733
-     * @access public
734
-     * @return void
735
-     */
736
-    public function admin_init_global()
737
-    {
738
-        $post = isset($this->_req_data['post']) ? get_post($this->_req_data['post']) : null;
739
-        //its possible this is a new save so let's catch that instead
740
-        $post = isset($this->_req_data['post_ID']) ? get_post($this->_req_data['post_ID']) : $post;
741
-        $post_type = $post ? $post->post_type : false;
742
-        $current_route = isset($this->_req_data['current_route'])
743
-            ? $this->_req_data['current_route']
744
-            : 'shouldneverwork';
745
-        $route_to_check = $post_type && isset($this->_cpt_routes[$current_route])
746
-            ? $this->_cpt_routes[$current_route]
747
-            : '';
748
-        add_filter('get_delete_post_link', array($this, 'modify_delete_post_link'), 10, 3);
749
-        add_filter('get_edit_post_link', array($this, 'modify_edit_post_link'), 10, 3);
750
-        if ($post_type === $route_to_check) {
751
-            add_filter('redirect_post_location', array($this, 'cpt_post_location_redirect'), 10, 2);
752
-        }
753
-        //now let's filter redirect if we're on a revision page and the revision is for an event CPT.
754
-        $revision = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
755
-        if ( ! empty($revision)) {
756
-            $action = isset($this->_req_data['action']) ? $this->_req_data['action'] : null;
757
-            //doing a restore?
758
-            if ( ! empty($action) && $action === 'restore') {
759
-                //get post for revision
760
-                $rev_post = get_post($revision);
761
-                $rev_parent = get_post($rev_post->post_parent);
762
-                //only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
763
-                if ($rev_parent && $rev_parent->post_type === $this->page_slug) {
764
-                    add_filter('wp_redirect', array($this, 'revision_redirect'), 10, 2);
765
-                    //restores of revisions
766
-                    add_action('wp_restore_post_revision', array($this, 'restore_revision'), 10, 2);
767
-                }
768
-            }
769
-        }
770
-        //NOTE we ONLY want to run these hooks if we're on the right class for the given post type.  Otherwise we could see some really freaky things happen!
771
-        if ($post_type && $post_type === $route_to_check) {
772
-            //$post_id, $post
773
-            add_action('save_post', array($this, 'insert_update'), 10, 3);
774
-            //$post_id
775
-            add_action('trashed_post', array($this, 'before_trash_cpt_item'), 10);
776
-            add_action('trashed_post', array($this, 'dont_permanently_delete_ee_cpts'), 10);
777
-            add_action('untrashed_post', array($this, 'before_restore_cpt_item'), 10);
778
-            add_action('after_delete_post', array($this, 'before_delete_cpt_item'), 10);
779
-        }
780
-    }
781
-
782
-
783
-
784
-    /**
785
-     * Callback for the WordPress trashed_post hook.
786
-     * Execute some basic checks before calling the trash_cpt_item declared in the child class.
787
-     *
788
-     * @param int $post_id
789
-     * @throws \EE_Error
790
-     */
791
-    public function before_trash_cpt_item($post_id)
792
-    {
793
-        $this->_set_model_object($post_id, true, 'trash');
794
-        //if our cpt object isn't existent then get out immediately.
795
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
796
-            return;
797
-        }
798
-        $this->trash_cpt_item($post_id);
799
-    }
800
-
801
-
802
-
803
-    /**
804
-     * Callback for the WordPress untrashed_post hook.
805
-     * Execute some basic checks before calling the restore_cpt_method in the child class.
806
-     *
807
-     * @param $post_id
808
-     * @throws \EE_Error
809
-     */
810
-    public function before_restore_cpt_item($post_id)
811
-    {
812
-        $this->_set_model_object($post_id, true, 'restore');
813
-        //if our cpt object isn't existent then get out immediately.
814
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
815
-            return;
816
-        }
817
-        $this->restore_cpt_item($post_id);
818
-    }
819
-
820
-
821
-
822
-    /**
823
-     * Callback for the WordPress after_delete_post hook.
824
-     * Execute some basic checks before calling the delete_cpt_item method in the child class.
825
-     *
826
-     * @param $post_id
827
-     * @throws \EE_Error
828
-     */
829
-    public function before_delete_cpt_item($post_id)
830
-    {
831
-        $this->_set_model_object($post_id, true, 'delete');
832
-        //if our cpt object isn't existent then get out immediately.
833
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
834
-            return;
835
-        }
836
-        $this->delete_cpt_item($post_id);
837
-    }
838
-
839
-
840
-
841
-    /**
842
-     * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
843
-     * accordingly.
844
-     *
845
-     * @access public
846
-     * @throws EE_Error
847
-     * @return void
848
-     */
849
-    public function verify_cpt_object()
850
-    {
851
-        $label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
852
-        // verify event object
853
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
854
-            throw new EE_Error(sprintf(__('Something has gone wrong with the page load because we are unable to set up the object for the %1$s.  This usually happens when the given id for the page route is NOT for the correct custom post type for this page',
855
-                    'event_espresso'), $label));
856
-        }
857
-        //if auto-draft then throw an error
858
-        if ($this->_cpt_model_obj->get('status') === 'auto-draft') {
859
-            EE_Error::overwrite_errors();
860
-            EE_Error::add_error(sprintf(__('This %1$s was saved without a title, description, or excerpt which means that none of the extra details you added were saved properly.  All autodrafts will show up in the "draft" view of your event list table.  You can delete them from there. Please click the "Add %1$s" button to refresh and restart.'),
861
-                    $label), __FILE__, __FUNCTION__, __LINE__);
862
-        }
863
-    }
864
-
865
-
866
-
867
-    /**
868
-     * admin_footer_scripts_global
869
-     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
870
-     * will apply on ALL EE_Admin pages.
871
-     *
872
-     * @access public
873
-     * @return void
874
-     */
875
-    public function admin_footer_scripts_global()
876
-    {
877
-        $this->_add_admin_page_ajax_loading_img();
878
-        $this->_add_admin_page_overlay();
879
-    }
880
-
881
-
882
-
883
-    /**
884
-     * add in any global scripts for cpt routes
885
-     *
886
-     * @return void
887
-     */
888
-    public function load_global_scripts_styles()
889
-    {
890
-        parent::load_global_scripts_styles();
891
-        if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
892
-            //setup custom post status object for localize script but only if we've got a cpt object
893
-            $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
894
-            if ( ! empty($statuses)) {
895
-                //get ALL statuses!
896
-                $statuses = $this->_cpt_model_obj->get_all_post_statuses();
897
-                //setup object
898
-                $ee_cpt_statuses = array();
899
-                foreach ($statuses as $status => $label) {
900
-                    $ee_cpt_statuses[$status] = array(
901
-                        'label'      => $label,
902
-                        'save_label' => sprintf(__('Save as %s', 'event_espresso'), $label),
903
-                    );
904
-                }
905
-                wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
906
-            }
907
-        }
908
-    }
909
-
910
-
911
-
912
-    /**
913
-     * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
914
-     * insert/updates
915
-     *
916
-     * @param  int     $post_id ID of post being updated
917
-     * @param  WP_Post $post    Post object from WP
918
-     * @param  bool    $update  Whether this is an update or a new save.
919
-     * @return void
920
-     * @throws \EE_Error
921
-     */
922
-    public function insert_update($post_id, $post, $update)
923
-    {
924
-        //make sure that if this is a revision OR trash action that we don't do any updates!
925
-        if (
926
-            isset($this->_req_data['action'])
927
-            && (
928
-                $this->_req_data['action'] === 'restore'
929
-                || $this->_req_data['action'] === 'trash'
930
-            )
931
-        ) {
932
-            return;
933
-        }
934
-        $this->_set_model_object($post_id, true, 'insert_update');
935
-        //if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
936
-        if ($update
937
-            && (
938
-                ! $this->_cpt_model_obj instanceof EE_CPT_Base
939
-                || $this->_cpt_model_obj->ID() !== $post_id
940
-            )
941
-        ) {
942
-            return;
943
-        }
944
-        //check for autosave and update our req_data property accordingly.
945
-        /*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
502
+	}
503
+
504
+
505
+
506
+	/**
507
+	 * if this post is a draft or scheduled post then we provide a preview button for user to click
508
+	 * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
509
+	 *
510
+	 * @param  string $return    the current html
511
+	 * @param  int    $id        the post id for the page
512
+	 * @param  string $new_title What the title is
513
+	 * @param  string $new_slug  what the slug is
514
+	 * @return string            The new html string for the permalink area
515
+	 */
516
+	public function preview_button_html($return, $id, $new_title, $new_slug)
517
+	{
518
+		$post = get_post($id);
519
+		if ('publish' !== get_post_status($post)) {
520
+			//include shims for the `get_preview_post_link` function
521
+			require_once( EE_CORE . 'wordpress-shims.php' );
522
+			$return .= '<span_id="view-post-btn"><a target="_blank" href="'
523
+					   . get_preview_post_link($id)
524
+					   . '" class="button button-small">'
525
+					   . __('Preview', 'event_espresso')
526
+					   . '</a></span>'
527
+					   . "\n";
528
+		}
529
+		return $return;
530
+	}
531
+
532
+
533
+
534
+	/**
535
+	 * add our custom post stati dropdown on the wp post page for this cpt
536
+	 *
537
+	 * @return void
538
+	 */
539
+	public function custom_post_stati_dropdown()
540
+	{
541
+
542
+		$statuses         = $this->_cpt_model_obj->get_custom_post_statuses();
543
+		$cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
544
+			? $statuses[$this->_cpt_model_obj->status()]
545
+			: '';
546
+		$template_args    = array(
547
+			'cur_status'            => $this->_cpt_model_obj->status(),
548
+			'statuses'              => $statuses,
549
+			'cur_status_label'      => $cur_status_label,
550
+			'localized_status_save' => sprintf(__('Save %s', 'event_espresso'), $cur_status_label),
551
+		);
552
+		//we'll add a trash post status (WP doesn't add one for some reason)
553
+		if ($this->_cpt_model_obj->status() === 'trash') {
554
+			$template_args['cur_status_label'] = __('Trashed', 'event_espresso');
555
+			$statuses['trash']                 = __('Trashed', 'event_espresso');
556
+			$template_args['statuses']         = $statuses;
557
+		}
558
+
559
+		$template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
560
+		EEH_Template::display_template($template, $template_args);
561
+	}
562
+
563
+
564
+
565
+	public function setup_autosave_hooks()
566
+	{
567
+		$this->_set_autosave_containers();
568
+		$this->_load_autosave_scripts_styles();
569
+	}
570
+
571
+
572
+
573
+	/**
574
+	 * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a $_POST object (available
575
+	 * in $this->_req_data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
576
+	 * for the nonce in here, but then this method looks for two things:
577
+	 * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
578
+	 * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
579
+	 * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
580
+	 * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
581
+	 * template args.
582
+	 *    1. $template_args['error'] = IF there is an error you can add the message in here.
583
+	 *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
584
+	 *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
585
+	 *    $this->_template_args['data']['items'] = array(
586
+	 *        'event-datetime-ids' => '1,2,3';
587
+	 *    );
588
+	 *    Keep in mind the following things:
589
+	 *    - "where" index is for the input with the id as that string.
590
+	 *    - "what" index is what will be used for the value of that input.
591
+	 *
592
+	 * @return void
593
+	 */
594
+	public function do_extra_autosave_stuff()
595
+	{
596
+		//next let's check for the autosave nonce (we'll use _verify_nonce )
597
+		$nonce = isset($this->_req_data['autosavenonce'])
598
+				? $this->_req_data['autosavenonce']
599
+				: null;
600
+		$this->_verify_nonce($nonce, 'autosave');
601
+		//make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
602
+		if ( ! defined('DOING_AUTOSAVE')) {
603
+			define('DOING_AUTOSAVE', true);
604
+		}
605
+		//if we made it here then the nonce checked out.  Let's run our methods and actions
606
+		$autosave = "_ee_autosave_{$this->_current_view}";
607
+		if (method_exists($this, $autosave)) {
608
+			$this->$autosave();
609
+		} else {
610
+			$this->_template_args['success'] = true;
611
+		}
612
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
613
+		do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
614
+		//now let's return json
615
+		$this->_return_json();
616
+	}
617
+
618
+
619
+
620
+	/**
621
+	 * This takes care of setting up default routes and pages that utilize the core WP admin pages.
622
+	 * Child classes can override the defaults (in cases for adding metaboxes etc.)
623
+	 * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
624
+	 *
625
+	 * @access protected
626
+	 * @throws EE_Error
627
+	 * @return void
628
+	 */
629
+	protected function _extend_page_config_for_cpt()
630
+	{
631
+		//before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
632
+		if (isset($this->_req_data['page']) && $this->_req_data['page'] !== $this->page_slug) {
633
+			return;
634
+		}
635
+		//set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
636
+		if ( ! empty($this->_cpt_object)) {
637
+			$this->_page_routes = array_merge(array(
638
+				'create_new' => '_create_new_cpt_item',
639
+				'edit'       => '_edit_cpt_item',
640
+			), $this->_page_routes);
641
+			$this->_page_config = array_merge(array(
642
+				'create_new' => array(
643
+					'nav'           => array(
644
+						'label' => $this->_cpt_object->labels->add_new_item,
645
+						'order' => 5,
646
+					),
647
+					'require_nonce' => false,
648
+				),
649
+				'edit'       => array(
650
+					'nav'           => array(
651
+						'label'      => $this->_cpt_object->labels->edit_item,
652
+						'order'      => 5,
653
+						'persistent' => false,
654
+						'url'        => '',
655
+					),
656
+					'require_nonce' => false,
657
+				),
658
+			),
659
+				$this->_page_config
660
+			);
661
+		}
662
+		//load the next section only if this is a matching cpt route as set in the cpt routes array.
663
+		if ( ! isset($this->_cpt_routes[$this->_req_action])) {
664
+			return;
665
+		}
666
+		$this->_cpt_route = isset($this->_cpt_routes[$this->_req_action]) ? true : false;
667
+		//add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
668
+		if (empty($this->_cpt_object)) {
669
+			$msg = sprintf(__('This page has been set as being related to a registered custom post type, however, the custom post type object could not be retrieved. There are two possible reasons for this:  1. The "%s" does not match a registered post type. or 2. The custom post type is not registered for the "%s" action as indexed in the "$_cpt_routes" property on this class (%s).'),
670
+				$this->page_slug, $this->_req_action, get_class($this));
671
+			throw new EE_Error($msg);
672
+		}
673
+		if ($this->_cpt_route) {
674
+			$id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
675
+			$this->_set_model_object($id);
676
+		}
677
+	}
678
+
679
+
680
+
681
+	/**
682
+	 * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
683
+	 *
684
+	 * @access protected
685
+	 * @param int  $id The id to retrieve the model object for. If empty we set a default object.
686
+	 * @param bool $ignore_route_check
687
+	 * @param string $req_type whether the current route is for inserting, updating, or deleting the CPT
688
+	 * @throws EE_Error
689
+	 */
690
+	protected function _set_model_object($id = null, $ignore_route_check = false, $req_type = '')
691
+	{
692
+		$model = null;
693
+		if (
694
+			empty($this->_cpt_model_names)
695
+			|| (
696
+				! $ignore_route_check
697
+				&& ! isset($this->_cpt_routes[$this->_req_action])
698
+			) || (
699
+				$this->_cpt_model_obj instanceof EE_CPT_Base
700
+				&& $this->_cpt_model_obj->ID() === $id
701
+			)
702
+		) {
703
+			//get out cuz we either don't have a model name OR the object has already been set and it has the same id as what has been sent.
704
+			return;
705
+		}
706
+		//if ignore_route_check is true, then get the model name via EE_Register_CPTs
707
+		if ($ignore_route_check) {
708
+			$model_names = EE_Register_CPTs::get_cpt_model_names();
709
+			$post_type   = get_post_type($id);
710
+			if (isset($model_names[$post_type])) {
711
+				$model = EE_Registry::instance()->load_model($model_names[$post_type]);
712
+			}
713
+		} else {
714
+			$model = EE_Registry::instance()->load_model($this->_cpt_model_names[$this->_req_action]);
715
+		}
716
+		if ($model instanceof EEM_Base) {
717
+			$this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
718
+		}
719
+		do_action(
720
+			'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
721
+			$this->_cpt_model_obj,
722
+			$req_type
723
+		);
724
+	}
725
+
726
+
727
+
728
+	/**
729
+	 * admin_init_global
730
+	 * This runs all the code that we want executed within the WP admin_init hook.
731
+	 * This method executes for ALL EE Admin pages.
732
+	 *
733
+	 * @access public
734
+	 * @return void
735
+	 */
736
+	public function admin_init_global()
737
+	{
738
+		$post = isset($this->_req_data['post']) ? get_post($this->_req_data['post']) : null;
739
+		//its possible this is a new save so let's catch that instead
740
+		$post = isset($this->_req_data['post_ID']) ? get_post($this->_req_data['post_ID']) : $post;
741
+		$post_type = $post ? $post->post_type : false;
742
+		$current_route = isset($this->_req_data['current_route'])
743
+			? $this->_req_data['current_route']
744
+			: 'shouldneverwork';
745
+		$route_to_check = $post_type && isset($this->_cpt_routes[$current_route])
746
+			? $this->_cpt_routes[$current_route]
747
+			: '';
748
+		add_filter('get_delete_post_link', array($this, 'modify_delete_post_link'), 10, 3);
749
+		add_filter('get_edit_post_link', array($this, 'modify_edit_post_link'), 10, 3);
750
+		if ($post_type === $route_to_check) {
751
+			add_filter('redirect_post_location', array($this, 'cpt_post_location_redirect'), 10, 2);
752
+		}
753
+		//now let's filter redirect if we're on a revision page and the revision is for an event CPT.
754
+		$revision = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
755
+		if ( ! empty($revision)) {
756
+			$action = isset($this->_req_data['action']) ? $this->_req_data['action'] : null;
757
+			//doing a restore?
758
+			if ( ! empty($action) && $action === 'restore') {
759
+				//get post for revision
760
+				$rev_post = get_post($revision);
761
+				$rev_parent = get_post($rev_post->post_parent);
762
+				//only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
763
+				if ($rev_parent && $rev_parent->post_type === $this->page_slug) {
764
+					add_filter('wp_redirect', array($this, 'revision_redirect'), 10, 2);
765
+					//restores of revisions
766
+					add_action('wp_restore_post_revision', array($this, 'restore_revision'), 10, 2);
767
+				}
768
+			}
769
+		}
770
+		//NOTE we ONLY want to run these hooks if we're on the right class for the given post type.  Otherwise we could see some really freaky things happen!
771
+		if ($post_type && $post_type === $route_to_check) {
772
+			//$post_id, $post
773
+			add_action('save_post', array($this, 'insert_update'), 10, 3);
774
+			//$post_id
775
+			add_action('trashed_post', array($this, 'before_trash_cpt_item'), 10);
776
+			add_action('trashed_post', array($this, 'dont_permanently_delete_ee_cpts'), 10);
777
+			add_action('untrashed_post', array($this, 'before_restore_cpt_item'), 10);
778
+			add_action('after_delete_post', array($this, 'before_delete_cpt_item'), 10);
779
+		}
780
+	}
781
+
782
+
783
+
784
+	/**
785
+	 * Callback for the WordPress trashed_post hook.
786
+	 * Execute some basic checks before calling the trash_cpt_item declared in the child class.
787
+	 *
788
+	 * @param int $post_id
789
+	 * @throws \EE_Error
790
+	 */
791
+	public function before_trash_cpt_item($post_id)
792
+	{
793
+		$this->_set_model_object($post_id, true, 'trash');
794
+		//if our cpt object isn't existent then get out immediately.
795
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
796
+			return;
797
+		}
798
+		$this->trash_cpt_item($post_id);
799
+	}
800
+
801
+
802
+
803
+	/**
804
+	 * Callback for the WordPress untrashed_post hook.
805
+	 * Execute some basic checks before calling the restore_cpt_method in the child class.
806
+	 *
807
+	 * @param $post_id
808
+	 * @throws \EE_Error
809
+	 */
810
+	public function before_restore_cpt_item($post_id)
811
+	{
812
+		$this->_set_model_object($post_id, true, 'restore');
813
+		//if our cpt object isn't existent then get out immediately.
814
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
815
+			return;
816
+		}
817
+		$this->restore_cpt_item($post_id);
818
+	}
819
+
820
+
821
+
822
+	/**
823
+	 * Callback for the WordPress after_delete_post hook.
824
+	 * Execute some basic checks before calling the delete_cpt_item method in the child class.
825
+	 *
826
+	 * @param $post_id
827
+	 * @throws \EE_Error
828
+	 */
829
+	public function before_delete_cpt_item($post_id)
830
+	{
831
+		$this->_set_model_object($post_id, true, 'delete');
832
+		//if our cpt object isn't existent then get out immediately.
833
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
834
+			return;
835
+		}
836
+		$this->delete_cpt_item($post_id);
837
+	}
838
+
839
+
840
+
841
+	/**
842
+	 * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
843
+	 * accordingly.
844
+	 *
845
+	 * @access public
846
+	 * @throws EE_Error
847
+	 * @return void
848
+	 */
849
+	public function verify_cpt_object()
850
+	{
851
+		$label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
852
+		// verify event object
853
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
854
+			throw new EE_Error(sprintf(__('Something has gone wrong with the page load because we are unable to set up the object for the %1$s.  This usually happens when the given id for the page route is NOT for the correct custom post type for this page',
855
+					'event_espresso'), $label));
856
+		}
857
+		//if auto-draft then throw an error
858
+		if ($this->_cpt_model_obj->get('status') === 'auto-draft') {
859
+			EE_Error::overwrite_errors();
860
+			EE_Error::add_error(sprintf(__('This %1$s was saved without a title, description, or excerpt which means that none of the extra details you added were saved properly.  All autodrafts will show up in the "draft" view of your event list table.  You can delete them from there. Please click the "Add %1$s" button to refresh and restart.'),
861
+					$label), __FILE__, __FUNCTION__, __LINE__);
862
+		}
863
+	}
864
+
865
+
866
+
867
+	/**
868
+	 * admin_footer_scripts_global
869
+	 * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
870
+	 * will apply on ALL EE_Admin pages.
871
+	 *
872
+	 * @access public
873
+	 * @return void
874
+	 */
875
+	public function admin_footer_scripts_global()
876
+	{
877
+		$this->_add_admin_page_ajax_loading_img();
878
+		$this->_add_admin_page_overlay();
879
+	}
880
+
881
+
882
+
883
+	/**
884
+	 * add in any global scripts for cpt routes
885
+	 *
886
+	 * @return void
887
+	 */
888
+	public function load_global_scripts_styles()
889
+	{
890
+		parent::load_global_scripts_styles();
891
+		if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
892
+			//setup custom post status object for localize script but only if we've got a cpt object
893
+			$statuses = $this->_cpt_model_obj->get_custom_post_statuses();
894
+			if ( ! empty($statuses)) {
895
+				//get ALL statuses!
896
+				$statuses = $this->_cpt_model_obj->get_all_post_statuses();
897
+				//setup object
898
+				$ee_cpt_statuses = array();
899
+				foreach ($statuses as $status => $label) {
900
+					$ee_cpt_statuses[$status] = array(
901
+						'label'      => $label,
902
+						'save_label' => sprintf(__('Save as %s', 'event_espresso'), $label),
903
+					);
904
+				}
905
+				wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
906
+			}
907
+		}
908
+	}
909
+
910
+
911
+
912
+	/**
913
+	 * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
914
+	 * insert/updates
915
+	 *
916
+	 * @param  int     $post_id ID of post being updated
917
+	 * @param  WP_Post $post    Post object from WP
918
+	 * @param  bool    $update  Whether this is an update or a new save.
919
+	 * @return void
920
+	 * @throws \EE_Error
921
+	 */
922
+	public function insert_update($post_id, $post, $update)
923
+	{
924
+		//make sure that if this is a revision OR trash action that we don't do any updates!
925
+		if (
926
+			isset($this->_req_data['action'])
927
+			&& (
928
+				$this->_req_data['action'] === 'restore'
929
+				|| $this->_req_data['action'] === 'trash'
930
+			)
931
+		) {
932
+			return;
933
+		}
934
+		$this->_set_model_object($post_id, true, 'insert_update');
935
+		//if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
936
+		if ($update
937
+			&& (
938
+				! $this->_cpt_model_obj instanceof EE_CPT_Base
939
+				|| $this->_cpt_model_obj->ID() !== $post_id
940
+			)
941
+		) {
942
+			return;
943
+		}
944
+		//check for autosave and update our req_data property accordingly.
945
+		/*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
946 946
             foreach( (array) $this->_req_data['ee_autosave_data'] as $id => $values ) {
947 947
 
948 948
                 foreach ( (array) $values as $key => $value ) {
@@ -952,542 +952,542 @@  discard block
 block discarded – undo
952 952
 
953 953
         }/**/ //TODO reactivate after autosave is implemented in 4.2
954 954
 
955
-        //take care of updating any selected page_template IF this cpt supports it.
956
-        if ($this->_supports_page_templates($post->post_type) && ! empty($this->_req_data['page_template'])) {
957
-            //wp version aware.
958
-            if (RecommendedVersions::compareWordPressVersion('4.7', '>=')) {
959
-                $page_templates = wp_get_theme()->get_page_templates();
960
-            } else {
961
-                $post->page_template = $this->_req_data['page_template'];
962
-                $page_templates      = wp_get_theme()->get_page_templates($post);
963
-            }
964
-            if ('default' != $this->_req_data['page_template'] && ! isset($page_templates[$this->_req_data['page_template']])) {
965
-                EE_Error::add_error(__('Invalid Page Template.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
966
-            } else {
967
-                update_post_meta($post_id, '_wp_page_template', $this->_req_data['page_template']);
968
-            }
969
-        }
970
-        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
971
-            return;
972
-        } //TODO we'll remove this after reimplementing autosave in 4.2
973
-        $this->_insert_update_cpt_item($post_id, $post);
974
-    }
975
-
976
-
977
-
978
-    /**
979
-     * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
980
-     * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
981
-     * so we don't have to check for our CPT.
982
-     *
983
-     * @param  int $post_id ID of the post
984
-     * @return void
985
-     */
986
-    public function dont_permanently_delete_ee_cpts($post_id)
987
-    {
988
-        //only do this if we're actually processing one of our CPTs
989
-        //if our cpt object isn't existent then get out immediately.
990
-        if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
991
-            return;
992
-        }
993
-        delete_post_meta($post_id, '_wp_trash_meta_status');
994
-        delete_post_meta($post_id, '_wp_trash_meta_time');
995
-        //our cpts may have comments so let's take care of that too
996
-        delete_post_meta($post_id, '_wp_trash_meta_comments_status');
997
-    }
998
-
999
-
1000
-
1001
-    /**
1002
-     * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
1003
-     * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
1004
-     * in them. We also have our OWN action in here so addons can hook into the restore process easily.
1005
-     *
1006
-     * @param  int $post_id     ID of cpt item
1007
-     * @param  int $revision_id ID of revision being restored
1008
-     * @return void
1009
-     */
1010
-    public function restore_revision($post_id, $revision_id)
1011
-    {
1012
-        $this->_restore_cpt_item($post_id, $revision_id);
1013
-        //global action
1014
-        do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1015
-        //class specific action so you can limit hooking into a specific page.
1016
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1017
-    }
1018
-
1019
-
1020
-
1021
-    /**
1022
-     * @see restore_revision() for details
1023
-     * @param  int $post_id     ID of cpt item
1024
-     * @param  int $revision_id ID of revision for item
1025
-     * @return void
1026
-     */
1027
-    abstract protected function _restore_cpt_item($post_id, $revision_id);
1028
-
1029
-
1030
-
1031
-    /**
1032
-     * Execution of this method is added to the end of the load_page_dependencies method in the parent
1033
-     * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
1034
-     * To fix we have to reset the current_screen using the page_slug
1035
-     * (which is identical - or should be - to our registered_post_type id.)
1036
-     * Also, since the core WP file loads the admin_header.php for WP
1037
-     * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
1038
-     * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
1039
-     *
1040
-     * @return void
1041
-     */
1042
-    public function modify_current_screen()
1043
-    {
1044
-        //ONLY do this if the current page_route IS a cpt route
1045
-        if ( ! $this->_cpt_route) {
1046
-            return;
1047
-        }
1048
-        //routing things REALLY early b/c this is a cpt admin page
1049
-        set_current_screen($this->_cpt_routes[$this->_req_action]);
1050
-        $this->_current_screen       = get_current_screen();
1051
-        $this->_current_screen->base = 'event-espresso';
1052
-        $this->_add_help_tabs(); //we make sure we add any help tabs back in!
1053
-        /*try {
955
+		//take care of updating any selected page_template IF this cpt supports it.
956
+		if ($this->_supports_page_templates($post->post_type) && ! empty($this->_req_data['page_template'])) {
957
+			//wp version aware.
958
+			if (RecommendedVersions::compareWordPressVersion('4.7', '>=')) {
959
+				$page_templates = wp_get_theme()->get_page_templates();
960
+			} else {
961
+				$post->page_template = $this->_req_data['page_template'];
962
+				$page_templates      = wp_get_theme()->get_page_templates($post);
963
+			}
964
+			if ('default' != $this->_req_data['page_template'] && ! isset($page_templates[$this->_req_data['page_template']])) {
965
+				EE_Error::add_error(__('Invalid Page Template.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
966
+			} else {
967
+				update_post_meta($post_id, '_wp_page_template', $this->_req_data['page_template']);
968
+			}
969
+		}
970
+		if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
971
+			return;
972
+		} //TODO we'll remove this after reimplementing autosave in 4.2
973
+		$this->_insert_update_cpt_item($post_id, $post);
974
+	}
975
+
976
+
977
+
978
+	/**
979
+	 * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
980
+	 * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
981
+	 * so we don't have to check for our CPT.
982
+	 *
983
+	 * @param  int $post_id ID of the post
984
+	 * @return void
985
+	 */
986
+	public function dont_permanently_delete_ee_cpts($post_id)
987
+	{
988
+		//only do this if we're actually processing one of our CPTs
989
+		//if our cpt object isn't existent then get out immediately.
990
+		if ( ! $this->_cpt_model_obj instanceof EE_CPT_Base) {
991
+			return;
992
+		}
993
+		delete_post_meta($post_id, '_wp_trash_meta_status');
994
+		delete_post_meta($post_id, '_wp_trash_meta_time');
995
+		//our cpts may have comments so let's take care of that too
996
+		delete_post_meta($post_id, '_wp_trash_meta_comments_status');
997
+	}
998
+
999
+
1000
+
1001
+	/**
1002
+	 * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
1003
+	 * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
1004
+	 * in them. We also have our OWN action in here so addons can hook into the restore process easily.
1005
+	 *
1006
+	 * @param  int $post_id     ID of cpt item
1007
+	 * @param  int $revision_id ID of revision being restored
1008
+	 * @return void
1009
+	 */
1010
+	public function restore_revision($post_id, $revision_id)
1011
+	{
1012
+		$this->_restore_cpt_item($post_id, $revision_id);
1013
+		//global action
1014
+		do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1015
+		//class specific action so you can limit hooking into a specific page.
1016
+		do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1017
+	}
1018
+
1019
+
1020
+
1021
+	/**
1022
+	 * @see restore_revision() for details
1023
+	 * @param  int $post_id     ID of cpt item
1024
+	 * @param  int $revision_id ID of revision for item
1025
+	 * @return void
1026
+	 */
1027
+	abstract protected function _restore_cpt_item($post_id, $revision_id);
1028
+
1029
+
1030
+
1031
+	/**
1032
+	 * Execution of this method is added to the end of the load_page_dependencies method in the parent
1033
+	 * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
1034
+	 * To fix we have to reset the current_screen using the page_slug
1035
+	 * (which is identical - or should be - to our registered_post_type id.)
1036
+	 * Also, since the core WP file loads the admin_header.php for WP
1037
+	 * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
1038
+	 * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
1039
+	 *
1040
+	 * @return void
1041
+	 */
1042
+	public function modify_current_screen()
1043
+	{
1044
+		//ONLY do this if the current page_route IS a cpt route
1045
+		if ( ! $this->_cpt_route) {
1046
+			return;
1047
+		}
1048
+		//routing things REALLY early b/c this is a cpt admin page
1049
+		set_current_screen($this->_cpt_routes[$this->_req_action]);
1050
+		$this->_current_screen       = get_current_screen();
1051
+		$this->_current_screen->base = 'event-espresso';
1052
+		$this->_add_help_tabs(); //we make sure we add any help tabs back in!
1053
+		/*try {
1054 1054
             $this->_route_admin_request();
1055 1055
         } catch ( EE_Error $e ) {
1056 1056
             $e->get_error();
1057 1057
         }/**/
1058
-    }
1059
-
1060
-
1061
-
1062
-    /**
1063
-     * This allows child classes to modify the default editor title that appears when people add a new or edit an
1064
-     * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
1065
-     * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
1066
-     * default to be.
1067
-     *
1068
-     * @param string $title The new title (or existing if there is no editor_title defined)
1069
-     * @return string
1070
-     */
1071
-    public function add_custom_editor_default_title($title)
1072
-    {
1073
-        return isset($this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]])
1074
-            ? $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]]
1075
-            : $title;
1076
-    }
1077
-
1078
-
1079
-
1080
-    /**
1081
-     * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1082
-     *
1083
-     * @param string $shortlink   The already generated shortlink
1084
-     * @param int    $id          Post ID for this item
1085
-     * @param string $context     The context for the link
1086
-     * @param bool   $allow_slugs Whether to allow post slugs in the shortlink.
1087
-     * @return string
1088
-     */
1089
-    public function add_shortlink_button_to_editor($shortlink, $id, $context, $allow_slugs)
1090
-    {
1091
-        if ( ! empty($id) && get_option('permalink_structure') !== '') {
1092
-            $post = get_post($id);
1093
-            if (isset($post->post_type) && $this->page_slug === $post->post_type) {
1094
-                $shortlink = home_url('?p=' . $post->ID);
1095
-            }
1096
-        }
1097
-        return $shortlink;
1098
-    }
1099
-
1100
-
1101
-
1102
-    /**
1103
-     * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1104
-     * already run in modify_current_screen())
1105
-     *
1106
-     * @return void
1107
-     */
1108
-    public function route_admin_request()
1109
-    {
1110
-        if ($this->_cpt_route) {
1111
-            return;
1112
-        }
1113
-        try {
1114
-            $this->_route_admin_request();
1115
-        } catch (EE_Error $e) {
1116
-            $e->get_error();
1117
-        }
1118
-    }
1119
-
1120
-
1121
-
1122
-    /**
1123
-     * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1124
-     *
1125
-     * @return void
1126
-     */
1127
-    public function cpt_post_form_hidden_input()
1128
-    {
1129
-        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1130
-        //we're also going to add the route value and the current page so we can direct autosave parsing correctly
1131
-        echo '<div id="ee-cpt-hidden-inputs">';
1132
-        echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1133
-        echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1134
-        echo '</div>';
1135
-    }
1136
-
1137
-
1138
-
1139
-    /**
1140
-     * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1141
-     *
1142
-     * @param  string $location Original location url
1143
-     * @param  int    $status   Status for http header
1144
-     * @return string           new (or original) url to redirect to.
1145
-     */
1146
-    public function revision_redirect($location, $status)
1147
-    {
1148
-        //get revision
1149
-        $rev_id = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
1150
-        //can't do anything without revision so let's get out if not present
1151
-        if (empty($rev_id)) {
1152
-            return $location;
1153
-        }
1154
-        //get rev_post_data
1155
-        $rev = get_post($rev_id);
1156
-        $admin_url = $this->_admin_base_url;
1157
-        $query_args = array(
1158
-            'action'   => 'edit',
1159
-            'post'     => $rev->post_parent,
1160
-            'revision' => $rev_id,
1161
-            'message'  => 5,
1162
-        );
1163
-        $this->_process_notices($query_args, true);
1164
-        return self::add_query_args_and_nonce($query_args, $admin_url);
1165
-    }
1166
-
1167
-
1168
-
1169
-    /**
1170
-     * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1171
-     *
1172
-     * @param  string $link    the original generated link
1173
-     * @param  int    $id      post id
1174
-     * @param  string $context optional, defaults to display.  How to write the '&'
1175
-     * @return string          the link
1176
-     */
1177
-    public function modify_edit_post_link($link, $id, $context)
1178
-    {
1179
-        $post = get_post($id);
1180
-        if ( ! isset($this->_req_data['action'])
1181
-             || ! isset($this->_cpt_routes[$this->_req_data['action']])
1182
-             || $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1183
-        ) {
1184
-            return $link;
1185
-        }
1186
-        $query_args = array(
1187
-            'action' => isset($this->_cpt_edit_routes[$post->post_type])
1188
-                ? $this->_cpt_edit_routes[$post->post_type]
1189
-                : 'edit',
1190
-            'post'   => $id,
1191
-        );
1192
-        return self::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1193
-    }
1194
-
1195
-
1196
-    /**
1197
-     * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1198
-     * our routes.
1199
-     *
1200
-     * @param  string $delete_link  original delete link
1201
-     * @param  int    $post_id      id of cpt object
1202
-     * @param  bool   $force_delete whether this is forcing a hard delete instead of trash
1203
-     * @return string new delete link
1204
-     * @throws EE_Error
1205
-     */
1206
-    public function modify_delete_post_link($delete_link, $post_id, $force_delete)
1207
-    {
1208
-        $post = get_post($post_id);
1209
-
1210
-        if (empty($this->_req_data['action'])
1211
-            || ! isset($this->_cpt_routes[$this->_req_data['action']])
1212
-            || ! $post instanceof WP_Post
1213
-            || $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1214
-        ) {
1215
-            return $delete_link;
1216
-        }
1217
-        $this->_set_model_object($post->ID, true);
1218
-
1219
-        //returns something like `trash_event` or `trash_attendee` or `trash_venue`
1220
-        $action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1221
-
1222
-        return EE_Admin_Page::add_query_args_and_nonce(
1223
-            array(
1224
-                'page' => $this->_req_data['page'],
1225
-                'action' => $action,
1226
-                $this->_cpt_model_obj->get_model()->get_primary_key_field()->get_name()
1227
-                    => $post->ID
1228
-            ),
1229
-            admin_url()
1230
-        );
1231
-    }
1232
-
1233
-
1234
-
1235
-    /**
1236
-     * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1237
-     * so that we can hijack the default redirect locations for wp custom post types
1238
-     * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1239
-     *
1240
-     * @param  string $location This is the incoming currently set redirect location
1241
-     * @param  string $post_id  This is the 'ID' value of the wp_posts table
1242
-     * @return string           the new location to redirect to
1243
-     */
1244
-    public function cpt_post_location_redirect($location, $post_id)
1245
-    {
1246
-        //we DO have a match so let's setup the url
1247
-        //we have to get the post to determine our route
1248
-        $post       = get_post($post_id);
1249
-        $edit_route = $this->_cpt_edit_routes[$post->post_type];
1250
-        //shared query_args
1251
-        $query_args = array('action' => $edit_route, 'post' => $post_id);
1252
-        $admin_url  = $this->_admin_base_url;
1253
-        if (isset($this->_req_data['save']) || isset($this->_req_data['publish'])) {
1254
-            $status = get_post_status($post_id);
1255
-            if (isset($this->_req_data['publish'])) {
1256
-                switch ($status) {
1257
-                    case 'pending':
1258
-                        $message = 8;
1259
-                        break;
1260
-                    case 'future':
1261
-                        $message = 9;
1262
-                        break;
1263
-                    default:
1264
-                        $message = 6;
1265
-                }
1266
-            } else {
1267
-                $message = 'draft' === $status ? 10 : 1;
1268
-            }
1269
-        } else if (isset($this->_req_data['addmeta']) && $this->_req_data['addmeta']) {
1270
-            $message = 2;
1271
-            //			$append = '#postcustom';
1272
-        } else if (isset($this->_req_data['deletemeta']) && $this->_req_data['deletemeta']) {
1273
-            $message = 3;
1274
-            //			$append = '#postcustom';
1275
-        } elseif ($this->_req_data['action'] === 'post-quickpress-save-cont') {
1276
-            $message = 7;
1277
-        } else {
1278
-            $message = 4;
1279
-        }
1280
-        //change the message if the post type is not viewable on the frontend
1281
-        $this->_cpt_object = get_post_type_object($post->post_type);
1282
-        $message           = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1283
-        $query_args = array_merge(array('message' => $message), $query_args);
1284
-        $this->_process_notices($query_args, true);
1285
-        return self::add_query_args_and_nonce($query_args, $admin_url);
1286
-    }
1287
-
1288
-
1289
-
1290
-    /**
1291
-     * This method is called to inject nav tabs on core WP cpt pages
1292
-     *
1293
-     * @access public
1294
-     * @return void
1295
-     */
1296
-    public function inject_nav_tabs()
1297
-    {
1298
-        //can we hijack and insert the nav_tabs?
1299
-        $nav_tabs = $this->_get_main_nav_tabs();
1300
-        //first close off existing form tag
1301
-        $html = '>';
1302
-        $html .= $nav_tabs;
1303
-        //now let's handle the remaining tag ( missing ">" is CORRECT )
1304
-        $html .= '<span></span';
1305
-        echo $html;
1306
-    }
1307
-
1308
-
1309
-
1310
-    /**
1311
-     * This just sets up the post update messages when an update form is loaded
1312
-     *
1313
-     * @access public
1314
-     * @param  array $messages the original messages array
1315
-     * @return array           the new messages array
1316
-     */
1317
-    public function post_update_messages($messages)
1318
-    {
1319
-        global $post;
1320
-        $id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1321
-        $id = empty($id) && is_object($post) ? $post->ID : null;
1322
-        //		$post_type = $post ? $post->post_type : false;
1323
-        /*$current_route = isset($this->_req_data['current_route']) ? $this->_req_data['current_route'] : 'shouldneverwork';
1058
+	}
1059
+
1060
+
1061
+
1062
+	/**
1063
+	 * This allows child classes to modify the default editor title that appears when people add a new or edit an
1064
+	 * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
1065
+	 * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
1066
+	 * default to be.
1067
+	 *
1068
+	 * @param string $title The new title (or existing if there is no editor_title defined)
1069
+	 * @return string
1070
+	 */
1071
+	public function add_custom_editor_default_title($title)
1072
+	{
1073
+		return isset($this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]])
1074
+			? $this->_labels['editor_title'][$this->_cpt_routes[$this->_req_action]]
1075
+			: $title;
1076
+	}
1077
+
1078
+
1079
+
1080
+	/**
1081
+	 * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1082
+	 *
1083
+	 * @param string $shortlink   The already generated shortlink
1084
+	 * @param int    $id          Post ID for this item
1085
+	 * @param string $context     The context for the link
1086
+	 * @param bool   $allow_slugs Whether to allow post slugs in the shortlink.
1087
+	 * @return string
1088
+	 */
1089
+	public function add_shortlink_button_to_editor($shortlink, $id, $context, $allow_slugs)
1090
+	{
1091
+		if ( ! empty($id) && get_option('permalink_structure') !== '') {
1092
+			$post = get_post($id);
1093
+			if (isset($post->post_type) && $this->page_slug === $post->post_type) {
1094
+				$shortlink = home_url('?p=' . $post->ID);
1095
+			}
1096
+		}
1097
+		return $shortlink;
1098
+	}
1099
+
1100
+
1101
+
1102
+	/**
1103
+	 * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1104
+	 * already run in modify_current_screen())
1105
+	 *
1106
+	 * @return void
1107
+	 */
1108
+	public function route_admin_request()
1109
+	{
1110
+		if ($this->_cpt_route) {
1111
+			return;
1112
+		}
1113
+		try {
1114
+			$this->_route_admin_request();
1115
+		} catch (EE_Error $e) {
1116
+			$e->get_error();
1117
+		}
1118
+	}
1119
+
1120
+
1121
+
1122
+	/**
1123
+	 * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1124
+	 *
1125
+	 * @return void
1126
+	 */
1127
+	public function cpt_post_form_hidden_input()
1128
+	{
1129
+		echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1130
+		//we're also going to add the route value and the current page so we can direct autosave parsing correctly
1131
+		echo '<div id="ee-cpt-hidden-inputs">';
1132
+		echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1133
+		echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1134
+		echo '</div>';
1135
+	}
1136
+
1137
+
1138
+
1139
+	/**
1140
+	 * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1141
+	 *
1142
+	 * @param  string $location Original location url
1143
+	 * @param  int    $status   Status for http header
1144
+	 * @return string           new (or original) url to redirect to.
1145
+	 */
1146
+	public function revision_redirect($location, $status)
1147
+	{
1148
+		//get revision
1149
+		$rev_id = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
1150
+		//can't do anything without revision so let's get out if not present
1151
+		if (empty($rev_id)) {
1152
+			return $location;
1153
+		}
1154
+		//get rev_post_data
1155
+		$rev = get_post($rev_id);
1156
+		$admin_url = $this->_admin_base_url;
1157
+		$query_args = array(
1158
+			'action'   => 'edit',
1159
+			'post'     => $rev->post_parent,
1160
+			'revision' => $rev_id,
1161
+			'message'  => 5,
1162
+		);
1163
+		$this->_process_notices($query_args, true);
1164
+		return self::add_query_args_and_nonce($query_args, $admin_url);
1165
+	}
1166
+
1167
+
1168
+
1169
+	/**
1170
+	 * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1171
+	 *
1172
+	 * @param  string $link    the original generated link
1173
+	 * @param  int    $id      post id
1174
+	 * @param  string $context optional, defaults to display.  How to write the '&'
1175
+	 * @return string          the link
1176
+	 */
1177
+	public function modify_edit_post_link($link, $id, $context)
1178
+	{
1179
+		$post = get_post($id);
1180
+		if ( ! isset($this->_req_data['action'])
1181
+			 || ! isset($this->_cpt_routes[$this->_req_data['action']])
1182
+			 || $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1183
+		) {
1184
+			return $link;
1185
+		}
1186
+		$query_args = array(
1187
+			'action' => isset($this->_cpt_edit_routes[$post->post_type])
1188
+				? $this->_cpt_edit_routes[$post->post_type]
1189
+				: 'edit',
1190
+			'post'   => $id,
1191
+		);
1192
+		return self::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1193
+	}
1194
+
1195
+
1196
+	/**
1197
+	 * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1198
+	 * our routes.
1199
+	 *
1200
+	 * @param  string $delete_link  original delete link
1201
+	 * @param  int    $post_id      id of cpt object
1202
+	 * @param  bool   $force_delete whether this is forcing a hard delete instead of trash
1203
+	 * @return string new delete link
1204
+	 * @throws EE_Error
1205
+	 */
1206
+	public function modify_delete_post_link($delete_link, $post_id, $force_delete)
1207
+	{
1208
+		$post = get_post($post_id);
1209
+
1210
+		if (empty($this->_req_data['action'])
1211
+			|| ! isset($this->_cpt_routes[$this->_req_data['action']])
1212
+			|| ! $post instanceof WP_Post
1213
+			|| $post->post_type !== $this->_cpt_routes[$this->_req_data['action']]
1214
+		) {
1215
+			return $delete_link;
1216
+		}
1217
+		$this->_set_model_object($post->ID, true);
1218
+
1219
+		//returns something like `trash_event` or `trash_attendee` or `trash_venue`
1220
+		$action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1221
+
1222
+		return EE_Admin_Page::add_query_args_and_nonce(
1223
+			array(
1224
+				'page' => $this->_req_data['page'],
1225
+				'action' => $action,
1226
+				$this->_cpt_model_obj->get_model()->get_primary_key_field()->get_name()
1227
+					=> $post->ID
1228
+			),
1229
+			admin_url()
1230
+		);
1231
+	}
1232
+
1233
+
1234
+
1235
+	/**
1236
+	 * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1237
+	 * so that we can hijack the default redirect locations for wp custom post types
1238
+	 * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1239
+	 *
1240
+	 * @param  string $location This is the incoming currently set redirect location
1241
+	 * @param  string $post_id  This is the 'ID' value of the wp_posts table
1242
+	 * @return string           the new location to redirect to
1243
+	 */
1244
+	public function cpt_post_location_redirect($location, $post_id)
1245
+	{
1246
+		//we DO have a match so let's setup the url
1247
+		//we have to get the post to determine our route
1248
+		$post       = get_post($post_id);
1249
+		$edit_route = $this->_cpt_edit_routes[$post->post_type];
1250
+		//shared query_args
1251
+		$query_args = array('action' => $edit_route, 'post' => $post_id);
1252
+		$admin_url  = $this->_admin_base_url;
1253
+		if (isset($this->_req_data['save']) || isset($this->_req_data['publish'])) {
1254
+			$status = get_post_status($post_id);
1255
+			if (isset($this->_req_data['publish'])) {
1256
+				switch ($status) {
1257
+					case 'pending':
1258
+						$message = 8;
1259
+						break;
1260
+					case 'future':
1261
+						$message = 9;
1262
+						break;
1263
+					default:
1264
+						$message = 6;
1265
+				}
1266
+			} else {
1267
+				$message = 'draft' === $status ? 10 : 1;
1268
+			}
1269
+		} else if (isset($this->_req_data['addmeta']) && $this->_req_data['addmeta']) {
1270
+			$message = 2;
1271
+			//			$append = '#postcustom';
1272
+		} else if (isset($this->_req_data['deletemeta']) && $this->_req_data['deletemeta']) {
1273
+			$message = 3;
1274
+			//			$append = '#postcustom';
1275
+		} elseif ($this->_req_data['action'] === 'post-quickpress-save-cont') {
1276
+			$message = 7;
1277
+		} else {
1278
+			$message = 4;
1279
+		}
1280
+		//change the message if the post type is not viewable on the frontend
1281
+		$this->_cpt_object = get_post_type_object($post->post_type);
1282
+		$message           = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1283
+		$query_args = array_merge(array('message' => $message), $query_args);
1284
+		$this->_process_notices($query_args, true);
1285
+		return self::add_query_args_and_nonce($query_args, $admin_url);
1286
+	}
1287
+
1288
+
1289
+
1290
+	/**
1291
+	 * This method is called to inject nav tabs on core WP cpt pages
1292
+	 *
1293
+	 * @access public
1294
+	 * @return void
1295
+	 */
1296
+	public function inject_nav_tabs()
1297
+	{
1298
+		//can we hijack and insert the nav_tabs?
1299
+		$nav_tabs = $this->_get_main_nav_tabs();
1300
+		//first close off existing form tag
1301
+		$html = '>';
1302
+		$html .= $nav_tabs;
1303
+		//now let's handle the remaining tag ( missing ">" is CORRECT )
1304
+		$html .= '<span></span';
1305
+		echo $html;
1306
+	}
1307
+
1308
+
1309
+
1310
+	/**
1311
+	 * This just sets up the post update messages when an update form is loaded
1312
+	 *
1313
+	 * @access public
1314
+	 * @param  array $messages the original messages array
1315
+	 * @return array           the new messages array
1316
+	 */
1317
+	public function post_update_messages($messages)
1318
+	{
1319
+		global $post;
1320
+		$id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1321
+		$id = empty($id) && is_object($post) ? $post->ID : null;
1322
+		//		$post_type = $post ? $post->post_type : false;
1323
+		/*$current_route = isset($this->_req_data['current_route']) ? $this->_req_data['current_route'] : 'shouldneverwork';
1324 1324
 
1325 1325
         $route_to_check = $post_type && isset( $this->_cpt_routes[$current_route]) ? $this->_cpt_routes[$current_route] : '';/**/
1326
-        $messages[$post->post_type] = array(
1327
-            0 => '', //Unused. Messages start at index 1.
1328
-            1 => sprintf(
1329
-                __('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1330
-                $this->_cpt_object->labels->singular_name,
1331
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1332
-                '</a>'
1333
-            ),
1334
-            2 => __('Custom field updated'),
1335
-            3 => __('Custom field deleted.'),
1336
-            4 => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1337
-            5 => isset($_GET['revision']) ? sprintf(__('%s restored to revision from %s', 'event_espresso'),
1338
-                $this->_cpt_object->labels->singular_name, wp_post_revision_title((int)$_GET['revision'], false))
1339
-                : false,
1340
-            6 => sprintf(
1341
-                __('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1342
-                $this->_cpt_object->labels->singular_name,
1343
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1344
-                '</a>'
1345
-            ),
1346
-            7 => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1347
-            8 => sprintf(
1348
-                __('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1349
-                $this->_cpt_object->labels->singular_name,
1350
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1351
-                '</a>'
1352
-            ),
1353
-            9 => sprintf(
1354
-                __('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1355
-                $this->_cpt_object->labels->singular_name,
1356
-                '<strong>' . date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)) . '</strong>',
1357
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1358
-                '</a>'
1359
-            ),
1360
-            10 => sprintf(
1361
-                __('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1362
-                $this->_cpt_object->labels->singular_name,
1363
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1364
-                '</a>'
1365
-            ),
1366
-        );
1367
-        return $messages;
1368
-    }
1369
-
1370
-
1371
-
1372
-    /**
1373
-     * default method for the 'create_new' route for cpt admin pages.
1374
-     * For reference what to include in here, see wp-admin/post-new.php
1375
-     *
1376
-     * @access  protected
1377
-     * @return void
1378
-     */
1379
-    protected function _create_new_cpt_item()
1380
-    {
1381
-        // gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1382
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1383
-        $post_type        = $this->_cpt_routes[$this->_req_action];
1384
-        $post_type_object = $this->_cpt_object;
1385
-        $title            = $post_type_object->labels->add_new_item;
1386
-        $post    = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1387
-        add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1388
-        //modify the default editor title field with default title.
1389
-        add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1390
-        $this->loadEditorTemplate(true);
1391
-    }
1392
-
1393
-
1394
-    /**
1395
-     * Enqueues auto-save and loads the editor template
1396
-     *
1397
-     * @param bool $creating
1398
-     */
1399
-    private function loadEditorTemplate($creating = true) {
1400
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1401
-        //these vars are used by the template
1402
-        $editing = true;
1403
-        $post_ID = $post->ID;
1404
-        if (apply_filters('FHEE__EE_Admin_Page_CPT___create_new_cpt_item__replace_editor', false, $post) === false) {
1405
-            //only enqueue autosave when creating event (necessary to get permalink/url generated)
1406
-            //otherwise EE doesn't support autosave fully, so to prevent user confusion we disable it in edit context.
1407
-            if ($creating) {
1408
-                wp_enqueue_script('autosave');
1409
-            } else {
1410
-                if (isset($this->_cpt_routes[$this->_req_data['action']])
1411
-                    && ! isset($this->_labels['hide_add_button_on_cpt_route'][$this->_req_data['action']])
1412
-                ) {
1413
-                    $create_new_action = apply_filters('FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1414
-                        'create_new', $this);
1415
-                    $post_new_file = EE_Admin_Page::add_query_args_and_nonce(array(
1416
-                        'action' => $create_new_action,
1417
-                        'page'   => $this->page_slug,
1418
-                    ), 'admin.php');
1419
-                }
1420
-            }
1421
-            include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1422
-        }
1423
-    }
1424
-
1425
-
1426
-
1427
-    public function add_new_admin_page_global()
1428
-    {
1429
-        $admin_page = ! empty($this->_req_data['post']) ? 'post-php' : 'post-new-php';
1430
-        ?>
1326
+		$messages[$post->post_type] = array(
1327
+			0 => '', //Unused. Messages start at index 1.
1328
+			1 => sprintf(
1329
+				__('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1330
+				$this->_cpt_object->labels->singular_name,
1331
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1332
+				'</a>'
1333
+			),
1334
+			2 => __('Custom field updated'),
1335
+			3 => __('Custom field deleted.'),
1336
+			4 => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1337
+			5 => isset($_GET['revision']) ? sprintf(__('%s restored to revision from %s', 'event_espresso'),
1338
+				$this->_cpt_object->labels->singular_name, wp_post_revision_title((int)$_GET['revision'], false))
1339
+				: false,
1340
+			6 => sprintf(
1341
+				__('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1342
+				$this->_cpt_object->labels->singular_name,
1343
+				'<a href="' . esc_url(get_permalink($id)) . '">',
1344
+				'</a>'
1345
+			),
1346
+			7 => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1347
+			8 => sprintf(
1348
+				__('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1349
+				$this->_cpt_object->labels->singular_name,
1350
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1351
+				'</a>'
1352
+			),
1353
+			9 => sprintf(
1354
+				__('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1355
+				$this->_cpt_object->labels->singular_name,
1356
+				'<strong>' . date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)) . '</strong>',
1357
+				'<a target="_blank" href="' . esc_url(get_permalink($id)),
1358
+				'</a>'
1359
+			),
1360
+			10 => sprintf(
1361
+				__('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1362
+				$this->_cpt_object->labels->singular_name,
1363
+				'<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1364
+				'</a>'
1365
+			),
1366
+		);
1367
+		return $messages;
1368
+	}
1369
+
1370
+
1371
+
1372
+	/**
1373
+	 * default method for the 'create_new' route for cpt admin pages.
1374
+	 * For reference what to include in here, see wp-admin/post-new.php
1375
+	 *
1376
+	 * @access  protected
1377
+	 * @return void
1378
+	 */
1379
+	protected function _create_new_cpt_item()
1380
+	{
1381
+		// gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1382
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1383
+		$post_type        = $this->_cpt_routes[$this->_req_action];
1384
+		$post_type_object = $this->_cpt_object;
1385
+		$title            = $post_type_object->labels->add_new_item;
1386
+		$post    = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1387
+		add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1388
+		//modify the default editor title field with default title.
1389
+		add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1390
+		$this->loadEditorTemplate(true);
1391
+	}
1392
+
1393
+
1394
+	/**
1395
+	 * Enqueues auto-save and loads the editor template
1396
+	 *
1397
+	 * @param bool $creating
1398
+	 */
1399
+	private function loadEditorTemplate($creating = true) {
1400
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1401
+		//these vars are used by the template
1402
+		$editing = true;
1403
+		$post_ID = $post->ID;
1404
+		if (apply_filters('FHEE__EE_Admin_Page_CPT___create_new_cpt_item__replace_editor', false, $post) === false) {
1405
+			//only enqueue autosave when creating event (necessary to get permalink/url generated)
1406
+			//otherwise EE doesn't support autosave fully, so to prevent user confusion we disable it in edit context.
1407
+			if ($creating) {
1408
+				wp_enqueue_script('autosave');
1409
+			} else {
1410
+				if (isset($this->_cpt_routes[$this->_req_data['action']])
1411
+					&& ! isset($this->_labels['hide_add_button_on_cpt_route'][$this->_req_data['action']])
1412
+				) {
1413
+					$create_new_action = apply_filters('FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1414
+						'create_new', $this);
1415
+					$post_new_file = EE_Admin_Page::add_query_args_and_nonce(array(
1416
+						'action' => $create_new_action,
1417
+						'page'   => $this->page_slug,
1418
+					), 'admin.php');
1419
+				}
1420
+			}
1421
+			include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1422
+		}
1423
+	}
1424
+
1425
+
1426
+
1427
+	public function add_new_admin_page_global()
1428
+	{
1429
+		$admin_page = ! empty($this->_req_data['post']) ? 'post-php' : 'post-new-php';
1430
+		?>
1431 1431
         <script type="text/javascript">
1432 1432
             adminpage = '<?php echo $admin_page; ?>';
1433 1433
         </script>
1434 1434
         <?php
1435
-    }
1436
-
1437
-
1438
-
1439
-    /**
1440
-     * default method for the 'edit' route for cpt admin pages
1441
-     * For reference on what to put in here, refer to wp-admin/post.php
1442
-     *
1443
-     * @access protected
1444
-     * @return string   template for edit cpt form
1445
-     */
1446
-    protected function _edit_cpt_item()
1447
-    {
1448
-        global $post, $title, $is_IE, $post_type, $post_type_object;
1449
-        $post_id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1450
-        $post = ! empty($post_id) ? get_post($post_id, OBJECT, 'edit') : null;
1451
-        if (empty ($post)) {
1452
-            wp_die(__('You attempted to edit an item that doesn&#8217;t exist. Perhaps it was deleted?'));
1453
-        }
1454
-        if ( ! empty($_GET['get-post-lock'])) {
1455
-            wp_set_post_lock($post_id);
1456
-            wp_redirect(get_edit_post_link($post_id, 'url'));
1457
-            exit();
1458
-        }
1459
-
1460
-        // template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1461
-        $post_type        = $this->_cpt_routes[$this->_req_action];
1462
-        $post_type_object = $this->_cpt_object;
1463
-
1464
-        if ( ! wp_check_post_lock($post->ID)) {
1465
-            wp_set_post_lock($post->ID);
1466
-        }
1467
-        add_action('admin_footer', '_admin_notice_post_locked');
1468
-        if (post_type_supports($this->_cpt_routes[$this->_req_action], 'comments')) {
1469
-            wp_enqueue_script('admin-comments');
1470
-            enqueue_comment_hotkeys_js();
1471
-        }
1472
-        add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1473
-        //modify the default editor title field with default title.
1474
-        add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1475
-        $this->loadEditorTemplate(false);
1476
-    }
1477
-
1478
-
1479
-
1480
-    /**
1481
-     * some getters
1482
-     */
1483
-    /**
1484
-     * This returns the protected _cpt_model_obj property
1485
-     *
1486
-     * @return EE_CPT_Base
1487
-     */
1488
-    public function get_cpt_model_obj()
1489
-    {
1490
-        return $this->_cpt_model_obj;
1491
-    }
1435
+	}
1436
+
1437
+
1438
+
1439
+	/**
1440
+	 * default method for the 'edit' route for cpt admin pages
1441
+	 * For reference on what to put in here, refer to wp-admin/post.php
1442
+	 *
1443
+	 * @access protected
1444
+	 * @return string   template for edit cpt form
1445
+	 */
1446
+	protected function _edit_cpt_item()
1447
+	{
1448
+		global $post, $title, $is_IE, $post_type, $post_type_object;
1449
+		$post_id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1450
+		$post = ! empty($post_id) ? get_post($post_id, OBJECT, 'edit') : null;
1451
+		if (empty ($post)) {
1452
+			wp_die(__('You attempted to edit an item that doesn&#8217;t exist. Perhaps it was deleted?'));
1453
+		}
1454
+		if ( ! empty($_GET['get-post-lock'])) {
1455
+			wp_set_post_lock($post_id);
1456
+			wp_redirect(get_edit_post_link($post_id, 'url'));
1457
+			exit();
1458
+		}
1459
+
1460
+		// template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1461
+		$post_type        = $this->_cpt_routes[$this->_req_action];
1462
+		$post_type_object = $this->_cpt_object;
1463
+
1464
+		if ( ! wp_check_post_lock($post->ID)) {
1465
+			wp_set_post_lock($post->ID);
1466
+		}
1467
+		add_action('admin_footer', '_admin_notice_post_locked');
1468
+		if (post_type_supports($this->_cpt_routes[$this->_req_action], 'comments')) {
1469
+			wp_enqueue_script('admin-comments');
1470
+			enqueue_comment_hotkeys_js();
1471
+		}
1472
+		add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1473
+		//modify the default editor title field with default title.
1474
+		add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1475
+		$this->loadEditorTemplate(false);
1476
+	}
1477
+
1478
+
1479
+
1480
+	/**
1481
+	 * some getters
1482
+	 */
1483
+	/**
1484
+	 * This returns the protected _cpt_model_obj property
1485
+	 *
1486
+	 * @return EE_CPT_Base
1487
+	 */
1488
+	public function get_cpt_model_obj()
1489
+	{
1490
+		return $this->_cpt_model_obj;
1491
+	}
1492 1492
 
1493 1493
 }
Please login to merge, or discard this patch.
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -239,7 +239,7 @@  discard block
 block discarded – undo
239 239
      */
240 240
     protected function _register_autosave_containers($ids)
241 241
     {
242
-        $this->_autosave_containers = array_merge($this->_autosave_fields, (array)$ids);
242
+        $this->_autosave_containers = array_merge($this->_autosave_fields, (array) $ids);
243 243
     }
244 244
 
245 245
 
@@ -286,7 +286,7 @@  discard block
 block discarded – undo
286 286
         //filter _autosave_containers
287 287
         $containers = apply_filters('FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
288 288
             $this->_autosave_containers, $this);
289
-        $containers = apply_filters('FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
289
+        $containers = apply_filters('FHEE__EE_Admin_Page_CPT__'.get_class($this).'___load_autosave_scripts_styles__containers',
290 290
             $containers, $this);
291 291
 
292 292
         wp_localize_script('event_editor_js', 'EE_AUTOSAVE_IDS',
@@ -398,7 +398,7 @@  discard block
 block discarded – undo
398 398
         // This is for any plugins that are doing things properly
399 399
         // and hooking into the load page hook for core wp cpt routes.
400 400
         global $pagenow;
401
-        do_action('load-' . $pagenow);
401
+        do_action('load-'.$pagenow);
402 402
         $this->modify_current_screen();
403 403
         add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
404 404
         //we route REALLY early.
@@ -429,8 +429,8 @@  discard block
 block discarded – undo
429 429
                 'admin.php?page=espresso_registrations&action=contact_list',
430 430
             ),
431 431
             1 => array(
432
-                'edit.php?post_type=' . $this->_cpt_object->name,
433
-                'admin.php?page=' . $this->_cpt_object->name,
432
+                'edit.php?post_type='.$this->_cpt_object->name,
433
+                'admin.php?page='.$this->_cpt_object->name,
434 434
             ),
435 435
         );
436 436
         foreach ($routes_to_match as $route_matches) {
@@ -458,7 +458,7 @@  discard block
 block discarded – undo
458 458
         $cpt_has_support = ! empty($cpt_args['page_templates']);
459 459
 
460 460
         //if the installed version of WP is > 4.7 we do some additional checks.
461
-        if (RecommendedVersions::compareWordPressVersion('4.7','>=')) {
461
+        if (RecommendedVersions::compareWordPressVersion('4.7', '>=')) {
462 462
             $post_templates = wp_get_theme()->get_post_templates();
463 463
             //if there are $post_templates for this cpt, then we return false for this method because
464 464
             //that means we aren't going to load our page template manager and leave that up to the native
@@ -481,7 +481,7 @@  discard block
 block discarded – undo
481 481
         global $post;
482 482
         $template = '';
483 483
 
484
-        if (RecommendedVersions::compareWordPressVersion('4.7','>=')) {
484
+        if (RecommendedVersions::compareWordPressVersion('4.7', '>=')) {
485 485
             $page_template_count = count(get_page_templates());
486 486
         } else {
487 487
             $page_template_count = count(get_page_templates($post));
@@ -518,7 +518,7 @@  discard block
 block discarded – undo
518 518
         $post = get_post($id);
519 519
         if ('publish' !== get_post_status($post)) {
520 520
             //include shims for the `get_preview_post_link` function
521
-            require_once( EE_CORE . 'wordpress-shims.php' );
521
+            require_once(EE_CORE.'wordpress-shims.php');
522 522
             $return .= '<span_id="view-post-btn"><a target="_blank" href="'
523 523
                        . get_preview_post_link($id)
524 524
                        . '" class="button button-small">'
@@ -556,7 +556,7 @@  discard block
 block discarded – undo
556 556
             $template_args['statuses']         = $statuses;
557 557
         }
558 558
 
559
-        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
559
+        $template = EE_ADMIN_TEMPLATE.'status_dropdown.template.php';
560 560
         EEH_Template::display_template($template, $template_args);
561 561
     }
562 562
 
@@ -610,7 +610,7 @@  discard block
 block discarded – undo
610 610
             $this->_template_args['success'] = true;
611 611
         }
612 612
         do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
613
-        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
613
+        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_'.get_class($this), $this);
614 614
         //now let's return json
615 615
         $this->_return_json();
616 616
     }
@@ -1013,7 +1013,7 @@  discard block
 block discarded – undo
1013 1013
         //global action
1014 1014
         do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1015 1015
         //class specific action so you can limit hooking into a specific page.
1016
-        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1016
+        do_action('AHEE_EE_Admin_Page_CPT_'.get_class($this).'__restore_revision', $post_id, $revision_id);
1017 1017
     }
1018 1018
 
1019 1019
 
@@ -1091,7 +1091,7 @@  discard block
 block discarded – undo
1091 1091
         if ( ! empty($id) && get_option('permalink_structure') !== '') {
1092 1092
             $post = get_post($id);
1093 1093
             if (isset($post->post_type) && $this->page_slug === $post->post_type) {
1094
-                $shortlink = home_url('?p=' . $post->ID);
1094
+                $shortlink = home_url('?p='.$post->ID);
1095 1095
             }
1096 1096
         }
1097 1097
         return $shortlink;
@@ -1126,11 +1126,11 @@  discard block
 block discarded – undo
1126 1126
      */
1127 1127
     public function cpt_post_form_hidden_input()
1128 1128
     {
1129
-        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1129
+        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="'.$this->_admin_base_url.'" />';
1130 1130
         //we're also going to add the route value and the current page so we can direct autosave parsing correctly
1131 1131
         echo '<div id="ee-cpt-hidden-inputs">';
1132
-        echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1133
-        echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1132
+        echo '<input type="hidden" id="current_route" name="current_route" value="'.$this->_current_view.'" />';
1133
+        echo '<input type="hidden" id="current_page" name="current_page" value="'.$this->page_slug.'" />';
1134 1134
         echo '</div>';
1135 1135
     }
1136 1136
 
@@ -1217,7 +1217,7 @@  discard block
 block discarded – undo
1217 1217
         $this->_set_model_object($post->ID, true);
1218 1218
 
1219 1219
         //returns something like `trash_event` or `trash_attendee` or `trash_venue`
1220
-        $action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1220
+        $action = 'trash_'.str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1221 1221
 
1222 1222
         return EE_Admin_Page::add_query_args_and_nonce(
1223 1223
             array(
@@ -1328,39 +1328,39 @@  discard block
 block discarded – undo
1328 1328
             1 => sprintf(
1329 1329
                 __('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1330 1330
                 $this->_cpt_object->labels->singular_name,
1331
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1331
+                '<a href="'.esc_url(get_permalink($id)).'">',
1332 1332
                 '</a>'
1333 1333
             ),
1334 1334
             2 => __('Custom field updated'),
1335 1335
             3 => __('Custom field deleted.'),
1336 1336
             4 => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1337 1337
             5 => isset($_GET['revision']) ? sprintf(__('%s restored to revision from %s', 'event_espresso'),
1338
-                $this->_cpt_object->labels->singular_name, wp_post_revision_title((int)$_GET['revision'], false))
1338
+                $this->_cpt_object->labels->singular_name, wp_post_revision_title((int) $_GET['revision'], false))
1339 1339
                 : false,
1340 1340
             6 => sprintf(
1341 1341
                 __('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1342 1342
                 $this->_cpt_object->labels->singular_name,
1343
-                '<a href="' . esc_url(get_permalink($id)) . '">',
1343
+                '<a href="'.esc_url(get_permalink($id)).'">',
1344 1344
                 '</a>'
1345 1345
             ),
1346 1346
             7 => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1347 1347
             8 => sprintf(
1348 1348
                 __('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1349 1349
                 $this->_cpt_object->labels->singular_name,
1350
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1350
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))).'">',
1351 1351
                 '</a>'
1352 1352
             ),
1353 1353
             9 => sprintf(
1354 1354
                 __('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1355 1355
                 $this->_cpt_object->labels->singular_name,
1356
-                '<strong>' . date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)) . '</strong>',
1357
-                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1356
+                '<strong>'.date_i18n(__('M j, Y @ G:i'), strtotime($post->post_date)).'</strong>',
1357
+                '<a target="_blank" href="'.esc_url(get_permalink($id)),
1358 1358
                 '</a>'
1359 1359
             ),
1360 1360
             10 => sprintf(
1361 1361
                 __('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1362 1362
                 $this->_cpt_object->labels->singular_name,
1363
-                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1363
+                '<a target="_blank" href="'.esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1364 1364
                 '</a>'
1365 1365
             ),
1366 1366
         );
@@ -1383,7 +1383,7 @@  discard block
 block discarded – undo
1383 1383
         $post_type        = $this->_cpt_routes[$this->_req_action];
1384 1384
         $post_type_object = $this->_cpt_object;
1385 1385
         $title            = $post_type_object->labels->add_new_item;
1386
-        $post    = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1386
+        $post = $post = get_default_post_to_edit($this->_cpt_routes[$this->_req_action], true);
1387 1387
         add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1388 1388
         //modify the default editor title field with default title.
1389 1389
         add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
@@ -1418,7 +1418,7 @@  discard block
 block discarded – undo
1418 1418
                     ), 'admin.php');
1419 1419
                 }
1420 1420
             }
1421
-            include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1421
+            include_once WP_ADMIN_PATH.'edit-form-advanced.php';
1422 1422
         }
1423 1423
     }
1424 1424
 
Please login to merge, or discard this patch.
core/db_classes/EE_Base_Class.class.php 3 patches
Indentation   +3141 added lines, -3141 removed lines patch added patch discarded remove patch
@@ -15,3147 +15,3147 @@
 block discarded – undo
15 15
 abstract class EE_Base_Class
16 16
 {
17 17
 
18
-    /**
19
-     * This is an array of the original properties and values provided during construction
20
-     * of this model object. (keys are model field names, values are their values).
21
-     * This list is important to remember so that when we are merging data from the db, we know
22
-     * which values to override and which to not override.
23
-     *
24
-     * @var array
25
-     */
26
-    protected $_props_n_values_provided_in_constructor;
27
-
28
-    /**
29
-     * Timezone
30
-     * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
31
-     * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
32
-     * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
33
-     * access to it.
34
-     *
35
-     * @var string
36
-     */
37
-    protected $_timezone;
38
-
39
-    /**
40
-     * date format
41
-     * pattern or format for displaying dates
42
-     *
43
-     * @var string $_dt_frmt
44
-     */
45
-    protected $_dt_frmt;
46
-
47
-    /**
48
-     * time format
49
-     * pattern or format for displaying time
50
-     *
51
-     * @var string $_tm_frmt
52
-     */
53
-    protected $_tm_frmt;
54
-
55
-    /**
56
-     * This property is for holding a cached array of object properties indexed by property name as the key.
57
-     * The purpose of this is for setting a cache on properties that may have calculated values after a
58
-     * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
59
-     * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
60
-     *
61
-     * @var array
62
-     */
63
-    protected $_cached_properties = array();
64
-
65
-    /**
66
-     * An array containing keys of the related model, and values are either an array of related mode objects or a
67
-     * single
68
-     * related model object. see the model's _model_relations. The keys should match those specified. And if the
69
-     * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
70
-     * all others have an array)
71
-     *
72
-     * @var array
73
-     */
74
-    protected $_model_relations = array();
75
-
76
-    /**
77
-     * Array where keys are field names (see the model's _fields property) and values are their values. To see what
78
-     * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
79
-     *
80
-     * @var array
81
-     */
82
-    protected $_fields = array();
83
-
84
-    /**
85
-     * @var boolean indicating whether or not this model object is intended to ever be saved
86
-     * For example, we might create model objects intended to only be used for the duration
87
-     * of this request and to be thrown away, and if they were accidentally saved
88
-     * it would be a bug.
89
-     */
90
-    protected $_allow_persist = true;
91
-
92
-    /**
93
-     * @var boolean indicating whether or not this model object's properties have changed since construction
94
-     */
95
-    protected $_has_changes = false;
96
-
97
-    /**
98
-     * @var EEM_Base
99
-     */
100
-    protected $_model;
101
-
102
-    /**
103
-     * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
104
-     * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
105
-     * the db.  They also do not automatically update if there are any changes to the data that produced their results.
106
-     * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
107
-     * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
108
-     * array as:
109
-     * array(
110
-     *  'Registration_Count' => 24
111
-     * );
112
-     * Note: if the custom select configuration for the query included a data type, the value will be in the data type
113
-     * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
114
-     * info)
115
-     *
116
-     * @var array
117
-     */
118
-    protected $custom_selection_results = array();
119
-
120
-
121
-    /**
122
-     * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
123
-     * play nice
124
-     *
125
-     * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
126
-     *                                                         layer of the model's _fields array, (eg, EVT_ID,
127
-     *                                                         TXN_amount, QST_name, etc) and values are their values
128
-     * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
129
-     *                                                         corresponding db model or not.
130
-     * @param string  $timezone                                indicate what timezone you want any datetime fields to
131
-     *                                                         be in when instantiating a EE_Base_Class object.
132
-     * @param array   $date_formats                            An array of date formats to set on construct where first
133
-     *                                                         value is the date_format and second value is the time
134
-     *                                                         format.
135
-     * @throws InvalidArgumentException
136
-     * @throws InvalidInterfaceException
137
-     * @throws InvalidDataTypeException
138
-     * @throws EE_Error
139
-     * @throws ReflectionException
140
-     */
141
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
142
-    {
143
-        $className = get_class($this);
144
-        do_action("AHEE__{$className}__construct", $this, $fieldValues);
145
-        $model        = $this->get_model();
146
-        $model_fields = $model->field_settings(false);
147
-        // ensure $fieldValues is an array
148
-        $fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
149
-        // verify client code has not passed any invalid field names
150
-        foreach ($fieldValues as $field_name => $field_value) {
151
-            if (! isset($model_fields[ $field_name ])) {
152
-                throw new EE_Error(
153
-                    sprintf(
154
-                        esc_html__(
155
-                            'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
156
-                            'event_espresso'
157
-                        ),
158
-                        $field_name,
159
-                        get_class($this),
160
-                        implode(', ', array_keys($model_fields))
161
-                    )
162
-                );
163
-            }
164
-        }
165
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
166
-        if (! empty($date_formats) && is_array($date_formats)) {
167
-            list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
168
-        } else {
169
-            //set default formats for date and time
170
-            $this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
171
-            $this->_tm_frmt = (string) get_option('time_format', 'g:i a');
172
-        }
173
-        //if db model is instantiating
174
-        if ($bydb) {
175
-            //client code has indicated these field values are from the database
176
-            foreach ($model_fields as $fieldName => $field) {
177
-                $this->set_from_db(
178
-                    $fieldName,
179
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
180
-                );
181
-            }
182
-        } else {
183
-            //we're constructing a brand
184
-            //new instance of the model object. Generally, this means we'll need to do more field validation
185
-            foreach ($model_fields as $fieldName => $field) {
186
-                $this->set(
187
-                    $fieldName,
188
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null, true
189
-                );
190
-            }
191
-        }
192
-        //remember what values were passed to this constructor
193
-        $this->_props_n_values_provided_in_constructor = $fieldValues;
194
-        //remember in entity mapper
195
-        if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
196
-            $model->add_to_entity_map($this);
197
-        }
198
-        //setup all the relations
199
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
200
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
201
-                $this->_model_relations[ $relation_name ] = null;
202
-            } else {
203
-                $this->_model_relations[ $relation_name ] = array();
204
-            }
205
-        }
206
-        /**
207
-         * Action done at the end of each model object construction
208
-         *
209
-         * @param EE_Base_Class $this the model object just created
210
-         */
211
-        do_action('AHEE__EE_Base_Class__construct__finished', $this);
212
-    }
213
-
214
-
215
-    /**
216
-     * Gets whether or not this model object is allowed to persist/be saved to the database.
217
-     *
218
-     * @return boolean
219
-     */
220
-    public function allow_persist()
221
-    {
222
-        return $this->_allow_persist;
223
-    }
224
-
225
-
226
-    /**
227
-     * Sets whether or not this model object should be allowed to be saved to the DB.
228
-     * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
229
-     * you got new information that somehow made you change your mind.
230
-     *
231
-     * @param boolean $allow_persist
232
-     * @return boolean
233
-     */
234
-    public function set_allow_persist($allow_persist)
235
-    {
236
-        return $this->_allow_persist = $allow_persist;
237
-    }
238
-
239
-
240
-    /**
241
-     * Gets the field's original value when this object was constructed during this request.
242
-     * This can be helpful when determining if a model object has changed or not
243
-     *
244
-     * @param string $field_name
245
-     * @return mixed|null
246
-     * @throws ReflectionException
247
-     * @throws InvalidArgumentException
248
-     * @throws InvalidInterfaceException
249
-     * @throws InvalidDataTypeException
250
-     * @throws EE_Error
251
-     */
252
-    public function get_original($field_name)
253
-    {
254
-        if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
255
-            && $field_settings = $this->get_model()->field_settings_for($field_name)
256
-        ) {
257
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
258
-        }
259
-        return null;
260
-    }
261
-
262
-
263
-    /**
264
-     * @param EE_Base_Class $obj
265
-     * @return string
266
-     */
267
-    public function get_class($obj)
268
-    {
269
-        return get_class($obj);
270
-    }
271
-
272
-
273
-    /**
274
-     * Overrides parent because parent expects old models.
275
-     * This also doesn't do any validation, and won't work for serialized arrays
276
-     *
277
-     * @param    string $field_name
278
-     * @param    mixed  $field_value
279
-     * @param bool      $use_default
280
-     * @throws InvalidArgumentException
281
-     * @throws InvalidInterfaceException
282
-     * @throws InvalidDataTypeException
283
-     * @throws EE_Error
284
-     * @throws ReflectionException
285
-     * @throws ReflectionException
286
-     * @throws ReflectionException
287
-     */
288
-    public function set($field_name, $field_value, $use_default = false)
289
-    {
290
-        // if not using default and nothing has changed, and object has already been setup (has ID),
291
-        // then don't do anything
292
-        if (
293
-            ! $use_default
294
-            && $this->_fields[ $field_name ] === $field_value
295
-            && $this->ID()
296
-        ) {
297
-            return;
298
-        }
299
-        $model              = $this->get_model();
300
-        $this->_has_changes = true;
301
-        $field_obj          = $model->field_settings_for($field_name);
302
-        if ($field_obj instanceof EE_Model_Field_Base) {
303
-            //			if ( method_exists( $field_obj, 'set_timezone' )) {
304
-            if ($field_obj instanceof EE_Datetime_Field) {
305
-                $field_obj->set_timezone($this->_timezone);
306
-                $field_obj->set_date_format($this->_dt_frmt);
307
-                $field_obj->set_time_format($this->_tm_frmt);
308
-            }
309
-            $holder_of_value = $field_obj->prepare_for_set($field_value);
310
-            //should the value be null?
311
-            if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
313
-                /**
314
-                 * To save having to refactor all the models, if a default value is used for a
315
-                 * EE_Datetime_Field, and that value is not null nor is it a DateTime
316
-                 * object.  Then let's do a set again to ensure that it becomes a DateTime
317
-                 * object.
318
-                 *
319
-                 * @since 4.6.10+
320
-                 */
321
-                if (
322
-                    $field_obj instanceof EE_Datetime_Field
323
-                    && $this->_fields[ $field_name ] !== null
324
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
325
-                ) {
326
-                    empty($this->_fields[ $field_name ])
327
-                        ? $this->set($field_name, time())
328
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
329
-                }
330
-            } else {
331
-                $this->_fields[ $field_name ] = $holder_of_value;
332
-            }
333
-            //if we're not in the constructor...
334
-            //now check if what we set was a primary key
335
-            if (
336
-                //note: props_n_values_provided_in_constructor is only set at the END of the constructor
337
-                $this->_props_n_values_provided_in_constructor
338
-                && $field_value
339
-                && $field_name === $model->primary_key_name()
340
-            ) {
341
-                //if so, we want all this object's fields to be filled either with
342
-                //what we've explicitly set on this model
343
-                //or what we have in the db
344
-                // echo "setting primary key!";
345
-                $fields_on_model = self::_get_model(get_class($this))->field_settings();
346
-                $obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347
-                foreach ($fields_on_model as $field_obj) {
348
-                    if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
349
-                        && $field_obj->get_name() !== $field_name
350
-                    ) {
351
-                        $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
352
-                    }
353
-                }
354
-                //oh this model object has an ID? well make sure its in the entity mapper
355
-                $model->add_to_entity_map($this);
356
-            }
357
-            //let's unset any cache for this field_name from the $_cached_properties property.
358
-            $this->_clear_cached_property($field_name);
359
-        } else {
360
-            throw new EE_Error(
361
-                sprintf(
362
-                    esc_html__(
363
-                        'A valid EE_Model_Field_Base could not be found for the given field name: %s',
364
-                        'event_espresso'
365
-                    ),
366
-                    $field_name
367
-                )
368
-            );
369
-        }
370
-    }
371
-
372
-
373
-    /**
374
-     * Set custom select values for model.
375
-     *
376
-     * @param array $custom_select_values
377
-     */
378
-    public function setCustomSelectsValues(array $custom_select_values)
379
-    {
380
-        $this->custom_selection_results = $custom_select_values;
381
-    }
382
-
383
-
384
-    /**
385
-     * Returns the custom select value for the provided alias if its set.
386
-     * If not set, returns null.
387
-     *
388
-     * @param string $alias
389
-     * @return string|int|float|null
390
-     */
391
-    public function getCustomSelect($alias)
392
-    {
393
-        return isset($this->custom_selection_results[ $alias ])
394
-            ? $this->custom_selection_results[ $alias ]
395
-            : null;
396
-    }
397
-
398
-
399
-    /**
400
-     * This sets the field value on the db column if it exists for the given $column_name or
401
-     * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
402
-     *
403
-     * @see EE_message::get_column_value for related documentation on the necessity of this method.
404
-     * @param string $field_name  Must be the exact column name.
405
-     * @param mixed  $field_value The value to set.
406
-     * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
407
-     * @throws InvalidArgumentException
408
-     * @throws InvalidInterfaceException
409
-     * @throws InvalidDataTypeException
410
-     * @throws EE_Error
411
-     * @throws ReflectionException
412
-     */
413
-    public function set_field_or_extra_meta($field_name, $field_value)
414
-    {
415
-        if ($this->get_model()->has_field($field_name)) {
416
-            $this->set($field_name, $field_value);
417
-            return true;
418
-        }
419
-        //ensure this object is saved first so that extra meta can be properly related.
420
-        $this->save();
421
-        return $this->update_extra_meta($field_name, $field_value);
422
-    }
423
-
424
-
425
-    /**
426
-     * This retrieves the value of the db column set on this class or if that's not present
427
-     * it will attempt to retrieve from extra_meta if found.
428
-     * Example Usage:
429
-     * Via EE_Message child class:
430
-     * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
431
-     * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
432
-     * also have additional main fields specific to the messenger.  The system accommodates those extra
433
-     * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
434
-     * value for those extra fields dynamically via the EE_message object.
435
-     *
436
-     * @param  string $field_name expecting the fully qualified field name.
437
-     * @return mixed|null  value for the field if found.  null if not found.
438
-     * @throws ReflectionException
439
-     * @throws InvalidArgumentException
440
-     * @throws InvalidInterfaceException
441
-     * @throws InvalidDataTypeException
442
-     * @throws EE_Error
443
-     */
444
-    public function get_field_or_extra_meta($field_name)
445
-    {
446
-        if ($this->get_model()->has_field($field_name)) {
447
-            $column_value = $this->get($field_name);
448
-        } else {
449
-            //This isn't a column in the main table, let's see if it is in the extra meta.
450
-            $column_value = $this->get_extra_meta($field_name, true, null);
451
-        }
452
-        return $column_value;
453
-    }
454
-
455
-
456
-    /**
457
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
458
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
459
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
460
-     * available to all child classes that may be using the EE_Datetime_Field for a field data type.
461
-     *
462
-     * @access public
463
-     * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
464
-     * @return void
465
-     * @throws InvalidArgumentException
466
-     * @throws InvalidInterfaceException
467
-     * @throws InvalidDataTypeException
468
-     * @throws EE_Error
469
-     * @throws ReflectionException
470
-     */
471
-    public function set_timezone($timezone = '')
472
-    {
473
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
474
-        //make sure we clear all cached properties because they won't be relevant now
475
-        $this->_clear_cached_properties();
476
-        //make sure we update field settings and the date for all EE_Datetime_Fields
477
-        $model_fields = $this->get_model()->field_settings(false);
478
-        foreach ($model_fields as $field_name => $field_obj) {
479
-            if ($field_obj instanceof EE_Datetime_Field) {
480
-                $field_obj->set_timezone($this->_timezone);
481
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
482
-                    $this->_fields[ $field_name ]->setTimezone(new DateTimeZone($this->_timezone));
483
-                }
484
-            }
485
-        }
486
-    }
487
-
488
-
489
-    /**
490
-     * This just returns whatever is set for the current timezone.
491
-     *
492
-     * @access public
493
-     * @return string timezone string
494
-     */
495
-    public function get_timezone()
496
-    {
497
-        return $this->_timezone;
498
-    }
499
-
500
-
501
-    /**
502
-     * This sets the internal date format to what is sent in to be used as the new default for the class
503
-     * internally instead of wp set date format options
504
-     *
505
-     * @since 4.6
506
-     * @param string $format should be a format recognizable by PHP date() functions.
507
-     */
508
-    public function set_date_format($format)
509
-    {
510
-        $this->_dt_frmt = $format;
511
-        //clear cached_properties because they won't be relevant now.
512
-        $this->_clear_cached_properties();
513
-    }
514
-
515
-
516
-    /**
517
-     * This sets the internal time format string to what is sent in to be used as the new default for the
518
-     * class internally instead of wp set time format options.
519
-     *
520
-     * @since 4.6
521
-     * @param string $format should be a format recognizable by PHP date() functions.
522
-     */
523
-    public function set_time_format($format)
524
-    {
525
-        $this->_tm_frmt = $format;
526
-        //clear cached_properties because they won't be relevant now.
527
-        $this->_clear_cached_properties();
528
-    }
529
-
530
-
531
-    /**
532
-     * This returns the current internal set format for the date and time formats.
533
-     *
534
-     * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
535
-     *                             where the first value is the date format and the second value is the time format.
536
-     * @return mixed string|array
537
-     */
538
-    public function get_format($full = true)
539
-    {
540
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541
-    }
542
-
543
-
544
-    /**
545
-     * cache
546
-     * stores the passed model object on the current model object.
547
-     * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
548
-     *
549
-     * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
550
-     *                                       'Registration' associated with this model object
551
-     * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
552
-     *                                       that could be a payment or a registration)
553
-     * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
554
-     *                                       items which will be stored in an array on this object
555
-     * @throws ReflectionException
556
-     * @throws InvalidArgumentException
557
-     * @throws InvalidInterfaceException
558
-     * @throws InvalidDataTypeException
559
-     * @throws EE_Error
560
-     * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
561
-     *                                       related thing, no array)
562
-     */
563
-    public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
564
-    {
565
-        // its entirely possible that there IS no related object yet in which case there is nothing to cache.
566
-        if (! $object_to_cache instanceof EE_Base_Class) {
567
-            return false;
568
-        }
569
-        // also get "how" the object is related, or throw an error
570
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571
-            throw new EE_Error(
572
-                sprintf(
573
-                    esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
574
-                    $relationName,
575
-                    get_class($this)
576
-                )
577
-            );
578
-        }
579
-        // how many things are related ?
580
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
581
-            // if it's a "belongs to" relationship, then there's only one related model object
582
-            // eg, if this is a registration, there's only 1 attendee for it
583
-            // so for these model objects just set it to be cached
584
-            $this->_model_relations[ $relationName ] = $object_to_cache;
585
-            $return                                  = true;
586
-        } else {
587
-            // otherwise, this is the "many" side of a one to many relationship,
588
-            // so we'll add the object to the array of related objects for that type.
589
-            // eg: if this is an event, there are many registrations for that event,
590
-            // so we cache the registrations in an array
591
-            if (! is_array($this->_model_relations[ $relationName ])) {
592
-                // if for some reason, the cached item is a model object,
593
-                // then stick that in the array, otherwise start with an empty array
594
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
595
-                                                           instanceof
596
-                                                           EE_Base_Class
597
-                    ? array($this->_model_relations[ $relationName ]) : array();
598
-            }
599
-            // first check for a cache_id which is normally empty
600
-            if (! empty($cache_id)) {
601
-                // if the cache_id exists, then it means we are purposely trying to cache this
602
-                // with a known key that can then be used to retrieve the object later on
603
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
604
-                $return                                               = $cache_id;
605
-            } elseif ($object_to_cache->ID()) {
606
-                // OR the cached object originally came from the db, so let's just use it's PK for an ID
607
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
608
-                $return                                                            = $object_to_cache->ID();
609
-            } else {
610
-                // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
611
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
612
-                // move the internal pointer to the end of the array
613
-                end($this->_model_relations[ $relationName ]);
614
-                // and grab the key so that we can return it
615
-                $return = key($this->_model_relations[ $relationName ]);
616
-            }
617
-        }
618
-        return $return;
619
-    }
620
-
621
-
622
-    /**
623
-     * For adding an item to the cached_properties property.
624
-     *
625
-     * @access protected
626
-     * @param string      $fieldname the property item the corresponding value is for.
627
-     * @param mixed       $value     The value we are caching.
628
-     * @param string|null $cache_type
629
-     * @return void
630
-     * @throws ReflectionException
631
-     * @throws InvalidArgumentException
632
-     * @throws InvalidInterfaceException
633
-     * @throws InvalidDataTypeException
634
-     * @throws EE_Error
635
-     */
636
-    protected function _set_cached_property($fieldname, $value, $cache_type = null)
637
-    {
638
-        //first make sure this property exists
639
-        $this->get_model()->field_settings_for($fieldname);
640
-        $cache_type                                            = empty($cache_type) ? 'standard' : $cache_type;
641
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
642
-    }
643
-
644
-
645
-    /**
646
-     * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
647
-     * This also SETS the cache if we return the actual property!
648
-     *
649
-     * @param string $fieldname        the name of the property we're trying to retrieve
650
-     * @param bool   $pretty
651
-     * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
652
-     *                                 (in cases where the same property may be used for different outputs
653
-     *                                 - i.e. datetime, money etc.)
654
-     *                                 It can also accept certain pre-defined "schema" strings
655
-     *                                 to define how to output the property.
656
-     *                                 see the field's prepare_for_pretty_echoing for what strings can be used
657
-     * @return mixed                   whatever the value for the property is we're retrieving
658
-     * @throws ReflectionException
659
-     * @throws InvalidArgumentException
660
-     * @throws InvalidInterfaceException
661
-     * @throws InvalidDataTypeException
662
-     * @throws EE_Error
663
-     */
664
-    protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
665
-    {
666
-        //verify the field exists
667
-        $model = $this->get_model();
668
-        $model->field_settings_for($fieldname);
669
-        $cache_type = $pretty ? 'pretty' : 'standard';
670
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
671
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
672
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
673
-        }
674
-        $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
675
-        $this->_set_cached_property($fieldname, $value, $cache_type);
676
-        return $value;
677
-    }
678
-
679
-
680
-    /**
681
-     * If the cache didn't fetch the needed item, this fetches it.
682
-     *
683
-     * @param string $fieldname
684
-     * @param bool   $pretty
685
-     * @param string $extra_cache_ref
686
-     * @return mixed
687
-     * @throws InvalidArgumentException
688
-     * @throws InvalidInterfaceException
689
-     * @throws InvalidDataTypeException
690
-     * @throws EE_Error
691
-     * @throws ReflectionException
692
-     */
693
-    protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
694
-    {
695
-        $field_obj = $this->get_model()->field_settings_for($fieldname);
696
-        // If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
697
-        if ($field_obj instanceof EE_Datetime_Field) {
698
-            $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
699
-        }
700
-        if (! isset($this->_fields[ $fieldname ])) {
701
-            $this->_fields[ $fieldname ] = null;
702
-        }
703
-        $value = $pretty
704
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
705
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
706
-        return $value;
707
-    }
708
-
709
-
710
-    /**
711
-     * set timezone, formats, and output for EE_Datetime_Field objects
712
-     *
713
-     * @param \EE_Datetime_Field $datetime_field
714
-     * @param bool               $pretty
715
-     * @param null               $date_or_time
716
-     * @return void
717
-     * @throws InvalidArgumentException
718
-     * @throws InvalidInterfaceException
719
-     * @throws InvalidDataTypeException
720
-     * @throws EE_Error
721
-     */
722
-    protected function _prepare_datetime_field(
723
-        EE_Datetime_Field $datetime_field,
724
-        $pretty = false,
725
-        $date_or_time = null
726
-    ) {
727
-        $datetime_field->set_timezone($this->_timezone);
728
-        $datetime_field->set_date_format($this->_dt_frmt, $pretty);
729
-        $datetime_field->set_time_format($this->_tm_frmt, $pretty);
730
-        //set the output returned
731
-        switch ($date_or_time) {
732
-            case 'D' :
733
-                $datetime_field->set_date_time_output('date');
734
-                break;
735
-            case 'T' :
736
-                $datetime_field->set_date_time_output('time');
737
-                break;
738
-            default :
739
-                $datetime_field->set_date_time_output();
740
-        }
741
-    }
742
-
743
-
744
-    /**
745
-     * This just takes care of clearing out the cached_properties
746
-     *
747
-     * @return void
748
-     */
749
-    protected function _clear_cached_properties()
750
-    {
751
-        $this->_cached_properties = array();
752
-    }
753
-
754
-
755
-    /**
756
-     * This just clears out ONE property if it exists in the cache
757
-     *
758
-     * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
759
-     * @return void
760
-     */
761
-    protected function _clear_cached_property($property_name)
762
-    {
763
-        if (isset($this->_cached_properties[ $property_name ])) {
764
-            unset($this->_cached_properties[ $property_name ]);
765
-        }
766
-    }
767
-
768
-
769
-    /**
770
-     * Ensures that this related thing is a model object.
771
-     *
772
-     * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
773
-     * @param string $model_name   name of the related thing, eg 'Attendee',
774
-     * @return EE_Base_Class
775
-     * @throws ReflectionException
776
-     * @throws InvalidArgumentException
777
-     * @throws InvalidInterfaceException
778
-     * @throws InvalidDataTypeException
779
-     * @throws EE_Error
780
-     */
781
-    protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
782
-    {
783
-        $other_model_instance = self::_get_model_instance_with_name(
784
-            self::_get_model_classname($model_name),
785
-            $this->_timezone
786
-        );
787
-        return $other_model_instance->ensure_is_obj($object_or_id);
788
-    }
789
-
790
-
791
-    /**
792
-     * Forgets the cached model of the given relation Name. So the next time we request it,
793
-     * we will fetch it again from the database. (Handy if you know it's changed somehow).
794
-     * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
795
-     * then only remove that one object from our cached array. Otherwise, clear the entire list
796
-     *
797
-     * @param string $relationName                         one of the keys in the _model_relations array on the model.
798
-     *                                                     Eg 'Registration'
799
-     * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
800
-     *                                                     if you intend to use $clear_all = TRUE, or the relation only
801
-     *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
802
-     * @param bool   $clear_all                            This flags clearing the entire cache relation property if
803
-     *                                                     this is HasMany or HABTM.
804
-     * @throws ReflectionException
805
-     * @throws InvalidArgumentException
806
-     * @throws InvalidInterfaceException
807
-     * @throws InvalidDataTypeException
808
-     * @throws EE_Error
809
-     * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
810
-     *                                                     relation from all
811
-     */
812
-    public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
813
-    {
814
-        $relationship_to_model = $this->get_model()->related_settings_for($relationName);
815
-        $index_in_cache        = '';
816
-        if (! $relationship_to_model) {
817
-            throw new EE_Error(
818
-                sprintf(
819
-                    esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
820
-                    $relationName,
821
-                    get_class($this)
822
-                )
823
-            );
824
-        }
825
-        if ($clear_all) {
826
-            $obj_removed                             = true;
827
-            $this->_model_relations[ $relationName ] = null;
828
-        } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
829
-            $obj_removed                             = $this->_model_relations[ $relationName ];
830
-            $this->_model_relations[ $relationName ] = null;
831
-        } else {
832
-            if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
833
-                && $object_to_remove_or_index_into_array->ID()
834
-            ) {
835
-                $index_in_cache = $object_to_remove_or_index_into_array->ID();
836
-                if (is_array($this->_model_relations[ $relationName ])
837
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
838
-                ) {
839
-                    $index_found_at = null;
840
-                    //find this object in the array even though it has a different key
841
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
842
-                        /** @noinspection TypeUnsafeComparisonInspection */
843
-                        if (
844
-                            $obj instanceof EE_Base_Class
845
-                            && (
846
-                                $obj == $object_to_remove_or_index_into_array
847
-                                || $obj->ID() === $object_to_remove_or_index_into_array->ID()
848
-                            )
849
-                        ) {
850
-                            $index_found_at = $index;
851
-                            break;
852
-                        }
853
-                    }
854
-                    if ($index_found_at) {
855
-                        $index_in_cache = $index_found_at;
856
-                    } else {
857
-                        //it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
858
-                        //if it wasn't in it to begin with. So we're done
859
-                        return $object_to_remove_or_index_into_array;
860
-                    }
861
-                }
862
-            } elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
863
-                //so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
864
-                foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
865
-                    /** @noinspection TypeUnsafeComparisonInspection */
866
-                    if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
867
-                        $index_in_cache = $index;
868
-                    }
869
-                }
870
-            } else {
871
-                $index_in_cache = $object_to_remove_or_index_into_array;
872
-            }
873
-            //supposedly we've found it. But it could just be that the client code
874
-            //provided a bad index/object
875
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
876
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
877
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
878
-            } else {
879
-                //that thing was never cached anyways.
880
-                $obj_removed = null;
881
-            }
882
-        }
883
-        return $obj_removed;
884
-    }
885
-
886
-
887
-    /**
888
-     * update_cache_after_object_save
889
-     * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
890
-     * obtained after being saved to the db
891
-     *
892
-     * @param string        $relationName       - the type of object that is cached
893
-     * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
894
-     * @param string        $current_cache_id   - the ID that was used when originally caching the object
895
-     * @return boolean TRUE on success, FALSE on fail
896
-     * @throws ReflectionException
897
-     * @throws InvalidArgumentException
898
-     * @throws InvalidInterfaceException
899
-     * @throws InvalidDataTypeException
900
-     * @throws EE_Error
901
-     */
902
-    public function update_cache_after_object_save(
903
-        $relationName,
904
-        EE_Base_Class $newly_saved_object,
905
-        $current_cache_id = ''
906
-    ) {
907
-        // verify that incoming object is of the correct type
908
-        $obj_class = 'EE_' . $relationName;
909
-        if ($newly_saved_object instanceof $obj_class) {
910
-            /* @type EE_Base_Class $newly_saved_object */
911
-            // now get the type of relation
912
-            $relationship_to_model = $this->get_model()->related_settings_for($relationName);
913
-            // if this is a 1:1 relationship
914
-            if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
915
-                // then just replace the cached object with the newly saved object
916
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
917
-                return true;
918
-                // or if it's some kind of sordid feral polyamorous relationship...
919
-            }
920
-            if (is_array($this->_model_relations[ $relationName ])
921
-                      && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
922
-            ) {
923
-                // then remove the current cached item
924
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
925
-                // and cache the newly saved object using it's new ID
926
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
927
-                return true;
928
-            }
929
-        }
930
-        return false;
931
-    }
932
-
933
-
934
-    /**
935
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
936
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
937
-     *
938
-     * @param string $relationName
939
-     * @return EE_Base_Class
940
-     */
941
-    public function get_one_from_cache($relationName)
942
-    {
943
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
944
-            ? $this->_model_relations[ $relationName ]
945
-            : null;
946
-        if (is_array($cached_array_or_object)) {
947
-            return array_shift($cached_array_or_object);
948
-        }
949
-        return $cached_array_or_object;
950
-    }
951
-
952
-
953
-    /**
954
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
955
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
956
-     *
957
-     * @param string $relationName
958
-     * @throws ReflectionException
959
-     * @throws InvalidArgumentException
960
-     * @throws InvalidInterfaceException
961
-     * @throws InvalidDataTypeException
962
-     * @throws EE_Error
963
-     * @return EE_Base_Class[] NOT necessarily indexed by primary keys
964
-     */
965
-    public function get_all_from_cache($relationName)
966
-    {
967
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
968
-        // if the result is not an array, but exists, make it an array
969
-        $objects = is_array($objects) ? $objects : array($objects);
970
-        //bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
971
-        //basically, if this model object was stored in the session, and these cached model objects
972
-        //already have IDs, let's make sure they're in their model's entity mapper
973
-        //otherwise we will have duplicates next time we call
974
-        // EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
975
-        $model = EE_Registry::instance()->load_model($relationName);
976
-        foreach ($objects as $model_object) {
977
-            if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
978
-                //ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
979
-                if ($model_object->ID()) {
980
-                    $model->add_to_entity_map($model_object);
981
-                }
982
-            } else {
983
-                throw new EE_Error(
984
-                    sprintf(
985
-                        esc_html__(
986
-                            'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
987
-                            'event_espresso'
988
-                        ),
989
-                        $relationName,
990
-                        gettype($model_object)
991
-                    )
992
-                );
993
-            }
994
-        }
995
-        return $objects;
996
-    }
997
-
998
-
999
-    /**
1000
-     * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1001
-     * matching the given query conditions.
1002
-     *
1003
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1004
-     * @param int   $limit              How many objects to return.
1005
-     * @param array $query_params       Any additional conditions on the query.
1006
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1007
-     *                                  you can indicate just the columns you want returned
1008
-     * @return array|EE_Base_Class[]
1009
-     * @throws ReflectionException
1010
-     * @throws InvalidArgumentException
1011
-     * @throws InvalidInterfaceException
1012
-     * @throws InvalidDataTypeException
1013
-     * @throws EE_Error
1014
-     */
1015
-    public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1016
-    {
1017
-        $model         = $this->get_model();
1018
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1019
-            ? $model->get_primary_key_field()->get_name()
1020
-            : $field_to_order_by;
1021
-        $current_value = ! empty($field) ? $this->get($field) : null;
1022
-        if (empty($field) || empty($current_value)) {
1023
-            return array();
1024
-        }
1025
-        return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1026
-    }
1027
-
1028
-
1029
-    /**
1030
-     * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1031
-     * matching the given query conditions.
1032
-     *
1033
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1034
-     * @param int   $limit              How many objects to return.
1035
-     * @param array $query_params       Any additional conditions on the query.
1036
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1037
-     *                                  you can indicate just the columns you want returned
1038
-     * @return array|EE_Base_Class[]
1039
-     * @throws ReflectionException
1040
-     * @throws InvalidArgumentException
1041
-     * @throws InvalidInterfaceException
1042
-     * @throws InvalidDataTypeException
1043
-     * @throws EE_Error
1044
-     */
1045
-    public function previous_x(
1046
-        $field_to_order_by = null,
1047
-        $limit = 1,
1048
-        $query_params = array(),
1049
-        $columns_to_select = null
1050
-    ) {
1051
-        $model         = $this->get_model();
1052
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1053
-            ? $model->get_primary_key_field()->get_name()
1054
-            : $field_to_order_by;
1055
-        $current_value = ! empty($field) ? $this->get($field) : null;
1056
-        if (empty($field) || empty($current_value)) {
1057
-            return array();
1058
-        }
1059
-        return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1060
-    }
1061
-
1062
-
1063
-    /**
1064
-     * Returns the next EE_Base_Class object in sequence from this object as found in the database
1065
-     * matching the given query conditions.
1066
-     *
1067
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1068
-     * @param array $query_params       Any additional conditions on the query.
1069
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1070
-     *                                  you can indicate just the columns you want returned
1071
-     * @return array|EE_Base_Class
1072
-     * @throws ReflectionException
1073
-     * @throws InvalidArgumentException
1074
-     * @throws InvalidInterfaceException
1075
-     * @throws InvalidDataTypeException
1076
-     * @throws EE_Error
1077
-     */
1078
-    public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1079
-    {
1080
-        $model         = $this->get_model();
1081
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1082
-            ? $model->get_primary_key_field()->get_name()
1083
-            : $field_to_order_by;
1084
-        $current_value = ! empty($field) ? $this->get($field) : null;
1085
-        if (empty($field) || empty($current_value)) {
1086
-            return array();
1087
-        }
1088
-        return $model->next($current_value, $field, $query_params, $columns_to_select);
1089
-    }
1090
-
1091
-
1092
-    /**
1093
-     * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1094
-     * matching the given query conditions.
1095
-     *
1096
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1097
-     * @param array $query_params       Any additional conditions on the query.
1098
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1099
-     *                                  you can indicate just the column you want returned
1100
-     * @return array|EE_Base_Class
1101
-     * @throws ReflectionException
1102
-     * @throws InvalidArgumentException
1103
-     * @throws InvalidInterfaceException
1104
-     * @throws InvalidDataTypeException
1105
-     * @throws EE_Error
1106
-     */
1107
-    public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1108
-    {
1109
-        $model         = $this->get_model();
1110
-        $field         = empty($field_to_order_by) && $model->has_primary_key_field()
1111
-            ? $model->get_primary_key_field()->get_name()
1112
-            : $field_to_order_by;
1113
-        $current_value = ! empty($field) ? $this->get($field) : null;
1114
-        if (empty($field) || empty($current_value)) {
1115
-            return array();
1116
-        }
1117
-        return $model->previous($current_value, $field, $query_params, $columns_to_select);
1118
-    }
1119
-
1120
-
1121
-    /**
1122
-     * Overrides parent because parent expects old models.
1123
-     * This also doesn't do any validation, and won't work for serialized arrays
1124
-     *
1125
-     * @param string $field_name
1126
-     * @param mixed  $field_value_from_db
1127
-     * @throws ReflectionException
1128
-     * @throws InvalidArgumentException
1129
-     * @throws InvalidInterfaceException
1130
-     * @throws InvalidDataTypeException
1131
-     * @throws EE_Error
1132
-     */
1133
-    public function set_from_db($field_name, $field_value_from_db)
1134
-    {
1135
-        $field_obj = $this->get_model()->field_settings_for($field_name);
1136
-        if ($field_obj instanceof EE_Model_Field_Base) {
1137
-            //you would think the DB has no NULLs for non-null label fields right? wrong!
1138
-            //eg, a CPT model object could have an entry in the posts table, but no
1139
-            //entry in the meta table. Meaning that all its columns in the meta table
1140
-            //are null! yikes! so when we find one like that, use defaults for its meta columns
1141
-            if ($field_value_from_db === null) {
1142
-                if ($field_obj->is_nullable()) {
1143
-                    //if the field allows nulls, then let it be null
1144
-                    $field_value = null;
1145
-                } else {
1146
-                    $field_value = $field_obj->get_default_value();
1147
-                }
1148
-            } else {
1149
-                $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1150
-            }
1151
-            $this->_fields[ $field_name ] = $field_value;
1152
-            $this->_clear_cached_property($field_name);
1153
-        }
1154
-    }
1155
-
1156
-
1157
-    /**
1158
-     * verifies that the specified field is of the correct type
1159
-     *
1160
-     * @param string $field_name
1161
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1162
-     *                                (in cases where the same property may be used for different outputs
1163
-     *                                - i.e. datetime, money etc.)
1164
-     * @return mixed
1165
-     * @throws ReflectionException
1166
-     * @throws InvalidArgumentException
1167
-     * @throws InvalidInterfaceException
1168
-     * @throws InvalidDataTypeException
1169
-     * @throws EE_Error
1170
-     */
1171
-    public function get($field_name, $extra_cache_ref = null)
1172
-    {
1173
-        return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1174
-    }
1175
-
1176
-
1177
-    /**
1178
-     * This method simply returns the RAW unprocessed value for the given property in this class
1179
-     *
1180
-     * @param  string $field_name A valid fieldname
1181
-     * @return mixed              Whatever the raw value stored on the property is.
1182
-     * @throws ReflectionException
1183
-     * @throws InvalidArgumentException
1184
-     * @throws InvalidInterfaceException
1185
-     * @throws InvalidDataTypeException
1186
-     * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1187
-     */
1188
-    public function get_raw($field_name)
1189
-    {
1190
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1191
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1192
-            ? $this->_fields[ $field_name ]->format('U')
1193
-            : $this->_fields[ $field_name ];
1194
-    }
1195
-
1196
-
1197
-    /**
1198
-     * This is used to return the internal DateTime object used for a field that is a
1199
-     * EE_Datetime_Field.
1200
-     *
1201
-     * @param string $field_name               The field name retrieving the DateTime object.
1202
-     * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1203
-     * @throws EE_Error an error is set and false returned.  If the field IS an
1204
-     *                                         EE_Datetime_Field and but the field value is null, then
1205
-     *                                         just null is returned (because that indicates that likely
1206
-     *                                         this field is nullable).
1207
-     * @throws InvalidArgumentException
1208
-     * @throws InvalidDataTypeException
1209
-     * @throws InvalidInterfaceException
1210
-     * @throws ReflectionException
1211
-     */
1212
-    public function get_DateTime_object($field_name)
1213
-    {
1214
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1215
-        if (! $field_settings instanceof EE_Datetime_Field) {
1216
-            EE_Error::add_error(
1217
-                sprintf(
1218
-                    esc_html__(
1219
-                        'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1220
-                        'event_espresso'
1221
-                    ),
1222
-                    $field_name
1223
-                ),
1224
-                __FILE__,
1225
-                __FUNCTION__,
1226
-                __LINE__
1227
-            );
1228
-            return false;
1229
-        }
1230
-        return isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime
1231
-            ? clone $this->_fields[$field_name]
1232
-            : null;
1233
-    }
1234
-
1235
-
1236
-    /**
1237
-     * To be used in template to immediately echo out the value, and format it for output.
1238
-     * Eg, should call stripslashes and whatnot before echoing
1239
-     *
1240
-     * @param string $field_name      the name of the field as it appears in the DB
1241
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1242
-     *                                (in cases where the same property may be used for different outputs
1243
-     *                                - i.e. datetime, money etc.)
1244
-     * @return void
1245
-     * @throws ReflectionException
1246
-     * @throws InvalidArgumentException
1247
-     * @throws InvalidInterfaceException
1248
-     * @throws InvalidDataTypeException
1249
-     * @throws EE_Error
1250
-     */
1251
-    public function e($field_name, $extra_cache_ref = null)
1252
-    {
1253
-        echo $this->get_pretty($field_name, $extra_cache_ref);
1254
-    }
1255
-
1256
-
1257
-    /**
1258
-     * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1259
-     * can be easily used as the value of form input.
1260
-     *
1261
-     * @param string $field_name
1262
-     * @return void
1263
-     * @throws ReflectionException
1264
-     * @throws InvalidArgumentException
1265
-     * @throws InvalidInterfaceException
1266
-     * @throws InvalidDataTypeException
1267
-     * @throws EE_Error
1268
-     */
1269
-    public function f($field_name)
1270
-    {
1271
-        $this->e($field_name, 'form_input');
1272
-    }
1273
-
1274
-
1275
-    /**
1276
-     * Same as `f()` but just returns the value instead of echoing it
1277
-     *
1278
-     * @param string $field_name
1279
-     * @return string
1280
-     * @throws ReflectionException
1281
-     * @throws InvalidArgumentException
1282
-     * @throws InvalidInterfaceException
1283
-     * @throws InvalidDataTypeException
1284
-     * @throws EE_Error
1285
-     */
1286
-    public function get_f($field_name)
1287
-    {
1288
-        return (string) $this->get_pretty($field_name, 'form_input');
1289
-    }
1290
-
1291
-
1292
-    /**
1293
-     * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1294
-     * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1295
-     * to see what options are available.
1296
-     *
1297
-     * @param string $field_name
1298
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1299
-     *                                (in cases where the same property may be used for different outputs
1300
-     *                                - i.e. datetime, money etc.)
1301
-     * @return mixed
1302
-     * @throws ReflectionException
1303
-     * @throws InvalidArgumentException
1304
-     * @throws InvalidInterfaceException
1305
-     * @throws InvalidDataTypeException
1306
-     * @throws EE_Error
1307
-     */
1308
-    public function get_pretty($field_name, $extra_cache_ref = null)
1309
-    {
1310
-        return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1311
-    }
1312
-
1313
-
1314
-    /**
1315
-     * This simply returns the datetime for the given field name
1316
-     * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1317
-     * (and the equivalent e_date, e_time, e_datetime).
1318
-     *
1319
-     * @access   protected
1320
-     * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1321
-     * @param string   $dt_frmt      valid datetime format used for date
1322
-     *                               (if '' then we just use the default on the field,
1323
-     *                               if NULL we use the last-used format)
1324
-     * @param string   $tm_frmt      Same as above except this is for time format
1325
-     * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1326
-     * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1327
-     * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1328
-     *                               if field is not a valid dtt field, or void if echoing
1329
-     * @throws ReflectionException
1330
-     * @throws InvalidArgumentException
1331
-     * @throws InvalidInterfaceException
1332
-     * @throws InvalidDataTypeException
1333
-     * @throws EE_Error
1334
-     */
1335
-    protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1336
-    {
1337
-        // clear cached property
1338
-        $this->_clear_cached_property($field_name);
1339
-        //reset format properties because they are used in get()
1340
-        $this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1341
-        $this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1342
-        if ($echo) {
1343
-            $this->e($field_name, $date_or_time);
1344
-            return '';
1345
-        }
1346
-        return $this->get($field_name, $date_or_time);
1347
-    }
1348
-
1349
-
1350
-    /**
1351
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1352
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1353
-     * other echoes the pretty value for dtt)
1354
-     *
1355
-     * @param  string $field_name name of model object datetime field holding the value
1356
-     * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1357
-     * @return string            datetime value formatted
1358
-     * @throws ReflectionException
1359
-     * @throws InvalidArgumentException
1360
-     * @throws InvalidInterfaceException
1361
-     * @throws InvalidDataTypeException
1362
-     * @throws EE_Error
1363
-     */
1364
-    public function get_date($field_name, $format = '')
1365
-    {
1366
-        return $this->_get_datetime($field_name, $format, null, 'D');
1367
-    }
1368
-
1369
-
1370
-    /**
1371
-     * @param        $field_name
1372
-     * @param string $format
1373
-     * @throws ReflectionException
1374
-     * @throws InvalidArgumentException
1375
-     * @throws InvalidInterfaceException
1376
-     * @throws InvalidDataTypeException
1377
-     * @throws EE_Error
1378
-     */
1379
-    public function e_date($field_name, $format = '')
1380
-    {
1381
-        $this->_get_datetime($field_name, $format, null, 'D', true);
1382
-    }
1383
-
1384
-
1385
-    /**
1386
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1387
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1388
-     * other echoes the pretty value for dtt)
1389
-     *
1390
-     * @param  string $field_name name of model object datetime field holding the value
1391
-     * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1392
-     * @return string             datetime value formatted
1393
-     * @throws ReflectionException
1394
-     * @throws InvalidArgumentException
1395
-     * @throws InvalidInterfaceException
1396
-     * @throws InvalidDataTypeException
1397
-     * @throws EE_Error
1398
-     */
1399
-    public function get_time($field_name, $format = '')
1400
-    {
1401
-        return $this->_get_datetime($field_name, null, $format, 'T');
1402
-    }
1403
-
1404
-
1405
-    /**
1406
-     * @param        $field_name
1407
-     * @param string $format
1408
-     * @throws ReflectionException
1409
-     * @throws InvalidArgumentException
1410
-     * @throws InvalidInterfaceException
1411
-     * @throws InvalidDataTypeException
1412
-     * @throws EE_Error
1413
-     */
1414
-    public function e_time($field_name, $format = '')
1415
-    {
1416
-        $this->_get_datetime($field_name, null, $format, 'T', true);
1417
-    }
1418
-
1419
-
1420
-    /**
1421
-     * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1422
-     * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1423
-     * other echoes the pretty value for dtt)
1424
-     *
1425
-     * @param  string $field_name name of model object datetime field holding the value
1426
-     * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1427
-     * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1428
-     * @return string             datetime value formatted
1429
-     * @throws ReflectionException
1430
-     * @throws InvalidArgumentException
1431
-     * @throws InvalidInterfaceException
1432
-     * @throws InvalidDataTypeException
1433
-     * @throws EE_Error
1434
-     */
1435
-    public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1436
-    {
1437
-        return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1438
-    }
1439
-
1440
-
1441
-    /**
1442
-     * @param string $field_name
1443
-     * @param string $dt_frmt
1444
-     * @param string $tm_frmt
1445
-     * @throws ReflectionException
1446
-     * @throws InvalidArgumentException
1447
-     * @throws InvalidInterfaceException
1448
-     * @throws InvalidDataTypeException
1449
-     * @throws EE_Error
1450
-     */
1451
-    public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1452
-    {
1453
-        $this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1454
-    }
1455
-
1456
-
1457
-    /**
1458
-     * Get the i8ln value for a date using the WordPress @see date_i18n function.
1459
-     *
1460
-     * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1461
-     * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1462
-     *                           on the object will be used.
1463
-     * @return string Date and time string in set locale or false if no field exists for the given
1464
-     * @throws ReflectionException
1465
-     * @throws InvalidArgumentException
1466
-     * @throws InvalidInterfaceException
1467
-     * @throws InvalidDataTypeException
1468
-     * @throws EE_Error
1469
-     *                           field name.
1470
-     */
1471
-    public function get_i18n_datetime($field_name, $format = '')
1472
-    {
1473
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1474
-        return date_i18n(
1475
-            $format,
1476
-            EEH_DTT_Helper::get_timestamp_with_offset(
1477
-                $this->get_raw($field_name),
1478
-                $this->_timezone
1479
-            )
1480
-        );
1481
-    }
1482
-
1483
-
1484
-    /**
1485
-     * This method validates whether the given field name is a valid field on the model object as well as it is of a
1486
-     * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1487
-     * thrown.
1488
-     *
1489
-     * @param  string $field_name The field name being checked
1490
-     * @throws ReflectionException
1491
-     * @throws InvalidArgumentException
1492
-     * @throws InvalidInterfaceException
1493
-     * @throws InvalidDataTypeException
1494
-     * @throws EE_Error
1495
-     * @return EE_Datetime_Field
1496
-     */
1497
-    protected function _get_dtt_field_settings($field_name)
1498
-    {
1499
-        $field = $this->get_model()->field_settings_for($field_name);
1500
-        //check if field is dtt
1501
-        if ($field instanceof EE_Datetime_Field) {
1502
-            return $field;
1503
-        }
1504
-        throw new EE_Error(
1505
-            sprintf(
1506
-                esc_html__(
1507
-                    'The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1508
-                    'event_espresso'
1509
-                ),
1510
-                $field_name,
1511
-                self::_get_model_classname(get_class($this))
1512
-            )
1513
-        );
1514
-    }
1515
-
1516
-
1517
-
1518
-
1519
-    /**
1520
-     * NOTE ABOUT BELOW:
1521
-     * These convenience date and time setters are for setting date and time independently.  In other words you might
1522
-     * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1523
-     * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1524
-     * method and make sure you send the entire datetime value for setting.
1525
-     */
1526
-    /**
1527
-     * sets the time on a datetime property
1528
-     *
1529
-     * @access protected
1530
-     * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1531
-     * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1532
-     * @throws ReflectionException
1533
-     * @throws InvalidArgumentException
1534
-     * @throws InvalidInterfaceException
1535
-     * @throws InvalidDataTypeException
1536
-     * @throws EE_Error
1537
-     */
1538
-    protected function _set_time_for($time, $fieldname)
1539
-    {
1540
-        $this->_set_date_time('T', $time, $fieldname);
1541
-    }
1542
-
1543
-
1544
-    /**
1545
-     * sets the date on a datetime property
1546
-     *
1547
-     * @access protected
1548
-     * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1549
-     * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1550
-     * @throws ReflectionException
1551
-     * @throws InvalidArgumentException
1552
-     * @throws InvalidInterfaceException
1553
-     * @throws InvalidDataTypeException
1554
-     * @throws EE_Error
1555
-     */
1556
-    protected function _set_date_for($date, $fieldname)
1557
-    {
1558
-        $this->_set_date_time('D', $date, $fieldname);
1559
-    }
1560
-
1561
-
1562
-    /**
1563
-     * This takes care of setting a date or time independently on a given model object property. This method also
1564
-     * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1565
-     *
1566
-     * @access protected
1567
-     * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1568
-     * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1569
-     * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1570
-     *                                        EE_Datetime_Field property)
1571
-     * @throws ReflectionException
1572
-     * @throws InvalidArgumentException
1573
-     * @throws InvalidInterfaceException
1574
-     * @throws InvalidDataTypeException
1575
-     * @throws EE_Error
1576
-     */
1577
-    protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1578
-    {
1579
-        $field = $this->_get_dtt_field_settings($fieldname);
1580
-        $field->set_timezone($this->_timezone);
1581
-        $field->set_date_format($this->_dt_frmt);
1582
-        $field->set_time_format($this->_tm_frmt);
1583
-        switch ($what) {
1584
-            case 'T' :
1585
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1586
-                    $datetime_value,
1587
-                    $this->_fields[ $fieldname ]
1588
-                );
1589
-                break;
1590
-            case 'D' :
1591
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1592
-                    $datetime_value,
1593
-                    $this->_fields[ $fieldname ]
1594
-                );
1595
-                break;
1596
-            case 'B' :
1597
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1598
-                break;
1599
-        }
1600
-        $this->_clear_cached_property($fieldname);
1601
-    }
1602
-
1603
-
1604
-    /**
1605
-     * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1606
-     * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1607
-     * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1608
-     * that could lead to some unexpected results!
1609
-     *
1610
-     * @access public
1611
-     * @param string $field_name               This is the name of the field on the object that contains the date/time
1612
-     *                                         value being returned.
1613
-     * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1614
-     * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1615
-     * @param string $prepend                  You can include something to prepend on the timestamp
1616
-     * @param string $append                   You can include something to append on the timestamp
1617
-     * @throws ReflectionException
1618
-     * @throws InvalidArgumentException
1619
-     * @throws InvalidInterfaceException
1620
-     * @throws InvalidDataTypeException
1621
-     * @throws EE_Error
1622
-     * @return string timestamp
1623
-     */
1624
-    public function display_in_my_timezone(
1625
-        $field_name,
1626
-        $callback = 'get_datetime',
1627
-        $args = null,
1628
-        $prepend = '',
1629
-        $append = ''
1630
-    ) {
1631
-        $timezone = EEH_DTT_Helper::get_timezone();
1632
-        if ($timezone === $this->_timezone) {
1633
-            return '';
1634
-        }
1635
-        $original_timezone = $this->_timezone;
1636
-        $this->set_timezone($timezone);
1637
-        $fn   = (array) $field_name;
1638
-        $args = array_merge($fn, (array) $args);
1639
-        if (! method_exists($this, $callback)) {
1640
-            throw new EE_Error(
1641
-                sprintf(
1642
-                    esc_html__(
1643
-                        'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1644
-                        'event_espresso'
1645
-                    ),
1646
-                    $callback
1647
-                )
1648
-            );
1649
-        }
1650
-        $args   = (array) $args;
1651
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1652
-        $this->set_timezone($original_timezone);
1653
-        return $return;
1654
-    }
1655
-
1656
-
1657
-    /**
1658
-     * Deletes this model object.
1659
-     * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1660
-     * override
1661
-     * `EE_Base_Class::_delete` NOT this class.
1662
-     *
1663
-     * @return boolean | int
1664
-     * @throws ReflectionException
1665
-     * @throws InvalidArgumentException
1666
-     * @throws InvalidInterfaceException
1667
-     * @throws InvalidDataTypeException
1668
-     * @throws EE_Error
1669
-     */
1670
-    public function delete()
1671
-    {
1672
-        /**
1673
-         * Called just before the `EE_Base_Class::_delete` method call.
1674
-         * Note:
1675
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1676
-         * should be aware that `_delete` may not always result in a permanent delete.
1677
-         * For example, `EE_Soft_Delete_Base_Class::_delete`
1678
-         * soft deletes (trash) the object and does not permanently delete it.
1679
-         *
1680
-         * @param EE_Base_Class $model_object about to be 'deleted'
1681
-         */
1682
-        do_action('AHEE__EE_Base_Class__delete__before', $this);
1683
-        $result = $this->_delete();
1684
-        /**
1685
-         * Called just after the `EE_Base_Class::_delete` method call.
1686
-         * Note:
1687
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1688
-         * should be aware that `_delete` may not always result in a permanent delete.
1689
-         * For example `EE_Soft_Base_Class::_delete`
1690
-         * soft deletes (trash) the object and does not permanently delete it.
1691
-         *
1692
-         * @param EE_Base_Class $model_object that was just 'deleted'
1693
-         * @param boolean       $result
1694
-         */
1695
-        do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1696
-        return $result;
1697
-    }
1698
-
1699
-
1700
-    /**
1701
-     * Calls the specific delete method for the instantiated class.
1702
-     * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1703
-     * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1704
-     * `EE_Base_Class::delete`
1705
-     *
1706
-     * @return bool|int
1707
-     * @throws ReflectionException
1708
-     * @throws InvalidArgumentException
1709
-     * @throws InvalidInterfaceException
1710
-     * @throws InvalidDataTypeException
1711
-     * @throws EE_Error
1712
-     */
1713
-    protected function _delete()
1714
-    {
1715
-        return $this->delete_permanently();
1716
-    }
1717
-
1718
-
1719
-    /**
1720
-     * Deletes this model object permanently from db
1721
-     * (but keep in mind related models may block the delete and return an error)
1722
-     *
1723
-     * @return bool | int
1724
-     * @throws ReflectionException
1725
-     * @throws InvalidArgumentException
1726
-     * @throws InvalidInterfaceException
1727
-     * @throws InvalidDataTypeException
1728
-     * @throws EE_Error
1729
-     */
1730
-    public function delete_permanently()
1731
-    {
1732
-        /**
1733
-         * Called just before HARD deleting a model object
1734
-         *
1735
-         * @param EE_Base_Class $model_object about to be 'deleted'
1736
-         */
1737
-        do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1738
-        $model  = $this->get_model();
1739
-        $result = $model->delete_permanently_by_ID($this->ID());
1740
-        $this->refresh_cache_of_related_objects();
1741
-        /**
1742
-         * Called just after HARD deleting a model object
1743
-         *
1744
-         * @param EE_Base_Class $model_object that was just 'deleted'
1745
-         * @param boolean       $result
1746
-         */
1747
-        do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1748
-        return $result;
1749
-    }
1750
-
1751
-
1752
-    /**
1753
-     * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1754
-     * related model objects
1755
-     *
1756
-     * @throws ReflectionException
1757
-     * @throws InvalidArgumentException
1758
-     * @throws InvalidInterfaceException
1759
-     * @throws InvalidDataTypeException
1760
-     * @throws EE_Error
1761
-     */
1762
-    public function refresh_cache_of_related_objects()
1763
-    {
1764
-        $model = $this->get_model();
1765
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1766
-            if (! empty($this->_model_relations[ $relation_name ])) {
1767
-                $related_objects = $this->_model_relations[ $relation_name ];
1768
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
1769
-                    //this relation only stores a single model object, not an array
1770
-                    //but let's make it consistent
1771
-                    $related_objects = array($related_objects);
1772
-                }
1773
-                foreach ($related_objects as $related_object) {
1774
-                    //only refresh their cache if they're in memory
1775
-                    if ($related_object instanceof EE_Base_Class) {
1776
-                        $related_object->clear_cache(
1777
-                            $model->get_this_model_name(),
1778
-                            $this
1779
-                        );
1780
-                    }
1781
-                }
1782
-            }
1783
-        }
1784
-    }
1785
-
1786
-
1787
-    /**
1788
-     *        Saves this object to the database. An array may be supplied to set some values on this
1789
-     * object just before saving.
1790
-     *
1791
-     * @access public
1792
-     * @param array $set_cols_n_values keys are field names, values are their new values,
1793
-     *                                 if provided during the save() method (often client code will change the fields'
1794
-     *                                 values before calling save)
1795
-     * @throws InvalidArgumentException
1796
-     * @throws InvalidInterfaceException
1797
-     * @throws InvalidDataTypeException
1798
-     * @throws EE_Error
1799
-     * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1800
-     *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1801
-     * @throws ReflectionException
1802
-     * @throws ReflectionException
1803
-     * @throws ReflectionException
1804
-     */
1805
-    public function save($set_cols_n_values = array())
1806
-    {
1807
-        $model = $this->get_model();
1808
-        /**
1809
-         * Filters the fields we're about to save on the model object
1810
-         *
1811
-         * @param array         $set_cols_n_values
1812
-         * @param EE_Base_Class $model_object
1813
-         */
1814
-        $set_cols_n_values = (array) apply_filters(
1815
-            'FHEE__EE_Base_Class__save__set_cols_n_values',
1816
-            $set_cols_n_values,
1817
-            $this
1818
-        );
1819
-        //set attributes as provided in $set_cols_n_values
1820
-        foreach ($set_cols_n_values as $column => $value) {
1821
-            $this->set($column, $value);
1822
-        }
1823
-        // no changes ? then don't do anything
1824
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1825
-            return 0;
1826
-        }
1827
-        /**
1828
-         * Saving a model object.
1829
-         * Before we perform a save, this action is fired.
1830
-         *
1831
-         * @param EE_Base_Class $model_object the model object about to be saved.
1832
-         */
1833
-        do_action('AHEE__EE_Base_Class__save__begin', $this);
1834
-        if (! $this->allow_persist()) {
1835
-            return 0;
1836
-        }
1837
-        // now get current attribute values
1838
-        $save_cols_n_values = $this->_fields;
1839
-        // if the object already has an ID, update it. Otherwise, insert it
1840
-        // also: change the assumption about values passed to the model NOT being prepare dby the model object.
1841
-        // They have been
1842
-        $old_assumption_concerning_value_preparation = $model
1843
-            ->get_assumption_concerning_values_already_prepared_by_model_object();
1844
-        $model->assume_values_already_prepared_by_model_object(true);
1845
-        //does this model have an autoincrement PK?
1846
-        if ($model->has_primary_key_field()) {
1847
-            if ($model->get_primary_key_field()->is_auto_increment()) {
1848
-                //ok check if it's set, if so: update; if not, insert
1849
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1850
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1851
-                } else {
1852
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1853
-                    $results = $model->insert($save_cols_n_values);
1854
-                    if ($results) {
1855
-                        //if successful, set the primary key
1856
-                        //but don't use the normal SET method, because it will check if
1857
-                        //an item with the same ID exists in the mapper & db, then
1858
-                        //will find it in the db (because we just added it) and THAT object
1859
-                        //will get added to the mapper before we can add this one!
1860
-                        //but if we just avoid using the SET method, all that headache can be avoided
1861
-                        $pk_field_name                   = $model->primary_key_name();
1862
-                        $this->_fields[ $pk_field_name ] = $results;
1863
-                        $this->_clear_cached_property($pk_field_name);
1864
-                        $model->add_to_entity_map($this);
1865
-                        $this->_update_cached_related_model_objs_fks();
1866
-                    }
1867
-                }
1868
-            } else {//PK is NOT auto-increment
1869
-                //so check if one like it already exists in the db
1870
-                if ($model->exists_by_ID($this->ID())) {
1871
-                    if (WP_DEBUG && ! $this->in_entity_map()) {
1872
-                        throw new EE_Error(
1873
-                            sprintf(
1874
-                                esc_html__(
1875
-                                    'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1876
-                                    'event_espresso'
1877
-                                ),
1878
-                                get_class($this),
1879
-                                get_class($model) . '::instance()->add_to_entity_map()',
1880
-                                get_class($model) . '::instance()->get_one_by_ID()',
1881
-                                '<br />'
1882
-                            )
1883
-                        );
1884
-                    }
1885
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1886
-                } else {
1887
-                    $results = $model->insert($save_cols_n_values);
1888
-                    $this->_update_cached_related_model_objs_fks();
1889
-                }
1890
-            }
1891
-        } else {//there is NO primary key
1892
-            $already_in_db = false;
1893
-            foreach ($model->unique_indexes() as $index) {
1894
-                $uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1895
-                if ($model->exists(array($uniqueness_where_params))) {
1896
-                    $already_in_db = true;
1897
-                }
1898
-            }
1899
-            if ($already_in_db) {
1900
-                $combined_pk_fields_n_values = array_intersect_key($save_cols_n_values,
1901
-                    $model->get_combined_primary_key_fields());
1902
-                $results                     = $model->update(
1903
-                    $save_cols_n_values,
1904
-                    $combined_pk_fields_n_values
1905
-                );
1906
-            } else {
1907
-                $results = $model->insert($save_cols_n_values);
1908
-            }
1909
-        }
1910
-        //restore the old assumption about values being prepared by the model object
1911
-        $model->assume_values_already_prepared_by_model_object(
1912
-                $old_assumption_concerning_value_preparation
1913
-            );
1914
-        /**
1915
-         * After saving the model object this action is called
1916
-         *
1917
-         * @param EE_Base_Class $model_object which was just saved
1918
-         * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1919
-         *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1920
-         */
1921
-        do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1922
-        $this->_has_changes = false;
1923
-        return $results;
1924
-    }
1925
-
1926
-
1927
-    /**
1928
-     * Updates the foreign key on related models objects pointing to this to have this model object's ID
1929
-     * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1930
-     * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1931
-     * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1932
-     * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1933
-     * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1934
-     * or not they exist in the DB (if they do, their DB records will be automatically updated)
1935
-     *
1936
-     * @return void
1937
-     * @throws ReflectionException
1938
-     * @throws InvalidArgumentException
1939
-     * @throws InvalidInterfaceException
1940
-     * @throws InvalidDataTypeException
1941
-     * @throws EE_Error
1942
-     */
1943
-    protected function _update_cached_related_model_objs_fks()
1944
-    {
1945
-        $model = $this->get_model();
1946
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1947
-            if ($relation_obj instanceof EE_Has_Many_Relation) {
1948
-                foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1949
-                    $fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1950
-                        $model->get_this_model_name()
1951
-                    );
1952
-                    $related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1953
-                    if ($related_model_obj_in_cache->ID()) {
1954
-                        $related_model_obj_in_cache->save();
1955
-                    }
1956
-                }
1957
-            }
1958
-        }
1959
-    }
1960
-
1961
-
1962
-    /**
1963
-     * Saves this model object and its NEW cached relations to the database.
1964
-     * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1965
-     * In order for that to work, we would need to mark model objects as dirty/clean...
1966
-     * because otherwise, there's a potential for infinite looping of saving
1967
-     * Saves the cached related model objects, and ensures the relation between them
1968
-     * and this object and properly setup
1969
-     *
1970
-     * @return int ID of new model object on save; 0 on failure+
1971
-     * @throws ReflectionException
1972
-     * @throws InvalidArgumentException
1973
-     * @throws InvalidInterfaceException
1974
-     * @throws InvalidDataTypeException
1975
-     * @throws EE_Error
1976
-     */
1977
-    public function save_new_cached_related_model_objs()
1978
-    {
1979
-        //make sure this has been saved
1980
-        if (! $this->ID()) {
1981
-            $id = $this->save();
1982
-        } else {
1983
-            $id = $this->ID();
1984
-        }
1985
-        //now save all the NEW cached model objects  (ie they don't exist in the DB)
1986
-        foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1987
-            if ($this->_model_relations[ $relationName ]) {
1988
-                //is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1989
-                //or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1990
-                /* @var $related_model_obj EE_Base_Class */
1991
-                if ($relationObj instanceof EE_Belongs_To_Relation) {
1992
-                    //add a relation to that relation type (which saves the appropriate thing in the process)
1993
-                    //but ONLY if it DOES NOT exist in the DB
1994
-                    $related_model_obj = $this->_model_relations[ $relationName ];
1995
-                    //					if( ! $related_model_obj->ID()){
1996
-                    $this->_add_relation_to($related_model_obj, $relationName);
1997
-                    $related_model_obj->save_new_cached_related_model_objs();
1998
-                    //					}
1999
-                } else {
2000
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2001
-                        //add a relation to that relation type (which saves the appropriate thing in the process)
2002
-                        //but ONLY if it DOES NOT exist in the DB
2003
-                        //						if( ! $related_model_obj->ID()){
2004
-                        $this->_add_relation_to($related_model_obj, $relationName);
2005
-                        $related_model_obj->save_new_cached_related_model_objs();
2006
-                        //						}
2007
-                    }
2008
-                }
2009
-            }
2010
-        }
2011
-        return $id;
2012
-    }
2013
-
2014
-
2015
-    /**
2016
-     * for getting a model while instantiated.
2017
-     *
2018
-     * @return EEM_Base | EEM_CPT_Base
2019
-     * @throws ReflectionException
2020
-     * @throws InvalidArgumentException
2021
-     * @throws InvalidInterfaceException
2022
-     * @throws InvalidDataTypeException
2023
-     * @throws EE_Error
2024
-     */
2025
-    public function get_model()
2026
-    {
2027
-        if (! $this->_model) {
2028
-            $modelName    = self::_get_model_classname(get_class($this));
2029
-            $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2030
-        } else {
2031
-            $this->_model->set_timezone($this->_timezone);
2032
-        }
2033
-        return $this->_model;
2034
-    }
2035
-
2036
-
2037
-    /**
2038
-     * @param $props_n_values
2039
-     * @param $classname
2040
-     * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2041
-     * @throws ReflectionException
2042
-     * @throws InvalidArgumentException
2043
-     * @throws InvalidInterfaceException
2044
-     * @throws InvalidDataTypeException
2045
-     * @throws EE_Error
2046
-     */
2047
-    protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2048
-    {
2049
-        //TODO: will not work for Term_Relationships because they have no PK!
2050
-        $primary_id_ref = self::_get_primary_key_name($classname);
2051
-        if (
2052
-            array_key_exists($primary_id_ref, $props_n_values)
2053
-            && ! empty($props_n_values[ $primary_id_ref ])
2054
-        ) {
2055
-            $id = $props_n_values[ $primary_id_ref ];
2056
-            return self::_get_model($classname)->get_from_entity_map($id);
2057
-        }
2058
-        return false;
2059
-    }
2060
-
2061
-
2062
-    /**
2063
-     * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2064
-     * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2065
-     * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
2066
-     * we return false.
2067
-     *
2068
-     * @param  array  $props_n_values   incoming array of properties and their values
2069
-     * @param  string $classname        the classname of the child class
2070
-     * @param null    $timezone
2071
-     * @param array   $date_formats     incoming date_formats in an array where the first value is the
2072
-     *                                  date_format and the second value is the time format
2073
-     * @return mixed (EE_Base_Class|bool)
2074
-     * @throws InvalidArgumentException
2075
-     * @throws InvalidInterfaceException
2076
-     * @throws InvalidDataTypeException
2077
-     * @throws EE_Error
2078
-     * @throws ReflectionException
2079
-     * @throws ReflectionException
2080
-     * @throws ReflectionException
2081
-     */
2082
-    protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2083
-    {
2084
-        $existing = null;
2085
-        $model    = self::_get_model($classname, $timezone);
2086
-        if ($model->has_primary_key_field()) {
2087
-            $primary_id_ref = self::_get_primary_key_name($classname);
2088
-            if (array_key_exists($primary_id_ref, $props_n_values)
2089
-                && ! empty($props_n_values[ $primary_id_ref ])
2090
-            ) {
2091
-                $existing = $model->get_one_by_ID(
2092
-                    $props_n_values[ $primary_id_ref ]
2093
-                );
2094
-            }
2095
-        } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2096
-            //no primary key on this model, but there's still a matching item in the DB
2097
-            $existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2098
-                self::_get_model($classname, $timezone)
2099
-                    ->get_index_primary_key_string($props_n_values)
2100
-            );
2101
-        }
2102
-        if ($existing) {
2103
-            //set date formats if present before setting values
2104
-            if (! empty($date_formats) && is_array($date_formats)) {
2105
-                $existing->set_date_format($date_formats[0]);
2106
-                $existing->set_time_format($date_formats[1]);
2107
-            } else {
2108
-                //set default formats for date and time
2109
-                $existing->set_date_format(get_option('date_format'));
2110
-                $existing->set_time_format(get_option('time_format'));
2111
-            }
2112
-            foreach ($props_n_values as $property => $field_value) {
2113
-                $existing->set($property, $field_value);
2114
-            }
2115
-            return $existing;
2116
-        }
2117
-        return false;
2118
-    }
2119
-
2120
-
2121
-    /**
2122
-     * Gets the EEM_*_Model for this class
2123
-     *
2124
-     * @access public now, as this is more convenient
2125
-     * @param      $classname
2126
-     * @param null $timezone
2127
-     * @throws ReflectionException
2128
-     * @throws InvalidArgumentException
2129
-     * @throws InvalidInterfaceException
2130
-     * @throws InvalidDataTypeException
2131
-     * @throws EE_Error
2132
-     * @return EEM_Base
2133
-     */
2134
-    protected static function _get_model($classname, $timezone = null)
2135
-    {
2136
-        //find model for this class
2137
-        if (! $classname) {
2138
-            throw new EE_Error(
2139
-                sprintf(
2140
-                    esc_html__(
2141
-                        'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2142
-                        'event_espresso'
2143
-                    ),
2144
-                    $classname
2145
-                )
2146
-            );
2147
-        }
2148
-        $modelName = self::_get_model_classname($classname);
2149
-        return self::_get_model_instance_with_name($modelName, $timezone);
2150
-    }
2151
-
2152
-
2153
-    /**
2154
-     * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2155
-     *
2156
-     * @param string $model_classname
2157
-     * @param null   $timezone
2158
-     * @return EEM_Base
2159
-     * @throws ReflectionException
2160
-     * @throws InvalidArgumentException
2161
-     * @throws InvalidInterfaceException
2162
-     * @throws InvalidDataTypeException
2163
-     * @throws EE_Error
2164
-     */
2165
-    protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2166
-    {
2167
-        $model_classname = str_replace('EEM_', '', $model_classname);
2168
-        $model           = EE_Registry::instance()->load_model($model_classname);
2169
-        $model->set_timezone($timezone);
2170
-        return $model;
2171
-    }
2172
-
2173
-
2174
-    /**
2175
-     * If a model name is provided (eg Registration), gets the model classname for that model.
2176
-     * Also works if a model class's classname is provided (eg EE_Registration).
2177
-     *
2178
-     * @param null $model_name
2179
-     * @return string like EEM_Attendee
2180
-     */
2181
-    private static function _get_model_classname($model_name = null)
2182
-    {
2183
-        if (strpos($model_name, 'EE_') === 0) {
2184
-            $model_classname = str_replace('EE_', 'EEM_', $model_name);
2185
-        } else {
2186
-            $model_classname = 'EEM_' . $model_name;
2187
-        }
2188
-        return $model_classname;
2189
-    }
2190
-
2191
-
2192
-    /**
2193
-     * returns the name of the primary key attribute
2194
-     *
2195
-     * @param null $classname
2196
-     * @throws ReflectionException
2197
-     * @throws InvalidArgumentException
2198
-     * @throws InvalidInterfaceException
2199
-     * @throws InvalidDataTypeException
2200
-     * @throws EE_Error
2201
-     * @return string
2202
-     */
2203
-    protected static function _get_primary_key_name($classname = null)
2204
-    {
2205
-        if (! $classname) {
2206
-            throw new EE_Error(
2207
-                sprintf(
2208
-                    esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2209
-                    $classname
2210
-                )
2211
-            );
2212
-        }
2213
-        return self::_get_model($classname)->get_primary_key_field()->get_name();
2214
-    }
2215
-
2216
-
2217
-    /**
2218
-     * Gets the value of the primary key.
2219
-     * If the object hasn't yet been saved, it should be whatever the model field's default was
2220
-     * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2221
-     * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2222
-     *
2223
-     * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2224
-     * @throws ReflectionException
2225
-     * @throws InvalidArgumentException
2226
-     * @throws InvalidInterfaceException
2227
-     * @throws InvalidDataTypeException
2228
-     * @throws EE_Error
2229
-     */
2230
-    public function ID()
2231
-    {
2232
-        $model = $this->get_model();
2233
-        //now that we know the name of the variable, use a variable variable to get its value and return its
2234
-        if ($model->has_primary_key_field()) {
2235
-            return $this->_fields[ $model->primary_key_name() ];
2236
-        }
2237
-        return $model->get_index_primary_key_string($this->_fields);
2238
-    }
2239
-
2240
-
2241
-    /**
2242
-     * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2243
-     * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2244
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2245
-     *
2246
-     * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2247
-     * @param string $relationName                     eg 'Events','Question',etc.
2248
-     *                                                 an attendee to a group, you also want to specify which role they
2249
-     *                                                 will have in that group. So you would use this parameter to
2250
-     *                                                 specify array('role-column-name'=>'role-id')
2251
-     * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2252
-     *                                                 allow you to further constrict the relation to being added.
2253
-     *                                                 However, keep in mind that the columns (keys) given must match a
2254
-     *                                                 column on the JOIN table and currently only the HABTM models
2255
-     *                                                 accept these additional conditions.  Also remember that if an
2256
-     *                                                 exact match isn't found for these extra cols/val pairs, then a
2257
-     *                                                 NEW row is created in the join table.
2258
-     * @param null   $cache_id
2259
-     * @throws ReflectionException
2260
-     * @throws InvalidArgumentException
2261
-     * @throws InvalidInterfaceException
2262
-     * @throws InvalidDataTypeException
2263
-     * @throws EE_Error
2264
-     * @return EE_Base_Class the object the relation was added to
2265
-     */
2266
-    public function _add_relation_to(
2267
-        $otherObjectModelObjectOrID,
2268
-        $relationName,
2269
-        $extra_join_model_fields_n_values = array(),
2270
-        $cache_id = null
2271
-    ) {
2272
-        $model = $this->get_model();
2273
-        //if this thing exists in the DB, save the relation to the DB
2274
-        if ($this->ID()) {
2275
-            $otherObject = $model->add_relationship_to(
2276
-                $this,
2277
-                $otherObjectModelObjectOrID,
2278
-                $relationName,
2279
-                $extra_join_model_fields_n_values
2280
-            );
2281
-            //clear cache so future get_many_related and get_first_related() return new results.
2282
-            $this->clear_cache($relationName, $otherObject, true);
2283
-            if ($otherObject instanceof EE_Base_Class) {
2284
-                $otherObject->clear_cache($model->get_this_model_name(), $this);
2285
-            }
2286
-        } else {
2287
-            //this thing doesn't exist in the DB,  so just cache it
2288
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2289
-                throw new EE_Error(
2290
-                    sprintf(
2291
-                        esc_html__(
2292
-                            'Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
2293
-                            'event_espresso'
2294
-                        ),
2295
-                        $otherObjectModelObjectOrID,
2296
-                        get_class($this)
2297
-                    )
2298
-                );
2299
-            }
2300
-            $otherObject = $otherObjectModelObjectOrID;
2301
-            $this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2302
-        }
2303
-        if ($otherObject instanceof EE_Base_Class) {
2304
-            //fix the reciprocal relation too
2305
-            if ($otherObject->ID()) {
2306
-                //its saved so assumed relations exist in the DB, so we can just
2307
-                //clear the cache so future queries use the updated info in the DB
2308
-                $otherObject->clear_cache(
2309
-                    $model->get_this_model_name(),
2310
-                    null,
2311
-                    true
2312
-                );
2313
-            } else {
2314
-                //it's not saved, so it caches relations like this
2315
-                $otherObject->cache($model->get_this_model_name(), $this);
2316
-            }
2317
-        }
2318
-        return $otherObject;
2319
-    }
2320
-
2321
-
2322
-    /**
2323
-     * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2324
-     * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2325
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2326
-     * from the cache
2327
-     *
2328
-     * @param mixed  $otherObjectModelObjectOrID
2329
-     *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2330
-     *                to the DB yet
2331
-     * @param string $relationName
2332
-     * @param array  $where_query
2333
-     *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2334
-     *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2335
-     *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2336
-     *                remember that if an exact match isn't found for these extra cols/val pairs, then a NEW row is
2337
-     *                created in the join table.
2338
-     * @return EE_Base_Class the relation was removed from
2339
-     * @throws ReflectionException
2340
-     * @throws InvalidArgumentException
2341
-     * @throws InvalidInterfaceException
2342
-     * @throws InvalidDataTypeException
2343
-     * @throws EE_Error
2344
-     */
2345
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2346
-    {
2347
-        if ($this->ID()) {
2348
-            //if this exists in the DB, save the relation change to the DB too
2349
-            $otherObject = $this->get_model()->remove_relationship_to(
2350
-                $this,
2351
-                $otherObjectModelObjectOrID,
2352
-                $relationName,
2353
-                $where_query
2354
-            );
2355
-            $this->clear_cache(
2356
-                $relationName,
2357
-                $otherObject
2358
-            );
2359
-        } else {
2360
-            //this doesn't exist in the DB, just remove it from the cache
2361
-            $otherObject = $this->clear_cache(
2362
-                $relationName,
2363
-                $otherObjectModelObjectOrID
2364
-            );
2365
-        }
2366
-        if ($otherObject instanceof EE_Base_Class) {
2367
-            $otherObject->clear_cache(
2368
-                $this->get_model()->get_this_model_name(),
2369
-                $this
2370
-            );
2371
-        }
2372
-        return $otherObject;
2373
-    }
2374
-
2375
-
2376
-    /**
2377
-     * Removes ALL the related things for the $relationName.
2378
-     *
2379
-     * @param string $relationName
2380
-     * @param array  $where_query_params like EEM_Base::get_all's $query_params[0] (where conditions)
2381
-     * @return EE_Base_Class
2382
-     * @throws ReflectionException
2383
-     * @throws InvalidArgumentException
2384
-     * @throws InvalidInterfaceException
2385
-     * @throws InvalidDataTypeException
2386
-     * @throws EE_Error
2387
-     */
2388
-    public function _remove_relations($relationName, $where_query_params = array())
2389
-    {
2390
-        if ($this->ID()) {
2391
-            //if this exists in the DB, save the relation change to the DB too
2392
-            $otherObjects = $this->get_model()->remove_relations(
2393
-                $this,
2394
-                $relationName,
2395
-                $where_query_params
2396
-            );
2397
-            $this->clear_cache(
2398
-                $relationName,
2399
-                null,
2400
-                true
2401
-            );
2402
-        } else {
2403
-            //this doesn't exist in the DB, just remove it from the cache
2404
-            $otherObjects = $this->clear_cache(
2405
-                $relationName,
2406
-                null,
2407
-                true
2408
-            );
2409
-        }
2410
-        if (is_array($otherObjects)) {
2411
-            foreach ($otherObjects as $otherObject) {
2412
-                $otherObject->clear_cache(
2413
-                    $this->get_model()->get_this_model_name(),
2414
-                    $this
2415
-                );
2416
-            }
2417
-        }
2418
-        return $otherObjects;
2419
-    }
2420
-
2421
-
2422
-    /**
2423
-     * Gets all the related model objects of the specified type. Eg, if the current class if
2424
-     * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2425
-     * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2426
-     * because we want to get even deleted items etc.
2427
-     *
2428
-     * @param string $relationName key in the model's _model_relations array
2429
-     * @param array  $query_params like EEM_Base::get_all
2430
-     * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2431
-     *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2432
-     *                             results if you want IDs
2433
-     * @throws ReflectionException
2434
-     * @throws InvalidArgumentException
2435
-     * @throws InvalidInterfaceException
2436
-     * @throws InvalidDataTypeException
2437
-     * @throws EE_Error
2438
-     */
2439
-    public function get_many_related($relationName, $query_params = array())
2440
-    {
2441
-        if ($this->ID()) {
2442
-            //this exists in the DB, so get the related things from either the cache or the DB
2443
-            //if there are query parameters, forget about caching the related model objects.
2444
-            if ($query_params) {
2445
-                $related_model_objects = $this->get_model()->get_all_related(
2446
-                    $this,
2447
-                    $relationName,
2448
-                    $query_params
2449
-                );
2450
-            } else {
2451
-                //did we already cache the result of this query?
2452
-                $cached_results = $this->get_all_from_cache($relationName);
2453
-                if (! $cached_results) {
2454
-                    $related_model_objects = $this->get_model()->get_all_related(
2455
-                        $this,
2456
-                        $relationName,
2457
-                        $query_params
2458
-                    );
2459
-                    //if no query parameters were passed, then we got all the related model objects
2460
-                    //for that relation. We can cache them then.
2461
-                    foreach ($related_model_objects as $related_model_object) {
2462
-                        $this->cache($relationName, $related_model_object);
2463
-                    }
2464
-                } else {
2465
-                    $related_model_objects = $cached_results;
2466
-                }
2467
-            }
2468
-        } else {
2469
-            //this doesn't exist in the DB, so just get the related things from the cache
2470
-            $related_model_objects = $this->get_all_from_cache($relationName);
2471
-        }
2472
-        return $related_model_objects;
2473
-    }
2474
-
2475
-
2476
-    /**
2477
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2478
-     * unless otherwise specified in the $query_params
2479
-     *
2480
-     * @param string $relation_name  model_name like 'Event', or 'Registration'
2481
-     * @param array  $query_params   like EEM_Base::get_all's
2482
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2483
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2484
-     *                               that by the setting $distinct to TRUE;
2485
-     * @return int
2486
-     * @throws ReflectionException
2487
-     * @throws InvalidArgumentException
2488
-     * @throws InvalidInterfaceException
2489
-     * @throws InvalidDataTypeException
2490
-     * @throws EE_Error
2491
-     */
2492
-    public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2493
-    {
2494
-        return $this->get_model()->count_related(
2495
-            $this,
2496
-            $relation_name,
2497
-            $query_params,
2498
-            $field_to_count,
2499
-            $distinct
2500
-        );
2501
-    }
2502
-
2503
-
2504
-    /**
2505
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2506
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2507
-     *
2508
-     * @param string $relation_name model_name like 'Event', or 'Registration'
2509
-     * @param array  $query_params  like EEM_Base::get_all's
2510
-     * @param string $field_to_sum  name of field to count by.
2511
-     *                              By default, uses primary key
2512
-     *                              (which doesn't make much sense, so you should probably change it)
2513
-     * @return int
2514
-     * @throws ReflectionException
2515
-     * @throws InvalidArgumentException
2516
-     * @throws InvalidInterfaceException
2517
-     * @throws InvalidDataTypeException
2518
-     * @throws EE_Error
2519
-     */
2520
-    public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2521
-    {
2522
-        return $this->get_model()->sum_related(
2523
-            $this,
2524
-            $relation_name,
2525
-            $query_params,
2526
-            $field_to_sum
2527
-        );
2528
-    }
2529
-
2530
-
2531
-    /**
2532
-     * Gets the first (ie, one) related model object of the specified type.
2533
-     *
2534
-     * @param string $relationName key in the model's _model_relations array
2535
-     * @param array  $query_params like EEM_Base::get_all
2536
-     * @return EE_Base_Class (not an array, a single object)
2537
-     * @throws ReflectionException
2538
-     * @throws InvalidArgumentException
2539
-     * @throws InvalidInterfaceException
2540
-     * @throws InvalidDataTypeException
2541
-     * @throws EE_Error
2542
-     */
2543
-    public function get_first_related($relationName, $query_params = array())
2544
-    {
2545
-        $model = $this->get_model();
2546
-        if ($this->ID()) {//this exists in the DB, get from the cache OR the DB
2547
-            //if they've provided some query parameters, don't bother trying to cache the result
2548
-            //also make sure we're not caching the result of get_first_related
2549
-            //on a relation which should have an array of objects (because the cache might have an array of objects)
2550
-            if ($query_params
2551
-                || ! $model->related_settings_for($relationName)
2552
-                     instanceof
2553
-                     EE_Belongs_To_Relation
2554
-            ) {
2555
-                $related_model_object = $model->get_first_related(
2556
-                    $this,
2557
-                    $relationName,
2558
-                    $query_params
2559
-                );
2560
-            } else {
2561
-                //first, check if we've already cached the result of this query
2562
-                $cached_result = $this->get_one_from_cache($relationName);
2563
-                if (! $cached_result) {
2564
-                    $related_model_object = $model->get_first_related(
2565
-                        $this,
2566
-                        $relationName,
2567
-                        $query_params
2568
-                    );
2569
-                    $this->cache($relationName, $related_model_object);
2570
-                } else {
2571
-                    $related_model_object = $cached_result;
2572
-                }
2573
-            }
2574
-        } else {
2575
-            $related_model_object = null;
2576
-            // this doesn't exist in the Db,
2577
-            // but maybe the relation is of type belongs to, and so the related thing might
2578
-            if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2579
-                $related_model_object = $model->get_first_related(
2580
-                    $this,
2581
-                    $relationName,
2582
-                    $query_params
2583
-                );
2584
-            }
2585
-            // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2586
-            // just get what's cached on this object
2587
-            if (! $related_model_object) {
2588
-                $related_model_object = $this->get_one_from_cache($relationName);
2589
-            }
2590
-        }
2591
-        return $related_model_object;
2592
-    }
2593
-
2594
-
2595
-    /**
2596
-     * Does a delete on all related objects of type $relationName and removes
2597
-     * the current model object's relation to them. If they can't be deleted (because
2598
-     * of blocking related model objects) does nothing. If the related model objects are
2599
-     * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2600
-     * If this model object doesn't exist yet in the DB, just removes its related things
2601
-     *
2602
-     * @param string $relationName
2603
-     * @param array  $query_params like EEM_Base::get_all's
2604
-     * @return int how many deleted
2605
-     * @throws ReflectionException
2606
-     * @throws InvalidArgumentException
2607
-     * @throws InvalidInterfaceException
2608
-     * @throws InvalidDataTypeException
2609
-     * @throws EE_Error
2610
-     */
2611
-    public function delete_related($relationName, $query_params = array())
2612
-    {
2613
-        if ($this->ID()) {
2614
-            $count = $this->get_model()->delete_related(
2615
-                $this,
2616
-                $relationName,
2617
-                $query_params
2618
-            );
2619
-        } else {
2620
-            $count = count($this->get_all_from_cache($relationName));
2621
-            $this->clear_cache($relationName, null, true);
2622
-        }
2623
-        return $count;
2624
-    }
2625
-
2626
-
2627
-    /**
2628
-     * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2629
-     * the current model object's relation to them. If they can't be deleted (because
2630
-     * of blocking related model objects) just does a soft delete on it instead, if possible.
2631
-     * If the related thing isn't a soft-deletable model object, this function is identical
2632
-     * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2633
-     *
2634
-     * @param string $relationName
2635
-     * @param array  $query_params like EEM_Base::get_all's
2636
-     * @return int how many deleted (including those soft deleted)
2637
-     * @throws ReflectionException
2638
-     * @throws InvalidArgumentException
2639
-     * @throws InvalidInterfaceException
2640
-     * @throws InvalidDataTypeException
2641
-     * @throws EE_Error
2642
-     */
2643
-    public function delete_related_permanently($relationName, $query_params = array())
2644
-    {
2645
-        if ($this->ID()) {
2646
-            $count = $this->get_model()->delete_related_permanently(
2647
-                $this,
2648
-                $relationName,
2649
-                $query_params
2650
-            );
2651
-        } else {
2652
-            $count = count($this->get_all_from_cache($relationName));
2653
-        }
2654
-        $this->clear_cache($relationName, null, true);
2655
-        return $count;
2656
-    }
2657
-
2658
-
2659
-    /**
2660
-     * is_set
2661
-     * Just a simple utility function children can use for checking if property exists
2662
-     *
2663
-     * @access  public
2664
-     * @param  string $field_name property to check
2665
-     * @return bool                              TRUE if existing,FALSE if not.
2666
-     */
2667
-    public function is_set($field_name)
2668
-    {
2669
-        return isset($this->_fields[ $field_name ]);
2670
-    }
2671
-
2672
-
2673
-    /**
2674
-     * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2675
-     * EE_Error exception if they don't
2676
-     *
2677
-     * @param  mixed (string|array) $properties properties to check
2678
-     * @throws EE_Error
2679
-     * @return bool                              TRUE if existing, throw EE_Error if not.
2680
-     */
2681
-    protected function _property_exists($properties)
2682
-    {
2683
-        foreach ((array) $properties as $property_name) {
2684
-            //first make sure this property exists
2685
-            if (! $this->_fields[ $property_name ]) {
2686
-                throw new EE_Error(
2687
-                    sprintf(
2688
-                        esc_html__(
2689
-                            'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2690
-                            'event_espresso'
2691
-                        ),
2692
-                        $property_name
2693
-                    )
2694
-                );
2695
-            }
2696
-        }
2697
-        return true;
2698
-    }
2699
-
2700
-
2701
-    /**
2702
-     * This simply returns an array of model fields for this object
2703
-     *
2704
-     * @return array
2705
-     * @throws ReflectionException
2706
-     * @throws InvalidArgumentException
2707
-     * @throws InvalidInterfaceException
2708
-     * @throws InvalidDataTypeException
2709
-     * @throws EE_Error
2710
-     */
2711
-    public function model_field_array()
2712
-    {
2713
-        $fields     = $this->get_model()->field_settings(false);
2714
-        $properties = array();
2715
-        //remove prepended underscore
2716
-        foreach ($fields as $field_name => $settings) {
2717
-            $properties[ $field_name ] = $this->get($field_name);
2718
-        }
2719
-        return $properties;
2720
-    }
2721
-
2722
-
2723
-    /**
2724
-     * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2725
-     * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2726
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2727
-     * Instead of requiring a plugin to extend the EE_Base_Class
2728
-     * (which works fine is there's only 1 plugin, but when will that happen?)
2729
-     * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2730
-     * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2731
-     * and accepts 2 arguments: the object on which the function was called,
2732
-     * and an array of the original arguments passed to the function.
2733
-     * Whatever their callback function returns will be returned by this function.
2734
-     * Example: in functions.php (or in a plugin):
2735
-     *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2736
-     *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2737
-     *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2738
-     *          return $previousReturnValue.$returnString;
2739
-     *      }
2740
-     * require('EE_Answer.class.php');
2741
-     * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2742
-     * echo $answer->my_callback('monkeys',100);
2743
-     * //will output "you called my_callback! and passed args:monkeys,100"
2744
-     *
2745
-     * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2746
-     * @param array  $args       array of original arguments passed to the function
2747
-     * @throws EE_Error
2748
-     * @return mixed whatever the plugin which calls add_filter decides
2749
-     */
2750
-    public function __call($methodName, $args)
2751
-    {
2752
-        $className = get_class($this);
2753
-        $tagName   = "FHEE__{$className}__{$methodName}";
2754
-        if (! has_filter($tagName)) {
2755
-            throw new EE_Error(
2756
-                sprintf(
2757
-                    esc_html__(
2758
-                        "Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2759
-                        'event_espresso'
2760
-                    ),
2761
-                    $methodName,
2762
-                    $className,
2763
-                    $tagName
2764
-                )
2765
-            );
2766
-        }
2767
-        return apply_filters($tagName, null, $this, $args);
2768
-    }
2769
-
2770
-
2771
-    /**
2772
-     * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2773
-     * A $previous_value can be specified in case there are many meta rows with the same key
2774
-     *
2775
-     * @param string $meta_key
2776
-     * @param mixed  $meta_value
2777
-     * @param mixed  $previous_value
2778
-     * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2779
-     *                  NOTE: if the values haven't changed, returns 0
2780
-     * @throws InvalidArgumentException
2781
-     * @throws InvalidInterfaceException
2782
-     * @throws InvalidDataTypeException
2783
-     * @throws EE_Error
2784
-     * @throws ReflectionException
2785
-     */
2786
-    public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2787
-    {
2788
-        $query_params = array(
2789
-            array(
2790
-                'EXM_key'  => $meta_key,
2791
-                'OBJ_ID'   => $this->ID(),
2792
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2793
-            ),
2794
-        );
2795
-        if ($previous_value !== null) {
2796
-            $query_params[0]['EXM_value'] = $meta_value;
2797
-        }
2798
-        $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2799
-        if (! $existing_rows_like_that) {
2800
-            return $this->add_extra_meta($meta_key, $meta_value);
2801
-        }
2802
-        foreach ($existing_rows_like_that as $existing_row) {
2803
-            $existing_row->save(array('EXM_value' => $meta_value));
2804
-        }
2805
-        return count($existing_rows_like_that);
2806
-    }
2807
-
2808
-
2809
-    /**
2810
-     * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2811
-     * no other extra meta for this model object have the same key. Returns TRUE if the
2812
-     * extra meta row was entered, false if not
2813
-     *
2814
-     * @param string  $meta_key
2815
-     * @param mixed   $meta_value
2816
-     * @param boolean $unique
2817
-     * @return boolean
2818
-     * @throws InvalidArgumentException
2819
-     * @throws InvalidInterfaceException
2820
-     * @throws InvalidDataTypeException
2821
-     * @throws EE_Error
2822
-     * @throws ReflectionException
2823
-     * @throws ReflectionException
2824
-     */
2825
-    public function add_extra_meta($meta_key, $meta_value, $unique = false)
2826
-    {
2827
-        if ($unique) {
2828
-            $existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2829
-                array(
2830
-                    array(
2831
-                        'EXM_key'  => $meta_key,
2832
-                        'OBJ_ID'   => $this->ID(),
2833
-                        'EXM_type' => $this->get_model()->get_this_model_name(),
2834
-                    ),
2835
-                )
2836
-            );
2837
-            if ($existing_extra_meta) {
2838
-                return false;
2839
-            }
2840
-        }
2841
-        $new_extra_meta = EE_Extra_Meta::new_instance(
2842
-            array(
2843
-                'EXM_key'   => $meta_key,
2844
-                'EXM_value' => $meta_value,
2845
-                'OBJ_ID'    => $this->ID(),
2846
-                'EXM_type'  => $this->get_model()->get_this_model_name(),
2847
-            )
2848
-        );
2849
-        $new_extra_meta->save();
2850
-        return true;
2851
-    }
2852
-
2853
-
2854
-    /**
2855
-     * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2856
-     * is specified, only deletes extra meta records with that value.
2857
-     *
2858
-     * @param string $meta_key
2859
-     * @param mixed  $meta_value
2860
-     * @return int number of extra meta rows deleted
2861
-     * @throws InvalidArgumentException
2862
-     * @throws InvalidInterfaceException
2863
-     * @throws InvalidDataTypeException
2864
-     * @throws EE_Error
2865
-     * @throws ReflectionException
2866
-     */
2867
-    public function delete_extra_meta($meta_key, $meta_value = null)
2868
-    {
2869
-        $query_params = array(
2870
-            array(
2871
-                'EXM_key'  => $meta_key,
2872
-                'OBJ_ID'   => $this->ID(),
2873
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2874
-            ),
2875
-        );
2876
-        if ($meta_value !== null) {
2877
-            $query_params[0]['EXM_value'] = $meta_value;
2878
-        }
2879
-        return EEM_Extra_Meta::instance()->delete($query_params);
2880
-    }
2881
-
2882
-
2883
-    /**
2884
-     * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2885
-     * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2886
-     * You can specify $default is case you haven't found the extra meta
2887
-     *
2888
-     * @param string  $meta_key
2889
-     * @param boolean $single
2890
-     * @param mixed   $default if we don't find anything, what should we return?
2891
-     * @return mixed single value if $single; array if ! $single
2892
-     * @throws ReflectionException
2893
-     * @throws InvalidArgumentException
2894
-     * @throws InvalidInterfaceException
2895
-     * @throws InvalidDataTypeException
2896
-     * @throws EE_Error
2897
-     */
2898
-    public function get_extra_meta($meta_key, $single = false, $default = null)
2899
-    {
2900
-        if ($single) {
2901
-            $result = $this->get_first_related(
2902
-                'Extra_Meta',
2903
-                array(array('EXM_key' => $meta_key))
2904
-            );
2905
-            if ($result instanceof EE_Extra_Meta) {
2906
-                return $result->value();
2907
-            }
2908
-        } else {
2909
-            $results = $this->get_many_related(
2910
-                'Extra_Meta',
2911
-                array(array('EXM_key' => $meta_key))
2912
-            );
2913
-            if ($results) {
2914
-                $values = array();
2915
-                foreach ($results as $result) {
2916
-                    if ($result instanceof EE_Extra_Meta) {
2917
-                        $values[ $result->ID() ] = $result->value();
2918
-                    }
2919
-                }
2920
-                return $values;
2921
-            }
2922
-        }
2923
-        //if nothing discovered yet return default.
2924
-        return apply_filters(
2925
-            'FHEE__EE_Base_Class__get_extra_meta__default_value',
2926
-            $default,
2927
-            $meta_key,
2928
-            $single,
2929
-            $this
2930
-        );
2931
-    }
2932
-
2933
-
2934
-    /**
2935
-     * Returns a simple array of all the extra meta associated with this model object.
2936
-     * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2937
-     * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2938
-     * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2939
-     * If $one_of_each_key is false, it will return an array with the top-level keys being
2940
-     * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2941
-     * finally the extra meta's value as each sub-value. (eg
2942
-     * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2943
-     *
2944
-     * @param boolean $one_of_each_key
2945
-     * @return array
2946
-     * @throws ReflectionException
2947
-     * @throws InvalidArgumentException
2948
-     * @throws InvalidInterfaceException
2949
-     * @throws InvalidDataTypeException
2950
-     * @throws EE_Error
2951
-     */
2952
-    public function all_extra_meta_array($one_of_each_key = true)
2953
-    {
2954
-        $return_array = array();
2955
-        if ($one_of_each_key) {
2956
-            $extra_meta_objs = $this->get_many_related(
2957
-                'Extra_Meta',
2958
-                array('group_by' => 'EXM_key')
2959
-            );
2960
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2961
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2962
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2963
-                }
2964
-            }
2965
-        } else {
2966
-            $extra_meta_objs = $this->get_many_related('Extra_Meta');
2967
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2968
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2969
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2970
-                        $return_array[ $extra_meta_obj->key() ] = array();
2971
-                    }
2972
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2973
-                }
2974
-            }
2975
-        }
2976
-        return $return_array;
2977
-    }
2978
-
2979
-
2980
-    /**
2981
-     * Gets a pretty nice displayable nice for this model object. Often overridden
2982
-     *
2983
-     * @return string
2984
-     * @throws ReflectionException
2985
-     * @throws InvalidArgumentException
2986
-     * @throws InvalidInterfaceException
2987
-     * @throws InvalidDataTypeException
2988
-     * @throws EE_Error
2989
-     */
2990
-    public function name()
2991
-    {
2992
-        //find a field that's not a text field
2993
-        $field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2994
-        if ($field_we_can_use) {
2995
-            return $this->get($field_we_can_use->get_name());
2996
-        }
2997
-        $first_few_properties = $this->model_field_array();
2998
-        $first_few_properties = array_slice($first_few_properties, 0, 3);
2999
-        $name_parts           = array();
3000
-        foreach ($first_few_properties as $name => $value) {
3001
-            $name_parts[] = "$name:$value";
3002
-        }
3003
-        return implode(',', $name_parts);
3004
-    }
3005
-
3006
-
3007
-    /**
3008
-     * in_entity_map
3009
-     * Checks if this model object has been proven to already be in the entity map
3010
-     *
3011
-     * @return boolean
3012
-     * @throws ReflectionException
3013
-     * @throws InvalidArgumentException
3014
-     * @throws InvalidInterfaceException
3015
-     * @throws InvalidDataTypeException
3016
-     * @throws EE_Error
3017
-     */
3018
-    public function in_entity_map()
3019
-    {
3020
-        // well, if we looked, did we find it in the entity map?
3021
-        return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3022
-    }
3023
-
3024
-
3025
-    /**
3026
-     * refresh_from_db
3027
-     * Makes sure the fields and values on this model object are in-sync with what's in the database.
3028
-     *
3029
-     * @throws ReflectionException
3030
-     * @throws InvalidArgumentException
3031
-     * @throws InvalidInterfaceException
3032
-     * @throws InvalidDataTypeException
3033
-     * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3034
-     * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3035
-     */
3036
-    public function refresh_from_db()
3037
-    {
3038
-        if ($this->ID() && $this->in_entity_map()) {
3039
-            $this->get_model()->refresh_entity_map_from_db($this->ID());
3040
-        } else {
3041
-            //if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3042
-            //if it has an ID but it's not in the map, and you're asking me to refresh it
3043
-            //that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3044
-            //absolutely nothing in it for this ID
3045
-            if (WP_DEBUG) {
3046
-                throw new EE_Error(
3047
-                    sprintf(
3048
-                        esc_html__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3049
-                            'event_espresso'),
3050
-                        $this->ID(),
3051
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3052
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3053
-                    )
3054
-                );
3055
-            }
3056
-        }
3057
-    }
3058
-
3059
-
3060
-    /**
3061
-     * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3062
-     * (probably a bad assumption they have made, oh well)
3063
-     *
3064
-     * @return string
3065
-     */
3066
-    public function __toString()
3067
-    {
3068
-        try {
3069
-            return sprintf('%s (%s)', $this->name(), $this->ID());
3070
-        } catch (Exception $e) {
3071
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3072
-            return '';
3073
-        }
3074
-    }
3075
-
3076
-
3077
-    /**
3078
-     * Clear related model objects if they're already in the DB, because otherwise when we
3079
-     * UN-serialize this model object we'll need to be careful to add them to the entity map.
3080
-     * This means if we have made changes to those related model objects, and want to unserialize
3081
-     * the this model object on a subsequent request, changes to those related model objects will be lost.
3082
-     * Instead, those related model objects should be directly serialized and stored.
3083
-     * Eg, the following won't work:
3084
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3085
-     * $att = $reg->attendee();
3086
-     * $att->set( 'ATT_fname', 'Dirk' );
3087
-     * update_option( 'my_option', serialize( $reg ) );
3088
-     * //END REQUEST
3089
-     * //START NEXT REQUEST
3090
-     * $reg = get_option( 'my_option' );
3091
-     * $reg->attendee()->save();
3092
-     * And would need to be replace with:
3093
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3094
-     * $att = $reg->attendee();
3095
-     * $att->set( 'ATT_fname', 'Dirk' );
3096
-     * update_option( 'my_option', serialize( $reg ) );
3097
-     * //END REQUEST
3098
-     * //START NEXT REQUEST
3099
-     * $att = get_option( 'my_option' );
3100
-     * $att->save();
3101
-     *
3102
-     * @return array
3103
-     * @throws ReflectionException
3104
-     * @throws InvalidArgumentException
3105
-     * @throws InvalidInterfaceException
3106
-     * @throws InvalidDataTypeException
3107
-     * @throws EE_Error
3108
-     */
3109
-    public function __sleep()
3110
-    {
3111
-        $model = $this->get_model();
3112
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3113
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
3114
-                $classname = 'EE_' . $model->get_this_model_name();
3115
-                if (
3116
-                    $this->get_one_from_cache($relation_name) instanceof $classname
3117
-                    && $this->get_one_from_cache($relation_name)->ID()
3118
-                ) {
3119
-                    $this->clear_cache(
3120
-                        $relation_name,
3121
-                        $this->get_one_from_cache($relation_name)->ID()
3122
-                    );
3123
-                }
3124
-            }
3125
-        }
3126
-        $this->_props_n_values_provided_in_constructor = array();
3127
-        $properties_to_serialize                       = get_object_vars($this);
3128
-        //don't serialize the model. It's big and that risks recursion
3129
-        unset($properties_to_serialize['_model']);
3130
-        return array_keys($properties_to_serialize);
3131
-    }
3132
-
3133
-
3134
-    /**
3135
-     * restore _props_n_values_provided_in_constructor
3136
-     * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3137
-     * and therefore should NOT be used to determine if state change has occurred since initial construction.
3138
-     * At best, you would only be able to detect if state change has occurred during THIS request.
3139
-     */
3140
-    public function __wakeup()
3141
-    {
3142
-        $this->_props_n_values_provided_in_constructor = $this->_fields;
3143
-    }
3144
-
3145
-
3146
-    /**
3147
-     * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3148
-     * distinct with the clone host instance are also cloned.
3149
-     */
3150
-    public function __clone()
3151
-    {
3152
-        //handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3153
-        foreach ($this->_fields as $field => $value) {
3154
-            if ($value instanceof DateTime) {
3155
-                $this->_fields[$field] = clone $value;
3156
-            }
3157
-        }
3158
-    }
18
+	/**
19
+	 * This is an array of the original properties and values provided during construction
20
+	 * of this model object. (keys are model field names, values are their values).
21
+	 * This list is important to remember so that when we are merging data from the db, we know
22
+	 * which values to override and which to not override.
23
+	 *
24
+	 * @var array
25
+	 */
26
+	protected $_props_n_values_provided_in_constructor;
27
+
28
+	/**
29
+	 * Timezone
30
+	 * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
31
+	 * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
32
+	 * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
33
+	 * access to it.
34
+	 *
35
+	 * @var string
36
+	 */
37
+	protected $_timezone;
38
+
39
+	/**
40
+	 * date format
41
+	 * pattern or format for displaying dates
42
+	 *
43
+	 * @var string $_dt_frmt
44
+	 */
45
+	protected $_dt_frmt;
46
+
47
+	/**
48
+	 * time format
49
+	 * pattern or format for displaying time
50
+	 *
51
+	 * @var string $_tm_frmt
52
+	 */
53
+	protected $_tm_frmt;
54
+
55
+	/**
56
+	 * This property is for holding a cached array of object properties indexed by property name as the key.
57
+	 * The purpose of this is for setting a cache on properties that may have calculated values after a
58
+	 * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
59
+	 * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
60
+	 *
61
+	 * @var array
62
+	 */
63
+	protected $_cached_properties = array();
64
+
65
+	/**
66
+	 * An array containing keys of the related model, and values are either an array of related mode objects or a
67
+	 * single
68
+	 * related model object. see the model's _model_relations. The keys should match those specified. And if the
69
+	 * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
70
+	 * all others have an array)
71
+	 *
72
+	 * @var array
73
+	 */
74
+	protected $_model_relations = array();
75
+
76
+	/**
77
+	 * Array where keys are field names (see the model's _fields property) and values are their values. To see what
78
+	 * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
79
+	 *
80
+	 * @var array
81
+	 */
82
+	protected $_fields = array();
83
+
84
+	/**
85
+	 * @var boolean indicating whether or not this model object is intended to ever be saved
86
+	 * For example, we might create model objects intended to only be used for the duration
87
+	 * of this request and to be thrown away, and if they were accidentally saved
88
+	 * it would be a bug.
89
+	 */
90
+	protected $_allow_persist = true;
91
+
92
+	/**
93
+	 * @var boolean indicating whether or not this model object's properties have changed since construction
94
+	 */
95
+	protected $_has_changes = false;
96
+
97
+	/**
98
+	 * @var EEM_Base
99
+	 */
100
+	protected $_model;
101
+
102
+	/**
103
+	 * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
104
+	 * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
105
+	 * the db.  They also do not automatically update if there are any changes to the data that produced their results.
106
+	 * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
107
+	 * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
108
+	 * array as:
109
+	 * array(
110
+	 *  'Registration_Count' => 24
111
+	 * );
112
+	 * Note: if the custom select configuration for the query included a data type, the value will be in the data type
113
+	 * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
114
+	 * info)
115
+	 *
116
+	 * @var array
117
+	 */
118
+	protected $custom_selection_results = array();
119
+
120
+
121
+	/**
122
+	 * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
123
+	 * play nice
124
+	 *
125
+	 * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
126
+	 *                                                         layer of the model's _fields array, (eg, EVT_ID,
127
+	 *                                                         TXN_amount, QST_name, etc) and values are their values
128
+	 * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
129
+	 *                                                         corresponding db model or not.
130
+	 * @param string  $timezone                                indicate what timezone you want any datetime fields to
131
+	 *                                                         be in when instantiating a EE_Base_Class object.
132
+	 * @param array   $date_formats                            An array of date formats to set on construct where first
133
+	 *                                                         value is the date_format and second value is the time
134
+	 *                                                         format.
135
+	 * @throws InvalidArgumentException
136
+	 * @throws InvalidInterfaceException
137
+	 * @throws InvalidDataTypeException
138
+	 * @throws EE_Error
139
+	 * @throws ReflectionException
140
+	 */
141
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
142
+	{
143
+		$className = get_class($this);
144
+		do_action("AHEE__{$className}__construct", $this, $fieldValues);
145
+		$model        = $this->get_model();
146
+		$model_fields = $model->field_settings(false);
147
+		// ensure $fieldValues is an array
148
+		$fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
149
+		// verify client code has not passed any invalid field names
150
+		foreach ($fieldValues as $field_name => $field_value) {
151
+			if (! isset($model_fields[ $field_name ])) {
152
+				throw new EE_Error(
153
+					sprintf(
154
+						esc_html__(
155
+							'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
156
+							'event_espresso'
157
+						),
158
+						$field_name,
159
+						get_class($this),
160
+						implode(', ', array_keys($model_fields))
161
+					)
162
+				);
163
+			}
164
+		}
165
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
166
+		if (! empty($date_formats) && is_array($date_formats)) {
167
+			list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
168
+		} else {
169
+			//set default formats for date and time
170
+			$this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
171
+			$this->_tm_frmt = (string) get_option('time_format', 'g:i a');
172
+		}
173
+		//if db model is instantiating
174
+		if ($bydb) {
175
+			//client code has indicated these field values are from the database
176
+			foreach ($model_fields as $fieldName => $field) {
177
+				$this->set_from_db(
178
+					$fieldName,
179
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
180
+				);
181
+			}
182
+		} else {
183
+			//we're constructing a brand
184
+			//new instance of the model object. Generally, this means we'll need to do more field validation
185
+			foreach ($model_fields as $fieldName => $field) {
186
+				$this->set(
187
+					$fieldName,
188
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null, true
189
+				);
190
+			}
191
+		}
192
+		//remember what values were passed to this constructor
193
+		$this->_props_n_values_provided_in_constructor = $fieldValues;
194
+		//remember in entity mapper
195
+		if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
196
+			$model->add_to_entity_map($this);
197
+		}
198
+		//setup all the relations
199
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
200
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
201
+				$this->_model_relations[ $relation_name ] = null;
202
+			} else {
203
+				$this->_model_relations[ $relation_name ] = array();
204
+			}
205
+		}
206
+		/**
207
+		 * Action done at the end of each model object construction
208
+		 *
209
+		 * @param EE_Base_Class $this the model object just created
210
+		 */
211
+		do_action('AHEE__EE_Base_Class__construct__finished', $this);
212
+	}
213
+
214
+
215
+	/**
216
+	 * Gets whether or not this model object is allowed to persist/be saved to the database.
217
+	 *
218
+	 * @return boolean
219
+	 */
220
+	public function allow_persist()
221
+	{
222
+		return $this->_allow_persist;
223
+	}
224
+
225
+
226
+	/**
227
+	 * Sets whether or not this model object should be allowed to be saved to the DB.
228
+	 * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
229
+	 * you got new information that somehow made you change your mind.
230
+	 *
231
+	 * @param boolean $allow_persist
232
+	 * @return boolean
233
+	 */
234
+	public function set_allow_persist($allow_persist)
235
+	{
236
+		return $this->_allow_persist = $allow_persist;
237
+	}
238
+
239
+
240
+	/**
241
+	 * Gets the field's original value when this object was constructed during this request.
242
+	 * This can be helpful when determining if a model object has changed or not
243
+	 *
244
+	 * @param string $field_name
245
+	 * @return mixed|null
246
+	 * @throws ReflectionException
247
+	 * @throws InvalidArgumentException
248
+	 * @throws InvalidInterfaceException
249
+	 * @throws InvalidDataTypeException
250
+	 * @throws EE_Error
251
+	 */
252
+	public function get_original($field_name)
253
+	{
254
+		if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
255
+			&& $field_settings = $this->get_model()->field_settings_for($field_name)
256
+		) {
257
+			return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
258
+		}
259
+		return null;
260
+	}
261
+
262
+
263
+	/**
264
+	 * @param EE_Base_Class $obj
265
+	 * @return string
266
+	 */
267
+	public function get_class($obj)
268
+	{
269
+		return get_class($obj);
270
+	}
271
+
272
+
273
+	/**
274
+	 * Overrides parent because parent expects old models.
275
+	 * This also doesn't do any validation, and won't work for serialized arrays
276
+	 *
277
+	 * @param    string $field_name
278
+	 * @param    mixed  $field_value
279
+	 * @param bool      $use_default
280
+	 * @throws InvalidArgumentException
281
+	 * @throws InvalidInterfaceException
282
+	 * @throws InvalidDataTypeException
283
+	 * @throws EE_Error
284
+	 * @throws ReflectionException
285
+	 * @throws ReflectionException
286
+	 * @throws ReflectionException
287
+	 */
288
+	public function set($field_name, $field_value, $use_default = false)
289
+	{
290
+		// if not using default and nothing has changed, and object has already been setup (has ID),
291
+		// then don't do anything
292
+		if (
293
+			! $use_default
294
+			&& $this->_fields[ $field_name ] === $field_value
295
+			&& $this->ID()
296
+		) {
297
+			return;
298
+		}
299
+		$model              = $this->get_model();
300
+		$this->_has_changes = true;
301
+		$field_obj          = $model->field_settings_for($field_name);
302
+		if ($field_obj instanceof EE_Model_Field_Base) {
303
+			//			if ( method_exists( $field_obj, 'set_timezone' )) {
304
+			if ($field_obj instanceof EE_Datetime_Field) {
305
+				$field_obj->set_timezone($this->_timezone);
306
+				$field_obj->set_date_format($this->_dt_frmt);
307
+				$field_obj->set_time_format($this->_tm_frmt);
308
+			}
309
+			$holder_of_value = $field_obj->prepare_for_set($field_value);
310
+			//should the value be null?
311
+			if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
+				$this->_fields[ $field_name ] = $field_obj->get_default_value();
313
+				/**
314
+				 * To save having to refactor all the models, if a default value is used for a
315
+				 * EE_Datetime_Field, and that value is not null nor is it a DateTime
316
+				 * object.  Then let's do a set again to ensure that it becomes a DateTime
317
+				 * object.
318
+				 *
319
+				 * @since 4.6.10+
320
+				 */
321
+				if (
322
+					$field_obj instanceof EE_Datetime_Field
323
+					&& $this->_fields[ $field_name ] !== null
324
+					&& ! $this->_fields[ $field_name ] instanceof DateTime
325
+				) {
326
+					empty($this->_fields[ $field_name ])
327
+						? $this->set($field_name, time())
328
+						: $this->set($field_name, $this->_fields[ $field_name ]);
329
+				}
330
+			} else {
331
+				$this->_fields[ $field_name ] = $holder_of_value;
332
+			}
333
+			//if we're not in the constructor...
334
+			//now check if what we set was a primary key
335
+			if (
336
+				//note: props_n_values_provided_in_constructor is only set at the END of the constructor
337
+				$this->_props_n_values_provided_in_constructor
338
+				&& $field_value
339
+				&& $field_name === $model->primary_key_name()
340
+			) {
341
+				//if so, we want all this object's fields to be filled either with
342
+				//what we've explicitly set on this model
343
+				//or what we have in the db
344
+				// echo "setting primary key!";
345
+				$fields_on_model = self::_get_model(get_class($this))->field_settings();
346
+				$obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347
+				foreach ($fields_on_model as $field_obj) {
348
+					if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
349
+						&& $field_obj->get_name() !== $field_name
350
+					) {
351
+						$this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
352
+					}
353
+				}
354
+				//oh this model object has an ID? well make sure its in the entity mapper
355
+				$model->add_to_entity_map($this);
356
+			}
357
+			//let's unset any cache for this field_name from the $_cached_properties property.
358
+			$this->_clear_cached_property($field_name);
359
+		} else {
360
+			throw new EE_Error(
361
+				sprintf(
362
+					esc_html__(
363
+						'A valid EE_Model_Field_Base could not be found for the given field name: %s',
364
+						'event_espresso'
365
+					),
366
+					$field_name
367
+				)
368
+			);
369
+		}
370
+	}
371
+
372
+
373
+	/**
374
+	 * Set custom select values for model.
375
+	 *
376
+	 * @param array $custom_select_values
377
+	 */
378
+	public function setCustomSelectsValues(array $custom_select_values)
379
+	{
380
+		$this->custom_selection_results = $custom_select_values;
381
+	}
382
+
383
+
384
+	/**
385
+	 * Returns the custom select value for the provided alias if its set.
386
+	 * If not set, returns null.
387
+	 *
388
+	 * @param string $alias
389
+	 * @return string|int|float|null
390
+	 */
391
+	public function getCustomSelect($alias)
392
+	{
393
+		return isset($this->custom_selection_results[ $alias ])
394
+			? $this->custom_selection_results[ $alias ]
395
+			: null;
396
+	}
397
+
398
+
399
+	/**
400
+	 * This sets the field value on the db column if it exists for the given $column_name or
401
+	 * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
402
+	 *
403
+	 * @see EE_message::get_column_value for related documentation on the necessity of this method.
404
+	 * @param string $field_name  Must be the exact column name.
405
+	 * @param mixed  $field_value The value to set.
406
+	 * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
407
+	 * @throws InvalidArgumentException
408
+	 * @throws InvalidInterfaceException
409
+	 * @throws InvalidDataTypeException
410
+	 * @throws EE_Error
411
+	 * @throws ReflectionException
412
+	 */
413
+	public function set_field_or_extra_meta($field_name, $field_value)
414
+	{
415
+		if ($this->get_model()->has_field($field_name)) {
416
+			$this->set($field_name, $field_value);
417
+			return true;
418
+		}
419
+		//ensure this object is saved first so that extra meta can be properly related.
420
+		$this->save();
421
+		return $this->update_extra_meta($field_name, $field_value);
422
+	}
423
+
424
+
425
+	/**
426
+	 * This retrieves the value of the db column set on this class or if that's not present
427
+	 * it will attempt to retrieve from extra_meta if found.
428
+	 * Example Usage:
429
+	 * Via EE_Message child class:
430
+	 * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
431
+	 * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
432
+	 * also have additional main fields specific to the messenger.  The system accommodates those extra
433
+	 * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
434
+	 * value for those extra fields dynamically via the EE_message object.
435
+	 *
436
+	 * @param  string $field_name expecting the fully qualified field name.
437
+	 * @return mixed|null  value for the field if found.  null if not found.
438
+	 * @throws ReflectionException
439
+	 * @throws InvalidArgumentException
440
+	 * @throws InvalidInterfaceException
441
+	 * @throws InvalidDataTypeException
442
+	 * @throws EE_Error
443
+	 */
444
+	public function get_field_or_extra_meta($field_name)
445
+	{
446
+		if ($this->get_model()->has_field($field_name)) {
447
+			$column_value = $this->get($field_name);
448
+		} else {
449
+			//This isn't a column in the main table, let's see if it is in the extra meta.
450
+			$column_value = $this->get_extra_meta($field_name, true, null);
451
+		}
452
+		return $column_value;
453
+	}
454
+
455
+
456
+	/**
457
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
458
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
459
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
460
+	 * available to all child classes that may be using the EE_Datetime_Field for a field data type.
461
+	 *
462
+	 * @access public
463
+	 * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
464
+	 * @return void
465
+	 * @throws InvalidArgumentException
466
+	 * @throws InvalidInterfaceException
467
+	 * @throws InvalidDataTypeException
468
+	 * @throws EE_Error
469
+	 * @throws ReflectionException
470
+	 */
471
+	public function set_timezone($timezone = '')
472
+	{
473
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
474
+		//make sure we clear all cached properties because they won't be relevant now
475
+		$this->_clear_cached_properties();
476
+		//make sure we update field settings and the date for all EE_Datetime_Fields
477
+		$model_fields = $this->get_model()->field_settings(false);
478
+		foreach ($model_fields as $field_name => $field_obj) {
479
+			if ($field_obj instanceof EE_Datetime_Field) {
480
+				$field_obj->set_timezone($this->_timezone);
481
+				if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
482
+					$this->_fields[ $field_name ]->setTimezone(new DateTimeZone($this->_timezone));
483
+				}
484
+			}
485
+		}
486
+	}
487
+
488
+
489
+	/**
490
+	 * This just returns whatever is set for the current timezone.
491
+	 *
492
+	 * @access public
493
+	 * @return string timezone string
494
+	 */
495
+	public function get_timezone()
496
+	{
497
+		return $this->_timezone;
498
+	}
499
+
500
+
501
+	/**
502
+	 * This sets the internal date format to what is sent in to be used as the new default for the class
503
+	 * internally instead of wp set date format options
504
+	 *
505
+	 * @since 4.6
506
+	 * @param string $format should be a format recognizable by PHP date() functions.
507
+	 */
508
+	public function set_date_format($format)
509
+	{
510
+		$this->_dt_frmt = $format;
511
+		//clear cached_properties because they won't be relevant now.
512
+		$this->_clear_cached_properties();
513
+	}
514
+
515
+
516
+	/**
517
+	 * This sets the internal time format string to what is sent in to be used as the new default for the
518
+	 * class internally instead of wp set time format options.
519
+	 *
520
+	 * @since 4.6
521
+	 * @param string $format should be a format recognizable by PHP date() functions.
522
+	 */
523
+	public function set_time_format($format)
524
+	{
525
+		$this->_tm_frmt = $format;
526
+		//clear cached_properties because they won't be relevant now.
527
+		$this->_clear_cached_properties();
528
+	}
529
+
530
+
531
+	/**
532
+	 * This returns the current internal set format for the date and time formats.
533
+	 *
534
+	 * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
535
+	 *                             where the first value is the date format and the second value is the time format.
536
+	 * @return mixed string|array
537
+	 */
538
+	public function get_format($full = true)
539
+	{
540
+		return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541
+	}
542
+
543
+
544
+	/**
545
+	 * cache
546
+	 * stores the passed model object on the current model object.
547
+	 * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
548
+	 *
549
+	 * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
550
+	 *                                       'Registration' associated with this model object
551
+	 * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
552
+	 *                                       that could be a payment or a registration)
553
+	 * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
554
+	 *                                       items which will be stored in an array on this object
555
+	 * @throws ReflectionException
556
+	 * @throws InvalidArgumentException
557
+	 * @throws InvalidInterfaceException
558
+	 * @throws InvalidDataTypeException
559
+	 * @throws EE_Error
560
+	 * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
561
+	 *                                       related thing, no array)
562
+	 */
563
+	public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
564
+	{
565
+		// its entirely possible that there IS no related object yet in which case there is nothing to cache.
566
+		if (! $object_to_cache instanceof EE_Base_Class) {
567
+			return false;
568
+		}
569
+		// also get "how" the object is related, or throw an error
570
+		if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571
+			throw new EE_Error(
572
+				sprintf(
573
+					esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
574
+					$relationName,
575
+					get_class($this)
576
+				)
577
+			);
578
+		}
579
+		// how many things are related ?
580
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
581
+			// if it's a "belongs to" relationship, then there's only one related model object
582
+			// eg, if this is a registration, there's only 1 attendee for it
583
+			// so for these model objects just set it to be cached
584
+			$this->_model_relations[ $relationName ] = $object_to_cache;
585
+			$return                                  = true;
586
+		} else {
587
+			// otherwise, this is the "many" side of a one to many relationship,
588
+			// so we'll add the object to the array of related objects for that type.
589
+			// eg: if this is an event, there are many registrations for that event,
590
+			// so we cache the registrations in an array
591
+			if (! is_array($this->_model_relations[ $relationName ])) {
592
+				// if for some reason, the cached item is a model object,
593
+				// then stick that in the array, otherwise start with an empty array
594
+				$this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
595
+														   instanceof
596
+														   EE_Base_Class
597
+					? array($this->_model_relations[ $relationName ]) : array();
598
+			}
599
+			// first check for a cache_id which is normally empty
600
+			if (! empty($cache_id)) {
601
+				// if the cache_id exists, then it means we are purposely trying to cache this
602
+				// with a known key that can then be used to retrieve the object later on
603
+				$this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
604
+				$return                                               = $cache_id;
605
+			} elseif ($object_to_cache->ID()) {
606
+				// OR the cached object originally came from the db, so let's just use it's PK for an ID
607
+				$this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
608
+				$return                                                            = $object_to_cache->ID();
609
+			} else {
610
+				// OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
611
+				$this->_model_relations[ $relationName ][] = $object_to_cache;
612
+				// move the internal pointer to the end of the array
613
+				end($this->_model_relations[ $relationName ]);
614
+				// and grab the key so that we can return it
615
+				$return = key($this->_model_relations[ $relationName ]);
616
+			}
617
+		}
618
+		return $return;
619
+	}
620
+
621
+
622
+	/**
623
+	 * For adding an item to the cached_properties property.
624
+	 *
625
+	 * @access protected
626
+	 * @param string      $fieldname the property item the corresponding value is for.
627
+	 * @param mixed       $value     The value we are caching.
628
+	 * @param string|null $cache_type
629
+	 * @return void
630
+	 * @throws ReflectionException
631
+	 * @throws InvalidArgumentException
632
+	 * @throws InvalidInterfaceException
633
+	 * @throws InvalidDataTypeException
634
+	 * @throws EE_Error
635
+	 */
636
+	protected function _set_cached_property($fieldname, $value, $cache_type = null)
637
+	{
638
+		//first make sure this property exists
639
+		$this->get_model()->field_settings_for($fieldname);
640
+		$cache_type                                            = empty($cache_type) ? 'standard' : $cache_type;
641
+		$this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
642
+	}
643
+
644
+
645
+	/**
646
+	 * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
647
+	 * This also SETS the cache if we return the actual property!
648
+	 *
649
+	 * @param string $fieldname        the name of the property we're trying to retrieve
650
+	 * @param bool   $pretty
651
+	 * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
652
+	 *                                 (in cases where the same property may be used for different outputs
653
+	 *                                 - i.e. datetime, money etc.)
654
+	 *                                 It can also accept certain pre-defined "schema" strings
655
+	 *                                 to define how to output the property.
656
+	 *                                 see the field's prepare_for_pretty_echoing for what strings can be used
657
+	 * @return mixed                   whatever the value for the property is we're retrieving
658
+	 * @throws ReflectionException
659
+	 * @throws InvalidArgumentException
660
+	 * @throws InvalidInterfaceException
661
+	 * @throws InvalidDataTypeException
662
+	 * @throws EE_Error
663
+	 */
664
+	protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
665
+	{
666
+		//verify the field exists
667
+		$model = $this->get_model();
668
+		$model->field_settings_for($fieldname);
669
+		$cache_type = $pretty ? 'pretty' : 'standard';
670
+		$cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
671
+		if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
672
+			return $this->_cached_properties[ $fieldname ][ $cache_type ];
673
+		}
674
+		$value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
675
+		$this->_set_cached_property($fieldname, $value, $cache_type);
676
+		return $value;
677
+	}
678
+
679
+
680
+	/**
681
+	 * If the cache didn't fetch the needed item, this fetches it.
682
+	 *
683
+	 * @param string $fieldname
684
+	 * @param bool   $pretty
685
+	 * @param string $extra_cache_ref
686
+	 * @return mixed
687
+	 * @throws InvalidArgumentException
688
+	 * @throws InvalidInterfaceException
689
+	 * @throws InvalidDataTypeException
690
+	 * @throws EE_Error
691
+	 * @throws ReflectionException
692
+	 */
693
+	protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
694
+	{
695
+		$field_obj = $this->get_model()->field_settings_for($fieldname);
696
+		// If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
697
+		if ($field_obj instanceof EE_Datetime_Field) {
698
+			$this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
699
+		}
700
+		if (! isset($this->_fields[ $fieldname ])) {
701
+			$this->_fields[ $fieldname ] = null;
702
+		}
703
+		$value = $pretty
704
+			? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
705
+			: $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
706
+		return $value;
707
+	}
708
+
709
+
710
+	/**
711
+	 * set timezone, formats, and output for EE_Datetime_Field objects
712
+	 *
713
+	 * @param \EE_Datetime_Field $datetime_field
714
+	 * @param bool               $pretty
715
+	 * @param null               $date_or_time
716
+	 * @return void
717
+	 * @throws InvalidArgumentException
718
+	 * @throws InvalidInterfaceException
719
+	 * @throws InvalidDataTypeException
720
+	 * @throws EE_Error
721
+	 */
722
+	protected function _prepare_datetime_field(
723
+		EE_Datetime_Field $datetime_field,
724
+		$pretty = false,
725
+		$date_or_time = null
726
+	) {
727
+		$datetime_field->set_timezone($this->_timezone);
728
+		$datetime_field->set_date_format($this->_dt_frmt, $pretty);
729
+		$datetime_field->set_time_format($this->_tm_frmt, $pretty);
730
+		//set the output returned
731
+		switch ($date_or_time) {
732
+			case 'D' :
733
+				$datetime_field->set_date_time_output('date');
734
+				break;
735
+			case 'T' :
736
+				$datetime_field->set_date_time_output('time');
737
+				break;
738
+			default :
739
+				$datetime_field->set_date_time_output();
740
+		}
741
+	}
742
+
743
+
744
+	/**
745
+	 * This just takes care of clearing out the cached_properties
746
+	 *
747
+	 * @return void
748
+	 */
749
+	protected function _clear_cached_properties()
750
+	{
751
+		$this->_cached_properties = array();
752
+	}
753
+
754
+
755
+	/**
756
+	 * This just clears out ONE property if it exists in the cache
757
+	 *
758
+	 * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
759
+	 * @return void
760
+	 */
761
+	protected function _clear_cached_property($property_name)
762
+	{
763
+		if (isset($this->_cached_properties[ $property_name ])) {
764
+			unset($this->_cached_properties[ $property_name ]);
765
+		}
766
+	}
767
+
768
+
769
+	/**
770
+	 * Ensures that this related thing is a model object.
771
+	 *
772
+	 * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
773
+	 * @param string $model_name   name of the related thing, eg 'Attendee',
774
+	 * @return EE_Base_Class
775
+	 * @throws ReflectionException
776
+	 * @throws InvalidArgumentException
777
+	 * @throws InvalidInterfaceException
778
+	 * @throws InvalidDataTypeException
779
+	 * @throws EE_Error
780
+	 */
781
+	protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
782
+	{
783
+		$other_model_instance = self::_get_model_instance_with_name(
784
+			self::_get_model_classname($model_name),
785
+			$this->_timezone
786
+		);
787
+		return $other_model_instance->ensure_is_obj($object_or_id);
788
+	}
789
+
790
+
791
+	/**
792
+	 * Forgets the cached model of the given relation Name. So the next time we request it,
793
+	 * we will fetch it again from the database. (Handy if you know it's changed somehow).
794
+	 * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
795
+	 * then only remove that one object from our cached array. Otherwise, clear the entire list
796
+	 *
797
+	 * @param string $relationName                         one of the keys in the _model_relations array on the model.
798
+	 *                                                     Eg 'Registration'
799
+	 * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
800
+	 *                                                     if you intend to use $clear_all = TRUE, or the relation only
801
+	 *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
802
+	 * @param bool   $clear_all                            This flags clearing the entire cache relation property if
803
+	 *                                                     this is HasMany or HABTM.
804
+	 * @throws ReflectionException
805
+	 * @throws InvalidArgumentException
806
+	 * @throws InvalidInterfaceException
807
+	 * @throws InvalidDataTypeException
808
+	 * @throws EE_Error
809
+	 * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
810
+	 *                                                     relation from all
811
+	 */
812
+	public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
813
+	{
814
+		$relationship_to_model = $this->get_model()->related_settings_for($relationName);
815
+		$index_in_cache        = '';
816
+		if (! $relationship_to_model) {
817
+			throw new EE_Error(
818
+				sprintf(
819
+					esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
820
+					$relationName,
821
+					get_class($this)
822
+				)
823
+			);
824
+		}
825
+		if ($clear_all) {
826
+			$obj_removed                             = true;
827
+			$this->_model_relations[ $relationName ] = null;
828
+		} elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
829
+			$obj_removed                             = $this->_model_relations[ $relationName ];
830
+			$this->_model_relations[ $relationName ] = null;
831
+		} else {
832
+			if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
833
+				&& $object_to_remove_or_index_into_array->ID()
834
+			) {
835
+				$index_in_cache = $object_to_remove_or_index_into_array->ID();
836
+				if (is_array($this->_model_relations[ $relationName ])
837
+					&& ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
838
+				) {
839
+					$index_found_at = null;
840
+					//find this object in the array even though it has a different key
841
+					foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
842
+						/** @noinspection TypeUnsafeComparisonInspection */
843
+						if (
844
+							$obj instanceof EE_Base_Class
845
+							&& (
846
+								$obj == $object_to_remove_or_index_into_array
847
+								|| $obj->ID() === $object_to_remove_or_index_into_array->ID()
848
+							)
849
+						) {
850
+							$index_found_at = $index;
851
+							break;
852
+						}
853
+					}
854
+					if ($index_found_at) {
855
+						$index_in_cache = $index_found_at;
856
+					} else {
857
+						//it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
858
+						//if it wasn't in it to begin with. So we're done
859
+						return $object_to_remove_or_index_into_array;
860
+					}
861
+				}
862
+			} elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
863
+				//so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
864
+				foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
865
+					/** @noinspection TypeUnsafeComparisonInspection */
866
+					if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
867
+						$index_in_cache = $index;
868
+					}
869
+				}
870
+			} else {
871
+				$index_in_cache = $object_to_remove_or_index_into_array;
872
+			}
873
+			//supposedly we've found it. But it could just be that the client code
874
+			//provided a bad index/object
875
+			if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
876
+				$obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
877
+				unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
878
+			} else {
879
+				//that thing was never cached anyways.
880
+				$obj_removed = null;
881
+			}
882
+		}
883
+		return $obj_removed;
884
+	}
885
+
886
+
887
+	/**
888
+	 * update_cache_after_object_save
889
+	 * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
890
+	 * obtained after being saved to the db
891
+	 *
892
+	 * @param string        $relationName       - the type of object that is cached
893
+	 * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
894
+	 * @param string        $current_cache_id   - the ID that was used when originally caching the object
895
+	 * @return boolean TRUE on success, FALSE on fail
896
+	 * @throws ReflectionException
897
+	 * @throws InvalidArgumentException
898
+	 * @throws InvalidInterfaceException
899
+	 * @throws InvalidDataTypeException
900
+	 * @throws EE_Error
901
+	 */
902
+	public function update_cache_after_object_save(
903
+		$relationName,
904
+		EE_Base_Class $newly_saved_object,
905
+		$current_cache_id = ''
906
+	) {
907
+		// verify that incoming object is of the correct type
908
+		$obj_class = 'EE_' . $relationName;
909
+		if ($newly_saved_object instanceof $obj_class) {
910
+			/* @type EE_Base_Class $newly_saved_object */
911
+			// now get the type of relation
912
+			$relationship_to_model = $this->get_model()->related_settings_for($relationName);
913
+			// if this is a 1:1 relationship
914
+			if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
915
+				// then just replace the cached object with the newly saved object
916
+				$this->_model_relations[ $relationName ] = $newly_saved_object;
917
+				return true;
918
+				// or if it's some kind of sordid feral polyamorous relationship...
919
+			}
920
+			if (is_array($this->_model_relations[ $relationName ])
921
+					  && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
922
+			) {
923
+				// then remove the current cached item
924
+				unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
925
+				// and cache the newly saved object using it's new ID
926
+				$this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
927
+				return true;
928
+			}
929
+		}
930
+		return false;
931
+	}
932
+
933
+
934
+	/**
935
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
936
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
937
+	 *
938
+	 * @param string $relationName
939
+	 * @return EE_Base_Class
940
+	 */
941
+	public function get_one_from_cache($relationName)
942
+	{
943
+		$cached_array_or_object = isset($this->_model_relations[ $relationName ])
944
+			? $this->_model_relations[ $relationName ]
945
+			: null;
946
+		if (is_array($cached_array_or_object)) {
947
+			return array_shift($cached_array_or_object);
948
+		}
949
+		return $cached_array_or_object;
950
+	}
951
+
952
+
953
+	/**
954
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
955
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
956
+	 *
957
+	 * @param string $relationName
958
+	 * @throws ReflectionException
959
+	 * @throws InvalidArgumentException
960
+	 * @throws InvalidInterfaceException
961
+	 * @throws InvalidDataTypeException
962
+	 * @throws EE_Error
963
+	 * @return EE_Base_Class[] NOT necessarily indexed by primary keys
964
+	 */
965
+	public function get_all_from_cache($relationName)
966
+	{
967
+		$objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
968
+		// if the result is not an array, but exists, make it an array
969
+		$objects = is_array($objects) ? $objects : array($objects);
970
+		//bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
971
+		//basically, if this model object was stored in the session, and these cached model objects
972
+		//already have IDs, let's make sure they're in their model's entity mapper
973
+		//otherwise we will have duplicates next time we call
974
+		// EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
975
+		$model = EE_Registry::instance()->load_model($relationName);
976
+		foreach ($objects as $model_object) {
977
+			if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
978
+				//ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
979
+				if ($model_object->ID()) {
980
+					$model->add_to_entity_map($model_object);
981
+				}
982
+			} else {
983
+				throw new EE_Error(
984
+					sprintf(
985
+						esc_html__(
986
+							'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
987
+							'event_espresso'
988
+						),
989
+						$relationName,
990
+						gettype($model_object)
991
+					)
992
+				);
993
+			}
994
+		}
995
+		return $objects;
996
+	}
997
+
998
+
999
+	/**
1000
+	 * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1001
+	 * matching the given query conditions.
1002
+	 *
1003
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1004
+	 * @param int   $limit              How many objects to return.
1005
+	 * @param array $query_params       Any additional conditions on the query.
1006
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1007
+	 *                                  you can indicate just the columns you want returned
1008
+	 * @return array|EE_Base_Class[]
1009
+	 * @throws ReflectionException
1010
+	 * @throws InvalidArgumentException
1011
+	 * @throws InvalidInterfaceException
1012
+	 * @throws InvalidDataTypeException
1013
+	 * @throws EE_Error
1014
+	 */
1015
+	public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1016
+	{
1017
+		$model         = $this->get_model();
1018
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1019
+			? $model->get_primary_key_field()->get_name()
1020
+			: $field_to_order_by;
1021
+		$current_value = ! empty($field) ? $this->get($field) : null;
1022
+		if (empty($field) || empty($current_value)) {
1023
+			return array();
1024
+		}
1025
+		return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1026
+	}
1027
+
1028
+
1029
+	/**
1030
+	 * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1031
+	 * matching the given query conditions.
1032
+	 *
1033
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1034
+	 * @param int   $limit              How many objects to return.
1035
+	 * @param array $query_params       Any additional conditions on the query.
1036
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1037
+	 *                                  you can indicate just the columns you want returned
1038
+	 * @return array|EE_Base_Class[]
1039
+	 * @throws ReflectionException
1040
+	 * @throws InvalidArgumentException
1041
+	 * @throws InvalidInterfaceException
1042
+	 * @throws InvalidDataTypeException
1043
+	 * @throws EE_Error
1044
+	 */
1045
+	public function previous_x(
1046
+		$field_to_order_by = null,
1047
+		$limit = 1,
1048
+		$query_params = array(),
1049
+		$columns_to_select = null
1050
+	) {
1051
+		$model         = $this->get_model();
1052
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1053
+			? $model->get_primary_key_field()->get_name()
1054
+			: $field_to_order_by;
1055
+		$current_value = ! empty($field) ? $this->get($field) : null;
1056
+		if (empty($field) || empty($current_value)) {
1057
+			return array();
1058
+		}
1059
+		return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1060
+	}
1061
+
1062
+
1063
+	/**
1064
+	 * Returns the next EE_Base_Class object in sequence from this object as found in the database
1065
+	 * matching the given query conditions.
1066
+	 *
1067
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1068
+	 * @param array $query_params       Any additional conditions on the query.
1069
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1070
+	 *                                  you can indicate just the columns you want returned
1071
+	 * @return array|EE_Base_Class
1072
+	 * @throws ReflectionException
1073
+	 * @throws InvalidArgumentException
1074
+	 * @throws InvalidInterfaceException
1075
+	 * @throws InvalidDataTypeException
1076
+	 * @throws EE_Error
1077
+	 */
1078
+	public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1079
+	{
1080
+		$model         = $this->get_model();
1081
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1082
+			? $model->get_primary_key_field()->get_name()
1083
+			: $field_to_order_by;
1084
+		$current_value = ! empty($field) ? $this->get($field) : null;
1085
+		if (empty($field) || empty($current_value)) {
1086
+			return array();
1087
+		}
1088
+		return $model->next($current_value, $field, $query_params, $columns_to_select);
1089
+	}
1090
+
1091
+
1092
+	/**
1093
+	 * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1094
+	 * matching the given query conditions.
1095
+	 *
1096
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1097
+	 * @param array $query_params       Any additional conditions on the query.
1098
+	 * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1099
+	 *                                  you can indicate just the column you want returned
1100
+	 * @return array|EE_Base_Class
1101
+	 * @throws ReflectionException
1102
+	 * @throws InvalidArgumentException
1103
+	 * @throws InvalidInterfaceException
1104
+	 * @throws InvalidDataTypeException
1105
+	 * @throws EE_Error
1106
+	 */
1107
+	public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1108
+	{
1109
+		$model         = $this->get_model();
1110
+		$field         = empty($field_to_order_by) && $model->has_primary_key_field()
1111
+			? $model->get_primary_key_field()->get_name()
1112
+			: $field_to_order_by;
1113
+		$current_value = ! empty($field) ? $this->get($field) : null;
1114
+		if (empty($field) || empty($current_value)) {
1115
+			return array();
1116
+		}
1117
+		return $model->previous($current_value, $field, $query_params, $columns_to_select);
1118
+	}
1119
+
1120
+
1121
+	/**
1122
+	 * Overrides parent because parent expects old models.
1123
+	 * This also doesn't do any validation, and won't work for serialized arrays
1124
+	 *
1125
+	 * @param string $field_name
1126
+	 * @param mixed  $field_value_from_db
1127
+	 * @throws ReflectionException
1128
+	 * @throws InvalidArgumentException
1129
+	 * @throws InvalidInterfaceException
1130
+	 * @throws InvalidDataTypeException
1131
+	 * @throws EE_Error
1132
+	 */
1133
+	public function set_from_db($field_name, $field_value_from_db)
1134
+	{
1135
+		$field_obj = $this->get_model()->field_settings_for($field_name);
1136
+		if ($field_obj instanceof EE_Model_Field_Base) {
1137
+			//you would think the DB has no NULLs for non-null label fields right? wrong!
1138
+			//eg, a CPT model object could have an entry in the posts table, but no
1139
+			//entry in the meta table. Meaning that all its columns in the meta table
1140
+			//are null! yikes! so when we find one like that, use defaults for its meta columns
1141
+			if ($field_value_from_db === null) {
1142
+				if ($field_obj->is_nullable()) {
1143
+					//if the field allows nulls, then let it be null
1144
+					$field_value = null;
1145
+				} else {
1146
+					$field_value = $field_obj->get_default_value();
1147
+				}
1148
+			} else {
1149
+				$field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1150
+			}
1151
+			$this->_fields[ $field_name ] = $field_value;
1152
+			$this->_clear_cached_property($field_name);
1153
+		}
1154
+	}
1155
+
1156
+
1157
+	/**
1158
+	 * verifies that the specified field is of the correct type
1159
+	 *
1160
+	 * @param string $field_name
1161
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1162
+	 *                                (in cases where the same property may be used for different outputs
1163
+	 *                                - i.e. datetime, money etc.)
1164
+	 * @return mixed
1165
+	 * @throws ReflectionException
1166
+	 * @throws InvalidArgumentException
1167
+	 * @throws InvalidInterfaceException
1168
+	 * @throws InvalidDataTypeException
1169
+	 * @throws EE_Error
1170
+	 */
1171
+	public function get($field_name, $extra_cache_ref = null)
1172
+	{
1173
+		return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1174
+	}
1175
+
1176
+
1177
+	/**
1178
+	 * This method simply returns the RAW unprocessed value for the given property in this class
1179
+	 *
1180
+	 * @param  string $field_name A valid fieldname
1181
+	 * @return mixed              Whatever the raw value stored on the property is.
1182
+	 * @throws ReflectionException
1183
+	 * @throws InvalidArgumentException
1184
+	 * @throws InvalidInterfaceException
1185
+	 * @throws InvalidDataTypeException
1186
+	 * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1187
+	 */
1188
+	public function get_raw($field_name)
1189
+	{
1190
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1191
+		return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1192
+			? $this->_fields[ $field_name ]->format('U')
1193
+			: $this->_fields[ $field_name ];
1194
+	}
1195
+
1196
+
1197
+	/**
1198
+	 * This is used to return the internal DateTime object used for a field that is a
1199
+	 * EE_Datetime_Field.
1200
+	 *
1201
+	 * @param string $field_name               The field name retrieving the DateTime object.
1202
+	 * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1203
+	 * @throws EE_Error an error is set and false returned.  If the field IS an
1204
+	 *                                         EE_Datetime_Field and but the field value is null, then
1205
+	 *                                         just null is returned (because that indicates that likely
1206
+	 *                                         this field is nullable).
1207
+	 * @throws InvalidArgumentException
1208
+	 * @throws InvalidDataTypeException
1209
+	 * @throws InvalidInterfaceException
1210
+	 * @throws ReflectionException
1211
+	 */
1212
+	public function get_DateTime_object($field_name)
1213
+	{
1214
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1215
+		if (! $field_settings instanceof EE_Datetime_Field) {
1216
+			EE_Error::add_error(
1217
+				sprintf(
1218
+					esc_html__(
1219
+						'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1220
+						'event_espresso'
1221
+					),
1222
+					$field_name
1223
+				),
1224
+				__FILE__,
1225
+				__FUNCTION__,
1226
+				__LINE__
1227
+			);
1228
+			return false;
1229
+		}
1230
+		return isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime
1231
+			? clone $this->_fields[$field_name]
1232
+			: null;
1233
+	}
1234
+
1235
+
1236
+	/**
1237
+	 * To be used in template to immediately echo out the value, and format it for output.
1238
+	 * Eg, should call stripslashes and whatnot before echoing
1239
+	 *
1240
+	 * @param string $field_name      the name of the field as it appears in the DB
1241
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1242
+	 *                                (in cases where the same property may be used for different outputs
1243
+	 *                                - i.e. datetime, money etc.)
1244
+	 * @return void
1245
+	 * @throws ReflectionException
1246
+	 * @throws InvalidArgumentException
1247
+	 * @throws InvalidInterfaceException
1248
+	 * @throws InvalidDataTypeException
1249
+	 * @throws EE_Error
1250
+	 */
1251
+	public function e($field_name, $extra_cache_ref = null)
1252
+	{
1253
+		echo $this->get_pretty($field_name, $extra_cache_ref);
1254
+	}
1255
+
1256
+
1257
+	/**
1258
+	 * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1259
+	 * can be easily used as the value of form input.
1260
+	 *
1261
+	 * @param string $field_name
1262
+	 * @return void
1263
+	 * @throws ReflectionException
1264
+	 * @throws InvalidArgumentException
1265
+	 * @throws InvalidInterfaceException
1266
+	 * @throws InvalidDataTypeException
1267
+	 * @throws EE_Error
1268
+	 */
1269
+	public function f($field_name)
1270
+	{
1271
+		$this->e($field_name, 'form_input');
1272
+	}
1273
+
1274
+
1275
+	/**
1276
+	 * Same as `f()` but just returns the value instead of echoing it
1277
+	 *
1278
+	 * @param string $field_name
1279
+	 * @return string
1280
+	 * @throws ReflectionException
1281
+	 * @throws InvalidArgumentException
1282
+	 * @throws InvalidInterfaceException
1283
+	 * @throws InvalidDataTypeException
1284
+	 * @throws EE_Error
1285
+	 */
1286
+	public function get_f($field_name)
1287
+	{
1288
+		return (string) $this->get_pretty($field_name, 'form_input');
1289
+	}
1290
+
1291
+
1292
+	/**
1293
+	 * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1294
+	 * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1295
+	 * to see what options are available.
1296
+	 *
1297
+	 * @param string $field_name
1298
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1299
+	 *                                (in cases where the same property may be used for different outputs
1300
+	 *                                - i.e. datetime, money etc.)
1301
+	 * @return mixed
1302
+	 * @throws ReflectionException
1303
+	 * @throws InvalidArgumentException
1304
+	 * @throws InvalidInterfaceException
1305
+	 * @throws InvalidDataTypeException
1306
+	 * @throws EE_Error
1307
+	 */
1308
+	public function get_pretty($field_name, $extra_cache_ref = null)
1309
+	{
1310
+		return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1311
+	}
1312
+
1313
+
1314
+	/**
1315
+	 * This simply returns the datetime for the given field name
1316
+	 * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1317
+	 * (and the equivalent e_date, e_time, e_datetime).
1318
+	 *
1319
+	 * @access   protected
1320
+	 * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1321
+	 * @param string   $dt_frmt      valid datetime format used for date
1322
+	 *                               (if '' then we just use the default on the field,
1323
+	 *                               if NULL we use the last-used format)
1324
+	 * @param string   $tm_frmt      Same as above except this is for time format
1325
+	 * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1326
+	 * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1327
+	 * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1328
+	 *                               if field is not a valid dtt field, or void if echoing
1329
+	 * @throws ReflectionException
1330
+	 * @throws InvalidArgumentException
1331
+	 * @throws InvalidInterfaceException
1332
+	 * @throws InvalidDataTypeException
1333
+	 * @throws EE_Error
1334
+	 */
1335
+	protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1336
+	{
1337
+		// clear cached property
1338
+		$this->_clear_cached_property($field_name);
1339
+		//reset format properties because they are used in get()
1340
+		$this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1341
+		$this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1342
+		if ($echo) {
1343
+			$this->e($field_name, $date_or_time);
1344
+			return '';
1345
+		}
1346
+		return $this->get($field_name, $date_or_time);
1347
+	}
1348
+
1349
+
1350
+	/**
1351
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1352
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1353
+	 * other echoes the pretty value for dtt)
1354
+	 *
1355
+	 * @param  string $field_name name of model object datetime field holding the value
1356
+	 * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1357
+	 * @return string            datetime value formatted
1358
+	 * @throws ReflectionException
1359
+	 * @throws InvalidArgumentException
1360
+	 * @throws InvalidInterfaceException
1361
+	 * @throws InvalidDataTypeException
1362
+	 * @throws EE_Error
1363
+	 */
1364
+	public function get_date($field_name, $format = '')
1365
+	{
1366
+		return $this->_get_datetime($field_name, $format, null, 'D');
1367
+	}
1368
+
1369
+
1370
+	/**
1371
+	 * @param        $field_name
1372
+	 * @param string $format
1373
+	 * @throws ReflectionException
1374
+	 * @throws InvalidArgumentException
1375
+	 * @throws InvalidInterfaceException
1376
+	 * @throws InvalidDataTypeException
1377
+	 * @throws EE_Error
1378
+	 */
1379
+	public function e_date($field_name, $format = '')
1380
+	{
1381
+		$this->_get_datetime($field_name, $format, null, 'D', true);
1382
+	}
1383
+
1384
+
1385
+	/**
1386
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1387
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1388
+	 * other echoes the pretty value for dtt)
1389
+	 *
1390
+	 * @param  string $field_name name of model object datetime field holding the value
1391
+	 * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1392
+	 * @return string             datetime value formatted
1393
+	 * @throws ReflectionException
1394
+	 * @throws InvalidArgumentException
1395
+	 * @throws InvalidInterfaceException
1396
+	 * @throws InvalidDataTypeException
1397
+	 * @throws EE_Error
1398
+	 */
1399
+	public function get_time($field_name, $format = '')
1400
+	{
1401
+		return $this->_get_datetime($field_name, null, $format, 'T');
1402
+	}
1403
+
1404
+
1405
+	/**
1406
+	 * @param        $field_name
1407
+	 * @param string $format
1408
+	 * @throws ReflectionException
1409
+	 * @throws InvalidArgumentException
1410
+	 * @throws InvalidInterfaceException
1411
+	 * @throws InvalidDataTypeException
1412
+	 * @throws EE_Error
1413
+	 */
1414
+	public function e_time($field_name, $format = '')
1415
+	{
1416
+		$this->_get_datetime($field_name, null, $format, 'T', true);
1417
+	}
1418
+
1419
+
1420
+	/**
1421
+	 * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1422
+	 * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1423
+	 * other echoes the pretty value for dtt)
1424
+	 *
1425
+	 * @param  string $field_name name of model object datetime field holding the value
1426
+	 * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1427
+	 * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1428
+	 * @return string             datetime value formatted
1429
+	 * @throws ReflectionException
1430
+	 * @throws InvalidArgumentException
1431
+	 * @throws InvalidInterfaceException
1432
+	 * @throws InvalidDataTypeException
1433
+	 * @throws EE_Error
1434
+	 */
1435
+	public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1436
+	{
1437
+		return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1438
+	}
1439
+
1440
+
1441
+	/**
1442
+	 * @param string $field_name
1443
+	 * @param string $dt_frmt
1444
+	 * @param string $tm_frmt
1445
+	 * @throws ReflectionException
1446
+	 * @throws InvalidArgumentException
1447
+	 * @throws InvalidInterfaceException
1448
+	 * @throws InvalidDataTypeException
1449
+	 * @throws EE_Error
1450
+	 */
1451
+	public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1452
+	{
1453
+		$this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1454
+	}
1455
+
1456
+
1457
+	/**
1458
+	 * Get the i8ln value for a date using the WordPress @see date_i18n function.
1459
+	 *
1460
+	 * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1461
+	 * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1462
+	 *                           on the object will be used.
1463
+	 * @return string Date and time string in set locale or false if no field exists for the given
1464
+	 * @throws ReflectionException
1465
+	 * @throws InvalidArgumentException
1466
+	 * @throws InvalidInterfaceException
1467
+	 * @throws InvalidDataTypeException
1468
+	 * @throws EE_Error
1469
+	 *                           field name.
1470
+	 */
1471
+	public function get_i18n_datetime($field_name, $format = '')
1472
+	{
1473
+		$format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1474
+		return date_i18n(
1475
+			$format,
1476
+			EEH_DTT_Helper::get_timestamp_with_offset(
1477
+				$this->get_raw($field_name),
1478
+				$this->_timezone
1479
+			)
1480
+		);
1481
+	}
1482
+
1483
+
1484
+	/**
1485
+	 * This method validates whether the given field name is a valid field on the model object as well as it is of a
1486
+	 * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1487
+	 * thrown.
1488
+	 *
1489
+	 * @param  string $field_name The field name being checked
1490
+	 * @throws ReflectionException
1491
+	 * @throws InvalidArgumentException
1492
+	 * @throws InvalidInterfaceException
1493
+	 * @throws InvalidDataTypeException
1494
+	 * @throws EE_Error
1495
+	 * @return EE_Datetime_Field
1496
+	 */
1497
+	protected function _get_dtt_field_settings($field_name)
1498
+	{
1499
+		$field = $this->get_model()->field_settings_for($field_name);
1500
+		//check if field is dtt
1501
+		if ($field instanceof EE_Datetime_Field) {
1502
+			return $field;
1503
+		}
1504
+		throw new EE_Error(
1505
+			sprintf(
1506
+				esc_html__(
1507
+					'The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1508
+					'event_espresso'
1509
+				),
1510
+				$field_name,
1511
+				self::_get_model_classname(get_class($this))
1512
+			)
1513
+		);
1514
+	}
1515
+
1516
+
1517
+
1518
+
1519
+	/**
1520
+	 * NOTE ABOUT BELOW:
1521
+	 * These convenience date and time setters are for setting date and time independently.  In other words you might
1522
+	 * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1523
+	 * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1524
+	 * method and make sure you send the entire datetime value for setting.
1525
+	 */
1526
+	/**
1527
+	 * sets the time on a datetime property
1528
+	 *
1529
+	 * @access protected
1530
+	 * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1531
+	 * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1532
+	 * @throws ReflectionException
1533
+	 * @throws InvalidArgumentException
1534
+	 * @throws InvalidInterfaceException
1535
+	 * @throws InvalidDataTypeException
1536
+	 * @throws EE_Error
1537
+	 */
1538
+	protected function _set_time_for($time, $fieldname)
1539
+	{
1540
+		$this->_set_date_time('T', $time, $fieldname);
1541
+	}
1542
+
1543
+
1544
+	/**
1545
+	 * sets the date on a datetime property
1546
+	 *
1547
+	 * @access protected
1548
+	 * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1549
+	 * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1550
+	 * @throws ReflectionException
1551
+	 * @throws InvalidArgumentException
1552
+	 * @throws InvalidInterfaceException
1553
+	 * @throws InvalidDataTypeException
1554
+	 * @throws EE_Error
1555
+	 */
1556
+	protected function _set_date_for($date, $fieldname)
1557
+	{
1558
+		$this->_set_date_time('D', $date, $fieldname);
1559
+	}
1560
+
1561
+
1562
+	/**
1563
+	 * This takes care of setting a date or time independently on a given model object property. This method also
1564
+	 * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1565
+	 *
1566
+	 * @access protected
1567
+	 * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1568
+	 * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1569
+	 * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1570
+	 *                                        EE_Datetime_Field property)
1571
+	 * @throws ReflectionException
1572
+	 * @throws InvalidArgumentException
1573
+	 * @throws InvalidInterfaceException
1574
+	 * @throws InvalidDataTypeException
1575
+	 * @throws EE_Error
1576
+	 */
1577
+	protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1578
+	{
1579
+		$field = $this->_get_dtt_field_settings($fieldname);
1580
+		$field->set_timezone($this->_timezone);
1581
+		$field->set_date_format($this->_dt_frmt);
1582
+		$field->set_time_format($this->_tm_frmt);
1583
+		switch ($what) {
1584
+			case 'T' :
1585
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1586
+					$datetime_value,
1587
+					$this->_fields[ $fieldname ]
1588
+				);
1589
+				break;
1590
+			case 'D' :
1591
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1592
+					$datetime_value,
1593
+					$this->_fields[ $fieldname ]
1594
+				);
1595
+				break;
1596
+			case 'B' :
1597
+				$this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1598
+				break;
1599
+		}
1600
+		$this->_clear_cached_property($fieldname);
1601
+	}
1602
+
1603
+
1604
+	/**
1605
+	 * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1606
+	 * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1607
+	 * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1608
+	 * that could lead to some unexpected results!
1609
+	 *
1610
+	 * @access public
1611
+	 * @param string $field_name               This is the name of the field on the object that contains the date/time
1612
+	 *                                         value being returned.
1613
+	 * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1614
+	 * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1615
+	 * @param string $prepend                  You can include something to prepend on the timestamp
1616
+	 * @param string $append                   You can include something to append on the timestamp
1617
+	 * @throws ReflectionException
1618
+	 * @throws InvalidArgumentException
1619
+	 * @throws InvalidInterfaceException
1620
+	 * @throws InvalidDataTypeException
1621
+	 * @throws EE_Error
1622
+	 * @return string timestamp
1623
+	 */
1624
+	public function display_in_my_timezone(
1625
+		$field_name,
1626
+		$callback = 'get_datetime',
1627
+		$args = null,
1628
+		$prepend = '',
1629
+		$append = ''
1630
+	) {
1631
+		$timezone = EEH_DTT_Helper::get_timezone();
1632
+		if ($timezone === $this->_timezone) {
1633
+			return '';
1634
+		}
1635
+		$original_timezone = $this->_timezone;
1636
+		$this->set_timezone($timezone);
1637
+		$fn   = (array) $field_name;
1638
+		$args = array_merge($fn, (array) $args);
1639
+		if (! method_exists($this, $callback)) {
1640
+			throw new EE_Error(
1641
+				sprintf(
1642
+					esc_html__(
1643
+						'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1644
+						'event_espresso'
1645
+					),
1646
+					$callback
1647
+				)
1648
+			);
1649
+		}
1650
+		$args   = (array) $args;
1651
+		$return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1652
+		$this->set_timezone($original_timezone);
1653
+		return $return;
1654
+	}
1655
+
1656
+
1657
+	/**
1658
+	 * Deletes this model object.
1659
+	 * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1660
+	 * override
1661
+	 * `EE_Base_Class::_delete` NOT this class.
1662
+	 *
1663
+	 * @return boolean | int
1664
+	 * @throws ReflectionException
1665
+	 * @throws InvalidArgumentException
1666
+	 * @throws InvalidInterfaceException
1667
+	 * @throws InvalidDataTypeException
1668
+	 * @throws EE_Error
1669
+	 */
1670
+	public function delete()
1671
+	{
1672
+		/**
1673
+		 * Called just before the `EE_Base_Class::_delete` method call.
1674
+		 * Note:
1675
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1676
+		 * should be aware that `_delete` may not always result in a permanent delete.
1677
+		 * For example, `EE_Soft_Delete_Base_Class::_delete`
1678
+		 * soft deletes (trash) the object and does not permanently delete it.
1679
+		 *
1680
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1681
+		 */
1682
+		do_action('AHEE__EE_Base_Class__delete__before', $this);
1683
+		$result = $this->_delete();
1684
+		/**
1685
+		 * Called just after the `EE_Base_Class::_delete` method call.
1686
+		 * Note:
1687
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1688
+		 * should be aware that `_delete` may not always result in a permanent delete.
1689
+		 * For example `EE_Soft_Base_Class::_delete`
1690
+		 * soft deletes (trash) the object and does not permanently delete it.
1691
+		 *
1692
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1693
+		 * @param boolean       $result
1694
+		 */
1695
+		do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1696
+		return $result;
1697
+	}
1698
+
1699
+
1700
+	/**
1701
+	 * Calls the specific delete method for the instantiated class.
1702
+	 * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1703
+	 * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1704
+	 * `EE_Base_Class::delete`
1705
+	 *
1706
+	 * @return bool|int
1707
+	 * @throws ReflectionException
1708
+	 * @throws InvalidArgumentException
1709
+	 * @throws InvalidInterfaceException
1710
+	 * @throws InvalidDataTypeException
1711
+	 * @throws EE_Error
1712
+	 */
1713
+	protected function _delete()
1714
+	{
1715
+		return $this->delete_permanently();
1716
+	}
1717
+
1718
+
1719
+	/**
1720
+	 * Deletes this model object permanently from db
1721
+	 * (but keep in mind related models may block the delete and return an error)
1722
+	 *
1723
+	 * @return bool | int
1724
+	 * @throws ReflectionException
1725
+	 * @throws InvalidArgumentException
1726
+	 * @throws InvalidInterfaceException
1727
+	 * @throws InvalidDataTypeException
1728
+	 * @throws EE_Error
1729
+	 */
1730
+	public function delete_permanently()
1731
+	{
1732
+		/**
1733
+		 * Called just before HARD deleting a model object
1734
+		 *
1735
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1736
+		 */
1737
+		do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1738
+		$model  = $this->get_model();
1739
+		$result = $model->delete_permanently_by_ID($this->ID());
1740
+		$this->refresh_cache_of_related_objects();
1741
+		/**
1742
+		 * Called just after HARD deleting a model object
1743
+		 *
1744
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1745
+		 * @param boolean       $result
1746
+		 */
1747
+		do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1748
+		return $result;
1749
+	}
1750
+
1751
+
1752
+	/**
1753
+	 * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1754
+	 * related model objects
1755
+	 *
1756
+	 * @throws ReflectionException
1757
+	 * @throws InvalidArgumentException
1758
+	 * @throws InvalidInterfaceException
1759
+	 * @throws InvalidDataTypeException
1760
+	 * @throws EE_Error
1761
+	 */
1762
+	public function refresh_cache_of_related_objects()
1763
+	{
1764
+		$model = $this->get_model();
1765
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1766
+			if (! empty($this->_model_relations[ $relation_name ])) {
1767
+				$related_objects = $this->_model_relations[ $relation_name ];
1768
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
1769
+					//this relation only stores a single model object, not an array
1770
+					//but let's make it consistent
1771
+					$related_objects = array($related_objects);
1772
+				}
1773
+				foreach ($related_objects as $related_object) {
1774
+					//only refresh their cache if they're in memory
1775
+					if ($related_object instanceof EE_Base_Class) {
1776
+						$related_object->clear_cache(
1777
+							$model->get_this_model_name(),
1778
+							$this
1779
+						);
1780
+					}
1781
+				}
1782
+			}
1783
+		}
1784
+	}
1785
+
1786
+
1787
+	/**
1788
+	 *        Saves this object to the database. An array may be supplied to set some values on this
1789
+	 * object just before saving.
1790
+	 *
1791
+	 * @access public
1792
+	 * @param array $set_cols_n_values keys are field names, values are their new values,
1793
+	 *                                 if provided during the save() method (often client code will change the fields'
1794
+	 *                                 values before calling save)
1795
+	 * @throws InvalidArgumentException
1796
+	 * @throws InvalidInterfaceException
1797
+	 * @throws InvalidDataTypeException
1798
+	 * @throws EE_Error
1799
+	 * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1800
+	 *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1801
+	 * @throws ReflectionException
1802
+	 * @throws ReflectionException
1803
+	 * @throws ReflectionException
1804
+	 */
1805
+	public function save($set_cols_n_values = array())
1806
+	{
1807
+		$model = $this->get_model();
1808
+		/**
1809
+		 * Filters the fields we're about to save on the model object
1810
+		 *
1811
+		 * @param array         $set_cols_n_values
1812
+		 * @param EE_Base_Class $model_object
1813
+		 */
1814
+		$set_cols_n_values = (array) apply_filters(
1815
+			'FHEE__EE_Base_Class__save__set_cols_n_values',
1816
+			$set_cols_n_values,
1817
+			$this
1818
+		);
1819
+		//set attributes as provided in $set_cols_n_values
1820
+		foreach ($set_cols_n_values as $column => $value) {
1821
+			$this->set($column, $value);
1822
+		}
1823
+		// no changes ? then don't do anything
1824
+		if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1825
+			return 0;
1826
+		}
1827
+		/**
1828
+		 * Saving a model object.
1829
+		 * Before we perform a save, this action is fired.
1830
+		 *
1831
+		 * @param EE_Base_Class $model_object the model object about to be saved.
1832
+		 */
1833
+		do_action('AHEE__EE_Base_Class__save__begin', $this);
1834
+		if (! $this->allow_persist()) {
1835
+			return 0;
1836
+		}
1837
+		// now get current attribute values
1838
+		$save_cols_n_values = $this->_fields;
1839
+		// if the object already has an ID, update it. Otherwise, insert it
1840
+		// also: change the assumption about values passed to the model NOT being prepare dby the model object.
1841
+		// They have been
1842
+		$old_assumption_concerning_value_preparation = $model
1843
+			->get_assumption_concerning_values_already_prepared_by_model_object();
1844
+		$model->assume_values_already_prepared_by_model_object(true);
1845
+		//does this model have an autoincrement PK?
1846
+		if ($model->has_primary_key_field()) {
1847
+			if ($model->get_primary_key_field()->is_auto_increment()) {
1848
+				//ok check if it's set, if so: update; if not, insert
1849
+				if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1850
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1851
+				} else {
1852
+					unset($save_cols_n_values[ $model->primary_key_name() ]);
1853
+					$results = $model->insert($save_cols_n_values);
1854
+					if ($results) {
1855
+						//if successful, set the primary key
1856
+						//but don't use the normal SET method, because it will check if
1857
+						//an item with the same ID exists in the mapper & db, then
1858
+						//will find it in the db (because we just added it) and THAT object
1859
+						//will get added to the mapper before we can add this one!
1860
+						//but if we just avoid using the SET method, all that headache can be avoided
1861
+						$pk_field_name                   = $model->primary_key_name();
1862
+						$this->_fields[ $pk_field_name ] = $results;
1863
+						$this->_clear_cached_property($pk_field_name);
1864
+						$model->add_to_entity_map($this);
1865
+						$this->_update_cached_related_model_objs_fks();
1866
+					}
1867
+				}
1868
+			} else {//PK is NOT auto-increment
1869
+				//so check if one like it already exists in the db
1870
+				if ($model->exists_by_ID($this->ID())) {
1871
+					if (WP_DEBUG && ! $this->in_entity_map()) {
1872
+						throw new EE_Error(
1873
+							sprintf(
1874
+								esc_html__(
1875
+									'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1876
+									'event_espresso'
1877
+								),
1878
+								get_class($this),
1879
+								get_class($model) . '::instance()->add_to_entity_map()',
1880
+								get_class($model) . '::instance()->get_one_by_ID()',
1881
+								'<br />'
1882
+							)
1883
+						);
1884
+					}
1885
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1886
+				} else {
1887
+					$results = $model->insert($save_cols_n_values);
1888
+					$this->_update_cached_related_model_objs_fks();
1889
+				}
1890
+			}
1891
+		} else {//there is NO primary key
1892
+			$already_in_db = false;
1893
+			foreach ($model->unique_indexes() as $index) {
1894
+				$uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1895
+				if ($model->exists(array($uniqueness_where_params))) {
1896
+					$already_in_db = true;
1897
+				}
1898
+			}
1899
+			if ($already_in_db) {
1900
+				$combined_pk_fields_n_values = array_intersect_key($save_cols_n_values,
1901
+					$model->get_combined_primary_key_fields());
1902
+				$results                     = $model->update(
1903
+					$save_cols_n_values,
1904
+					$combined_pk_fields_n_values
1905
+				);
1906
+			} else {
1907
+				$results = $model->insert($save_cols_n_values);
1908
+			}
1909
+		}
1910
+		//restore the old assumption about values being prepared by the model object
1911
+		$model->assume_values_already_prepared_by_model_object(
1912
+				$old_assumption_concerning_value_preparation
1913
+			);
1914
+		/**
1915
+		 * After saving the model object this action is called
1916
+		 *
1917
+		 * @param EE_Base_Class $model_object which was just saved
1918
+		 * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1919
+		 *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1920
+		 */
1921
+		do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1922
+		$this->_has_changes = false;
1923
+		return $results;
1924
+	}
1925
+
1926
+
1927
+	/**
1928
+	 * Updates the foreign key on related models objects pointing to this to have this model object's ID
1929
+	 * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1930
+	 * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1931
+	 * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1932
+	 * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1933
+	 * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1934
+	 * or not they exist in the DB (if they do, their DB records will be automatically updated)
1935
+	 *
1936
+	 * @return void
1937
+	 * @throws ReflectionException
1938
+	 * @throws InvalidArgumentException
1939
+	 * @throws InvalidInterfaceException
1940
+	 * @throws InvalidDataTypeException
1941
+	 * @throws EE_Error
1942
+	 */
1943
+	protected function _update_cached_related_model_objs_fks()
1944
+	{
1945
+		$model = $this->get_model();
1946
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1947
+			if ($relation_obj instanceof EE_Has_Many_Relation) {
1948
+				foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1949
+					$fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1950
+						$model->get_this_model_name()
1951
+					);
1952
+					$related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1953
+					if ($related_model_obj_in_cache->ID()) {
1954
+						$related_model_obj_in_cache->save();
1955
+					}
1956
+				}
1957
+			}
1958
+		}
1959
+	}
1960
+
1961
+
1962
+	/**
1963
+	 * Saves this model object and its NEW cached relations to the database.
1964
+	 * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1965
+	 * In order for that to work, we would need to mark model objects as dirty/clean...
1966
+	 * because otherwise, there's a potential for infinite looping of saving
1967
+	 * Saves the cached related model objects, and ensures the relation between them
1968
+	 * and this object and properly setup
1969
+	 *
1970
+	 * @return int ID of new model object on save; 0 on failure+
1971
+	 * @throws ReflectionException
1972
+	 * @throws InvalidArgumentException
1973
+	 * @throws InvalidInterfaceException
1974
+	 * @throws InvalidDataTypeException
1975
+	 * @throws EE_Error
1976
+	 */
1977
+	public function save_new_cached_related_model_objs()
1978
+	{
1979
+		//make sure this has been saved
1980
+		if (! $this->ID()) {
1981
+			$id = $this->save();
1982
+		} else {
1983
+			$id = $this->ID();
1984
+		}
1985
+		//now save all the NEW cached model objects  (ie they don't exist in the DB)
1986
+		foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1987
+			if ($this->_model_relations[ $relationName ]) {
1988
+				//is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1989
+				//or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1990
+				/* @var $related_model_obj EE_Base_Class */
1991
+				if ($relationObj instanceof EE_Belongs_To_Relation) {
1992
+					//add a relation to that relation type (which saves the appropriate thing in the process)
1993
+					//but ONLY if it DOES NOT exist in the DB
1994
+					$related_model_obj = $this->_model_relations[ $relationName ];
1995
+					//					if( ! $related_model_obj->ID()){
1996
+					$this->_add_relation_to($related_model_obj, $relationName);
1997
+					$related_model_obj->save_new_cached_related_model_objs();
1998
+					//					}
1999
+				} else {
2000
+					foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2001
+						//add a relation to that relation type (which saves the appropriate thing in the process)
2002
+						//but ONLY if it DOES NOT exist in the DB
2003
+						//						if( ! $related_model_obj->ID()){
2004
+						$this->_add_relation_to($related_model_obj, $relationName);
2005
+						$related_model_obj->save_new_cached_related_model_objs();
2006
+						//						}
2007
+					}
2008
+				}
2009
+			}
2010
+		}
2011
+		return $id;
2012
+	}
2013
+
2014
+
2015
+	/**
2016
+	 * for getting a model while instantiated.
2017
+	 *
2018
+	 * @return EEM_Base | EEM_CPT_Base
2019
+	 * @throws ReflectionException
2020
+	 * @throws InvalidArgumentException
2021
+	 * @throws InvalidInterfaceException
2022
+	 * @throws InvalidDataTypeException
2023
+	 * @throws EE_Error
2024
+	 */
2025
+	public function get_model()
2026
+	{
2027
+		if (! $this->_model) {
2028
+			$modelName    = self::_get_model_classname(get_class($this));
2029
+			$this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2030
+		} else {
2031
+			$this->_model->set_timezone($this->_timezone);
2032
+		}
2033
+		return $this->_model;
2034
+	}
2035
+
2036
+
2037
+	/**
2038
+	 * @param $props_n_values
2039
+	 * @param $classname
2040
+	 * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2041
+	 * @throws ReflectionException
2042
+	 * @throws InvalidArgumentException
2043
+	 * @throws InvalidInterfaceException
2044
+	 * @throws InvalidDataTypeException
2045
+	 * @throws EE_Error
2046
+	 */
2047
+	protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2048
+	{
2049
+		//TODO: will not work for Term_Relationships because they have no PK!
2050
+		$primary_id_ref = self::_get_primary_key_name($classname);
2051
+		if (
2052
+			array_key_exists($primary_id_ref, $props_n_values)
2053
+			&& ! empty($props_n_values[ $primary_id_ref ])
2054
+		) {
2055
+			$id = $props_n_values[ $primary_id_ref ];
2056
+			return self::_get_model($classname)->get_from_entity_map($id);
2057
+		}
2058
+		return false;
2059
+	}
2060
+
2061
+
2062
+	/**
2063
+	 * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2064
+	 * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2065
+	 * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
2066
+	 * we return false.
2067
+	 *
2068
+	 * @param  array  $props_n_values   incoming array of properties and their values
2069
+	 * @param  string $classname        the classname of the child class
2070
+	 * @param null    $timezone
2071
+	 * @param array   $date_formats     incoming date_formats in an array where the first value is the
2072
+	 *                                  date_format and the second value is the time format
2073
+	 * @return mixed (EE_Base_Class|bool)
2074
+	 * @throws InvalidArgumentException
2075
+	 * @throws InvalidInterfaceException
2076
+	 * @throws InvalidDataTypeException
2077
+	 * @throws EE_Error
2078
+	 * @throws ReflectionException
2079
+	 * @throws ReflectionException
2080
+	 * @throws ReflectionException
2081
+	 */
2082
+	protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2083
+	{
2084
+		$existing = null;
2085
+		$model    = self::_get_model($classname, $timezone);
2086
+		if ($model->has_primary_key_field()) {
2087
+			$primary_id_ref = self::_get_primary_key_name($classname);
2088
+			if (array_key_exists($primary_id_ref, $props_n_values)
2089
+				&& ! empty($props_n_values[ $primary_id_ref ])
2090
+			) {
2091
+				$existing = $model->get_one_by_ID(
2092
+					$props_n_values[ $primary_id_ref ]
2093
+				);
2094
+			}
2095
+		} elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2096
+			//no primary key on this model, but there's still a matching item in the DB
2097
+			$existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2098
+				self::_get_model($classname, $timezone)
2099
+					->get_index_primary_key_string($props_n_values)
2100
+			);
2101
+		}
2102
+		if ($existing) {
2103
+			//set date formats if present before setting values
2104
+			if (! empty($date_formats) && is_array($date_formats)) {
2105
+				$existing->set_date_format($date_formats[0]);
2106
+				$existing->set_time_format($date_formats[1]);
2107
+			} else {
2108
+				//set default formats for date and time
2109
+				$existing->set_date_format(get_option('date_format'));
2110
+				$existing->set_time_format(get_option('time_format'));
2111
+			}
2112
+			foreach ($props_n_values as $property => $field_value) {
2113
+				$existing->set($property, $field_value);
2114
+			}
2115
+			return $existing;
2116
+		}
2117
+		return false;
2118
+	}
2119
+
2120
+
2121
+	/**
2122
+	 * Gets the EEM_*_Model for this class
2123
+	 *
2124
+	 * @access public now, as this is more convenient
2125
+	 * @param      $classname
2126
+	 * @param null $timezone
2127
+	 * @throws ReflectionException
2128
+	 * @throws InvalidArgumentException
2129
+	 * @throws InvalidInterfaceException
2130
+	 * @throws InvalidDataTypeException
2131
+	 * @throws EE_Error
2132
+	 * @return EEM_Base
2133
+	 */
2134
+	protected static function _get_model($classname, $timezone = null)
2135
+	{
2136
+		//find model for this class
2137
+		if (! $classname) {
2138
+			throw new EE_Error(
2139
+				sprintf(
2140
+					esc_html__(
2141
+						'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2142
+						'event_espresso'
2143
+					),
2144
+					$classname
2145
+				)
2146
+			);
2147
+		}
2148
+		$modelName = self::_get_model_classname($classname);
2149
+		return self::_get_model_instance_with_name($modelName, $timezone);
2150
+	}
2151
+
2152
+
2153
+	/**
2154
+	 * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2155
+	 *
2156
+	 * @param string $model_classname
2157
+	 * @param null   $timezone
2158
+	 * @return EEM_Base
2159
+	 * @throws ReflectionException
2160
+	 * @throws InvalidArgumentException
2161
+	 * @throws InvalidInterfaceException
2162
+	 * @throws InvalidDataTypeException
2163
+	 * @throws EE_Error
2164
+	 */
2165
+	protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2166
+	{
2167
+		$model_classname = str_replace('EEM_', '', $model_classname);
2168
+		$model           = EE_Registry::instance()->load_model($model_classname);
2169
+		$model->set_timezone($timezone);
2170
+		return $model;
2171
+	}
2172
+
2173
+
2174
+	/**
2175
+	 * If a model name is provided (eg Registration), gets the model classname for that model.
2176
+	 * Also works if a model class's classname is provided (eg EE_Registration).
2177
+	 *
2178
+	 * @param null $model_name
2179
+	 * @return string like EEM_Attendee
2180
+	 */
2181
+	private static function _get_model_classname($model_name = null)
2182
+	{
2183
+		if (strpos($model_name, 'EE_') === 0) {
2184
+			$model_classname = str_replace('EE_', 'EEM_', $model_name);
2185
+		} else {
2186
+			$model_classname = 'EEM_' . $model_name;
2187
+		}
2188
+		return $model_classname;
2189
+	}
2190
+
2191
+
2192
+	/**
2193
+	 * returns the name of the primary key attribute
2194
+	 *
2195
+	 * @param null $classname
2196
+	 * @throws ReflectionException
2197
+	 * @throws InvalidArgumentException
2198
+	 * @throws InvalidInterfaceException
2199
+	 * @throws InvalidDataTypeException
2200
+	 * @throws EE_Error
2201
+	 * @return string
2202
+	 */
2203
+	protected static function _get_primary_key_name($classname = null)
2204
+	{
2205
+		if (! $classname) {
2206
+			throw new EE_Error(
2207
+				sprintf(
2208
+					esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2209
+					$classname
2210
+				)
2211
+			);
2212
+		}
2213
+		return self::_get_model($classname)->get_primary_key_field()->get_name();
2214
+	}
2215
+
2216
+
2217
+	/**
2218
+	 * Gets the value of the primary key.
2219
+	 * If the object hasn't yet been saved, it should be whatever the model field's default was
2220
+	 * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2221
+	 * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2222
+	 *
2223
+	 * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2224
+	 * @throws ReflectionException
2225
+	 * @throws InvalidArgumentException
2226
+	 * @throws InvalidInterfaceException
2227
+	 * @throws InvalidDataTypeException
2228
+	 * @throws EE_Error
2229
+	 */
2230
+	public function ID()
2231
+	{
2232
+		$model = $this->get_model();
2233
+		//now that we know the name of the variable, use a variable variable to get its value and return its
2234
+		if ($model->has_primary_key_field()) {
2235
+			return $this->_fields[ $model->primary_key_name() ];
2236
+		}
2237
+		return $model->get_index_primary_key_string($this->_fields);
2238
+	}
2239
+
2240
+
2241
+	/**
2242
+	 * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2243
+	 * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2244
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2245
+	 *
2246
+	 * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2247
+	 * @param string $relationName                     eg 'Events','Question',etc.
2248
+	 *                                                 an attendee to a group, you also want to specify which role they
2249
+	 *                                                 will have in that group. So you would use this parameter to
2250
+	 *                                                 specify array('role-column-name'=>'role-id')
2251
+	 * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2252
+	 *                                                 allow you to further constrict the relation to being added.
2253
+	 *                                                 However, keep in mind that the columns (keys) given must match a
2254
+	 *                                                 column on the JOIN table and currently only the HABTM models
2255
+	 *                                                 accept these additional conditions.  Also remember that if an
2256
+	 *                                                 exact match isn't found for these extra cols/val pairs, then a
2257
+	 *                                                 NEW row is created in the join table.
2258
+	 * @param null   $cache_id
2259
+	 * @throws ReflectionException
2260
+	 * @throws InvalidArgumentException
2261
+	 * @throws InvalidInterfaceException
2262
+	 * @throws InvalidDataTypeException
2263
+	 * @throws EE_Error
2264
+	 * @return EE_Base_Class the object the relation was added to
2265
+	 */
2266
+	public function _add_relation_to(
2267
+		$otherObjectModelObjectOrID,
2268
+		$relationName,
2269
+		$extra_join_model_fields_n_values = array(),
2270
+		$cache_id = null
2271
+	) {
2272
+		$model = $this->get_model();
2273
+		//if this thing exists in the DB, save the relation to the DB
2274
+		if ($this->ID()) {
2275
+			$otherObject = $model->add_relationship_to(
2276
+				$this,
2277
+				$otherObjectModelObjectOrID,
2278
+				$relationName,
2279
+				$extra_join_model_fields_n_values
2280
+			);
2281
+			//clear cache so future get_many_related and get_first_related() return new results.
2282
+			$this->clear_cache($relationName, $otherObject, true);
2283
+			if ($otherObject instanceof EE_Base_Class) {
2284
+				$otherObject->clear_cache($model->get_this_model_name(), $this);
2285
+			}
2286
+		} else {
2287
+			//this thing doesn't exist in the DB,  so just cache it
2288
+			if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2289
+				throw new EE_Error(
2290
+					sprintf(
2291
+						esc_html__(
2292
+							'Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
2293
+							'event_espresso'
2294
+						),
2295
+						$otherObjectModelObjectOrID,
2296
+						get_class($this)
2297
+					)
2298
+				);
2299
+			}
2300
+			$otherObject = $otherObjectModelObjectOrID;
2301
+			$this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2302
+		}
2303
+		if ($otherObject instanceof EE_Base_Class) {
2304
+			//fix the reciprocal relation too
2305
+			if ($otherObject->ID()) {
2306
+				//its saved so assumed relations exist in the DB, so we can just
2307
+				//clear the cache so future queries use the updated info in the DB
2308
+				$otherObject->clear_cache(
2309
+					$model->get_this_model_name(),
2310
+					null,
2311
+					true
2312
+				);
2313
+			} else {
2314
+				//it's not saved, so it caches relations like this
2315
+				$otherObject->cache($model->get_this_model_name(), $this);
2316
+			}
2317
+		}
2318
+		return $otherObject;
2319
+	}
2320
+
2321
+
2322
+	/**
2323
+	 * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2324
+	 * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2325
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2326
+	 * from the cache
2327
+	 *
2328
+	 * @param mixed  $otherObjectModelObjectOrID
2329
+	 *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2330
+	 *                to the DB yet
2331
+	 * @param string $relationName
2332
+	 * @param array  $where_query
2333
+	 *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2334
+	 *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2335
+	 *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2336
+	 *                remember that if an exact match isn't found for these extra cols/val pairs, then a NEW row is
2337
+	 *                created in the join table.
2338
+	 * @return EE_Base_Class the relation was removed from
2339
+	 * @throws ReflectionException
2340
+	 * @throws InvalidArgumentException
2341
+	 * @throws InvalidInterfaceException
2342
+	 * @throws InvalidDataTypeException
2343
+	 * @throws EE_Error
2344
+	 */
2345
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2346
+	{
2347
+		if ($this->ID()) {
2348
+			//if this exists in the DB, save the relation change to the DB too
2349
+			$otherObject = $this->get_model()->remove_relationship_to(
2350
+				$this,
2351
+				$otherObjectModelObjectOrID,
2352
+				$relationName,
2353
+				$where_query
2354
+			);
2355
+			$this->clear_cache(
2356
+				$relationName,
2357
+				$otherObject
2358
+			);
2359
+		} else {
2360
+			//this doesn't exist in the DB, just remove it from the cache
2361
+			$otherObject = $this->clear_cache(
2362
+				$relationName,
2363
+				$otherObjectModelObjectOrID
2364
+			);
2365
+		}
2366
+		if ($otherObject instanceof EE_Base_Class) {
2367
+			$otherObject->clear_cache(
2368
+				$this->get_model()->get_this_model_name(),
2369
+				$this
2370
+			);
2371
+		}
2372
+		return $otherObject;
2373
+	}
2374
+
2375
+
2376
+	/**
2377
+	 * Removes ALL the related things for the $relationName.
2378
+	 *
2379
+	 * @param string $relationName
2380
+	 * @param array  $where_query_params like EEM_Base::get_all's $query_params[0] (where conditions)
2381
+	 * @return EE_Base_Class
2382
+	 * @throws ReflectionException
2383
+	 * @throws InvalidArgumentException
2384
+	 * @throws InvalidInterfaceException
2385
+	 * @throws InvalidDataTypeException
2386
+	 * @throws EE_Error
2387
+	 */
2388
+	public function _remove_relations($relationName, $where_query_params = array())
2389
+	{
2390
+		if ($this->ID()) {
2391
+			//if this exists in the DB, save the relation change to the DB too
2392
+			$otherObjects = $this->get_model()->remove_relations(
2393
+				$this,
2394
+				$relationName,
2395
+				$where_query_params
2396
+			);
2397
+			$this->clear_cache(
2398
+				$relationName,
2399
+				null,
2400
+				true
2401
+			);
2402
+		} else {
2403
+			//this doesn't exist in the DB, just remove it from the cache
2404
+			$otherObjects = $this->clear_cache(
2405
+				$relationName,
2406
+				null,
2407
+				true
2408
+			);
2409
+		}
2410
+		if (is_array($otherObjects)) {
2411
+			foreach ($otherObjects as $otherObject) {
2412
+				$otherObject->clear_cache(
2413
+					$this->get_model()->get_this_model_name(),
2414
+					$this
2415
+				);
2416
+			}
2417
+		}
2418
+		return $otherObjects;
2419
+	}
2420
+
2421
+
2422
+	/**
2423
+	 * Gets all the related model objects of the specified type. Eg, if the current class if
2424
+	 * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2425
+	 * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2426
+	 * because we want to get even deleted items etc.
2427
+	 *
2428
+	 * @param string $relationName key in the model's _model_relations array
2429
+	 * @param array  $query_params like EEM_Base::get_all
2430
+	 * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2431
+	 *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2432
+	 *                             results if you want IDs
2433
+	 * @throws ReflectionException
2434
+	 * @throws InvalidArgumentException
2435
+	 * @throws InvalidInterfaceException
2436
+	 * @throws InvalidDataTypeException
2437
+	 * @throws EE_Error
2438
+	 */
2439
+	public function get_many_related($relationName, $query_params = array())
2440
+	{
2441
+		if ($this->ID()) {
2442
+			//this exists in the DB, so get the related things from either the cache or the DB
2443
+			//if there are query parameters, forget about caching the related model objects.
2444
+			if ($query_params) {
2445
+				$related_model_objects = $this->get_model()->get_all_related(
2446
+					$this,
2447
+					$relationName,
2448
+					$query_params
2449
+				);
2450
+			} else {
2451
+				//did we already cache the result of this query?
2452
+				$cached_results = $this->get_all_from_cache($relationName);
2453
+				if (! $cached_results) {
2454
+					$related_model_objects = $this->get_model()->get_all_related(
2455
+						$this,
2456
+						$relationName,
2457
+						$query_params
2458
+					);
2459
+					//if no query parameters were passed, then we got all the related model objects
2460
+					//for that relation. We can cache them then.
2461
+					foreach ($related_model_objects as $related_model_object) {
2462
+						$this->cache($relationName, $related_model_object);
2463
+					}
2464
+				} else {
2465
+					$related_model_objects = $cached_results;
2466
+				}
2467
+			}
2468
+		} else {
2469
+			//this doesn't exist in the DB, so just get the related things from the cache
2470
+			$related_model_objects = $this->get_all_from_cache($relationName);
2471
+		}
2472
+		return $related_model_objects;
2473
+	}
2474
+
2475
+
2476
+	/**
2477
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2478
+	 * unless otherwise specified in the $query_params
2479
+	 *
2480
+	 * @param string $relation_name  model_name like 'Event', or 'Registration'
2481
+	 * @param array  $query_params   like EEM_Base::get_all's
2482
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2483
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2484
+	 *                               that by the setting $distinct to TRUE;
2485
+	 * @return int
2486
+	 * @throws ReflectionException
2487
+	 * @throws InvalidArgumentException
2488
+	 * @throws InvalidInterfaceException
2489
+	 * @throws InvalidDataTypeException
2490
+	 * @throws EE_Error
2491
+	 */
2492
+	public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2493
+	{
2494
+		return $this->get_model()->count_related(
2495
+			$this,
2496
+			$relation_name,
2497
+			$query_params,
2498
+			$field_to_count,
2499
+			$distinct
2500
+		);
2501
+	}
2502
+
2503
+
2504
+	/**
2505
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2506
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2507
+	 *
2508
+	 * @param string $relation_name model_name like 'Event', or 'Registration'
2509
+	 * @param array  $query_params  like EEM_Base::get_all's
2510
+	 * @param string $field_to_sum  name of field to count by.
2511
+	 *                              By default, uses primary key
2512
+	 *                              (which doesn't make much sense, so you should probably change it)
2513
+	 * @return int
2514
+	 * @throws ReflectionException
2515
+	 * @throws InvalidArgumentException
2516
+	 * @throws InvalidInterfaceException
2517
+	 * @throws InvalidDataTypeException
2518
+	 * @throws EE_Error
2519
+	 */
2520
+	public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2521
+	{
2522
+		return $this->get_model()->sum_related(
2523
+			$this,
2524
+			$relation_name,
2525
+			$query_params,
2526
+			$field_to_sum
2527
+		);
2528
+	}
2529
+
2530
+
2531
+	/**
2532
+	 * Gets the first (ie, one) related model object of the specified type.
2533
+	 *
2534
+	 * @param string $relationName key in the model's _model_relations array
2535
+	 * @param array  $query_params like EEM_Base::get_all
2536
+	 * @return EE_Base_Class (not an array, a single object)
2537
+	 * @throws ReflectionException
2538
+	 * @throws InvalidArgumentException
2539
+	 * @throws InvalidInterfaceException
2540
+	 * @throws InvalidDataTypeException
2541
+	 * @throws EE_Error
2542
+	 */
2543
+	public function get_first_related($relationName, $query_params = array())
2544
+	{
2545
+		$model = $this->get_model();
2546
+		if ($this->ID()) {//this exists in the DB, get from the cache OR the DB
2547
+			//if they've provided some query parameters, don't bother trying to cache the result
2548
+			//also make sure we're not caching the result of get_first_related
2549
+			//on a relation which should have an array of objects (because the cache might have an array of objects)
2550
+			if ($query_params
2551
+				|| ! $model->related_settings_for($relationName)
2552
+					 instanceof
2553
+					 EE_Belongs_To_Relation
2554
+			) {
2555
+				$related_model_object = $model->get_first_related(
2556
+					$this,
2557
+					$relationName,
2558
+					$query_params
2559
+				);
2560
+			} else {
2561
+				//first, check if we've already cached the result of this query
2562
+				$cached_result = $this->get_one_from_cache($relationName);
2563
+				if (! $cached_result) {
2564
+					$related_model_object = $model->get_first_related(
2565
+						$this,
2566
+						$relationName,
2567
+						$query_params
2568
+					);
2569
+					$this->cache($relationName, $related_model_object);
2570
+				} else {
2571
+					$related_model_object = $cached_result;
2572
+				}
2573
+			}
2574
+		} else {
2575
+			$related_model_object = null;
2576
+			// this doesn't exist in the Db,
2577
+			// but maybe the relation is of type belongs to, and so the related thing might
2578
+			if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2579
+				$related_model_object = $model->get_first_related(
2580
+					$this,
2581
+					$relationName,
2582
+					$query_params
2583
+				);
2584
+			}
2585
+			// this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2586
+			// just get what's cached on this object
2587
+			if (! $related_model_object) {
2588
+				$related_model_object = $this->get_one_from_cache($relationName);
2589
+			}
2590
+		}
2591
+		return $related_model_object;
2592
+	}
2593
+
2594
+
2595
+	/**
2596
+	 * Does a delete on all related objects of type $relationName and removes
2597
+	 * the current model object's relation to them. If they can't be deleted (because
2598
+	 * of blocking related model objects) does nothing. If the related model objects are
2599
+	 * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2600
+	 * If this model object doesn't exist yet in the DB, just removes its related things
2601
+	 *
2602
+	 * @param string $relationName
2603
+	 * @param array  $query_params like EEM_Base::get_all's
2604
+	 * @return int how many deleted
2605
+	 * @throws ReflectionException
2606
+	 * @throws InvalidArgumentException
2607
+	 * @throws InvalidInterfaceException
2608
+	 * @throws InvalidDataTypeException
2609
+	 * @throws EE_Error
2610
+	 */
2611
+	public function delete_related($relationName, $query_params = array())
2612
+	{
2613
+		if ($this->ID()) {
2614
+			$count = $this->get_model()->delete_related(
2615
+				$this,
2616
+				$relationName,
2617
+				$query_params
2618
+			);
2619
+		} else {
2620
+			$count = count($this->get_all_from_cache($relationName));
2621
+			$this->clear_cache($relationName, null, true);
2622
+		}
2623
+		return $count;
2624
+	}
2625
+
2626
+
2627
+	/**
2628
+	 * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2629
+	 * the current model object's relation to them. If they can't be deleted (because
2630
+	 * of blocking related model objects) just does a soft delete on it instead, if possible.
2631
+	 * If the related thing isn't a soft-deletable model object, this function is identical
2632
+	 * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2633
+	 *
2634
+	 * @param string $relationName
2635
+	 * @param array  $query_params like EEM_Base::get_all's
2636
+	 * @return int how many deleted (including those soft deleted)
2637
+	 * @throws ReflectionException
2638
+	 * @throws InvalidArgumentException
2639
+	 * @throws InvalidInterfaceException
2640
+	 * @throws InvalidDataTypeException
2641
+	 * @throws EE_Error
2642
+	 */
2643
+	public function delete_related_permanently($relationName, $query_params = array())
2644
+	{
2645
+		if ($this->ID()) {
2646
+			$count = $this->get_model()->delete_related_permanently(
2647
+				$this,
2648
+				$relationName,
2649
+				$query_params
2650
+			);
2651
+		} else {
2652
+			$count = count($this->get_all_from_cache($relationName));
2653
+		}
2654
+		$this->clear_cache($relationName, null, true);
2655
+		return $count;
2656
+	}
2657
+
2658
+
2659
+	/**
2660
+	 * is_set
2661
+	 * Just a simple utility function children can use for checking if property exists
2662
+	 *
2663
+	 * @access  public
2664
+	 * @param  string $field_name property to check
2665
+	 * @return bool                              TRUE if existing,FALSE if not.
2666
+	 */
2667
+	public function is_set($field_name)
2668
+	{
2669
+		return isset($this->_fields[ $field_name ]);
2670
+	}
2671
+
2672
+
2673
+	/**
2674
+	 * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2675
+	 * EE_Error exception if they don't
2676
+	 *
2677
+	 * @param  mixed (string|array) $properties properties to check
2678
+	 * @throws EE_Error
2679
+	 * @return bool                              TRUE if existing, throw EE_Error if not.
2680
+	 */
2681
+	protected function _property_exists($properties)
2682
+	{
2683
+		foreach ((array) $properties as $property_name) {
2684
+			//first make sure this property exists
2685
+			if (! $this->_fields[ $property_name ]) {
2686
+				throw new EE_Error(
2687
+					sprintf(
2688
+						esc_html__(
2689
+							'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2690
+							'event_espresso'
2691
+						),
2692
+						$property_name
2693
+					)
2694
+				);
2695
+			}
2696
+		}
2697
+		return true;
2698
+	}
2699
+
2700
+
2701
+	/**
2702
+	 * This simply returns an array of model fields for this object
2703
+	 *
2704
+	 * @return array
2705
+	 * @throws ReflectionException
2706
+	 * @throws InvalidArgumentException
2707
+	 * @throws InvalidInterfaceException
2708
+	 * @throws InvalidDataTypeException
2709
+	 * @throws EE_Error
2710
+	 */
2711
+	public function model_field_array()
2712
+	{
2713
+		$fields     = $this->get_model()->field_settings(false);
2714
+		$properties = array();
2715
+		//remove prepended underscore
2716
+		foreach ($fields as $field_name => $settings) {
2717
+			$properties[ $field_name ] = $this->get($field_name);
2718
+		}
2719
+		return $properties;
2720
+	}
2721
+
2722
+
2723
+	/**
2724
+	 * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2725
+	 * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2726
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2727
+	 * Instead of requiring a plugin to extend the EE_Base_Class
2728
+	 * (which works fine is there's only 1 plugin, but when will that happen?)
2729
+	 * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2730
+	 * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2731
+	 * and accepts 2 arguments: the object on which the function was called,
2732
+	 * and an array of the original arguments passed to the function.
2733
+	 * Whatever their callback function returns will be returned by this function.
2734
+	 * Example: in functions.php (or in a plugin):
2735
+	 *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2736
+	 *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2737
+	 *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2738
+	 *          return $previousReturnValue.$returnString;
2739
+	 *      }
2740
+	 * require('EE_Answer.class.php');
2741
+	 * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2742
+	 * echo $answer->my_callback('monkeys',100);
2743
+	 * //will output "you called my_callback! and passed args:monkeys,100"
2744
+	 *
2745
+	 * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2746
+	 * @param array  $args       array of original arguments passed to the function
2747
+	 * @throws EE_Error
2748
+	 * @return mixed whatever the plugin which calls add_filter decides
2749
+	 */
2750
+	public function __call($methodName, $args)
2751
+	{
2752
+		$className = get_class($this);
2753
+		$tagName   = "FHEE__{$className}__{$methodName}";
2754
+		if (! has_filter($tagName)) {
2755
+			throw new EE_Error(
2756
+				sprintf(
2757
+					esc_html__(
2758
+						"Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2759
+						'event_espresso'
2760
+					),
2761
+					$methodName,
2762
+					$className,
2763
+					$tagName
2764
+				)
2765
+			);
2766
+		}
2767
+		return apply_filters($tagName, null, $this, $args);
2768
+	}
2769
+
2770
+
2771
+	/**
2772
+	 * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2773
+	 * A $previous_value can be specified in case there are many meta rows with the same key
2774
+	 *
2775
+	 * @param string $meta_key
2776
+	 * @param mixed  $meta_value
2777
+	 * @param mixed  $previous_value
2778
+	 * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2779
+	 *                  NOTE: if the values haven't changed, returns 0
2780
+	 * @throws InvalidArgumentException
2781
+	 * @throws InvalidInterfaceException
2782
+	 * @throws InvalidDataTypeException
2783
+	 * @throws EE_Error
2784
+	 * @throws ReflectionException
2785
+	 */
2786
+	public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2787
+	{
2788
+		$query_params = array(
2789
+			array(
2790
+				'EXM_key'  => $meta_key,
2791
+				'OBJ_ID'   => $this->ID(),
2792
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2793
+			),
2794
+		);
2795
+		if ($previous_value !== null) {
2796
+			$query_params[0]['EXM_value'] = $meta_value;
2797
+		}
2798
+		$existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2799
+		if (! $existing_rows_like_that) {
2800
+			return $this->add_extra_meta($meta_key, $meta_value);
2801
+		}
2802
+		foreach ($existing_rows_like_that as $existing_row) {
2803
+			$existing_row->save(array('EXM_value' => $meta_value));
2804
+		}
2805
+		return count($existing_rows_like_that);
2806
+	}
2807
+
2808
+
2809
+	/**
2810
+	 * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2811
+	 * no other extra meta for this model object have the same key. Returns TRUE if the
2812
+	 * extra meta row was entered, false if not
2813
+	 *
2814
+	 * @param string  $meta_key
2815
+	 * @param mixed   $meta_value
2816
+	 * @param boolean $unique
2817
+	 * @return boolean
2818
+	 * @throws InvalidArgumentException
2819
+	 * @throws InvalidInterfaceException
2820
+	 * @throws InvalidDataTypeException
2821
+	 * @throws EE_Error
2822
+	 * @throws ReflectionException
2823
+	 * @throws ReflectionException
2824
+	 */
2825
+	public function add_extra_meta($meta_key, $meta_value, $unique = false)
2826
+	{
2827
+		if ($unique) {
2828
+			$existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2829
+				array(
2830
+					array(
2831
+						'EXM_key'  => $meta_key,
2832
+						'OBJ_ID'   => $this->ID(),
2833
+						'EXM_type' => $this->get_model()->get_this_model_name(),
2834
+					),
2835
+				)
2836
+			);
2837
+			if ($existing_extra_meta) {
2838
+				return false;
2839
+			}
2840
+		}
2841
+		$new_extra_meta = EE_Extra_Meta::new_instance(
2842
+			array(
2843
+				'EXM_key'   => $meta_key,
2844
+				'EXM_value' => $meta_value,
2845
+				'OBJ_ID'    => $this->ID(),
2846
+				'EXM_type'  => $this->get_model()->get_this_model_name(),
2847
+			)
2848
+		);
2849
+		$new_extra_meta->save();
2850
+		return true;
2851
+	}
2852
+
2853
+
2854
+	/**
2855
+	 * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2856
+	 * is specified, only deletes extra meta records with that value.
2857
+	 *
2858
+	 * @param string $meta_key
2859
+	 * @param mixed  $meta_value
2860
+	 * @return int number of extra meta rows deleted
2861
+	 * @throws InvalidArgumentException
2862
+	 * @throws InvalidInterfaceException
2863
+	 * @throws InvalidDataTypeException
2864
+	 * @throws EE_Error
2865
+	 * @throws ReflectionException
2866
+	 */
2867
+	public function delete_extra_meta($meta_key, $meta_value = null)
2868
+	{
2869
+		$query_params = array(
2870
+			array(
2871
+				'EXM_key'  => $meta_key,
2872
+				'OBJ_ID'   => $this->ID(),
2873
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2874
+			),
2875
+		);
2876
+		if ($meta_value !== null) {
2877
+			$query_params[0]['EXM_value'] = $meta_value;
2878
+		}
2879
+		return EEM_Extra_Meta::instance()->delete($query_params);
2880
+	}
2881
+
2882
+
2883
+	/**
2884
+	 * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2885
+	 * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2886
+	 * You can specify $default is case you haven't found the extra meta
2887
+	 *
2888
+	 * @param string  $meta_key
2889
+	 * @param boolean $single
2890
+	 * @param mixed   $default if we don't find anything, what should we return?
2891
+	 * @return mixed single value if $single; array if ! $single
2892
+	 * @throws ReflectionException
2893
+	 * @throws InvalidArgumentException
2894
+	 * @throws InvalidInterfaceException
2895
+	 * @throws InvalidDataTypeException
2896
+	 * @throws EE_Error
2897
+	 */
2898
+	public function get_extra_meta($meta_key, $single = false, $default = null)
2899
+	{
2900
+		if ($single) {
2901
+			$result = $this->get_first_related(
2902
+				'Extra_Meta',
2903
+				array(array('EXM_key' => $meta_key))
2904
+			);
2905
+			if ($result instanceof EE_Extra_Meta) {
2906
+				return $result->value();
2907
+			}
2908
+		} else {
2909
+			$results = $this->get_many_related(
2910
+				'Extra_Meta',
2911
+				array(array('EXM_key' => $meta_key))
2912
+			);
2913
+			if ($results) {
2914
+				$values = array();
2915
+				foreach ($results as $result) {
2916
+					if ($result instanceof EE_Extra_Meta) {
2917
+						$values[ $result->ID() ] = $result->value();
2918
+					}
2919
+				}
2920
+				return $values;
2921
+			}
2922
+		}
2923
+		//if nothing discovered yet return default.
2924
+		return apply_filters(
2925
+			'FHEE__EE_Base_Class__get_extra_meta__default_value',
2926
+			$default,
2927
+			$meta_key,
2928
+			$single,
2929
+			$this
2930
+		);
2931
+	}
2932
+
2933
+
2934
+	/**
2935
+	 * Returns a simple array of all the extra meta associated with this model object.
2936
+	 * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2937
+	 * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2938
+	 * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2939
+	 * If $one_of_each_key is false, it will return an array with the top-level keys being
2940
+	 * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2941
+	 * finally the extra meta's value as each sub-value. (eg
2942
+	 * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2943
+	 *
2944
+	 * @param boolean $one_of_each_key
2945
+	 * @return array
2946
+	 * @throws ReflectionException
2947
+	 * @throws InvalidArgumentException
2948
+	 * @throws InvalidInterfaceException
2949
+	 * @throws InvalidDataTypeException
2950
+	 * @throws EE_Error
2951
+	 */
2952
+	public function all_extra_meta_array($one_of_each_key = true)
2953
+	{
2954
+		$return_array = array();
2955
+		if ($one_of_each_key) {
2956
+			$extra_meta_objs = $this->get_many_related(
2957
+				'Extra_Meta',
2958
+				array('group_by' => 'EXM_key')
2959
+			);
2960
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2961
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2962
+					$return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2963
+				}
2964
+			}
2965
+		} else {
2966
+			$extra_meta_objs = $this->get_many_related('Extra_Meta');
2967
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2968
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2969
+					if (! isset($return_array[ $extra_meta_obj->key() ])) {
2970
+						$return_array[ $extra_meta_obj->key() ] = array();
2971
+					}
2972
+					$return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2973
+				}
2974
+			}
2975
+		}
2976
+		return $return_array;
2977
+	}
2978
+
2979
+
2980
+	/**
2981
+	 * Gets a pretty nice displayable nice for this model object. Often overridden
2982
+	 *
2983
+	 * @return string
2984
+	 * @throws ReflectionException
2985
+	 * @throws InvalidArgumentException
2986
+	 * @throws InvalidInterfaceException
2987
+	 * @throws InvalidDataTypeException
2988
+	 * @throws EE_Error
2989
+	 */
2990
+	public function name()
2991
+	{
2992
+		//find a field that's not a text field
2993
+		$field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2994
+		if ($field_we_can_use) {
2995
+			return $this->get($field_we_can_use->get_name());
2996
+		}
2997
+		$first_few_properties = $this->model_field_array();
2998
+		$first_few_properties = array_slice($first_few_properties, 0, 3);
2999
+		$name_parts           = array();
3000
+		foreach ($first_few_properties as $name => $value) {
3001
+			$name_parts[] = "$name:$value";
3002
+		}
3003
+		return implode(',', $name_parts);
3004
+	}
3005
+
3006
+
3007
+	/**
3008
+	 * in_entity_map
3009
+	 * Checks if this model object has been proven to already be in the entity map
3010
+	 *
3011
+	 * @return boolean
3012
+	 * @throws ReflectionException
3013
+	 * @throws InvalidArgumentException
3014
+	 * @throws InvalidInterfaceException
3015
+	 * @throws InvalidDataTypeException
3016
+	 * @throws EE_Error
3017
+	 */
3018
+	public function in_entity_map()
3019
+	{
3020
+		// well, if we looked, did we find it in the entity map?
3021
+		return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3022
+	}
3023
+
3024
+
3025
+	/**
3026
+	 * refresh_from_db
3027
+	 * Makes sure the fields and values on this model object are in-sync with what's in the database.
3028
+	 *
3029
+	 * @throws ReflectionException
3030
+	 * @throws InvalidArgumentException
3031
+	 * @throws InvalidInterfaceException
3032
+	 * @throws InvalidDataTypeException
3033
+	 * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3034
+	 * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3035
+	 */
3036
+	public function refresh_from_db()
3037
+	{
3038
+		if ($this->ID() && $this->in_entity_map()) {
3039
+			$this->get_model()->refresh_entity_map_from_db($this->ID());
3040
+		} else {
3041
+			//if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3042
+			//if it has an ID but it's not in the map, and you're asking me to refresh it
3043
+			//that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3044
+			//absolutely nothing in it for this ID
3045
+			if (WP_DEBUG) {
3046
+				throw new EE_Error(
3047
+					sprintf(
3048
+						esc_html__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3049
+							'event_espresso'),
3050
+						$this->ID(),
3051
+						get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3052
+						get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3053
+					)
3054
+				);
3055
+			}
3056
+		}
3057
+	}
3058
+
3059
+
3060
+	/**
3061
+	 * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3062
+	 * (probably a bad assumption they have made, oh well)
3063
+	 *
3064
+	 * @return string
3065
+	 */
3066
+	public function __toString()
3067
+	{
3068
+		try {
3069
+			return sprintf('%s (%s)', $this->name(), $this->ID());
3070
+		} catch (Exception $e) {
3071
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3072
+			return '';
3073
+		}
3074
+	}
3075
+
3076
+
3077
+	/**
3078
+	 * Clear related model objects if they're already in the DB, because otherwise when we
3079
+	 * UN-serialize this model object we'll need to be careful to add them to the entity map.
3080
+	 * This means if we have made changes to those related model objects, and want to unserialize
3081
+	 * the this model object on a subsequent request, changes to those related model objects will be lost.
3082
+	 * Instead, those related model objects should be directly serialized and stored.
3083
+	 * Eg, the following won't work:
3084
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3085
+	 * $att = $reg->attendee();
3086
+	 * $att->set( 'ATT_fname', 'Dirk' );
3087
+	 * update_option( 'my_option', serialize( $reg ) );
3088
+	 * //END REQUEST
3089
+	 * //START NEXT REQUEST
3090
+	 * $reg = get_option( 'my_option' );
3091
+	 * $reg->attendee()->save();
3092
+	 * And would need to be replace with:
3093
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3094
+	 * $att = $reg->attendee();
3095
+	 * $att->set( 'ATT_fname', 'Dirk' );
3096
+	 * update_option( 'my_option', serialize( $reg ) );
3097
+	 * //END REQUEST
3098
+	 * //START NEXT REQUEST
3099
+	 * $att = get_option( 'my_option' );
3100
+	 * $att->save();
3101
+	 *
3102
+	 * @return array
3103
+	 * @throws ReflectionException
3104
+	 * @throws InvalidArgumentException
3105
+	 * @throws InvalidInterfaceException
3106
+	 * @throws InvalidDataTypeException
3107
+	 * @throws EE_Error
3108
+	 */
3109
+	public function __sleep()
3110
+	{
3111
+		$model = $this->get_model();
3112
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3113
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
3114
+				$classname = 'EE_' . $model->get_this_model_name();
3115
+				if (
3116
+					$this->get_one_from_cache($relation_name) instanceof $classname
3117
+					&& $this->get_one_from_cache($relation_name)->ID()
3118
+				) {
3119
+					$this->clear_cache(
3120
+						$relation_name,
3121
+						$this->get_one_from_cache($relation_name)->ID()
3122
+					);
3123
+				}
3124
+			}
3125
+		}
3126
+		$this->_props_n_values_provided_in_constructor = array();
3127
+		$properties_to_serialize                       = get_object_vars($this);
3128
+		//don't serialize the model. It's big and that risks recursion
3129
+		unset($properties_to_serialize['_model']);
3130
+		return array_keys($properties_to_serialize);
3131
+	}
3132
+
3133
+
3134
+	/**
3135
+	 * restore _props_n_values_provided_in_constructor
3136
+	 * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3137
+	 * and therefore should NOT be used to determine if state change has occurred since initial construction.
3138
+	 * At best, you would only be able to detect if state change has occurred during THIS request.
3139
+	 */
3140
+	public function __wakeup()
3141
+	{
3142
+		$this->_props_n_values_provided_in_constructor = $this->_fields;
3143
+	}
3144
+
3145
+
3146
+	/**
3147
+	 * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3148
+	 * distinct with the clone host instance are also cloned.
3149
+	 */
3150
+	public function __clone()
3151
+	{
3152
+		//handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3153
+		foreach ($this->_fields as $field => $value) {
3154
+			if ($value instanceof DateTime) {
3155
+				$this->_fields[$field] = clone $value;
3156
+			}
3157
+		}
3158
+	}
3159 3159
 }
3160 3160
 
3161 3161
 
Please login to merge, or discard this patch.
Spacing   +116 added lines, -116 removed lines patch added patch discarded remove patch
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
         $fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
149 149
         // verify client code has not passed any invalid field names
150 150
         foreach ($fieldValues as $field_name => $field_value) {
151
-            if (! isset($model_fields[ $field_name ])) {
151
+            if ( ! isset($model_fields[$field_name])) {
152 152
                 throw new EE_Error(
153 153
                     sprintf(
154 154
                         esc_html__(
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
             }
164 164
         }
165 165
         $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
166
-        if (! empty($date_formats) && is_array($date_formats)) {
166
+        if ( ! empty($date_formats) && is_array($date_formats)) {
167 167
             list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
168 168
         } else {
169 169
             //set default formats for date and time
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
             foreach ($model_fields as $fieldName => $field) {
177 177
                 $this->set_from_db(
178 178
                     $fieldName,
179
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
179
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null
180 180
                 );
181 181
             }
182 182
         } else {
@@ -185,22 +185,22 @@  discard block
 block discarded – undo
185 185
             foreach ($model_fields as $fieldName => $field) {
186 186
                 $this->set(
187 187
                     $fieldName,
188
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null, true
188
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null, true
189 189
                 );
190 190
             }
191 191
         }
192 192
         //remember what values were passed to this constructor
193 193
         $this->_props_n_values_provided_in_constructor = $fieldValues;
194 194
         //remember in entity mapper
195
-        if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
195
+        if ( ! $bydb && $model->has_primary_key_field() && $this->ID()) {
196 196
             $model->add_to_entity_map($this);
197 197
         }
198 198
         //setup all the relations
199 199
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
200 200
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
201
-                $this->_model_relations[ $relation_name ] = null;
201
+                $this->_model_relations[$relation_name] = null;
202 202
             } else {
203
-                $this->_model_relations[ $relation_name ] = array();
203
+                $this->_model_relations[$relation_name] = array();
204 204
             }
205 205
         }
206 206
         /**
@@ -251,10 +251,10 @@  discard block
 block discarded – undo
251 251
      */
252 252
     public function get_original($field_name)
253 253
     {
254
-        if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
254
+        if (isset($this->_props_n_values_provided_in_constructor[$field_name])
255 255
             && $field_settings = $this->get_model()->field_settings_for($field_name)
256 256
         ) {
257
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
257
+            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[$field_name]);
258 258
         }
259 259
         return null;
260 260
     }
@@ -291,7 +291,7 @@  discard block
 block discarded – undo
291 291
         // then don't do anything
292 292
         if (
293 293
             ! $use_default
294
-            && $this->_fields[ $field_name ] === $field_value
294
+            && $this->_fields[$field_name] === $field_value
295 295
             && $this->ID()
296 296
         ) {
297 297
             return;
@@ -309,7 +309,7 @@  discard block
 block discarded – undo
309 309
             $holder_of_value = $field_obj->prepare_for_set($field_value);
310 310
             //should the value be null?
311 311
             if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
312
+                $this->_fields[$field_name] = $field_obj->get_default_value();
313 313
                 /**
314 314
                  * To save having to refactor all the models, if a default value is used for a
315 315
                  * EE_Datetime_Field, and that value is not null nor is it a DateTime
@@ -320,15 +320,15 @@  discard block
 block discarded – undo
320 320
                  */
321 321
                 if (
322 322
                     $field_obj instanceof EE_Datetime_Field
323
-                    && $this->_fields[ $field_name ] !== null
324
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
323
+                    && $this->_fields[$field_name] !== null
324
+                    && ! $this->_fields[$field_name] instanceof DateTime
325 325
                 ) {
326
-                    empty($this->_fields[ $field_name ])
326
+                    empty($this->_fields[$field_name])
327 327
                         ? $this->set($field_name, time())
328
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
328
+                        : $this->set($field_name, $this->_fields[$field_name]);
329 329
                 }
330 330
             } else {
331
-                $this->_fields[ $field_name ] = $holder_of_value;
331
+                $this->_fields[$field_name] = $holder_of_value;
332 332
             }
333 333
             //if we're not in the constructor...
334 334
             //now check if what we set was a primary key
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
                 $fields_on_model = self::_get_model(get_class($this))->field_settings();
346 346
                 $obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347 347
                 foreach ($fields_on_model as $field_obj) {
348
-                    if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
348
+                    if ( ! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
349 349
                         && $field_obj->get_name() !== $field_name
350 350
                     ) {
351 351
                         $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
@@ -390,8 +390,8 @@  discard block
 block discarded – undo
390 390
      */
391 391
     public function getCustomSelect($alias)
392 392
     {
393
-        return isset($this->custom_selection_results[ $alias ])
394
-            ? $this->custom_selection_results[ $alias ]
393
+        return isset($this->custom_selection_results[$alias])
394
+            ? $this->custom_selection_results[$alias]
395 395
             : null;
396 396
     }
397 397
 
@@ -478,8 +478,8 @@  discard block
 block discarded – undo
478 478
         foreach ($model_fields as $field_name => $field_obj) {
479 479
             if ($field_obj instanceof EE_Datetime_Field) {
480 480
                 $field_obj->set_timezone($this->_timezone);
481
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
482
-                    $this->_fields[ $field_name ]->setTimezone(new DateTimeZone($this->_timezone));
481
+                if (isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime) {
482
+                    $this->_fields[$field_name]->setTimezone(new DateTimeZone($this->_timezone));
483 483
                 }
484 484
             }
485 485
         }
@@ -537,7 +537,7 @@  discard block
 block discarded – undo
537 537
      */
538 538
     public function get_format($full = true)
539 539
     {
540
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
540
+        return $full ? $this->_dt_frmt.' '.$this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541 541
     }
542 542
 
543 543
 
@@ -563,11 +563,11 @@  discard block
 block discarded – undo
563 563
     public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
564 564
     {
565 565
         // its entirely possible that there IS no related object yet in which case there is nothing to cache.
566
-        if (! $object_to_cache instanceof EE_Base_Class) {
566
+        if ( ! $object_to_cache instanceof EE_Base_Class) {
567 567
             return false;
568 568
         }
569 569
         // also get "how" the object is related, or throw an error
570
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
570
+        if ( ! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571 571
             throw new EE_Error(
572 572
                 sprintf(
573 573
                     esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
@@ -581,38 +581,38 @@  discard block
 block discarded – undo
581 581
             // if it's a "belongs to" relationship, then there's only one related model object
582 582
             // eg, if this is a registration, there's only 1 attendee for it
583 583
             // so for these model objects just set it to be cached
584
-            $this->_model_relations[ $relationName ] = $object_to_cache;
584
+            $this->_model_relations[$relationName] = $object_to_cache;
585 585
             $return                                  = true;
586 586
         } else {
587 587
             // otherwise, this is the "many" side of a one to many relationship,
588 588
             // so we'll add the object to the array of related objects for that type.
589 589
             // eg: if this is an event, there are many registrations for that event,
590 590
             // so we cache the registrations in an array
591
-            if (! is_array($this->_model_relations[ $relationName ])) {
591
+            if ( ! is_array($this->_model_relations[$relationName])) {
592 592
                 // if for some reason, the cached item is a model object,
593 593
                 // then stick that in the array, otherwise start with an empty array
594
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
594
+                $this->_model_relations[$relationName] = $this->_model_relations[$relationName]
595 595
                                                            instanceof
596 596
                                                            EE_Base_Class
597
-                    ? array($this->_model_relations[ $relationName ]) : array();
597
+                    ? array($this->_model_relations[$relationName]) : array();
598 598
             }
599 599
             // first check for a cache_id which is normally empty
600
-            if (! empty($cache_id)) {
600
+            if ( ! empty($cache_id)) {
601 601
                 // if the cache_id exists, then it means we are purposely trying to cache this
602 602
                 // with a known key that can then be used to retrieve the object later on
603
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
603
+                $this->_model_relations[$relationName][$cache_id] = $object_to_cache;
604 604
                 $return                                               = $cache_id;
605 605
             } elseif ($object_to_cache->ID()) {
606 606
                 // OR the cached object originally came from the db, so let's just use it's PK for an ID
607
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
607
+                $this->_model_relations[$relationName][$object_to_cache->ID()] = $object_to_cache;
608 608
                 $return                                                            = $object_to_cache->ID();
609 609
             } else {
610 610
                 // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
611
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
611
+                $this->_model_relations[$relationName][] = $object_to_cache;
612 612
                 // move the internal pointer to the end of the array
613
-                end($this->_model_relations[ $relationName ]);
613
+                end($this->_model_relations[$relationName]);
614 614
                 // and grab the key so that we can return it
615
-                $return = key($this->_model_relations[ $relationName ]);
615
+                $return = key($this->_model_relations[$relationName]);
616 616
             }
617 617
         }
618 618
         return $return;
@@ -638,7 +638,7 @@  discard block
 block discarded – undo
638 638
         //first make sure this property exists
639 639
         $this->get_model()->field_settings_for($fieldname);
640 640
         $cache_type                                            = empty($cache_type) ? 'standard' : $cache_type;
641
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
641
+        $this->_cached_properties[$fieldname][$cache_type] = $value;
642 642
     }
643 643
 
644 644
 
@@ -667,9 +667,9 @@  discard block
 block discarded – undo
667 667
         $model = $this->get_model();
668 668
         $model->field_settings_for($fieldname);
669 669
         $cache_type = $pretty ? 'pretty' : 'standard';
670
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
671
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
672
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
670
+        $cache_type .= ! empty($extra_cache_ref) ? '_'.$extra_cache_ref : '';
671
+        if (isset($this->_cached_properties[$fieldname][$cache_type])) {
672
+            return $this->_cached_properties[$fieldname][$cache_type];
673 673
         }
674 674
         $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
675 675
         $this->_set_cached_property($fieldname, $value, $cache_type);
@@ -697,12 +697,12 @@  discard block
 block discarded – undo
697 697
         if ($field_obj instanceof EE_Datetime_Field) {
698 698
             $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
699 699
         }
700
-        if (! isset($this->_fields[ $fieldname ])) {
701
-            $this->_fields[ $fieldname ] = null;
700
+        if ( ! isset($this->_fields[$fieldname])) {
701
+            $this->_fields[$fieldname] = null;
702 702
         }
703 703
         $value = $pretty
704
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
705
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
704
+            ? $field_obj->prepare_for_pretty_echoing($this->_fields[$fieldname], $extra_cache_ref)
705
+            : $field_obj->prepare_for_get($this->_fields[$fieldname]);
706 706
         return $value;
707 707
     }
708 708
 
@@ -760,8 +760,8 @@  discard block
 block discarded – undo
760 760
      */
761 761
     protected function _clear_cached_property($property_name)
762 762
     {
763
-        if (isset($this->_cached_properties[ $property_name ])) {
764
-            unset($this->_cached_properties[ $property_name ]);
763
+        if (isset($this->_cached_properties[$property_name])) {
764
+            unset($this->_cached_properties[$property_name]);
765 765
         }
766 766
     }
767 767
 
@@ -813,7 +813,7 @@  discard block
 block discarded – undo
813 813
     {
814 814
         $relationship_to_model = $this->get_model()->related_settings_for($relationName);
815 815
         $index_in_cache        = '';
816
-        if (! $relationship_to_model) {
816
+        if ( ! $relationship_to_model) {
817 817
             throw new EE_Error(
818 818
                 sprintf(
819 819
                     esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
@@ -824,21 +824,21 @@  discard block
 block discarded – undo
824 824
         }
825 825
         if ($clear_all) {
826 826
             $obj_removed                             = true;
827
-            $this->_model_relations[ $relationName ] = null;
827
+            $this->_model_relations[$relationName] = null;
828 828
         } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
829
-            $obj_removed                             = $this->_model_relations[ $relationName ];
830
-            $this->_model_relations[ $relationName ] = null;
829
+            $obj_removed                             = $this->_model_relations[$relationName];
830
+            $this->_model_relations[$relationName] = null;
831 831
         } else {
832 832
             if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
833 833
                 && $object_to_remove_or_index_into_array->ID()
834 834
             ) {
835 835
                 $index_in_cache = $object_to_remove_or_index_into_array->ID();
836
-                if (is_array($this->_model_relations[ $relationName ])
837
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
836
+                if (is_array($this->_model_relations[$relationName])
837
+                    && ! isset($this->_model_relations[$relationName][$index_in_cache])
838 838
                 ) {
839 839
                     $index_found_at = null;
840 840
                     //find this object in the array even though it has a different key
841
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
841
+                    foreach ($this->_model_relations[$relationName] as $index => $obj) {
842 842
                         /** @noinspection TypeUnsafeComparisonInspection */
843 843
                         if (
844 844
                             $obj instanceof EE_Base_Class
@@ -872,9 +872,9 @@  discard block
 block discarded – undo
872 872
             }
873 873
             //supposedly we've found it. But it could just be that the client code
874 874
             //provided a bad index/object
875
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
876
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
877
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
875
+            if (isset($this->_model_relations[$relationName][$index_in_cache])) {
876
+                $obj_removed = $this->_model_relations[$relationName][$index_in_cache];
877
+                unset($this->_model_relations[$relationName][$index_in_cache]);
878 878
             } else {
879 879
                 //that thing was never cached anyways.
880 880
                 $obj_removed = null;
@@ -905,7 +905,7 @@  discard block
 block discarded – undo
905 905
         $current_cache_id = ''
906 906
     ) {
907 907
         // verify that incoming object is of the correct type
908
-        $obj_class = 'EE_' . $relationName;
908
+        $obj_class = 'EE_'.$relationName;
909 909
         if ($newly_saved_object instanceof $obj_class) {
910 910
             /* @type EE_Base_Class $newly_saved_object */
911 911
             // now get the type of relation
@@ -913,17 +913,17 @@  discard block
 block discarded – undo
913 913
             // if this is a 1:1 relationship
914 914
             if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
915 915
                 // then just replace the cached object with the newly saved object
916
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
916
+                $this->_model_relations[$relationName] = $newly_saved_object;
917 917
                 return true;
918 918
                 // or if it's some kind of sordid feral polyamorous relationship...
919 919
             }
920
-            if (is_array($this->_model_relations[ $relationName ])
921
-                      && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
920
+            if (is_array($this->_model_relations[$relationName])
921
+                      && isset($this->_model_relations[$relationName][$current_cache_id])
922 922
             ) {
923 923
                 // then remove the current cached item
924
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
924
+                unset($this->_model_relations[$relationName][$current_cache_id]);
925 925
                 // and cache the newly saved object using it's new ID
926
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
926
+                $this->_model_relations[$relationName][$newly_saved_object->ID()] = $newly_saved_object;
927 927
                 return true;
928 928
             }
929 929
         }
@@ -940,8 +940,8 @@  discard block
 block discarded – undo
940 940
      */
941 941
     public function get_one_from_cache($relationName)
942 942
     {
943
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
944
-            ? $this->_model_relations[ $relationName ]
943
+        $cached_array_or_object = isset($this->_model_relations[$relationName])
944
+            ? $this->_model_relations[$relationName]
945 945
             : null;
946 946
         if (is_array($cached_array_or_object)) {
947 947
             return array_shift($cached_array_or_object);
@@ -964,7 +964,7 @@  discard block
 block discarded – undo
964 964
      */
965 965
     public function get_all_from_cache($relationName)
966 966
     {
967
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
967
+        $objects = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName] : array();
968 968
         // if the result is not an array, but exists, make it an array
969 969
         $objects = is_array($objects) ? $objects : array($objects);
970 970
         //bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
@@ -1148,7 +1148,7 @@  discard block
 block discarded – undo
1148 1148
             } else {
1149 1149
                 $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1150 1150
             }
1151
-            $this->_fields[ $field_name ] = $field_value;
1151
+            $this->_fields[$field_name] = $field_value;
1152 1152
             $this->_clear_cached_property($field_name);
1153 1153
         }
1154 1154
     }
@@ -1188,9 +1188,9 @@  discard block
 block discarded – undo
1188 1188
     public function get_raw($field_name)
1189 1189
     {
1190 1190
         $field_settings = $this->get_model()->field_settings_for($field_name);
1191
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1192
-            ? $this->_fields[ $field_name ]->format('U')
1193
-            : $this->_fields[ $field_name ];
1191
+        return $field_settings instanceof EE_Datetime_Field && $this->_fields[$field_name] instanceof DateTime
1192
+            ? $this->_fields[$field_name]->format('U')
1193
+            : $this->_fields[$field_name];
1194 1194
     }
1195 1195
 
1196 1196
 
@@ -1212,7 +1212,7 @@  discard block
 block discarded – undo
1212 1212
     public function get_DateTime_object($field_name)
1213 1213
     {
1214 1214
         $field_settings = $this->get_model()->field_settings_for($field_name);
1215
-        if (! $field_settings instanceof EE_Datetime_Field) {
1215
+        if ( ! $field_settings instanceof EE_Datetime_Field) {
1216 1216
             EE_Error::add_error(
1217 1217
                 sprintf(
1218 1218
                     esc_html__(
@@ -1470,7 +1470,7 @@  discard block
 block discarded – undo
1470 1470
      */
1471 1471
     public function get_i18n_datetime($field_name, $format = '')
1472 1472
     {
1473
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1473
+        $format = empty($format) ? $this->_dt_frmt.' '.$this->_tm_frmt : $format;
1474 1474
         return date_i18n(
1475 1475
             $format,
1476 1476
             EEH_DTT_Helper::get_timestamp_with_offset(
@@ -1582,19 +1582,19 @@  discard block
 block discarded – undo
1582 1582
         $field->set_time_format($this->_tm_frmt);
1583 1583
         switch ($what) {
1584 1584
             case 'T' :
1585
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1585
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_time(
1586 1586
                     $datetime_value,
1587
-                    $this->_fields[ $fieldname ]
1587
+                    $this->_fields[$fieldname]
1588 1588
                 );
1589 1589
                 break;
1590 1590
             case 'D' :
1591
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1591
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_date(
1592 1592
                     $datetime_value,
1593
-                    $this->_fields[ $fieldname ]
1593
+                    $this->_fields[$fieldname]
1594 1594
                 );
1595 1595
                 break;
1596 1596
             case 'B' :
1597
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1597
+                $this->_fields[$fieldname] = $field->prepare_for_set($datetime_value);
1598 1598
                 break;
1599 1599
         }
1600 1600
         $this->_clear_cached_property($fieldname);
@@ -1636,7 +1636,7 @@  discard block
 block discarded – undo
1636 1636
         $this->set_timezone($timezone);
1637 1637
         $fn   = (array) $field_name;
1638 1638
         $args = array_merge($fn, (array) $args);
1639
-        if (! method_exists($this, $callback)) {
1639
+        if ( ! method_exists($this, $callback)) {
1640 1640
             throw new EE_Error(
1641 1641
                 sprintf(
1642 1642
                     esc_html__(
@@ -1648,7 +1648,7 @@  discard block
 block discarded – undo
1648 1648
             );
1649 1649
         }
1650 1650
         $args   = (array) $args;
1651
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1651
+        $return = $prepend.call_user_func_array(array($this, $callback), $args).$append;
1652 1652
         $this->set_timezone($original_timezone);
1653 1653
         return $return;
1654 1654
     }
@@ -1763,8 +1763,8 @@  discard block
 block discarded – undo
1763 1763
     {
1764 1764
         $model = $this->get_model();
1765 1765
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1766
-            if (! empty($this->_model_relations[ $relation_name ])) {
1767
-                $related_objects = $this->_model_relations[ $relation_name ];
1766
+            if ( ! empty($this->_model_relations[$relation_name])) {
1767
+                $related_objects = $this->_model_relations[$relation_name];
1768 1768
                 if ($relation_obj instanceof EE_Belongs_To_Relation) {
1769 1769
                     //this relation only stores a single model object, not an array
1770 1770
                     //but let's make it consistent
@@ -1821,7 +1821,7 @@  discard block
 block discarded – undo
1821 1821
             $this->set($column, $value);
1822 1822
         }
1823 1823
         // no changes ? then don't do anything
1824
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1824
+        if ( ! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1825 1825
             return 0;
1826 1826
         }
1827 1827
         /**
@@ -1831,7 +1831,7 @@  discard block
 block discarded – undo
1831 1831
          * @param EE_Base_Class $model_object the model object about to be saved.
1832 1832
          */
1833 1833
         do_action('AHEE__EE_Base_Class__save__begin', $this);
1834
-        if (! $this->allow_persist()) {
1834
+        if ( ! $this->allow_persist()) {
1835 1835
             return 0;
1836 1836
         }
1837 1837
         // now get current attribute values
@@ -1846,10 +1846,10 @@  discard block
 block discarded – undo
1846 1846
         if ($model->has_primary_key_field()) {
1847 1847
             if ($model->get_primary_key_field()->is_auto_increment()) {
1848 1848
                 //ok check if it's set, if so: update; if not, insert
1849
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1849
+                if ( ! empty($save_cols_n_values[$model->primary_key_name()])) {
1850 1850
                     $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1851 1851
                 } else {
1852
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1852
+                    unset($save_cols_n_values[$model->primary_key_name()]);
1853 1853
                     $results = $model->insert($save_cols_n_values);
1854 1854
                     if ($results) {
1855 1855
                         //if successful, set the primary key
@@ -1859,7 +1859,7 @@  discard block
 block discarded – undo
1859 1859
                         //will get added to the mapper before we can add this one!
1860 1860
                         //but if we just avoid using the SET method, all that headache can be avoided
1861 1861
                         $pk_field_name                   = $model->primary_key_name();
1862
-                        $this->_fields[ $pk_field_name ] = $results;
1862
+                        $this->_fields[$pk_field_name] = $results;
1863 1863
                         $this->_clear_cached_property($pk_field_name);
1864 1864
                         $model->add_to_entity_map($this);
1865 1865
                         $this->_update_cached_related_model_objs_fks();
@@ -1876,8 +1876,8 @@  discard block
 block discarded – undo
1876 1876
                                     'event_espresso'
1877 1877
                                 ),
1878 1878
                                 get_class($this),
1879
-                                get_class($model) . '::instance()->add_to_entity_map()',
1880
-                                get_class($model) . '::instance()->get_one_by_ID()',
1879
+                                get_class($model).'::instance()->add_to_entity_map()',
1880
+                                get_class($model).'::instance()->get_one_by_ID()',
1881 1881
                                 '<br />'
1882 1882
                             )
1883 1883
                         );
@@ -1977,27 +1977,27 @@  discard block
 block discarded – undo
1977 1977
     public function save_new_cached_related_model_objs()
1978 1978
     {
1979 1979
         //make sure this has been saved
1980
-        if (! $this->ID()) {
1980
+        if ( ! $this->ID()) {
1981 1981
             $id = $this->save();
1982 1982
         } else {
1983 1983
             $id = $this->ID();
1984 1984
         }
1985 1985
         //now save all the NEW cached model objects  (ie they don't exist in the DB)
1986 1986
         foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1987
-            if ($this->_model_relations[ $relationName ]) {
1987
+            if ($this->_model_relations[$relationName]) {
1988 1988
                 //is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1989 1989
                 //or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1990 1990
                 /* @var $related_model_obj EE_Base_Class */
1991 1991
                 if ($relationObj instanceof EE_Belongs_To_Relation) {
1992 1992
                     //add a relation to that relation type (which saves the appropriate thing in the process)
1993 1993
                     //but ONLY if it DOES NOT exist in the DB
1994
-                    $related_model_obj = $this->_model_relations[ $relationName ];
1994
+                    $related_model_obj = $this->_model_relations[$relationName];
1995 1995
                     //					if( ! $related_model_obj->ID()){
1996 1996
                     $this->_add_relation_to($related_model_obj, $relationName);
1997 1997
                     $related_model_obj->save_new_cached_related_model_objs();
1998 1998
                     //					}
1999 1999
                 } else {
2000
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2000
+                    foreach ($this->_model_relations[$relationName] as $related_model_obj) {
2001 2001
                         //add a relation to that relation type (which saves the appropriate thing in the process)
2002 2002
                         //but ONLY if it DOES NOT exist in the DB
2003 2003
                         //						if( ! $related_model_obj->ID()){
@@ -2024,7 +2024,7 @@  discard block
 block discarded – undo
2024 2024
      */
2025 2025
     public function get_model()
2026 2026
     {
2027
-        if (! $this->_model) {
2027
+        if ( ! $this->_model) {
2028 2028
             $modelName    = self::_get_model_classname(get_class($this));
2029 2029
             $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2030 2030
         } else {
@@ -2050,9 +2050,9 @@  discard block
 block discarded – undo
2050 2050
         $primary_id_ref = self::_get_primary_key_name($classname);
2051 2051
         if (
2052 2052
             array_key_exists($primary_id_ref, $props_n_values)
2053
-            && ! empty($props_n_values[ $primary_id_ref ])
2053
+            && ! empty($props_n_values[$primary_id_ref])
2054 2054
         ) {
2055
-            $id = $props_n_values[ $primary_id_ref ];
2055
+            $id = $props_n_values[$primary_id_ref];
2056 2056
             return self::_get_model($classname)->get_from_entity_map($id);
2057 2057
         }
2058 2058
         return false;
@@ -2086,10 +2086,10 @@  discard block
 block discarded – undo
2086 2086
         if ($model->has_primary_key_field()) {
2087 2087
             $primary_id_ref = self::_get_primary_key_name($classname);
2088 2088
             if (array_key_exists($primary_id_ref, $props_n_values)
2089
-                && ! empty($props_n_values[ $primary_id_ref ])
2089
+                && ! empty($props_n_values[$primary_id_ref])
2090 2090
             ) {
2091 2091
                 $existing = $model->get_one_by_ID(
2092
-                    $props_n_values[ $primary_id_ref ]
2092
+                    $props_n_values[$primary_id_ref]
2093 2093
                 );
2094 2094
             }
2095 2095
         } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
@@ -2101,7 +2101,7 @@  discard block
 block discarded – undo
2101 2101
         }
2102 2102
         if ($existing) {
2103 2103
             //set date formats if present before setting values
2104
-            if (! empty($date_formats) && is_array($date_formats)) {
2104
+            if ( ! empty($date_formats) && is_array($date_formats)) {
2105 2105
                 $existing->set_date_format($date_formats[0]);
2106 2106
                 $existing->set_time_format($date_formats[1]);
2107 2107
             } else {
@@ -2134,7 +2134,7 @@  discard block
 block discarded – undo
2134 2134
     protected static function _get_model($classname, $timezone = null)
2135 2135
     {
2136 2136
         //find model for this class
2137
-        if (! $classname) {
2137
+        if ( ! $classname) {
2138 2138
             throw new EE_Error(
2139 2139
                 sprintf(
2140 2140
                     esc_html__(
@@ -2183,7 +2183,7 @@  discard block
 block discarded – undo
2183 2183
         if (strpos($model_name, 'EE_') === 0) {
2184 2184
             $model_classname = str_replace('EE_', 'EEM_', $model_name);
2185 2185
         } else {
2186
-            $model_classname = 'EEM_' . $model_name;
2186
+            $model_classname = 'EEM_'.$model_name;
2187 2187
         }
2188 2188
         return $model_classname;
2189 2189
     }
@@ -2202,7 +2202,7 @@  discard block
 block discarded – undo
2202 2202
      */
2203 2203
     protected static function _get_primary_key_name($classname = null)
2204 2204
     {
2205
-        if (! $classname) {
2205
+        if ( ! $classname) {
2206 2206
             throw new EE_Error(
2207 2207
                 sprintf(
2208 2208
                     esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
@@ -2232,7 +2232,7 @@  discard block
 block discarded – undo
2232 2232
         $model = $this->get_model();
2233 2233
         //now that we know the name of the variable, use a variable variable to get its value and return its
2234 2234
         if ($model->has_primary_key_field()) {
2235
-            return $this->_fields[ $model->primary_key_name() ];
2235
+            return $this->_fields[$model->primary_key_name()];
2236 2236
         }
2237 2237
         return $model->get_index_primary_key_string($this->_fields);
2238 2238
     }
@@ -2285,7 +2285,7 @@  discard block
 block discarded – undo
2285 2285
             }
2286 2286
         } else {
2287 2287
             //this thing doesn't exist in the DB,  so just cache it
2288
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2288
+            if ( ! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2289 2289
                 throw new EE_Error(
2290 2290
                     sprintf(
2291 2291
                         esc_html__(
@@ -2450,7 +2450,7 @@  discard block
 block discarded – undo
2450 2450
             } else {
2451 2451
                 //did we already cache the result of this query?
2452 2452
                 $cached_results = $this->get_all_from_cache($relationName);
2453
-                if (! $cached_results) {
2453
+                if ( ! $cached_results) {
2454 2454
                     $related_model_objects = $this->get_model()->get_all_related(
2455 2455
                         $this,
2456 2456
                         $relationName,
@@ -2560,7 +2560,7 @@  discard block
 block discarded – undo
2560 2560
             } else {
2561 2561
                 //first, check if we've already cached the result of this query
2562 2562
                 $cached_result = $this->get_one_from_cache($relationName);
2563
-                if (! $cached_result) {
2563
+                if ( ! $cached_result) {
2564 2564
                     $related_model_object = $model->get_first_related(
2565 2565
                         $this,
2566 2566
                         $relationName,
@@ -2584,7 +2584,7 @@  discard block
 block discarded – undo
2584 2584
             }
2585 2585
             // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2586 2586
             // just get what's cached on this object
2587
-            if (! $related_model_object) {
2587
+            if ( ! $related_model_object) {
2588 2588
                 $related_model_object = $this->get_one_from_cache($relationName);
2589 2589
             }
2590 2590
         }
@@ -2666,7 +2666,7 @@  discard block
 block discarded – undo
2666 2666
      */
2667 2667
     public function is_set($field_name)
2668 2668
     {
2669
-        return isset($this->_fields[ $field_name ]);
2669
+        return isset($this->_fields[$field_name]);
2670 2670
     }
2671 2671
 
2672 2672
 
@@ -2682,7 +2682,7 @@  discard block
 block discarded – undo
2682 2682
     {
2683 2683
         foreach ((array) $properties as $property_name) {
2684 2684
             //first make sure this property exists
2685
-            if (! $this->_fields[ $property_name ]) {
2685
+            if ( ! $this->_fields[$property_name]) {
2686 2686
                 throw new EE_Error(
2687 2687
                     sprintf(
2688 2688
                         esc_html__(
@@ -2714,7 +2714,7 @@  discard block
 block discarded – undo
2714 2714
         $properties = array();
2715 2715
         //remove prepended underscore
2716 2716
         foreach ($fields as $field_name => $settings) {
2717
-            $properties[ $field_name ] = $this->get($field_name);
2717
+            $properties[$field_name] = $this->get($field_name);
2718 2718
         }
2719 2719
         return $properties;
2720 2720
     }
@@ -2751,7 +2751,7 @@  discard block
 block discarded – undo
2751 2751
     {
2752 2752
         $className = get_class($this);
2753 2753
         $tagName   = "FHEE__{$className}__{$methodName}";
2754
-        if (! has_filter($tagName)) {
2754
+        if ( ! has_filter($tagName)) {
2755 2755
             throw new EE_Error(
2756 2756
                 sprintf(
2757 2757
                     esc_html__(
@@ -2796,7 +2796,7 @@  discard block
 block discarded – undo
2796 2796
             $query_params[0]['EXM_value'] = $meta_value;
2797 2797
         }
2798 2798
         $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2799
-        if (! $existing_rows_like_that) {
2799
+        if ( ! $existing_rows_like_that) {
2800 2800
             return $this->add_extra_meta($meta_key, $meta_value);
2801 2801
         }
2802 2802
         foreach ($existing_rows_like_that as $existing_row) {
@@ -2914,7 +2914,7 @@  discard block
 block discarded – undo
2914 2914
                 $values = array();
2915 2915
                 foreach ($results as $result) {
2916 2916
                     if ($result instanceof EE_Extra_Meta) {
2917
-                        $values[ $result->ID() ] = $result->value();
2917
+                        $values[$result->ID()] = $result->value();
2918 2918
                     }
2919 2919
                 }
2920 2920
                 return $values;
@@ -2959,17 +2959,17 @@  discard block
 block discarded – undo
2959 2959
             );
2960 2960
             foreach ($extra_meta_objs as $extra_meta_obj) {
2961 2961
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2962
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2962
+                    $return_array[$extra_meta_obj->key()] = $extra_meta_obj->value();
2963 2963
                 }
2964 2964
             }
2965 2965
         } else {
2966 2966
             $extra_meta_objs = $this->get_many_related('Extra_Meta');
2967 2967
             foreach ($extra_meta_objs as $extra_meta_obj) {
2968 2968
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2969
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2970
-                        $return_array[ $extra_meta_obj->key() ] = array();
2969
+                    if ( ! isset($return_array[$extra_meta_obj->key()])) {
2970
+                        $return_array[$extra_meta_obj->key()] = array();
2971 2971
                     }
2972
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2972
+                    $return_array[$extra_meta_obj->key()][$extra_meta_obj->ID()] = $extra_meta_obj->value();
2973 2973
                 }
2974 2974
             }
2975 2975
         }
@@ -3048,8 +3048,8 @@  discard block
 block discarded – undo
3048 3048
                         esc_html__('Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3049 3049
                             'event_espresso'),
3050 3050
                         $this->ID(),
3051
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3052
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3051
+                        get_class($this->get_model()).'::instance()->add_to_entity_map()',
3052
+                        get_class($this->get_model()).'::instance()->refresh_entity_map()'
3053 3053
                     )
3054 3054
                 );
3055 3055
             }
@@ -3111,7 +3111,7 @@  discard block
 block discarded – undo
3111 3111
         $model = $this->get_model();
3112 3112
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3113 3113
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
3114
-                $classname = 'EE_' . $model->get_this_model_name();
3114
+                $classname = 'EE_'.$model->get_this_model_name();
3115 3115
                 if (
3116 3116
                     $this->get_one_from_cache($relation_name) instanceof $classname
3117 3117
                     && $this->get_one_from_cache($relation_name)->ID()
Please login to merge, or discard this patch.
Doc Comments   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -712,7 +712,7 @@  discard block
 block discarded – undo
712 712
      *
713 713
      * @param \EE_Datetime_Field $datetime_field
714 714
      * @param bool               $pretty
715
-     * @param null               $date_or_time
715
+     * @param string|null               $date_or_time
716 716
      * @return void
717 717
      * @throws InvalidArgumentException
718 718
      * @throws InvalidInterfaceException
@@ -1066,7 +1066,7 @@  discard block
 block discarded – undo
1066 1066
      *
1067 1067
      * @param null  $field_to_order_by  What field is being used as the reference point.
1068 1068
      * @param array $query_params       Any additional conditions on the query.
1069
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1069
+     * @param string  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1070 1070
      *                                  you can indicate just the columns you want returned
1071 1071
      * @return array|EE_Base_Class
1072 1072
      * @throws ReflectionException
@@ -1095,7 +1095,7 @@  discard block
 block discarded – undo
1095 1095
      *
1096 1096
      * @param null  $field_to_order_by  What field is being used as the reference point.
1097 1097
      * @param array $query_params       Any additional conditions on the query.
1098
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1098
+     * @param string  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1099 1099
      *                                  you can indicate just the column you want returned
1100 1100
      * @return array|EE_Base_Class
1101 1101
      * @throws ReflectionException
@@ -1178,7 +1178,7 @@  discard block
 block discarded – undo
1178 1178
      * This method simply returns the RAW unprocessed value for the given property in this class
1179 1179
      *
1180 1180
      * @param  string $field_name A valid fieldname
1181
-     * @return mixed              Whatever the raw value stored on the property is.
1181
+     * @return integer|null              Whatever the raw value stored on the property is.
1182 1182
      * @throws ReflectionException
1183 1183
      * @throws InvalidArgumentException
1184 1184
      * @throws InvalidInterfaceException
@@ -1526,7 +1526,7 @@  discard block
 block discarded – undo
1526 1526
      * sets the time on a datetime property
1527 1527
      *
1528 1528
      * @access protected
1529
-     * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1529
+     * @param string $time      a valid time string for php datetime functions (or DateTime object)
1530 1530
      * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1531 1531
      * @throws ReflectionException
1532 1532
      * @throws InvalidArgumentException
@@ -1544,7 +1544,7 @@  discard block
 block discarded – undo
1544 1544
      * sets the date on a datetime property
1545 1545
      *
1546 1546
      * @access protected
1547
-     * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1547
+     * @param string $date      a valid date string for php datetime functions ( or DateTime object)
1548 1548
      * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1549 1549
      * @throws ReflectionException
1550 1550
      * @throws InvalidArgumentException
@@ -2066,7 +2066,7 @@  discard block
 block discarded – undo
2066 2066
      *
2067 2067
      * @param  array  $props_n_values   incoming array of properties and their values
2068 2068
      * @param  string $classname        the classname of the child class
2069
-     * @param null    $timezone
2069
+     * @param string|null    $timezone
2070 2070
      * @param array   $date_formats     incoming date_formats in an array where the first value is the
2071 2071
      *                                  date_format and the second value is the time format
2072 2072
      * @return mixed (EE_Base_Class|bool)
@@ -2153,7 +2153,7 @@  discard block
 block discarded – undo
2153 2153
      * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2154 2154
      *
2155 2155
      * @param string $model_classname
2156
-     * @param null   $timezone
2156
+     * @param string|null   $timezone
2157 2157
      * @return EEM_Base
2158 2158
      * @throws ReflectionException
2159 2159
      * @throws InvalidArgumentException
Please login to merge, or discard this patch.
core/libraries/form_sections/form_handlers/FormHandler.php 1 patch
Indentation   +643 added lines, -643 removed lines patch added patch discarded remove patch
@@ -15,7 +15,7 @@  discard block
 block discarded – undo
15 15
 use EventEspresso\core\exceptions\InvalidFormSubmissionException;
16 16
 
17 17
 if (! defined('EVENT_ESPRESSO_VERSION')) {
18
-    exit('No direct script access allowed');
18
+	exit('No direct script access allowed');
19 19
 }
20 20
 
21 21
 
@@ -34,648 +34,648 @@  discard block
 block discarded – undo
34 34
 abstract class FormHandler implements FormHandlerInterface
35 35
 {
36 36
 
37
-    /**
38
-     * will add opening and closing HTML form tags as well as a submit button
39
-     */
40
-    const ADD_FORM_TAGS_AND_SUBMIT = 'add_form_tags_and_submit';
41
-
42
-    /**
43
-     * will add opening and closing HTML form tags but NOT a submit button
44
-     */
45
-    const ADD_FORM_TAGS_ONLY = 'add_form_tags_only';
46
-
47
-    /**
48
-     * will NOT add opening and closing HTML form tags but will add a submit button
49
-     */
50
-    const ADD_FORM_SUBMIT_ONLY = 'add_form_submit_only';
51
-
52
-    /**
53
-     * will NOT add opening and closing HTML form tags NOR a submit button
54
-     */
55
-    const DO_NOT_SETUP_FORM = 'do_not_setup_form';
56
-
57
-    /**
58
-     * if set to false, then this form has no displayable content,
59
-     * and will only be used for processing data sent passed via GET or POST
60
-     * defaults to true ( ie: form has displayable content )
61
-     *
62
-     * @var boolean $displayable
63
-     */
64
-    private $displayable = true;
65
-
66
-    /**
67
-     * @var string $form_name
68
-     */
69
-    private $form_name;
70
-
71
-    /**
72
-     * @var string $admin_name
73
-     */
74
-    private $admin_name;
75
-
76
-    /**
77
-     * @var string $slug
78
-     */
79
-    private $slug;
80
-
81
-    /**
82
-     * @var string $submit_btn_text
83
-     */
84
-    private $submit_btn_text;
85
-
86
-    /**
87
-     * @var string $form_action
88
-     */
89
-    private $form_action;
90
-
91
-    /**
92
-     * form params in key value pairs
93
-     * can be added to form action URL or as hidden inputs
94
-     *
95
-     * @var array $form_args
96
-     */
97
-    private $form_args = array();
98
-
99
-    /**
100
-     * value of one of the string constant above
101
-     *
102
-     * @var string $form_config
103
-     */
104
-    private $form_config;
105
-
106
-    /**
107
-     * whether or not the form was determined to be invalid
108
-     *
109
-     * @var boolean $form_has_errors
110
-     */
111
-    private $form_has_errors;
112
-
113
-    /**
114
-     * the absolute top level form section being used on the page
115
-     *
116
-     * @var EE_Form_Section_Proper $form
117
-     */
118
-    private $form;
119
-
120
-    /**
121
-     * @var EE_Registry $registry
122
-     */
123
-    protected $registry;
124
-
125
-
126
-
127
-    /**
128
-     * Form constructor.
129
-     *
130
-     * @param string      $form_name
131
-     * @param string      $admin_name
132
-     * @param string      $slug
133
-     * @param string      $form_action
134
-     * @param string      $form_config
135
-     * @param EE_Registry $registry
136
-     * @throws InvalidDataTypeException
137
-     * @throws DomainException
138
-     * @throws InvalidArgumentException
139
-     */
140
-    public function __construct(
141
-        $form_name,
142
-        $admin_name,
143
-        $slug,
144
-        $form_action = '',
145
-        $form_config = FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
146
-        EE_Registry $registry
147
-    ) {
148
-        $this->setFormName($form_name);
149
-        $this->setAdminName($admin_name);
150
-        $this->setSlug($slug);
151
-        $this->setFormAction($form_action);
152
-        $this->setFormConfig($form_config);
153
-        $this->setSubmitBtnText(esc_html__('Submit', 'event_espresso'));
154
-        $this->registry = $registry;
155
-    }
156
-
157
-
158
-
159
-    /**
160
-     * @return array
161
-     */
162
-    public static function getFormConfigConstants()
163
-    {
164
-        return array(
165
-            FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
166
-            FormHandler::ADD_FORM_TAGS_ONLY,
167
-            FormHandler::ADD_FORM_SUBMIT_ONLY,
168
-            FormHandler::DO_NOT_SETUP_FORM,
169
-        );
170
-    }
171
-
172
-
173
-
174
-    /**
175
-     * @param bool $for_display
176
-     * @return EE_Form_Section_Proper
177
-     * @throws EE_Error
178
-     * @throws LogicException
179
-     */
180
-    public function form($for_display = false)
181
-    {
182
-        if (! $this->formIsValid()) {
183
-            return null;
184
-        }
185
-        if ($for_display) {
186
-            $form_config = $this->formConfig();
187
-            if (
188
-                $form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
189
-                || $form_config === FormHandler::ADD_FORM_SUBMIT_ONLY
190
-            ) {
191
-                $this->appendSubmitButton();
192
-                $this->clearFormButtonFloats();
193
-            }
194
-        }
195
-        return $this->form;
196
-    }
197
-
198
-
199
-
200
-    /**
201
-     * @return boolean
202
-     * @throws LogicException
203
-     */
204
-    public function formIsValid()
205
-    {
206
-        if ($this->form instanceof EE_Form_Section_Proper) {
207
-            return true;
208
-        }
209
-        $form = apply_filters(
210
-            'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__formIsValid__generated_form_object',
211
-            $this->generate(),
212
-            $this
213
-        );
214
-        if ($this->verifyForm($form)) {
215
-            $this->setForm($form);
216
-        }
217
-        return true;
218
-    }
219
-
220
-
221
-
222
-    /**
223
-     * @param EE_Form_Section_Proper|null $form
224
-     * @return bool
225
-     * @throws LogicException
226
-     */
227
-    public function verifyForm(EE_Form_Section_Proper $form = null)
228
-    {
229
-        $form = $form !== null ? $form : $this->form;
230
-        if ($form instanceof EE_Form_Section_Proper) {
231
-            return true;
232
-        }
233
-        throw new LogicException(
234
-            sprintf(
235
-                esc_html__('The "%1$s" form is invalid or missing. %2$s', 'event_espresso'),
236
-                $this->form_name,
237
-                var_export($form, true)
238
-            )
239
-        );
240
-    }
241
-
242
-
243
-
244
-    /**
245
-     * @param EE_Form_Section_Proper $form
246
-     */
247
-    public function setForm(EE_Form_Section_Proper $form)
248
-    {
249
-        $this->form = $form;
250
-    }
251
-
252
-
253
-
254
-    /**
255
-     * @return boolean
256
-     */
257
-    public function displayable()
258
-    {
259
-        return $this->displayable;
260
-    }
261
-
262
-
263
-
264
-    /**
265
-     * @param boolean $displayable
266
-     */
267
-    public function setDisplayable($displayable = false)
268
-    {
269
-        $this->displayable = filter_var($displayable, FILTER_VALIDATE_BOOLEAN);
270
-    }
271
-
272
-
273
-
274
-    /**
275
-     * a public name for the form that can be displayed on the frontend of a site
276
-     *
277
-     * @return string
278
-     */
279
-    public function formName()
280
-    {
281
-        return $this->form_name;
282
-    }
283
-
284
-
285
-
286
-    /**
287
-     * @param string $form_name
288
-     * @throws InvalidDataTypeException
289
-     */
290
-    public function setFormName($form_name)
291
-    {
292
-        if (! is_string($form_name)) {
293
-            throw new InvalidDataTypeException('$form_name', $form_name, 'string');
294
-        }
295
-        $this->form_name = $form_name;
296
-    }
297
-
298
-
299
-
300
-    /**
301
-     * a public name for the form that can be displayed, but only in the admin
302
-     *
303
-     * @return string
304
-     */
305
-    public function adminName()
306
-    {
307
-        return $this->admin_name;
308
-    }
309
-
310
-
311
-
312
-    /**
313
-     * @param string $admin_name
314
-     * @throws InvalidDataTypeException
315
-     */
316
-    public function setAdminName($admin_name)
317
-    {
318
-        if (! is_string($admin_name)) {
319
-            throw new InvalidDataTypeException('$admin_name', $admin_name, 'string');
320
-        }
321
-        $this->admin_name = $admin_name;
322
-    }
323
-
324
-
325
-
326
-    /**
327
-     * a URL friendly string that can be used for identifying the form
328
-     *
329
-     * @return string
330
-     */
331
-    public function slug()
332
-    {
333
-        return $this->slug;
334
-    }
335
-
336
-
337
-
338
-    /**
339
-     * @param string $slug
340
-     * @throws InvalidDataTypeException
341
-     */
342
-    public function setSlug($slug)
343
-    {
344
-        if (! is_string($slug)) {
345
-            throw new InvalidDataTypeException('$slug', $slug, 'string');
346
-        }
347
-        $this->slug = $slug;
348
-    }
349
-
350
-
351
-
352
-    /**
353
-     * @return string
354
-     */
355
-    public function submitBtnText()
356
-    {
357
-        return $this->submit_btn_text;
358
-    }
359
-
360
-
361
-
362
-    /**
363
-     * @param string $submit_btn_text
364
-     * @throws InvalidDataTypeException
365
-     * @throws InvalidArgumentException
366
-     */
367
-    public function setSubmitBtnText($submit_btn_text)
368
-    {
369
-        if (! is_string($submit_btn_text)) {
370
-            throw new InvalidDataTypeException('$submit_btn_text', $submit_btn_text, 'string');
371
-        }
372
-        if (empty($submit_btn_text)) {
373
-            throw new InvalidArgumentException(
374
-                esc_html__('Can not set Submit button text because an empty string was provided.', 'event_espresso')
375
-            );
376
-        }
377
-        $this->submit_btn_text = $submit_btn_text;
378
-    }
379
-
380
-
381
-
382
-    /**
383
-     * @return string
384
-     */
385
-    public function formAction()
386
-    {
387
-        return ! empty($this->form_args)
388
-            ? add_query_arg($this->form_args, $this->form_action)
389
-            : $this->form_action;
390
-    }
391
-
392
-
393
-
394
-    /**
395
-     * @param string $form_action
396
-     * @throws InvalidDataTypeException
397
-     */
398
-    public function setFormAction($form_action)
399
-    {
400
-        if (! is_string($form_action)) {
401
-            throw new InvalidDataTypeException('$form_action', $form_action, 'string');
402
-        }
403
-        $this->form_action = $form_action;
404
-    }
405
-
406
-
407
-
408
-    /**
409
-     * @param array $form_args
410
-     * @throws InvalidDataTypeException
411
-     * @throws InvalidArgumentException
412
-     */
413
-    public function addFormActionArgs($form_args = array())
414
-    {
415
-        if (is_object($form_args)) {
416
-            throw new InvalidDataTypeException(
417
-                '$form_args',
418
-                $form_args,
419
-                'anything other than an object was expected.'
420
-            );
421
-        }
422
-        if (empty($form_args)) {
423
-            throw new InvalidArgumentException(
424
-                esc_html__('The redirect arguments can not be an empty array.', 'event_espresso')
425
-            );
426
-        }
427
-        $this->form_args = array_merge($this->form_args, $form_args);
428
-    }
429
-
430
-
431
-
432
-    /**
433
-     * @return string
434
-     */
435
-    public function formConfig()
436
-    {
437
-        return $this->form_config;
438
-    }
439
-
440
-
441
-
442
-    /**
443
-     * @param string $form_config
444
-     * @throws DomainException
445
-     */
446
-    public function setFormConfig($form_config)
447
-    {
448
-        if (
449
-        ! in_array(
450
-            $form_config,
451
-            array(
452
-                FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
453
-                FormHandler::ADD_FORM_TAGS_ONLY,
454
-                FormHandler::ADD_FORM_SUBMIT_ONLY,
455
-                FormHandler::DO_NOT_SETUP_FORM,
456
-            ),
457
-            true
458
-        )
459
-        ) {
460
-            throw new DomainException(
461
-                sprintf(
462
-                    esc_html__('"%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',
463
-                        'event_espresso'),
464
-                    $form_config
465
-                )
466
-            );
467
-        }
468
-        $this->form_config = $form_config;
469
-    }
470
-
471
-
472
-
473
-    /**
474
-     * called after the form is instantiated
475
-     * and used for performing any logic that needs to occur early
476
-     * before any of the other methods are called.
477
-     * returns true if everything is ok to proceed,
478
-     * and false if no further form logic should be implemented
479
-     *
480
-     * @return boolean
481
-     */
482
-    public function initialize()
483
-    {
484
-        $this->form_has_errors = EE_Error::has_error(true);
485
-        return true;
486
-    }
487
-
488
-
489
-
490
-    /**
491
-     * used for setting up css and js
492
-     *
493
-     * @return void
494
-     * @throws LogicException
495
-     * @throws EE_Error
496
-     */
497
-    public function enqueueStylesAndScripts()
498
-    {
499
-        $this->form()->enqueue_js();
500
-    }
501
-
502
-
503
-
504
-    /**
505
-     * creates and returns the actual form
506
-     *
507
-     * @return EE_Form_Section_Proper
508
-     */
509
-    abstract public function generate();
510
-
511
-
512
-
513
-    /**
514
-     * creates and returns an EE_Submit_Input labeled "Submit"
515
-     *
516
-     * @param string $text
517
-     * @return EE_Submit_Input
518
-     */
519
-    public function generateSubmitButton($text = '')
520
-    {
521
-        $text = ! empty($text) ? $text : $this->submitBtnText();
522
-        return new EE_Submit_Input(
523
-            array(
524
-                'html_name'             => 'ee-form-submit-' . $this->slug(),
525
-                'html_id'               => 'ee-form-submit-' . $this->slug(),
526
-                'html_class'            => 'ee-form-submit',
527
-                'html_label'            => '&nbsp;',
528
-                'other_html_attributes' => ' rel="' . $this->slug() . '"',
529
-                'default'               => $text,
530
-            )
531
-        );
532
-    }
533
-
534
-
535
-
536
-    /**
537
-     * calls generateSubmitButton() and appends it onto the form along with a float clearing div
538
-     *
539
-     * @param string $text
540
-     * @return void
541
-     * @throws EE_Error
542
-     */
543
-    public function appendSubmitButton($text = '')
544
-    {
545
-        if ($this->form->subsection_exists($this->slug() . '-submit-btn')) {
546
-            return;
547
-        }
548
-        $this->form->add_subsections(
549
-            array($this->slug() . '-submit-btn' => $this->generateSubmitButton($text)),
550
-            null,
551
-            false
552
-        );
553
-    }
554
-
555
-
556
-
557
-    /**
558
-     * creates and returns an EE_Submit_Input labeled "Cancel"
559
-     *
560
-     * @param string $text
561
-     * @return EE_Submit_Input
562
-     */
563
-    public function generateCancelButton($text = '')
564
-    {
565
-        $cancel_button = new EE_Submit_Input(
566
-            array(
567
-                'html_name'             => 'ee-form-submit-' . $this->slug(), // YES! Same name as submit !!!
568
-                'html_id'               => 'ee-cancel-form-' . $this->slug(),
569
-                'html_class'            => 'ee-cancel-form',
570
-                'html_label'            => '&nbsp;',
571
-                'other_html_attributes' => ' rel="' . $this->slug() . '"',
572
-                'default'               => ! empty($text) ? $text : esc_html__('Cancel', 'event_espresso'),
573
-            )
574
-        );
575
-        $cancel_button->set_button_css_attributes(false);
576
-        return $cancel_button;
577
-    }
578
-
579
-
580
-
581
-    /**
582
-     * appends a float clearing div onto end of form
583
-     *
584
-     * @return void
585
-     * @throws EE_Error
586
-     */
587
-    public function clearFormButtonFloats()
588
-    {
589
-        $this->form->add_subsections(
590
-            array(
591
-                'clear-submit-btn-float' => new EE_Form_Section_HTML(
592
-                    EEH_HTML::div('', '', 'clear-float') . EEH_HTML::divx()
593
-                ),
594
-            ),
595
-            null,
596
-            false
597
-        );
598
-    }
599
-
600
-
601
-    /**
602
-     * takes the generated form and displays it along with ony other non-form HTML that may be required
603
-     * returns a string of HTML that can be directly echoed in a template
604
-     *
605
-     * @return string
606
-     * @throws \InvalidArgumentException
607
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
608
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
609
-     * @throws LogicException
610
-     * @throws EE_Error
611
-     */
612
-    public function display()
613
-    {
614
-        $form_html = apply_filters(
615
-            'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__display__before_form',
616
-            ''
617
-        );
618
-        $form_config = $this->formConfig();
619
-        if (
620
-            $form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
621
-            || $form_config === FormHandler::ADD_FORM_TAGS_ONLY
622
-        ) {
623
-            $form_html .= $this->form()->form_open($this->formAction());
624
-        }
625
-        $form_html .= $this->form(true)->get_html($this->form_has_errors);
626
-        if (
627
-            $form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
628
-            || $form_config === FormHandler::ADD_FORM_TAGS_ONLY
629
-        ) {
630
-            $form_html .= $this->form()->form_close();
631
-        }
632
-        $form_html .= apply_filters(
633
-            'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__display__after_form',
634
-            ''
635
-        );
636
-        return $form_html;
637
-    }
638
-
639
-
640
-    /**
641
-     * handles processing the form submission
642
-     * returns true or false depending on whether the form was processed successfully or not
643
-     *
644
-     * @param array $submitted_form_data
645
-     * @return array
646
-     * @throws \InvalidArgumentException
647
-     * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
648
-     * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
649
-     * @throws EE_Error
650
-     * @throws LogicException
651
-     * @throws InvalidFormSubmissionException
652
-     */
653
-    public function process($submitted_form_data = array())
654
-    {
655
-        if (! $this->form()->was_submitted($submitted_form_data)) {
656
-            throw new InvalidFormSubmissionException($this->form_name);
657
-        }
658
-        $this->form(true)->receive_form_submission($submitted_form_data);
659
-        if (! $this->form()->is_valid()) {
660
-            throw new InvalidFormSubmissionException(
661
-                $this->form_name,
662
-                sprintf(
663
-                    esc_html__(
664
-                        'The "%1$s" form is invalid. Please correct the following errors and resubmit: %2$s %3$s',
665
-                        'event_espresso'
666
-                    ),
667
-                    $this->form_name,
668
-                    '<br />',
669
-                    implode('<br />', $this->form()->get_validation_errors_accumulated())
670
-                )
671
-            );
672
-        }
673
-        return apply_filters(
674
-            'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__process__valid_data',
675
-            $this->form()->valid_data(),
676
-            $this
677
-        );
678
-    }
37
+	/**
38
+	 * will add opening and closing HTML form tags as well as a submit button
39
+	 */
40
+	const ADD_FORM_TAGS_AND_SUBMIT = 'add_form_tags_and_submit';
41
+
42
+	/**
43
+	 * will add opening and closing HTML form tags but NOT a submit button
44
+	 */
45
+	const ADD_FORM_TAGS_ONLY = 'add_form_tags_only';
46
+
47
+	/**
48
+	 * will NOT add opening and closing HTML form tags but will add a submit button
49
+	 */
50
+	const ADD_FORM_SUBMIT_ONLY = 'add_form_submit_only';
51
+
52
+	/**
53
+	 * will NOT add opening and closing HTML form tags NOR a submit button
54
+	 */
55
+	const DO_NOT_SETUP_FORM = 'do_not_setup_form';
56
+
57
+	/**
58
+	 * if set to false, then this form has no displayable content,
59
+	 * and will only be used for processing data sent passed via GET or POST
60
+	 * defaults to true ( ie: form has displayable content )
61
+	 *
62
+	 * @var boolean $displayable
63
+	 */
64
+	private $displayable = true;
65
+
66
+	/**
67
+	 * @var string $form_name
68
+	 */
69
+	private $form_name;
70
+
71
+	/**
72
+	 * @var string $admin_name
73
+	 */
74
+	private $admin_name;
75
+
76
+	/**
77
+	 * @var string $slug
78
+	 */
79
+	private $slug;
80
+
81
+	/**
82
+	 * @var string $submit_btn_text
83
+	 */
84
+	private $submit_btn_text;
85
+
86
+	/**
87
+	 * @var string $form_action
88
+	 */
89
+	private $form_action;
90
+
91
+	/**
92
+	 * form params in key value pairs
93
+	 * can be added to form action URL or as hidden inputs
94
+	 *
95
+	 * @var array $form_args
96
+	 */
97
+	private $form_args = array();
98
+
99
+	/**
100
+	 * value of one of the string constant above
101
+	 *
102
+	 * @var string $form_config
103
+	 */
104
+	private $form_config;
105
+
106
+	/**
107
+	 * whether or not the form was determined to be invalid
108
+	 *
109
+	 * @var boolean $form_has_errors
110
+	 */
111
+	private $form_has_errors;
112
+
113
+	/**
114
+	 * the absolute top level form section being used on the page
115
+	 *
116
+	 * @var EE_Form_Section_Proper $form
117
+	 */
118
+	private $form;
119
+
120
+	/**
121
+	 * @var EE_Registry $registry
122
+	 */
123
+	protected $registry;
124
+
125
+
126
+
127
+	/**
128
+	 * Form constructor.
129
+	 *
130
+	 * @param string      $form_name
131
+	 * @param string      $admin_name
132
+	 * @param string      $slug
133
+	 * @param string      $form_action
134
+	 * @param string      $form_config
135
+	 * @param EE_Registry $registry
136
+	 * @throws InvalidDataTypeException
137
+	 * @throws DomainException
138
+	 * @throws InvalidArgumentException
139
+	 */
140
+	public function __construct(
141
+		$form_name,
142
+		$admin_name,
143
+		$slug,
144
+		$form_action = '',
145
+		$form_config = FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
146
+		EE_Registry $registry
147
+	) {
148
+		$this->setFormName($form_name);
149
+		$this->setAdminName($admin_name);
150
+		$this->setSlug($slug);
151
+		$this->setFormAction($form_action);
152
+		$this->setFormConfig($form_config);
153
+		$this->setSubmitBtnText(esc_html__('Submit', 'event_espresso'));
154
+		$this->registry = $registry;
155
+	}
156
+
157
+
158
+
159
+	/**
160
+	 * @return array
161
+	 */
162
+	public static function getFormConfigConstants()
163
+	{
164
+		return array(
165
+			FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
166
+			FormHandler::ADD_FORM_TAGS_ONLY,
167
+			FormHandler::ADD_FORM_SUBMIT_ONLY,
168
+			FormHandler::DO_NOT_SETUP_FORM,
169
+		);
170
+	}
171
+
172
+
173
+
174
+	/**
175
+	 * @param bool $for_display
176
+	 * @return EE_Form_Section_Proper
177
+	 * @throws EE_Error
178
+	 * @throws LogicException
179
+	 */
180
+	public function form($for_display = false)
181
+	{
182
+		if (! $this->formIsValid()) {
183
+			return null;
184
+		}
185
+		if ($for_display) {
186
+			$form_config = $this->formConfig();
187
+			if (
188
+				$form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
189
+				|| $form_config === FormHandler::ADD_FORM_SUBMIT_ONLY
190
+			) {
191
+				$this->appendSubmitButton();
192
+				$this->clearFormButtonFloats();
193
+			}
194
+		}
195
+		return $this->form;
196
+	}
197
+
198
+
199
+
200
+	/**
201
+	 * @return boolean
202
+	 * @throws LogicException
203
+	 */
204
+	public function formIsValid()
205
+	{
206
+		if ($this->form instanceof EE_Form_Section_Proper) {
207
+			return true;
208
+		}
209
+		$form = apply_filters(
210
+			'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__formIsValid__generated_form_object',
211
+			$this->generate(),
212
+			$this
213
+		);
214
+		if ($this->verifyForm($form)) {
215
+			$this->setForm($form);
216
+		}
217
+		return true;
218
+	}
219
+
220
+
221
+
222
+	/**
223
+	 * @param EE_Form_Section_Proper|null $form
224
+	 * @return bool
225
+	 * @throws LogicException
226
+	 */
227
+	public function verifyForm(EE_Form_Section_Proper $form = null)
228
+	{
229
+		$form = $form !== null ? $form : $this->form;
230
+		if ($form instanceof EE_Form_Section_Proper) {
231
+			return true;
232
+		}
233
+		throw new LogicException(
234
+			sprintf(
235
+				esc_html__('The "%1$s" form is invalid or missing. %2$s', 'event_espresso'),
236
+				$this->form_name,
237
+				var_export($form, true)
238
+			)
239
+		);
240
+	}
241
+
242
+
243
+
244
+	/**
245
+	 * @param EE_Form_Section_Proper $form
246
+	 */
247
+	public function setForm(EE_Form_Section_Proper $form)
248
+	{
249
+		$this->form = $form;
250
+	}
251
+
252
+
253
+
254
+	/**
255
+	 * @return boolean
256
+	 */
257
+	public function displayable()
258
+	{
259
+		return $this->displayable;
260
+	}
261
+
262
+
263
+
264
+	/**
265
+	 * @param boolean $displayable
266
+	 */
267
+	public function setDisplayable($displayable = false)
268
+	{
269
+		$this->displayable = filter_var($displayable, FILTER_VALIDATE_BOOLEAN);
270
+	}
271
+
272
+
273
+
274
+	/**
275
+	 * a public name for the form that can be displayed on the frontend of a site
276
+	 *
277
+	 * @return string
278
+	 */
279
+	public function formName()
280
+	{
281
+		return $this->form_name;
282
+	}
283
+
284
+
285
+
286
+	/**
287
+	 * @param string $form_name
288
+	 * @throws InvalidDataTypeException
289
+	 */
290
+	public function setFormName($form_name)
291
+	{
292
+		if (! is_string($form_name)) {
293
+			throw new InvalidDataTypeException('$form_name', $form_name, 'string');
294
+		}
295
+		$this->form_name = $form_name;
296
+	}
297
+
298
+
299
+
300
+	/**
301
+	 * a public name for the form that can be displayed, but only in the admin
302
+	 *
303
+	 * @return string
304
+	 */
305
+	public function adminName()
306
+	{
307
+		return $this->admin_name;
308
+	}
309
+
310
+
311
+
312
+	/**
313
+	 * @param string $admin_name
314
+	 * @throws InvalidDataTypeException
315
+	 */
316
+	public function setAdminName($admin_name)
317
+	{
318
+		if (! is_string($admin_name)) {
319
+			throw new InvalidDataTypeException('$admin_name', $admin_name, 'string');
320
+		}
321
+		$this->admin_name = $admin_name;
322
+	}
323
+
324
+
325
+
326
+	/**
327
+	 * a URL friendly string that can be used for identifying the form
328
+	 *
329
+	 * @return string
330
+	 */
331
+	public function slug()
332
+	{
333
+		return $this->slug;
334
+	}
335
+
336
+
337
+
338
+	/**
339
+	 * @param string $slug
340
+	 * @throws InvalidDataTypeException
341
+	 */
342
+	public function setSlug($slug)
343
+	{
344
+		if (! is_string($slug)) {
345
+			throw new InvalidDataTypeException('$slug', $slug, 'string');
346
+		}
347
+		$this->slug = $slug;
348
+	}
349
+
350
+
351
+
352
+	/**
353
+	 * @return string
354
+	 */
355
+	public function submitBtnText()
356
+	{
357
+		return $this->submit_btn_text;
358
+	}
359
+
360
+
361
+
362
+	/**
363
+	 * @param string $submit_btn_text
364
+	 * @throws InvalidDataTypeException
365
+	 * @throws InvalidArgumentException
366
+	 */
367
+	public function setSubmitBtnText($submit_btn_text)
368
+	{
369
+		if (! is_string($submit_btn_text)) {
370
+			throw new InvalidDataTypeException('$submit_btn_text', $submit_btn_text, 'string');
371
+		}
372
+		if (empty($submit_btn_text)) {
373
+			throw new InvalidArgumentException(
374
+				esc_html__('Can not set Submit button text because an empty string was provided.', 'event_espresso')
375
+			);
376
+		}
377
+		$this->submit_btn_text = $submit_btn_text;
378
+	}
379
+
380
+
381
+
382
+	/**
383
+	 * @return string
384
+	 */
385
+	public function formAction()
386
+	{
387
+		return ! empty($this->form_args)
388
+			? add_query_arg($this->form_args, $this->form_action)
389
+			: $this->form_action;
390
+	}
391
+
392
+
393
+
394
+	/**
395
+	 * @param string $form_action
396
+	 * @throws InvalidDataTypeException
397
+	 */
398
+	public function setFormAction($form_action)
399
+	{
400
+		if (! is_string($form_action)) {
401
+			throw new InvalidDataTypeException('$form_action', $form_action, 'string');
402
+		}
403
+		$this->form_action = $form_action;
404
+	}
405
+
406
+
407
+
408
+	/**
409
+	 * @param array $form_args
410
+	 * @throws InvalidDataTypeException
411
+	 * @throws InvalidArgumentException
412
+	 */
413
+	public function addFormActionArgs($form_args = array())
414
+	{
415
+		if (is_object($form_args)) {
416
+			throw new InvalidDataTypeException(
417
+				'$form_args',
418
+				$form_args,
419
+				'anything other than an object was expected.'
420
+			);
421
+		}
422
+		if (empty($form_args)) {
423
+			throw new InvalidArgumentException(
424
+				esc_html__('The redirect arguments can not be an empty array.', 'event_espresso')
425
+			);
426
+		}
427
+		$this->form_args = array_merge($this->form_args, $form_args);
428
+	}
429
+
430
+
431
+
432
+	/**
433
+	 * @return string
434
+	 */
435
+	public function formConfig()
436
+	{
437
+		return $this->form_config;
438
+	}
439
+
440
+
441
+
442
+	/**
443
+	 * @param string $form_config
444
+	 * @throws DomainException
445
+	 */
446
+	public function setFormConfig($form_config)
447
+	{
448
+		if (
449
+		! in_array(
450
+			$form_config,
451
+			array(
452
+				FormHandler::ADD_FORM_TAGS_AND_SUBMIT,
453
+				FormHandler::ADD_FORM_TAGS_ONLY,
454
+				FormHandler::ADD_FORM_SUBMIT_ONLY,
455
+				FormHandler::DO_NOT_SETUP_FORM,
456
+			),
457
+			true
458
+		)
459
+		) {
460
+			throw new DomainException(
461
+				sprintf(
462
+					esc_html__('"%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',
463
+						'event_espresso'),
464
+					$form_config
465
+				)
466
+			);
467
+		}
468
+		$this->form_config = $form_config;
469
+	}
470
+
471
+
472
+
473
+	/**
474
+	 * called after the form is instantiated
475
+	 * and used for performing any logic that needs to occur early
476
+	 * before any of the other methods are called.
477
+	 * returns true if everything is ok to proceed,
478
+	 * and false if no further form logic should be implemented
479
+	 *
480
+	 * @return boolean
481
+	 */
482
+	public function initialize()
483
+	{
484
+		$this->form_has_errors = EE_Error::has_error(true);
485
+		return true;
486
+	}
487
+
488
+
489
+
490
+	/**
491
+	 * used for setting up css and js
492
+	 *
493
+	 * @return void
494
+	 * @throws LogicException
495
+	 * @throws EE_Error
496
+	 */
497
+	public function enqueueStylesAndScripts()
498
+	{
499
+		$this->form()->enqueue_js();
500
+	}
501
+
502
+
503
+
504
+	/**
505
+	 * creates and returns the actual form
506
+	 *
507
+	 * @return EE_Form_Section_Proper
508
+	 */
509
+	abstract public function generate();
510
+
511
+
512
+
513
+	/**
514
+	 * creates and returns an EE_Submit_Input labeled "Submit"
515
+	 *
516
+	 * @param string $text
517
+	 * @return EE_Submit_Input
518
+	 */
519
+	public function generateSubmitButton($text = '')
520
+	{
521
+		$text = ! empty($text) ? $text : $this->submitBtnText();
522
+		return new EE_Submit_Input(
523
+			array(
524
+				'html_name'             => 'ee-form-submit-' . $this->slug(),
525
+				'html_id'               => 'ee-form-submit-' . $this->slug(),
526
+				'html_class'            => 'ee-form-submit',
527
+				'html_label'            => '&nbsp;',
528
+				'other_html_attributes' => ' rel="' . $this->slug() . '"',
529
+				'default'               => $text,
530
+			)
531
+		);
532
+	}
533
+
534
+
535
+
536
+	/**
537
+	 * calls generateSubmitButton() and appends it onto the form along with a float clearing div
538
+	 *
539
+	 * @param string $text
540
+	 * @return void
541
+	 * @throws EE_Error
542
+	 */
543
+	public function appendSubmitButton($text = '')
544
+	{
545
+		if ($this->form->subsection_exists($this->slug() . '-submit-btn')) {
546
+			return;
547
+		}
548
+		$this->form->add_subsections(
549
+			array($this->slug() . '-submit-btn' => $this->generateSubmitButton($text)),
550
+			null,
551
+			false
552
+		);
553
+	}
554
+
555
+
556
+
557
+	/**
558
+	 * creates and returns an EE_Submit_Input labeled "Cancel"
559
+	 *
560
+	 * @param string $text
561
+	 * @return EE_Submit_Input
562
+	 */
563
+	public function generateCancelButton($text = '')
564
+	{
565
+		$cancel_button = new EE_Submit_Input(
566
+			array(
567
+				'html_name'             => 'ee-form-submit-' . $this->slug(), // YES! Same name as submit !!!
568
+				'html_id'               => 'ee-cancel-form-' . $this->slug(),
569
+				'html_class'            => 'ee-cancel-form',
570
+				'html_label'            => '&nbsp;',
571
+				'other_html_attributes' => ' rel="' . $this->slug() . '"',
572
+				'default'               => ! empty($text) ? $text : esc_html__('Cancel', 'event_espresso'),
573
+			)
574
+		);
575
+		$cancel_button->set_button_css_attributes(false);
576
+		return $cancel_button;
577
+	}
578
+
579
+
580
+
581
+	/**
582
+	 * appends a float clearing div onto end of form
583
+	 *
584
+	 * @return void
585
+	 * @throws EE_Error
586
+	 */
587
+	public function clearFormButtonFloats()
588
+	{
589
+		$this->form->add_subsections(
590
+			array(
591
+				'clear-submit-btn-float' => new EE_Form_Section_HTML(
592
+					EEH_HTML::div('', '', 'clear-float') . EEH_HTML::divx()
593
+				),
594
+			),
595
+			null,
596
+			false
597
+		);
598
+	}
599
+
600
+
601
+	/**
602
+	 * takes the generated form and displays it along with ony other non-form HTML that may be required
603
+	 * returns a string of HTML that can be directly echoed in a template
604
+	 *
605
+	 * @return string
606
+	 * @throws \InvalidArgumentException
607
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
608
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
609
+	 * @throws LogicException
610
+	 * @throws EE_Error
611
+	 */
612
+	public function display()
613
+	{
614
+		$form_html = apply_filters(
615
+			'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__display__before_form',
616
+			''
617
+		);
618
+		$form_config = $this->formConfig();
619
+		if (
620
+			$form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
621
+			|| $form_config === FormHandler::ADD_FORM_TAGS_ONLY
622
+		) {
623
+			$form_html .= $this->form()->form_open($this->formAction());
624
+		}
625
+		$form_html .= $this->form(true)->get_html($this->form_has_errors);
626
+		if (
627
+			$form_config === FormHandler::ADD_FORM_TAGS_AND_SUBMIT
628
+			|| $form_config === FormHandler::ADD_FORM_TAGS_ONLY
629
+		) {
630
+			$form_html .= $this->form()->form_close();
631
+		}
632
+		$form_html .= apply_filters(
633
+			'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__display__after_form',
634
+			''
635
+		);
636
+		return $form_html;
637
+	}
638
+
639
+
640
+	/**
641
+	 * handles processing the form submission
642
+	 * returns true or false depending on whether the form was processed successfully or not
643
+	 *
644
+	 * @param array $submitted_form_data
645
+	 * @return array
646
+	 * @throws \InvalidArgumentException
647
+	 * @throws \EventEspresso\core\exceptions\InvalidInterfaceException
648
+	 * @throws \EventEspresso\core\exceptions\InvalidDataTypeException
649
+	 * @throws EE_Error
650
+	 * @throws LogicException
651
+	 * @throws InvalidFormSubmissionException
652
+	 */
653
+	public function process($submitted_form_data = array())
654
+	{
655
+		if (! $this->form()->was_submitted($submitted_form_data)) {
656
+			throw new InvalidFormSubmissionException($this->form_name);
657
+		}
658
+		$this->form(true)->receive_form_submission($submitted_form_data);
659
+		if (! $this->form()->is_valid()) {
660
+			throw new InvalidFormSubmissionException(
661
+				$this->form_name,
662
+				sprintf(
663
+					esc_html__(
664
+						'The "%1$s" form is invalid. Please correct the following errors and resubmit: %2$s %3$s',
665
+						'event_espresso'
666
+					),
667
+					$this->form_name,
668
+					'<br />',
669
+					implode('<br />', $this->form()->get_validation_errors_accumulated())
670
+				)
671
+			);
672
+		}
673
+		return apply_filters(
674
+			'FHEE__EventEspresso_core_libraries_form_sections_form_handlers_FormHandler__process__valid_data',
675
+			$this->form()->valid_data(),
676
+			$this
677
+		);
678
+	}
679 679
 
680 680
 
681 681
 
Please login to merge, or discard this patch.
form_sections/strategies/normalization/EE_Null_Normalization.strategy.php 2 patches
Indentation   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if (! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -15,26 +15,26 @@  discard block
 block discarded – undo
15 15
 class EE_Null_Normalization extends EE_Normalization_Strategy_Base
16 16
 {
17 17
 
18
-    /**
19
-     * @param string $value_to_normalize
20
-     * @return null
21
-     */
22
-    public function normalize($value_to_normalize)
23
-    {
24
-        return null;
25
-    }
26
-
27
-
28
-
29
-    /**
30
-     * In the form input we need some string, so use a blank one.
31
-     *
32
-     * @param string $normalized_value
33
-     * @return string
34
-     */
35
-    public function unnormalize($normalized_value)
36
-    {
37
-        return '';
38
-    }
18
+	/**
19
+	 * @param string $value_to_normalize
20
+	 * @return null
21
+	 */
22
+	public function normalize($value_to_normalize)
23
+	{
24
+		return null;
25
+	}
26
+
27
+
28
+
29
+	/**
30
+	 * In the form input we need some string, so use a blank one.
31
+	 *
32
+	 * @param string $normalized_value
33
+	 * @return string
34
+	 */
35
+	public function unnormalize($normalized_value)
36
+	{
37
+		return '';
38
+	}
39 39
 
40 40
 }
41 41
\ No newline at end of file
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -1,4 +1,4 @@
 block discarded – undo
1
-<?php if (! defined('EVENT_ESPRESSO_VERSION')) {
1
+<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2 2
     exit('No direct script access allowed');
3 3
 }
4 4
 
Please login to merge, or discard this patch.
core/libraries/form_sections/inputs/EE_Form_Input_Base.input.php 2 patches
Indentation   +1171 added lines, -1171 removed lines patch added patch discarded remove patch
@@ -1,5 +1,5 @@  discard block
 block discarded – undo
1 1
 <?php if (! defined('EVENT_ESPRESSO_VERSION')) {
2
-    exit('No direct script access allowed');
2
+	exit('No direct script access allowed');
3 3
 }
4 4
 
5 5
 
@@ -16,1174 +16,1174 @@  discard block
 block discarded – undo
16 16
 abstract class EE_Form_Input_Base extends EE_Form_Section_Validatable
17 17
 {
18 18
 
19
-    /**
20
-     * the input's name attribute
21
-     *
22
-     * @var string
23
-     */
24
-    protected $_html_name;
25
-
26
-    /**
27
-     * id for the html label tag
28
-     *
29
-     * @var string
30
-     */
31
-    protected $_html_label_id;
32
-
33
-    /**
34
-     * class for teh html label tag
35
-     *
36
-     * @var string
37
-     */
38
-    protected $_html_label_class;
39
-
40
-    /**
41
-     * any additional html attributes that you may want to add
42
-     *
43
-     * @var string
44
-     */
45
-    protected $_html_other_attributes;
46
-
47
-    /**
48
-     * style for teh html label tag
49
-     *
50
-     * @var string
51
-     */
52
-    protected $_html_label_style;
53
-
54
-    /**
55
-     * text to be placed in the html label
56
-     *
57
-     * @var string
58
-     */
59
-    protected $_html_label_text;
60
-
61
-    /**
62
-     * the full html label. If used, all other html_label_* properties are invalid
63
-     *
64
-     * @var string
65
-     */
66
-    protected $_html_label;
67
-
68
-    /**
69
-     * HTML to use for help text (normally placed below form input), in a span which normally
70
-     * has a class of 'description'
71
-     *
72
-     * @var string
73
-     */
74
-    protected $_html_help_text;
75
-
76
-    /**
77
-     * CSS classes for displaying the help span
78
-     *
79
-     * @var string
80
-     */
81
-    protected $_html_help_class = 'description';
82
-
83
-    /**
84
-     * CSS to put in the style attribute on the help span
85
-     *
86
-     * @var string
87
-     */
88
-    protected $_html_help_style;
89
-
90
-    /**
91
-     * Stores whether or not this input's response is required.
92
-     * Because certain styling elements may also want to know that this
93
-     * input is required etc.
94
-     *
95
-     * @var boolean
96
-     */
97
-    protected $_required;
98
-
99
-    /**
100
-     * css class added to required inputs
101
-     *
102
-     * @var string
103
-     */
104
-    protected $_required_css_class = 'ee-required';
105
-
106
-    /**
107
-     * css styles applied to button type inputs
108
-     *
109
-     * @var string
110
-     */
111
-    protected $_button_css_attributes;
112
-
113
-    /**
114
-     * The raw data submitted for this, like in the $_POST super global.
115
-     * Generally unsafe for usage in client code
116
-     *
117
-     * @var mixed string or array
118
-     */
119
-    protected $_raw_value;
120
-
121
-    /**
122
-     * Value normalized according to the input's normalization strategy.
123
-     * The normalization strategy dictates whether this is a string, int, float,
124
-     * boolean, or array of any of those.
125
-     *
126
-     * @var mixed
127
-     */
128
-    protected $_normalized_value;
129
-
130
-
131
-    /**
132
-     * Normalized default value either initially set on the input, or provided by calling
133
-     * set_default().
134
-     * @var mixed
135
-     */
136
-    protected $_default;
137
-
138
-    /**
139
-     * Strategy used for displaying this field.
140
-     * Child classes must use _get_display_strategy to access it.
141
-     *
142
-     * @var EE_Display_Strategy_Base
143
-     */
144
-    private $_display_strategy;
145
-
146
-    /**
147
-     * Gets all the validation strategies used on this field
148
-     *
149
-     * @var EE_Validation_Strategy_Base[]
150
-     */
151
-    private $_validation_strategies = array();
152
-
153
-    /**
154
-     * The normalization strategy for this field
155
-     *
156
-     * @var EE_Normalization_Strategy_Base
157
-     */
158
-    private $_normalization_strategy;
159
-
160
-    /**
161
-     * Strategy for removing sensitive data after we're done with the form input
162
-     *
163
-     * @var EE_Sensitive_Data_Removal_Base
164
-     */
165
-    protected $_sensitive_data_removal_strategy;
166
-
167
-
168
-
169
-    /**
170
-     * @param array                         $input_args       {
171
-     * @type string                         $html_name        the html name for the input
172
-     * @type string                         $html_label_id    the id attribute to give to the html label tag
173
-     * @type string                         $html_label_class the class attribute to give to the html label tag
174
-     * @type string                         $html_label_style the style attribute to give ot teh label tag
175
-     * @type string                         $html_label_text  the text to put in the label tag
176
-     * @type string                         $html_label       the full html label. If used,
177
-     *                                                        all other html_label_* args are invalid
178
-     * @type string                         $html_help_text   text to put in help element
179
-     * @type string                         $html_help_style  style attribute to give to teh help element
180
-     * @type string                         $html_help_class  class attribute to give to the help element
181
-     * @type string                         $default          default value NORMALIZED (eg, if providing the default
182
-     *       for a Yes_No_Input, you should provide TRUE or FALSE, not '1' or '0')
183
-     * @type EE_Display_Strategy_Base       $display          strategy
184
-     * @type EE_Normalization_Strategy_Base $normalization_strategy
185
-     * @type EE_Validation_Strategy_Base[]  $validation_strategies
186
-     * @type boolean                        $ignore_input special argument which can be used to avoid adding any validation strategies,
187
-     *                                                    and sets the normalization strategy to the Null normalization. This is good
188
-     *                                                    when you want the input to be totally ignored server-side (like when using
189
-     *                                                    React.js form inputs)
190
-     *                                                        }
191
-     */
192
-    public function __construct($input_args = array())
193
-    {
194
-        $input_args = (array)apply_filters('FHEE__EE_Form_Input_Base___construct__input_args', $input_args, $this);
195
-        // the following properties must be cast as arrays
196
-        if (isset($input_args['validation_strategies'])) {
197
-            foreach ((array)$input_args['validation_strategies'] as $validation_strategy) {
198
-                if ($validation_strategy instanceof EE_Validation_Strategy_Base && empty($input_args['ignore_input'])) {
199
-                    $this->_validation_strategies[get_class($validation_strategy)] = $validation_strategy;
200
-                }
201
-            }
202
-            unset($input_args['validation_strategies']);
203
-        }
204
-        if(isset($input_args['ignore_input'])) {
205
-            $this->_validation_strategies = array();
206
-        }
207
-        // loop thru incoming options
208
-        foreach ($input_args as $key => $value) {
209
-            // add underscore to $key to match property names
210
-            $_key = '_' . $key;
211
-            if (property_exists($this, $_key)) {
212
-                $this->{$_key} = $value;
213
-            }
214
-        }
215
-        // ensure that "required" is set correctly
216
-        $this->set_required(
217
-            $this->_required, isset($input_args['required_validation_error_message'])
218
-            ? $input_args['required_validation_error_message']
219
-            : null
220
-        );
221
-        //$this->_html_name_specified = isset( $input_args['html_name'] ) ? TRUE : FALSE;
222
-        $this->_display_strategy->_construct_finalize($this);
223
-        foreach ($this->_validation_strategies as $validation_strategy) {
224
-            $validation_strategy->_construct_finalize($this);
225
-        }
226
-        if (isset($input_args['ignore_input'])) {
227
-            $this->_normalization_strategy = new EE_Null_Normalization();
228
-        }
229
-        if (! $this->_normalization_strategy) {
230
-                $this->_normalization_strategy = new EE_Text_Normalization();
231
-        }
232
-        $this->_normalization_strategy->_construct_finalize($this);
233
-        //at least we can use the normalization strategy to populate the default
234
-        if (isset($input_args['default'])) {
235
-            $this->set_default($input_args['default']);
236
-            unset($input_args['default']);
237
-        }
238
-        if (! $this->_sensitive_data_removal_strategy) {
239
-            $this->_sensitive_data_removal_strategy = new EE_No_Sensitive_Data_Removal();
240
-        }
241
-        $this->_sensitive_data_removal_strategy->_construct_finalize($this);
242
-        parent::__construct($input_args);
243
-    }
244
-
245
-
246
-
247
-    /**
248
-     * Sets the html_name to its default value, if none was specified in teh constructor.
249
-     * Calculation involves using the name and the parent's html_name
250
-     *
251
-     * @throws \EE_Error
252
-     */
253
-    protected function _set_default_html_name_if_empty()
254
-    {
255
-        if (! $this->_html_name) {
256
-            $this->_html_name = $this->name();
257
-            if ($this->_parent_section && $this->_parent_section instanceof EE_Form_Section_Proper) {
258
-                $this->_html_name = $this->_parent_section->html_name_prefix() . "[{$this->name()}]";
259
-            }
260
-        }
261
-    }
262
-
263
-
264
-
265
-    /**
266
-     * @param $parent_form_section
267
-     * @param $name
268
-     * @throws \EE_Error
269
-     */
270
-    public function _construct_finalize($parent_form_section, $name)
271
-    {
272
-        parent::_construct_finalize($parent_form_section, $name);
273
-        if ($this->_html_label === null && $this->_html_label_text === null) {
274
-            $this->_html_label_text = ucwords(str_replace("_", " ", $name));
275
-        }
276
-        do_action('AHEE__EE_Form_Input_Base___construct_finalize__end', $this, $parent_form_section, $name);
277
-    }
278
-
279
-
280
-
281
-    /**
282
-     * Returns the strategy for displaying this form input. If none is set, throws an exception.
283
-     *
284
-     * @return EE_Display_Strategy_Base
285
-     * @throws EE_Error
286
-     */
287
-    protected function _get_display_strategy()
288
-    {
289
-        $this->ensure_construct_finalized_called();
290
-        if (! $this->_display_strategy || ! $this->_display_strategy instanceof EE_Display_Strategy_Base) {
291
-            throw new EE_Error(
292
-                sprintf(
293
-                    __(
294
-                        "Cannot get display strategy for form input with name %s and id %s, because it has not been set in the constructor",
295
-                        "event_espresso"
296
-                    ),
297
-                    $this->html_name(),
298
-                    $this->html_id()
299
-                )
300
-            );
301
-        } else {
302
-            return $this->_display_strategy;
303
-        }
304
-    }
305
-
306
-
307
-
308
-    /**
309
-     * Sets the display strategy.
310
-     *
311
-     * @param EE_Display_Strategy_Base $strategy
312
-     */
313
-    protected function _set_display_strategy(EE_Display_Strategy_Base $strategy)
314
-    {
315
-        $this->_display_strategy = $strategy;
316
-    }
317
-
318
-
319
-
320
-    /**
321
-     * Sets the sanitization strategy
322
-     *
323
-     * @param EE_Normalization_Strategy_Base $strategy
324
-     */
325
-    protected function _set_normalization_strategy(EE_Normalization_Strategy_Base $strategy)
326
-    {
327
-        $this->_normalization_strategy = $strategy;
328
-    }
329
-
330
-
331
-
332
-    /**
333
-     * Gets sensitive_data_removal_strategy
334
-     *
335
-     * @return EE_Sensitive_Data_Removal_Base
336
-     */
337
-    public function get_sensitive_data_removal_strategy()
338
-    {
339
-        return $this->_sensitive_data_removal_strategy;
340
-    }
341
-
342
-
343
-
344
-    /**
345
-     * Sets sensitive_data_removal_strategy
346
-     *
347
-     * @param EE_Sensitive_Data_Removal_Base $sensitive_data_removal_strategy
348
-     * @return boolean
349
-     */
350
-    public function set_sensitive_data_removal_strategy($sensitive_data_removal_strategy)
351
-    {
352
-        $this->_sensitive_data_removal_strategy = $sensitive_data_removal_strategy;
353
-    }
354
-
355
-
356
-
357
-    /**
358
-     * Gets the display strategy for this input
359
-     *
360
-     * @return EE_Display_Strategy_Base
361
-     */
362
-    public function get_display_strategy()
363
-    {
364
-        return $this->_display_strategy;
365
-    }
366
-
367
-
368
-
369
-    /**
370
-     * Overwrites the display strategy
371
-     *
372
-     * @param EE_Display_Strategy_Base $display_strategy
373
-     */
374
-    public function set_display_strategy($display_strategy)
375
-    {
376
-        $this->_display_strategy = $display_strategy;
377
-        $this->_display_strategy->_construct_finalize($this);
378
-    }
379
-
380
-
381
-
382
-    /**
383
-     * Gets the normalization strategy set on this input
384
-     *
385
-     * @return EE_Normalization_Strategy_Base
386
-     */
387
-    public function get_normalization_strategy()
388
-    {
389
-        return $this->_normalization_strategy;
390
-    }
391
-
392
-
393
-
394
-    /**
395
-     * Overwrites the normalization strategy
396
-     *
397
-     * @param EE_Normalization_Strategy_Base $normalization_strategy
398
-     */
399
-    public function set_normalization_strategy($normalization_strategy)
400
-    {
401
-        $this->_normalization_strategy = $normalization_strategy;
402
-        $this->_normalization_strategy->_construct_finalize($this);
403
-    }
404
-
405
-
406
-
407
-    /**
408
-     * Returns all teh validation strategies which apply to this field, numerically indexed
409
-     *
410
-     * @return EE_Validation_Strategy_Base[]
411
-     */
412
-    public function get_validation_strategies()
413
-    {
414
-        return $this->_validation_strategies;
415
-    }
416
-
417
-
418
-
419
-    /**
420
-     * Adds this strategy to the field so it will be used in both JS validation and server-side validation
421
-     *
422
-     * @param EE_Validation_Strategy_Base $validation_strategy
423
-     * @return void
424
-     */
425
-    protected function _add_validation_strategy(EE_Validation_Strategy_Base $validation_strategy)
426
-    {
427
-        $validation_strategy->_construct_finalize($this);
428
-        $this->_validation_strategies[] = $validation_strategy;
429
-    }
430
-
431
-
432
-
433
-    /**
434
-     * Adds a new validation strategy onto the form input
435
-     *
436
-     * @param EE_Validation_Strategy_Base $validation_strategy
437
-     * @return void
438
-     */
439
-    public function add_validation_strategy(EE_Validation_Strategy_Base $validation_strategy)
440
-    {
441
-        $this->_add_validation_strategy($validation_strategy);
442
-    }
443
-
444
-
445
-
446
-    /**
447
-     * The classname of the validation strategy to remove
448
-     *
449
-     * @param string $validation_strategy_classname
450
-     */
451
-    public function remove_validation_strategy($validation_strategy_classname)
452
-    {
453
-        foreach ($this->_validation_strategies as $key => $validation_strategy) {
454
-            if (
455
-                $validation_strategy instanceof $validation_strategy_classname
456
-                || is_subclass_of($validation_strategy, $validation_strategy_classname)
457
-            ) {
458
-                unset($this->_validation_strategies[$key]);
459
-            }
460
-        }
461
-    }
462
-
463
-
464
-
465
-    /**
466
-     * returns true if input employs any of the validation strategy defined by the supplied array of classnames
467
-     *
468
-     * @param array $validation_strategy_classnames
469
-     * @return bool
470
-     */
471
-    public function has_validation_strategy($validation_strategy_classnames)
472
-    {
473
-        $validation_strategy_classnames = is_array($validation_strategy_classnames)
474
-            ? $validation_strategy_classnames
475
-            : array($validation_strategy_classnames);
476
-        foreach ($this->_validation_strategies as $key => $validation_strategy) {
477
-            if (in_array($key, $validation_strategy_classnames)) {
478
-                return true;
479
-            }
480
-        }
481
-        return false;
482
-    }
483
-
484
-
485
-
486
-    /**
487
-     * Gets the HTML
488
-     *
489
-     * @return string
490
-     */
491
-    public function get_html()
492
-    {
493
-        return $this->_parent_section->get_html_for_input($this);
494
-    }
495
-
496
-
497
-
498
-    /**
499
-     * Gets the HTML for the input itself (no label or errors) according to the
500
-     * input's display strategy
501
-     * Makes sure the JS and CSS are enqueued for it
502
-     *
503
-     * @return string
504
-     * @throws \EE_Error
505
-     */
506
-    public function get_html_for_input()
507
-    {
508
-        return $this->_form_html_filter
509
-            ? $this->_form_html_filter->filterHtml(
510
-                $this->_get_display_strategy()->display(),
511
-                $this
512
-            )
513
-            : $this->_get_display_strategy()->display();
514
-    }
515
-
516
-
517
-
518
-    /**
519
-     * @return string
520
-     */
521
-    public function html_other_attributes()
522
-    {
523
-        return ! empty($this->_html_other_attributes) ? ' ' . $this->_html_other_attributes : '';
524
-    }
525
-
526
-
527
-
528
-    /**
529
-     * @param string $html_other_attributes
530
-     */
531
-    public function set_html_other_attributes($html_other_attributes)
532
-    {
533
-        $this->_html_other_attributes = $html_other_attributes;
534
-    }
535
-
536
-
537
-
538
-    /**
539
-     * Gets the HTML for displaying the label for this form input
540
-     * according to the form section's layout strategy
541
-     *
542
-     * @return string
543
-     */
544
-    public function get_html_for_label()
545
-    {
546
-        return $this->_parent_section->get_layout_strategy()->display_label($this);
547
-    }
548
-
549
-
550
-
551
-    /**
552
-     * Gets the HTML for displaying the errors section for this form input
553
-     * according to the form section's layout strategy
554
-     *
555
-     * @return string
556
-     */
557
-    public function get_html_for_errors()
558
-    {
559
-        return $this->_parent_section->get_layout_strategy()->display_errors($this);
560
-    }
561
-
562
-
563
-
564
-    /**
565
-     * Gets the HTML for displaying the help text for this form input
566
-     * according to the form section's layout strategy
567
-     *
568
-     * @return string
569
-     */
570
-    public function get_html_for_help()
571
-    {
572
-        return $this->_parent_section->get_layout_strategy()->display_help_text($this);
573
-    }
574
-
575
-
576
-
577
-    /**
578
-     * Validates the input's sanitized value (assumes _sanitize() has already been called)
579
-     * and returns whether or not the form input's submitted value is value
580
-     *
581
-     * @return boolean
582
-     */
583
-    protected function _validate()
584
-    {
585
-        foreach ($this->_validation_strategies as $validation_strategy) {
586
-            if ($validation_strategy instanceof EE_Validation_Strategy_Base) {
587
-                try {
588
-                    $validation_strategy->validate($this->normalized_value());
589
-                } catch (EE_Validation_Error $e) {
590
-                    $this->add_validation_error($e);
591
-                }
592
-            }
593
-        }
594
-        if ($this->get_validation_errors()) {
595
-            return false;
596
-        } else {
597
-            return true;
598
-        }
599
-    }
600
-
601
-
602
-
603
-    /**
604
-     * Performs basic sanitization on this value. But what sanitization can be performed anyways?
605
-     * This value MIGHT be allowed to have tags, so we can't really remove them.
606
-     *
607
-     * @param string $value
608
-     * @return null|string
609
-     */
610
-    private function _sanitize($value)
611
-    {
612
-        return $value !== null ? stripslashes(html_entity_decode(trim($value))) : null;
613
-    }
614
-
615
-
616
-
617
-    /**
618
-     * Picks out the form value that relates to this form input,
619
-     * and stores it as the sanitized value on the form input, and sets the normalized value.
620
-     * Returns whether or not any validation errors occurred
621
-     *
622
-     * @param array $req_data like $_POST
623
-     * @return boolean whether or not there was an error
624
-     * @throws \EE_Error
625
-     */
626
-    protected function _normalize($req_data)
627
-    {
628
-        //any existing validation errors don't apply so clear them
629
-        $this->_validation_errors = array();
630
-        try {
631
-            $raw_input = $this->find_form_data_for_this_section($req_data);
632
-            //super simple sanitization for now
633
-            if (is_array($raw_input)) {
634
-                $raw_value = array();
635
-                foreach ($raw_input as $key => $value) {
636
-                    $raw_value[$key] = $this->_sanitize($value);
637
-                }
638
-                $this->_set_raw_value($raw_value);
639
-            } else {
640
-                $this->_set_raw_value($this->_sanitize($raw_input));
641
-            }
642
-            //we want to mostly leave the input alone in case we need to re-display it to the user
643
-            $this->_set_normalized_value($this->_normalization_strategy->normalize($this->raw_value()));
644
-        } catch (EE_Validation_Error $e) {
645
-            $this->add_validation_error($e);
646
-        }
647
-    }
648
-
649
-
650
-
651
-    /**
652
-     * @return string
653
-     */
654
-    public function html_name()
655
-    {
656
-        $this->_set_default_html_name_if_empty();
657
-        return $this->_html_name;
658
-    }
659
-
660
-
661
-
662
-    /**
663
-     * @return string
664
-     */
665
-    public function html_label_id()
666
-    {
667
-        return ! empty($this->_html_label_id) ? $this->_html_label_id : $this->html_id() . '-lbl';
668
-    }
669
-
670
-
671
-
672
-    /**
673
-     * @return string
674
-     */
675
-    public function html_label_class()
676
-    {
677
-        return $this->_html_label_class;
678
-    }
679
-
680
-
681
-
682
-    /**
683
-     * @return string
684
-     */
685
-    public function html_label_style()
686
-    {
687
-        return $this->_html_label_style;
688
-    }
689
-
690
-
691
-
692
-    /**
693
-     * @return string
694
-     */
695
-    public function html_label_text()
696
-    {
697
-        return $this->_html_label_text;
698
-    }
699
-
700
-
701
-
702
-    /**
703
-     * @return string
704
-     */
705
-    public function html_help_text()
706
-    {
707
-        return $this->_html_help_text;
708
-    }
709
-
710
-
711
-
712
-    /**
713
-     * @return string
714
-     */
715
-    public function html_help_class()
716
-    {
717
-        return $this->_html_help_class;
718
-    }
719
-
720
-
721
-
722
-    /**
723
-     * @return string
724
-     */
725
-    public function html_help_style()
726
-    {
727
-        return $this->_html_style;
728
-    }
729
-
730
-
731
-
732
-    /**
733
-     * returns the raw, UNSAFE, input, almost exactly as the user submitted it.
734
-     * Please note that almost all client code should instead use the normalized_value;
735
-     * or possibly raw_value_in_form (which prepares the string for displaying in an HTML attribute on a tag,
736
-     * mostly by escaping quotes)
737
-     * Note, we do not store the exact original value sent in the user's request because
738
-     * it may have malicious content, and we MIGHT want to store the form input in a transient or something...
739
-     * in which case, we would have stored the malicious content to our database.
740
-     *
741
-     * @return string
742
-     */
743
-    public function raw_value()
744
-    {
745
-        return $this->_raw_value;
746
-    }
747
-
748
-
749
-
750
-    /**
751
-     * Returns a string safe to usage in form inputs when displaying, because
752
-     * it escapes all html entities
753
-     *
754
-     * @return string
755
-     */
756
-    public function raw_value_in_form()
757
-    {
758
-        return htmlentities($this->raw_value(), ENT_QUOTES, 'UTF-8');
759
-    }
760
-
761
-
762
-
763
-    /**
764
-     * returns the value after it's been sanitized, and then converted into it's proper type
765
-     * in PHP. Eg, a string, an int, an array,
766
-     *
767
-     * @return mixed
768
-     */
769
-    public function normalized_value()
770
-    {
771
-        return $this->_normalized_value;
772
-    }
773
-
774
-
775
-
776
-    /**
777
-     * Returns the normalized value is a presentable way. By default this is just
778
-     * the normalized value by itself, but it can be overridden for when that's not
779
-     * the best thing to display
780
-     *
781
-     * @return string
782
-     */
783
-    public function pretty_value()
784
-    {
785
-        return $this->_normalized_value;
786
-    }
787
-
788
-
789
-
790
-    /**
791
-     * When generating the JS for the jquery validation rules like<br>
792
-     * <code>$( "#myform" ).validate({
793
-     * rules: {
794
-     * password: "required",
795
-     * password_again: {
796
-     * equalTo: "#password"
797
-     * }
798
-     * }
799
-     * });</code>
800
-     * if this field had the name 'password_again', it should return
801
-     * <br><code>password_again: {
802
-     * equalTo: "#password"
803
-     * }</code>
804
-     *
805
-     * @return array
806
-     */
807
-    public function get_jquery_validation_rules()
808
-    {
809
-        $jquery_validation_js = array();
810
-        $jquery_validation_rules = array();
811
-        foreach ($this->get_validation_strategies() as $validation_strategy) {
812
-            $jquery_validation_rules = array_replace_recursive(
813
-                $jquery_validation_rules,
814
-                $validation_strategy->get_jquery_validation_rule_array()
815
-            );
816
-        }
817
-        if (! empty($jquery_validation_rules)) {
818
-            foreach ($this->get_display_strategy()->get_html_input_ids(true) as $html_id_with_pound_sign) {
819
-                $jquery_validation_js[$html_id_with_pound_sign] = $jquery_validation_rules;
820
-            }
821
-        }
822
-        return $jquery_validation_js;
823
-    }
824
-
825
-
826
-
827
-    /**
828
-     * Sets the input's default value for use in displaying in the form. Note: value should be
829
-     * normalized (Eg, if providing a default of ra Yes_NO_Input you would provide TRUE or FALSE, not '1' or '0')
830
-     *
831
-     * @param mixed $value
832
-     * @return void
833
-     */
834
-    public function set_default($value)
835
-    {
836
-        $this->_default = $value;
837
-        $this->_set_normalized_value($value);
838
-        $this->_set_raw_value($value);
839
-    }
840
-
841
-
842
-
843
-    /**
844
-     * Sets the normalized value on this input
845
-     *
846
-     * @param mixed $value
847
-     */
848
-    protected function _set_normalized_value($value)
849
-    {
850
-        $this->_normalized_value = $value;
851
-    }
852
-
853
-
854
-
855
-    /**
856
-     * Sets the raw value on this input (ie, exactly as the user submitted it)
857
-     *
858
-     * @param mixed $value
859
-     */
860
-    protected function _set_raw_value($value)
861
-    {
862
-        $this->_raw_value = $this->_normalization_strategy->unnormalize($value);
863
-    }
864
-
865
-
866
-
867
-    /**
868
-     * Sets the HTML label text after it has already been defined
869
-     *
870
-     * @param string $label
871
-     * @return void
872
-     */
873
-    public function set_html_label_text($label)
874
-    {
875
-        $this->_html_label_text = $label;
876
-    }
877
-
878
-
879
-
880
-    /**
881
-     * Sets whether or not this field is required, and adjusts the validation strategy.
882
-     * If you want to use the EE_Conditionally_Required_Validation_Strategy,
883
-     * please add it as a validation strategy using add_validation_strategy as normal
884
-     *
885
-     * @param boolean $required boolean
886
-     * @param null    $required_text
887
-     */
888
-    public function set_required($required = true, $required_text = null)
889
-    {
890
-        $required = filter_var($required, FILTER_VALIDATE_BOOLEAN);
891
-        //whether $required is a string or a boolean, we want to add a required validation strategy
892
-        if ($required) {
893
-            $this->_add_validation_strategy(new EE_Required_Validation_Strategy($required_text));
894
-        } else {
895
-            $this->remove_validation_strategy('EE_Required_Validation_Strategy');
896
-        }
897
-        $this->_required = $required;
898
-    }
899
-
900
-
901
-
902
-    /**
903
-     * Returns whether or not this field is required
904
-     *
905
-     * @return boolean
906
-     */
907
-    public function required()
908
-    {
909
-        return $this->_required;
910
-    }
911
-
912
-
913
-
914
-    /**
915
-     * @param string $required_css_class
916
-     */
917
-    public function set_required_css_class($required_css_class)
918
-    {
919
-        $this->_required_css_class = $required_css_class;
920
-    }
921
-
922
-
923
-
924
-    /**
925
-     * @return string
926
-     */
927
-    public function required_css_class()
928
-    {
929
-        return $this->_required_css_class;
930
-    }
931
-
932
-
933
-
934
-    /**
935
-     * @param bool $add_required
936
-     * @return string
937
-     */
938
-    public function html_class($add_required = false)
939
-    {
940
-        return $add_required && $this->required()
941
-            ? $this->required_css_class() . ' ' . $this->_html_class
942
-            : $this->_html_class;
943
-    }
944
-
945
-
946
-    /**
947
-     * Sets the help text, in case
948
-     *
949
-     * @param string $text
950
-     */
951
-    public function set_html_help_text($text)
952
-    {
953
-        $this->_html_help_text = $text;
954
-    }
955
-
956
-
957
-
958
-    /**
959
-     * Uses the sensitive data removal strategy to remove the sensitive data from this
960
-     * input. If there is any kind of sensitive data removal on this input, we clear
961
-     * out the raw value completely
962
-     *
963
-     * @return void
964
-     */
965
-    public function clean_sensitive_data()
966
-    {
967
-        //if we do ANY kind of sensitive data removal on this, then just clear out the raw value
968
-        //if we need more logic than this we'll make a strategy for it
969
-        if ($this->_sensitive_data_removal_strategy
970
-            && ! $this->_sensitive_data_removal_strategy instanceof EE_No_Sensitive_Data_Removal
971
-        ) {
972
-            $this->_set_raw_value(null);
973
-        }
974
-        //and clean the normalized value according to the appropriate strategy
975
-        $this->_set_normalized_value(
976
-            $this->get_sensitive_data_removal_strategy()->remove_sensitive_data(
977
-                $this->_normalized_value
978
-            )
979
-        );
980
-    }
981
-
982
-
983
-
984
-    /**
985
-     * @param bool   $primary
986
-     * @param string $button_size
987
-     * @param string $other_attributes
988
-     */
989
-    public function set_button_css_attributes($primary = true, $button_size = '', $other_attributes = '')
990
-    {
991
-        $button_css_attributes = 'button';
992
-        $button_css_attributes .= $primary === true ? ' button-primary' : ' button-secondary';
993
-        switch ($button_size) {
994
-            case 'xs' :
995
-            case 'extra-small' :
996
-                $button_css_attributes .= ' button-xs';
997
-                break;
998
-            case 'sm' :
999
-            case 'small' :
1000
-                $button_css_attributes .= ' button-sm';
1001
-                break;
1002
-            case 'lg' :
1003
-            case 'large' :
1004
-                $button_css_attributes .= ' button-lg';
1005
-                break;
1006
-            case 'block' :
1007
-                $button_css_attributes .= ' button-block';
1008
-                break;
1009
-            case 'md' :
1010
-            case 'medium' :
1011
-            default :
1012
-                $button_css_attributes .= '';
1013
-        }
1014
-        $this->_button_css_attributes .= ! empty($other_attributes)
1015
-            ? $button_css_attributes . ' ' . $other_attributes
1016
-            : $button_css_attributes;
1017
-    }
1018
-
1019
-
1020
-
1021
-    /**
1022
-     * @return string
1023
-     */
1024
-    public function button_css_attributes()
1025
-    {
1026
-        if (empty($this->_button_css_attributes)) {
1027
-            $this->set_button_css_attributes();
1028
-        }
1029
-        return $this->_button_css_attributes;
1030
-    }
1031
-
1032
-
1033
-
1034
-    /**
1035
-     * find_form_data_for_this_section
1036
-     * using this section's name and its parents, finds the value of the form data that corresponds to it.
1037
-     * For example, if this form section's HTML name is my_form[subform][form_input_1],
1038
-     * then it's value should be in $_REQUEST at $_REQUEST['my_form']['subform']['form_input_1'].
1039
-     * (If that doesn't exist, we also check for this subsection's name
1040
-     * at the TOP LEVEL of the request data. Eg $_REQUEST['form_input_1'].)
1041
-     * This function finds its value in the form.
1042
-     *
1043
-     * @param array $req_data
1044
-     * @return mixed whatever the raw value of this form section is in the request data
1045
-     * @throws \EE_Error
1046
-     */
1047
-    public function find_form_data_for_this_section($req_data)
1048
-    {
1049
-        // break up the html name by "[]"
1050
-        if (strpos($this->html_name(), '[') !== false) {
1051
-            $before_any_brackets = substr($this->html_name(), 0, strpos($this->html_name(), '['));
1052
-        } else {
1053
-            $before_any_brackets = $this->html_name();
1054
-        }
1055
-        // grab all of the segments
1056
-        preg_match_all('~\[([^]]*)\]~', $this->html_name(), $matches);
1057
-        if (isset($matches[1]) && is_array($matches[1])) {
1058
-            $name_parts = $matches[1];
1059
-            array_unshift($name_parts, $before_any_brackets);
1060
-        } else {
1061
-            $name_parts = array($before_any_brackets);
1062
-        }
1063
-        // now get the value for the input
1064
-        $value = $this->_find_form_data_for_this_section_using_name_parts($name_parts, $req_data);
1065
-        // check if this thing's name is at the TOP level of the request data
1066
-        if ($value === null && isset($req_data[$this->name()])) {
1067
-            $value = $req_data[$this->name()];
1068
-        }
1069
-        return $value;
1070
-    }
1071
-
1072
-
1073
-
1074
-    /**
1075
-     * @param array $html_name_parts
1076
-     * @param array $req_data
1077
-     * @return array | NULL
1078
-     */
1079
-    public function _find_form_data_for_this_section_using_name_parts($html_name_parts, $req_data)
1080
-    {
1081
-        $first_part_to_consider = array_shift($html_name_parts);
1082
-        if (isset($req_data[$first_part_to_consider])) {
1083
-            if (empty($html_name_parts)) {
1084
-                return $req_data[$first_part_to_consider];
1085
-            } else {
1086
-                return $this->_find_form_data_for_this_section_using_name_parts(
1087
-                    $html_name_parts,
1088
-                    $req_data[$first_part_to_consider]
1089
-                );
1090
-            }
1091
-        } else {
1092
-            return null;
1093
-        }
1094
-    }
1095
-
1096
-
1097
-
1098
-    /**
1099
-     * Checks if this form input's data is in the request data
1100
-     *
1101
-     * @param array $req_data like $_POST
1102
-     * @return boolean
1103
-     * @throws \EE_Error
1104
-     */
1105
-    public function form_data_present_in($req_data = null)
1106
-    {
1107
-        if ($req_data === null) {
1108
-            $req_data = $_POST;
1109
-        }
1110
-        $checked_value = $this->find_form_data_for_this_section($req_data);
1111
-        if ($checked_value !== null) {
1112
-            return true;
1113
-        } else {
1114
-            return false;
1115
-        }
1116
-    }
1117
-
1118
-
1119
-
1120
-    /**
1121
-     * Overrides parent to add js data from validation and display strategies
1122
-     *
1123
-     * @param array $form_other_js_data
1124
-     * @return array
1125
-     */
1126
-    public function get_other_js_data($form_other_js_data = array())
1127
-    {
1128
-        $form_other_js_data = $this->get_other_js_data_from_strategies($form_other_js_data);
1129
-        return $form_other_js_data;
1130
-    }
1131
-
1132
-
1133
-
1134
-    /**
1135
-     * Gets other JS data for localization from this input's strategies, like
1136
-     * the validation strategies and the display strategy
1137
-     *
1138
-     * @param array $form_other_js_data
1139
-     * @return array
1140
-     */
1141
-    public function get_other_js_data_from_strategies($form_other_js_data = array())
1142
-    {
1143
-        $form_other_js_data = $this->get_display_strategy()->get_other_js_data($form_other_js_data);
1144
-        foreach ($this->get_validation_strategies() as $validation_strategy) {
1145
-            $form_other_js_data = $validation_strategy->get_other_js_data($form_other_js_data);
1146
-        }
1147
-        return $form_other_js_data;
1148
-    }
1149
-
1150
-
1151
-
1152
-    /**
1153
-     * Override parent because we want to give our strategies an opportunity to enqueue some js and css
1154
-     *
1155
-     * @return void
1156
-     */
1157
-    public function enqueue_js()
1158
-    {
1159
-        //ask our display strategy and validation strategies if they have js to enqueue
1160
-        $this->enqueue_js_from_strategies();
1161
-    }
1162
-
1163
-
1164
-
1165
-    /**
1166
-     * Tells strategies when its ok to enqueue their js and css
1167
-     *
1168
-     * @return void
1169
-     */
1170
-    public function enqueue_js_from_strategies()
1171
-    {
1172
-        $this->get_display_strategy()->enqueue_js();
1173
-        foreach ($this->get_validation_strategies() as $validation_strategy) {
1174
-            $validation_strategy->enqueue_js();
1175
-        }
1176
-    }
1177
-
1178
-
1179
-
1180
-    /**
1181
-     * Gets the default value set on the input (not the current value, which may have been
1182
-     * changed because of a form submission). If no default was set, this us null.
1183
-     * @return mixed
1184
-     */
1185
-    public function get_default()
1186
-    {
1187
-        return $this->_default;
1188
-    }
19
+	/**
20
+	 * the input's name attribute
21
+	 *
22
+	 * @var string
23
+	 */
24
+	protected $_html_name;
25
+
26
+	/**
27
+	 * id for the html label tag
28
+	 *
29
+	 * @var string
30
+	 */
31
+	protected $_html_label_id;
32
+
33
+	/**
34
+	 * class for teh html label tag
35
+	 *
36
+	 * @var string
37
+	 */
38
+	protected $_html_label_class;
39
+
40
+	/**
41
+	 * any additional html attributes that you may want to add
42
+	 *
43
+	 * @var string
44
+	 */
45
+	protected $_html_other_attributes;
46
+
47
+	/**
48
+	 * style for teh html label tag
49
+	 *
50
+	 * @var string
51
+	 */
52
+	protected $_html_label_style;
53
+
54
+	/**
55
+	 * text to be placed in the html label
56
+	 *
57
+	 * @var string
58
+	 */
59
+	protected $_html_label_text;
60
+
61
+	/**
62
+	 * the full html label. If used, all other html_label_* properties are invalid
63
+	 *
64
+	 * @var string
65
+	 */
66
+	protected $_html_label;
67
+
68
+	/**
69
+	 * HTML to use for help text (normally placed below form input), in a span which normally
70
+	 * has a class of 'description'
71
+	 *
72
+	 * @var string
73
+	 */
74
+	protected $_html_help_text;
75
+
76
+	/**
77
+	 * CSS classes for displaying the help span
78
+	 *
79
+	 * @var string
80
+	 */
81
+	protected $_html_help_class = 'description';
82
+
83
+	/**
84
+	 * CSS to put in the style attribute on the help span
85
+	 *
86
+	 * @var string
87
+	 */
88
+	protected $_html_help_style;
89
+
90
+	/**
91
+	 * Stores whether or not this input's response is required.
92
+	 * Because certain styling elements may also want to know that this
93
+	 * input is required etc.
94
+	 *
95
+	 * @var boolean
96
+	 */
97
+	protected $_required;
98
+
99
+	/**
100
+	 * css class added to required inputs
101
+	 *
102
+	 * @var string
103
+	 */
104
+	protected $_required_css_class = 'ee-required';
105
+
106
+	/**
107
+	 * css styles applied to button type inputs
108
+	 *
109
+	 * @var string
110
+	 */
111
+	protected $_button_css_attributes;
112
+
113
+	/**
114
+	 * The raw data submitted for this, like in the $_POST super global.
115
+	 * Generally unsafe for usage in client code
116
+	 *
117
+	 * @var mixed string or array
118
+	 */
119
+	protected $_raw_value;
120
+
121
+	/**
122
+	 * Value normalized according to the input's normalization strategy.
123
+	 * The normalization strategy dictates whether this is a string, int, float,
124
+	 * boolean, or array of any of those.
125
+	 *
126
+	 * @var mixed
127
+	 */
128
+	protected $_normalized_value;
129
+
130
+
131
+	/**
132
+	 * Normalized default value either initially set on the input, or provided by calling
133
+	 * set_default().
134
+	 * @var mixed
135
+	 */
136
+	protected $_default;
137
+
138
+	/**
139
+	 * Strategy used for displaying this field.
140
+	 * Child classes must use _get_display_strategy to access it.
141
+	 *
142
+	 * @var EE_Display_Strategy_Base
143
+	 */
144
+	private $_display_strategy;
145
+
146
+	/**
147
+	 * Gets all the validation strategies used on this field
148
+	 *
149
+	 * @var EE_Validation_Strategy_Base[]
150
+	 */
151
+	private $_validation_strategies = array();
152
+
153
+	/**
154
+	 * The normalization strategy for this field
155
+	 *
156
+	 * @var EE_Normalization_Strategy_Base
157
+	 */
158
+	private $_normalization_strategy;
159
+
160
+	/**
161
+	 * Strategy for removing sensitive data after we're done with the form input
162
+	 *
163
+	 * @var EE_Sensitive_Data_Removal_Base
164
+	 */
165
+	protected $_sensitive_data_removal_strategy;
166
+
167
+
168
+
169
+	/**
170
+	 * @param array                         $input_args       {
171
+	 * @type string                         $html_name        the html name for the input
172
+	 * @type string                         $html_label_id    the id attribute to give to the html label tag
173
+	 * @type string                         $html_label_class the class attribute to give to the html label tag
174
+	 * @type string                         $html_label_style the style attribute to give ot teh label tag
175
+	 * @type string                         $html_label_text  the text to put in the label tag
176
+	 * @type string                         $html_label       the full html label. If used,
177
+	 *                                                        all other html_label_* args are invalid
178
+	 * @type string                         $html_help_text   text to put in help element
179
+	 * @type string                         $html_help_style  style attribute to give to teh help element
180
+	 * @type string                         $html_help_class  class attribute to give to the help element
181
+	 * @type string                         $default          default value NORMALIZED (eg, if providing the default
182
+	 *       for a Yes_No_Input, you should provide TRUE or FALSE, not '1' or '0')
183
+	 * @type EE_Display_Strategy_Base       $display          strategy
184
+	 * @type EE_Normalization_Strategy_Base $normalization_strategy
185
+	 * @type EE_Validation_Strategy_Base[]  $validation_strategies
186
+	 * @type boolean                        $ignore_input special argument which can be used to avoid adding any validation strategies,
187
+	 *                                                    and sets the normalization strategy to the Null normalization. This is good
188
+	 *                                                    when you want the input to be totally ignored server-side (like when using
189
+	 *                                                    React.js form inputs)
190
+	 *                                                        }
191
+	 */
192
+	public function __construct($input_args = array())
193
+	{
194
+		$input_args = (array)apply_filters('FHEE__EE_Form_Input_Base___construct__input_args', $input_args, $this);
195
+		// the following properties must be cast as arrays
196
+		if (isset($input_args['validation_strategies'])) {
197
+			foreach ((array)$input_args['validation_strategies'] as $validation_strategy) {
198
+				if ($validation_strategy instanceof EE_Validation_Strategy_Base && empty($input_args['ignore_input'])) {
199
+					$this->_validation_strategies[get_class($validation_strategy)] = $validation_strategy;
200
+				}
201
+			}
202
+			unset($input_args['validation_strategies']);
203
+		}
204
+		if(isset($input_args['ignore_input'])) {
205
+			$this->_validation_strategies = array();
206
+		}
207
+		// loop thru incoming options
208
+		foreach ($input_args as $key => $value) {
209
+			// add underscore to $key to match property names
210
+			$_key = '_' . $key;
211
+			if (property_exists($this, $_key)) {
212
+				$this->{$_key} = $value;
213
+			}
214
+		}
215
+		// ensure that "required" is set correctly
216
+		$this->set_required(
217
+			$this->_required, isset($input_args['required_validation_error_message'])
218
+			? $input_args['required_validation_error_message']
219
+			: null
220
+		);
221
+		//$this->_html_name_specified = isset( $input_args['html_name'] ) ? TRUE : FALSE;
222
+		$this->_display_strategy->_construct_finalize($this);
223
+		foreach ($this->_validation_strategies as $validation_strategy) {
224
+			$validation_strategy->_construct_finalize($this);
225
+		}
226
+		if (isset($input_args['ignore_input'])) {
227
+			$this->_normalization_strategy = new EE_Null_Normalization();
228
+		}
229
+		if (! $this->_normalization_strategy) {
230
+				$this->_normalization_strategy = new EE_Text_Normalization();
231
+		}
232
+		$this->_normalization_strategy->_construct_finalize($this);
233
+		//at least we can use the normalization strategy to populate the default
234
+		if (isset($input_args['default'])) {
235
+			$this->set_default($input_args['default']);
236
+			unset($input_args['default']);
237
+		}
238
+		if (! $this->_sensitive_data_removal_strategy) {
239
+			$this->_sensitive_data_removal_strategy = new EE_No_Sensitive_Data_Removal();
240
+		}
241
+		$this->_sensitive_data_removal_strategy->_construct_finalize($this);
242
+		parent::__construct($input_args);
243
+	}
244
+
245
+
246
+
247
+	/**
248
+	 * Sets the html_name to its default value, if none was specified in teh constructor.
249
+	 * Calculation involves using the name and the parent's html_name
250
+	 *
251
+	 * @throws \EE_Error
252
+	 */
253
+	protected function _set_default_html_name_if_empty()
254
+	{
255
+		if (! $this->_html_name) {
256
+			$this->_html_name = $this->name();
257
+			if ($this->_parent_section && $this->_parent_section instanceof EE_Form_Section_Proper) {
258
+				$this->_html_name = $this->_parent_section->html_name_prefix() . "[{$this->name()}]";
259
+			}
260
+		}
261
+	}
262
+
263
+
264
+
265
+	/**
266
+	 * @param $parent_form_section
267
+	 * @param $name
268
+	 * @throws \EE_Error
269
+	 */
270
+	public function _construct_finalize($parent_form_section, $name)
271
+	{
272
+		parent::_construct_finalize($parent_form_section, $name);
273
+		if ($this->_html_label === null && $this->_html_label_text === null) {
274
+			$this->_html_label_text = ucwords(str_replace("_", " ", $name));
275
+		}
276
+		do_action('AHEE__EE_Form_Input_Base___construct_finalize__end', $this, $parent_form_section, $name);
277
+	}
278
+
279
+
280
+
281
+	/**
282
+	 * Returns the strategy for displaying this form input. If none is set, throws an exception.
283
+	 *
284
+	 * @return EE_Display_Strategy_Base
285
+	 * @throws EE_Error
286
+	 */
287
+	protected function _get_display_strategy()
288
+	{
289
+		$this->ensure_construct_finalized_called();
290
+		if (! $this->_display_strategy || ! $this->_display_strategy instanceof EE_Display_Strategy_Base) {
291
+			throw new EE_Error(
292
+				sprintf(
293
+					__(
294
+						"Cannot get display strategy for form input with name %s and id %s, because it has not been set in the constructor",
295
+						"event_espresso"
296
+					),
297
+					$this->html_name(),
298
+					$this->html_id()
299
+				)
300
+			);
301
+		} else {
302
+			return $this->_display_strategy;
303
+		}
304
+	}
305
+
306
+
307
+
308
+	/**
309
+	 * Sets the display strategy.
310
+	 *
311
+	 * @param EE_Display_Strategy_Base $strategy
312
+	 */
313
+	protected function _set_display_strategy(EE_Display_Strategy_Base $strategy)
314
+	{
315
+		$this->_display_strategy = $strategy;
316
+	}
317
+
318
+
319
+
320
+	/**
321
+	 * Sets the sanitization strategy
322
+	 *
323
+	 * @param EE_Normalization_Strategy_Base $strategy
324
+	 */
325
+	protected function _set_normalization_strategy(EE_Normalization_Strategy_Base $strategy)
326
+	{
327
+		$this->_normalization_strategy = $strategy;
328
+	}
329
+
330
+
331
+
332
+	/**
333
+	 * Gets sensitive_data_removal_strategy
334
+	 *
335
+	 * @return EE_Sensitive_Data_Removal_Base
336
+	 */
337
+	public function get_sensitive_data_removal_strategy()
338
+	{
339
+		return $this->_sensitive_data_removal_strategy;
340
+	}
341
+
342
+
343
+
344
+	/**
345
+	 * Sets sensitive_data_removal_strategy
346
+	 *
347
+	 * @param EE_Sensitive_Data_Removal_Base $sensitive_data_removal_strategy
348
+	 * @return boolean
349
+	 */
350
+	public function set_sensitive_data_removal_strategy($sensitive_data_removal_strategy)
351
+	{
352
+		$this->_sensitive_data_removal_strategy = $sensitive_data_removal_strategy;
353
+	}
354
+
355
+
356
+
357
+	/**
358
+	 * Gets the display strategy for this input
359
+	 *
360
+	 * @return EE_Display_Strategy_Base
361
+	 */
362
+	public function get_display_strategy()
363
+	{
364
+		return $this->_display_strategy;
365
+	}
366
+
367
+
368
+
369
+	/**
370
+	 * Overwrites the display strategy
371
+	 *
372
+	 * @param EE_Display_Strategy_Base $display_strategy
373
+	 */
374
+	public function set_display_strategy($display_strategy)
375
+	{
376
+		$this->_display_strategy = $display_strategy;
377
+		$this->_display_strategy->_construct_finalize($this);
378
+	}
379
+
380
+
381
+
382
+	/**
383
+	 * Gets the normalization strategy set on this input
384
+	 *
385
+	 * @return EE_Normalization_Strategy_Base
386
+	 */
387
+	public function get_normalization_strategy()
388
+	{
389
+		return $this->_normalization_strategy;
390
+	}
391
+
392
+
393
+
394
+	/**
395
+	 * Overwrites the normalization strategy
396
+	 *
397
+	 * @param EE_Normalization_Strategy_Base $normalization_strategy
398
+	 */
399
+	public function set_normalization_strategy($normalization_strategy)
400
+	{
401
+		$this->_normalization_strategy = $normalization_strategy;
402
+		$this->_normalization_strategy->_construct_finalize($this);
403
+	}
404
+
405
+
406
+
407
+	/**
408
+	 * Returns all teh validation strategies which apply to this field, numerically indexed
409
+	 *
410
+	 * @return EE_Validation_Strategy_Base[]
411
+	 */
412
+	public function get_validation_strategies()
413
+	{
414
+		return $this->_validation_strategies;
415
+	}
416
+
417
+
418
+
419
+	/**
420
+	 * Adds this strategy to the field so it will be used in both JS validation and server-side validation
421
+	 *
422
+	 * @param EE_Validation_Strategy_Base $validation_strategy
423
+	 * @return void
424
+	 */
425
+	protected function _add_validation_strategy(EE_Validation_Strategy_Base $validation_strategy)
426
+	{
427
+		$validation_strategy->_construct_finalize($this);
428
+		$this->_validation_strategies[] = $validation_strategy;
429
+	}
430
+
431
+
432
+
433
+	/**
434
+	 * Adds a new validation strategy onto the form input
435
+	 *
436
+	 * @param EE_Validation_Strategy_Base $validation_strategy
437
+	 * @return void
438
+	 */
439
+	public function add_validation_strategy(EE_Validation_Strategy_Base $validation_strategy)
440
+	{
441
+		$this->_add_validation_strategy($validation_strategy);
442
+	}
443
+
444
+
445
+
446
+	/**
447
+	 * The classname of the validation strategy to remove
448
+	 *
449
+	 * @param string $validation_strategy_classname
450
+	 */
451
+	public function remove_validation_strategy($validation_strategy_classname)
452
+	{
453
+		foreach ($this->_validation_strategies as $key => $validation_strategy) {
454
+			if (
455
+				$validation_strategy instanceof $validation_strategy_classname
456
+				|| is_subclass_of($validation_strategy, $validation_strategy_classname)
457
+			) {
458
+				unset($this->_validation_strategies[$key]);
459
+			}
460
+		}
461
+	}
462
+
463
+
464
+
465
+	/**
466
+	 * returns true if input employs any of the validation strategy defined by the supplied array of classnames
467
+	 *
468
+	 * @param array $validation_strategy_classnames
469
+	 * @return bool
470
+	 */
471
+	public function has_validation_strategy($validation_strategy_classnames)
472
+	{
473
+		$validation_strategy_classnames = is_array($validation_strategy_classnames)
474
+			? $validation_strategy_classnames
475
+			: array($validation_strategy_classnames);
476
+		foreach ($this->_validation_strategies as $key => $validation_strategy) {
477
+			if (in_array($key, $validation_strategy_classnames)) {
478
+				return true;
479
+			}
480
+		}
481
+		return false;
482
+	}
483
+
484
+
485
+
486
+	/**
487
+	 * Gets the HTML
488
+	 *
489
+	 * @return string
490
+	 */
491
+	public function get_html()
492
+	{
493
+		return $this->_parent_section->get_html_for_input($this);
494
+	}
495
+
496
+
497
+
498
+	/**
499
+	 * Gets the HTML for the input itself (no label or errors) according to the
500
+	 * input's display strategy
501
+	 * Makes sure the JS and CSS are enqueued for it
502
+	 *
503
+	 * @return string
504
+	 * @throws \EE_Error
505
+	 */
506
+	public function get_html_for_input()
507
+	{
508
+		return $this->_form_html_filter
509
+			? $this->_form_html_filter->filterHtml(
510
+				$this->_get_display_strategy()->display(),
511
+				$this
512
+			)
513
+			: $this->_get_display_strategy()->display();
514
+	}
515
+
516
+
517
+
518
+	/**
519
+	 * @return string
520
+	 */
521
+	public function html_other_attributes()
522
+	{
523
+		return ! empty($this->_html_other_attributes) ? ' ' . $this->_html_other_attributes : '';
524
+	}
525
+
526
+
527
+
528
+	/**
529
+	 * @param string $html_other_attributes
530
+	 */
531
+	public function set_html_other_attributes($html_other_attributes)
532
+	{
533
+		$this->_html_other_attributes = $html_other_attributes;
534
+	}
535
+
536
+
537
+
538
+	/**
539
+	 * Gets the HTML for displaying the label for this form input
540
+	 * according to the form section's layout strategy
541
+	 *
542
+	 * @return string
543
+	 */
544
+	public function get_html_for_label()
545
+	{
546
+		return $this->_parent_section->get_layout_strategy()->display_label($this);
547
+	}
548
+
549
+
550
+
551
+	/**
552
+	 * Gets the HTML for displaying the errors section for this form input
553
+	 * according to the form section's layout strategy
554
+	 *
555
+	 * @return string
556
+	 */
557
+	public function get_html_for_errors()
558
+	{
559
+		return $this->_parent_section->get_layout_strategy()->display_errors($this);
560
+	}
561
+
562
+
563
+
564
+	/**
565
+	 * Gets the HTML for displaying the help text for this form input
566
+	 * according to the form section's layout strategy
567
+	 *
568
+	 * @return string
569
+	 */
570
+	public function get_html_for_help()
571
+	{
572
+		return $this->_parent_section->get_layout_strategy()->display_help_text($this);
573
+	}
574
+
575
+
576
+
577
+	/**
578
+	 * Validates the input's sanitized value (assumes _sanitize() has already been called)
579
+	 * and returns whether or not the form input's submitted value is value
580
+	 *
581
+	 * @return boolean
582
+	 */
583
+	protected function _validate()
584
+	{
585
+		foreach ($this->_validation_strategies as $validation_strategy) {
586
+			if ($validation_strategy instanceof EE_Validation_Strategy_Base) {
587
+				try {
588
+					$validation_strategy->validate($this->normalized_value());
589
+				} catch (EE_Validation_Error $e) {
590
+					$this->add_validation_error($e);
591
+				}
592
+			}
593
+		}
594
+		if ($this->get_validation_errors()) {
595
+			return false;
596
+		} else {
597
+			return true;
598
+		}
599
+	}
600
+
601
+
602
+
603
+	/**
604
+	 * Performs basic sanitization on this value. But what sanitization can be performed anyways?
605
+	 * This value MIGHT be allowed to have tags, so we can't really remove them.
606
+	 *
607
+	 * @param string $value
608
+	 * @return null|string
609
+	 */
610
+	private function _sanitize($value)
611
+	{
612
+		return $value !== null ? stripslashes(html_entity_decode(trim($value))) : null;
613
+	}
614
+
615
+
616
+
617
+	/**
618
+	 * Picks out the form value that relates to this form input,
619
+	 * and stores it as the sanitized value on the form input, and sets the normalized value.
620
+	 * Returns whether or not any validation errors occurred
621
+	 *
622
+	 * @param array $req_data like $_POST
623
+	 * @return boolean whether or not there was an error
624
+	 * @throws \EE_Error
625
+	 */
626
+	protected function _normalize($req_data)
627
+	{
628
+		//any existing validation errors don't apply so clear them
629
+		$this->_validation_errors = array();
630
+		try {
631
+			$raw_input = $this->find_form_data_for_this_section($req_data);
632
+			//super simple sanitization for now
633
+			if (is_array($raw_input)) {
634
+				$raw_value = array();
635
+				foreach ($raw_input as $key => $value) {
636
+					$raw_value[$key] = $this->_sanitize($value);
637
+				}
638
+				$this->_set_raw_value($raw_value);
639
+			} else {
640
+				$this->_set_raw_value($this->_sanitize($raw_input));
641
+			}
642
+			//we want to mostly leave the input alone in case we need to re-display it to the user
643
+			$this->_set_normalized_value($this->_normalization_strategy->normalize($this->raw_value()));
644
+		} catch (EE_Validation_Error $e) {
645
+			$this->add_validation_error($e);
646
+		}
647
+	}
648
+
649
+
650
+
651
+	/**
652
+	 * @return string
653
+	 */
654
+	public function html_name()
655
+	{
656
+		$this->_set_default_html_name_if_empty();
657
+		return $this->_html_name;
658
+	}
659
+
660
+
661
+
662
+	/**
663
+	 * @return string
664
+	 */
665
+	public function html_label_id()
666
+	{
667
+		return ! empty($this->_html_label_id) ? $this->_html_label_id : $this->html_id() . '-lbl';
668
+	}
669
+
670
+
671
+
672
+	/**
673
+	 * @return string
674
+	 */
675
+	public function html_label_class()
676
+	{
677
+		return $this->_html_label_class;
678
+	}
679
+
680
+
681
+
682
+	/**
683
+	 * @return string
684
+	 */
685
+	public function html_label_style()
686
+	{
687
+		return $this->_html_label_style;
688
+	}
689
+
690
+
691
+
692
+	/**
693
+	 * @return string
694
+	 */
695
+	public function html_label_text()
696
+	{
697
+		return $this->_html_label_text;
698
+	}
699
+
700
+
701
+
702
+	/**
703
+	 * @return string
704
+	 */
705
+	public function html_help_text()
706
+	{
707
+		return $this->_html_help_text;
708
+	}
709
+
710
+
711
+
712
+	/**
713
+	 * @return string
714
+	 */
715
+	public function html_help_class()
716
+	{
717
+		return $this->_html_help_class;
718
+	}
719
+
720
+
721
+
722
+	/**
723
+	 * @return string
724
+	 */
725
+	public function html_help_style()
726
+	{
727
+		return $this->_html_style;
728
+	}
729
+
730
+
731
+
732
+	/**
733
+	 * returns the raw, UNSAFE, input, almost exactly as the user submitted it.
734
+	 * Please note that almost all client code should instead use the normalized_value;
735
+	 * or possibly raw_value_in_form (which prepares the string for displaying in an HTML attribute on a tag,
736
+	 * mostly by escaping quotes)
737
+	 * Note, we do not store the exact original value sent in the user's request because
738
+	 * it may have malicious content, and we MIGHT want to store the form input in a transient or something...
739
+	 * in which case, we would have stored the malicious content to our database.
740
+	 *
741
+	 * @return string
742
+	 */
743
+	public function raw_value()
744
+	{
745
+		return $this->_raw_value;
746
+	}
747
+
748
+
749
+
750
+	/**
751
+	 * Returns a string safe to usage in form inputs when displaying, because
752
+	 * it escapes all html entities
753
+	 *
754
+	 * @return string
755
+	 */
756
+	public function raw_value_in_form()
757
+	{
758
+		return htmlentities($this->raw_value(), ENT_QUOTES, 'UTF-8');
759
+	}
760
+
761
+
762
+
763
+	/**
764
+	 * returns the value after it's been sanitized, and then converted into it's proper type
765
+	 * in PHP. Eg, a string, an int, an array,
766
+	 *
767
+	 * @return mixed
768
+	 */
769
+	public function normalized_value()
770
+	{
771
+		return $this->_normalized_value;
772
+	}
773
+
774
+
775
+
776
+	/**
777
+	 * Returns the normalized value is a presentable way. By default this is just
778
+	 * the normalized value by itself, but it can be overridden for when that's not
779
+	 * the best thing to display
780
+	 *
781
+	 * @return string
782
+	 */
783
+	public function pretty_value()
784
+	{
785
+		return $this->_normalized_value;
786
+	}
787
+
788
+
789
+
790
+	/**
791
+	 * When generating the JS for the jquery validation rules like<br>
792
+	 * <code>$( "#myform" ).validate({
793
+	 * rules: {
794
+	 * password: "required",
795
+	 * password_again: {
796
+	 * equalTo: "#password"
797
+	 * }
798
+	 * }
799
+	 * });</code>
800
+	 * if this field had the name 'password_again', it should return
801
+	 * <br><code>password_again: {
802
+	 * equalTo: "#password"
803
+	 * }</code>
804
+	 *
805
+	 * @return array
806
+	 */
807
+	public function get_jquery_validation_rules()
808
+	{
809
+		$jquery_validation_js = array();
810
+		$jquery_validation_rules = array();
811
+		foreach ($this->get_validation_strategies() as $validation_strategy) {
812
+			$jquery_validation_rules = array_replace_recursive(
813
+				$jquery_validation_rules,
814
+				$validation_strategy->get_jquery_validation_rule_array()
815
+			);
816
+		}
817
+		if (! empty($jquery_validation_rules)) {
818
+			foreach ($this->get_display_strategy()->get_html_input_ids(true) as $html_id_with_pound_sign) {
819
+				$jquery_validation_js[$html_id_with_pound_sign] = $jquery_validation_rules;
820
+			}
821
+		}
822
+		return $jquery_validation_js;
823
+	}
824
+
825
+
826
+
827
+	/**
828
+	 * Sets the input's default value for use in displaying in the form. Note: value should be
829
+	 * normalized (Eg, if providing a default of ra Yes_NO_Input you would provide TRUE or FALSE, not '1' or '0')
830
+	 *
831
+	 * @param mixed $value
832
+	 * @return void
833
+	 */
834
+	public function set_default($value)
835
+	{
836
+		$this->_default = $value;
837
+		$this->_set_normalized_value($value);
838
+		$this->_set_raw_value($value);
839
+	}
840
+
841
+
842
+
843
+	/**
844
+	 * Sets the normalized value on this input
845
+	 *
846
+	 * @param mixed $value
847
+	 */
848
+	protected function _set_normalized_value($value)
849
+	{
850
+		$this->_normalized_value = $value;
851
+	}
852
+
853
+
854
+
855
+	/**
856
+	 * Sets the raw value on this input (ie, exactly as the user submitted it)
857
+	 *
858
+	 * @param mixed $value
859
+	 */
860
+	protected function _set_raw_value($value)
861
+	{
862
+		$this->_raw_value = $this->_normalization_strategy->unnormalize($value);
863
+	}
864
+
865
+
866
+
867
+	/**
868
+	 * Sets the HTML label text after it has already been defined
869
+	 *
870
+	 * @param string $label
871
+	 * @return void
872
+	 */
873
+	public function set_html_label_text($label)
874
+	{
875
+		$this->_html_label_text = $label;
876
+	}
877
+
878
+
879
+
880
+	/**
881
+	 * Sets whether or not this field is required, and adjusts the validation strategy.
882
+	 * If you want to use the EE_Conditionally_Required_Validation_Strategy,
883
+	 * please add it as a validation strategy using add_validation_strategy as normal
884
+	 *
885
+	 * @param boolean $required boolean
886
+	 * @param null    $required_text
887
+	 */
888
+	public function set_required($required = true, $required_text = null)
889
+	{
890
+		$required = filter_var($required, FILTER_VALIDATE_BOOLEAN);
891
+		//whether $required is a string or a boolean, we want to add a required validation strategy
892
+		if ($required) {
893
+			$this->_add_validation_strategy(new EE_Required_Validation_Strategy($required_text));
894
+		} else {
895
+			$this->remove_validation_strategy('EE_Required_Validation_Strategy');
896
+		}
897
+		$this->_required = $required;
898
+	}
899
+
900
+
901
+
902
+	/**
903
+	 * Returns whether or not this field is required
904
+	 *
905
+	 * @return boolean
906
+	 */
907
+	public function required()
908
+	{
909
+		return $this->_required;
910
+	}
911
+
912
+
913
+
914
+	/**
915
+	 * @param string $required_css_class
916
+	 */
917
+	public function set_required_css_class($required_css_class)
918
+	{
919
+		$this->_required_css_class = $required_css_class;
920
+	}
921
+
922
+
923
+
924
+	/**
925
+	 * @return string
926
+	 */
927
+	public function required_css_class()
928
+	{
929
+		return $this->_required_css_class;
930
+	}
931
+
932
+
933
+
934
+	/**
935
+	 * @param bool $add_required
936
+	 * @return string
937
+	 */
938
+	public function html_class($add_required = false)
939
+	{
940
+		return $add_required && $this->required()
941
+			? $this->required_css_class() . ' ' . $this->_html_class
942
+			: $this->_html_class;
943
+	}
944
+
945
+
946
+	/**
947
+	 * Sets the help text, in case
948
+	 *
949
+	 * @param string $text
950
+	 */
951
+	public function set_html_help_text($text)
952
+	{
953
+		$this->_html_help_text = $text;
954
+	}
955
+
956
+
957
+
958
+	/**
959
+	 * Uses the sensitive data removal strategy to remove the sensitive data from this
960
+	 * input. If there is any kind of sensitive data removal on this input, we clear
961
+	 * out the raw value completely
962
+	 *
963
+	 * @return void
964
+	 */
965
+	public function clean_sensitive_data()
966
+	{
967
+		//if we do ANY kind of sensitive data removal on this, then just clear out the raw value
968
+		//if we need more logic than this we'll make a strategy for it
969
+		if ($this->_sensitive_data_removal_strategy
970
+			&& ! $this->_sensitive_data_removal_strategy instanceof EE_No_Sensitive_Data_Removal
971
+		) {
972
+			$this->_set_raw_value(null);
973
+		}
974
+		//and clean the normalized value according to the appropriate strategy
975
+		$this->_set_normalized_value(
976
+			$this->get_sensitive_data_removal_strategy()->remove_sensitive_data(
977
+				$this->_normalized_value
978
+			)
979
+		);
980
+	}
981
+
982
+
983
+
984
+	/**
985
+	 * @param bool   $primary
986
+	 * @param string $button_size
987
+	 * @param string $other_attributes
988
+	 */
989
+	public function set_button_css_attributes($primary = true, $button_size = '', $other_attributes = '')
990
+	{
991
+		$button_css_attributes = 'button';
992
+		$button_css_attributes .= $primary === true ? ' button-primary' : ' button-secondary';
993
+		switch ($button_size) {
994
+			case 'xs' :
995
+			case 'extra-small' :
996
+				$button_css_attributes .= ' button-xs';
997
+				break;
998
+			case 'sm' :
999
+			case 'small' :
1000
+				$button_css_attributes .= ' button-sm';
1001
+				break;
1002
+			case 'lg' :
1003
+			case 'large' :
1004
+				$button_css_attributes .= ' button-lg';
1005
+				break;
1006
+			case 'block' :
1007
+				$button_css_attributes .= ' button-block';
1008
+				break;
1009
+			case 'md' :
1010
+			case 'medium' :
1011
+			default :
1012
+				$button_css_attributes .= '';
1013
+		}
1014
+		$this->_button_css_attributes .= ! empty($other_attributes)
1015
+			? $button_css_attributes . ' ' . $other_attributes
1016
+			: $button_css_attributes;
1017
+	}
1018
+
1019
+
1020
+
1021
+	/**
1022
+	 * @return string
1023
+	 */
1024
+	public function button_css_attributes()
1025
+	{
1026
+		if (empty($this->_button_css_attributes)) {
1027
+			$this->set_button_css_attributes();
1028
+		}
1029
+		return $this->_button_css_attributes;
1030
+	}
1031
+
1032
+
1033
+
1034
+	/**
1035
+	 * find_form_data_for_this_section
1036
+	 * using this section's name and its parents, finds the value of the form data that corresponds to it.
1037
+	 * For example, if this form section's HTML name is my_form[subform][form_input_1],
1038
+	 * then it's value should be in $_REQUEST at $_REQUEST['my_form']['subform']['form_input_1'].
1039
+	 * (If that doesn't exist, we also check for this subsection's name
1040
+	 * at the TOP LEVEL of the request data. Eg $_REQUEST['form_input_1'].)
1041
+	 * This function finds its value in the form.
1042
+	 *
1043
+	 * @param array $req_data
1044
+	 * @return mixed whatever the raw value of this form section is in the request data
1045
+	 * @throws \EE_Error
1046
+	 */
1047
+	public function find_form_data_for_this_section($req_data)
1048
+	{
1049
+		// break up the html name by "[]"
1050
+		if (strpos($this->html_name(), '[') !== false) {
1051
+			$before_any_brackets = substr($this->html_name(), 0, strpos($this->html_name(), '['));
1052
+		} else {
1053
+			$before_any_brackets = $this->html_name();
1054
+		}
1055
+		// grab all of the segments
1056
+		preg_match_all('~\[([^]]*)\]~', $this->html_name(), $matches);
1057
+		if (isset($matches[1]) && is_array($matches[1])) {
1058
+			$name_parts = $matches[1];
1059
+			array_unshift($name_parts, $before_any_brackets);
1060
+		} else {
1061
+			$name_parts = array($before_any_brackets);
1062
+		}
1063
+		// now get the value for the input
1064
+		$value = $this->_find_form_data_for_this_section_using_name_parts($name_parts, $req_data);
1065
+		// check if this thing's name is at the TOP level of the request data
1066
+		if ($value === null && isset($req_data[$this->name()])) {
1067
+			$value = $req_data[$this->name()];
1068
+		}
1069
+		return $value;
1070
+	}
1071
+
1072
+
1073
+
1074
+	/**
1075
+	 * @param array $html_name_parts
1076
+	 * @param array $req_data
1077
+	 * @return array | NULL
1078
+	 */
1079
+	public function _find_form_data_for_this_section_using_name_parts($html_name_parts, $req_data)
1080
+	{
1081
+		$first_part_to_consider = array_shift($html_name_parts);
1082
+		if (isset($req_data[$first_part_to_consider])) {
1083
+			if (empty($html_name_parts)) {
1084
+				return $req_data[$first_part_to_consider];
1085
+			} else {
1086
+				return $this->_find_form_data_for_this_section_using_name_parts(
1087
+					$html_name_parts,
1088
+					$req_data[$first_part_to_consider]
1089
+				);
1090
+			}
1091
+		} else {
1092
+			return null;
1093
+		}
1094
+	}
1095
+
1096
+
1097
+
1098
+	/**
1099
+	 * Checks if this form input's data is in the request data
1100
+	 *
1101
+	 * @param array $req_data like $_POST
1102
+	 * @return boolean
1103
+	 * @throws \EE_Error
1104
+	 */
1105
+	public function form_data_present_in($req_data = null)
1106
+	{
1107
+		if ($req_data === null) {
1108
+			$req_data = $_POST;
1109
+		}
1110
+		$checked_value = $this->find_form_data_for_this_section($req_data);
1111
+		if ($checked_value !== null) {
1112
+			return true;
1113
+		} else {
1114
+			return false;
1115
+		}
1116
+	}
1117
+
1118
+
1119
+
1120
+	/**
1121
+	 * Overrides parent to add js data from validation and display strategies
1122
+	 *
1123
+	 * @param array $form_other_js_data
1124
+	 * @return array
1125
+	 */
1126
+	public function get_other_js_data($form_other_js_data = array())
1127
+	{
1128
+		$form_other_js_data = $this->get_other_js_data_from_strategies($form_other_js_data);
1129
+		return $form_other_js_data;
1130
+	}
1131
+
1132
+
1133
+
1134
+	/**
1135
+	 * Gets other JS data for localization from this input's strategies, like
1136
+	 * the validation strategies and the display strategy
1137
+	 *
1138
+	 * @param array $form_other_js_data
1139
+	 * @return array
1140
+	 */
1141
+	public function get_other_js_data_from_strategies($form_other_js_data = array())
1142
+	{
1143
+		$form_other_js_data = $this->get_display_strategy()->get_other_js_data($form_other_js_data);
1144
+		foreach ($this->get_validation_strategies() as $validation_strategy) {
1145
+			$form_other_js_data = $validation_strategy->get_other_js_data($form_other_js_data);
1146
+		}
1147
+		return $form_other_js_data;
1148
+	}
1149
+
1150
+
1151
+
1152
+	/**
1153
+	 * Override parent because we want to give our strategies an opportunity to enqueue some js and css
1154
+	 *
1155
+	 * @return void
1156
+	 */
1157
+	public function enqueue_js()
1158
+	{
1159
+		//ask our display strategy and validation strategies if they have js to enqueue
1160
+		$this->enqueue_js_from_strategies();
1161
+	}
1162
+
1163
+
1164
+
1165
+	/**
1166
+	 * Tells strategies when its ok to enqueue their js and css
1167
+	 *
1168
+	 * @return void
1169
+	 */
1170
+	public function enqueue_js_from_strategies()
1171
+	{
1172
+		$this->get_display_strategy()->enqueue_js();
1173
+		foreach ($this->get_validation_strategies() as $validation_strategy) {
1174
+			$validation_strategy->enqueue_js();
1175
+		}
1176
+	}
1177
+
1178
+
1179
+
1180
+	/**
1181
+	 * Gets the default value set on the input (not the current value, which may have been
1182
+	 * changed because of a form submission). If no default was set, this us null.
1183
+	 * @return mixed
1184
+	 */
1185
+	public function get_default()
1186
+	{
1187
+		return $this->_default;
1188
+	}
1189 1189
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -1,4 +1,4 @@  discard block
 block discarded – undo
1
-<?php if (! defined('EVENT_ESPRESSO_VERSION')) {
1
+<?php if ( ! defined('EVENT_ESPRESSO_VERSION')) {
2 2
     exit('No direct script access allowed');
3 3
 }
4 4
 
@@ -191,23 +191,23 @@  discard block
 block discarded – undo
191 191
      */
192 192
     public function __construct($input_args = array())
193 193
     {
194
-        $input_args = (array)apply_filters('FHEE__EE_Form_Input_Base___construct__input_args', $input_args, $this);
194
+        $input_args = (array) apply_filters('FHEE__EE_Form_Input_Base___construct__input_args', $input_args, $this);
195 195
         // the following properties must be cast as arrays
196 196
         if (isset($input_args['validation_strategies'])) {
197
-            foreach ((array)$input_args['validation_strategies'] as $validation_strategy) {
197
+            foreach ((array) $input_args['validation_strategies'] as $validation_strategy) {
198 198
                 if ($validation_strategy instanceof EE_Validation_Strategy_Base && empty($input_args['ignore_input'])) {
199 199
                     $this->_validation_strategies[get_class($validation_strategy)] = $validation_strategy;
200 200
                 }
201 201
             }
202 202
             unset($input_args['validation_strategies']);
203 203
         }
204
-        if(isset($input_args['ignore_input'])) {
204
+        if (isset($input_args['ignore_input'])) {
205 205
             $this->_validation_strategies = array();
206 206
         }
207 207
         // loop thru incoming options
208 208
         foreach ($input_args as $key => $value) {
209 209
             // add underscore to $key to match property names
210
-            $_key = '_' . $key;
210
+            $_key = '_'.$key;
211 211
             if (property_exists($this, $_key)) {
212 212
                 $this->{$_key} = $value;
213 213
             }
@@ -226,7 +226,7 @@  discard block
 block discarded – undo
226 226
         if (isset($input_args['ignore_input'])) {
227 227
             $this->_normalization_strategy = new EE_Null_Normalization();
228 228
         }
229
-        if (! $this->_normalization_strategy) {
229
+        if ( ! $this->_normalization_strategy) {
230 230
                 $this->_normalization_strategy = new EE_Text_Normalization();
231 231
         }
232 232
         $this->_normalization_strategy->_construct_finalize($this);
@@ -235,7 +235,7 @@  discard block
 block discarded – undo
235 235
             $this->set_default($input_args['default']);
236 236
             unset($input_args['default']);
237 237
         }
238
-        if (! $this->_sensitive_data_removal_strategy) {
238
+        if ( ! $this->_sensitive_data_removal_strategy) {
239 239
             $this->_sensitive_data_removal_strategy = new EE_No_Sensitive_Data_Removal();
240 240
         }
241 241
         $this->_sensitive_data_removal_strategy->_construct_finalize($this);
@@ -252,10 +252,10 @@  discard block
 block discarded – undo
252 252
      */
253 253
     protected function _set_default_html_name_if_empty()
254 254
     {
255
-        if (! $this->_html_name) {
255
+        if ( ! $this->_html_name) {
256 256
             $this->_html_name = $this->name();
257 257
             if ($this->_parent_section && $this->_parent_section instanceof EE_Form_Section_Proper) {
258
-                $this->_html_name = $this->_parent_section->html_name_prefix() . "[{$this->name()}]";
258
+                $this->_html_name = $this->_parent_section->html_name_prefix()."[{$this->name()}]";
259 259
             }
260 260
         }
261 261
     }
@@ -287,7 +287,7 @@  discard block
 block discarded – undo
287 287
     protected function _get_display_strategy()
288 288
     {
289 289
         $this->ensure_construct_finalized_called();
290
-        if (! $this->_display_strategy || ! $this->_display_strategy instanceof EE_Display_Strategy_Base) {
290
+        if ( ! $this->_display_strategy || ! $this->_display_strategy instanceof EE_Display_Strategy_Base) {
291 291
             throw new EE_Error(
292 292
                 sprintf(
293 293
                     __(
@@ -520,7 +520,7 @@  discard block
 block discarded – undo
520 520
      */
521 521
     public function html_other_attributes()
522 522
     {
523
-        return ! empty($this->_html_other_attributes) ? ' ' . $this->_html_other_attributes : '';
523
+        return ! empty($this->_html_other_attributes) ? ' '.$this->_html_other_attributes : '';
524 524
     }
525 525
 
526 526
 
@@ -664,7 +664,7 @@  discard block
 block discarded – undo
664 664
      */
665 665
     public function html_label_id()
666 666
     {
667
-        return ! empty($this->_html_label_id) ? $this->_html_label_id : $this->html_id() . '-lbl';
667
+        return ! empty($this->_html_label_id) ? $this->_html_label_id : $this->html_id().'-lbl';
668 668
     }
669 669
 
670 670
 
@@ -814,7 +814,7 @@  discard block
 block discarded – undo
814 814
                 $validation_strategy->get_jquery_validation_rule_array()
815 815
             );
816 816
         }
817
-        if (! empty($jquery_validation_rules)) {
817
+        if ( ! empty($jquery_validation_rules)) {
818 818
             foreach ($this->get_display_strategy()->get_html_input_ids(true) as $html_id_with_pound_sign) {
819 819
                 $jquery_validation_js[$html_id_with_pound_sign] = $jquery_validation_rules;
820 820
             }
@@ -938,7 +938,7 @@  discard block
 block discarded – undo
938 938
     public function html_class($add_required = false)
939 939
     {
940 940
         return $add_required && $this->required()
941
-            ? $this->required_css_class() . ' ' . $this->_html_class
941
+            ? $this->required_css_class().' '.$this->_html_class
942 942
             : $this->_html_class;
943 943
     }
944 944
 
@@ -1012,7 +1012,7 @@  discard block
 block discarded – undo
1012 1012
                 $button_css_attributes .= '';
1013 1013
         }
1014 1014
         $this->_button_css_attributes .= ! empty($other_attributes)
1015
-            ? $button_css_attributes . ' ' . $other_attributes
1015
+            ? $button_css_attributes.' '.$other_attributes
1016 1016
             : $button_css_attributes;
1017 1017
     }
1018 1018
 
Please login to merge, or discard this patch.
core/libraries/form_sections/base/EE_Form_Section_Proper.form.php 3 patches
Doc Comments   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -459,7 +459,7 @@  discard block
 block discarded – undo
459 459
      *                                                      with construction finalize being called later
460 460
      *                                                      (realizing that the subsections' html names
461 461
      *                                                      might not be set yet, etc.)
462
-     * @return EE_Form_Section_Base
462
+     * @return EE_Form_Section_Validatable|null
463 463
      * @throws EE_Error
464 464
      */
465 465
     public function get_subsection($name, $require_construction_to_be_finalized = true)
@@ -1289,7 +1289,6 @@  discard block
 block discarded – undo
1289 1289
     /**
1290 1290
      * Sets the submission error message (aka validation error message for this form section and all sub-sections)
1291 1291
      * @param string                           $form_submission_error_message
1292
-     * @param EE_Form_Section_Validatable $form_section unused
1293 1292
      * @throws EE_Error
1294 1293
      */
1295 1294
     public function set_submission_error_message(
Please login to merge, or discard this patch.
Spacing   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -111,8 +111,8 @@  discard block
 block discarded – undo
111 111
             //AND we are going to make sure they're in that specified order
112 112
             $reordered_subsections = array();
113 113
             foreach ($options_array['include'] as $input_name) {
114
-                if (isset($this->_subsections[ $input_name ])) {
115
-                    $reordered_subsections[ $input_name ] = $this->_subsections[ $input_name ];
114
+                if (isset($this->_subsections[$input_name])) {
115
+                    $reordered_subsections[$input_name] = $this->_subsections[$input_name];
116 116
                 }
117 117
             }
118 118
             $this->_subsections = $reordered_subsections;
@@ -124,7 +124,7 @@  discard block
 block discarded – undo
124 124
         if (isset($options_array['layout_strategy'])) {
125 125
             $this->_layout_strategy = $options_array['layout_strategy'];
126 126
         }
127
-        if (! $this->_layout_strategy) {
127
+        if ( ! $this->_layout_strategy) {
128 128
             $this->_layout_strategy = is_admin() ? new EE_Admin_Two_Column_Layout() : new EE_Two_Column_Layout();
129 129
         }
130 130
         $this->_layout_strategy->_construct_finalize($this);
@@ -277,7 +277,7 @@  discard block
 block discarded – undo
277 277
                 $req_data,
278 278
                 $this
279 279
             );
280
-            $this->cached_request_data = (array)$req_data;
280
+            $this->cached_request_data = (array) $req_data;
281 281
         }
282 282
         return $this->cached_request_data;
283 283
     }
@@ -313,7 +313,7 @@  discard block
 block discarded – undo
313 313
         if ($validate) {
314 314
             $this->_validate();
315 315
             //if it's invalid, we're going to want to re-display so remember what they submitted
316
-            if (! $this->is_valid()) {
316
+            if ( ! $this->is_valid()) {
317 317
                 $this->store_submitted_form_data_in_session();
318 318
             }
319 319
         }
@@ -426,11 +426,11 @@  discard block
 block discarded – undo
426 426
     public function populate_defaults($default_data)
427 427
     {
428 428
         foreach ($this->subsections(false) as $subsection_name => $subsection) {
429
-            if (isset($default_data[ $subsection_name ])) {
429
+            if (isset($default_data[$subsection_name])) {
430 430
                 if ($subsection instanceof EE_Form_Input_Base) {
431
-                    $subsection->set_default($default_data[ $subsection_name ]);
431
+                    $subsection->set_default($default_data[$subsection_name]);
432 432
                 } elseif ($subsection instanceof EE_Form_Section_Proper) {
433
-                    $subsection->populate_defaults($default_data[ $subsection_name ]);
433
+                    $subsection->populate_defaults($default_data[$subsection_name]);
434 434
                 }
435 435
             }
436 436
         }
@@ -445,7 +445,7 @@  discard block
 block discarded – undo
445 445
      */
446 446
     public function subsection_exists($name)
447 447
     {
448
-        return isset($this->_subsections[ $name ]) ? true : false;
448
+        return isset($this->_subsections[$name]) ? true : false;
449 449
     }
450 450
 
451 451
 
@@ -467,7 +467,7 @@  discard block
 block discarded – undo
467 467
         if ($require_construction_to_be_finalized) {
468 468
             $this->ensure_construct_finalized_called();
469 469
         }
470
-        return $this->subsection_exists($name) ? $this->_subsections[ $name ] : null;
470
+        return $this->subsection_exists($name) ? $this->_subsections[$name] : null;
471 471
     }
472 472
 
473 473
 
@@ -482,7 +482,7 @@  discard block
 block discarded – undo
482 482
         $validatable_subsections = array();
483 483
         foreach ($this->subsections() as $name => $obj) {
484 484
             if ($obj instanceof EE_Form_Section_Validatable) {
485
-                $validatable_subsections[ $name ] = $obj;
485
+                $validatable_subsections[$name] = $obj;
486 486
             }
487 487
         }
488 488
         return $validatable_subsections;
@@ -509,7 +509,7 @@  discard block
 block discarded – undo
509 509
             $name,
510 510
             $require_construction_to_be_finalized
511 511
         );
512
-        if (! $subsection instanceof EE_Form_Input_Base) {
512
+        if ( ! $subsection instanceof EE_Form_Input_Base) {
513 513
             throw new EE_Error(
514 514
                 sprintf(
515 515
                     esc_html__(
@@ -546,7 +546,7 @@  discard block
 block discarded – undo
546 546
             $name,
547 547
             $require_construction_to_be_finalized
548 548
         );
549
-        if (! $subsection instanceof EE_Form_Section_Proper) {
549
+        if ( ! $subsection instanceof EE_Form_Section_Proper) {
550 550
             throw new EE_Error(
551 551
                 sprintf(
552 552
                     esc_html__(
@@ -585,8 +585,8 @@  discard block
 block discarded – undo
585 585
      */
586 586
     public function is_valid()
587 587
     {
588
-        if($this->is_valid === null) {
589
-            if (! $this->has_received_submission()) {
588
+        if ($this->is_valid === null) {
589
+            if ( ! $this->has_received_submission()) {
590 590
                 throw new EE_Error(
591 591
                     sprintf(
592 592
                         esc_html__(
@@ -596,14 +596,14 @@  discard block
 block discarded – undo
596 596
                     )
597 597
                 );
598 598
             }
599
-            if (! parent::is_valid()) {
599
+            if ( ! parent::is_valid()) {
600 600
                 $this->is_valid = false;
601 601
             } else {
602 602
                 // ok so no general errors to this entire form section.
603 603
                 // so let's check the subsections, but only set errors if that hasn't been done yet
604 604
                 $this->is_valid = true;
605 605
                 foreach ($this->get_validatable_subsections() as $subsection) {
606
-                    if (! $subsection->is_valid()) {
606
+                    if ( ! $subsection->is_valid()) {
607 607
                         $this->is_valid = false;
608 608
                     }
609 609
                 }
@@ -620,7 +620,7 @@  discard block
 block discarded – undo
620 620
      */
621 621
     protected function _set_default_name_if_empty()
622 622
     {
623
-        if (! $this->_name) {
623
+        if ( ! $this->_name) {
624 624
             $classname    = get_class($this);
625 625
             $default_name = str_replace('EE_', '', $classname);
626 626
             $this->_name  = $default_name;
@@ -710,7 +710,7 @@  discard block
 block discarded – undo
710 710
     {
711 711
         wp_register_script(
712 712
             'ee_form_section_validation',
713
-            EE_GLOBAL_ASSETS_URL . 'scripts' . DS . 'form_section_validation.js',
713
+            EE_GLOBAL_ASSETS_URL.'scripts'.DS.'form_section_validation.js',
714 714
             array('jquery-validate', 'jquery-ui-datepicker', 'jquery-validate-extra-methods'),
715 715
             EVENT_ESPRESSO_VERSION,
716 716
             true
@@ -754,13 +754,13 @@  discard block
 block discarded – undo
754 754
         // we only want to localize vars ONCE for the entire form,
755 755
         // so if the form section doesn't have a parent, then it must be the top dog
756 756
         if ($return_for_subsection || ! $this->parent_section()) {
757
-            EE_Form_Section_Proper::$_js_localization['form_data'][ $this->html_id() ] = array(
757
+            EE_Form_Section_Proper::$_js_localization['form_data'][$this->html_id()] = array(
758 758
                 'form_section_id'  => $this->html_id(true),
759 759
                 'validation_rules' => $this->get_jquery_validation_rules(),
760 760
                 'other_data'       => $this->get_other_js_data(),
761 761
                 'errors'           => $this->subsection_validation_errors_by_html_name(),
762 762
             );
763
-            EE_Form_Section_Proper::$_scripts_localized                                = true;
763
+            EE_Form_Section_Proper::$_scripts_localized = true;
764 764
         }
765 765
     }
766 766
 
@@ -795,7 +795,7 @@  discard block
 block discarded – undo
795 795
         $inputs = array();
796 796
         foreach ($this->subsections() as $subsection) {
797 797
             if ($subsection instanceof EE_Form_Input_Base) {
798
-                $inputs[ $subsection->html_name() ] = $subsection;
798
+                $inputs[$subsection->html_name()] = $subsection;
799 799
             } elseif ($subsection instanceof EE_Form_Section_Proper) {
800 800
                 $inputs += $subsection->inputs_in_subsections();
801 801
             }
@@ -818,7 +818,7 @@  discard block
 block discarded – undo
818 818
         $errors = array();
819 819
         foreach ($inputs as $form_input) {
820 820
             if ($form_input instanceof EE_Form_Input_Base && $form_input->get_validation_errors()) {
821
-                $errors[ $form_input->html_name() ] = $form_input->get_validation_error_string();
821
+                $errors[$form_input->html_name()] = $form_input->get_validation_error_string();
822 822
             }
823 823
         }
824 824
         return $errors;
@@ -841,7 +841,7 @@  discard block
 block discarded – undo
841 841
         $email_validation_level = isset(EE_Registry::instance()->CFG->registration->email_validation_level)
842 842
             ? EE_Registry::instance()->CFG->registration->email_validation_level
843 843
             : 'wp_default';
844
-        EE_Form_Section_Proper::$_js_localization['email_validation_level']   = $email_validation_level;
844
+        EE_Form_Section_Proper::$_js_localization['email_validation_level'] = $email_validation_level;
845 845
         wp_enqueue_script('ee_form_section_validation');
846 846
         wp_localize_script(
847 847
             'ee_form_section_validation',
@@ -858,7 +858,7 @@  discard block
 block discarded – undo
858 858
      */
859 859
     public function ensure_scripts_localized()
860 860
     {
861
-        if (! EE_Form_Section_Proper::$_scripts_localized) {
861
+        if ( ! EE_Form_Section_Proper::$_scripts_localized) {
862 862
             $this->_enqueue_and_localize_form_js();
863 863
         }
864 864
     }
@@ -954,8 +954,8 @@  discard block
 block discarded – undo
954 954
         //reset the cache of whether this form is valid or not- we're re-validating it now
955 955
         $this->is_valid = null;
956 956
         foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
957
-            if (method_exists($this, '_validate_' . $subsection_name)) {
958
-                call_user_func_array(array($this, '_validate_' . $subsection_name), array($subsection));
957
+            if (method_exists($this, '_validate_'.$subsection_name)) {
958
+                call_user_func_array(array($this, '_validate_'.$subsection_name), array($subsection));
959 959
             }
960 960
             $subsection->_validate();
961 961
         }
@@ -973,9 +973,9 @@  discard block
 block discarded – undo
973 973
         $inputs = array();
974 974
         foreach ($this->subsections() as $subsection_name => $subsection) {
975 975
             if ($subsection instanceof EE_Form_Section_Proper) {
976
-                $inputs[ $subsection_name ] = $subsection->valid_data();
976
+                $inputs[$subsection_name] = $subsection->valid_data();
977 977
             } elseif ($subsection instanceof EE_Form_Input_Base) {
978
-                $inputs[ $subsection_name ] = $subsection->normalized_value();
978
+                $inputs[$subsection_name] = $subsection->normalized_value();
979 979
             }
980 980
         }
981 981
         return $inputs;
@@ -993,7 +993,7 @@  discard block
 block discarded – undo
993 993
         $inputs = array();
994 994
         foreach ($this->subsections() as $subsection_name => $subsection) {
995 995
             if ($subsection instanceof EE_Form_Input_Base) {
996
-                $inputs[ $subsection_name ] = $subsection;
996
+                $inputs[$subsection_name] = $subsection;
997 997
             }
998 998
         }
999 999
         return $inputs;
@@ -1011,7 +1011,7 @@  discard block
 block discarded – undo
1011 1011
         $form_sections = array();
1012 1012
         foreach ($this->subsections() as $name => $obj) {
1013 1013
             if ($obj instanceof EE_Form_Section_Proper) {
1014
-                $form_sections[ $name ] = $obj;
1014
+                $form_sections[$name] = $obj;
1015 1015
             }
1016 1016
         }
1017 1017
         return $form_sections;
@@ -1118,7 +1118,7 @@  discard block
 block discarded – undo
1118 1118
         $input_values = array();
1119 1119
         foreach ($this->subsections() as $subsection_name => $subsection) {
1120 1120
             if ($subsection instanceof EE_Form_Input_Base) {
1121
-                $input_values[ $subsection_name ] = $pretty
1121
+                $input_values[$subsection_name] = $pretty
1122 1122
                     ? $subsection->pretty_value()
1123 1123
                     : $subsection->normalized_value();
1124 1124
             } elseif ($subsection instanceof EE_Form_Section_Proper && $include_subform_inputs) {
@@ -1130,7 +1130,7 @@  discard block
 block discarded – undo
1130 1130
                 if ($flatten) {
1131 1131
                     $input_values = array_merge($input_values, $subform_input_values);
1132 1132
                 } else {
1133
-                    $input_values[ $subsection_name ] = $subform_input_values;
1133
+                    $input_values[$subsection_name] = $subform_input_values;
1134 1134
                 }
1135 1135
             }
1136 1136
         }
@@ -1158,7 +1158,7 @@  discard block
 block discarded – undo
1158 1158
             if ($subsection instanceof EE_Form_Input_Base) {
1159 1159
                 // is this input part of an array of inputs?
1160 1160
                 if (strpos($subsection->html_name(), '[') !== false) {
1161
-                    $full_input_name  = EEH_Array::convert_array_values_to_keys(
1161
+                    $full_input_name = EEH_Array::convert_array_values_to_keys(
1162 1162
                         explode(
1163 1163
                             '[',
1164 1164
                             str_replace(']', '', $subsection->html_name())
@@ -1167,7 +1167,7 @@  discard block
 block discarded – undo
1167 1167
                     );
1168 1168
                     $submitted_values = array_replace_recursive($submitted_values, $full_input_name);
1169 1169
                 } else {
1170
-                    $submitted_values[ $subsection->html_name() ] = $subsection->raw_value();
1170
+                    $submitted_values[$subsection->html_name()] = $subsection->raw_value();
1171 1171
                 }
1172 1172
             } elseif ($subsection instanceof EE_Form_Section_Proper && $include_subforms) {
1173 1173
                 $subform_input_values = $subsection->submitted_values($include_subforms);
@@ -1202,7 +1202,7 @@  discard block
 block discarded – undo
1202 1202
     public function exclude(array $inputs_to_exclude = array())
1203 1203
     {
1204 1204
         foreach ($inputs_to_exclude as $input_to_exclude_name) {
1205
-            unset($this->_subsections[ $input_to_exclude_name ]);
1205
+            unset($this->_subsections[$input_to_exclude_name]);
1206 1206
         }
1207 1207
     }
1208 1208
 
@@ -1244,7 +1244,7 @@  discard block
 block discarded – undo
1244 1244
     public function add_subsections($new_subsections, $subsection_name_to_target = null, $add_before = true)
1245 1245
     {
1246 1246
         foreach ($new_subsections as $subsection_name => $subsection) {
1247
-            if (! $subsection instanceof EE_Form_Section_Base) {
1247
+            if ( ! $subsection instanceof EE_Form_Section_Base) {
1248 1248
                 EE_Error::add_error(
1249 1249
                     sprintf(
1250 1250
                         esc_html__(
@@ -1256,7 +1256,7 @@  discard block
 block discarded – undo
1256 1256
                         $this->name()
1257 1257
                     )
1258 1258
                 );
1259
-                unset($new_subsections[ $subsection_name ]);
1259
+                unset($new_subsections[$subsection_name]);
1260 1260
             }
1261 1261
         }
1262 1262
         $this->_subsections = EEH_Array::insert_into_array(
@@ -1348,7 +1348,7 @@  discard block
 block discarded – undo
1348 1348
     public function html_name_prefix()
1349 1349
     {
1350 1350
         if ($this->parent_section() instanceof EE_Form_Section_Proper) {
1351
-            return $this->parent_section()->html_name_prefix() . '[' . $this->name() . ']';
1351
+            return $this->parent_section()->html_name_prefix().'['.$this->name().']';
1352 1352
         }
1353 1353
         return $this->name();
1354 1354
     }
@@ -1388,7 +1388,7 @@  discard block
 block discarded – undo
1388 1388
      */
1389 1389
     public function ensure_construct_finalized_called()
1390 1390
     {
1391
-        if (! $this->_construction_finalized) {
1391
+        if ( ! $this->_construction_finalized) {
1392 1392
             $this->_construct_finalize($this->_parent_section, $this->_name);
1393 1393
         }
1394 1394
     }
@@ -1460,7 +1460,7 @@  discard block
 block discarded – undo
1460 1460
                 $form_section = $validation_error->get_form_section();
1461 1461
                 if ($form_section instanceof EE_Form_Input_Base) {
1462 1462
                    $label = $validation_error->get_form_section()->html_label_text();
1463
-                } elseif($form_section instanceof EE_Form_Section_Validatable) {
1463
+                } elseif ($form_section instanceof EE_Form_Section_Validatable) {
1464 1464
                     $label = $validation_error->get_form_section()->name();
1465 1465
                 } else {
1466 1466
                     $label = esc_html__('Unknown', 'event_espresso');
Please login to merge, or discard this patch.
Indentation   +1501 added lines, -1501 removed lines patch added patch discarded remove patch
@@ -14,1506 +14,1506 @@
 block discarded – undo
14 14
 class EE_Form_Section_Proper extends EE_Form_Section_Validatable
15 15
 {
16 16
 
17
-    const SUBMITTED_FORM_DATA_SSN_KEY = 'submitted_form_data';
18
-
19
-    /**
20
-     * Subsections
21
-     *
22
-     * @var EE_Form_Section_Validatable[]
23
-     */
24
-    protected $_subsections = array();
25
-
26
-    /**
27
-     * Strategy for laying out the form
28
-     *
29
-     * @var EE_Form_Section_Layout_Base
30
-     */
31
-    protected $_layout_strategy;
32
-
33
-    /**
34
-     * Whether or not this form has received and validated a form submission yet
35
-     *
36
-     * @var boolean
37
-     */
38
-    protected $_received_submission = false;
39
-
40
-    /**
41
-     * message displayed to users upon successful form submission
42
-     *
43
-     * @var string
44
-     */
45
-    protected $_form_submission_success_message = '';
46
-
47
-    /**
48
-     * message displayed to users upon unsuccessful form submission
49
-     *
50
-     * @var string
51
-     */
52
-    protected $_form_submission_error_message = '';
53
-
54
-    /**
55
-     * @var array like $_REQUEST
56
-     */
57
-    protected $cached_request_data;
58
-
59
-    /**
60
-     * Stores whether this form (and its sub-sections) were found to be valid or not.
61
-     * Starts off as null, but once the form is validated, it set to either true or false
62
-     * @var boolean|null
63
-     */
64
-    protected $is_valid;
65
-
66
-    /**
67
-     * Stores all the data that will localized for form validation
68
-     *
69
-     * @var array
70
-     */
71
-    static protected $_js_localization = array();
72
-
73
-    /**
74
-     * whether or not the form's localized validation JS vars have been set
75
-     *
76
-     * @type boolean
77
-     */
78
-    static protected $_scripts_localized = false;
79
-
80
-
81
-    /**
82
-     * when constructing a proper form section, calls _construct_finalize on children
83
-     * so that they know who their parent is, and what name they've been given.
84
-     *
85
-     * @param array[] $options_array   {
86
-     * @type          $subsections     EE_Form_Section_Validatable[] where keys are the section's name
87
-     * @type          $include         string[] numerically-indexed where values are section names to be included,
88
-     *                                 and in that order. This is handy if you want
89
-     *                                 the subsections to be ordered differently than the default, and if you override
90
-     *                                 which fields are shown
91
-     * @type          $exclude         string[] values are subsections to be excluded. This is handy if you want
92
-     *                                 to remove certain default subsections (note: if you specify BOTH 'include' AND
93
-     *                                 'exclude', the inclusions will be applied first, and the exclusions will exclude
94
-     *                                 items from that list of inclusions)
95
-     * @type          $layout_strategy EE_Form_Section_Layout_Base strategy for laying out the form
96
-     *                                 } @see EE_Form_Section_Validatable::__construct()
97
-     * @throws EE_Error
98
-     */
99
-    public function __construct($options_array = array())
100
-    {
101
-        $options_array = (array) apply_filters(
102
-            'FHEE__EE_Form_Section_Proper___construct__options_array',
103
-            $options_array,
104
-            $this
105
-        );
106
-        //call parent first, as it may be setting the name
107
-        parent::__construct($options_array);
108
-        //if they've included subsections in the constructor, add them now
109
-        if (isset($options_array['include'])) {
110
-            //we are going to make sure we ONLY have those subsections to include
111
-            //AND we are going to make sure they're in that specified order
112
-            $reordered_subsections = array();
113
-            foreach ($options_array['include'] as $input_name) {
114
-                if (isset($this->_subsections[ $input_name ])) {
115
-                    $reordered_subsections[ $input_name ] = $this->_subsections[ $input_name ];
116
-                }
117
-            }
118
-            $this->_subsections = $reordered_subsections;
119
-        }
120
-        if (isset($options_array['exclude'])) {
121
-            $exclude            = $options_array['exclude'];
122
-            $this->_subsections = array_diff_key($this->_subsections, array_flip($exclude));
123
-        }
124
-        if (isset($options_array['layout_strategy'])) {
125
-            $this->_layout_strategy = $options_array['layout_strategy'];
126
-        }
127
-        if (! $this->_layout_strategy) {
128
-            $this->_layout_strategy = is_admin() ? new EE_Admin_Two_Column_Layout() : new EE_Two_Column_Layout();
129
-        }
130
-        $this->_layout_strategy->_construct_finalize($this);
131
-        //ok so we are definitely going to want the forms JS,
132
-        //so enqueue it or remember to enqueue it during wp_enqueue_scripts
133
-        if (did_action('wp_enqueue_scripts') || did_action('admin_enqueue_scripts')) {
134
-            //ok so they've constructed this object after when they should have.
135
-            //just enqueue the generic form scripts and initialize the form immediately in the JS
136
-            EE_Form_Section_Proper::wp_enqueue_scripts(true);
137
-        } else {
138
-            add_action('wp_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
139
-            add_action('admin_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
140
-        }
141
-        add_action('wp_footer', array($this, 'ensure_scripts_localized'), 1);
142
-        /**
143
-         * Gives other plugins a chance to hook in before construct finalize is called.
144
-         * The form probably doesn't yet have a parent form section.
145
-         * Since 4.9.32, when this action was introduced, this is the best place to add a subsection onto a form,
146
-         * assuming you don't care what the form section's name, HTML ID, or HTML name etc are.
147
-         * Also see AHEE__EE_Form_Section_Proper___construct_finalize__end
148
-         *
149
-         * @since 4.9.32
150
-         * @param EE_Form_Section_Proper $this          before __construct is done, but all of its logic,
151
-         *                                              except maybe calling _construct_finalize has been done
152
-         * @param array                  $options_array options passed into the constructor
153
-         */
154
-        do_action(
155
-            'AHEE__EE_Form_Input_Base___construct__before_construct_finalize_called',
156
-            $this,
157
-            $options_array
158
-        );
159
-        if (isset($options_array['name'])) {
160
-            $this->_construct_finalize(null, $options_array['name']);
161
-        }
162
-    }
163
-
164
-
165
-    /**
166
-     * Finishes construction given the parent form section and this form section's name
167
-     *
168
-     * @param EE_Form_Section_Proper $parent_form_section
169
-     * @param string                 $name
170
-     * @throws EE_Error
171
-     */
172
-    public function _construct_finalize($parent_form_section, $name)
173
-    {
174
-        parent::_construct_finalize($parent_form_section, $name);
175
-        $this->_set_default_name_if_empty();
176
-        $this->_set_default_html_id_if_empty();
177
-        foreach ($this->_subsections as $subsection_name => $subsection) {
178
-            if ($subsection instanceof EE_Form_Section_Base) {
179
-                $subsection->_construct_finalize($this, $subsection_name);
180
-            } else {
181
-                throw new EE_Error(
182
-                    sprintf(
183
-                        esc_html__(
184
-                            'Subsection "%s" is not an instanceof EE_Form_Section_Base on form "%s". It is a "%s"',
185
-                            'event_espresso'
186
-                        ),
187
-                        $subsection_name,
188
-                        get_class($this),
189
-                        $subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
190
-                    )
191
-                );
192
-            }
193
-        }
194
-        /**
195
-         * Action performed just after form has been given a name (and HTML ID etc) and is fully constructed.
196
-         * If you have code that should modify the form and needs it and its subsections to have a name, HTML ID
197
-         * (or other attributes derived from the name like the HTML label id, etc), this is where it should be done.
198
-         * This might only happen just before displaying the form, or just before it receives form submission data.
199
-         * If you need to modify the form or its subsections before _construct_finalize is called on it (and we've
200
-         * ensured it has a name, HTML IDs, etc
201
-         *
202
-         * @param EE_Form_Section_Proper      $this
203
-         * @param EE_Form_Section_Proper|null $parent_form_section
204
-         * @param string                      $name
205
-         */
206
-        do_action(
207
-            'AHEE__EE_Form_Section_Proper___construct_finalize__end',
208
-            $this,
209
-            $parent_form_section,
210
-            $name
211
-        );
212
-    }
213
-
214
-
215
-    /**
216
-     * Gets the layout strategy for this form section
217
-     *
218
-     * @return EE_Form_Section_Layout_Base
219
-     */
220
-    public function get_layout_strategy()
221
-    {
222
-        return $this->_layout_strategy;
223
-    }
224
-
225
-
226
-    /**
227
-     * Gets the HTML for a single input for this form section according
228
-     * to the layout strategy
229
-     *
230
-     * @param EE_Form_Input_Base $input
231
-     * @return string
232
-     */
233
-    public function get_html_for_input($input)
234
-    {
235
-        return $this->_layout_strategy->layout_input($input);
236
-    }
237
-
238
-
239
-    /**
240
-     * was_submitted - checks if form inputs are present in request data
241
-     * Basically an alias for form_data_present_in() (which is used by both
242
-     * proper form sections and form inputs)
243
-     *
244
-     * @param null $form_data
245
-     * @return boolean
246
-     * @throws EE_Error
247
-     */
248
-    public function was_submitted($form_data = null)
249
-    {
250
-        return $this->form_data_present_in($form_data);
251
-    }
252
-
253
-    /**
254
-     * Gets the cached request data; but if there is none, or $req_data was set with
255
-     * something different, refresh the cache, and then return it
256
-     * @param null $req_data
257
-     * @return array
258
-     */
259
-    protected function getCachedRequest($req_data = null)
260
-    {
261
-        if ($this->cached_request_data === null
262
-            || (
263
-                $req_data !== null &&
264
-                $req_data !== $this->cached_request_data
265
-            )
266
-        ) {
267
-            $req_data = apply_filters(
268
-                'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
269
-                $req_data,
270
-                $this
271
-            );
272
-            if ($req_data === null) {
273
-                $req_data = array_merge($_GET, $_POST);
274
-            }
275
-            $req_data = apply_filters(
276
-                'FHEE__EE_Form_Section_Proper__receive_form_submission__request_data',
277
-                $req_data,
278
-                $this
279
-            );
280
-            $this->cached_request_data = (array)$req_data;
281
-        }
282
-        return $this->cached_request_data;
283
-    }
284
-
285
-
286
-    /**
287
-     * After the form section is initially created, call this to sanitize the data in the submission
288
-     * which relates to this form section, validate it, and set it as properties on the form.
289
-     *
290
-     * @param array|null $req_data should usually be $_POST (the default).
291
-     *                             However, you CAN supply a different array.
292
-     *                             Consider using set_defaults() instead however.
293
-     *                             (If you rendered the form in the page using echo $form_x->get_html()
294
-     *                             the inputs will have the correct name in the request data for this function
295
-     *                             to find them and populate the form with them.
296
-     *                             If you have a flat form (with only input subsections),
297
-     *                             you can supply a flat array where keys
298
-     *                             are the form input names and values are their values)
299
-     * @param boolean    $validate whether or not to perform validation on this data. Default is,
300
-     *                             of course, to validate that data, and set errors on the invalid values.
301
-     *                             But if the data has already been validated
302
-     *                             (eg you validated the data then stored it in the DB)
303
-     *                             you may want to skip this step.
304
-     * @throws InvalidArgumentException
305
-     * @throws InvalidInterfaceException
306
-     * @throws InvalidDataTypeException
307
-     * @throws EE_Error
308
-     */
309
-    public function receive_form_submission($req_data = null, $validate = true)
310
-    {
311
-        $req_data = $this->getCachedRequest($req_data);
312
-        $this->_normalize($req_data);
313
-        if ($validate) {
314
-            $this->_validate();
315
-            //if it's invalid, we're going to want to re-display so remember what they submitted
316
-            if (! $this->is_valid()) {
317
-                $this->store_submitted_form_data_in_session();
318
-            }
319
-        }
320
-        if ($this->submission_error_message() === '' && ! $this->is_valid()) {
321
-            $this->set_submission_error_message();
322
-        }
323
-        do_action(
324
-            'AHEE__EE_Form_Section_Proper__receive_form_submission__end',
325
-            $req_data,
326
-            $this,
327
-            $validate
328
-        );
329
-    }
330
-
331
-
332
-    /**
333
-     * caches the originally submitted input values in the session
334
-     * so that they can be used to repopulate the form if it failed validation
335
-     *
336
-     * @return boolean whether or not the data was successfully stored in the session
337
-     * @throws InvalidArgumentException
338
-     * @throws InvalidInterfaceException
339
-     * @throws InvalidDataTypeException
340
-     * @throws EE_Error
341
-     */
342
-    protected function store_submitted_form_data_in_session()
343
-    {
344
-        return EE_Registry::instance()->SSN->set_session_data(
345
-            array(
346
-                EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY => $this->submitted_values(true),
347
-            )
348
-        );
349
-    }
350
-
351
-
352
-    /**
353
-     * retrieves the originally submitted input values in the session
354
-     * so that they can be used to repopulate the form if it failed validation
355
-     *
356
-     * @return array
357
-     * @throws InvalidArgumentException
358
-     * @throws InvalidInterfaceException
359
-     * @throws InvalidDataTypeException
360
-     */
361
-    protected function get_submitted_form_data_from_session()
362
-    {
363
-        $session = EE_Registry::instance()->SSN;
364
-        if ($session instanceof EE_Session) {
365
-            return $session->get_session_data(
366
-                EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY
367
-            );
368
-        }
369
-        return array();
370
-    }
371
-
372
-
373
-    /**
374
-     * flushed the originally submitted input values from the session
375
-     *
376
-     * @return boolean whether or not the data was successfully removed from the session
377
-     * @throws InvalidArgumentException
378
-     * @throws InvalidInterfaceException
379
-     * @throws InvalidDataTypeException
380
-     */
381
-    protected function flush_submitted_form_data_from_session()
382
-    {
383
-        return EE_Registry::instance()->SSN->reset_data(
384
-            array(EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY)
385
-        );
386
-    }
387
-
388
-
389
-    /**
390
-     * Populates this form and its subsections with data from the session.
391
-     * (Wrapper for EE_Form_Section_Proper::receive_form_submission, so it shows
392
-     * validation errors when displaying too)
393
-     * Returns true if the form was populated from the session, false otherwise
394
-     *
395
-     * @return boolean
396
-     * @throws InvalidArgumentException
397
-     * @throws InvalidInterfaceException
398
-     * @throws InvalidDataTypeException
399
-     * @throws EE_Error
400
-     */
401
-    public function populate_from_session()
402
-    {
403
-        $form_data_in_session = $this->get_submitted_form_data_from_session();
404
-        if (empty($form_data_in_session)) {
405
-            return false;
406
-        }
407
-        $this->receive_form_submission($form_data_in_session);
408
-        $this->flush_submitted_form_data_from_session();
409
-        if ($this->form_data_present_in($form_data_in_session)) {
410
-            return true;
411
-        }
412
-        return false;
413
-    }
414
-
415
-
416
-    /**
417
-     * Populates the default data for the form, given an array where keys are
418
-     * the input names, and values are their values (preferably normalized to be their
419
-     * proper PHP types, not all strings... although that should be ok too).
420
-     * Proper subsections are sub-arrays, the key being the subsection's name, and
421
-     * the value being an array formatted in teh same way
422
-     *
423
-     * @param array $default_data
424
-     * @throws EE_Error
425
-     */
426
-    public function populate_defaults($default_data)
427
-    {
428
-        foreach ($this->subsections(false) as $subsection_name => $subsection) {
429
-            if (isset($default_data[ $subsection_name ])) {
430
-                if ($subsection instanceof EE_Form_Input_Base) {
431
-                    $subsection->set_default($default_data[ $subsection_name ]);
432
-                } elseif ($subsection instanceof EE_Form_Section_Proper) {
433
-                    $subsection->populate_defaults($default_data[ $subsection_name ]);
434
-                }
435
-            }
436
-        }
437
-    }
438
-
439
-
440
-    /**
441
-     * returns true if subsection exists
442
-     *
443
-     * @param string $name
444
-     * @return boolean
445
-     */
446
-    public function subsection_exists($name)
447
-    {
448
-        return isset($this->_subsections[ $name ]) ? true : false;
449
-    }
450
-
451
-
452
-    /**
453
-     * Gets the subsection specified by its name
454
-     *
455
-     * @param string  $name
456
-     * @param boolean $require_construction_to_be_finalized most client code should leave this as TRUE
457
-     *                                                      so that the inputs will be properly configured.
458
-     *                                                      However, some client code may be ok
459
-     *                                                      with construction finalize being called later
460
-     *                                                      (realizing that the subsections' html names
461
-     *                                                      might not be set yet, etc.)
462
-     * @return EE_Form_Section_Base
463
-     * @throws EE_Error
464
-     */
465
-    public function get_subsection($name, $require_construction_to_be_finalized = true)
466
-    {
467
-        if ($require_construction_to_be_finalized) {
468
-            $this->ensure_construct_finalized_called();
469
-        }
470
-        return $this->subsection_exists($name) ? $this->_subsections[ $name ] : null;
471
-    }
472
-
473
-
474
-    /**
475
-     * Gets all the validatable subsections of this form section
476
-     *
477
-     * @return EE_Form_Section_Validatable[]
478
-     * @throws EE_Error
479
-     */
480
-    public function get_validatable_subsections()
481
-    {
482
-        $validatable_subsections = array();
483
-        foreach ($this->subsections() as $name => $obj) {
484
-            if ($obj instanceof EE_Form_Section_Validatable) {
485
-                $validatable_subsections[ $name ] = $obj;
486
-            }
487
-        }
488
-        return $validatable_subsections;
489
-    }
490
-
491
-
492
-    /**
493
-     * Gets an input by the given name. If not found, or if its not an EE_FOrm_Input_Base child,
494
-     * throw an EE_Error.
495
-     *
496
-     * @param string  $name
497
-     * @param boolean $require_construction_to_be_finalized most client code should
498
-     *                                                      leave this as TRUE so that the inputs will be properly
499
-     *                                                      configured. However, some client code may be ok with
500
-     *                                                      construction finalize being called later
501
-     *                                                      (realizing that the subsections' html names might not be
502
-     *                                                      set yet, etc.)
503
-     * @return EE_Form_Input_Base
504
-     * @throws EE_Error
505
-     */
506
-    public function get_input($name, $require_construction_to_be_finalized = true)
507
-    {
508
-        $subsection = $this->get_subsection(
509
-            $name,
510
-            $require_construction_to_be_finalized
511
-        );
512
-        if (! $subsection instanceof EE_Form_Input_Base) {
513
-            throw new EE_Error(
514
-                sprintf(
515
-                    esc_html__(
516
-                        "Subsection '%s' is not an instanceof EE_Form_Input_Base on form '%s'. It is a '%s'",
517
-                        'event_espresso'
518
-                    ),
519
-                    $name,
520
-                    get_class($this),
521
-                    $subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
522
-                )
523
-            );
524
-        }
525
-        return $subsection;
526
-    }
527
-
528
-
529
-    /**
530
-     * Like get_input(), gets the proper subsection of the form given the name,
531
-     * otherwise throws an EE_Error
532
-     *
533
-     * @param string  $name
534
-     * @param boolean $require_construction_to_be_finalized most client code should
535
-     *                                                      leave this as TRUE so that the inputs will be properly
536
-     *                                                      configured. However, some client code may be ok with
537
-     *                                                      construction finalize being called later
538
-     *                                                      (realizing that the subsections' html names might not be
539
-     *                                                      set yet, etc.)
540
-     * @return EE_Form_Section_Proper
541
-     * @throws EE_Error
542
-     */
543
-    public function get_proper_subsection($name, $require_construction_to_be_finalized = true)
544
-    {
545
-        $subsection = $this->get_subsection(
546
-            $name,
547
-            $require_construction_to_be_finalized
548
-        );
549
-        if (! $subsection instanceof EE_Form_Section_Proper) {
550
-            throw new EE_Error(
551
-                sprintf(
552
-                    esc_html__(
553
-                        "Subsection '%'s is not an instanceof EE_Form_Section_Proper on form '%s'",
554
-                        'event_espresso'
555
-                    ),
556
-                    $name,
557
-                    get_class($this)
558
-                )
559
-            );
560
-        }
561
-        return $subsection;
562
-    }
563
-
564
-
565
-    /**
566
-     * Gets the value of the specified input. Should be called after receive_form_submission()
567
-     * or populate_defaults() on the form, where the normalized value on the input is set.
568
-     *
569
-     * @param string $name
570
-     * @return mixed depending on the input's type and its normalization strategy
571
-     * @throws EE_Error
572
-     */
573
-    public function get_input_value($name)
574
-    {
575
-        $input = $this->get_input($name);
576
-        return $input->normalized_value();
577
-    }
578
-
579
-
580
-    /**
581
-     * Checks if this form section itself is valid, and then checks its subsections
582
-     *
583
-     * @throws EE_Error
584
-     * @return boolean
585
-     */
586
-    public function is_valid()
587
-    {
588
-        if($this->is_valid === null) {
589
-            if (! $this->has_received_submission()) {
590
-                throw new EE_Error(
591
-                    sprintf(
592
-                        esc_html__(
593
-                            'You cannot check if a form is valid before receiving the form submission using receive_form_submission',
594
-                            'event_espresso'
595
-                        )
596
-                    )
597
-                );
598
-            }
599
-            if (! parent::is_valid()) {
600
-                $this->is_valid = false;
601
-            } else {
602
-                // ok so no general errors to this entire form section.
603
-                // so let's check the subsections, but only set errors if that hasn't been done yet
604
-                $this->is_valid = true;
605
-                foreach ($this->get_validatable_subsections() as $subsection) {
606
-                    if (! $subsection->is_valid()) {
607
-                        $this->is_valid = false;
608
-                    }
609
-                }
610
-            }
611
-        }
612
-        return $this->is_valid;
613
-    }
614
-
615
-
616
-    /**
617
-     * gets the default name of this form section if none is specified
618
-     *
619
-     * @return void
620
-     */
621
-    protected function _set_default_name_if_empty()
622
-    {
623
-        if (! $this->_name) {
624
-            $classname    = get_class($this);
625
-            $default_name = str_replace('EE_', '', $classname);
626
-            $this->_name  = $default_name;
627
-        }
628
-    }
629
-
630
-
631
-    /**
632
-     * Returns the HTML for the form, except for the form opening and closing tags
633
-     * (as the form section doesn't know where you necessarily want to send the information to),
634
-     * and except for a submit button. Enqueues JS and CSS; if called early enough we will
635
-     * try to enqueue them in the header, otherwise they'll be enqueued in the footer.
636
-     * Not doing_it_wrong because theoretically this CAN be used properly,
637
-     * provided its used during "wp_enqueue_scripts", or it doesn't need to enqueue
638
-     * any CSS.
639
-     *
640
-     * @throws InvalidArgumentException
641
-     * @throws InvalidInterfaceException
642
-     * @throws InvalidDataTypeException
643
-     * @throws EE_Error
644
-     */
645
-    public function get_html_and_js()
646
-    {
647
-        $this->enqueue_js();
648
-        return $this->get_html();
649
-    }
650
-
651
-
652
-    /**
653
-     * returns HTML for displaying this form section. recursively calls display_section() on all subsections
654
-     *
655
-     * @param bool $display_previously_submitted_data
656
-     * @return string
657
-     * @throws InvalidArgumentException
658
-     * @throws InvalidInterfaceException
659
-     * @throws InvalidDataTypeException
660
-     * @throws EE_Error
661
-     * @throws EE_Error
662
-     * @throws EE_Error
663
-     */
664
-    public function get_html($display_previously_submitted_data = true)
665
-    {
666
-        $this->ensure_construct_finalized_called();
667
-        if ($display_previously_submitted_data) {
668
-            $this->populate_from_session();
669
-        }
670
-        return $this->_form_html_filter
671
-            ? $this->_form_html_filter->filterHtml($this->_layout_strategy->layout_form(), $this)
672
-            : $this->_layout_strategy->layout_form();
673
-    }
674
-
675
-
676
-    /**
677
-     * enqueues JS and CSS for the form.
678
-     * It is preferred to call this before wp_enqueue_scripts so the
679
-     * scripts and styles can be put in the header, but if called later
680
-     * they will be put in the footer (which is OK for JS, but in HTML4 CSS should
681
-     * only be in the header; but in HTML5 its ok in the body.
682
-     * See http://stackoverflow.com/questions/4957446/load-external-css-file-in-body-tag.
683
-     * So if your form enqueues CSS, it's preferred to call this before wp_enqueue_scripts.)
684
-     *
685
-     * @return void
686
-     * @throws EE_Error
687
-     */
688
-    public function enqueue_js()
689
-    {
690
-        $this->_enqueue_and_localize_form_js();
691
-        foreach ($this->subsections() as $subsection) {
692
-            $subsection->enqueue_js();
693
-        }
694
-    }
695
-
696
-
697
-    /**
698
-     * adds a filter so that jquery validate gets enqueued in EE_System::wp_enqueue_scripts().
699
-     * This must be done BEFORE wp_enqueue_scripts() gets called, which is on
700
-     * the wp_enqueue_scripts hook.
701
-     * However, registering the form js and localizing it can happen when we
702
-     * actually output the form (which is preferred, seeing how teh form's fields
703
-     * could change until it's actually outputted)
704
-     *
705
-     * @param boolean $init_form_validation_automatically whether or not we want the form validation
706
-     *                                                    to be triggered automatically or not
707
-     * @return void
708
-     */
709
-    public static function wp_enqueue_scripts($init_form_validation_automatically = true)
710
-    {
711
-        wp_register_script(
712
-            'ee_form_section_validation',
713
-            EE_GLOBAL_ASSETS_URL . 'scripts' . DS . 'form_section_validation.js',
714
-            array('jquery-validate', 'jquery-ui-datepicker', 'jquery-validate-extra-methods'),
715
-            EVENT_ESPRESSO_VERSION,
716
-            true
717
-        );
718
-        wp_localize_script(
719
-            'ee_form_section_validation',
720
-            'ee_form_section_validation_init',
721
-            array('init' => $init_form_validation_automatically ? '1' : '0')
722
-        );
723
-    }
724
-
725
-
726
-    /**
727
-     * gets the variables used by form_section_validation.js.
728
-     * This needs to be called AFTER we've called $this->_enqueue_jquery_validate_script,
729
-     * but before the wordpress hook wp_loaded
730
-     *
731
-     * @throws EE_Error
732
-     */
733
-    public function _enqueue_and_localize_form_js()
734
-    {
735
-        $this->ensure_construct_finalized_called();
736
-        //actually, we don't want to localize just yet. There may be other forms on the page.
737
-        //so we need to add our form section data to a static variable accessible by all form sections
738
-        //and localize it just before the footer
739
-        $this->localize_validation_rules();
740
-        add_action('wp_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'), 2);
741
-        add_action('admin_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'));
742
-    }
743
-
744
-
745
-    /**
746
-     * add our form section data to a static variable accessible by all form sections
747
-     *
748
-     * @param bool $return_for_subsection
749
-     * @return void
750
-     * @throws EE_Error
751
-     */
752
-    public function localize_validation_rules($return_for_subsection = false)
753
-    {
754
-        // we only want to localize vars ONCE for the entire form,
755
-        // so if the form section doesn't have a parent, then it must be the top dog
756
-        if ($return_for_subsection || ! $this->parent_section()) {
757
-            EE_Form_Section_Proper::$_js_localization['form_data'][ $this->html_id() ] = array(
758
-                'form_section_id'  => $this->html_id(true),
759
-                'validation_rules' => $this->get_jquery_validation_rules(),
760
-                'other_data'       => $this->get_other_js_data(),
761
-                'errors'           => $this->subsection_validation_errors_by_html_name(),
762
-            );
763
-            EE_Form_Section_Proper::$_scripts_localized                                = true;
764
-        }
765
-    }
766
-
767
-
768
-    /**
769
-     * Gets an array of extra data that will be useful for client-side javascript.
770
-     * This is primarily data added by inputs and forms in addition to any
771
-     * scripts they might enqueue
772
-     *
773
-     * @param array $form_other_js_data
774
-     * @return array
775
-     * @throws EE_Error
776
-     */
777
-    public function get_other_js_data($form_other_js_data = array())
778
-    {
779
-        foreach ($this->subsections() as $subsection) {
780
-            $form_other_js_data = $subsection->get_other_js_data($form_other_js_data);
781
-        }
782
-        return $form_other_js_data;
783
-    }
784
-
785
-
786
-    /**
787
-     * Gets a flat array of inputs for this form section and its subsections.
788
-     * Keys are their form names, and values are the inputs themselves
789
-     *
790
-     * @return EE_Form_Input_Base
791
-     * @throws EE_Error
792
-     */
793
-    public function inputs_in_subsections()
794
-    {
795
-        $inputs = array();
796
-        foreach ($this->subsections() as $subsection) {
797
-            if ($subsection instanceof EE_Form_Input_Base) {
798
-                $inputs[ $subsection->html_name() ] = $subsection;
799
-            } elseif ($subsection instanceof EE_Form_Section_Proper) {
800
-                $inputs += $subsection->inputs_in_subsections();
801
-            }
802
-        }
803
-        return $inputs;
804
-    }
805
-
806
-
807
-    /**
808
-     * Gets a flat array of all the validation errors.
809
-     * Keys are html names (because those should be unique)
810
-     * and values are a string of all their validation errors
811
-     *
812
-     * @return string[]
813
-     * @throws EE_Error
814
-     */
815
-    public function subsection_validation_errors_by_html_name()
816
-    {
817
-        $inputs = $this->inputs();
818
-        $errors = array();
819
-        foreach ($inputs as $form_input) {
820
-            if ($form_input instanceof EE_Form_Input_Base && $form_input->get_validation_errors()) {
821
-                $errors[ $form_input->html_name() ] = $form_input->get_validation_error_string();
822
-            }
823
-        }
824
-        return $errors;
825
-    }
826
-
827
-
828
-    /**
829
-     * passes all the form data required by the JS to the JS, and enqueues the few required JS files.
830
-     * Should be setup by each form during the _enqueues_and_localize_form_js
831
-     *
832
-     * @throws InvalidArgumentException
833
-     * @throws InvalidInterfaceException
834
-     * @throws InvalidDataTypeException
835
-     */
836
-    public static function localize_script_for_all_forms()
837
-    {
838
-        //allow inputs and stuff to hook in their JS and stuff here
839
-        do_action('AHEE__EE_Form_Section_Proper__localize_script_for_all_forms__begin');
840
-        EE_Form_Section_Proper::$_js_localization['localized_error_messages'] = EE_Form_Section_Proper::_get_localized_error_messages();
841
-        $email_validation_level = isset(EE_Registry::instance()->CFG->registration->email_validation_level)
842
-            ? EE_Registry::instance()->CFG->registration->email_validation_level
843
-            : 'wp_default';
844
-        EE_Form_Section_Proper::$_js_localization['email_validation_level']   = $email_validation_level;
845
-        wp_enqueue_script('ee_form_section_validation');
846
-        wp_localize_script(
847
-            'ee_form_section_validation',
848
-            'ee_form_section_vars',
849
-            EE_Form_Section_Proper::$_js_localization
850
-        );
851
-    }
852
-
853
-
854
-    /**
855
-     * ensure_scripts_localized
856
-     *
857
-     * @throws EE_Error
858
-     */
859
-    public function ensure_scripts_localized()
860
-    {
861
-        if (! EE_Form_Section_Proper::$_scripts_localized) {
862
-            $this->_enqueue_and_localize_form_js();
863
-        }
864
-    }
865
-
866
-
867
-    /**
868
-     * Gets the hard-coded validation error messages to be used in the JS. The convention
869
-     * is that the key here should be the same as the custom validation rule put in the JS file
870
-     *
871
-     * @return array keys are custom validation rules, and values are internationalized strings
872
-     */
873
-    private static function _get_localized_error_messages()
874
-    {
875
-        return array(
876
-            'validUrl' => esc_html__('This is not a valid absolute URL. Eg, http://domain.com/monkey.jpg', 'event_espresso'),
877
-            'regex'    => esc_html__('Please check your input', 'event_espresso'),
878
-        );
879
-    }
880
-
881
-
882
-    /**
883
-     * @return array
884
-     */
885
-    public static function js_localization()
886
-    {
887
-        return self::$_js_localization;
888
-    }
889
-
890
-
891
-    /**
892
-     * @return void
893
-     */
894
-    public static function reset_js_localization()
895
-    {
896
-        self::$_js_localization = array();
897
-    }
898
-
899
-
900
-    /**
901
-     * Gets the JS to put inside the jquery validation rules for subsection of this form section.
902
-     * See parent function for more...
903
-     *
904
-     * @return array
905
-     * @throws EE_Error
906
-     */
907
-    public function get_jquery_validation_rules()
908
-    {
909
-        $jquery_validation_rules = array();
910
-        foreach ($this->get_validatable_subsections() as $subsection) {
911
-            $jquery_validation_rules = array_merge(
912
-                $jquery_validation_rules,
913
-                $subsection->get_jquery_validation_rules()
914
-            );
915
-        }
916
-        return $jquery_validation_rules;
917
-    }
918
-
919
-
920
-    /**
921
-     * Sanitizes all the data and sets the sanitized value of each field
922
-     *
923
-     * @param array $req_data like $_POST
924
-     * @return void
925
-     * @throws EE_Error
926
-     */
927
-    protected function _normalize($req_data)
928
-    {
929
-        $this->_received_submission = true;
930
-        $this->_validation_errors   = array();
931
-        foreach ($this->get_validatable_subsections() as $subsection) {
932
-            try {
933
-                $subsection->_normalize($req_data);
934
-            } catch (EE_Validation_Error $e) {
935
-                $subsection->add_validation_error($e);
936
-            }
937
-        }
938
-    }
939
-
940
-
941
-    /**
942
-     * Performs validation on this form section and its subsections.
943
-     * For each subsection,
944
-     * calls _validate_{subsection_name} on THIS form (if the function exists)
945
-     * and passes it the subsection, then calls _validate on that subsection.
946
-     * If you need to perform validation on the form as a whole (considering multiple)
947
-     * you would be best to override this _validate method,
948
-     * calling parent::_validate() first.
949
-     *
950
-     * @throws EE_Error
951
-     */
952
-    protected function _validate()
953
-    {
954
-        //reset the cache of whether this form is valid or not- we're re-validating it now
955
-        $this->is_valid = null;
956
-        foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
957
-            if (method_exists($this, '_validate_' . $subsection_name)) {
958
-                call_user_func_array(array($this, '_validate_' . $subsection_name), array($subsection));
959
-            }
960
-            $subsection->_validate();
961
-        }
962
-    }
963
-
964
-
965
-    /**
966
-     * Gets all the validated inputs for the form section
967
-     *
968
-     * @return array
969
-     * @throws EE_Error
970
-     */
971
-    public function valid_data()
972
-    {
973
-        $inputs = array();
974
-        foreach ($this->subsections() as $subsection_name => $subsection) {
975
-            if ($subsection instanceof EE_Form_Section_Proper) {
976
-                $inputs[ $subsection_name ] = $subsection->valid_data();
977
-            } elseif ($subsection instanceof EE_Form_Input_Base) {
978
-                $inputs[ $subsection_name ] = $subsection->normalized_value();
979
-            }
980
-        }
981
-        return $inputs;
982
-    }
983
-
984
-
985
-    /**
986
-     * Gets all the inputs on this form section
987
-     *
988
-     * @return EE_Form_Input_Base[]
989
-     * @throws EE_Error
990
-     */
991
-    public function inputs()
992
-    {
993
-        $inputs = array();
994
-        foreach ($this->subsections() as $subsection_name => $subsection) {
995
-            if ($subsection instanceof EE_Form_Input_Base) {
996
-                $inputs[ $subsection_name ] = $subsection;
997
-            }
998
-        }
999
-        return $inputs;
1000
-    }
1001
-
1002
-
1003
-    /**
1004
-     * Gets all the subsections which are a proper form
1005
-     *
1006
-     * @return EE_Form_Section_Proper[]
1007
-     * @throws EE_Error
1008
-     */
1009
-    public function subforms()
1010
-    {
1011
-        $form_sections = array();
1012
-        foreach ($this->subsections() as $name => $obj) {
1013
-            if ($obj instanceof EE_Form_Section_Proper) {
1014
-                $form_sections[ $name ] = $obj;
1015
-            }
1016
-        }
1017
-        return $form_sections;
1018
-    }
1019
-
1020
-
1021
-    /**
1022
-     * Gets all the subsections (inputs, proper subsections, or html-only sections).
1023
-     * Consider using inputs() or subforms()
1024
-     * if you only want form inputs or proper form sections.
1025
-     *
1026
-     * @param boolean $require_construction_to_be_finalized most client code should
1027
-     *                                                      leave this as TRUE so that the inputs will be properly
1028
-     *                                                      configured. However, some client code may be ok with
1029
-     *                                                      construction finalize being called later
1030
-     *                                                      (realizing that the subsections' html names might not be
1031
-     *                                                      set yet, etc.)
1032
-     * @return EE_Form_Section_Proper[]
1033
-     * @throws EE_Error
1034
-     */
1035
-    public function subsections($require_construction_to_be_finalized = true)
1036
-    {
1037
-        if ($require_construction_to_be_finalized) {
1038
-            $this->ensure_construct_finalized_called();
1039
-        }
1040
-        return $this->_subsections;
1041
-    }
1042
-
1043
-
1044
-    /**
1045
-     * Returns whether this form has any subforms or inputs
1046
-     * @return bool
1047
-     */
1048
-    public function hasSubsections()
1049
-    {
1050
-        return ! empty($this->_subsections);
1051
-    }
1052
-
1053
-
1054
-    /**
1055
-     * Returns a simple array where keys are input names, and values are their normalized
1056
-     * values. (Similar to calling get_input_value on inputs)
1057
-     *
1058
-     * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1059
-     *                                        or just this forms' direct children inputs
1060
-     * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1061
-     *                                        or allow multidimensional array
1062
-     * @return array if $flatten is TRUE it will always be a 1-dimensional array
1063
-     *                                        with array keys being input names
1064
-     *                                        (regardless of whether they are from a subsection or not),
1065
-     *                                        and if $flatten is FALSE it can be a multidimensional array
1066
-     *                                        where keys are always subsection names and values are either
1067
-     *                                        the input's normalized value, or an array like the top-level array
1068
-     * @throws EE_Error
1069
-     */
1070
-    public function input_values($include_subform_inputs = false, $flatten = false)
1071
-    {
1072
-        return $this->_input_values(false, $include_subform_inputs, $flatten);
1073
-    }
1074
-
1075
-
1076
-    /**
1077
-     * Similar to EE_Form_Section_Proper::input_values(), except this returns the 'display_value'
1078
-     * of each input. On some inputs (especially radio boxes or checkboxes), the value stored
1079
-     * is not necessarily the value we want to display to users. This creates an array
1080
-     * where keys are the input names, and values are their display values
1081
-     *
1082
-     * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1083
-     *                                        or just this forms' direct children inputs
1084
-     * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1085
-     *                                        or allow multidimensional array
1086
-     * @return array if $flatten is TRUE it will always be a 1-dimensional array
1087
-     *                                        with array keys being input names
1088
-     *                                        (regardless of whether they are from a subsection or not),
1089
-     *                                        and if $flatten is FALSE it can be a multidimensional array
1090
-     *                                        where keys are always subsection names and values are either
1091
-     *                                        the input's normalized value, or an array like the top-level array
1092
-     * @throws EE_Error
1093
-     */
1094
-    public function input_pretty_values($include_subform_inputs = false, $flatten = false)
1095
-    {
1096
-        return $this->_input_values(true, $include_subform_inputs, $flatten);
1097
-    }
1098
-
1099
-
1100
-    /**
1101
-     * Gets the input values from the form
1102
-     *
1103
-     * @param boolean $pretty                 Whether to retrieve the pretty value,
1104
-     *                                        or just the normalized value
1105
-     * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1106
-     *                                        or just this forms' direct children inputs
1107
-     * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1108
-     *                                        or allow multidimensional array
1109
-     * @return array if $flatten is TRUE it will always be a 1-dimensional array with array keys being
1110
-     *                                        input names (regardless of whether they are from a subsection or not),
1111
-     *                                        and if $flatten is FALSE it can be a multidimensional array
1112
-     *                                        where keys are always subsection names and values are either
1113
-     *                                        the input's normalized value, or an array like the top-level array
1114
-     * @throws EE_Error
1115
-     */
1116
-    public function _input_values($pretty = false, $include_subform_inputs = false, $flatten = false)
1117
-    {
1118
-        $input_values = array();
1119
-        foreach ($this->subsections() as $subsection_name => $subsection) {
1120
-            if ($subsection instanceof EE_Form_Input_Base) {
1121
-                $input_values[ $subsection_name ] = $pretty
1122
-                    ? $subsection->pretty_value()
1123
-                    : $subsection->normalized_value();
1124
-            } elseif ($subsection instanceof EE_Form_Section_Proper && $include_subform_inputs) {
1125
-                $subform_input_values = $subsection->_input_values(
1126
-                    $pretty,
1127
-                    $include_subform_inputs,
1128
-                    $flatten
1129
-                );
1130
-                if ($flatten) {
1131
-                    $input_values = array_merge($input_values, $subform_input_values);
1132
-                } else {
1133
-                    $input_values[ $subsection_name ] = $subform_input_values;
1134
-                }
1135
-            }
1136
-        }
1137
-        return $input_values;
1138
-    }
1139
-
1140
-
1141
-    /**
1142
-     * Gets the originally submitted input values from the form
1143
-     *
1144
-     * @param boolean $include_subforms  Whether to include inputs from subforms,
1145
-     *                                   or just this forms' direct children inputs
1146
-     * @return array                     if $flatten is TRUE it will always be a 1-dimensional array
1147
-     *                                   with array keys being input names
1148
-     *                                   (regardless of whether they are from a subsection or not),
1149
-     *                                   and if $flatten is FALSE it can be a multidimensional array
1150
-     *                                   where keys are always subsection names and values are either
1151
-     *                                   the input's normalized value, or an array like the top-level array
1152
-     * @throws EE_Error
1153
-     */
1154
-    public function submitted_values($include_subforms = false)
1155
-    {
1156
-        $submitted_values = array();
1157
-        foreach ($this->subsections() as $subsection) {
1158
-            if ($subsection instanceof EE_Form_Input_Base) {
1159
-                // is this input part of an array of inputs?
1160
-                if (strpos($subsection->html_name(), '[') !== false) {
1161
-                    $full_input_name  = EEH_Array::convert_array_values_to_keys(
1162
-                        explode(
1163
-                            '[',
1164
-                            str_replace(']', '', $subsection->html_name())
1165
-                        ),
1166
-                        $subsection->raw_value()
1167
-                    );
1168
-                    $submitted_values = array_replace_recursive($submitted_values, $full_input_name);
1169
-                } else {
1170
-                    $submitted_values[ $subsection->html_name() ] = $subsection->raw_value();
1171
-                }
1172
-            } elseif ($subsection instanceof EE_Form_Section_Proper && $include_subforms) {
1173
-                $subform_input_values = $subsection->submitted_values($include_subforms);
1174
-                $submitted_values     = array_replace_recursive($submitted_values, $subform_input_values);
1175
-            }
1176
-        }
1177
-        return $submitted_values;
1178
-    }
1179
-
1180
-
1181
-    /**
1182
-     * Indicates whether or not this form has received a submission yet
1183
-     * (ie, had receive_form_submission called on it yet)
1184
-     *
1185
-     * @return boolean
1186
-     * @throws EE_Error
1187
-     */
1188
-    public function has_received_submission()
1189
-    {
1190
-        $this->ensure_construct_finalized_called();
1191
-        return $this->_received_submission;
1192
-    }
1193
-
1194
-
1195
-    /**
1196
-     * Equivalent to passing 'exclude' in the constructor's options array.
1197
-     * Removes the listed inputs from the form
1198
-     *
1199
-     * @param array $inputs_to_exclude values are the input names
1200
-     * @return void
1201
-     */
1202
-    public function exclude(array $inputs_to_exclude = array())
1203
-    {
1204
-        foreach ($inputs_to_exclude as $input_to_exclude_name) {
1205
-            unset($this->_subsections[ $input_to_exclude_name ]);
1206
-        }
1207
-    }
1208
-
1209
-
1210
-    /**
1211
-     * Changes these inputs' display strategy to be EE_Hidden_Display_Strategy.
1212
-     * @param array $inputs_to_hide
1213
-     * @throws EE_Error
1214
-     */
1215
-    public function hide(array $inputs_to_hide = array())
1216
-    {
1217
-        foreach ($inputs_to_hide as $input_to_hide) {
1218
-            $input = $this->get_input($input_to_hide);
1219
-            $input->set_display_strategy(new EE_Hidden_Display_Strategy());
1220
-        }
1221
-    }
1222
-
1223
-
1224
-    /**
1225
-     * add_subsections
1226
-     * Adds the listed subsections to the form section.
1227
-     * If $subsection_name_to_target is provided,
1228
-     * then new subsections are added before or after that subsection,
1229
-     * otherwise to the start or end of the entire subsections array.
1230
-     *
1231
-     * @param EE_Form_Section_Base[] $new_subsections           array of new form subsections
1232
-     *                                                          where keys are their names
1233
-     * @param string                 $subsection_name_to_target an existing for section that $new_subsections
1234
-     *                                                          should be added before or after
1235
-     *                                                          IF $subsection_name_to_target is null,
1236
-     *                                                          then $new_subsections will be added to
1237
-     *                                                          the beginning or end of the entire subsections array
1238
-     * @param boolean                $add_before                whether to add $new_subsections, before or after
1239
-     *                                                          $subsection_name_to_target,
1240
-     *                                                          or if $subsection_name_to_target is null,
1241
-     *                                                          before or after entire subsections array
1242
-     * @return void
1243
-     * @throws EE_Error
1244
-     */
1245
-    public function add_subsections($new_subsections, $subsection_name_to_target = null, $add_before = true)
1246
-    {
1247
-        foreach ($new_subsections as $subsection_name => $subsection) {
1248
-            if (! $subsection instanceof EE_Form_Section_Base) {
1249
-                EE_Error::add_error(
1250
-                    sprintf(
1251
-                        esc_html__(
1252
-                            "Trying to add a %s as a subsection (it was named '%s') to the form section '%s'. It was removed.",
1253
-                            'event_espresso'
1254
-                        ),
1255
-                        get_class($subsection),
1256
-                        $subsection_name,
1257
-                        $this->name()
1258
-                    )
1259
-                );
1260
-                unset($new_subsections[ $subsection_name ]);
1261
-            }
1262
-        }
1263
-        $this->_subsections = EEH_Array::insert_into_array(
1264
-            $this->_subsections,
1265
-            $new_subsections,
1266
-            $subsection_name_to_target,
1267
-            $add_before
1268
-        );
1269
-        if ($this->_construction_finalized) {
1270
-            foreach ($this->_subsections as $name => $subsection) {
1271
-                $subsection->_construct_finalize($this, $name);
1272
-            }
1273
-        }
1274
-    }
1275
-
1276
-
1277
-    /**
1278
-     * Just gets all validatable subsections to clean their sensitive data
1279
-     *
1280
-     * @throws EE_Error
1281
-     */
1282
-    public function clean_sensitive_data()
1283
-    {
1284
-        foreach ($this->get_validatable_subsections() as $subsection) {
1285
-            $subsection->clean_sensitive_data();
1286
-        }
1287
-    }
1288
-
1289
-
1290
-    /**
1291
-     * Sets the submission error message (aka validation error message for this form section and all sub-sections)
1292
-     * @param string                           $form_submission_error_message
1293
-     * @param EE_Form_Section_Validatable $form_section unused
1294
-     * @throws EE_Error
1295
-     */
1296
-    public function set_submission_error_message(
1297
-        $form_submission_error_message = ''
1298
-    ) {
1299
-        $this->_form_submission_error_message = ! empty($form_submission_error_message)
1300
-            ? $form_submission_error_message
1301
-            : $this->getAllValidationErrorsString();
1302
-    }
1303
-
1304
-
1305
-    /**
1306
-     * Returns the cached error message. A default value is set for this during _validate(),
1307
-     * (called during receive_form_submission) but it can be explicitly set using
1308
-     * set_submission_error_message
1309
-     *
1310
-     * @return string
1311
-     */
1312
-    public function submission_error_message()
1313
-    {
1314
-        return $this->_form_submission_error_message;
1315
-    }
1316
-
1317
-
1318
-    /**
1319
-     * Sets a message to display if the data submitted to the form was valid.
1320
-     * @param string $form_submission_success_message
1321
-     */
1322
-    public function set_submission_success_message($form_submission_success_message = '')
1323
-    {
1324
-        $this->_form_submission_success_message = ! empty($form_submission_success_message)
1325
-            ? $form_submission_success_message
1326
-            : esc_html__('Form submitted successfully', 'event_espresso');
1327
-    }
1328
-
1329
-
1330
-    /**
1331
-     * Gets a message appropriate for display when the form is correctly submitted
1332
-     * @return string
1333
-     */
1334
-    public function submission_success_message()
1335
-    {
1336
-        return $this->_form_submission_success_message;
1337
-    }
1338
-
1339
-
1340
-    /**
1341
-     * Returns the prefix that should be used on child of this form section for
1342
-     * their html names. If this form section itself has a parent, prepends ITS
1343
-     * prefix onto this form section's prefix. Used primarily by
1344
-     * EE_Form_Input_Base::_set_default_html_name_if_empty
1345
-     *
1346
-     * @return string
1347
-     * @throws EE_Error
1348
-     */
1349
-    public function html_name_prefix()
1350
-    {
1351
-        if ($this->parent_section() instanceof EE_Form_Section_Proper) {
1352
-            return $this->parent_section()->html_name_prefix() . '[' . $this->name() . ']';
1353
-        }
1354
-        return $this->name();
1355
-    }
1356
-
1357
-
1358
-    /**
1359
-     * Gets the name, but first checks _construct_finalize has been called. If not,
1360
-     * calls it (assumes there is no parent and that we want the name to be whatever
1361
-     * was set, which is probably nothing, or the classname)
1362
-     *
1363
-     * @return string
1364
-     * @throws EE_Error
1365
-     */
1366
-    public function name()
1367
-    {
1368
-        $this->ensure_construct_finalized_called();
1369
-        return parent::name();
1370
-    }
1371
-
1372
-
1373
-    /**
1374
-     * @return EE_Form_Section_Proper
1375
-     * @throws EE_Error
1376
-     */
1377
-    public function parent_section()
1378
-    {
1379
-        $this->ensure_construct_finalized_called();
1380
-        return parent::parent_section();
1381
-    }
1382
-
1383
-
1384
-    /**
1385
-     * make sure construction finalized was called, otherwise children might not be ready
1386
-     *
1387
-     * @return void
1388
-     * @throws EE_Error
1389
-     */
1390
-    public function ensure_construct_finalized_called()
1391
-    {
1392
-        if (! $this->_construction_finalized) {
1393
-            $this->_construct_finalize($this->_parent_section, $this->_name);
1394
-        }
1395
-    }
1396
-
1397
-
1398
-    /**
1399
-     * Checks if any of this form section's inputs, or any of its children's inputs,
1400
-     * are in teh form data. If any are found, returns true. Else false
1401
-     *
1402
-     * @param array $req_data
1403
-     * @return boolean
1404
-     * @throws EE_Error
1405
-     */
1406
-    public function form_data_present_in($req_data = null)
1407
-    {
1408
-        $req_data = $this->getCachedRequest($req_data);
1409
-        foreach ($this->subsections() as $subsection) {
1410
-            if ($subsection instanceof EE_Form_Input_Base) {
1411
-                if ($subsection->form_data_present_in($req_data)) {
1412
-                    return true;
1413
-                }
1414
-            } elseif ($subsection instanceof EE_Form_Section_Proper) {
1415
-                if ($subsection->form_data_present_in($req_data)) {
1416
-                    return true;
1417
-                }
1418
-            }
1419
-        }
1420
-        return false;
1421
-    }
1422
-
1423
-
1424
-    /**
1425
-     * Gets validation errors for this form section and subsections
1426
-     * Similar to EE_Form_Section_Validatable::get_validation_errors() except this
1427
-     * gets the validation errors for ALL subsection
1428
-     *
1429
-     * @return EE_Validation_Error[]
1430
-     * @throws EE_Error
1431
-     */
1432
-    public function get_validation_errors_accumulated()
1433
-    {
1434
-        $validation_errors = $this->get_validation_errors();
1435
-        foreach ($this->get_validatable_subsections() as $subsection) {
1436
-            if ($subsection instanceof EE_Form_Section_Proper) {
1437
-                $validation_errors_on_this_subsection = $subsection->get_validation_errors_accumulated();
1438
-            } else {
1439
-                $validation_errors_on_this_subsection = $subsection->get_validation_errors();
1440
-            }
1441
-            if ($validation_errors_on_this_subsection) {
1442
-                $validation_errors = array_merge($validation_errors, $validation_errors_on_this_subsection);
1443
-            }
1444
-        }
1445
-        return $validation_errors;
1446
-    }
1447
-
1448
-    /**
1449
-     * Fetch validation errors from children and grandchildren and puts them in a single string.
1450
-     * This traverses the form section tree to generate this, but you probably want to instead use
1451
-     * get_form_submission_error_message() which is usually this message cached (or a custom validation error message)
1452
-     *
1453
-     * @return string
1454
-     * @since $VID:$
1455
-     */
1456
-    protected function getAllValidationErrorsString()
1457
-    {
1458
-        $submission_error_messages = array();
1459
-        // bad, bad, bad registrant
1460
-        foreach ($this->get_validation_errors_accumulated() as $validation_error) {
1461
-            if ($validation_error instanceof EE_Validation_Error) {
1462
-                $form_section = $validation_error->get_form_section();
1463
-                if ($form_section instanceof EE_Form_Input_Base) {
1464
-                   $label = $validation_error->get_form_section()->html_label_text();
1465
-                } elseif($form_section instanceof EE_Form_Section_Validatable) {
1466
-                    $label = $validation_error->get_form_section()->name();
1467
-                } else {
1468
-                    $label = esc_html__('Unknown', 'event_espresso');
1469
-                }
1470
-                $submission_error_messages[] = sprintf(
1471
-                    __('%s : %s', 'event_espresso'),
1472
-                    $label,
1473
-                    $validation_error->getMessage()
1474
-                );
1475
-            }
1476
-        }
1477
-        return implode('<br', $submission_error_messages);
1478
-    }
1479
-
1480
-
1481
-    /**
1482
-     * This isn't just the name of an input, it's a path pointing to an input. The
1483
-     * path is similar to a folder path: slash (/) means to descend into a subsection,
1484
-     * dot-dot-slash (../) means to ascend into the parent section.
1485
-     * After a series of slashes and dot-dot-slashes, there should be the name of an input,
1486
-     * which will be returned.
1487
-     * Eg, if you want the related input to be conditional on a sibling input name 'foobar'
1488
-     * just use 'foobar'. If you want it to be conditional on an aunt/uncle input name
1489
-     * 'baz', use '../baz'. If you want it to be conditional on a cousin input,
1490
-     * the child of 'baz_section' named 'baz_child', use '../baz_section/baz_child'.
1491
-     * Etc
1492
-     *
1493
-     * @param string|false $form_section_path we accept false also because substr( '../', '../' ) = false
1494
-     * @return EE_Form_Section_Base
1495
-     * @throws EE_Error
1496
-     */
1497
-    public function find_section_from_path($form_section_path)
1498
-    {
1499
-        //check if we can find the input from purely going straight up the tree
1500
-        $input = parent::find_section_from_path($form_section_path);
1501
-        if ($input instanceof EE_Form_Section_Base) {
1502
-            return $input;
1503
-        }
1504
-        $next_slash_pos = strpos($form_section_path, '/');
1505
-        if ($next_slash_pos !== false) {
1506
-            $child_section_name = substr($form_section_path, 0, $next_slash_pos);
1507
-            $subpath            = substr($form_section_path, $next_slash_pos + 1);
1508
-        } else {
1509
-            $child_section_name = $form_section_path;
1510
-            $subpath            = '';
1511
-        }
1512
-        $child_section = $this->get_subsection($child_section_name);
1513
-        if ($child_section instanceof EE_Form_Section_Base) {
1514
-            return $child_section->find_section_from_path($subpath);
1515
-        }
1516
-        return null;
1517
-    }
17
+	const SUBMITTED_FORM_DATA_SSN_KEY = 'submitted_form_data';
18
+
19
+	/**
20
+	 * Subsections
21
+	 *
22
+	 * @var EE_Form_Section_Validatable[]
23
+	 */
24
+	protected $_subsections = array();
25
+
26
+	/**
27
+	 * Strategy for laying out the form
28
+	 *
29
+	 * @var EE_Form_Section_Layout_Base
30
+	 */
31
+	protected $_layout_strategy;
32
+
33
+	/**
34
+	 * Whether or not this form has received and validated a form submission yet
35
+	 *
36
+	 * @var boolean
37
+	 */
38
+	protected $_received_submission = false;
39
+
40
+	/**
41
+	 * message displayed to users upon successful form submission
42
+	 *
43
+	 * @var string
44
+	 */
45
+	protected $_form_submission_success_message = '';
46
+
47
+	/**
48
+	 * message displayed to users upon unsuccessful form submission
49
+	 *
50
+	 * @var string
51
+	 */
52
+	protected $_form_submission_error_message = '';
53
+
54
+	/**
55
+	 * @var array like $_REQUEST
56
+	 */
57
+	protected $cached_request_data;
58
+
59
+	/**
60
+	 * Stores whether this form (and its sub-sections) were found to be valid or not.
61
+	 * Starts off as null, but once the form is validated, it set to either true or false
62
+	 * @var boolean|null
63
+	 */
64
+	protected $is_valid;
65
+
66
+	/**
67
+	 * Stores all the data that will localized for form validation
68
+	 *
69
+	 * @var array
70
+	 */
71
+	static protected $_js_localization = array();
72
+
73
+	/**
74
+	 * whether or not the form's localized validation JS vars have been set
75
+	 *
76
+	 * @type boolean
77
+	 */
78
+	static protected $_scripts_localized = false;
79
+
80
+
81
+	/**
82
+	 * when constructing a proper form section, calls _construct_finalize on children
83
+	 * so that they know who their parent is, and what name they've been given.
84
+	 *
85
+	 * @param array[] $options_array   {
86
+	 * @type          $subsections     EE_Form_Section_Validatable[] where keys are the section's name
87
+	 * @type          $include         string[] numerically-indexed where values are section names to be included,
88
+	 *                                 and in that order. This is handy if you want
89
+	 *                                 the subsections to be ordered differently than the default, and if you override
90
+	 *                                 which fields are shown
91
+	 * @type          $exclude         string[] values are subsections to be excluded. This is handy if you want
92
+	 *                                 to remove certain default subsections (note: if you specify BOTH 'include' AND
93
+	 *                                 'exclude', the inclusions will be applied first, and the exclusions will exclude
94
+	 *                                 items from that list of inclusions)
95
+	 * @type          $layout_strategy EE_Form_Section_Layout_Base strategy for laying out the form
96
+	 *                                 } @see EE_Form_Section_Validatable::__construct()
97
+	 * @throws EE_Error
98
+	 */
99
+	public function __construct($options_array = array())
100
+	{
101
+		$options_array = (array) apply_filters(
102
+			'FHEE__EE_Form_Section_Proper___construct__options_array',
103
+			$options_array,
104
+			$this
105
+		);
106
+		//call parent first, as it may be setting the name
107
+		parent::__construct($options_array);
108
+		//if they've included subsections in the constructor, add them now
109
+		if (isset($options_array['include'])) {
110
+			//we are going to make sure we ONLY have those subsections to include
111
+			//AND we are going to make sure they're in that specified order
112
+			$reordered_subsections = array();
113
+			foreach ($options_array['include'] as $input_name) {
114
+				if (isset($this->_subsections[ $input_name ])) {
115
+					$reordered_subsections[ $input_name ] = $this->_subsections[ $input_name ];
116
+				}
117
+			}
118
+			$this->_subsections = $reordered_subsections;
119
+		}
120
+		if (isset($options_array['exclude'])) {
121
+			$exclude            = $options_array['exclude'];
122
+			$this->_subsections = array_diff_key($this->_subsections, array_flip($exclude));
123
+		}
124
+		if (isset($options_array['layout_strategy'])) {
125
+			$this->_layout_strategy = $options_array['layout_strategy'];
126
+		}
127
+		if (! $this->_layout_strategy) {
128
+			$this->_layout_strategy = is_admin() ? new EE_Admin_Two_Column_Layout() : new EE_Two_Column_Layout();
129
+		}
130
+		$this->_layout_strategy->_construct_finalize($this);
131
+		//ok so we are definitely going to want the forms JS,
132
+		//so enqueue it or remember to enqueue it during wp_enqueue_scripts
133
+		if (did_action('wp_enqueue_scripts') || did_action('admin_enqueue_scripts')) {
134
+			//ok so they've constructed this object after when they should have.
135
+			//just enqueue the generic form scripts and initialize the form immediately in the JS
136
+			EE_Form_Section_Proper::wp_enqueue_scripts(true);
137
+		} else {
138
+			add_action('wp_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
139
+			add_action('admin_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
140
+		}
141
+		add_action('wp_footer', array($this, 'ensure_scripts_localized'), 1);
142
+		/**
143
+		 * Gives other plugins a chance to hook in before construct finalize is called.
144
+		 * The form probably doesn't yet have a parent form section.
145
+		 * Since 4.9.32, when this action was introduced, this is the best place to add a subsection onto a form,
146
+		 * assuming you don't care what the form section's name, HTML ID, or HTML name etc are.
147
+		 * Also see AHEE__EE_Form_Section_Proper___construct_finalize__end
148
+		 *
149
+		 * @since 4.9.32
150
+		 * @param EE_Form_Section_Proper $this          before __construct is done, but all of its logic,
151
+		 *                                              except maybe calling _construct_finalize has been done
152
+		 * @param array                  $options_array options passed into the constructor
153
+		 */
154
+		do_action(
155
+			'AHEE__EE_Form_Input_Base___construct__before_construct_finalize_called',
156
+			$this,
157
+			$options_array
158
+		);
159
+		if (isset($options_array['name'])) {
160
+			$this->_construct_finalize(null, $options_array['name']);
161
+		}
162
+	}
163
+
164
+
165
+	/**
166
+	 * Finishes construction given the parent form section and this form section's name
167
+	 *
168
+	 * @param EE_Form_Section_Proper $parent_form_section
169
+	 * @param string                 $name
170
+	 * @throws EE_Error
171
+	 */
172
+	public function _construct_finalize($parent_form_section, $name)
173
+	{
174
+		parent::_construct_finalize($parent_form_section, $name);
175
+		$this->_set_default_name_if_empty();
176
+		$this->_set_default_html_id_if_empty();
177
+		foreach ($this->_subsections as $subsection_name => $subsection) {
178
+			if ($subsection instanceof EE_Form_Section_Base) {
179
+				$subsection->_construct_finalize($this, $subsection_name);
180
+			} else {
181
+				throw new EE_Error(
182
+					sprintf(
183
+						esc_html__(
184
+							'Subsection "%s" is not an instanceof EE_Form_Section_Base on form "%s". It is a "%s"',
185
+							'event_espresso'
186
+						),
187
+						$subsection_name,
188
+						get_class($this),
189
+						$subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
190
+					)
191
+				);
192
+			}
193
+		}
194
+		/**
195
+		 * Action performed just after form has been given a name (and HTML ID etc) and is fully constructed.
196
+		 * If you have code that should modify the form and needs it and its subsections to have a name, HTML ID
197
+		 * (or other attributes derived from the name like the HTML label id, etc), this is where it should be done.
198
+		 * This might only happen just before displaying the form, or just before it receives form submission data.
199
+		 * If you need to modify the form or its subsections before _construct_finalize is called on it (and we've
200
+		 * ensured it has a name, HTML IDs, etc
201
+		 *
202
+		 * @param EE_Form_Section_Proper      $this
203
+		 * @param EE_Form_Section_Proper|null $parent_form_section
204
+		 * @param string                      $name
205
+		 */
206
+		do_action(
207
+			'AHEE__EE_Form_Section_Proper___construct_finalize__end',
208
+			$this,
209
+			$parent_form_section,
210
+			$name
211
+		);
212
+	}
213
+
214
+
215
+	/**
216
+	 * Gets the layout strategy for this form section
217
+	 *
218
+	 * @return EE_Form_Section_Layout_Base
219
+	 */
220
+	public function get_layout_strategy()
221
+	{
222
+		return $this->_layout_strategy;
223
+	}
224
+
225
+
226
+	/**
227
+	 * Gets the HTML for a single input for this form section according
228
+	 * to the layout strategy
229
+	 *
230
+	 * @param EE_Form_Input_Base $input
231
+	 * @return string
232
+	 */
233
+	public function get_html_for_input($input)
234
+	{
235
+		return $this->_layout_strategy->layout_input($input);
236
+	}
237
+
238
+
239
+	/**
240
+	 * was_submitted - checks if form inputs are present in request data
241
+	 * Basically an alias for form_data_present_in() (which is used by both
242
+	 * proper form sections and form inputs)
243
+	 *
244
+	 * @param null $form_data
245
+	 * @return boolean
246
+	 * @throws EE_Error
247
+	 */
248
+	public function was_submitted($form_data = null)
249
+	{
250
+		return $this->form_data_present_in($form_data);
251
+	}
252
+
253
+	/**
254
+	 * Gets the cached request data; but if there is none, or $req_data was set with
255
+	 * something different, refresh the cache, and then return it
256
+	 * @param null $req_data
257
+	 * @return array
258
+	 */
259
+	protected function getCachedRequest($req_data = null)
260
+	{
261
+		if ($this->cached_request_data === null
262
+			|| (
263
+				$req_data !== null &&
264
+				$req_data !== $this->cached_request_data
265
+			)
266
+		) {
267
+			$req_data = apply_filters(
268
+				'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
269
+				$req_data,
270
+				$this
271
+			);
272
+			if ($req_data === null) {
273
+				$req_data = array_merge($_GET, $_POST);
274
+			}
275
+			$req_data = apply_filters(
276
+				'FHEE__EE_Form_Section_Proper__receive_form_submission__request_data',
277
+				$req_data,
278
+				$this
279
+			);
280
+			$this->cached_request_data = (array)$req_data;
281
+		}
282
+		return $this->cached_request_data;
283
+	}
284
+
285
+
286
+	/**
287
+	 * After the form section is initially created, call this to sanitize the data in the submission
288
+	 * which relates to this form section, validate it, and set it as properties on the form.
289
+	 *
290
+	 * @param array|null $req_data should usually be $_POST (the default).
291
+	 *                             However, you CAN supply a different array.
292
+	 *                             Consider using set_defaults() instead however.
293
+	 *                             (If you rendered the form in the page using echo $form_x->get_html()
294
+	 *                             the inputs will have the correct name in the request data for this function
295
+	 *                             to find them and populate the form with them.
296
+	 *                             If you have a flat form (with only input subsections),
297
+	 *                             you can supply a flat array where keys
298
+	 *                             are the form input names and values are their values)
299
+	 * @param boolean    $validate whether or not to perform validation on this data. Default is,
300
+	 *                             of course, to validate that data, and set errors on the invalid values.
301
+	 *                             But if the data has already been validated
302
+	 *                             (eg you validated the data then stored it in the DB)
303
+	 *                             you may want to skip this step.
304
+	 * @throws InvalidArgumentException
305
+	 * @throws InvalidInterfaceException
306
+	 * @throws InvalidDataTypeException
307
+	 * @throws EE_Error
308
+	 */
309
+	public function receive_form_submission($req_data = null, $validate = true)
310
+	{
311
+		$req_data = $this->getCachedRequest($req_data);
312
+		$this->_normalize($req_data);
313
+		if ($validate) {
314
+			$this->_validate();
315
+			//if it's invalid, we're going to want to re-display so remember what they submitted
316
+			if (! $this->is_valid()) {
317
+				$this->store_submitted_form_data_in_session();
318
+			}
319
+		}
320
+		if ($this->submission_error_message() === '' && ! $this->is_valid()) {
321
+			$this->set_submission_error_message();
322
+		}
323
+		do_action(
324
+			'AHEE__EE_Form_Section_Proper__receive_form_submission__end',
325
+			$req_data,
326
+			$this,
327
+			$validate
328
+		);
329
+	}
330
+
331
+
332
+	/**
333
+	 * caches the originally submitted input values in the session
334
+	 * so that they can be used to repopulate the form if it failed validation
335
+	 *
336
+	 * @return boolean whether or not the data was successfully stored in the session
337
+	 * @throws InvalidArgumentException
338
+	 * @throws InvalidInterfaceException
339
+	 * @throws InvalidDataTypeException
340
+	 * @throws EE_Error
341
+	 */
342
+	protected function store_submitted_form_data_in_session()
343
+	{
344
+		return EE_Registry::instance()->SSN->set_session_data(
345
+			array(
346
+				EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY => $this->submitted_values(true),
347
+			)
348
+		);
349
+	}
350
+
351
+
352
+	/**
353
+	 * retrieves the originally submitted input values in the session
354
+	 * so that they can be used to repopulate the form if it failed validation
355
+	 *
356
+	 * @return array
357
+	 * @throws InvalidArgumentException
358
+	 * @throws InvalidInterfaceException
359
+	 * @throws InvalidDataTypeException
360
+	 */
361
+	protected function get_submitted_form_data_from_session()
362
+	{
363
+		$session = EE_Registry::instance()->SSN;
364
+		if ($session instanceof EE_Session) {
365
+			return $session->get_session_data(
366
+				EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY
367
+			);
368
+		}
369
+		return array();
370
+	}
371
+
372
+
373
+	/**
374
+	 * flushed the originally submitted input values from the session
375
+	 *
376
+	 * @return boolean whether or not the data was successfully removed from the session
377
+	 * @throws InvalidArgumentException
378
+	 * @throws InvalidInterfaceException
379
+	 * @throws InvalidDataTypeException
380
+	 */
381
+	protected function flush_submitted_form_data_from_session()
382
+	{
383
+		return EE_Registry::instance()->SSN->reset_data(
384
+			array(EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY)
385
+		);
386
+	}
387
+
388
+
389
+	/**
390
+	 * Populates this form and its subsections with data from the session.
391
+	 * (Wrapper for EE_Form_Section_Proper::receive_form_submission, so it shows
392
+	 * validation errors when displaying too)
393
+	 * Returns true if the form was populated from the session, false otherwise
394
+	 *
395
+	 * @return boolean
396
+	 * @throws InvalidArgumentException
397
+	 * @throws InvalidInterfaceException
398
+	 * @throws InvalidDataTypeException
399
+	 * @throws EE_Error
400
+	 */
401
+	public function populate_from_session()
402
+	{
403
+		$form_data_in_session = $this->get_submitted_form_data_from_session();
404
+		if (empty($form_data_in_session)) {
405
+			return false;
406
+		}
407
+		$this->receive_form_submission($form_data_in_session);
408
+		$this->flush_submitted_form_data_from_session();
409
+		if ($this->form_data_present_in($form_data_in_session)) {
410
+			return true;
411
+		}
412
+		return false;
413
+	}
414
+
415
+
416
+	/**
417
+	 * Populates the default data for the form, given an array where keys are
418
+	 * the input names, and values are their values (preferably normalized to be their
419
+	 * proper PHP types, not all strings... although that should be ok too).
420
+	 * Proper subsections are sub-arrays, the key being the subsection's name, and
421
+	 * the value being an array formatted in teh same way
422
+	 *
423
+	 * @param array $default_data
424
+	 * @throws EE_Error
425
+	 */
426
+	public function populate_defaults($default_data)
427
+	{
428
+		foreach ($this->subsections(false) as $subsection_name => $subsection) {
429
+			if (isset($default_data[ $subsection_name ])) {
430
+				if ($subsection instanceof EE_Form_Input_Base) {
431
+					$subsection->set_default($default_data[ $subsection_name ]);
432
+				} elseif ($subsection instanceof EE_Form_Section_Proper) {
433
+					$subsection->populate_defaults($default_data[ $subsection_name ]);
434
+				}
435
+			}
436
+		}
437
+	}
438
+
439
+
440
+	/**
441
+	 * returns true if subsection exists
442
+	 *
443
+	 * @param string $name
444
+	 * @return boolean
445
+	 */
446
+	public function subsection_exists($name)
447
+	{
448
+		return isset($this->_subsections[ $name ]) ? true : false;
449
+	}
450
+
451
+
452
+	/**
453
+	 * Gets the subsection specified by its name
454
+	 *
455
+	 * @param string  $name
456
+	 * @param boolean $require_construction_to_be_finalized most client code should leave this as TRUE
457
+	 *                                                      so that the inputs will be properly configured.
458
+	 *                                                      However, some client code may be ok
459
+	 *                                                      with construction finalize being called later
460
+	 *                                                      (realizing that the subsections' html names
461
+	 *                                                      might not be set yet, etc.)
462
+	 * @return EE_Form_Section_Base
463
+	 * @throws EE_Error
464
+	 */
465
+	public function get_subsection($name, $require_construction_to_be_finalized = true)
466
+	{
467
+		if ($require_construction_to_be_finalized) {
468
+			$this->ensure_construct_finalized_called();
469
+		}
470
+		return $this->subsection_exists($name) ? $this->_subsections[ $name ] : null;
471
+	}
472
+
473
+
474
+	/**
475
+	 * Gets all the validatable subsections of this form section
476
+	 *
477
+	 * @return EE_Form_Section_Validatable[]
478
+	 * @throws EE_Error
479
+	 */
480
+	public function get_validatable_subsections()
481
+	{
482
+		$validatable_subsections = array();
483
+		foreach ($this->subsections() as $name => $obj) {
484
+			if ($obj instanceof EE_Form_Section_Validatable) {
485
+				$validatable_subsections[ $name ] = $obj;
486
+			}
487
+		}
488
+		return $validatable_subsections;
489
+	}
490
+
491
+
492
+	/**
493
+	 * Gets an input by the given name. If not found, or if its not an EE_FOrm_Input_Base child,
494
+	 * throw an EE_Error.
495
+	 *
496
+	 * @param string  $name
497
+	 * @param boolean $require_construction_to_be_finalized most client code should
498
+	 *                                                      leave this as TRUE so that the inputs will be properly
499
+	 *                                                      configured. However, some client code may be ok with
500
+	 *                                                      construction finalize being called later
501
+	 *                                                      (realizing that the subsections' html names might not be
502
+	 *                                                      set yet, etc.)
503
+	 * @return EE_Form_Input_Base
504
+	 * @throws EE_Error
505
+	 */
506
+	public function get_input($name, $require_construction_to_be_finalized = true)
507
+	{
508
+		$subsection = $this->get_subsection(
509
+			$name,
510
+			$require_construction_to_be_finalized
511
+		);
512
+		if (! $subsection instanceof EE_Form_Input_Base) {
513
+			throw new EE_Error(
514
+				sprintf(
515
+					esc_html__(
516
+						"Subsection '%s' is not an instanceof EE_Form_Input_Base on form '%s'. It is a '%s'",
517
+						'event_espresso'
518
+					),
519
+					$name,
520
+					get_class($this),
521
+					$subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
522
+				)
523
+			);
524
+		}
525
+		return $subsection;
526
+	}
527
+
528
+
529
+	/**
530
+	 * Like get_input(), gets the proper subsection of the form given the name,
531
+	 * otherwise throws an EE_Error
532
+	 *
533
+	 * @param string  $name
534
+	 * @param boolean $require_construction_to_be_finalized most client code should
535
+	 *                                                      leave this as TRUE so that the inputs will be properly
536
+	 *                                                      configured. However, some client code may be ok with
537
+	 *                                                      construction finalize being called later
538
+	 *                                                      (realizing that the subsections' html names might not be
539
+	 *                                                      set yet, etc.)
540
+	 * @return EE_Form_Section_Proper
541
+	 * @throws EE_Error
542
+	 */
543
+	public function get_proper_subsection($name, $require_construction_to_be_finalized = true)
544
+	{
545
+		$subsection = $this->get_subsection(
546
+			$name,
547
+			$require_construction_to_be_finalized
548
+		);
549
+		if (! $subsection instanceof EE_Form_Section_Proper) {
550
+			throw new EE_Error(
551
+				sprintf(
552
+					esc_html__(
553
+						"Subsection '%'s is not an instanceof EE_Form_Section_Proper on form '%s'",
554
+						'event_espresso'
555
+					),
556
+					$name,
557
+					get_class($this)
558
+				)
559
+			);
560
+		}
561
+		return $subsection;
562
+	}
563
+
564
+
565
+	/**
566
+	 * Gets the value of the specified input. Should be called after receive_form_submission()
567
+	 * or populate_defaults() on the form, where the normalized value on the input is set.
568
+	 *
569
+	 * @param string $name
570
+	 * @return mixed depending on the input's type and its normalization strategy
571
+	 * @throws EE_Error
572
+	 */
573
+	public function get_input_value($name)
574
+	{
575
+		$input = $this->get_input($name);
576
+		return $input->normalized_value();
577
+	}
578
+
579
+
580
+	/**
581
+	 * Checks if this form section itself is valid, and then checks its subsections
582
+	 *
583
+	 * @throws EE_Error
584
+	 * @return boolean
585
+	 */
586
+	public function is_valid()
587
+	{
588
+		if($this->is_valid === null) {
589
+			if (! $this->has_received_submission()) {
590
+				throw new EE_Error(
591
+					sprintf(
592
+						esc_html__(
593
+							'You cannot check if a form is valid before receiving the form submission using receive_form_submission',
594
+							'event_espresso'
595
+						)
596
+					)
597
+				);
598
+			}
599
+			if (! parent::is_valid()) {
600
+				$this->is_valid = false;
601
+			} else {
602
+				// ok so no general errors to this entire form section.
603
+				// so let's check the subsections, but only set errors if that hasn't been done yet
604
+				$this->is_valid = true;
605
+				foreach ($this->get_validatable_subsections() as $subsection) {
606
+					if (! $subsection->is_valid()) {
607
+						$this->is_valid = false;
608
+					}
609
+				}
610
+			}
611
+		}
612
+		return $this->is_valid;
613
+	}
614
+
615
+
616
+	/**
617
+	 * gets the default name of this form section if none is specified
618
+	 *
619
+	 * @return void
620
+	 */
621
+	protected function _set_default_name_if_empty()
622
+	{
623
+		if (! $this->_name) {
624
+			$classname    = get_class($this);
625
+			$default_name = str_replace('EE_', '', $classname);
626
+			$this->_name  = $default_name;
627
+		}
628
+	}
629
+
630
+
631
+	/**
632
+	 * Returns the HTML for the form, except for the form opening and closing tags
633
+	 * (as the form section doesn't know where you necessarily want to send the information to),
634
+	 * and except for a submit button. Enqueues JS and CSS; if called early enough we will
635
+	 * try to enqueue them in the header, otherwise they'll be enqueued in the footer.
636
+	 * Not doing_it_wrong because theoretically this CAN be used properly,
637
+	 * provided its used during "wp_enqueue_scripts", or it doesn't need to enqueue
638
+	 * any CSS.
639
+	 *
640
+	 * @throws InvalidArgumentException
641
+	 * @throws InvalidInterfaceException
642
+	 * @throws InvalidDataTypeException
643
+	 * @throws EE_Error
644
+	 */
645
+	public function get_html_and_js()
646
+	{
647
+		$this->enqueue_js();
648
+		return $this->get_html();
649
+	}
650
+
651
+
652
+	/**
653
+	 * returns HTML for displaying this form section. recursively calls display_section() on all subsections
654
+	 *
655
+	 * @param bool $display_previously_submitted_data
656
+	 * @return string
657
+	 * @throws InvalidArgumentException
658
+	 * @throws InvalidInterfaceException
659
+	 * @throws InvalidDataTypeException
660
+	 * @throws EE_Error
661
+	 * @throws EE_Error
662
+	 * @throws EE_Error
663
+	 */
664
+	public function get_html($display_previously_submitted_data = true)
665
+	{
666
+		$this->ensure_construct_finalized_called();
667
+		if ($display_previously_submitted_data) {
668
+			$this->populate_from_session();
669
+		}
670
+		return $this->_form_html_filter
671
+			? $this->_form_html_filter->filterHtml($this->_layout_strategy->layout_form(), $this)
672
+			: $this->_layout_strategy->layout_form();
673
+	}
674
+
675
+
676
+	/**
677
+	 * enqueues JS and CSS for the form.
678
+	 * It is preferred to call this before wp_enqueue_scripts so the
679
+	 * scripts and styles can be put in the header, but if called later
680
+	 * they will be put in the footer (which is OK for JS, but in HTML4 CSS should
681
+	 * only be in the header; but in HTML5 its ok in the body.
682
+	 * See http://stackoverflow.com/questions/4957446/load-external-css-file-in-body-tag.
683
+	 * So if your form enqueues CSS, it's preferred to call this before wp_enqueue_scripts.)
684
+	 *
685
+	 * @return void
686
+	 * @throws EE_Error
687
+	 */
688
+	public function enqueue_js()
689
+	{
690
+		$this->_enqueue_and_localize_form_js();
691
+		foreach ($this->subsections() as $subsection) {
692
+			$subsection->enqueue_js();
693
+		}
694
+	}
695
+
696
+
697
+	/**
698
+	 * adds a filter so that jquery validate gets enqueued in EE_System::wp_enqueue_scripts().
699
+	 * This must be done BEFORE wp_enqueue_scripts() gets called, which is on
700
+	 * the wp_enqueue_scripts hook.
701
+	 * However, registering the form js and localizing it can happen when we
702
+	 * actually output the form (which is preferred, seeing how teh form's fields
703
+	 * could change until it's actually outputted)
704
+	 *
705
+	 * @param boolean $init_form_validation_automatically whether or not we want the form validation
706
+	 *                                                    to be triggered automatically or not
707
+	 * @return void
708
+	 */
709
+	public static function wp_enqueue_scripts($init_form_validation_automatically = true)
710
+	{
711
+		wp_register_script(
712
+			'ee_form_section_validation',
713
+			EE_GLOBAL_ASSETS_URL . 'scripts' . DS . 'form_section_validation.js',
714
+			array('jquery-validate', 'jquery-ui-datepicker', 'jquery-validate-extra-methods'),
715
+			EVENT_ESPRESSO_VERSION,
716
+			true
717
+		);
718
+		wp_localize_script(
719
+			'ee_form_section_validation',
720
+			'ee_form_section_validation_init',
721
+			array('init' => $init_form_validation_automatically ? '1' : '0')
722
+		);
723
+	}
724
+
725
+
726
+	/**
727
+	 * gets the variables used by form_section_validation.js.
728
+	 * This needs to be called AFTER we've called $this->_enqueue_jquery_validate_script,
729
+	 * but before the wordpress hook wp_loaded
730
+	 *
731
+	 * @throws EE_Error
732
+	 */
733
+	public function _enqueue_and_localize_form_js()
734
+	{
735
+		$this->ensure_construct_finalized_called();
736
+		//actually, we don't want to localize just yet. There may be other forms on the page.
737
+		//so we need to add our form section data to a static variable accessible by all form sections
738
+		//and localize it just before the footer
739
+		$this->localize_validation_rules();
740
+		add_action('wp_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'), 2);
741
+		add_action('admin_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'));
742
+	}
743
+
744
+
745
+	/**
746
+	 * add our form section data to a static variable accessible by all form sections
747
+	 *
748
+	 * @param bool $return_for_subsection
749
+	 * @return void
750
+	 * @throws EE_Error
751
+	 */
752
+	public function localize_validation_rules($return_for_subsection = false)
753
+	{
754
+		// we only want to localize vars ONCE for the entire form,
755
+		// so if the form section doesn't have a parent, then it must be the top dog
756
+		if ($return_for_subsection || ! $this->parent_section()) {
757
+			EE_Form_Section_Proper::$_js_localization['form_data'][ $this->html_id() ] = array(
758
+				'form_section_id'  => $this->html_id(true),
759
+				'validation_rules' => $this->get_jquery_validation_rules(),
760
+				'other_data'       => $this->get_other_js_data(),
761
+				'errors'           => $this->subsection_validation_errors_by_html_name(),
762
+			);
763
+			EE_Form_Section_Proper::$_scripts_localized                                = true;
764
+		}
765
+	}
766
+
767
+
768
+	/**
769
+	 * Gets an array of extra data that will be useful for client-side javascript.
770
+	 * This is primarily data added by inputs and forms in addition to any
771
+	 * scripts they might enqueue
772
+	 *
773
+	 * @param array $form_other_js_data
774
+	 * @return array
775
+	 * @throws EE_Error
776
+	 */
777
+	public function get_other_js_data($form_other_js_data = array())
778
+	{
779
+		foreach ($this->subsections() as $subsection) {
780
+			$form_other_js_data = $subsection->get_other_js_data($form_other_js_data);
781
+		}
782
+		return $form_other_js_data;
783
+	}
784
+
785
+
786
+	/**
787
+	 * Gets a flat array of inputs for this form section and its subsections.
788
+	 * Keys are their form names, and values are the inputs themselves
789
+	 *
790
+	 * @return EE_Form_Input_Base
791
+	 * @throws EE_Error
792
+	 */
793
+	public function inputs_in_subsections()
794
+	{
795
+		$inputs = array();
796
+		foreach ($this->subsections() as $subsection) {
797
+			if ($subsection instanceof EE_Form_Input_Base) {
798
+				$inputs[ $subsection->html_name() ] = $subsection;
799
+			} elseif ($subsection instanceof EE_Form_Section_Proper) {
800
+				$inputs += $subsection->inputs_in_subsections();
801
+			}
802
+		}
803
+		return $inputs;
804
+	}
805
+
806
+
807
+	/**
808
+	 * Gets a flat array of all the validation errors.
809
+	 * Keys are html names (because those should be unique)
810
+	 * and values are a string of all their validation errors
811
+	 *
812
+	 * @return string[]
813
+	 * @throws EE_Error
814
+	 */
815
+	public function subsection_validation_errors_by_html_name()
816
+	{
817
+		$inputs = $this->inputs();
818
+		$errors = array();
819
+		foreach ($inputs as $form_input) {
820
+			if ($form_input instanceof EE_Form_Input_Base && $form_input->get_validation_errors()) {
821
+				$errors[ $form_input->html_name() ] = $form_input->get_validation_error_string();
822
+			}
823
+		}
824
+		return $errors;
825
+	}
826
+
827
+
828
+	/**
829
+	 * passes all the form data required by the JS to the JS, and enqueues the few required JS files.
830
+	 * Should be setup by each form during the _enqueues_and_localize_form_js
831
+	 *
832
+	 * @throws InvalidArgumentException
833
+	 * @throws InvalidInterfaceException
834
+	 * @throws InvalidDataTypeException
835
+	 */
836
+	public static function localize_script_for_all_forms()
837
+	{
838
+		//allow inputs and stuff to hook in their JS and stuff here
839
+		do_action('AHEE__EE_Form_Section_Proper__localize_script_for_all_forms__begin');
840
+		EE_Form_Section_Proper::$_js_localization['localized_error_messages'] = EE_Form_Section_Proper::_get_localized_error_messages();
841
+		$email_validation_level = isset(EE_Registry::instance()->CFG->registration->email_validation_level)
842
+			? EE_Registry::instance()->CFG->registration->email_validation_level
843
+			: 'wp_default';
844
+		EE_Form_Section_Proper::$_js_localization['email_validation_level']   = $email_validation_level;
845
+		wp_enqueue_script('ee_form_section_validation');
846
+		wp_localize_script(
847
+			'ee_form_section_validation',
848
+			'ee_form_section_vars',
849
+			EE_Form_Section_Proper::$_js_localization
850
+		);
851
+	}
852
+
853
+
854
+	/**
855
+	 * ensure_scripts_localized
856
+	 *
857
+	 * @throws EE_Error
858
+	 */
859
+	public function ensure_scripts_localized()
860
+	{
861
+		if (! EE_Form_Section_Proper::$_scripts_localized) {
862
+			$this->_enqueue_and_localize_form_js();
863
+		}
864
+	}
865
+
866
+
867
+	/**
868
+	 * Gets the hard-coded validation error messages to be used in the JS. The convention
869
+	 * is that the key here should be the same as the custom validation rule put in the JS file
870
+	 *
871
+	 * @return array keys are custom validation rules, and values are internationalized strings
872
+	 */
873
+	private static function _get_localized_error_messages()
874
+	{
875
+		return array(
876
+			'validUrl' => esc_html__('This is not a valid absolute URL. Eg, http://domain.com/monkey.jpg', 'event_espresso'),
877
+			'regex'    => esc_html__('Please check your input', 'event_espresso'),
878
+		);
879
+	}
880
+
881
+
882
+	/**
883
+	 * @return array
884
+	 */
885
+	public static function js_localization()
886
+	{
887
+		return self::$_js_localization;
888
+	}
889
+
890
+
891
+	/**
892
+	 * @return void
893
+	 */
894
+	public static function reset_js_localization()
895
+	{
896
+		self::$_js_localization = array();
897
+	}
898
+
899
+
900
+	/**
901
+	 * Gets the JS to put inside the jquery validation rules for subsection of this form section.
902
+	 * See parent function for more...
903
+	 *
904
+	 * @return array
905
+	 * @throws EE_Error
906
+	 */
907
+	public function get_jquery_validation_rules()
908
+	{
909
+		$jquery_validation_rules = array();
910
+		foreach ($this->get_validatable_subsections() as $subsection) {
911
+			$jquery_validation_rules = array_merge(
912
+				$jquery_validation_rules,
913
+				$subsection->get_jquery_validation_rules()
914
+			);
915
+		}
916
+		return $jquery_validation_rules;
917
+	}
918
+
919
+
920
+	/**
921
+	 * Sanitizes all the data and sets the sanitized value of each field
922
+	 *
923
+	 * @param array $req_data like $_POST
924
+	 * @return void
925
+	 * @throws EE_Error
926
+	 */
927
+	protected function _normalize($req_data)
928
+	{
929
+		$this->_received_submission = true;
930
+		$this->_validation_errors   = array();
931
+		foreach ($this->get_validatable_subsections() as $subsection) {
932
+			try {
933
+				$subsection->_normalize($req_data);
934
+			} catch (EE_Validation_Error $e) {
935
+				$subsection->add_validation_error($e);
936
+			}
937
+		}
938
+	}
939
+
940
+
941
+	/**
942
+	 * Performs validation on this form section and its subsections.
943
+	 * For each subsection,
944
+	 * calls _validate_{subsection_name} on THIS form (if the function exists)
945
+	 * and passes it the subsection, then calls _validate on that subsection.
946
+	 * If you need to perform validation on the form as a whole (considering multiple)
947
+	 * you would be best to override this _validate method,
948
+	 * calling parent::_validate() first.
949
+	 *
950
+	 * @throws EE_Error
951
+	 */
952
+	protected function _validate()
953
+	{
954
+		//reset the cache of whether this form is valid or not- we're re-validating it now
955
+		$this->is_valid = null;
956
+		foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
957
+			if (method_exists($this, '_validate_' . $subsection_name)) {
958
+				call_user_func_array(array($this, '_validate_' . $subsection_name), array($subsection));
959
+			}
960
+			$subsection->_validate();
961
+		}
962
+	}
963
+
964
+
965
+	/**
966
+	 * Gets all the validated inputs for the form section
967
+	 *
968
+	 * @return array
969
+	 * @throws EE_Error
970
+	 */
971
+	public function valid_data()
972
+	{
973
+		$inputs = array();
974
+		foreach ($this->subsections() as $subsection_name => $subsection) {
975
+			if ($subsection instanceof EE_Form_Section_Proper) {
976
+				$inputs[ $subsection_name ] = $subsection->valid_data();
977
+			} elseif ($subsection instanceof EE_Form_Input_Base) {
978
+				$inputs[ $subsection_name ] = $subsection->normalized_value();
979
+			}
980
+		}
981
+		return $inputs;
982
+	}
983
+
984
+
985
+	/**
986
+	 * Gets all the inputs on this form section
987
+	 *
988
+	 * @return EE_Form_Input_Base[]
989
+	 * @throws EE_Error
990
+	 */
991
+	public function inputs()
992
+	{
993
+		$inputs = array();
994
+		foreach ($this->subsections() as $subsection_name => $subsection) {
995
+			if ($subsection instanceof EE_Form_Input_Base) {
996
+				$inputs[ $subsection_name ] = $subsection;
997
+			}
998
+		}
999
+		return $inputs;
1000
+	}
1001
+
1002
+
1003
+	/**
1004
+	 * Gets all the subsections which are a proper form
1005
+	 *
1006
+	 * @return EE_Form_Section_Proper[]
1007
+	 * @throws EE_Error
1008
+	 */
1009
+	public function subforms()
1010
+	{
1011
+		$form_sections = array();
1012
+		foreach ($this->subsections() as $name => $obj) {
1013
+			if ($obj instanceof EE_Form_Section_Proper) {
1014
+				$form_sections[ $name ] = $obj;
1015
+			}
1016
+		}
1017
+		return $form_sections;
1018
+	}
1019
+
1020
+
1021
+	/**
1022
+	 * Gets all the subsections (inputs, proper subsections, or html-only sections).
1023
+	 * Consider using inputs() or subforms()
1024
+	 * if you only want form inputs or proper form sections.
1025
+	 *
1026
+	 * @param boolean $require_construction_to_be_finalized most client code should
1027
+	 *                                                      leave this as TRUE so that the inputs will be properly
1028
+	 *                                                      configured. However, some client code may be ok with
1029
+	 *                                                      construction finalize being called later
1030
+	 *                                                      (realizing that the subsections' html names might not be
1031
+	 *                                                      set yet, etc.)
1032
+	 * @return EE_Form_Section_Proper[]
1033
+	 * @throws EE_Error
1034
+	 */
1035
+	public function subsections($require_construction_to_be_finalized = true)
1036
+	{
1037
+		if ($require_construction_to_be_finalized) {
1038
+			$this->ensure_construct_finalized_called();
1039
+		}
1040
+		return $this->_subsections;
1041
+	}
1042
+
1043
+
1044
+	/**
1045
+	 * Returns whether this form has any subforms or inputs
1046
+	 * @return bool
1047
+	 */
1048
+	public function hasSubsections()
1049
+	{
1050
+		return ! empty($this->_subsections);
1051
+	}
1052
+
1053
+
1054
+	/**
1055
+	 * Returns a simple array where keys are input names, and values are their normalized
1056
+	 * values. (Similar to calling get_input_value on inputs)
1057
+	 *
1058
+	 * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1059
+	 *                                        or just this forms' direct children inputs
1060
+	 * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1061
+	 *                                        or allow multidimensional array
1062
+	 * @return array if $flatten is TRUE it will always be a 1-dimensional array
1063
+	 *                                        with array keys being input names
1064
+	 *                                        (regardless of whether they are from a subsection or not),
1065
+	 *                                        and if $flatten is FALSE it can be a multidimensional array
1066
+	 *                                        where keys are always subsection names and values are either
1067
+	 *                                        the input's normalized value, or an array like the top-level array
1068
+	 * @throws EE_Error
1069
+	 */
1070
+	public function input_values($include_subform_inputs = false, $flatten = false)
1071
+	{
1072
+		return $this->_input_values(false, $include_subform_inputs, $flatten);
1073
+	}
1074
+
1075
+
1076
+	/**
1077
+	 * Similar to EE_Form_Section_Proper::input_values(), except this returns the 'display_value'
1078
+	 * of each input. On some inputs (especially radio boxes or checkboxes), the value stored
1079
+	 * is not necessarily the value we want to display to users. This creates an array
1080
+	 * where keys are the input names, and values are their display values
1081
+	 *
1082
+	 * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1083
+	 *                                        or just this forms' direct children inputs
1084
+	 * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1085
+	 *                                        or allow multidimensional array
1086
+	 * @return array if $flatten is TRUE it will always be a 1-dimensional array
1087
+	 *                                        with array keys being input names
1088
+	 *                                        (regardless of whether they are from a subsection or not),
1089
+	 *                                        and if $flatten is FALSE it can be a multidimensional array
1090
+	 *                                        where keys are always subsection names and values are either
1091
+	 *                                        the input's normalized value, or an array like the top-level array
1092
+	 * @throws EE_Error
1093
+	 */
1094
+	public function input_pretty_values($include_subform_inputs = false, $flatten = false)
1095
+	{
1096
+		return $this->_input_values(true, $include_subform_inputs, $flatten);
1097
+	}
1098
+
1099
+
1100
+	/**
1101
+	 * Gets the input values from the form
1102
+	 *
1103
+	 * @param boolean $pretty                 Whether to retrieve the pretty value,
1104
+	 *                                        or just the normalized value
1105
+	 * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1106
+	 *                                        or just this forms' direct children inputs
1107
+	 * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1108
+	 *                                        or allow multidimensional array
1109
+	 * @return array if $flatten is TRUE it will always be a 1-dimensional array with array keys being
1110
+	 *                                        input names (regardless of whether they are from a subsection or not),
1111
+	 *                                        and if $flatten is FALSE it can be a multidimensional array
1112
+	 *                                        where keys are always subsection names and values are either
1113
+	 *                                        the input's normalized value, or an array like the top-level array
1114
+	 * @throws EE_Error
1115
+	 */
1116
+	public function _input_values($pretty = false, $include_subform_inputs = false, $flatten = false)
1117
+	{
1118
+		$input_values = array();
1119
+		foreach ($this->subsections() as $subsection_name => $subsection) {
1120
+			if ($subsection instanceof EE_Form_Input_Base) {
1121
+				$input_values[ $subsection_name ] = $pretty
1122
+					? $subsection->pretty_value()
1123
+					: $subsection->normalized_value();
1124
+			} elseif ($subsection instanceof EE_Form_Section_Proper && $include_subform_inputs) {
1125
+				$subform_input_values = $subsection->_input_values(
1126
+					$pretty,
1127
+					$include_subform_inputs,
1128
+					$flatten
1129
+				);
1130
+				if ($flatten) {
1131
+					$input_values = array_merge($input_values, $subform_input_values);
1132
+				} else {
1133
+					$input_values[ $subsection_name ] = $subform_input_values;
1134
+				}
1135
+			}
1136
+		}
1137
+		return $input_values;
1138
+	}
1139
+
1140
+
1141
+	/**
1142
+	 * Gets the originally submitted input values from the form
1143
+	 *
1144
+	 * @param boolean $include_subforms  Whether to include inputs from subforms,
1145
+	 *                                   or just this forms' direct children inputs
1146
+	 * @return array                     if $flatten is TRUE it will always be a 1-dimensional array
1147
+	 *                                   with array keys being input names
1148
+	 *                                   (regardless of whether they are from a subsection or not),
1149
+	 *                                   and if $flatten is FALSE it can be a multidimensional array
1150
+	 *                                   where keys are always subsection names and values are either
1151
+	 *                                   the input's normalized value, or an array like the top-level array
1152
+	 * @throws EE_Error
1153
+	 */
1154
+	public function submitted_values($include_subforms = false)
1155
+	{
1156
+		$submitted_values = array();
1157
+		foreach ($this->subsections() as $subsection) {
1158
+			if ($subsection instanceof EE_Form_Input_Base) {
1159
+				// is this input part of an array of inputs?
1160
+				if (strpos($subsection->html_name(), '[') !== false) {
1161
+					$full_input_name  = EEH_Array::convert_array_values_to_keys(
1162
+						explode(
1163
+							'[',
1164
+							str_replace(']', '', $subsection->html_name())
1165
+						),
1166
+						$subsection->raw_value()
1167
+					);
1168
+					$submitted_values = array_replace_recursive($submitted_values, $full_input_name);
1169
+				} else {
1170
+					$submitted_values[ $subsection->html_name() ] = $subsection->raw_value();
1171
+				}
1172
+			} elseif ($subsection instanceof EE_Form_Section_Proper && $include_subforms) {
1173
+				$subform_input_values = $subsection->submitted_values($include_subforms);
1174
+				$submitted_values     = array_replace_recursive($submitted_values, $subform_input_values);
1175
+			}
1176
+		}
1177
+		return $submitted_values;
1178
+	}
1179
+
1180
+
1181
+	/**
1182
+	 * Indicates whether or not this form has received a submission yet
1183
+	 * (ie, had receive_form_submission called on it yet)
1184
+	 *
1185
+	 * @return boolean
1186
+	 * @throws EE_Error
1187
+	 */
1188
+	public function has_received_submission()
1189
+	{
1190
+		$this->ensure_construct_finalized_called();
1191
+		return $this->_received_submission;
1192
+	}
1193
+
1194
+
1195
+	/**
1196
+	 * Equivalent to passing 'exclude' in the constructor's options array.
1197
+	 * Removes the listed inputs from the form
1198
+	 *
1199
+	 * @param array $inputs_to_exclude values are the input names
1200
+	 * @return void
1201
+	 */
1202
+	public function exclude(array $inputs_to_exclude = array())
1203
+	{
1204
+		foreach ($inputs_to_exclude as $input_to_exclude_name) {
1205
+			unset($this->_subsections[ $input_to_exclude_name ]);
1206
+		}
1207
+	}
1208
+
1209
+
1210
+	/**
1211
+	 * Changes these inputs' display strategy to be EE_Hidden_Display_Strategy.
1212
+	 * @param array $inputs_to_hide
1213
+	 * @throws EE_Error
1214
+	 */
1215
+	public function hide(array $inputs_to_hide = array())
1216
+	{
1217
+		foreach ($inputs_to_hide as $input_to_hide) {
1218
+			$input = $this->get_input($input_to_hide);
1219
+			$input->set_display_strategy(new EE_Hidden_Display_Strategy());
1220
+		}
1221
+	}
1222
+
1223
+
1224
+	/**
1225
+	 * add_subsections
1226
+	 * Adds the listed subsections to the form section.
1227
+	 * If $subsection_name_to_target is provided,
1228
+	 * then new subsections are added before or after that subsection,
1229
+	 * otherwise to the start or end of the entire subsections array.
1230
+	 *
1231
+	 * @param EE_Form_Section_Base[] $new_subsections           array of new form subsections
1232
+	 *                                                          where keys are their names
1233
+	 * @param string                 $subsection_name_to_target an existing for section that $new_subsections
1234
+	 *                                                          should be added before or after
1235
+	 *                                                          IF $subsection_name_to_target is null,
1236
+	 *                                                          then $new_subsections will be added to
1237
+	 *                                                          the beginning or end of the entire subsections array
1238
+	 * @param boolean                $add_before                whether to add $new_subsections, before or after
1239
+	 *                                                          $subsection_name_to_target,
1240
+	 *                                                          or if $subsection_name_to_target is null,
1241
+	 *                                                          before or after entire subsections array
1242
+	 * @return void
1243
+	 * @throws EE_Error
1244
+	 */
1245
+	public function add_subsections($new_subsections, $subsection_name_to_target = null, $add_before = true)
1246
+	{
1247
+		foreach ($new_subsections as $subsection_name => $subsection) {
1248
+			if (! $subsection instanceof EE_Form_Section_Base) {
1249
+				EE_Error::add_error(
1250
+					sprintf(
1251
+						esc_html__(
1252
+							"Trying to add a %s as a subsection (it was named '%s') to the form section '%s'. It was removed.",
1253
+							'event_espresso'
1254
+						),
1255
+						get_class($subsection),
1256
+						$subsection_name,
1257
+						$this->name()
1258
+					)
1259
+				);
1260
+				unset($new_subsections[ $subsection_name ]);
1261
+			}
1262
+		}
1263
+		$this->_subsections = EEH_Array::insert_into_array(
1264
+			$this->_subsections,
1265
+			$new_subsections,
1266
+			$subsection_name_to_target,
1267
+			$add_before
1268
+		);
1269
+		if ($this->_construction_finalized) {
1270
+			foreach ($this->_subsections as $name => $subsection) {
1271
+				$subsection->_construct_finalize($this, $name);
1272
+			}
1273
+		}
1274
+	}
1275
+
1276
+
1277
+	/**
1278
+	 * Just gets all validatable subsections to clean their sensitive data
1279
+	 *
1280
+	 * @throws EE_Error
1281
+	 */
1282
+	public function clean_sensitive_data()
1283
+	{
1284
+		foreach ($this->get_validatable_subsections() as $subsection) {
1285
+			$subsection->clean_sensitive_data();
1286
+		}
1287
+	}
1288
+
1289
+
1290
+	/**
1291
+	 * Sets the submission error message (aka validation error message for this form section and all sub-sections)
1292
+	 * @param string                           $form_submission_error_message
1293
+	 * @param EE_Form_Section_Validatable $form_section unused
1294
+	 * @throws EE_Error
1295
+	 */
1296
+	public function set_submission_error_message(
1297
+		$form_submission_error_message = ''
1298
+	) {
1299
+		$this->_form_submission_error_message = ! empty($form_submission_error_message)
1300
+			? $form_submission_error_message
1301
+			: $this->getAllValidationErrorsString();
1302
+	}
1303
+
1304
+
1305
+	/**
1306
+	 * Returns the cached error message. A default value is set for this during _validate(),
1307
+	 * (called during receive_form_submission) but it can be explicitly set using
1308
+	 * set_submission_error_message
1309
+	 *
1310
+	 * @return string
1311
+	 */
1312
+	public function submission_error_message()
1313
+	{
1314
+		return $this->_form_submission_error_message;
1315
+	}
1316
+
1317
+
1318
+	/**
1319
+	 * Sets a message to display if the data submitted to the form was valid.
1320
+	 * @param string $form_submission_success_message
1321
+	 */
1322
+	public function set_submission_success_message($form_submission_success_message = '')
1323
+	{
1324
+		$this->_form_submission_success_message = ! empty($form_submission_success_message)
1325
+			? $form_submission_success_message
1326
+			: esc_html__('Form submitted successfully', 'event_espresso');
1327
+	}
1328
+
1329
+
1330
+	/**
1331
+	 * Gets a message appropriate for display when the form is correctly submitted
1332
+	 * @return string
1333
+	 */
1334
+	public function submission_success_message()
1335
+	{
1336
+		return $this->_form_submission_success_message;
1337
+	}
1338
+
1339
+
1340
+	/**
1341
+	 * Returns the prefix that should be used on child of this form section for
1342
+	 * their html names. If this form section itself has a parent, prepends ITS
1343
+	 * prefix onto this form section's prefix. Used primarily by
1344
+	 * EE_Form_Input_Base::_set_default_html_name_if_empty
1345
+	 *
1346
+	 * @return string
1347
+	 * @throws EE_Error
1348
+	 */
1349
+	public function html_name_prefix()
1350
+	{
1351
+		if ($this->parent_section() instanceof EE_Form_Section_Proper) {
1352
+			return $this->parent_section()->html_name_prefix() . '[' . $this->name() . ']';
1353
+		}
1354
+		return $this->name();
1355
+	}
1356
+
1357
+
1358
+	/**
1359
+	 * Gets the name, but first checks _construct_finalize has been called. If not,
1360
+	 * calls it (assumes there is no parent and that we want the name to be whatever
1361
+	 * was set, which is probably nothing, or the classname)
1362
+	 *
1363
+	 * @return string
1364
+	 * @throws EE_Error
1365
+	 */
1366
+	public function name()
1367
+	{
1368
+		$this->ensure_construct_finalized_called();
1369
+		return parent::name();
1370
+	}
1371
+
1372
+
1373
+	/**
1374
+	 * @return EE_Form_Section_Proper
1375
+	 * @throws EE_Error
1376
+	 */
1377
+	public function parent_section()
1378
+	{
1379
+		$this->ensure_construct_finalized_called();
1380
+		return parent::parent_section();
1381
+	}
1382
+
1383
+
1384
+	/**
1385
+	 * make sure construction finalized was called, otherwise children might not be ready
1386
+	 *
1387
+	 * @return void
1388
+	 * @throws EE_Error
1389
+	 */
1390
+	public function ensure_construct_finalized_called()
1391
+	{
1392
+		if (! $this->_construction_finalized) {
1393
+			$this->_construct_finalize($this->_parent_section, $this->_name);
1394
+		}
1395
+	}
1396
+
1397
+
1398
+	/**
1399
+	 * Checks if any of this form section's inputs, or any of its children's inputs,
1400
+	 * are in teh form data. If any are found, returns true. Else false
1401
+	 *
1402
+	 * @param array $req_data
1403
+	 * @return boolean
1404
+	 * @throws EE_Error
1405
+	 */
1406
+	public function form_data_present_in($req_data = null)
1407
+	{
1408
+		$req_data = $this->getCachedRequest($req_data);
1409
+		foreach ($this->subsections() as $subsection) {
1410
+			if ($subsection instanceof EE_Form_Input_Base) {
1411
+				if ($subsection->form_data_present_in($req_data)) {
1412
+					return true;
1413
+				}
1414
+			} elseif ($subsection instanceof EE_Form_Section_Proper) {
1415
+				if ($subsection->form_data_present_in($req_data)) {
1416
+					return true;
1417
+				}
1418
+			}
1419
+		}
1420
+		return false;
1421
+	}
1422
+
1423
+
1424
+	/**
1425
+	 * Gets validation errors for this form section and subsections
1426
+	 * Similar to EE_Form_Section_Validatable::get_validation_errors() except this
1427
+	 * gets the validation errors for ALL subsection
1428
+	 *
1429
+	 * @return EE_Validation_Error[]
1430
+	 * @throws EE_Error
1431
+	 */
1432
+	public function get_validation_errors_accumulated()
1433
+	{
1434
+		$validation_errors = $this->get_validation_errors();
1435
+		foreach ($this->get_validatable_subsections() as $subsection) {
1436
+			if ($subsection instanceof EE_Form_Section_Proper) {
1437
+				$validation_errors_on_this_subsection = $subsection->get_validation_errors_accumulated();
1438
+			} else {
1439
+				$validation_errors_on_this_subsection = $subsection->get_validation_errors();
1440
+			}
1441
+			if ($validation_errors_on_this_subsection) {
1442
+				$validation_errors = array_merge($validation_errors, $validation_errors_on_this_subsection);
1443
+			}
1444
+		}
1445
+		return $validation_errors;
1446
+	}
1447
+
1448
+	/**
1449
+	 * Fetch validation errors from children and grandchildren and puts them in a single string.
1450
+	 * This traverses the form section tree to generate this, but you probably want to instead use
1451
+	 * get_form_submission_error_message() which is usually this message cached (or a custom validation error message)
1452
+	 *
1453
+	 * @return string
1454
+	 * @since $VID:$
1455
+	 */
1456
+	protected function getAllValidationErrorsString()
1457
+	{
1458
+		$submission_error_messages = array();
1459
+		// bad, bad, bad registrant
1460
+		foreach ($this->get_validation_errors_accumulated() as $validation_error) {
1461
+			if ($validation_error instanceof EE_Validation_Error) {
1462
+				$form_section = $validation_error->get_form_section();
1463
+				if ($form_section instanceof EE_Form_Input_Base) {
1464
+				   $label = $validation_error->get_form_section()->html_label_text();
1465
+				} elseif($form_section instanceof EE_Form_Section_Validatable) {
1466
+					$label = $validation_error->get_form_section()->name();
1467
+				} else {
1468
+					$label = esc_html__('Unknown', 'event_espresso');
1469
+				}
1470
+				$submission_error_messages[] = sprintf(
1471
+					__('%s : %s', 'event_espresso'),
1472
+					$label,
1473
+					$validation_error->getMessage()
1474
+				);
1475
+			}
1476
+		}
1477
+		return implode('<br', $submission_error_messages);
1478
+	}
1479
+
1480
+
1481
+	/**
1482
+	 * This isn't just the name of an input, it's a path pointing to an input. The
1483
+	 * path is similar to a folder path: slash (/) means to descend into a subsection,
1484
+	 * dot-dot-slash (../) means to ascend into the parent section.
1485
+	 * After a series of slashes and dot-dot-slashes, there should be the name of an input,
1486
+	 * which will be returned.
1487
+	 * Eg, if you want the related input to be conditional on a sibling input name 'foobar'
1488
+	 * just use 'foobar'. If you want it to be conditional on an aunt/uncle input name
1489
+	 * 'baz', use '../baz'. If you want it to be conditional on a cousin input,
1490
+	 * the child of 'baz_section' named 'baz_child', use '../baz_section/baz_child'.
1491
+	 * Etc
1492
+	 *
1493
+	 * @param string|false $form_section_path we accept false also because substr( '../', '../' ) = false
1494
+	 * @return EE_Form_Section_Base
1495
+	 * @throws EE_Error
1496
+	 */
1497
+	public function find_section_from_path($form_section_path)
1498
+	{
1499
+		//check if we can find the input from purely going straight up the tree
1500
+		$input = parent::find_section_from_path($form_section_path);
1501
+		if ($input instanceof EE_Form_Section_Base) {
1502
+			return $input;
1503
+		}
1504
+		$next_slash_pos = strpos($form_section_path, '/');
1505
+		if ($next_slash_pos !== false) {
1506
+			$child_section_name = substr($form_section_path, 0, $next_slash_pos);
1507
+			$subpath            = substr($form_section_path, $next_slash_pos + 1);
1508
+		} else {
1509
+			$child_section_name = $form_section_path;
1510
+			$subpath            = '';
1511
+		}
1512
+		$child_section = $this->get_subsection($child_section_name);
1513
+		if ($child_section instanceof EE_Form_Section_Base) {
1514
+			return $child_section->find_section_from_path($subpath);
1515
+		}
1516
+		return null;
1517
+	}
1518 1518
 }
1519 1519
 
Please login to merge, or discard this patch.
core/libraries/form_sections/base/EE_Form_Section_Validatable.form.php 1 patch
Indentation   +147 added lines, -147 removed lines patch added patch discarded remove patch
@@ -1,6 +1,6 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 if (! defined('EVENT_ESPRESSO_VERSION')) {
3
-    exit('No direct script access allowed');
3
+	exit('No direct script access allowed');
4 4
 }
5 5
 
6 6
 
@@ -35,150 +35,150 @@  discard block
 block discarded – undo
35 35
 abstract class EE_Form_Section_Validatable extends EE_Form_Section_Base
36 36
 {
37 37
 
38
-    /**
39
-     * Array of validation errors in this section. Does not contain validation errors in subsections, however.
40
-     * Those are stored individually on each subsection.
41
-     *
42
-     * @var EE_Validation_Error[]
43
-     */
44
-    protected $_validation_errors = array();
45
-
46
-
47
-
48
-    /**
49
-     * Errors on this form section. Note: EE_Form_Section_Proper
50
-     * has another function for getting all errors in this form section and subsections
51
-     * called get_validation_errors_accumulated
52
-     *
53
-     * @return EE_Validation_Error[]
54
-     */
55
-    public function get_validation_errors()
56
-    {
57
-        return $this->_validation_errors;
58
-    }
59
-
60
-
61
-
62
-    /**
63
-     * returns a comma-separated list of all the validation errors in it.
64
-     * If we want this to be customizable, we may decide to create a strategy for displaying it
65
-     *
66
-     * @return string
67
-     */
68
-    public function get_validation_error_string()
69
-    {
70
-        $validation_error_messages = array();
71
-        if ($this->get_validation_errors()) {
72
-            foreach ($this->get_validation_errors() as $validation_error) {
73
-                if ($validation_error instanceof EE_Validation_Error) {
74
-                    $validation_error_messages[] = $validation_error->getMessage();
75
-                }
76
-            }
77
-        }
78
-        return implode(", ", $validation_error_messages);
79
-    }
80
-
81
-
82
-
83
-    /**
84
-     * Performs validation on this form section (and subsections). Should be called after _normalize()
85
-     *
86
-     * @return boolean of whether or not the form section is valid
87
-     */
88
-    abstract protected function _validate();
89
-
90
-
91
-
92
-    /**
93
-     * Checks if this field has any validation errors
94
-     *
95
-     * @return boolean
96
-     */
97
-    public function is_valid()
98
-    {
99
-        if (count($this->_validation_errors)) {
100
-            return false;
101
-        } else {
102
-            return true;
103
-        }
104
-    }
105
-
106
-
107
-
108
-    /**
109
-     * Sanitizes input for this form section
110
-     *
111
-     * @param array $req_data is the full request data like $_POST
112
-     * @return boolean of whether a normalization error occurred
113
-     */
114
-    abstract protected function _normalize($req_data);
115
-
116
-
117
-
118
-    /**
119
-     * Creates a validation error from the arguments provided, and adds it to the form section's list.
120
-     * If such an EE_Validation_Error object is passed in as the first arg, simply sets this as its form section, and
121
-     * adds it to the list of validation errors of errors
122
-     *
123
-     * @param mixed     $message_or_object  internationalized string describing the validation error; or it could be a
124
-     *                                      proper EE_Validation_Error object
125
-     * @param string    $error_code         a short key which can be used to uniquely identify the error
126
-     * @param Exception $previous_exception if there was an exception that caused the error, that exception
127
-     * @return void
128
-     */
129
-    public function add_validation_error($message_or_object, $error_code = null, $previous_exception = null)
130
-    {
131
-        if ($message_or_object instanceof EE_Validation_Error) {
132
-            $validation_error = $message_or_object;
133
-            $validation_error->set_form_section($this);
134
-        } else {
135
-            $validation_error = new EE_Validation_Error($message_or_object, $error_code, $this, $previous_exception);
136
-        }
137
-        $this->_validation_errors[] = $validation_error;
138
-    }
139
-
140
-
141
-
142
-    /**
143
-     * When generating the JS for the jquery validation rules like<br>
144
-     * <code>$( "#myform" ).validate({
145
-     * rules: {
146
-     * password: "required",
147
-     * password_again: {
148
-     * equalTo: "#password"
149
-     * }
150
-     * }
151
-     * });</code>
152
-     * gets the sections like
153
-     * <br><code>password: "required",
154
-     * password_again: {
155
-     * equalTo: "#password"
156
-     * }</code>
157
-     * except we leave it as a PHP object, and leave wp_localize_script to
158
-     * turn it into a JSON object which can be used by the js
159
-     *
160
-     * @return array
161
-     */
162
-    abstract public function get_jquery_validation_rules();
163
-
164
-
165
-
166
-    /**
167
-     * Checks if this form section's data is present in the req data specified
168
-     *
169
-     * @param array $req_data usually $_POST, if null that's what's used
170
-     * @return boolean
171
-     */
172
-    abstract public function form_data_present_in($req_data = null);
173
-
174
-
175
-
176
-    /**
177
-     * Removes teh sensitive data from this form section (usually done after
178
-     * utilizing the data business function, but before saving it somewhere. Eg,
179
-     * may remove a password from the form after verifying it was correct)
180
-     *
181
-     * @return void
182
-     */
183
-    abstract public function clean_sensitive_data();
38
+	/**
39
+	 * Array of validation errors in this section. Does not contain validation errors in subsections, however.
40
+	 * Those are stored individually on each subsection.
41
+	 *
42
+	 * @var EE_Validation_Error[]
43
+	 */
44
+	protected $_validation_errors = array();
45
+
46
+
47
+
48
+	/**
49
+	 * Errors on this form section. Note: EE_Form_Section_Proper
50
+	 * has another function for getting all errors in this form section and subsections
51
+	 * called get_validation_errors_accumulated
52
+	 *
53
+	 * @return EE_Validation_Error[]
54
+	 */
55
+	public function get_validation_errors()
56
+	{
57
+		return $this->_validation_errors;
58
+	}
59
+
60
+
61
+
62
+	/**
63
+	 * returns a comma-separated list of all the validation errors in it.
64
+	 * If we want this to be customizable, we may decide to create a strategy for displaying it
65
+	 *
66
+	 * @return string
67
+	 */
68
+	public function get_validation_error_string()
69
+	{
70
+		$validation_error_messages = array();
71
+		if ($this->get_validation_errors()) {
72
+			foreach ($this->get_validation_errors() as $validation_error) {
73
+				if ($validation_error instanceof EE_Validation_Error) {
74
+					$validation_error_messages[] = $validation_error->getMessage();
75
+				}
76
+			}
77
+		}
78
+		return implode(", ", $validation_error_messages);
79
+	}
80
+
81
+
82
+
83
+	/**
84
+	 * Performs validation on this form section (and subsections). Should be called after _normalize()
85
+	 *
86
+	 * @return boolean of whether or not the form section is valid
87
+	 */
88
+	abstract protected function _validate();
89
+
90
+
91
+
92
+	/**
93
+	 * Checks if this field has any validation errors
94
+	 *
95
+	 * @return boolean
96
+	 */
97
+	public function is_valid()
98
+	{
99
+		if (count($this->_validation_errors)) {
100
+			return false;
101
+		} else {
102
+			return true;
103
+		}
104
+	}
105
+
106
+
107
+
108
+	/**
109
+	 * Sanitizes input for this form section
110
+	 *
111
+	 * @param array $req_data is the full request data like $_POST
112
+	 * @return boolean of whether a normalization error occurred
113
+	 */
114
+	abstract protected function _normalize($req_data);
115
+
116
+
117
+
118
+	/**
119
+	 * Creates a validation error from the arguments provided, and adds it to the form section's list.
120
+	 * If such an EE_Validation_Error object is passed in as the first arg, simply sets this as its form section, and
121
+	 * adds it to the list of validation errors of errors
122
+	 *
123
+	 * @param mixed     $message_or_object  internationalized string describing the validation error; or it could be a
124
+	 *                                      proper EE_Validation_Error object
125
+	 * @param string    $error_code         a short key which can be used to uniquely identify the error
126
+	 * @param Exception $previous_exception if there was an exception that caused the error, that exception
127
+	 * @return void
128
+	 */
129
+	public function add_validation_error($message_or_object, $error_code = null, $previous_exception = null)
130
+	{
131
+		if ($message_or_object instanceof EE_Validation_Error) {
132
+			$validation_error = $message_or_object;
133
+			$validation_error->set_form_section($this);
134
+		} else {
135
+			$validation_error = new EE_Validation_Error($message_or_object, $error_code, $this, $previous_exception);
136
+		}
137
+		$this->_validation_errors[] = $validation_error;
138
+	}
139
+
140
+
141
+
142
+	/**
143
+	 * When generating the JS for the jquery validation rules like<br>
144
+	 * <code>$( "#myform" ).validate({
145
+	 * rules: {
146
+	 * password: "required",
147
+	 * password_again: {
148
+	 * equalTo: "#password"
149
+	 * }
150
+	 * }
151
+	 * });</code>
152
+	 * gets the sections like
153
+	 * <br><code>password: "required",
154
+	 * password_again: {
155
+	 * equalTo: "#password"
156
+	 * }</code>
157
+	 * except we leave it as a PHP object, and leave wp_localize_script to
158
+	 * turn it into a JSON object which can be used by the js
159
+	 *
160
+	 * @return array
161
+	 */
162
+	abstract public function get_jquery_validation_rules();
163
+
164
+
165
+
166
+	/**
167
+	 * Checks if this form section's data is present in the req data specified
168
+	 *
169
+	 * @param array $req_data usually $_POST, if null that's what's used
170
+	 * @return boolean
171
+	 */
172
+	abstract public function form_data_present_in($req_data = null);
173
+
174
+
175
+
176
+	/**
177
+	 * Removes teh sensitive data from this form section (usually done after
178
+	 * utilizing the data business function, but before saving it somewhere. Eg,
179
+	 * may remove a password from the form after verifying it was correct)
180
+	 *
181
+	 * @return void
182
+	 */
183
+	abstract public function clean_sensitive_data();
184 184
 }
185 185
\ No newline at end of file
Please login to merge, or discard this patch.
core/libraries/messages/messenger/EE_Email_messenger.class.php 1 patch
Indentation   +640 added lines, -640 removed lines patch added patch discarded remove patch
@@ -8,644 +8,644 @@
 block discarded – undo
8 8
 class EE_Email_messenger extends EE_messenger
9 9
 {
10 10
 
11
-    /**
12
-     * To field for email
13
-     * @var string
14
-     */
15
-    protected $_to = '';
16
-
17
-
18
-    /**
19
-     * CC field for email.
20
-     * @var string
21
-     */
22
-    protected $_cc = '';
23
-
24
-    /**
25
-     * From field for email
26
-     * @var string
27
-     */
28
-    protected $_from = '';
29
-
30
-
31
-    /**
32
-     * Subject field for email
33
-     * @var string
34
-     */
35
-    protected $_subject = '';
36
-
37
-
38
-    /**
39
-     * Content field for email
40
-     * @var string
41
-     */
42
-    protected $_content = '';
43
-
44
-
45
-    /**
46
-     * constructor
47
-     *
48
-     * @access public
49
-     */
50
-    public function __construct()
51
-    {
52
-        //set name and description properties
53
-        $this->name                = 'email';
54
-        $this->description         = sprintf(
55
-            esc_html__(
56
-                'This messenger delivers messages via email using the built-in %s function included with WordPress',
57
-                'event_espresso'
58
-            ),
59
-            '<code>wp_mail</code>'
60
-        );
61
-        $this->label               = array(
62
-            'singular' => esc_html__('email', 'event_espresso'),
63
-            'plural'   => esc_html__('emails', 'event_espresso'),
64
-        );
65
-        $this->activate_on_install = true;
66
-
67
-        //we're using defaults so let's call parent constructor that will take care of setting up all the other
68
-        // properties
69
-        parent::__construct();
70
-    }
71
-
72
-
73
-    /**
74
-     * see abstract declaration in parent class for details.
75
-     */
76
-    protected function _set_admin_pages()
77
-    {
78
-        $this->admin_registered_pages = array(
79
-            'events_edit' => true,
80
-        );
81
-    }
82
-
83
-
84
-    /**
85
-     * see abstract declaration in parent class for details
86
-     */
87
-    protected function _set_valid_shortcodes()
88
-    {
89
-        //remember by leaving the other fields not set, those fields will inherit the valid shortcodes from the
90
-        // message type.
91
-        $this->_valid_shortcodes = array(
92
-            'to'   => array('email', 'event_author', 'primary_registration_details', 'recipient_details'),
93
-            'cc' => array('email', 'event_author', 'primary_registration_details', 'recipient_details'),
94
-            'from' => array('email', 'event_author', 'primary_registration_details', 'recipient_details'),
95
-        );
96
-    }
97
-
98
-
99
-    /**
100
-     * see abstract declaration in parent class for details
101
-     *
102
-     * @access protected
103
-     * @return void
104
-     */
105
-    protected function _set_validator_config()
106
-    {
107
-        $valid_shortcodes = $this->get_valid_shortcodes();
108
-
109
-        $this->_validator_config = array(
110
-            'to'            => array(
111
-                'shortcodes' => $valid_shortcodes['to'],
112
-                'type'       => 'email',
113
-            ),
114
-            'cc' => array(
115
-                'shortcodes' => $valid_shortcodes['to'],
116
-                'type' => 'email',
117
-            ),
118
-            'from'          => array(
119
-                'shortcodes' => $valid_shortcodes['from'],
120
-                'type'       => 'email',
121
-            ),
122
-            'subject'       => array(
123
-                'shortcodes' => array(
124
-                    'organization',
125
-                    'primary_registration_details',
126
-                    'event_author',
127
-                    'primary_registration_details',
128
-                    'recipient_details',
129
-                ),
130
-            ),
131
-            'content'       => array(
132
-                'shortcodes' => array(
133
-                    'event_list',
134
-                    'attendee_list',
135
-                    'ticket_list',
136
-                    'organization',
137
-                    'primary_registration_details',
138
-                    'primary_registration_list',
139
-                    'event_author',
140
-                    'recipient_details',
141
-                    'recipient_list',
142
-                    'transaction',
143
-                    'messenger',
144
-                ),
145
-            ),
146
-            'attendee_list' => array(
147
-                'shortcodes' => array('attendee', 'event_list', 'ticket_list'),
148
-                'required'   => array('[ATTENDEE_LIST]'),
149
-            ),
150
-            'event_list'    => array(
151
-                'shortcodes' => array(
152
-                    'event',
153
-                    'attendee_list',
154
-                    'ticket_list',
155
-                    'venue',
156
-                    'datetime_list',
157
-                    'attendee',
158
-                    'primary_registration_details',
159
-                    'primary_registration_list',
160
-                    'event_author',
161
-                    'recipient_details',
162
-                    'recipient_list',
163
-                ),
164
-                'required'   => array('[EVENT_LIST]'),
165
-            ),
166
-            'ticket_list'   => array(
167
-                'shortcodes' => array(
168
-                    'event_list',
169
-                    'attendee_list',
170
-                    'ticket',
171
-                    'datetime_list',
172
-                    'primary_registration_details',
173
-                    'recipient_details',
174
-                ),
175
-                'required'   => array('[TICKET_LIST]'),
176
-            ),
177
-            'datetime_list' => array(
178
-                'shortcodes' => array('datetime'),
179
-                'required'   => array('[DATETIME_LIST]'),
180
-            ),
181
-        );
182
-    }
183
-
184
-
185
-    /**
186
-     * @see   parent EE_messenger class for docs
187
-     * @since 4.5.0
188
-     */
189
-    public function do_secondary_messenger_hooks($sending_messenger_name)
190
-    {
191
-        if ($sending_messenger_name = 'html') {
192
-            add_filter('FHEE__EE_Messages_Template_Pack__get_variation', array($this, 'add_email_css'), 10, 8);
193
-        }
194
-    }
195
-
196
-
197
-    public function add_email_css(
198
-        $variation_path,
199
-        $messenger,
200
-        $message_type,
201
-        $type,
202
-        $variation,
203
-        $file_extension,
204
-        $url,
205
-        EE_Messages_Template_Pack $template_pack
206
-    ) {
207
-        //prevent recursion on this callback.
208
-        remove_filter('FHEE__EE_Messages_Template_Pack__get_variation', array($this, 'add_email_css'), 10);
209
-        $variation = $this->get_variation($template_pack, $message_type, $url, 'main', $variation, false);
210
-
211
-        add_filter('FHEE__EE_Messages_Template_Pack__get_variation', array($this, 'add_email_css'), 10, 8);
212
-        return $variation;
213
-    }
214
-
215
-
216
-    /**
217
-     * See parent for details
218
-     *
219
-     * @access protected
220
-     * @return void
221
-     */
222
-    protected function _set_test_settings_fields()
223
-    {
224
-        $this->_test_settings_fields = array(
225
-            'to'      => array(
226
-                'input'      => 'text',
227
-                'label'      => esc_html__('Send a test email to', 'event_espresso'),
228
-                'type'       => 'email',
229
-                'required'   => true,
230
-                'validation' => true,
231
-                'css_class'  => 'large-text',
232
-                'format'     => '%s',
233
-                'default'    => get_bloginfo('admin_email'),
234
-            ),
235
-            'subject' => array(
236
-                'input'      => 'hidden',
237
-                'label'      => '',
238
-                'type'       => 'string',
239
-                'required'   => false,
240
-                'validation' => false,
241
-                'format'     => '%s',
242
-                'value'      => sprintf(__('Test email sent from %s', 'event_espresso'), get_bloginfo('name')),
243
-                'default'    => '',
244
-                'css_class'  => '',
245
-            ),
246
-        );
247
-    }
248
-
249
-
250
-    /**
251
-     * _set_template_fields
252
-     * This sets up the fields that a messenger requires for the message to go out.
253
-     *
254
-     * @access  protected
255
-     * @return void
256
-     */
257
-    protected function _set_template_fields()
258
-    {
259
-        // any extra template fields that are NOT used by the messenger but will get used by a messenger field for
260
-        // shortcode replacement get added to the 'extra' key in an associated array indexed by the messenger field
261
-        // they relate to.  This is important for the Messages_admin to know what fields to display to the user.
262
-        //  Also, notice that the "values" are equal to the field type that messages admin will use to know what
263
-        // kind of field to display. The values ALSO have one index labeled "shortcode".  the values in that array
264
-        // indicate which ACTUAL SHORTCODE (i.e. [SHORTCODE]) is required in order for this extra field to be
265
-        // displayed.  If the required shortcode isn't part of the shortcodes array then the field is not needed and
266
-        // will not be displayed/parsed.
267
-        $this->_template_fields = array(
268
-            'to'      => array(
269
-                'input'      => 'text',
270
-                'label'      => esc_html_x(
271
-                    'To',
272
-                    'Label for the "To" field for email addresses',
273
-                    'event_espresso'
274
-                ),
275
-                'type'       => 'string',
276
-                'required'   => true,
277
-                'validation' => true,
278
-                'css_class'  => 'large-text',
279
-                'format'     => '%s',
280
-            ),
281
-            'cc'      => array(
282
-                'input'      => 'text',
283
-                'label'      => esc_html_x(
284
-                    'CC',
285
-                    'Label for the "Carbon Copy" field used for additional email addresses',
286
-                    'event_espresso'
287
-                ),
288
-                'type'       => 'string',
289
-                'required'   => false,
290
-                'validation' => true,
291
-                'css_class'  => 'large-text',
292
-                'format'     => '%s',
293
-            ),
294
-            'from'    => array(
295
-                'input'      => 'text',
296
-                'label'      => esc_html_x(
297
-                    'From',
298
-                    'Label for the "From" field for email addresses.',
299
-                    'event_espresso'
300
-                ),
301
-                'type'       => 'string',
302
-                'required'   => true,
303
-                'validation' => true,
304
-                'css_class'  => 'large-text',
305
-                'format'     => '%s',
306
-            ),
307
-            'subject' => array(
308
-                'input'      => 'text',
309
-                'label'      => esc_html_x(
310
-                    'Subject',
311
-                    'Label for the "Subject" field (short description of contents) for emails.',
312
-                    'event_espresso'
313
-                ),
314
-                'type'       => 'string',
315
-                'required'   => true,
316
-                'validation' => true,
317
-                'css_class'  => 'large-text',
318
-                'format'     => '%s',
319
-            ),
320
-            'content' => '',
321
-            //left empty b/c it is in the "extra array" but messenger still needs needs to know this is a field.
322
-            'extra'   => array(
323
-                'content' => array(
324
-                    'main'          => array(
325
-                        'input'      => 'wp_editor',
326
-                        'label'      => esc_html__('Main Content', 'event_espresso'),
327
-                        'type'       => 'string',
328
-                        'required'   => true,
329
-                        'validation' => true,
330
-                        'format'     => '%s',
331
-                        'rows'       => '15',
332
-                    ),
333
-                    'event_list'    => array(
334
-                        'input'               => 'wp_editor',
335
-                        'label'               => '[EVENT_LIST]',
336
-                        'type'                => 'string',
337
-                        'required'            => true,
338
-                        'validation'          => true,
339
-                        'format'              => '%s',
340
-                        'rows'                => '15',
341
-                        'shortcodes_required' => array('[EVENT_LIST]'),
342
-                    ),
343
-                    'attendee_list' => array(
344
-                        'input'               => 'textarea',
345
-                        'label'               => '[ATTENDEE_LIST]',
346
-                        'type'                => 'string',
347
-                        'required'            => true,
348
-                        'validation'          => true,
349
-                        'format'              => '%s',
350
-                        'css_class'           => 'large-text',
351
-                        'rows'                => '5',
352
-                        'shortcodes_required' => array('[ATTENDEE_LIST]'),
353
-                    ),
354
-                    'ticket_list'   => array(
355
-                        'input'               => 'textarea',
356
-                        'label'               => '[TICKET_LIST]',
357
-                        'type'                => 'string',
358
-                        'required'            => true,
359
-                        'validation'          => true,
360
-                        'format'              => '%s',
361
-                        'css_class'           => 'large-text',
362
-                        'rows'                => '10',
363
-                        'shortcodes_required' => array('[TICKET_LIST]'),
364
-                    ),
365
-                    'datetime_list' => array(
366
-                        'input'               => 'textarea',
367
-                        'label'               => '[DATETIME_LIST]',
368
-                        'type'                => 'string',
369
-                        'required'            => true,
370
-                        'validation'          => true,
371
-                        'format'              => '%s',
372
-                        'css_class'           => 'large-text',
373
-                        'rows'                => '10',
374
-                        'shortcodes_required' => array('[DATETIME_LIST]'),
375
-                    ),
376
-                ),
377
-            ),
378
-        );
379
-    }
380
-
381
-
382
-    /**
383
-     * See definition of this class in parent
384
-     */
385
-    protected function _set_default_message_types()
386
-    {
387
-        $this->_default_message_types = array(
388
-            'payment',
389
-            'payment_refund',
390
-            'registration',
391
-            'not_approved_registration',
392
-            'pending_approval',
393
-        );
394
-    }
395
-
396
-
397
-    /**
398
-     * @see   definition of this class in parent
399
-     * @since 4.5.0
400
-     */
401
-    protected function _set_valid_message_types()
402
-    {
403
-        $this->_valid_message_types = array(
404
-            'payment',
405
-            'registration',
406
-            'not_approved_registration',
407
-            'declined_registration',
408
-            'cancelled_registration',
409
-            'pending_approval',
410
-            'registration_summary',
411
-            'payment_reminder',
412
-            'payment_declined',
413
-            'payment_refund',
414
-        );
415
-    }
416
-
417
-
418
-    /**
419
-     * setting up admin_settings_fields for messenger.
420
-     */
421
-    protected function _set_admin_settings_fields()
422
-    {
423
-    }
424
-
425
-    /**
426
-     * We just deliver the messages don't kill us!!
427
-     *
428
-     * @return bool|WP_Error true if message delivered, false if it didn't deliver OR bubble up any error object if
429
-     *              present.
430
-     * @throws EE_Error
431
-     * @throws \TijsVerkoyen\CssToInlineStyles\Exception
432
-     */
433
-    protected function _send_message()
434
-    {
435
-        $success = wp_mail(
436
-            $this->_to,
437
-            //some old values for subject may be expecting HTML entities to be decoded in the subject
438
-            //and subjects aren't interpreted as HTML, so there should be no HTML in them
439
-            wp_strip_all_tags(wp_specialchars_decode($this->_subject, ENT_QUOTES)),
440
-            $this->_body(),
441
-            $this->_headers()
442
-        );
443
-        if (! $success) {
444
-            EE_Error::add_error(
445
-                sprintf(
446
-                    esc_html__(
447
-                        'The email did not send successfully.%3$sThe WordPress wp_mail function is used for sending mails but does not give any useful information when an email fails to send.%3$sIt is possible the "to" address (%1$s) or "from" address (%2$s) is invalid.%3$s',
448
-                        'event_espresso'
449
-                    ),
450
-                    $this->_to,
451
-                    $this->_from,
452
-                    '<br />'
453
-                ),
454
-                __FILE__,
455
-                __FUNCTION__,
456
-                __LINE__
457
-            );
458
-        }
459
-        return $success;
460
-    }
461
-
462
-
463
-    /**
464
-     * see parent for definition
465
-     *
466
-     * @return string html body of the message content and the related css.
467
-     * @throws EE_Error
468
-     * @throws \TijsVerkoyen\CssToInlineStyles\Exception
469
-     */
470
-    protected function _preview()
471
-    {
472
-        return $this->_body(true);
473
-    }
474
-
475
-
476
-    /**
477
-     * Setup headers for email
478
-     *
479
-     * @access protected
480
-     * @return string formatted header for email
481
-     */
482
-    protected function _headers()
483
-    {
484
-        $this->_ensure_has_from_email_address();
485
-        $from    = $this->_from;
486
-        $headers = array(
487
-            'From:' . $from,
488
-            'Reply-To:' . $from,
489
-            'Content-Type:text/html; charset=utf-8',
490
-        );
491
-
492
-        if (! empty($this->_cc)) {
493
-            $headers[] = 'cc: ' . $this->_cc;
494
-        }
495
-
496
-        //but wait!  Header's for the from is NOT reliable because some plugins don't respect From: as set in the
497
-        // header.
498
-        add_filter('wp_mail_from', array($this, 'set_from_address'), 100);
499
-        add_filter('wp_mail_from_name', array($this, 'set_from_name'), 100);
500
-        return apply_filters('FHEE__EE_Email_messenger___headers', $headers, $this->_incoming_message_type, $this);
501
-    }
502
-
503
-
504
-    /**
505
-     * This simply ensures that the from address is not empty.  If it is, then we use whatever is set as the site email
506
-     * address for the from address to avoid problems with sending emails.
507
-     */
508
-    protected function _ensure_has_from_email_address()
509
-    {
510
-        if (empty($this->_from)) {
511
-            $this->_from = get_bloginfo('admin_email');
512
-        }
513
-    }
514
-
515
-
516
-    /**
517
-     * This simply parses whatever is set as the $_from address and determines if it is in the format {name} <{email}>
518
-     * or just {email} and returns an array with the "from_name" and "from_email" as the values. Note from_name *MAY*
519
-     * be empty
520
-     *
521
-     * @since 4.3.1
522
-     * @return array
523
-     */
524
-    private function _parse_from()
525
-    {
526
-        if (strpos($this->_from, '<') !== false) {
527
-            $from_name = substr($this->_from, 0, strpos($this->_from, '<') - 1);
528
-            $from_name = str_replace('"', '', $from_name);
529
-            $from_name = trim($from_name);
530
-
531
-            $from_email = substr($this->_from, strpos($this->_from, '<') + 1);
532
-            $from_email = str_replace('>', '', $from_email);
533
-            $from_email = trim($from_email);
534
-        } elseif (trim($this->_from) !== '') {
535
-            $from_name  = '';
536
-            $from_email = trim($this->_from);
537
-        } else {
538
-            $from_name = $from_email = '';
539
-        }
540
-        return array($from_name, $from_email);
541
-    }
542
-
543
-
544
-    /**
545
-     * Callback for the wp_mail_from filter.
546
-     *
547
-     * @since 4.3.1
548
-     * @param string $from_email What the original from_email is.
549
-     * @return string
550
-     */
551
-    public function set_from_address($from_email)
552
-    {
553
-        $parsed_from = $this->_parse_from();
554
-        //includes fallback if the parsing failed.
555
-        $from_email = is_array($parsed_from) && ! empty($parsed_from[1])
556
-            ? $parsed_from[1]
557
-            : get_bloginfo('admin_email');
558
-        return $from_email;
559
-    }
560
-
561
-
562
-    /**
563
-     * Callback fro the wp_mail_from_name filter.
564
-     *
565
-     * @since 4.3.1
566
-     * @param string $from_name The original from_name.
567
-     * @return string
568
-     */
569
-    public function set_from_name($from_name)
570
-    {
571
-        $parsed_from = $this->_parse_from();
572
-        if (is_array($parsed_from) && ! empty($parsed_from[0])) {
573
-            $from_name = $parsed_from[0];
574
-        }
575
-
576
-        //if from name is "WordPress" let's sub in the site name instead (more friendly!)
577
-        //but realize the default name is HTML entity-encoded
578
-        $from_name = $from_name == 'WordPress' ? wp_specialchars_decode(get_bloginfo(), ENT_QUOTES) : $from_name;
579
-
580
-        return $from_name;
581
-    }
582
-
583
-
584
-    /**
585
-     * setup body for email
586
-     *
587
-     * @param bool $preview will determine whether this is preview template or not.
588
-     * @return string formatted body for email.
589
-     * @throws EE_Error
590
-     * @throws \TijsVerkoyen\CssToInlineStyles\Exception
591
-     */
592
-    protected function _body($preview = false)
593
-    {
594
-        //setup template args!
595
-        $this->_template_args = array(
596
-            'subject'   => $this->_subject,
597
-            'from'      => $this->_from,
598
-            'main_body' => wpautop($this->_content),
599
-        );
600
-        $body                 = $this->_get_main_template($preview);
601
-
602
-        /**
603
-         * This filter allows one to bypass the CSSToInlineStyles tool and leave the body untouched.
604
-         *
605
-         * @type    bool $preview Indicates whether a preview is being generated or not.
606
-         * @return  bool    true  indicates to use the inliner, false bypasses it.
607
-         */
608
-        if (apply_filters('FHEE__EE_Email_messenger__apply_CSSInliner ', true, $preview)) {
609
-            //require CssToInlineStyles library and its dependencies via composer autoloader
610
-            require_once EE_THIRD_PARTY . 'cssinliner/vendor/autoload.php';
611
-
612
-            //now if this isn't a preview, let's setup the body so it has inline styles
613
-            if (! $preview || ($preview && defined('DOING_AJAX'))) {
614
-                $style = file_get_contents(
615
-                    $this->get_variation(
616
-                        $this->_tmp_pack,
617
-                        $this->_incoming_message_type->name,
618
-                        false,
619
-                        'main',
620
-                        $this->_variation
621
-                    ),
622
-                    true
623
-                );
624
-                $CSS   = new TijsVerkoyen\CssToInlineStyles\CssToInlineStyles($body, $style);
625
-                //for some reason the library has a bracket and new line at the beginning.  This takes care of that.
626
-                $body  = ltrim($CSS->convert(true), ">\n");
627
-                //see https://events.codebasehq.com/projects/event-espresso/tickets/8609
628
-                $body  = ltrim($body, "<?");
629
-            }
630
-
631
-        }
632
-        return $body;
633
-    }
634
-
635
-
636
-    /**
637
-     * This just returns any existing test settings that might be saved in the database
638
-     *
639
-     * @access public
640
-     * @return array
641
-     */
642
-    public function get_existing_test_settings()
643
-    {
644
-        $settings = parent::get_existing_test_settings();
645
-        //override subject if present because we always want it to be fresh.
646
-        if (is_array($settings) && ! empty($settings['subject'])) {
647
-            $settings['subject'] = sprintf(__('Test email sent from %s', 'event_espresso'), get_bloginfo('name'));
648
-        }
649
-        return $settings;
650
-    }
11
+	/**
12
+	 * To field for email
13
+	 * @var string
14
+	 */
15
+	protected $_to = '';
16
+
17
+
18
+	/**
19
+	 * CC field for email.
20
+	 * @var string
21
+	 */
22
+	protected $_cc = '';
23
+
24
+	/**
25
+	 * From field for email
26
+	 * @var string
27
+	 */
28
+	protected $_from = '';
29
+
30
+
31
+	/**
32
+	 * Subject field for email
33
+	 * @var string
34
+	 */
35
+	protected $_subject = '';
36
+
37
+
38
+	/**
39
+	 * Content field for email
40
+	 * @var string
41
+	 */
42
+	protected $_content = '';
43
+
44
+
45
+	/**
46
+	 * constructor
47
+	 *
48
+	 * @access public
49
+	 */
50
+	public function __construct()
51
+	{
52
+		//set name and description properties
53
+		$this->name                = 'email';
54
+		$this->description         = sprintf(
55
+			esc_html__(
56
+				'This messenger delivers messages via email using the built-in %s function included with WordPress',
57
+				'event_espresso'
58
+			),
59
+			'<code>wp_mail</code>'
60
+		);
61
+		$this->label               = array(
62
+			'singular' => esc_html__('email', 'event_espresso'),
63
+			'plural'   => esc_html__('emails', 'event_espresso'),
64
+		);
65
+		$this->activate_on_install = true;
66
+
67
+		//we're using defaults so let's call parent constructor that will take care of setting up all the other
68
+		// properties
69
+		parent::__construct();
70
+	}
71
+
72
+
73
+	/**
74
+	 * see abstract declaration in parent class for details.
75
+	 */
76
+	protected function _set_admin_pages()
77
+	{
78
+		$this->admin_registered_pages = array(
79
+			'events_edit' => true,
80
+		);
81
+	}
82
+
83
+
84
+	/**
85
+	 * see abstract declaration in parent class for details
86
+	 */
87
+	protected function _set_valid_shortcodes()
88
+	{
89
+		//remember by leaving the other fields not set, those fields will inherit the valid shortcodes from the
90
+		// message type.
91
+		$this->_valid_shortcodes = array(
92
+			'to'   => array('email', 'event_author', 'primary_registration_details', 'recipient_details'),
93
+			'cc' => array('email', 'event_author', 'primary_registration_details', 'recipient_details'),
94
+			'from' => array('email', 'event_author', 'primary_registration_details', 'recipient_details'),
95
+		);
96
+	}
97
+
98
+
99
+	/**
100
+	 * see abstract declaration in parent class for details
101
+	 *
102
+	 * @access protected
103
+	 * @return void
104
+	 */
105
+	protected function _set_validator_config()
106
+	{
107
+		$valid_shortcodes = $this->get_valid_shortcodes();
108
+
109
+		$this->_validator_config = array(
110
+			'to'            => array(
111
+				'shortcodes' => $valid_shortcodes['to'],
112
+				'type'       => 'email',
113
+			),
114
+			'cc' => array(
115
+				'shortcodes' => $valid_shortcodes['to'],
116
+				'type' => 'email',
117
+			),
118
+			'from'          => array(
119
+				'shortcodes' => $valid_shortcodes['from'],
120
+				'type'       => 'email',
121
+			),
122
+			'subject'       => array(
123
+				'shortcodes' => array(
124
+					'organization',
125
+					'primary_registration_details',
126
+					'event_author',
127
+					'primary_registration_details',
128
+					'recipient_details',
129
+				),
130
+			),
131
+			'content'       => array(
132
+				'shortcodes' => array(
133
+					'event_list',
134
+					'attendee_list',
135
+					'ticket_list',
136
+					'organization',
137
+					'primary_registration_details',
138
+					'primary_registration_list',
139
+					'event_author',
140
+					'recipient_details',
141
+					'recipient_list',
142
+					'transaction',
143
+					'messenger',
144
+				),
145
+			),
146
+			'attendee_list' => array(
147
+				'shortcodes' => array('attendee', 'event_list', 'ticket_list'),
148
+				'required'   => array('[ATTENDEE_LIST]'),
149
+			),
150
+			'event_list'    => array(
151
+				'shortcodes' => array(
152
+					'event',
153
+					'attendee_list',
154
+					'ticket_list',
155
+					'venue',
156
+					'datetime_list',
157
+					'attendee',
158
+					'primary_registration_details',
159
+					'primary_registration_list',
160
+					'event_author',
161
+					'recipient_details',
162
+					'recipient_list',
163
+				),
164
+				'required'   => array('[EVENT_LIST]'),
165
+			),
166
+			'ticket_list'   => array(
167
+				'shortcodes' => array(
168
+					'event_list',
169
+					'attendee_list',
170
+					'ticket',
171
+					'datetime_list',
172
+					'primary_registration_details',
173
+					'recipient_details',
174
+				),
175
+				'required'   => array('[TICKET_LIST]'),
176
+			),
177
+			'datetime_list' => array(
178
+				'shortcodes' => array('datetime'),
179
+				'required'   => array('[DATETIME_LIST]'),
180
+			),
181
+		);
182
+	}
183
+
184
+
185
+	/**
186
+	 * @see   parent EE_messenger class for docs
187
+	 * @since 4.5.0
188
+	 */
189
+	public function do_secondary_messenger_hooks($sending_messenger_name)
190
+	{
191
+		if ($sending_messenger_name = 'html') {
192
+			add_filter('FHEE__EE_Messages_Template_Pack__get_variation', array($this, 'add_email_css'), 10, 8);
193
+		}
194
+	}
195
+
196
+
197
+	public function add_email_css(
198
+		$variation_path,
199
+		$messenger,
200
+		$message_type,
201
+		$type,
202
+		$variation,
203
+		$file_extension,
204
+		$url,
205
+		EE_Messages_Template_Pack $template_pack
206
+	) {
207
+		//prevent recursion on this callback.
208
+		remove_filter('FHEE__EE_Messages_Template_Pack__get_variation', array($this, 'add_email_css'), 10);
209
+		$variation = $this->get_variation($template_pack, $message_type, $url, 'main', $variation, false);
210
+
211
+		add_filter('FHEE__EE_Messages_Template_Pack__get_variation', array($this, 'add_email_css'), 10, 8);
212
+		return $variation;
213
+	}
214
+
215
+
216
+	/**
217
+	 * See parent for details
218
+	 *
219
+	 * @access protected
220
+	 * @return void
221
+	 */
222
+	protected function _set_test_settings_fields()
223
+	{
224
+		$this->_test_settings_fields = array(
225
+			'to'      => array(
226
+				'input'      => 'text',
227
+				'label'      => esc_html__('Send a test email to', 'event_espresso'),
228
+				'type'       => 'email',
229
+				'required'   => true,
230
+				'validation' => true,
231
+				'css_class'  => 'large-text',
232
+				'format'     => '%s',
233
+				'default'    => get_bloginfo('admin_email'),
234
+			),
235
+			'subject' => array(
236
+				'input'      => 'hidden',
237
+				'label'      => '',
238
+				'type'       => 'string',
239
+				'required'   => false,
240
+				'validation' => false,
241
+				'format'     => '%s',
242
+				'value'      => sprintf(__('Test email sent from %s', 'event_espresso'), get_bloginfo('name')),
243
+				'default'    => '',
244
+				'css_class'  => '',
245
+			),
246
+		);
247
+	}
248
+
249
+
250
+	/**
251
+	 * _set_template_fields
252
+	 * This sets up the fields that a messenger requires for the message to go out.
253
+	 *
254
+	 * @access  protected
255
+	 * @return void
256
+	 */
257
+	protected function _set_template_fields()
258
+	{
259
+		// any extra template fields that are NOT used by the messenger but will get used by a messenger field for
260
+		// shortcode replacement get added to the 'extra' key in an associated array indexed by the messenger field
261
+		// they relate to.  This is important for the Messages_admin to know what fields to display to the user.
262
+		//  Also, notice that the "values" are equal to the field type that messages admin will use to know what
263
+		// kind of field to display. The values ALSO have one index labeled "shortcode".  the values in that array
264
+		// indicate which ACTUAL SHORTCODE (i.e. [SHORTCODE]) is required in order for this extra field to be
265
+		// displayed.  If the required shortcode isn't part of the shortcodes array then the field is not needed and
266
+		// will not be displayed/parsed.
267
+		$this->_template_fields = array(
268
+			'to'      => array(
269
+				'input'      => 'text',
270
+				'label'      => esc_html_x(
271
+					'To',
272
+					'Label for the "To" field for email addresses',
273
+					'event_espresso'
274
+				),
275
+				'type'       => 'string',
276
+				'required'   => true,
277
+				'validation' => true,
278
+				'css_class'  => 'large-text',
279
+				'format'     => '%s',
280
+			),
281
+			'cc'      => array(
282
+				'input'      => 'text',
283
+				'label'      => esc_html_x(
284
+					'CC',
285
+					'Label for the "Carbon Copy" field used for additional email addresses',
286
+					'event_espresso'
287
+				),
288
+				'type'       => 'string',
289
+				'required'   => false,
290
+				'validation' => true,
291
+				'css_class'  => 'large-text',
292
+				'format'     => '%s',
293
+			),
294
+			'from'    => array(
295
+				'input'      => 'text',
296
+				'label'      => esc_html_x(
297
+					'From',
298
+					'Label for the "From" field for email addresses.',
299
+					'event_espresso'
300
+				),
301
+				'type'       => 'string',
302
+				'required'   => true,
303
+				'validation' => true,
304
+				'css_class'  => 'large-text',
305
+				'format'     => '%s',
306
+			),
307
+			'subject' => array(
308
+				'input'      => 'text',
309
+				'label'      => esc_html_x(
310
+					'Subject',
311
+					'Label for the "Subject" field (short description of contents) for emails.',
312
+					'event_espresso'
313
+				),
314
+				'type'       => 'string',
315
+				'required'   => true,
316
+				'validation' => true,
317
+				'css_class'  => 'large-text',
318
+				'format'     => '%s',
319
+			),
320
+			'content' => '',
321
+			//left empty b/c it is in the "extra array" but messenger still needs needs to know this is a field.
322
+			'extra'   => array(
323
+				'content' => array(
324
+					'main'          => array(
325
+						'input'      => 'wp_editor',
326
+						'label'      => esc_html__('Main Content', 'event_espresso'),
327
+						'type'       => 'string',
328
+						'required'   => true,
329
+						'validation' => true,
330
+						'format'     => '%s',
331
+						'rows'       => '15',
332
+					),
333
+					'event_list'    => array(
334
+						'input'               => 'wp_editor',
335
+						'label'               => '[EVENT_LIST]',
336
+						'type'                => 'string',
337
+						'required'            => true,
338
+						'validation'          => true,
339
+						'format'              => '%s',
340
+						'rows'                => '15',
341
+						'shortcodes_required' => array('[EVENT_LIST]'),
342
+					),
343
+					'attendee_list' => array(
344
+						'input'               => 'textarea',
345
+						'label'               => '[ATTENDEE_LIST]',
346
+						'type'                => 'string',
347
+						'required'            => true,
348
+						'validation'          => true,
349
+						'format'              => '%s',
350
+						'css_class'           => 'large-text',
351
+						'rows'                => '5',
352
+						'shortcodes_required' => array('[ATTENDEE_LIST]'),
353
+					),
354
+					'ticket_list'   => array(
355
+						'input'               => 'textarea',
356
+						'label'               => '[TICKET_LIST]',
357
+						'type'                => 'string',
358
+						'required'            => true,
359
+						'validation'          => true,
360
+						'format'              => '%s',
361
+						'css_class'           => 'large-text',
362
+						'rows'                => '10',
363
+						'shortcodes_required' => array('[TICKET_LIST]'),
364
+					),
365
+					'datetime_list' => array(
366
+						'input'               => 'textarea',
367
+						'label'               => '[DATETIME_LIST]',
368
+						'type'                => 'string',
369
+						'required'            => true,
370
+						'validation'          => true,
371
+						'format'              => '%s',
372
+						'css_class'           => 'large-text',
373
+						'rows'                => '10',
374
+						'shortcodes_required' => array('[DATETIME_LIST]'),
375
+					),
376
+				),
377
+			),
378
+		);
379
+	}
380
+
381
+
382
+	/**
383
+	 * See definition of this class in parent
384
+	 */
385
+	protected function _set_default_message_types()
386
+	{
387
+		$this->_default_message_types = array(
388
+			'payment',
389
+			'payment_refund',
390
+			'registration',
391
+			'not_approved_registration',
392
+			'pending_approval',
393
+		);
394
+	}
395
+
396
+
397
+	/**
398
+	 * @see   definition of this class in parent
399
+	 * @since 4.5.0
400
+	 */
401
+	protected function _set_valid_message_types()
402
+	{
403
+		$this->_valid_message_types = array(
404
+			'payment',
405
+			'registration',
406
+			'not_approved_registration',
407
+			'declined_registration',
408
+			'cancelled_registration',
409
+			'pending_approval',
410
+			'registration_summary',
411
+			'payment_reminder',
412
+			'payment_declined',
413
+			'payment_refund',
414
+		);
415
+	}
416
+
417
+
418
+	/**
419
+	 * setting up admin_settings_fields for messenger.
420
+	 */
421
+	protected function _set_admin_settings_fields()
422
+	{
423
+	}
424
+
425
+	/**
426
+	 * We just deliver the messages don't kill us!!
427
+	 *
428
+	 * @return bool|WP_Error true if message delivered, false if it didn't deliver OR bubble up any error object if
429
+	 *              present.
430
+	 * @throws EE_Error
431
+	 * @throws \TijsVerkoyen\CssToInlineStyles\Exception
432
+	 */
433
+	protected function _send_message()
434
+	{
435
+		$success = wp_mail(
436
+			$this->_to,
437
+			//some old values for subject may be expecting HTML entities to be decoded in the subject
438
+			//and subjects aren't interpreted as HTML, so there should be no HTML in them
439
+			wp_strip_all_tags(wp_specialchars_decode($this->_subject, ENT_QUOTES)),
440
+			$this->_body(),
441
+			$this->_headers()
442
+		);
443
+		if (! $success) {
444
+			EE_Error::add_error(
445
+				sprintf(
446
+					esc_html__(
447
+						'The email did not send successfully.%3$sThe WordPress wp_mail function is used for sending mails but does not give any useful information when an email fails to send.%3$sIt is possible the "to" address (%1$s) or "from" address (%2$s) is invalid.%3$s',
448
+						'event_espresso'
449
+					),
450
+					$this->_to,
451
+					$this->_from,
452
+					'<br />'
453
+				),
454
+				__FILE__,
455
+				__FUNCTION__,
456
+				__LINE__
457
+			);
458
+		}
459
+		return $success;
460
+	}
461
+
462
+
463
+	/**
464
+	 * see parent for definition
465
+	 *
466
+	 * @return string html body of the message content and the related css.
467
+	 * @throws EE_Error
468
+	 * @throws \TijsVerkoyen\CssToInlineStyles\Exception
469
+	 */
470
+	protected function _preview()
471
+	{
472
+		return $this->_body(true);
473
+	}
474
+
475
+
476
+	/**
477
+	 * Setup headers for email
478
+	 *
479
+	 * @access protected
480
+	 * @return string formatted header for email
481
+	 */
482
+	protected function _headers()
483
+	{
484
+		$this->_ensure_has_from_email_address();
485
+		$from    = $this->_from;
486
+		$headers = array(
487
+			'From:' . $from,
488
+			'Reply-To:' . $from,
489
+			'Content-Type:text/html; charset=utf-8',
490
+		);
491
+
492
+		if (! empty($this->_cc)) {
493
+			$headers[] = 'cc: ' . $this->_cc;
494
+		}
495
+
496
+		//but wait!  Header's for the from is NOT reliable because some plugins don't respect From: as set in the
497
+		// header.
498
+		add_filter('wp_mail_from', array($this, 'set_from_address'), 100);
499
+		add_filter('wp_mail_from_name', array($this, 'set_from_name'), 100);
500
+		return apply_filters('FHEE__EE_Email_messenger___headers', $headers, $this->_incoming_message_type, $this);
501
+	}
502
+
503
+
504
+	/**
505
+	 * This simply ensures that the from address is not empty.  If it is, then we use whatever is set as the site email
506
+	 * address for the from address to avoid problems with sending emails.
507
+	 */
508
+	protected function _ensure_has_from_email_address()
509
+	{
510
+		if (empty($this->_from)) {
511
+			$this->_from = get_bloginfo('admin_email');
512
+		}
513
+	}
514
+
515
+
516
+	/**
517
+	 * This simply parses whatever is set as the $_from address and determines if it is in the format {name} <{email}>
518
+	 * or just {email} and returns an array with the "from_name" and "from_email" as the values. Note from_name *MAY*
519
+	 * be empty
520
+	 *
521
+	 * @since 4.3.1
522
+	 * @return array
523
+	 */
524
+	private function _parse_from()
525
+	{
526
+		if (strpos($this->_from, '<') !== false) {
527
+			$from_name = substr($this->_from, 0, strpos($this->_from, '<') - 1);
528
+			$from_name = str_replace('"', '', $from_name);
529
+			$from_name = trim($from_name);
530
+
531
+			$from_email = substr($this->_from, strpos($this->_from, '<') + 1);
532
+			$from_email = str_replace('>', '', $from_email);
533
+			$from_email = trim($from_email);
534
+		} elseif (trim($this->_from) !== '') {
535
+			$from_name  = '';
536
+			$from_email = trim($this->_from);
537
+		} else {
538
+			$from_name = $from_email = '';
539
+		}
540
+		return array($from_name, $from_email);
541
+	}
542
+
543
+
544
+	/**
545
+	 * Callback for the wp_mail_from filter.
546
+	 *
547
+	 * @since 4.3.1
548
+	 * @param string $from_email What the original from_email is.
549
+	 * @return string
550
+	 */
551
+	public function set_from_address($from_email)
552
+	{
553
+		$parsed_from = $this->_parse_from();
554
+		//includes fallback if the parsing failed.
555
+		$from_email = is_array($parsed_from) && ! empty($parsed_from[1])
556
+			? $parsed_from[1]
557
+			: get_bloginfo('admin_email');
558
+		return $from_email;
559
+	}
560
+
561
+
562
+	/**
563
+	 * Callback fro the wp_mail_from_name filter.
564
+	 *
565
+	 * @since 4.3.1
566
+	 * @param string $from_name The original from_name.
567
+	 * @return string
568
+	 */
569
+	public function set_from_name($from_name)
570
+	{
571
+		$parsed_from = $this->_parse_from();
572
+		if (is_array($parsed_from) && ! empty($parsed_from[0])) {
573
+			$from_name = $parsed_from[0];
574
+		}
575
+
576
+		//if from name is "WordPress" let's sub in the site name instead (more friendly!)
577
+		//but realize the default name is HTML entity-encoded
578
+		$from_name = $from_name == 'WordPress' ? wp_specialchars_decode(get_bloginfo(), ENT_QUOTES) : $from_name;
579
+
580
+		return $from_name;
581
+	}
582
+
583
+
584
+	/**
585
+	 * setup body for email
586
+	 *
587
+	 * @param bool $preview will determine whether this is preview template or not.
588
+	 * @return string formatted body for email.
589
+	 * @throws EE_Error
590
+	 * @throws \TijsVerkoyen\CssToInlineStyles\Exception
591
+	 */
592
+	protected function _body($preview = false)
593
+	{
594
+		//setup template args!
595
+		$this->_template_args = array(
596
+			'subject'   => $this->_subject,
597
+			'from'      => $this->_from,
598
+			'main_body' => wpautop($this->_content),
599
+		);
600
+		$body                 = $this->_get_main_template($preview);
601
+
602
+		/**
603
+		 * This filter allows one to bypass the CSSToInlineStyles tool and leave the body untouched.
604
+		 *
605
+		 * @type    bool $preview Indicates whether a preview is being generated or not.
606
+		 * @return  bool    true  indicates to use the inliner, false bypasses it.
607
+		 */
608
+		if (apply_filters('FHEE__EE_Email_messenger__apply_CSSInliner ', true, $preview)) {
609
+			//require CssToInlineStyles library and its dependencies via composer autoloader
610
+			require_once EE_THIRD_PARTY . 'cssinliner/vendor/autoload.php';
611
+
612
+			//now if this isn't a preview, let's setup the body so it has inline styles
613
+			if (! $preview || ($preview && defined('DOING_AJAX'))) {
614
+				$style = file_get_contents(
615
+					$this->get_variation(
616
+						$this->_tmp_pack,
617
+						$this->_incoming_message_type->name,
618
+						false,
619
+						'main',
620
+						$this->_variation
621
+					),
622
+					true
623
+				);
624
+				$CSS   = new TijsVerkoyen\CssToInlineStyles\CssToInlineStyles($body, $style);
625
+				//for some reason the library has a bracket and new line at the beginning.  This takes care of that.
626
+				$body  = ltrim($CSS->convert(true), ">\n");
627
+				//see https://events.codebasehq.com/projects/event-espresso/tickets/8609
628
+				$body  = ltrim($body, "<?");
629
+			}
630
+
631
+		}
632
+		return $body;
633
+	}
634
+
635
+
636
+	/**
637
+	 * This just returns any existing test settings that might be saved in the database
638
+	 *
639
+	 * @access public
640
+	 * @return array
641
+	 */
642
+	public function get_existing_test_settings()
643
+	{
644
+		$settings = parent::get_existing_test_settings();
645
+		//override subject if present because we always want it to be fresh.
646
+		if (is_array($settings) && ! empty($settings['subject'])) {
647
+			$settings['subject'] = sprintf(__('Test email sent from %s', 'event_espresso'), get_bloginfo('name'));
648
+		}
649
+		return $settings;
650
+	}
651 651
 }
Please login to merge, or discard this patch.