Completed
Branch master (24c3eb)
by
unknown
34:49 queued 28:57
created
core/services/modules/ModuleRoutesManager.php 2 patches
Indentation   +230 added lines, -230 removed lines patch added patch discarded remove patch
@@ -8,234 +8,234 @@
 block discarded – undo
8 8
 
9 9
 class ModuleRoutesManager
10 10
 {
11
-    public RegistryContainer $modules;
12
-
13
-    private array $module_route_map = [];
14
-
15
-    private array $module_forward_map = [];
16
-
17
-    private array $module_view_map = [];
18
-
19
-
20
-    public function __construct()
21
-    {
22
-        $this->modules = EE_Registry::instance()->modules;
23
-    }
24
-
25
-
26
-    /**
27
-     * adds module method routes to route_map
28
-     *
29
-     * @param string $route         "pretty" public alias for module method
30
-     * @param string $module        module name (classname without EED_ prefix)
31
-     * @param string $method_name   the actual module method to be routed to
32
-     * @param string $key           url param key indicating a route is being called
33
-     * @return bool
34
-     */
35
-    public function registerRoute(
36
-        string $route = '',
37
-        string $module = '',
38
-        string $method_name = '',
39
-        string $key = 'ee'
40
-    ): bool {
41
-        do_action('AHEE__EE_Config__register_route__begin', $route, $module, $method_name);
42
-        $module       = str_replace('EED_', '', $module);
43
-        $module_class = 'EED_' . $module;
44
-        if (! isset($this->modules->{$module_class})) {
45
-            $msg = sprintf(esc_html__('The module %s has not been registered.', 'event_espresso'), $module);
46
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
47
-            return false;
48
-        }
49
-        if (empty($route)) {
50
-            $msg = sprintf(esc_html__('No route has been supplied.', 'event_espresso'), $route);
51
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
52
-            return false;
53
-        }
54
-        if (! method_exists('EED_' . $module, $method_name)) {
55
-            $msg = sprintf(
56
-                esc_html__('A valid class method for the %s route has not been supplied.', 'event_espresso'),
57
-                $route
58
-            );
59
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
60
-            return false;
61
-        }
62
-        $this->module_route_map[ $key ][ $route ] = ['EED_' . $module, $method_name];
63
-        return true;
64
-    }
65
-
66
-
67
-    /**
68
-     * get module method route
69
-     *
70
-     * @param string $route  "pretty" public alias for module method
71
-     * @param string $key    url param key indicating a route is being called
72
-     * @return array
73
-     */
74
-    public function getRoute(string $route = '', string $key = 'ee'): array
75
-    {
76
-        do_action('AHEE__EE_Config__get_route__begin', $route);
77
-        $route = (string) apply_filters('FHEE__EE_Config__get_route', $route);
78
-        if (isset($this->module_route_map[ $key ][ $route ])) {
79
-            return $this->module_route_map[ $key ][ $route ];
80
-        }
81
-        return [];
82
-    }
83
-
84
-
85
-    /**
86
-     * get ALL module method routes
87
-     *
88
-     * @return array
89
-     */
90
-    public function getRoutes(): array
91
-    {
92
-        return $this->module_route_map;
93
-    }
94
-
95
-
96
-    /**
97
-     * get forwarding route
98
-     *
99
-     * @param array|string $route    "pretty" public alias for module method
100
-     * @param string       $key      url param key indicating a route is being called
101
-     * @param int          $status   integer value corresponding  to status constant strings set in module parent class,
102
-     *                               allows different forwards to be served based on status
103
-     * @return string
104
-     */
105
-    public function getForward($route = '', string $key = 'ee', int $status = 0): string
106
-    {
107
-        if (is_array($route) && isset($route[0], $route[1])) {
108
-            $key   = $route[0];
109
-            $route = $route[1];
110
-        }
111
-        do_action('AHEE__EE_Config__get_forward__begin', $route, $status);
112
-        if (isset($this->module_forward_map[ $key ][ $route ][ $status ])) {
113
-            return apply_filters(
114
-                'FHEE__EE_Config__get_forward',
115
-                $this->module_forward_map[ $key ][ $route ][ $status ],
116
-                $route,
117
-                $status
118
-            );
119
-        }
120
-        return '';
121
-    }
122
-
123
-
124
-    /**
125
-     * allows modules to specify different view templates for different method routes and status results
126
-     *
127
-     * @param string $view      path to templates file used for view
128
-     * @param string $route     "pretty" public alias for module method
129
-     * @param string $key       url param key indicating a route is being called
130
-     * @param int    $status    integer value corresponding  to status constant strings set in module parent class,
131
-     *                          allows different views to be served based on status
132
-     * @return bool
133
-     */
134
-    public function registerView(string $view, string $route, string $key = 'ee', int $status = 0): bool
135
-    {
136
-        do_action('AHEE__EE_Config__register_view__begin', $route, $status, $view);
137
-        if (! isset($this->module_route_map[ $key ][ $route ]) || empty($route)) {
138
-            $msg = sprintf(
139
-                esc_html__('The module route %s for this view has not been registered.', 'event_espresso'),
140
-                $route
141
-            );
142
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
143
-            return false;
144
-        }
145
-        if (! is_readable($view)) {
146
-            $msg = sprintf(
147
-                esc_html__(
148
-                    'The %s view file could not be found or is not readable due to file permissions.',
149
-                    'event_espresso'
150
-                ),
151
-                $view
152
-            );
153
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
154
-            return false;
155
-        }
156
-        $this->module_view_map[ $key ][ $route ][ $status ] = $view;
157
-        return true;
158
-    }
159
-
160
-
161
-    /**
162
-     * allows modules to forward request to another module for further processing
163
-     *
164
-     * @param array|string $forward  function name or array( class, method )
165
-     * @param string       $route    "pretty" public alias for module method
166
-     * @param string       $key      url param key indicating a route is being called
167
-     * @param int          $status   integer value corresponding  to status constant strings set in module parent
168
-     *                               class, allows different forwards to be served based on status
169
-     * @return bool
170
-     */
171
-    public function registerForward($forward, string $route, string $key = 'ee', int $status = 0): bool
172
-    {
173
-        do_action('AHEE__EE_Config__register_forward', $route, $status, $forward);
174
-        if (! isset($this->module_route_map[ $key ][ $route ]) || empty($route)) {
175
-            $msg = sprintf(
176
-                esc_html__('The module route %s for this forward has not been registered.', 'event_espresso'),
177
-                $route
178
-            );
179
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
180
-            return false;
181
-        }
182
-        if (empty($forward)) {
183
-            $msg = sprintf(esc_html__('No forwarding route has been supplied.', 'event_espresso'), $route);
184
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
185
-            return false;
186
-        }
187
-        if (is_array($forward)) {
188
-            if (! isset($forward[1])) {
189
-                $msg = sprintf(
190
-                    esc_html__('A class method for the %s forwarding route has not been supplied.', 'event_espresso'),
191
-                    $route
192
-                );
193
-                EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
194
-                return false;
195
-            }
196
-            if (! method_exists($forward[0], $forward[1])) {
197
-                $msg = sprintf(
198
-                    esc_html__('The class method %1$s for the %2$s forwarding route is in invalid.', 'event_espresso'),
199
-                    $forward[1],
200
-                    $route
201
-                );
202
-                EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
203
-                return false;
204
-            }
205
-        } elseif (! function_exists($forward)) {
206
-            $msg = sprintf(
207
-                esc_html__('The function %1$s for the %2$s forwarding route is in invalid.', 'event_espresso'),
208
-                $forward,
209
-                $route
210
-            );
211
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
212
-            return false;
213
-        }
214
-        $this->module_forward_map[ $key ][ $route ][ absint($status) ] = $forward;
215
-        return true;
216
-    }
217
-
218
-
219
-    /**
220
-     * get view for route and status
221
-     *
222
-     * @param string $route     "pretty" public alias for module method
223
-     * @param string $key       url param key indicating a route is being called
224
-     * @param int    $status    integer value corresponding  to status constant strings set in module parent class,
225
-     *                          allows different views to be served based on status
226
-     * @return string
227
-     */
228
-    public function getView(string $route = '', string $key = 'ee', int $status = 0): string
229
-    {
230
-        do_action('AHEE__EE_Config__get_view__begin', $route, $status);
231
-        if (isset($this->module_view_map[ $key ][ $route ][ $status ])) {
232
-            return (string) apply_filters(
233
-                'FHEE__EE_Config__get_view',
234
-                $this->module_view_map[ $key ][ $route ][ $status ],
235
-                $route,
236
-                $status
237
-            );
238
-        }
239
-        return '';
240
-    }
11
+	public RegistryContainer $modules;
12
+
13
+	private array $module_route_map = [];
14
+
15
+	private array $module_forward_map = [];
16
+
17
+	private array $module_view_map = [];
18
+
19
+
20
+	public function __construct()
21
+	{
22
+		$this->modules = EE_Registry::instance()->modules;
23
+	}
24
+
25
+
26
+	/**
27
+	 * adds module method routes to route_map
28
+	 *
29
+	 * @param string $route         "pretty" public alias for module method
30
+	 * @param string $module        module name (classname without EED_ prefix)
31
+	 * @param string $method_name   the actual module method to be routed to
32
+	 * @param string $key           url param key indicating a route is being called
33
+	 * @return bool
34
+	 */
35
+	public function registerRoute(
36
+		string $route = '',
37
+		string $module = '',
38
+		string $method_name = '',
39
+		string $key = 'ee'
40
+	): bool {
41
+		do_action('AHEE__EE_Config__register_route__begin', $route, $module, $method_name);
42
+		$module       = str_replace('EED_', '', $module);
43
+		$module_class = 'EED_' . $module;
44
+		if (! isset($this->modules->{$module_class})) {
45
+			$msg = sprintf(esc_html__('The module %s has not been registered.', 'event_espresso'), $module);
46
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
47
+			return false;
48
+		}
49
+		if (empty($route)) {
50
+			$msg = sprintf(esc_html__('No route has been supplied.', 'event_espresso'), $route);
51
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
52
+			return false;
53
+		}
54
+		if (! method_exists('EED_' . $module, $method_name)) {
55
+			$msg = sprintf(
56
+				esc_html__('A valid class method for the %s route has not been supplied.', 'event_espresso'),
57
+				$route
58
+			);
59
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
60
+			return false;
61
+		}
62
+		$this->module_route_map[ $key ][ $route ] = ['EED_' . $module, $method_name];
63
+		return true;
64
+	}
65
+
66
+
67
+	/**
68
+	 * get module method route
69
+	 *
70
+	 * @param string $route  "pretty" public alias for module method
71
+	 * @param string $key    url param key indicating a route is being called
72
+	 * @return array
73
+	 */
74
+	public function getRoute(string $route = '', string $key = 'ee'): array
75
+	{
76
+		do_action('AHEE__EE_Config__get_route__begin', $route);
77
+		$route = (string) apply_filters('FHEE__EE_Config__get_route', $route);
78
+		if (isset($this->module_route_map[ $key ][ $route ])) {
79
+			return $this->module_route_map[ $key ][ $route ];
80
+		}
81
+		return [];
82
+	}
83
+
84
+
85
+	/**
86
+	 * get ALL module method routes
87
+	 *
88
+	 * @return array
89
+	 */
90
+	public function getRoutes(): array
91
+	{
92
+		return $this->module_route_map;
93
+	}
94
+
95
+
96
+	/**
97
+	 * get forwarding route
98
+	 *
99
+	 * @param array|string $route    "pretty" public alias for module method
100
+	 * @param string       $key      url param key indicating a route is being called
101
+	 * @param int          $status   integer value corresponding  to status constant strings set in module parent class,
102
+	 *                               allows different forwards to be served based on status
103
+	 * @return string
104
+	 */
105
+	public function getForward($route = '', string $key = 'ee', int $status = 0): string
106
+	{
107
+		if (is_array($route) && isset($route[0], $route[1])) {
108
+			$key   = $route[0];
109
+			$route = $route[1];
110
+		}
111
+		do_action('AHEE__EE_Config__get_forward__begin', $route, $status);
112
+		if (isset($this->module_forward_map[ $key ][ $route ][ $status ])) {
113
+			return apply_filters(
114
+				'FHEE__EE_Config__get_forward',
115
+				$this->module_forward_map[ $key ][ $route ][ $status ],
116
+				$route,
117
+				$status
118
+			);
119
+		}
120
+		return '';
121
+	}
122
+
123
+
124
+	/**
125
+	 * allows modules to specify different view templates for different method routes and status results
126
+	 *
127
+	 * @param string $view      path to templates file used for view
128
+	 * @param string $route     "pretty" public alias for module method
129
+	 * @param string $key       url param key indicating a route is being called
130
+	 * @param int    $status    integer value corresponding  to status constant strings set in module parent class,
131
+	 *                          allows different views to be served based on status
132
+	 * @return bool
133
+	 */
134
+	public function registerView(string $view, string $route, string $key = 'ee', int $status = 0): bool
135
+	{
136
+		do_action('AHEE__EE_Config__register_view__begin', $route, $status, $view);
137
+		if (! isset($this->module_route_map[ $key ][ $route ]) || empty($route)) {
138
+			$msg = sprintf(
139
+				esc_html__('The module route %s for this view has not been registered.', 'event_espresso'),
140
+				$route
141
+			);
142
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
143
+			return false;
144
+		}
145
+		if (! is_readable($view)) {
146
+			$msg = sprintf(
147
+				esc_html__(
148
+					'The %s view file could not be found or is not readable due to file permissions.',
149
+					'event_espresso'
150
+				),
151
+				$view
152
+			);
153
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
154
+			return false;
155
+		}
156
+		$this->module_view_map[ $key ][ $route ][ $status ] = $view;
157
+		return true;
158
+	}
159
+
160
+
161
+	/**
162
+	 * allows modules to forward request to another module for further processing
163
+	 *
164
+	 * @param array|string $forward  function name or array( class, method )
165
+	 * @param string       $route    "pretty" public alias for module method
166
+	 * @param string       $key      url param key indicating a route is being called
167
+	 * @param int          $status   integer value corresponding  to status constant strings set in module parent
168
+	 *                               class, allows different forwards to be served based on status
169
+	 * @return bool
170
+	 */
171
+	public function registerForward($forward, string $route, string $key = 'ee', int $status = 0): bool
172
+	{
173
+		do_action('AHEE__EE_Config__register_forward', $route, $status, $forward);
174
+		if (! isset($this->module_route_map[ $key ][ $route ]) || empty($route)) {
175
+			$msg = sprintf(
176
+				esc_html__('The module route %s for this forward has not been registered.', 'event_espresso'),
177
+				$route
178
+			);
179
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
180
+			return false;
181
+		}
182
+		if (empty($forward)) {
183
+			$msg = sprintf(esc_html__('No forwarding route has been supplied.', 'event_espresso'), $route);
184
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
185
+			return false;
186
+		}
187
+		if (is_array($forward)) {
188
+			if (! isset($forward[1])) {
189
+				$msg = sprintf(
190
+					esc_html__('A class method for the %s forwarding route has not been supplied.', 'event_espresso'),
191
+					$route
192
+				);
193
+				EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
194
+				return false;
195
+			}
196
+			if (! method_exists($forward[0], $forward[1])) {
197
+				$msg = sprintf(
198
+					esc_html__('The class method %1$s for the %2$s forwarding route is in invalid.', 'event_espresso'),
199
+					$forward[1],
200
+					$route
201
+				);
202
+				EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
203
+				return false;
204
+			}
205
+		} elseif (! function_exists($forward)) {
206
+			$msg = sprintf(
207
+				esc_html__('The function %1$s for the %2$s forwarding route is in invalid.', 'event_espresso'),
208
+				$forward,
209
+				$route
210
+			);
211
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
212
+			return false;
213
+		}
214
+		$this->module_forward_map[ $key ][ $route ][ absint($status) ] = $forward;
215
+		return true;
216
+	}
217
+
218
+
219
+	/**
220
+	 * get view for route and status
221
+	 *
222
+	 * @param string $route     "pretty" public alias for module method
223
+	 * @param string $key       url param key indicating a route is being called
224
+	 * @param int    $status    integer value corresponding  to status constant strings set in module parent class,
225
+	 *                          allows different views to be served based on status
226
+	 * @return string
227
+	 */
228
+	public function getView(string $route = '', string $key = 'ee', int $status = 0): string
229
+	{
230
+		do_action('AHEE__EE_Config__get_view__begin', $route, $status);
231
+		if (isset($this->module_view_map[ $key ][ $route ][ $status ])) {
232
+			return (string) apply_filters(
233
+				'FHEE__EE_Config__get_view',
234
+				$this->module_view_map[ $key ][ $route ][ $status ],
235
+				$route,
236
+				$status
237
+			);
238
+		}
239
+		return '';
240
+	}
241 241
 }
Please login to merge, or discard this patch.
Spacing   +28 added lines, -28 removed lines patch added patch discarded remove patch
@@ -40,26 +40,26 @@  discard block
 block discarded – undo
40 40
     ): bool {
41 41
         do_action('AHEE__EE_Config__register_route__begin', $route, $module, $method_name);
42 42
         $module       = str_replace('EED_', '', $module);
43
-        $module_class = 'EED_' . $module;
44
-        if (! isset($this->modules->{$module_class})) {
43
+        $module_class = 'EED_'.$module;
44
+        if ( ! isset($this->modules->{$module_class})) {
45 45
             $msg = sprintf(esc_html__('The module %s has not been registered.', 'event_espresso'), $module);
46
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
46
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
47 47
             return false;
48 48
         }
49 49
         if (empty($route)) {
50 50
             $msg = sprintf(esc_html__('No route has been supplied.', 'event_espresso'), $route);
51
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
51
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
52 52
             return false;
53 53
         }
54
-        if (! method_exists('EED_' . $module, $method_name)) {
54
+        if ( ! method_exists('EED_'.$module, $method_name)) {
55 55
             $msg = sprintf(
56 56
                 esc_html__('A valid class method for the %s route has not been supplied.', 'event_espresso'),
57 57
                 $route
58 58
             );
59
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
59
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
60 60
             return false;
61 61
         }
62
-        $this->module_route_map[ $key ][ $route ] = ['EED_' . $module, $method_name];
62
+        $this->module_route_map[$key][$route] = ['EED_'.$module, $method_name];
63 63
         return true;
64 64
     }
65 65
 
@@ -75,8 +75,8 @@  discard block
 block discarded – undo
75 75
     {
76 76
         do_action('AHEE__EE_Config__get_route__begin', $route);
77 77
         $route = (string) apply_filters('FHEE__EE_Config__get_route', $route);
78
-        if (isset($this->module_route_map[ $key ][ $route ])) {
79
-            return $this->module_route_map[ $key ][ $route ];
78
+        if (isset($this->module_route_map[$key][$route])) {
79
+            return $this->module_route_map[$key][$route];
80 80
         }
81 81
         return [];
82 82
     }
@@ -109,10 +109,10 @@  discard block
 block discarded – undo
109 109
             $route = $route[1];
110 110
         }
111 111
         do_action('AHEE__EE_Config__get_forward__begin', $route, $status);
112
-        if (isset($this->module_forward_map[ $key ][ $route ][ $status ])) {
112
+        if (isset($this->module_forward_map[$key][$route][$status])) {
113 113
             return apply_filters(
114 114
                 'FHEE__EE_Config__get_forward',
115
-                $this->module_forward_map[ $key ][ $route ][ $status ],
115
+                $this->module_forward_map[$key][$route][$status],
116 116
                 $route,
117 117
                 $status
118 118
             );
@@ -134,15 +134,15 @@  discard block
 block discarded – undo
134 134
     public function registerView(string $view, string $route, string $key = 'ee', int $status = 0): bool
135 135
     {
136 136
         do_action('AHEE__EE_Config__register_view__begin', $route, $status, $view);
137
-        if (! isset($this->module_route_map[ $key ][ $route ]) || empty($route)) {
137
+        if ( ! isset($this->module_route_map[$key][$route]) || empty($route)) {
138 138
             $msg = sprintf(
139 139
                 esc_html__('The module route %s for this view has not been registered.', 'event_espresso'),
140 140
                 $route
141 141
             );
142
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
142
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
143 143
             return false;
144 144
         }
145
-        if (! is_readable($view)) {
145
+        if ( ! is_readable($view)) {
146 146
             $msg = sprintf(
147 147
                 esc_html__(
148 148
                     'The %s view file could not be found or is not readable due to file permissions.',
@@ -150,10 +150,10 @@  discard block
 block discarded – undo
150 150
                 ),
151 151
                 $view
152 152
             );
153
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
153
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
154 154
             return false;
155 155
         }
156
-        $this->module_view_map[ $key ][ $route ][ $status ] = $view;
156
+        $this->module_view_map[$key][$route][$status] = $view;
157 157
         return true;
158 158
     }
159 159
 
@@ -171,47 +171,47 @@  discard block
 block discarded – undo
171 171
     public function registerForward($forward, string $route, string $key = 'ee', int $status = 0): bool
172 172
     {
173 173
         do_action('AHEE__EE_Config__register_forward', $route, $status, $forward);
174
-        if (! isset($this->module_route_map[ $key ][ $route ]) || empty($route)) {
174
+        if ( ! isset($this->module_route_map[$key][$route]) || empty($route)) {
175 175
             $msg = sprintf(
176 176
                 esc_html__('The module route %s for this forward has not been registered.', 'event_espresso'),
177 177
                 $route
178 178
             );
179
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
179
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
180 180
             return false;
181 181
         }
182 182
         if (empty($forward)) {
183 183
             $msg = sprintf(esc_html__('No forwarding route has been supplied.', 'event_espresso'), $route);
184
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
184
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
185 185
             return false;
186 186
         }
187 187
         if (is_array($forward)) {
188
-            if (! isset($forward[1])) {
188
+            if ( ! isset($forward[1])) {
189 189
                 $msg = sprintf(
190 190
                     esc_html__('A class method for the %s forwarding route has not been supplied.', 'event_espresso'),
191 191
                     $route
192 192
                 );
193
-                EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
193
+                EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
194 194
                 return false;
195 195
             }
196
-            if (! method_exists($forward[0], $forward[1])) {
196
+            if ( ! method_exists($forward[0], $forward[1])) {
197 197
                 $msg = sprintf(
198 198
                     esc_html__('The class method %1$s for the %2$s forwarding route is in invalid.', 'event_espresso'),
199 199
                     $forward[1],
200 200
                     $route
201 201
                 );
202
-                EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
202
+                EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
203 203
                 return false;
204 204
             }
205
-        } elseif (! function_exists($forward)) {
205
+        } elseif ( ! function_exists($forward)) {
206 206
             $msg = sprintf(
207 207
                 esc_html__('The function %1$s for the %2$s forwarding route is in invalid.', 'event_espresso'),
208 208
                 $forward,
209 209
                 $route
210 210
             );
211
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
211
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
212 212
             return false;
213 213
         }
214
-        $this->module_forward_map[ $key ][ $route ][ absint($status) ] = $forward;
214
+        $this->module_forward_map[$key][$route][absint($status)] = $forward;
215 215
         return true;
216 216
     }
217 217
 
@@ -228,10 +228,10 @@  discard block
 block discarded – undo
228 228
     public function getView(string $route = '', string $key = 'ee', int $status = 0): string
229 229
     {
230 230
         do_action('AHEE__EE_Config__get_view__begin', $route, $status);
231
-        if (isset($this->module_view_map[ $key ][ $route ][ $status ])) {
231
+        if (isset($this->module_view_map[$key][$route][$status])) {
232 232
             return (string) apply_filters(
233 233
                 'FHEE__EE_Config__get_view',
234
-                $this->module_view_map[ $key ][ $route ][ $status ],
234
+                $this->module_view_map[$key][$route][$status],
235 235
                 $route,
236 236
                 $status
237 237
             );
Please login to merge, or discard this patch.
core/services/addon/AddonManager.php 1 patch
Indentation   +79 added lines, -79 removed lines patch added patch discarded remove patch
@@ -24,95 +24,95 @@
 block discarded – undo
24 24
  */
25 25
 class AddonManager
26 26
 {
27
-    private AddonCollection $addons;
27
+	private AddonCollection $addons;
28 28
 
29
-    private IncompatibleAddonHandler $incompatible_addon_handler;
29
+	private IncompatibleAddonHandler $incompatible_addon_handler;
30 30
 
31
-    private Psr4Autoloader $psr4_loader;
31
+	private Psr4Autoloader $psr4_loader;
32 32
 
33
-    private RegisterV1Addon $register_v1_addon;
33
+	private RegisterV1Addon $register_v1_addon;
34 34
 
35
-    private ThirdPartyPluginHandler $third_party_plugin_handler;
35
+	private ThirdPartyPluginHandler $third_party_plugin_handler;
36 36
 
37 37
 
38
-    /**
39
-     * AddonManager constructor.
40
-     *
41
-     * @param AddonCollection          $addons
42
-     * @param Psr4Autoloader           $psr4_loader
43
-     * @param RegisterV1Addon          $register_v1_addon
44
-     * @param IncompatibleAddonHandler $incompatible_addon_handler
45
-     * @param ThirdPartyPluginHandler  $third_party_plugin_handler
46
-     */
47
-    public function __construct(
48
-        AddonCollection $addons,
49
-        Psr4Autoloader $psr4_loader,
50
-        RegisterV1Addon $register_v1_addon,
51
-        IncompatibleAddonHandler $incompatible_addon_handler,
52
-        ThirdPartyPluginHandler $third_party_plugin_handler
53
-    ) {
54
-        $this->addons                     = $addons;
55
-        $this->psr4_loader                = $psr4_loader;
56
-        $this->register_v1_addon          = $register_v1_addon;
57
-        $this->incompatible_addon_handler = $incompatible_addon_handler;
58
-        $this->third_party_plugin_handler = $third_party_plugin_handler;
59
-    }
38
+	/**
39
+	 * AddonManager constructor.
40
+	 *
41
+	 * @param AddonCollection          $addons
42
+	 * @param Psr4Autoloader           $psr4_loader
43
+	 * @param RegisterV1Addon          $register_v1_addon
44
+	 * @param IncompatibleAddonHandler $incompatible_addon_handler
45
+	 * @param ThirdPartyPluginHandler  $third_party_plugin_handler
46
+	 */
47
+	public function __construct(
48
+		AddonCollection $addons,
49
+		Psr4Autoloader $psr4_loader,
50
+		RegisterV1Addon $register_v1_addon,
51
+		IncompatibleAddonHandler $incompatible_addon_handler,
52
+		ThirdPartyPluginHandler $third_party_plugin_handler
53
+	) {
54
+		$this->addons                     = $addons;
55
+		$this->psr4_loader                = $psr4_loader;
56
+		$this->register_v1_addon          = $register_v1_addon;
57
+		$this->incompatible_addon_handler = $incompatible_addon_handler;
58
+		$this->third_party_plugin_handler = $third_party_plugin_handler;
59
+	}
60 60
 
61 61
 
62
-    /**
63
-     * @throws Exception
64
-     */
65
-    public function initialize()
66
-    {
67
-        // set autoloaders for all of the classes implementing the legacy EEI_Plugin_API
68
-        // which provide helpers for EE plugin authors to more easily register certain components with EE.
69
-        EEH_Autoloader::register_autoloaders_for_each_file_in_folder(EE_LIBRARIES . 'plugin_api');
70
-    }
62
+	/**
63
+	 * @throws Exception
64
+	 */
65
+	public function initialize()
66
+	{
67
+		// set autoloaders for all of the classes implementing the legacy EEI_Plugin_API
68
+		// which provide helpers for EE plugin authors to more easily register certain components with EE.
69
+		EEH_Autoloader::register_autoloaders_for_each_file_in_folder(EE_LIBRARIES . 'plugin_api');
70
+	}
71 71
 
72 72
 
73
-    /**
74
-     * @throws Exception
75
-     * @throws Throwable
76
-     */
77
-    public function loadAddons()
78
-    {
79
-        try {
80
-            $this->incompatible_addon_handler->deactivateIncompatibleAddons();
81
-            // legacy add-on API
82
-            do_action('AHEE__EE_System__load_espresso_addons');
83
-            // new add-on API that uses versioning
84
-            do_action(
85
-                'AHEE__EventEspresso_core_services_addon_AddonManager__initialize__addons',
86
-                $this->addons,
87
-                espresso_version()
88
-            );
89
-            // addons are responsible for loading their AddonApiVersion into the AddonCollection
90
-            foreach ($this->addons as $addon) {
91
-                if ($addon instanceof AddonApiVersion) {
92
-                    $this->registerAddon($addon);
93
-                }
94
-            }
95
-            $this->third_party_plugin_handler->loadPlugins();
96
-            do_action('AHEE__EE_System__load_espresso_addons__complete');
97
-        } catch (Exception $exception) {
98
-            new ExceptionStackTraceDisplay($exception);
99
-        }
100
-    }
73
+	/**
74
+	 * @throws Exception
75
+	 * @throws Throwable
76
+	 */
77
+	public function loadAddons()
78
+	{
79
+		try {
80
+			$this->incompatible_addon_handler->deactivateIncompatibleAddons();
81
+			// legacy add-on API
82
+			do_action('AHEE__EE_System__load_espresso_addons');
83
+			// new add-on API that uses versioning
84
+			do_action(
85
+				'AHEE__EventEspresso_core_services_addon_AddonManager__initialize__addons',
86
+				$this->addons,
87
+				espresso_version()
88
+			);
89
+			// addons are responsible for loading their AddonApiVersion into the AddonCollection
90
+			foreach ($this->addons as $addon) {
91
+				if ($addon instanceof AddonApiVersion) {
92
+					$this->registerAddon($addon);
93
+				}
94
+			}
95
+			$this->third_party_plugin_handler->loadPlugins();
96
+			do_action('AHEE__EE_System__load_espresso_addons__complete');
97
+		} catch (Exception $exception) {
98
+			new ExceptionStackTraceDisplay($exception);
99
+		}
100
+	}
101 101
 
102 102
 
103
-    /**
104
-     * @param AddonApiVersion $addon
105
-     * @throws EE_Error
106
-     */
107
-    private function registerAddon(AddonApiVersion $addon)
108
-    {
109
-        // first register addon namespace so that FQCNs resolve correctly
110
-        $this->psr4_loader->addNamespace($addon->getNamespace(), dirname($addon->mainFile()) . '/src/');
111
-        // then allow add-on to perform any other setup that relied on PSR4 autoloading
112
-        $addon->initialize();
113
-        // now register each addon based on it's API version
114
-        if ($addon instanceof AddonApiV1) {
115
-            $this->register_v1_addon->register($addon);
116
-        }
117
-    }
103
+	/**
104
+	 * @param AddonApiVersion $addon
105
+	 * @throws EE_Error
106
+	 */
107
+	private function registerAddon(AddonApiVersion $addon)
108
+	{
109
+		// first register addon namespace so that FQCNs resolve correctly
110
+		$this->psr4_loader->addNamespace($addon->getNamespace(), dirname($addon->mainFile()) . '/src/');
111
+		// then allow add-on to perform any other setup that relied on PSR4 autoloading
112
+		$addon->initialize();
113
+		// now register each addon based on it's API version
114
+		if ($addon instanceof AddonApiV1) {
115
+			$this->register_v1_addon->register($addon);
116
+		}
117
+	}
118 118
 }
Please login to merge, or discard this patch.
core/services/widgets/LegacyWidgetsManager.php 2 patches
Indentation   +104 added lines, -104 removed lines patch added patch discarded remove patch
@@ -9,115 +9,115 @@
 block discarded – undo
9 9
 
10 10
 class LegacyWidgetsManager
11 11
 {
12
-    public RegistryContainer $widgets;
12
+	public RegistryContainer $widgets;
13 13
 
14
-    public function __construct()
15
-    {
16
-        $this->widgets = EE_Registry::instance()->widgets;
17
-    }
14
+	public function __construct()
15
+	{
16
+		$this->widgets = EE_Registry::instance()->widgets;
17
+	}
18 18
 
19 19
 
20
-    public function setHooks()
21
-    {
22
-        add_action('widgets_init', [$this, 'widgetsInit']);
23
-    }
20
+	public function setHooks()
21
+	{
22
+		add_action('widgets_init', [$this, 'widgetsInit']);
23
+	}
24 24
 
25 25
 
26
-    /**
27
-     * @return void
28
-     */
29
-    public function widgetsInit()
30
-    {
31
-        // only init widgets on admin pages when not in complete maintenance, and
32
-        // on frontend when not in any maintenance mode
33
-        if (
34
-            MaintenanceStatus::isDisabled()
35
-            || (is_admin() && MaintenanceStatus::isNotFullSite())
36
-        ) {
37
-            // grab list of installed widgets
38
-            $widgets_to_register = glob(EE_WIDGETS . '*', GLOB_ONLYDIR);
39
-            // filter list of modules to register
40
-            $widgets_to_register = apply_filters(
41
-                'FHEE__EE_Config__register_widgets__widgets_to_register',
42
-                $widgets_to_register
43
-            );
44
-            if (! empty($widgets_to_register)) {
45
-                // cycle thru widget folders
46
-                foreach ($widgets_to_register as $widget_path) {
47
-                    // add to list of installed widget modules
48
-                    $this->registerWidget($widget_path);
49
-                }
50
-            }
51
-            // filter list of installed modules
52
-            $this->widgets = apply_filters(
53
-                'FHEE__EE_Config__register_widgets__installed_widgets',
54
-                $this->widgets
55
-            );
56
-        }
57
-    }
26
+	/**
27
+	 * @return void
28
+	 */
29
+	public function widgetsInit()
30
+	{
31
+		// only init widgets on admin pages when not in complete maintenance, and
32
+		// on frontend when not in any maintenance mode
33
+		if (
34
+			MaintenanceStatus::isDisabled()
35
+			|| (is_admin() && MaintenanceStatus::isNotFullSite())
36
+		) {
37
+			// grab list of installed widgets
38
+			$widgets_to_register = glob(EE_WIDGETS . '*', GLOB_ONLYDIR);
39
+			// filter list of modules to register
40
+			$widgets_to_register = apply_filters(
41
+				'FHEE__EE_Config__register_widgets__widgets_to_register',
42
+				$widgets_to_register
43
+			);
44
+			if (! empty($widgets_to_register)) {
45
+				// cycle thru widget folders
46
+				foreach ($widgets_to_register as $widget_path) {
47
+					// add to list of installed widget modules
48
+					$this->registerWidget($widget_path);
49
+				}
50
+			}
51
+			// filter list of installed modules
52
+			$this->widgets = apply_filters(
53
+				'FHEE__EE_Config__register_widgets__installed_widgets',
54
+				$this->widgets
55
+			);
56
+		}
57
+	}
58 58
 
59 59
 
60
-    /**
61
-     * makes core aware of this widget
62
-     *
63
-     * @param string $widget_path - full path up to and including widget folder
64
-     * @return void
65
-     */
66
-    public function registerWidget(string $widget_path = '')
67
-    {
68
-        do_action('AHEE__EE_Config__register_widget__begin', $widget_path);
69
-        $widget_ext = '.widget.php';
70
-        // make all separators match
71
-        $widget_path = rtrim(str_replace('\\', DS, $widget_path), DS);
72
-        // does the file path INCLUDE the actual file name as part of the path ?
73
-        if (strpos($widget_path, $widget_ext) !== false) {
74
-            // grab and shortcode file name from directory name and break apart at dots
75
-            $file_name = explode('.', basename($widget_path));
76
-            // take first segment from file name pieces and remove class prefix if it exists
77
-            $widget = strpos($file_name[0], 'EEW_') === 0 ? substr($file_name[0], 4) : $file_name[0];
78
-            // sanitize shortcode directory name
79
-            $widget = sanitize_key($widget);
80
-            // now we need to rebuild the shortcode path
81
-            $widget_path = explode('/', $widget_path);
82
-            // remove last segment
83
-            array_pop($widget_path);
84
-            // glue it back together
85
-            $widget_path = implode(DS, $widget_path);
86
-        } else {
87
-            // grab and sanitize widget directory name
88
-            $widget = sanitize_key(basename($widget_path));
89
-        }
90
-        // create classname from widget directory name
91
-        $widget = str_replace(' ', '_', ucwords(str_replace('_', ' ', $widget)));
92
-        // add class prefix
93
-        $widget_class = 'EEW_' . $widget;
94
-        // is it already added?
95
-        if ($this->widgets->has($widget_class)) {
96
-            return;
97
-        }
98
-        // does the widget exist ?
99
-        if (! is_readable($widget_path . '/' . $widget_class . $widget_ext)) {
100
-            $msg = sprintf(
101
-                esc_html__(
102
-                    'The requested %1$s widget file could not be found or is not readable due to file permissions. Please ensure the following path is correct: %2$s',
103
-                    'event_espresso'
104
-                ),
105
-                $widget_class,
106
-                $widget_path . '/' . $widget_class . $widget_ext
107
-            );
108
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
109
-            return;
110
-        }
111
-        // load the widget class file
112
-        require_once($widget_path . '/' . $widget_class . $widget_ext);
113
-        // verify that class exists
114
-        if (! class_exists($widget_class)) {
115
-            $msg = sprintf(esc_html__('The requested %s widget class does not exist.', 'event_espresso'), $widget_class);
116
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
117
-            return;
118
-        }
119
-        register_widget($widget_class);
120
-        // add to array of registered widgets
121
-        $this->widgets->add($widget_class, $widget_path . '/' . $widget_class . $widget_ext);
122
-    }
60
+	/**
61
+	 * makes core aware of this widget
62
+	 *
63
+	 * @param string $widget_path - full path up to and including widget folder
64
+	 * @return void
65
+	 */
66
+	public function registerWidget(string $widget_path = '')
67
+	{
68
+		do_action('AHEE__EE_Config__register_widget__begin', $widget_path);
69
+		$widget_ext = '.widget.php';
70
+		// make all separators match
71
+		$widget_path = rtrim(str_replace('\\', DS, $widget_path), DS);
72
+		// does the file path INCLUDE the actual file name as part of the path ?
73
+		if (strpos($widget_path, $widget_ext) !== false) {
74
+			// grab and shortcode file name from directory name and break apart at dots
75
+			$file_name = explode('.', basename($widget_path));
76
+			// take first segment from file name pieces and remove class prefix if it exists
77
+			$widget = strpos($file_name[0], 'EEW_') === 0 ? substr($file_name[0], 4) : $file_name[0];
78
+			// sanitize shortcode directory name
79
+			$widget = sanitize_key($widget);
80
+			// now we need to rebuild the shortcode path
81
+			$widget_path = explode('/', $widget_path);
82
+			// remove last segment
83
+			array_pop($widget_path);
84
+			// glue it back together
85
+			$widget_path = implode(DS, $widget_path);
86
+		} else {
87
+			// grab and sanitize widget directory name
88
+			$widget = sanitize_key(basename($widget_path));
89
+		}
90
+		// create classname from widget directory name
91
+		$widget = str_replace(' ', '_', ucwords(str_replace('_', ' ', $widget)));
92
+		// add class prefix
93
+		$widget_class = 'EEW_' . $widget;
94
+		// is it already added?
95
+		if ($this->widgets->has($widget_class)) {
96
+			return;
97
+		}
98
+		// does the widget exist ?
99
+		if (! is_readable($widget_path . '/' . $widget_class . $widget_ext)) {
100
+			$msg = sprintf(
101
+				esc_html__(
102
+					'The requested %1$s widget file could not be found or is not readable due to file permissions. Please ensure the following path is correct: %2$s',
103
+					'event_espresso'
104
+				),
105
+				$widget_class,
106
+				$widget_path . '/' . $widget_class . $widget_ext
107
+			);
108
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
109
+			return;
110
+		}
111
+		// load the widget class file
112
+		require_once($widget_path . '/' . $widget_class . $widget_ext);
113
+		// verify that class exists
114
+		if (! class_exists($widget_class)) {
115
+			$msg = sprintf(esc_html__('The requested %s widget class does not exist.', 'event_espresso'), $widget_class);
116
+			EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
117
+			return;
118
+		}
119
+		register_widget($widget_class);
120
+		// add to array of registered widgets
121
+		$this->widgets->add($widget_class, $widget_path . '/' . $widget_class . $widget_ext);
122
+	}
123 123
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -35,13 +35,13 @@  discard block
 block discarded – undo
35 35
             || (is_admin() && MaintenanceStatus::isNotFullSite())
36 36
         ) {
37 37
             // grab list of installed widgets
38
-            $widgets_to_register = glob(EE_WIDGETS . '*', GLOB_ONLYDIR);
38
+            $widgets_to_register = glob(EE_WIDGETS.'*', GLOB_ONLYDIR);
39 39
             // filter list of modules to register
40 40
             $widgets_to_register = apply_filters(
41 41
                 'FHEE__EE_Config__register_widgets__widgets_to_register',
42 42
                 $widgets_to_register
43 43
             );
44
-            if (! empty($widgets_to_register)) {
44
+            if ( ! empty($widgets_to_register)) {
45 45
                 // cycle thru widget folders
46 46
                 foreach ($widgets_to_register as $widget_path) {
47 47
                     // add to list of installed widget modules
@@ -90,34 +90,34 @@  discard block
 block discarded – undo
90 90
         // create classname from widget directory name
91 91
         $widget = str_replace(' ', '_', ucwords(str_replace('_', ' ', $widget)));
92 92
         // add class prefix
93
-        $widget_class = 'EEW_' . $widget;
93
+        $widget_class = 'EEW_'.$widget;
94 94
         // is it already added?
95 95
         if ($this->widgets->has($widget_class)) {
96 96
             return;
97 97
         }
98 98
         // does the widget exist ?
99
-        if (! is_readable($widget_path . '/' . $widget_class . $widget_ext)) {
99
+        if ( ! is_readable($widget_path.'/'.$widget_class.$widget_ext)) {
100 100
             $msg = sprintf(
101 101
                 esc_html__(
102 102
                     'The requested %1$s widget file could not be found or is not readable due to file permissions. Please ensure the following path is correct: %2$s',
103 103
                     'event_espresso'
104 104
                 ),
105 105
                 $widget_class,
106
-                $widget_path . '/' . $widget_class . $widget_ext
106
+                $widget_path.'/'.$widget_class.$widget_ext
107 107
             );
108
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
108
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
109 109
             return;
110 110
         }
111 111
         // load the widget class file
112
-        require_once($widget_path . '/' . $widget_class . $widget_ext);
112
+        require_once($widget_path.'/'.$widget_class.$widget_ext);
113 113
         // verify that class exists
114
-        if (! class_exists($widget_class)) {
114
+        if ( ! class_exists($widget_class)) {
115 115
             $msg = sprintf(esc_html__('The requested %s widget class does not exist.', 'event_espresso'), $widget_class);
116
-            EE_Error::add_error($msg . '||' . $msg, __FILE__, __FUNCTION__, __LINE__);
116
+            EE_Error::add_error($msg.'||'.$msg, __FILE__, __FUNCTION__, __LINE__);
117 117
             return;
118 118
         }
119 119
         register_widget($widget_class);
120 120
         // add to array of registered widgets
121
-        $this->widgets->add($widget_class, $widget_path . '/' . $widget_class . $widget_ext);
121
+        $this->widgets->add($widget_class, $widget_path.'/'.$widget_class.$widget_ext);
122 122
     }
123 123
 }
Please login to merge, or discard this patch.
core/EE_Registry.core.php 1 patch
Indentation   +1700 added lines, -1700 removed lines patch added patch discarded remove patch
@@ -22,1704 +22,1704 @@
 block discarded – undo
22 22
  */
23 23
 class EE_Registry implements ResettableInterface
24 24
 {
25
-    private static ?EE_Registry $_instance = null;
26
-
27
-    protected EE_Dependency_Map $_dependency_map;
28
-
29
-    private Mirror $mirror;
30
-
31
-    private ClassInterfaceCache $class_cache;
32
-
33
-    private ObjectIdentifier $object_identifier;
34
-
35
-    protected array $_class_abbreviations = [];
36
-
37
-    public ?CommandBusInterface $BUS = null;
38
-
39
-    public ?EE_Cart $CART = null;
40
-
41
-    public ?EE_Config $CFG = null;
42
-
43
-    public ?EE_Network_Config $NET_CFG = null;
44
-
45
-    /**
46
-     * RegistryContainer for storing library classes in
47
-     *
48
-     * @var RegistryContainer|null $LIB
49
-     */
50
-    public ?RegistryContainer $LIB = null;
51
-
52
-    /**
53
-     * @var EE_Request_Handler|null $REQ
54
-     * @deprecated 4.10.14.p
55
-     */
56
-    public ?EE_Request_Handler $REQ = null;
57
-
58
-    public ?EE_Session $SSN = null;
59
-
60
-    /**
61
-     * @since 4.5.0
62
-     * @var EE_Capabilities|null $CAP
63
-     */
64
-    public ?EE_Capabilities $CAP = null;
65
-
66
-    /**
67
-     * @since 4.9.0
68
-     * @var EE_Message_Resource_Manager|null $MRM
69
-     */
70
-    public ?EE_Message_Resource_Manager $MRM = null;
71
-
72
-    public ?Registry $AssetsRegistry = null;
73
-
74
-    /**
75
-     * RegistryContainer for holding addons which have registered themselves to work with EE core
76
-     *
77
-     * @var RegistryContainer|EE_Addon[] $addons
78
-     */
79
-    public $addons = [];
80
-
81
-    /**
82
-     * keys are 'short names' (eg Event), values are class names (eg 'EEM_Event')
83
-     *
84
-     * @var EEM_Base[] $models
85
-     */
86
-    public array $models = [];
87
-
88
-    /**
89
-     * @var RegistryContainer|EED_Module[] $modules
90
-     */
91
-    public $modules = [];
92
-
93
-    /**
94
-     * @var RegistryContainer|EES_Shortcode[] $shortcodes
95
-     */
96
-    public $shortcodes = [];
97
-
98
-    /**
99
-     * @var RegistryContainer|WP_Widget[] $widgets
100
-     */
101
-    public $widgets = [];
102
-
103
-    /**
104
-     * this is an array of all implemented model names (i.e. not the parent abstract models, or models
105
-     * which don't actually fetch items from the DB in the normal way (ie, are not children of EEM_Base)).
106
-     * Keys are model "short names" (eg "Event") as used in model relations, and values are
107
-     * classnames (eg "EEM_Event")
108
-     *
109
-     * @var array $non_abstract_db_models
110
-     */
111
-    public array $non_abstract_db_models = [];
112
-
113
-    /**
114
-     * internationalization for JS strings
115
-     *    usage:   EE_Registry::i18n_js_strings['string_key'] = esc_html__( 'string to translate.', 'event_espresso' );
116
-     *    in js file:  var translatedString = eei18n.string_key;
117
-     *
118
-     * @var array $i18n_js_strings
119
-     */
120
-    public static array $i18n_js_strings = [];
121
-
122
-    /**
123
-     * $main_file - path to espresso.php
124
-     *
125
-     * @var string $main_file
126
-     */
127
-    public string $main_file = '';
128
-
129
-    /**
130
-     * boolean flag to indicate whether to load/save dependencies from/to the cache
131
-     *
132
-     * @var bool $_cache_on
133
-     */
134
-    protected bool $_cache_on = true;
135
-
136
-
137
-    /**
138
-     * @singleton method used to instantiate class object
139
-     * @param EE_Dependency_Map|null   $dependency_map
140
-     * @param Mirror|null              $mirror
141
-     * @param ClassInterfaceCache|null $class_cache
142
-     * @param ObjectIdentifier|null    $object_identifier
143
-     * @return EE_Registry instance
144
-     */
145
-    public static function instance(
146
-        EE_Dependency_Map $dependency_map = null,
147
-        Mirror $mirror = null,
148
-        ClassInterfaceCache $class_cache = null,
149
-        ObjectIdentifier $object_identifier = null
150
-    ): EE_Registry {
151
-        // check if class object is instantiated
152
-        if (
153
-            ! self::$_instance instanceof EE_Registry
154
-            && $dependency_map instanceof EE_Dependency_Map
155
-            && $mirror instanceof Mirror
156
-            && $class_cache instanceof ClassInterfaceCache
157
-            && $object_identifier instanceof ObjectIdentifier
158
-        ) {
159
-            self::$_instance = new self(
160
-                $dependency_map,
161
-                $mirror,
162
-                $class_cache,
163
-                $object_identifier
164
-            );
165
-        }
166
-        return self::$_instance;
167
-    }
168
-
169
-
170
-    /**
171
-     * protected constructor to prevent direct creation
172
-     *
173
-     * @Constructor
174
-     * @param EE_Dependency_Map   $dependency_map
175
-     * @param Mirror              $mirror
176
-     * @param ClassInterfaceCache $class_cache
177
-     * @param ObjectIdentifier    $object_identifier
178
-     */
179
-    protected function __construct(
180
-        EE_Dependency_Map $dependency_map,
181
-        Mirror $mirror,
182
-        ClassInterfaceCache $class_cache,
183
-        ObjectIdentifier $object_identifier
184
-    ) {
185
-        $this->_dependency_map   = $dependency_map;
186
-        $this->mirror            = $mirror;
187
-        $this->class_cache       = $class_cache;
188
-        $this->object_identifier = $object_identifier;
189
-
190
-        $this->LIB        = new RegistryContainer();
191
-        $this->addons     = new RegistryContainer();
192
-        $this->modules    = new RegistryContainer();
193
-        $this->shortcodes = new RegistryContainer();
194
-        $this->widgets    = new RegistryContainer();
195
-        add_action('EE_Load_Espresso_Core__handle_request__initialize_core_loading', [$this, 'initialize']);
196
-    }
197
-
198
-
199
-    /**
200
-     * initialize
201
-     *
202
-     * @throws OutOfBoundsException
203
-     * @throws InvalidArgumentException
204
-     * @throws InvalidInterfaceException
205
-     * @throws InvalidDataTypeException
206
-     * @throws EE_Error
207
-     * @throws ReflectionException
208
-     */
209
-    public function initialize()
210
-    {
211
-        $this->_class_abbreviations = apply_filters(
212
-            'FHEE__EE_Registry____construct___class_abbreviations',
213
-            [
214
-                'EE_Config'                                       => 'CFG',
215
-                'EE_Session'                                      => 'SSN',
216
-                'EE_Capabilities'                                 => 'CAP',
217
-                'EE_Cart'                                         => 'CART',
218
-                'EE_Network_Config'                               => 'NET_CFG',
219
-                'EE_Request_Handler'                              => 'REQ',
220
-                'EE_Message_Resource_Manager'                     => 'MRM',
221
-                'EventEspresso\core\services\commands\CommandBus' => 'BUS',
222
-                'EventEspresso\core\services\assets\Registry'     => 'AssetsRegistry',
223
-            ]
224
-        );
225
-        $this->load_core('Base', [], true);
226
-        // add our request and response objects to the cache
227
-        $request_loader = $this->_dependency_map->class_loader(
228
-            'EventEspresso\core\services\request\Request'
229
-        );
230
-        $this->_set_cached_class(
231
-            $request_loader(),
232
-            'EventEspresso\core\services\request\Request'
233
-        );
234
-        $response_loader = $this->_dependency_map->class_loader(
235
-            'EventEspresso\core\services\request\Response'
236
-        );
237
-        $this->_set_cached_class(
238
-            $response_loader(),
239
-            'EventEspresso\core\services\request\Response'
240
-        );
241
-        add_action('AHEE__EE_System__set_hooks_for_core', [$this, 'init']);
242
-    }
243
-
244
-
245
-    /**
246
-     * @return void
247
-     */
248
-    public function init()
249
-    {
250
-        // Get current page protocol
251
-        $protocol = is_ssl() ? 'https://' : 'http://';
252
-        // Output admin-ajax.php URL with same protocol as current page
253
-        self::$i18n_js_strings['ajax_url'] = admin_url('admin-ajax.php', $protocol);
254
-        self::$i18n_js_strings['wp_debug'] = defined('WP_DEBUG') && WP_DEBUG;
255
-    }
256
-
257
-
258
-    /**
259
-     * @return array
260
-     */
261
-    public static function sanitize_i18n_js_strings(): array
262
-    {
263
-        array_walk(EE_Registry::$i18n_js_strings, function (&$value) {
264
-            if (is_scalar($value)) {
265
-                $value = html_entity_decode((string) $value, ENT_QUOTES, 'UTF-8');
266
-                // replace all escaped instances of \n with actual new lines
267
-                $value = str_replace("\\n", "\n", $value);
268
-            }
269
-        });
270
-        // sort the array by key in a case-insensitive manner
271
-        ksort(EE_Registry::$i18n_js_strings, SORT_NATURAL | SORT_FLAG_CASE);
272
-        return EE_Registry::$i18n_js_strings;
273
-    }
274
-
275
-
276
-    /**
277
-     * localize_i18n_js_strings
278
-     *
279
-     * @return string
280
-     */
281
-    public static function localize_i18n_js_strings(): string
282
-    {
283
-        $i18n_js_strings = wp_json_encode(EE_Registry::sanitize_i18n_js_strings());
284
-        return "/* <![CDATA[ */ var eei18n = $i18n_js_strings; /* ]]> */";
285
-    }
286
-
287
-
288
-    /**
289
-     * @param mixed string | EED_Module $module
290
-     * @throws OutOfBoundsException
291
-     * @throws InvalidArgumentException
292
-     * @throws InvalidInterfaceException
293
-     * @throws InvalidDataTypeException
294
-     * @throws EE_Error
295
-     * @throws ReflectionException
296
-     */
297
-    public function add_module($module)
298
-    {
299
-        if ($module instanceof EED_Module) {
300
-            $module_class = get_class($module);
301
-            $this->modules->add($module_class, $module);
302
-        } else {
303
-            if (! class_exists('EE_Module_Request_Router', false)) {
304
-                $this->load_core('Module_Request_Router');
305
-            }
306
-            EE_Module_Request_Router::module_factory($module);
307
-        }
308
-    }
309
-
310
-
311
-    /**
312
-     * @param string $module_name
313
-     * @return mixed EED_Module | NULL
314
-     */
315
-    public function get_module(string $module_name = '')
316
-    {
317
-        return $this->modules->get($module_name);
318
-    }
319
-
320
-
321
-    /**
322
-     * loads core classes - must be singletons
323
-     *
324
-     * @param string $class_name - simple class name ie: session
325
-     * @param mixed  $arguments
326
-     * @param bool   $load_only
327
-     * @return bool|null|object
328
-     * @throws InvalidInterfaceException
329
-     * @throws InvalidDataTypeException
330
-     * @throws EE_Error
331
-     * @throws ReflectionException
332
-     * @throws InvalidArgumentException
333
-     */
334
-    public function load_core(string $class_name, $arguments = [], bool $load_only = false)
335
-    {
336
-        $core_paths = (array) apply_filters(
337
-            'FHEE__EE_Registry__load_core__core_paths',
338
-            [
339
-                EE_CORE,
340
-                EE_ADMIN,
341
-                EE_CPTS,
342
-                EE_CORE . 'CPTs/',
343
-                EE_CORE . 'data_migration_scripts/',
344
-                EE_CORE . 'request_stack/',
345
-                EE_CORE . 'middleware/',
346
-            ]
347
-        );
348
-        // retrieve instantiated class
349
-        return $this->_load(
350
-            $core_paths,
351
-            'EE_',
352
-            $class_name,
353
-            'core',
354
-            $arguments,
355
-            false,
356
-            true,
357
-            $load_only
358
-        );
359
-    }
360
-
361
-
362
-    /**
363
-     * loads service classes
364
-     *
365
-     * @param string $class_name - simple class name ie: session
366
-     * @param mixed  $arguments
367
-     * @param bool   $load_only
368
-     * @return bool|null|object
369
-     * @throws InvalidInterfaceException
370
-     * @throws InvalidDataTypeException
371
-     * @throws EE_Error
372
-     * @throws ReflectionException
373
-     * @throws InvalidArgumentException
374
-     * @deprecated  4.10.33.p
375
-     */
376
-    public function load_service(string $class_name, $arguments = [], bool $load_only = false)
377
-    {
378
-        $service_paths = (array) apply_filters(
379
-            'FHEE__EE_Registry__load_service__service_paths',
380
-            [
381
-                EE_CORE . 'services/',
382
-            ]
383
-        );
384
-        // retrieve instantiated class
385
-        return $this->_load(
386
-            $service_paths,
387
-            'EE_',
388
-            $class_name,
389
-            'class',
390
-            $arguments,
391
-            false,
392
-            true,
393
-            $load_only
394
-        );
395
-    }
396
-
397
-
398
-    /**
399
-     * loads data_migration_scripts
400
-     *
401
-     * @param string $class_name - class name for the DMS ie: EE_DMS_Core_4_2_0
402
-     * @param mixed  $arguments
403
-     * @return bool|null|object
404
-     * @throws InvalidInterfaceException
405
-     * @throws InvalidDataTypeException
406
-     * @throws EE_Error
407
-     * @throws ReflectionException
408
-     * @throws InvalidArgumentException
409
-     */
410
-    public function load_dms(string $class_name, $arguments = [])
411
-    {
412
-        // retrieve instantiated class
413
-        return $this->_load(
414
-            EE_Data_Migration_Manager::instance()->get_data_migration_script_folders(),
415
-            'EE_DMS_',
416
-            $class_name,
417
-            'dms',
418
-            $arguments,
419
-            false,
420
-            false
421
-        );
422
-    }
423
-
424
-
425
-    /**
426
-     * loads object creating classes - must be singletons
427
-     *
428
-     * @param string $class_name - simple class name ie: attendee
429
-     * @param mixed  $arguments  - an array of arguments to pass to the class
430
-     * @param bool   $from_db    - some classes are instantiated from the db and thus call a different method to
431
-     *                           instantiate
432
-     * @param bool   $cache      if you don't want the class to be stored in the internal cache (non-persistent) then
433
-     *                           set this to FALSE (ie. when instantiating model objects from client in a loop)
434
-     * @param bool   $load_only  whether or not to just load the file and NOT instantiate, or load AND instantiate
435
-     *                           (default)
436
-     * @return EE_Base_Class|object|bool|null
437
-     * @throws InvalidInterfaceException
438
-     * @throws InvalidDataTypeException
439
-     * @throws EE_Error
440
-     * @throws ReflectionException
441
-     * @throws InvalidArgumentException
442
-     */
443
-    public function load_class(
444
-        string $class_name,
445
-        $arguments = [],
446
-        bool $from_db = false,
447
-        bool $cache = true,
448
-        bool $load_only = false
449
-    ) {
450
-        $paths = (array) apply_filters(
451
-            'FHEE__EE_Registry__load_class__paths',
452
-            [
453
-                EE_CORE,
454
-                EE_CLASSES,
455
-                EE_BUSINESS,
456
-            ]
457
-        );
458
-        // retrieve instantiated class
459
-        return $this->_load(
460
-            $paths,
461
-            'EE_',
462
-            $class_name,
463
-            'class',
464
-            $arguments,
465
-            $from_db,
466
-            $cache,
467
-            $load_only
468
-        );
469
-    }
470
-
471
-
472
-    /**
473
-     * loads helper classes - must be singletons
474
-     *
475
-     * @param string $class_name - simple class name ie: price
476
-     * @param mixed  $arguments
477
-     * @param bool   $load_only
478
-     * @return bool|null|object
479
-     * @throws InvalidInterfaceException
480
-     * @throws InvalidDataTypeException
481
-     * @throws EE_Error
482
-     * @throws ReflectionException
483
-     * @throws InvalidArgumentException
484
-     */
485
-    public function load_helper(string $class_name, $arguments = [], bool $load_only = true)
486
-    {
487
-        // todo: add doing_it_wrong() in a few versions after all addons have had calls to this method removed
488
-        $helper_paths = (array) apply_filters('FHEE__EE_Registry__load_helper__helper_paths', [EE_HELPERS]);
489
-        // retrieve instantiated class
490
-        return $this->_load(
491
-            $helper_paths,
492
-            'EEH_',
493
-            $class_name,
494
-            'helper',
495
-            $arguments,
496
-            false,
497
-            true,
498
-            $load_only
499
-        );
500
-    }
501
-
502
-
503
-    /**
504
-     * loads core classes - must be singletons
505
-     *
506
-     * @param string $class_name - simple class name ie: session
507
-     * @param mixed  $arguments
508
-     * @param bool   $load_only
509
-     * @param bool   $cache      whether to cache the object or not.
510
-     * @return bool|null|object
511
-     * @throws InvalidInterfaceException
512
-     * @throws InvalidDataTypeException
513
-     * @throws EE_Error
514
-     * @throws ReflectionException
515
-     * @throws InvalidArgumentException
516
-     */
517
-    public function load_lib(string $class_name, $arguments = [], bool $load_only = false, bool $cache = true)
518
-    {
519
-        $paths = [
520
-            EE_LIBRARIES,
521
-            EE_LIBRARIES . 'messages/',
522
-            EE_LIBRARIES . 'shortcodes/',
523
-            EE_LIBRARIES . 'qtips/',
524
-            EE_LIBRARIES . 'payment_methods/',
525
-        ];
526
-        // retrieve instantiated class
527
-        return $this->_load(
528
-            $paths,
529
-            'EE_',
530
-            $class_name,
531
-            'lib',
532
-            $arguments,
533
-            false,
534
-            $cache,
535
-            $load_only
536
-        );
537
-    }
538
-
539
-
540
-    /**
541
-     * loads model classes - must be singletons
542
-     *
543
-     * @param string $class_name - simple class name ie: price
544
-     * @param mixed  $arguments
545
-     * @param bool   $load_only
546
-     * @return EEM_Base|null
547
-     * @throws InvalidInterfaceException
548
-     * @throws InvalidDataTypeException
549
-     * @throws EE_Error
550
-     * @throws ReflectionException
551
-     * @throws InvalidArgumentException
552
-     */
553
-    public function load_model(string $class_name, $arguments = [], bool $load_only = false): ?EEM_Base
554
-    {
555
-        // retrieve instantiated class
556
-        $model = $this->_load(
557
-            (array) apply_filters(
558
-                'FHEE__EE_Registry__load_model__paths',
559
-                [
560
-                    EE_MODELS,
561
-                    EE_CORE,
562
-                ]
563
-            ),
564
-            'EEM_',
565
-            $class_name,
566
-            'model',
567
-            $arguments,
568
-            false,
569
-            true,
570
-            $load_only
571
-        );
572
-        return $model instanceof EEM_Base ? $model : null;
573
-    }
574
-
575
-
576
-    /**
577
-     * loads model classes - must be singletons
578
-     *
579
-     * @param string $class_name - simple class name ie: price
580
-     * @param mixed  $arguments
581
-     * @param bool   $load_only
582
-     * @return bool|null|object
583
-     * @throws InvalidInterfaceException
584
-     * @throws InvalidDataTypeException
585
-     * @throws EE_Error
586
-     * @throws ReflectionException
587
-     * @throws InvalidArgumentException
588
-     * @deprecated  4.10.33.p
589
-     */
590
-    public function load_model_class(string $class_name, $arguments = [], bool $load_only = true)
591
-    {
592
-        $paths = [
593
-            EE_MODELS . 'fields/',
594
-            EE_MODELS . 'helpers/',
595
-            EE_MODELS . 'relations/',
596
-            EE_MODELS . 'strategies/',
597
-        ];
598
-        // retrieve instantiated class
599
-        return $this->_load(
600
-            $paths,
601
-            'EE_',
602
-            $class_name,
603
-            '',
604
-            $arguments,
605
-            false,
606
-            true,
607
-            $load_only
608
-        );
609
-    }
610
-
611
-
612
-    /**
613
-     * Determines if $model_name is the name of an actual EE model.
614
-     *
615
-     * @param string $model_name like Event, Attendee, Question_Group_Question, etc.
616
-     * @return boolean
617
-     */
618
-    public function is_model_name(string $model_name): bool
619
-    {
620
-        return isset($this->models[ $model_name ]);
621
-    }
622
-
623
-
624
-    /**
625
-     * generic class loader
626
-     *
627
-     * @param string $path_to_file - directory path to file location, not including filename
628
-     * @param string $file_name    - file name  ie:  my_file.php, including extension
629
-     * @param string $type         - file type - core? class? helper? model?
630
-     * @param mixed  $arguments
631
-     * @param bool   $load_only
632
-     * @return bool|null|object
633
-     * @throws InvalidInterfaceException
634
-     * @throws InvalidDataTypeException
635
-     * @throws EE_Error
636
-     * @throws ReflectionException
637
-     * @throws InvalidArgumentException
638
-     */
639
-    public function load_file(
640
-        string $path_to_file,
641
-        string $file_name,
642
-        string $type = '',
643
-        $arguments = [],
644
-        bool $load_only = true
645
-    ) {
646
-        // retrieve instantiated class
647
-        return $this->_load(
648
-            (array) $path_to_file,
649
-            '',
650
-            $file_name,
651
-            $type,
652
-            $arguments,
653
-            false,
654
-            true,
655
-            $load_only
656
-        );
657
-    }
658
-
659
-
660
-    /**
661
-     * @param string $path_to_file - directory path to file location, not including filename
662
-     * @param string $class_name   - full class name  ie:  My_Class
663
-     * @param string $type         - file type - core? class? helper? model?
664
-     * @param mixed  $arguments
665
-     * @param bool   $load_only
666
-     * @return bool|null|object
667
-     * @throws InvalidInterfaceException
668
-     * @throws InvalidDataTypeException
669
-     * @throws EE_Error
670
-     * @throws ReflectionException
671
-     * @throws InvalidArgumentException
672
-     * @deprecated  4.10.33.p
673
-     */
674
-    public function load_addon(
675
-        string $path_to_file,
676
-        string $class_name,
677
-        string $type = 'class',
678
-        $arguments = [],
679
-        bool $load_only = false
680
-    ) {
681
-        // retrieve instantiated class
682
-        return $this->_load(
683
-            (array) $path_to_file,
684
-            'addon',
685
-            $class_name,
686
-            $type,
687
-            $arguments,
688
-            false,
689
-            true,
690
-            $load_only
691
-        );
692
-    }
693
-
694
-
695
-    /**
696
-     * instantiates, caches, and automatically resolves dependencies
697
-     * for classes that use a Fully Qualified Class Name.
698
-     * if the class is not capable of being loaded using PSR-4 autoloading,
699
-     * then you need to use one of the existing load_*() methods
700
-     * which can resolve the classname and filepath from the passed arguments
701
-     *
702
-     * @param string      $class_name Fully Qualified Class Name
703
-     * @param array       $arguments  an argument, or array of arguments to pass to the class upon instantiation
704
-     * @param bool        $cache      whether to cache the instantiated object for reuse
705
-     * @param bool        $from_db    some classes are instantiated from the db
706
-     *                                and thus call a different method to instantiate
707
-     * @param bool        $load_only  if true, will only load the file, but will NOT instantiate an object
708
-     * @param bool|string $addon      if true, will cache the object in the EE_Registry->$addons array
709
-     * @return bool|null|mixed     null = failure to load or instantiate class object.
710
-     *                                object = class loaded and instantiated successfully.
711
-     *                                bool = fail or success when $load_only is true
712
-     * @throws InvalidInterfaceException
713
-     * @throws InvalidDataTypeException
714
-     * @throws EE_Error
715
-     * @throws ReflectionException
716
-     * @throws InvalidArgumentException
717
-     */
718
-    public function create(
719
-        string $class_name = '',
720
-        array $arguments = [],
721
-        bool $cache = false,
722
-        bool $from_db = false,
723
-        bool $load_only = false,
724
-        bool $addon = false
725
-    ) {
726
-        $class_name   = ltrim($class_name, '\\');
727
-        $class_name   = $this->class_cache->getFqnForAlias($class_name);
728
-        $class_exists = $this->loadOrVerifyClassExists($class_name, $arguments);
729
-        // if a non-FQCN was passed, then
730
-        // verifyClassExists() might return an object
731
-        // or it could return null if the class just could not be found anywhere
732
-        if ($class_exists instanceof $class_name || $class_exists === null) {
733
-            // either way, return the results
734
-            return $class_exists;
735
-        }
736
-        $class_name = $class_exists;
737
-        // if we're only loading the class and it already exists, then let's just return true immediately
738
-        if ($load_only) {
739
-            return true;
740
-        }
741
-        $addon = $addon ? 'addon' : '';
742
-        // $this->_cache_on is toggled during the recursive loading that can occur with dependency injection
743
-        // $cache is controlled by individual calls to separate Registry loader methods like load_class()
744
-        // $load_only is also controlled by individual calls to separate Registry loader methods like load_file()
745
-        if ($this->_cache_on && $cache) {
746
-            // return object if it's already cached
747
-            $cached_class = $this->_get_cached_class($class_name, $addon, $arguments);
748
-            if ($cached_class !== null) {
749
-                return $cached_class;
750
-            }
751
-        }                                                           // obtain the loader method from the dependency map
752
-        $loader = $this->_dependency_map->class_loader($class_name);// instantiate the requested object
753
-        if ($loader instanceof Closure) {
754
-            $class_obj = $loader($arguments);
755
-        } else {
756
-            if ($loader && method_exists($this, $loader)) {
757
-                $class_obj = $this->{$loader}($class_name, $arguments);
758
-            } else {
759
-                $class_obj = $this->_create_object($class_name, $arguments, $addon, $from_db);
760
-            }
761
-        }
762
-        if (($this->_cache_on && $cache) || $this->get_class_abbreviation($class_name, '')) {
763
-            // save it for later... kinda like gum  { : $
764
-            $this->_set_cached_class(
765
-                $class_obj,
766
-                $class_name,
767
-                $addon,
768
-                $from_db,
769
-                $arguments
770
-            );
771
-        }
772
-        $this->_cache_on = true;
773
-        return $class_obj;
774
-    }
775
-
776
-
777
-    /**
778
-     * Recursively checks that a class exists and potentially attempts to load classes with non-FQCNs
779
-     *
780
-     * @param string|mixed $class_name
781
-     * @param array        $arguments
782
-     * @param int          $attempt
783
-     * @return mixed
784
-     */
785
-    private function loadOrVerifyClassExists($class_name, array $arguments, int $attempt = 1)
786
-    {
787
-        if (is_object($class_name) || class_exists($class_name)) {
788
-            return $class_name;
789
-        }
790
-        switch ($attempt) {
791
-            case 1:
792
-                // if it's a FQCN then maybe the class is registered with a preceding \
793
-                $class_name = strpos($class_name, '\\') !== false
794
-                    ? '\\' . ltrim($class_name, '\\')
795
-                    : $class_name;
796
-                break;
797
-            case 2:
798
-                //
799
-                $loader = $this->_dependency_map->class_loader($class_name);
800
-                if ($loader && method_exists($this, $loader)) {
801
-                    return $this->{$loader}($class_name, $arguments);
802
-                }
803
-                break;
804
-            case 3:
805
-            default:
806
-                return null;
807
-        }
808
-        $attempt++;
809
-        return $this->loadOrVerifyClassExists($class_name, $arguments, $attempt);
810
-    }
811
-
812
-
813
-    /**
814
-     * instantiates, caches, and injects dependencies for classes
815
-     *
816
-     * @param array  $file_paths         an array of paths to folders to look in
817
-     * @param string $class_prefix       EE  or EEM or... ???
818
-     * @param string $class_name         $class name
819
-     * @param string $type               file type - core? class? helper? model?
820
-     * @param mixed  $arguments          an argument or array of arguments to pass to the class upon instantiation
821
-     * @param bool   $from_db            some classes are instantiated from the db
822
-     *                                   and thus call a different method to instantiate
823
-     * @param bool   $cache              whether to cache the instantiated object for reuse
824
-     * @param bool   $load_only          if true, will only load the file, but will NOT instantiate an object
825
-     * @return bool|null|object          null   = failure to load or instantiate class object.
826
-     *                                   object = class loaded and instantiated successfully.
827
-     *                                   bool   = fail or success when $load_only is true
828
-     * @throws EE_Error
829
-     * @throws ReflectionException
830
-     * @throws InvalidInterfaceException
831
-     * @throws InvalidDataTypeException
832
-     * @throws InvalidArgumentException
833
-     */
834
-    protected function _load(
835
-        array $file_paths = [],
836
-        string $class_prefix = 'EE_',
837
-        string $class_name = '',
838
-        string $type = 'class',
839
-        array $arguments = [],
840
-        bool $from_db = false,
841
-        bool $cache = true,
842
-        bool $load_only = false
843
-    ) {
844
-        $class_name = ltrim($class_name, '\\');
845
-        // strip php file extension
846
-        $class_name = str_replace('.php', '', trim($class_name));
847
-        // does the class have a prefix ?
848
-        if (! empty($class_prefix) && $class_prefix !== 'addon') {
849
-            // make sure $class_prefix is uppercase
850
-            $class_prefix = strtoupper(trim($class_prefix));
851
-            // add class prefix ONCE!!!
852
-            $class_name = $class_prefix . str_replace($class_prefix, '', $class_name);
853
-        }
854
-        $class_name   = $this->class_cache->getFqnForAlias($class_name);
855
-        $class_exists = class_exists($class_name, false);
856
-        // if we're only loading the class and it already exists, then let's just return true immediately
857
-        if ($load_only && $class_exists) {
858
-            return true;
859
-        }
860
-        $arguments = is_array($arguments) ? $arguments : [$arguments];
861
-        // $this->_cache_on is toggled during the recursive loading that can occur with dependency injection
862
-        // $cache is controlled by individual calls to separate Registry loader methods like load_class()
863
-        // $load_only is also controlled by individual calls to separate Registry loader methods like load_file()
864
-        if ($this->_cache_on && $cache && ! $load_only) {
865
-            // return object if it's already cached
866
-            $cached_class = $this->_get_cached_class($class_name, $class_prefix, $arguments);
867
-            if ($cached_class !== null) {
868
-                return $cached_class;
869
-            }
870
-        }
871
-        // if the class doesn't already exist.. then we need to try and find the file and load it
872
-        if (! $class_exists) {
873
-            // get full path to file
874
-            $path = $this->_resolve_path($class_name, $type, $file_paths);
875
-            // load the file
876
-            $loaded = $this->_require_file($path, $class_name, $type, $file_paths);
877
-            // if we are only loading a file but NOT instantiating an object
878
-            // then return boolean for whether class was loaded or not
879
-            if ($load_only) {
880
-                return $loaded;
881
-            }
882
-            // if an object was expected but loading failed, then return nothing
883
-            if (! $loaded) {
884
-                return null;
885
-            }
886
-        }
887
-        // instantiate the requested object
888
-        $class_obj = $this->_create_object($class_name, $arguments, $type, $from_db);
889
-        if ($this->_cache_on && $cache) {
890
-            // save it for later... kinda like gum  { : $
891
-            $this->_set_cached_class(
892
-                $class_obj,
893
-                $class_name,
894
-                $class_prefix,
895
-                $from_db,
896
-                $arguments
897
-            );
898
-        }
899
-        $this->_cache_on = true;
900
-        return $class_obj;
901
-    }
902
-
903
-
904
-    /**
905
-     * @param string $class_name
906
-     * @param string $default have to specify something, but not anything that will conflict
907
-     * @return mixed|string
908
-     */
909
-    protected function get_class_abbreviation(string $class_name, string $default = 'FANCY_BATMAN_PANTS')
910
-    {
911
-        return $this->_class_abbreviations[ $class_name ] ?? $default;
912
-    }
913
-
914
-
915
-    /**
916
-     * attempts to find a cached version of the requested class
917
-     * by looking in the following places:
918
-     *        $this->{$class_abbreviation}            ie:    $this->CART
919
-     *        $this->{$class_name}                        ie:    $this->Some_Class
920
-     *        $this->LIB->{$class_name}                ie:    $this->LIB->Some_Class
921
-     *        $this->addon->{$class_name}    ie:    $this->addon->Some_Addon_Class
922
-     *
923
-     * @param string $class_name
924
-     * @param string $class_prefix
925
-     * @param array  $arguments
926
-     * @return mixed
927
-     */
928
-    protected function _get_cached_class(
929
-        string $class_name,
930
-        string $class_prefix = '',
931
-        array $arguments = []
932
-    ) {
933
-        if ($class_name === 'EE_Registry') {
934
-            return $this;
935
-        }
936
-        $class_abbreviation = $this->get_class_abbreviation($class_name);
937
-        // check if class has already been loaded, and return it if it has been
938
-        if (isset($this->{$class_abbreviation})) {
939
-            return $this->{$class_abbreviation};
940
-        }
941
-        $class_name = str_replace('\\', '_', $class_name);
942
-        if (isset($this->{$class_name})) {
943
-            return $this->{$class_name};
944
-        }
945
-        if ($class_prefix === 'addon' && $this->addons->has($class_name)) {
946
-            return $this->addons->get($class_name);
947
-        }
948
-        $object_identifier = $this->object_identifier->getIdentifier($class_name, $arguments);
949
-        if ($this->LIB->has($object_identifier)) {
950
-            return $this->LIB->get($object_identifier);
951
-        }
952
-        foreach ($this->LIB as $key => $object) {
953
-            if (
954
-                // request does not contain new arguments and therefore no args identifier
955
-                ! $this->object_identifier->hasArguments($object_identifier)
956
-                // but previously cached class with args was found
957
-                && $this->object_identifier->fqcnMatchesObjectIdentifier($class_name, $key)
958
-            ) {
959
-                return $object;
960
-            }
961
-        }
962
-        return null;
963
-    }
964
-
965
-
966
-    /**
967
-     * removes a cached version of the requested class
968
-     *
969
-     * @param string  $class_name
970
-     * @param boolean $addon
971
-     * @param array   $arguments
972
-     * @return boolean
973
-     */
974
-    public function clear_cached_class(
975
-        string $class_name,
976
-        bool $addon = false,
977
-        array $arguments = []
978
-    ): bool {
979
-        $class_abbreviation = $this->get_class_abbreviation($class_name);
980
-        // check if class has already been loaded, and return it if it has been
981
-        if (isset($this->{$class_abbreviation})) {
982
-            $this->{$class_abbreviation} = null;
983
-            return true;
984
-        }
985
-        $class_name = str_replace('\\', '_', $class_name);
986
-        if (isset($this->{$class_name})) {
987
-            $this->{$class_name} = null;
988
-            return true;
989
-        }
990
-        if ($addon && $this->addons->has($class_name)) {
991
-            $this->addons->remove($class_name);
992
-            return true;
993
-        }
994
-        $class_name = $this->object_identifier->getIdentifier($class_name, $arguments);
995
-        if ($this->LIB->has($class_name)) {
996
-            $this->LIB->remove($class_name);
997
-            return true;
998
-        }
999
-        return false;
1000
-    }
1001
-
1002
-
1003
-    /**
1004
-     * _set_cached_class
1005
-     * attempts to cache the instantiated class locally
1006
-     * in one of the following places, in the following order:
1007
-     *        $this->{class_abbreviation}   ie:    $this->CART
1008
-     *        $this->{$class_name}          ie:    $this->Some_Class
1009
-     *        $this->addon->{$$class_name}    ie:    $this->addon->Some_Addon_Class
1010
-     *        $this->LIB->{$class_name}     ie:    $this->LIB->Some_Class
1011
-     *
1012
-     * @param object $class_obj
1013
-     * @param string $class_name
1014
-     * @param string $class_prefix
1015
-     * @param bool   $from_db
1016
-     * @param array  $arguments
1017
-     * @return void
1018
-     */
1019
-    protected function _set_cached_class(
1020
-        $class_obj,
1021
-        string $class_name,
1022
-        string $class_prefix = '',
1023
-        bool $from_db = false,
1024
-        array $arguments = []
1025
-    ) {
1026
-        if ($class_name === 'EE_Registry' || empty($class_obj)) {
1027
-            return;
1028
-        }
1029
-        // return newly instantiated class
1030
-        $class_abbreviation = $this->get_class_abbreviation($class_name, '');
1031
-        if ($class_abbreviation) {
1032
-            $this->{$class_abbreviation} = $class_obj;
1033
-            return;
1034
-        }
1035
-        $class_name = str_replace('\\', '_', $class_name);
1036
-        if (property_exists($this, $class_name)) {
1037
-            $this->{$class_name} = $class_obj;
1038
-            return;
1039
-        }
1040
-        if ($class_prefix === 'addon') {
1041
-            $this->addons->add($class_name, $class_obj);
1042
-            return;
1043
-        }
1044
-        if (! $from_db) {
1045
-            $class_name = $this->object_identifier->getIdentifier($class_name, $arguments);
1046
-            $this->LIB->add($class_name, $class_obj);
1047
-        }
1048
-    }
1049
-
1050
-
1051
-    /**
1052
-     * attempts to find a full valid filepath for the requested class.
1053
-     * loops thru each of the base paths in the $file_paths array and appends : "{classname} . {file type} . php"
1054
-     * then returns that path if the target file has been found and is readable
1055
-     *
1056
-     * @param string $class_name
1057
-     * @param string $type
1058
-     * @param array  $file_paths
1059
-     * @return string | bool
1060
-     */
1061
-    protected function _resolve_path(string $class_name, string $type = '', array $file_paths = [])
1062
-    {
1063
-        // make sure $file_paths is an array
1064
-        $file_paths = is_array($file_paths)
1065
-            ? $file_paths
1066
-            : [$file_paths];
1067
-        // cycle thru paths
1068
-        foreach ($file_paths as $key => $file_path) {
1069
-            // convert all separators to proper /, if no filepath, then use EE_CLASSES
1070
-            $file_path = $file_path
1071
-                ? str_replace(['/', '\\'], '/', $file_path)
1072
-                : EE_CLASSES;
1073
-            // prep file type
1074
-            $type = ! empty($type)
1075
-                ? trim($type, '.') . '.'
1076
-                : '';
1077
-            // build full file path
1078
-            $file_paths[ $key ] = rtrim($file_path, '/') . '/' . $class_name . '.' . $type . 'php';
1079
-            // does the file exist and can be read ?
1080
-            if (is_readable($file_paths[ $key ])) {
1081
-                return $file_paths[ $key ];
1082
-            }
1083
-        }
1084
-        return false;
1085
-    }
1086
-
1087
-
1088
-    /**
1089
-     * basically just performs a require_once()
1090
-     * but with some error handling
1091
-     *
1092
-     * @param string $path
1093
-     * @param string $class_name
1094
-     * @param string $type
1095
-     * @param array  $file_paths
1096
-     * @return bool
1097
-     * @throws EE_Error
1098
-     * @throws ReflectionException
1099
-     */
1100
-    protected function _require_file(string $path, string $class_name, string $type = '', array $file_paths = []): bool
1101
-    {
1102
-        $this->resolve_legacy_class_parent($class_name);
1103
-        // don't give up! you gotta...
1104
-        try {
1105
-            // does the file exist and can it be read ?
1106
-            if (! $path) {
1107
-                // just in case the file has already been autoloaded,
1108
-                // but discrepancies in the naming schema are preventing it from
1109
-                // being loaded via one of the EE_Registry::load_*() methods,
1110
-                // then let's try one last hail mary before throwing an exception
1111
-                // and call class_exists() again, but with autoloading turned ON
1112
-                if (class_exists($class_name)) {
1113
-                    return true;
1114
-                }
1115
-                // so sorry, can't find the file
1116
-                throw new EE_Error(
1117
-                    sprintf(
1118
-                        esc_html__(
1119
-                            'The %1$s file %2$s could not be located or is not readable due to file permissions. Please ensure that the following filepath(s) are correct: %3$s',
1120
-                            'event_espresso'
1121
-                        ),
1122
-                        trim($type, '.'),
1123
-                        $class_name,
1124
-                        '<br />' . implode(',<br />', $file_paths)
1125
-                    )
1126
-                );
1127
-            }
1128
-            // get the file
1129
-            require_once($path);
1130
-            // if the class isn't already declared somewhere
1131
-            if (class_exists($class_name, false) === false) {
1132
-                // so sorry, not a class
1133
-                throw new EE_Error(
1134
-                    sprintf(
1135
-                        esc_html__(
1136
-                            'The %s file %s does not appear to contain the %s Class.',
1137
-                            'event_espresso'
1138
-                        ),
1139
-                        $type,
1140
-                        $path,
1141
-                        $class_name
1142
-                    )
1143
-                );
1144
-            }
1145
-        } catch (EE_Error $e) {
1146
-            $e->get_error();
1147
-            return false;
1148
-        }
1149
-        return true;
1150
-    }
1151
-
1152
-
1153
-    /**
1154
-     * Some of our legacy classes that extended a parent class would simply use a require() statement
1155
-     * before their class declaration in order to ensure that the parent class was loaded.
1156
-     * This is not ideal, but it's nearly impossible to determine the parent class of a non-namespaced class,
1157
-     * without triggering a fatal error because the parent class has yet to be loaded and therefore doesn't exist.
1158
-     *
1159
-     * @param string $class_name
1160
-     */
1161
-    protected function resolve_legacy_class_parent(string $class_name = '')
1162
-    {
1163
-        try {
1164
-            $legacy_parent_class_map = [
1165
-                'EE_Payment_Processor' => 'core/business/EE_Processor_Base.class.php',
1166
-            ];
1167
-            if (isset($legacy_parent_class_map[ $class_name ])) {
1168
-                require_once EE_PLUGIN_DIR_PATH . $legacy_parent_class_map[ $class_name ];
1169
-            }
1170
-        } catch (Exception $exception) {
1171
-        }
1172
-    }
1173
-
1174
-
1175
-    /**
1176
-     * _create_object
1177
-     * Attempts to instantiate the requested class via any of the
1178
-     * commonly used instantiation methods employed throughout EE.
1179
-     * The priority for instantiation is as follows:
1180
-     *        - abstract classes or any class flagged as "load only" (no instantiation occurs)
1181
-     *        - model objects via their 'new_instance_from_db' method
1182
-     *        - model objects via their 'new_instance' method
1183
-     *        - "singleton" classes" via their 'instance' method
1184
-     *    - standard instantiable classes via their __constructor
1185
-     * Prior to instantiation, if the classname exists in the dependency_map,
1186
-     * then the constructor for the requested class will be examined to determine
1187
-     * if any dependencies exist, and if they can be injected.
1188
-     * If so, then those classes will be added to the array of arguments passed to the constructor
1189
-     *
1190
-     * @param string $class_name
1191
-     * @param array  $arguments
1192
-     * @param string $type
1193
-     * @param bool   $from_db
1194
-     * @return null|object|bool
1195
-     * @throws InvalidArgumentException
1196
-     * @throws InvalidInterfaceException
1197
-     * @throws EE_Error
1198
-     * @throws ReflectionException
1199
-     * @throws InvalidDataTypeException
1200
-     */
1201
-    protected function _create_object(
1202
-        string $class_name,
1203
-        array $arguments = [],
1204
-        string $type = '',
1205
-        bool $from_db = false
1206
-    ) {
1207
-        // create reflection
1208
-        $reflector = $this->mirror->getReflectionClass($class_name);
1209
-        // make sure arguments are an array
1210
-        $arguments = is_array($arguments)
1211
-            ? $arguments
1212
-            : [$arguments];
1213
-        // and if arguments array is numerically and sequentially indexed, then we want it to remain as is,
1214
-        // else wrap it in an additional array so that it doesn't get split into multiple parameters
1215
-        $arguments = $this->_array_is_numerically_and_sequentially_indexed($arguments)
1216
-            ? $arguments
1217
-            : [$arguments];
1218
-        // attempt to inject dependencies ?
1219
-        if ($this->_dependency_map->has($class_name)) {
1220
-            $arguments = $this->_resolve_dependencies($reflector, $class_name, $arguments);
1221
-        }
1222
-        // instantiate the class if possible
1223
-        if ($reflector->isAbstract()) {
1224
-            // nothing to instantiate, loading file was enough
1225
-            // does not throw an exception so $instantiation_mode is unused
1226
-            // $instantiation_mode = "1) no constructor abstract class";
1227
-            return true;
1228
-        }
1229
-        if (
1230
-            empty($arguments)
1231
-            && $this->mirror->getConstructorFromReflection($reflector) === null
1232
-            && $reflector->isInstantiable()
1233
-        ) {
1234
-            // no constructor = static methods only... nothing to instantiate, loading file was enough
1235
-            // $instantiation_mode = "2) no constructor but instantiable";
1236
-            return $reflector->newInstance();
1237
-        }
1238
-        if ($from_db && method_exists($class_name, 'new_instance_from_db')) {
1239
-            // $instantiation_mode = "3) new_instance_from_db()";
1240
-            return call_user_func_array([$class_name, 'new_instance_from_db'], $arguments);
1241
-        }
1242
-        if (method_exists($class_name, 'new_instance')) {
1243
-            // $instantiation_mode = "4) new_instance()";
1244
-            return call_user_func_array([$class_name, 'new_instance'], $arguments);
1245
-        }
1246
-        if (method_exists($class_name, 'instance')) {
1247
-            // $instantiation_mode = "5) instance()";
1248
-            return call_user_func_array([$class_name, 'instance'], $arguments);
1249
-        }
1250
-        if ($reflector->isInstantiable()) {
1251
-            $args_passed_count = count($arguments);
1252
-            $args_required_count = count($this->mirror->getRequiredParameters($class_name));
1253
-            if ($args_passed_count < $args_required_count) {
1254
-                throw new RuntimeException(
1255
-                    sprintf(
1256
-                        __(
1257
-                            'Invalid arguments supplied for the %1$s class, %2$s were required but %3$s were passed.',
1258
-                            'event_espresso'
1259
-                        ),
1260
-                        $class_name,
1261
-                        $args_required_count,
1262
-                        $args_passed_count
1263
-                    )
1264
-                );
1265
-            }
1266
-            // $instantiation_mode = "6) constructor";
1267
-            return $reflector->newInstanceArgs($arguments);
1268
-        }
1269
-        // heh ? something's not right !
1270
-        throw new EE_Error(
1271
-            sprintf(
1272
-                esc_html__('The %s file %s could not be instantiated.', 'event_espresso'),
1273
-                $type,
1274
-                $class_name
1275
-            )
1276
-        );
1277
-    }
1278
-
1279
-
1280
-    /**
1281
-     * @see http://stackoverflow.com/questions/173400/how-to-check-if-php-array-is-associative-or-sequential
1282
-     * @param array $array
1283
-     * @return bool
1284
-     */
1285
-    protected function _array_is_numerically_and_sequentially_indexed(array $array): bool
1286
-    {
1287
-        return empty($array) || array_keys($array) === range(0, count($array) - 1);
1288
-    }
1289
-
1290
-
1291
-    /**
1292
-     * _resolve_dependencies
1293
-     * examines the constructor for the requested class to determine
1294
-     * if any dependencies exist, and if they can be injected.
1295
-     * If so, then those classes will be added to the array of arguments passed to the constructor
1296
-     * PLZ NOTE: this is achieved by type hinting the constructor params
1297
-     * For example:
1298
-     *        if attempting to load a class "Foo" with the following constructor:
1299
-     *        __construct( Bar $bar_class, Fighter $grohl_class )
1300
-     *        then $bar_class and $grohl_class will be added to the $arguments array,
1301
-     *        but only IF they are NOT already present in the incoming arguments array,
1302
-     *        and the correct classes can be loaded
1303
-     *
1304
-     * @param ReflectionClass $reflector
1305
-     * @param string          $class_name
1306
-     * @param array           $arguments
1307
-     * @return array
1308
-     * @throws InvalidArgumentException
1309
-     * @throws InvalidDataTypeException
1310
-     * @throws InvalidInterfaceException
1311
-     * @throws ReflectionException
1312
-     */
1313
-    protected function _resolve_dependencies(
1314
-        ReflectionClass $reflector,
1315
-        string $class_name,
1316
-        array $arguments = []
1317
-    ): array {
1318
-        // let's examine the constructor
1319
-        $constructor = $this->mirror->getConstructorFromReflection($reflector);
1320
-        // whu? huh? nothing?
1321
-        if (! $constructor) {
1322
-            return $arguments;
1323
-        }
1324
-        // get constructor parameters
1325
-        $params = $this->mirror->getParametersFromReflection($reflector);
1326
-        // and the keys for the incoming arguments array so that we can compare existing arguments with what is expected
1327
-        $argument_keys = array_keys($arguments);
1328
-        // now loop thru all the constructors' expected parameters
1329
-        foreach ($params as $index => $param) {
1330
-            try {
1331
-                // is this a dependency for a specific class ?
1332
-                $param_class = $this->mirror->getParameterClassName($param, $class_name, $index);
1333
-            } catch (ReflectionException $exception) {
1334
-                // uh-oh... most likely a legacy class that has not been autoloaded
1335
-                // let's try to derive the classname from what we have now
1336
-                // and hope that the property var name is close to the class name
1337
-                $param_class = $param->getName();
1338
-                $param_class = str_replace('_', ' ', $param_class);
1339
-                $param_class = ucwords($param_class);
1340
-                $param_class = str_replace(' ', '_', $param_class);
1341
-            }
1342
-            // BUT WAIT !!! This class may be an alias for something else (or getting replaced at runtime)
1343
-            $param_class = $this->class_cache->isAlias($param_class, $class_name)
1344
-                ? $this->class_cache->getFqnForAlias($param_class, $class_name)
1345
-                : $param_class;
1346
-            if (
1347
-                // param is not even a class
1348
-                ($param_class === null || $this->parameterIsPrimitive($param_class))
1349
-                // and something already exists in the incoming arguments for this param
1350
-                && array_key_exists($index, $argument_keys)
1351
-                && isset($arguments[ $argument_keys[ $index ] ])
1352
-            ) {
1353
-                // so let's skip this argument and move on to the next
1354
-                continue;
1355
-            }
1356
-            // parameter is type hinted as a class
1357
-            if ($param_class !== null) {
1358
-                // parameter exists as an incoming argument, AND it's the correct class
1359
-                if (
1360
-                    array_key_exists($index, $argument_keys)
1361
-                    && isset($arguments[ $argument_keys[ $index ] ])
1362
-                    && $arguments[ $argument_keys[ $index ] ] instanceof $param_class
1363
-                ) {
1364
-                    // skip this argument and move on to the next
1365
-                    continue;
1366
-                }
1367
-                // parameter should be injected
1368
-                if ($this->_dependency_map->has_dependency_for_class($class_name, $param_class)) {
1369
-                    $arguments = $this->_resolve_dependency(
1370
-                        $class_name,
1371
-                        $param_class,
1372
-                        $arguments,
1373
-                        $index
1374
-                    );
1375
-                }
1376
-            }
1377
-            if (empty($arguments[ $index ])) {
1378
-                $default_value = $this->mirror->getParameterDefaultValue(
1379
-                    $param,
1380
-                    $class_name,
1381
-                    $index
1382
-                );
1383
-                // if there's no default value, and the incoming argument is an array (albeit empty), then use that
1384
-                $arguments[ $index ] = $default_value === null
1385
-                                       && isset($arguments[ $index ])
1386
-                                       && is_array($arguments[ $index ])
1387
-                    ? $arguments[ $index ]
1388
-                    : $default_value;
1389
-            }
1390
-        }
1391
-        return $arguments;
1392
-    }
1393
-
1394
-
1395
-    /**
1396
-     * @param string $class_name
1397
-     * @param string $param_class
1398
-     * @param array  $arguments
1399
-     * @param mixed  $index
1400
-     * @return array
1401
-     * @throws InvalidArgumentException
1402
-     * @throws InvalidInterfaceException
1403
-     * @throws InvalidDataTypeException
1404
-     */
1405
-    protected function _resolve_dependency(string $class_name, string $param_class, array $arguments, $index): array
1406
-    {
1407
-        $dependency = null;
1408
-        // should dependency be loaded from cache ?
1409
-        $cache_on = $this->_dependency_map->loading_strategy_for_class_dependency(
1410
-            $class_name,
1411
-            $param_class
1412
-        );
1413
-        $cache_on = $cache_on !== EE_Dependency_Map::load_new_object;
1414
-        // we might have a dependency...
1415
-        // let's MAYBE try and find it in our cache if that's what's been requested
1416
-        $cached_class = $cache_on
1417
-            ? $this->_get_cached_class($param_class)
1418
-            : null;
1419
-        // and grab it if it exists
1420
-        if ($cached_class instanceof $param_class) {
1421
-            $dependency = $cached_class;
1422
-        } elseif ($param_class !== $class_name) {
1423
-            // obtain the loader method from the dependency map
1424
-            $loader = $this->_dependency_map->class_loader($param_class);
1425
-            // is loader a custom closure ?
1426
-            if ($loader instanceof Closure) {
1427
-                $dependency = $loader($arguments);
1428
-            } else {
1429
-                // set the cache on property for the recursive loading call
1430
-                $this->_cache_on = $cache_on;
1431
-                // if not, then let's try and load it via the registry
1432
-                if ($loader && method_exists($this, $loader)) {
1433
-                    $dependency = $this->{$loader}($param_class);
1434
-                } else {
1435
-                    $dependency = LoaderFactory::getLoader()->load(
1436
-                        $param_class,
1437
-                        [],
1438
-                        $cache_on
1439
-                    );
1440
-                }
1441
-            }
1442
-        }
1443
-        // did we successfully find the correct dependency ?
1444
-        if ($dependency instanceof $param_class) {
1445
-            // then let's inject it into the incoming array of arguments at the correct location
1446
-            $arguments[ $index ] = $dependency;
1447
-        }
1448
-        return $arguments;
1449
-    }
1450
-
1451
-
1452
-    /**
1453
-     * call any loader that's been registered in the EE_Dependency_Map::$_class_loaders array
1454
-     *
1455
-     * @param string $classname PLEASE NOTE: the class name needs to match what's registered
1456
-     *                          in the EE_Dependency_Map::$_class_loaders array,
1457
-     *                          including the class prefix, ie: "EE_", "EEM_", "EEH_", etc
1458
-     * @param array  $arguments
1459
-     * @return object
1460
-     */
1461
-    public static function factory(string $classname, array $arguments = [])
1462
-    {
1463
-        $loader = self::instance()->_dependency_map->class_loader($classname);
1464
-        if ($loader instanceof Closure) {
1465
-            return $loader($arguments);
1466
-        }
1467
-        if (method_exists(self::instance(), $loader)) {
1468
-            return self::instance()->{$loader}($classname, $arguments);
1469
-        }
1470
-        return null;
1471
-    }
1472
-
1473
-
1474
-    /**
1475
-     * Gets the addon by its class name
1476
-     *
1477
-     * @param string $class_name
1478
-     * @return EE_Addon
1479
-     */
1480
-    public function getAddon(string $class_name): ?EE_Addon
1481
-    {
1482
-        $class_name = str_replace('\\', '_', $class_name);
1483
-        return $this->addons->{$class_name} ?? null;
1484
-    }
1485
-
1486
-
1487
-    /**
1488
-     * removes the addon from the internal cache
1489
-     *
1490
-     * @param string $class_name
1491
-     * @return void
1492
-     */
1493
-    public function removeAddon(string $class_name)
1494
-    {
1495
-        $class_name = str_replace('\\', '_', $class_name);
1496
-        $this->addons->remove($class_name);
1497
-    }
1498
-
1499
-
1500
-    /**
1501
-     * Gets the addon by its name/slug (not classname. For that, just
1502
-     * use the get_addon() method above
1503
-     *
1504
-     * @param string $name
1505
-     * @return EE_Addon
1506
-     */
1507
-    public function get_addon_by_name(string $name): ?EE_Addon
1508
-    {
1509
-        foreach ($this->addons as $addon) {
1510
-            if ($addon->name() === $name) {
1511
-                return $addon;
1512
-            }
1513
-        }
1514
-        return null;
1515
-    }
1516
-
1517
-
1518
-    /**
1519
-     * Gets an array of all the registered addons, where the keys are their names.
1520
-     * (ie, what each returns for their name() function)
1521
-     * They're already available on EE_Registry::instance()->addons as properties,
1522
-     * where each property's name is the addon's classname,
1523
-     * So if you just want to get the addon by classname,
1524
-     * OR use the get_addon() method above.
1525
-     * PLEASE  NOTE:
1526
-     * addons with Fully Qualified Class Names
1527
-     * have had the namespace separators converted to underscores,
1528
-     * so a classname like Fully\Qualified\ClassName
1529
-     * would have been converted to Fully_Qualified_ClassName
1530
-     *
1531
-     * @return EE_Addon[] where the KEYS are the addon's name()
1532
-     */
1533
-    public function get_addons_by_name(): array
1534
-    {
1535
-        $addons = [];
1536
-        foreach ($this->addons as $addon) {
1537
-            $addons[ $addon->name() ] = $addon;
1538
-        }
1539
-        return $addons;
1540
-    }
1541
-
1542
-
1543
-    /**
1544
-     * Resets the specified model's instance AND makes sure EE_Registry doesn't keep
1545
-     * a stale copy of it around
1546
-     *
1547
-     * @param string $model_name
1548
-     * @return EEM_Base
1549
-     * @throws EE_Error
1550
-     * @throws ReflectionException
1551
-     */
1552
-    public function reset_model(string $model_name): ?EEM_Base
1553
-    {
1554
-        $model_class_name = strpos($model_name, 'EEM_') !== 0
1555
-            ? "EEM_$model_name"
1556
-            : $model_name;
1557
-        if (! $this->LIB->has($model_class_name)) {
1558
-            return null;
1559
-        }
1560
-        $model = $this->LIB->get($model_class_name);
1561
-        if (! $model instanceof EEM_Base) {
1562
-            return null;
1563
-        }
1564
-        // get that model reset it and make sure we nuke the old reference to it
1565
-        if ($model instanceof $model_class_name && is_callable([$model_class_name, 'reset'])) {
1566
-            $this->LIB->remove($model_class_name);
1567
-            $this->LIB->add($model_class_name, $model->reset());
1568
-        } else {
1569
-            throw new EE_Error(
1570
-                sprintf(
1571
-                    esc_html__('Model %s does not have a method "reset"', 'event_espresso'),
1572
-                    $model_name
1573
-                )
1574
-            );
1575
-        }
1576
-        return $model;
1577
-    }
1578
-
1579
-
1580
-    /**
1581
-     * Resets the registry.
1582
-     * The criteria for what gets reset is based on what can be shared between sites on the same request when
1583
-     * switch_to_blog is used in a multisite install.  Here is a list of things that are NOT reset.
1584
-     * - $_dependency_map
1585
-     * - $_class_abbreviations
1586
-     * - $NET_CFG (EE_Network_Config): The config is shared network wide so no need to reset.
1587
-     * - $REQ:  Still on the same request so no need to change.
1588
-     * - $CAP: There is no site specific state in the EE_Capability class.
1589
-     * - $SSN: Although ideally, the session should not be shared between site switches, we can't reset it because only
1590
-     * one Session can be active in a single request.  Resetting could resolve in "headers already sent" errors.
1591
-     * - $addons:  In multisite, the state of the addons is something controlled via hooks etc in a normal request.  So
1592
-     *             for now, we won't reset the addons because it could break calls to an add-ons class/methods in the
1593
-     *             switch or on the restore.
1594
-     * - $modules
1595
-     * - $shortcodes
1596
-     * - $widgets
1597
-     *
1598
-     * @param boolean $hard             [deprecated]
1599
-     * @param boolean $reinstantiate    whether to create new instances of EE_Registry's singletons too,
1600
-     *                                  or just reset without re-instantiating (handy to set to FALSE if you're not
1601
-     *                                  sure if you CAN currently reinstantiate the singletons at the moment)
1602
-     * @param bool    $reset_models     Defaults to true.  When false, then the models are not reset.  This is so
1603
-     *                                  client
1604
-     *                                  code instead can just change the model context to a different blog id if
1605
-     *                                  necessary
1606
-     * @return EE_Registry
1607
-     * @throws EE_Error
1608
-     * @throws ReflectionException
1609
-     */
1610
-    public static function reset(bool $hard = false, bool $reinstantiate = true, bool $reset_models = true): EE_Registry
1611
-    {
1612
-        $instance            = self::instance();
1613
-        $instance->_cache_on = true;
1614
-        // reset some "special" classes
1615
-        EEH_Activation::reset();
1616
-        $hard = apply_filters('FHEE__EE_Registry__reset__hard', $hard);
1617
-        $instance->CFG = EE_Config::reset($hard, $reinstantiate);
1618
-        $instance->CART = null;
1619
-        $instance->MRM = null;
1620
-        // messages reset
1621
-        EED_Messages::reset();
1622
-        // handle of objects cached on LIB
1623
-        foreach (['LIB', 'modules'] as $cache) {
1624
-            foreach ($instance->{$cache} as $class_name => $class) {
1625
-                if (self::_reset_and_unset_object($class, $reset_models)) {
1626
-                    unset($instance->{$cache}->{$class_name});
1627
-                }
1628
-            }
1629
-        }
1630
-        return $instance;
1631
-    }
1632
-
1633
-
1634
-    /**
1635
-     * if passed object implements ResettableInterface, then call it's reset() method
1636
-     * if passed object implements InterminableInterface, then return false,
1637
-     * to indicate that it should NOT be cleared from the Registry cache
1638
-     *
1639
-     * @param      $object
1640
-     * @param bool $reset_models
1641
-     * @return bool returns true if cached object should be unset
1642
-     * @throws EE_Error
1643
-     * @throws ReflectionException
1644
-     */
1645
-    private static function _reset_and_unset_object($object, bool $reset_models): bool
1646
-    {
1647
-        if (! is_object($object)) {
1648
-            // don't unset anything that's not an object
1649
-            return false;
1650
-        }
1651
-        if ($object instanceof EED_Module) {
1652
-            $object::reset();
1653
-            // don't unset modules
1654
-            return false;
1655
-        }
1656
-        if ($object instanceof ResettableInterface) {
1657
-            if ($object instanceof EEM_Base) {
1658
-                if ($reset_models) {
1659
-                    $object->reset();
1660
-                    return true;
1661
-                }
1662
-                return false;
1663
-            }
1664
-            $object->reset();
1665
-            return true;
1666
-        }
1667
-        if (! $object instanceof InterminableInterface) {
1668
-            return true;
1669
-        }
1670
-        return false;
1671
-    }
1672
-
1673
-
1674
-    /**
1675
-     * Gets all the custom post type models defined
1676
-     *
1677
-     * @return array keys are model "short names" (Eg "Event") and keys are classnames (eg "EEM_Event")
1678
-     */
1679
-    public function cpt_models(): array
1680
-    {
1681
-        $cpt_models = [];
1682
-        foreach ($this->non_abstract_db_models as $short_name => $classname) {
1683
-            if (is_subclass_of($classname, 'EEM_CPT_Base')) {
1684
-                $cpt_models[ $short_name ] = $classname;
1685
-            }
1686
-        }
1687
-        return $cpt_models;
1688
-    }
1689
-
1690
-
1691
-    /**
1692
-     * @return EE_Config
1693
-     */
1694
-    public static function CFG(): EE_Config
1695
-    {
1696
-        return self::instance()->CFG;
1697
-    }
1698
-
1699
-
1700
-    /**
1701
-     * @param string $class_name
1702
-     * @return ReflectionClass
1703
-     * @throws ReflectionException
1704
-     * @throws InvalidDataTypeException
1705
-     * @deprecated 4.9.62.p
1706
-     */
1707
-    public function get_ReflectionClass(string $class_name): ReflectionClass
1708
-    {
1709
-        return $this->mirror->getReflectionClass($class_name);
1710
-    }
1711
-
1712
-    private function parameterIsPrimitive(?string $param_class): bool
1713
-    {
1714
-        return in_array(
1715
-            $param_class,
1716
-            [
1717
-                'array',
1718
-                'bool',
1719
-                'float',
1720
-                'int',
1721
-                'string',
1722
-            ]
1723
-        );
1724
-    }
25
+	private static ?EE_Registry $_instance = null;
26
+
27
+	protected EE_Dependency_Map $_dependency_map;
28
+
29
+	private Mirror $mirror;
30
+
31
+	private ClassInterfaceCache $class_cache;
32
+
33
+	private ObjectIdentifier $object_identifier;
34
+
35
+	protected array $_class_abbreviations = [];
36
+
37
+	public ?CommandBusInterface $BUS = null;
38
+
39
+	public ?EE_Cart $CART = null;
40
+
41
+	public ?EE_Config $CFG = null;
42
+
43
+	public ?EE_Network_Config $NET_CFG = null;
44
+
45
+	/**
46
+	 * RegistryContainer for storing library classes in
47
+	 *
48
+	 * @var RegistryContainer|null $LIB
49
+	 */
50
+	public ?RegistryContainer $LIB = null;
51
+
52
+	/**
53
+	 * @var EE_Request_Handler|null $REQ
54
+	 * @deprecated 4.10.14.p
55
+	 */
56
+	public ?EE_Request_Handler $REQ = null;
57
+
58
+	public ?EE_Session $SSN = null;
59
+
60
+	/**
61
+	 * @since 4.5.0
62
+	 * @var EE_Capabilities|null $CAP
63
+	 */
64
+	public ?EE_Capabilities $CAP = null;
65
+
66
+	/**
67
+	 * @since 4.9.0
68
+	 * @var EE_Message_Resource_Manager|null $MRM
69
+	 */
70
+	public ?EE_Message_Resource_Manager $MRM = null;
71
+
72
+	public ?Registry $AssetsRegistry = null;
73
+
74
+	/**
75
+	 * RegistryContainer for holding addons which have registered themselves to work with EE core
76
+	 *
77
+	 * @var RegistryContainer|EE_Addon[] $addons
78
+	 */
79
+	public $addons = [];
80
+
81
+	/**
82
+	 * keys are 'short names' (eg Event), values are class names (eg 'EEM_Event')
83
+	 *
84
+	 * @var EEM_Base[] $models
85
+	 */
86
+	public array $models = [];
87
+
88
+	/**
89
+	 * @var RegistryContainer|EED_Module[] $modules
90
+	 */
91
+	public $modules = [];
92
+
93
+	/**
94
+	 * @var RegistryContainer|EES_Shortcode[] $shortcodes
95
+	 */
96
+	public $shortcodes = [];
97
+
98
+	/**
99
+	 * @var RegistryContainer|WP_Widget[] $widgets
100
+	 */
101
+	public $widgets = [];
102
+
103
+	/**
104
+	 * this is an array of all implemented model names (i.e. not the parent abstract models, or models
105
+	 * which don't actually fetch items from the DB in the normal way (ie, are not children of EEM_Base)).
106
+	 * Keys are model "short names" (eg "Event") as used in model relations, and values are
107
+	 * classnames (eg "EEM_Event")
108
+	 *
109
+	 * @var array $non_abstract_db_models
110
+	 */
111
+	public array $non_abstract_db_models = [];
112
+
113
+	/**
114
+	 * internationalization for JS strings
115
+	 *    usage:   EE_Registry::i18n_js_strings['string_key'] = esc_html__( 'string to translate.', 'event_espresso' );
116
+	 *    in js file:  var translatedString = eei18n.string_key;
117
+	 *
118
+	 * @var array $i18n_js_strings
119
+	 */
120
+	public static array $i18n_js_strings = [];
121
+
122
+	/**
123
+	 * $main_file - path to espresso.php
124
+	 *
125
+	 * @var string $main_file
126
+	 */
127
+	public string $main_file = '';
128
+
129
+	/**
130
+	 * boolean flag to indicate whether to load/save dependencies from/to the cache
131
+	 *
132
+	 * @var bool $_cache_on
133
+	 */
134
+	protected bool $_cache_on = true;
135
+
136
+
137
+	/**
138
+	 * @singleton method used to instantiate class object
139
+	 * @param EE_Dependency_Map|null   $dependency_map
140
+	 * @param Mirror|null              $mirror
141
+	 * @param ClassInterfaceCache|null $class_cache
142
+	 * @param ObjectIdentifier|null    $object_identifier
143
+	 * @return EE_Registry instance
144
+	 */
145
+	public static function instance(
146
+		EE_Dependency_Map $dependency_map = null,
147
+		Mirror $mirror = null,
148
+		ClassInterfaceCache $class_cache = null,
149
+		ObjectIdentifier $object_identifier = null
150
+	): EE_Registry {
151
+		// check if class object is instantiated
152
+		if (
153
+			! self::$_instance instanceof EE_Registry
154
+			&& $dependency_map instanceof EE_Dependency_Map
155
+			&& $mirror instanceof Mirror
156
+			&& $class_cache instanceof ClassInterfaceCache
157
+			&& $object_identifier instanceof ObjectIdentifier
158
+		) {
159
+			self::$_instance = new self(
160
+				$dependency_map,
161
+				$mirror,
162
+				$class_cache,
163
+				$object_identifier
164
+			);
165
+		}
166
+		return self::$_instance;
167
+	}
168
+
169
+
170
+	/**
171
+	 * protected constructor to prevent direct creation
172
+	 *
173
+	 * @Constructor
174
+	 * @param EE_Dependency_Map   $dependency_map
175
+	 * @param Mirror              $mirror
176
+	 * @param ClassInterfaceCache $class_cache
177
+	 * @param ObjectIdentifier    $object_identifier
178
+	 */
179
+	protected function __construct(
180
+		EE_Dependency_Map $dependency_map,
181
+		Mirror $mirror,
182
+		ClassInterfaceCache $class_cache,
183
+		ObjectIdentifier $object_identifier
184
+	) {
185
+		$this->_dependency_map   = $dependency_map;
186
+		$this->mirror            = $mirror;
187
+		$this->class_cache       = $class_cache;
188
+		$this->object_identifier = $object_identifier;
189
+
190
+		$this->LIB        = new RegistryContainer();
191
+		$this->addons     = new RegistryContainer();
192
+		$this->modules    = new RegistryContainer();
193
+		$this->shortcodes = new RegistryContainer();
194
+		$this->widgets    = new RegistryContainer();
195
+		add_action('EE_Load_Espresso_Core__handle_request__initialize_core_loading', [$this, 'initialize']);
196
+	}
197
+
198
+
199
+	/**
200
+	 * initialize
201
+	 *
202
+	 * @throws OutOfBoundsException
203
+	 * @throws InvalidArgumentException
204
+	 * @throws InvalidInterfaceException
205
+	 * @throws InvalidDataTypeException
206
+	 * @throws EE_Error
207
+	 * @throws ReflectionException
208
+	 */
209
+	public function initialize()
210
+	{
211
+		$this->_class_abbreviations = apply_filters(
212
+			'FHEE__EE_Registry____construct___class_abbreviations',
213
+			[
214
+				'EE_Config'                                       => 'CFG',
215
+				'EE_Session'                                      => 'SSN',
216
+				'EE_Capabilities'                                 => 'CAP',
217
+				'EE_Cart'                                         => 'CART',
218
+				'EE_Network_Config'                               => 'NET_CFG',
219
+				'EE_Request_Handler'                              => 'REQ',
220
+				'EE_Message_Resource_Manager'                     => 'MRM',
221
+				'EventEspresso\core\services\commands\CommandBus' => 'BUS',
222
+				'EventEspresso\core\services\assets\Registry'     => 'AssetsRegistry',
223
+			]
224
+		);
225
+		$this->load_core('Base', [], true);
226
+		// add our request and response objects to the cache
227
+		$request_loader = $this->_dependency_map->class_loader(
228
+			'EventEspresso\core\services\request\Request'
229
+		);
230
+		$this->_set_cached_class(
231
+			$request_loader(),
232
+			'EventEspresso\core\services\request\Request'
233
+		);
234
+		$response_loader = $this->_dependency_map->class_loader(
235
+			'EventEspresso\core\services\request\Response'
236
+		);
237
+		$this->_set_cached_class(
238
+			$response_loader(),
239
+			'EventEspresso\core\services\request\Response'
240
+		);
241
+		add_action('AHEE__EE_System__set_hooks_for_core', [$this, 'init']);
242
+	}
243
+
244
+
245
+	/**
246
+	 * @return void
247
+	 */
248
+	public function init()
249
+	{
250
+		// Get current page protocol
251
+		$protocol = is_ssl() ? 'https://' : 'http://';
252
+		// Output admin-ajax.php URL with same protocol as current page
253
+		self::$i18n_js_strings['ajax_url'] = admin_url('admin-ajax.php', $protocol);
254
+		self::$i18n_js_strings['wp_debug'] = defined('WP_DEBUG') && WP_DEBUG;
255
+	}
256
+
257
+
258
+	/**
259
+	 * @return array
260
+	 */
261
+	public static function sanitize_i18n_js_strings(): array
262
+	{
263
+		array_walk(EE_Registry::$i18n_js_strings, function (&$value) {
264
+			if (is_scalar($value)) {
265
+				$value = html_entity_decode((string) $value, ENT_QUOTES, 'UTF-8');
266
+				// replace all escaped instances of \n with actual new lines
267
+				$value = str_replace("\\n", "\n", $value);
268
+			}
269
+		});
270
+		// sort the array by key in a case-insensitive manner
271
+		ksort(EE_Registry::$i18n_js_strings, SORT_NATURAL | SORT_FLAG_CASE);
272
+		return EE_Registry::$i18n_js_strings;
273
+	}
274
+
275
+
276
+	/**
277
+	 * localize_i18n_js_strings
278
+	 *
279
+	 * @return string
280
+	 */
281
+	public static function localize_i18n_js_strings(): string
282
+	{
283
+		$i18n_js_strings = wp_json_encode(EE_Registry::sanitize_i18n_js_strings());
284
+		return "/* <![CDATA[ */ var eei18n = $i18n_js_strings; /* ]]> */";
285
+	}
286
+
287
+
288
+	/**
289
+	 * @param mixed string | EED_Module $module
290
+	 * @throws OutOfBoundsException
291
+	 * @throws InvalidArgumentException
292
+	 * @throws InvalidInterfaceException
293
+	 * @throws InvalidDataTypeException
294
+	 * @throws EE_Error
295
+	 * @throws ReflectionException
296
+	 */
297
+	public function add_module($module)
298
+	{
299
+		if ($module instanceof EED_Module) {
300
+			$module_class = get_class($module);
301
+			$this->modules->add($module_class, $module);
302
+		} else {
303
+			if (! class_exists('EE_Module_Request_Router', false)) {
304
+				$this->load_core('Module_Request_Router');
305
+			}
306
+			EE_Module_Request_Router::module_factory($module);
307
+		}
308
+	}
309
+
310
+
311
+	/**
312
+	 * @param string $module_name
313
+	 * @return mixed EED_Module | NULL
314
+	 */
315
+	public function get_module(string $module_name = '')
316
+	{
317
+		return $this->modules->get($module_name);
318
+	}
319
+
320
+
321
+	/**
322
+	 * loads core classes - must be singletons
323
+	 *
324
+	 * @param string $class_name - simple class name ie: session
325
+	 * @param mixed  $arguments
326
+	 * @param bool   $load_only
327
+	 * @return bool|null|object
328
+	 * @throws InvalidInterfaceException
329
+	 * @throws InvalidDataTypeException
330
+	 * @throws EE_Error
331
+	 * @throws ReflectionException
332
+	 * @throws InvalidArgumentException
333
+	 */
334
+	public function load_core(string $class_name, $arguments = [], bool $load_only = false)
335
+	{
336
+		$core_paths = (array) apply_filters(
337
+			'FHEE__EE_Registry__load_core__core_paths',
338
+			[
339
+				EE_CORE,
340
+				EE_ADMIN,
341
+				EE_CPTS,
342
+				EE_CORE . 'CPTs/',
343
+				EE_CORE . 'data_migration_scripts/',
344
+				EE_CORE . 'request_stack/',
345
+				EE_CORE . 'middleware/',
346
+			]
347
+		);
348
+		// retrieve instantiated class
349
+		return $this->_load(
350
+			$core_paths,
351
+			'EE_',
352
+			$class_name,
353
+			'core',
354
+			$arguments,
355
+			false,
356
+			true,
357
+			$load_only
358
+		);
359
+	}
360
+
361
+
362
+	/**
363
+	 * loads service classes
364
+	 *
365
+	 * @param string $class_name - simple class name ie: session
366
+	 * @param mixed  $arguments
367
+	 * @param bool   $load_only
368
+	 * @return bool|null|object
369
+	 * @throws InvalidInterfaceException
370
+	 * @throws InvalidDataTypeException
371
+	 * @throws EE_Error
372
+	 * @throws ReflectionException
373
+	 * @throws InvalidArgumentException
374
+	 * @deprecated  4.10.33.p
375
+	 */
376
+	public function load_service(string $class_name, $arguments = [], bool $load_only = false)
377
+	{
378
+		$service_paths = (array) apply_filters(
379
+			'FHEE__EE_Registry__load_service__service_paths',
380
+			[
381
+				EE_CORE . 'services/',
382
+			]
383
+		);
384
+		// retrieve instantiated class
385
+		return $this->_load(
386
+			$service_paths,
387
+			'EE_',
388
+			$class_name,
389
+			'class',
390
+			$arguments,
391
+			false,
392
+			true,
393
+			$load_only
394
+		);
395
+	}
396
+
397
+
398
+	/**
399
+	 * loads data_migration_scripts
400
+	 *
401
+	 * @param string $class_name - class name for the DMS ie: EE_DMS_Core_4_2_0
402
+	 * @param mixed  $arguments
403
+	 * @return bool|null|object
404
+	 * @throws InvalidInterfaceException
405
+	 * @throws InvalidDataTypeException
406
+	 * @throws EE_Error
407
+	 * @throws ReflectionException
408
+	 * @throws InvalidArgumentException
409
+	 */
410
+	public function load_dms(string $class_name, $arguments = [])
411
+	{
412
+		// retrieve instantiated class
413
+		return $this->_load(
414
+			EE_Data_Migration_Manager::instance()->get_data_migration_script_folders(),
415
+			'EE_DMS_',
416
+			$class_name,
417
+			'dms',
418
+			$arguments,
419
+			false,
420
+			false
421
+		);
422
+	}
423
+
424
+
425
+	/**
426
+	 * loads object creating classes - must be singletons
427
+	 *
428
+	 * @param string $class_name - simple class name ie: attendee
429
+	 * @param mixed  $arguments  - an array of arguments to pass to the class
430
+	 * @param bool   $from_db    - some classes are instantiated from the db and thus call a different method to
431
+	 *                           instantiate
432
+	 * @param bool   $cache      if you don't want the class to be stored in the internal cache (non-persistent) then
433
+	 *                           set this to FALSE (ie. when instantiating model objects from client in a loop)
434
+	 * @param bool   $load_only  whether or not to just load the file and NOT instantiate, or load AND instantiate
435
+	 *                           (default)
436
+	 * @return EE_Base_Class|object|bool|null
437
+	 * @throws InvalidInterfaceException
438
+	 * @throws InvalidDataTypeException
439
+	 * @throws EE_Error
440
+	 * @throws ReflectionException
441
+	 * @throws InvalidArgumentException
442
+	 */
443
+	public function load_class(
444
+		string $class_name,
445
+		$arguments = [],
446
+		bool $from_db = false,
447
+		bool $cache = true,
448
+		bool $load_only = false
449
+	) {
450
+		$paths = (array) apply_filters(
451
+			'FHEE__EE_Registry__load_class__paths',
452
+			[
453
+				EE_CORE,
454
+				EE_CLASSES,
455
+				EE_BUSINESS,
456
+			]
457
+		);
458
+		// retrieve instantiated class
459
+		return $this->_load(
460
+			$paths,
461
+			'EE_',
462
+			$class_name,
463
+			'class',
464
+			$arguments,
465
+			$from_db,
466
+			$cache,
467
+			$load_only
468
+		);
469
+	}
470
+
471
+
472
+	/**
473
+	 * loads helper classes - must be singletons
474
+	 *
475
+	 * @param string $class_name - simple class name ie: price
476
+	 * @param mixed  $arguments
477
+	 * @param bool   $load_only
478
+	 * @return bool|null|object
479
+	 * @throws InvalidInterfaceException
480
+	 * @throws InvalidDataTypeException
481
+	 * @throws EE_Error
482
+	 * @throws ReflectionException
483
+	 * @throws InvalidArgumentException
484
+	 */
485
+	public function load_helper(string $class_name, $arguments = [], bool $load_only = true)
486
+	{
487
+		// todo: add doing_it_wrong() in a few versions after all addons have had calls to this method removed
488
+		$helper_paths = (array) apply_filters('FHEE__EE_Registry__load_helper__helper_paths', [EE_HELPERS]);
489
+		// retrieve instantiated class
490
+		return $this->_load(
491
+			$helper_paths,
492
+			'EEH_',
493
+			$class_name,
494
+			'helper',
495
+			$arguments,
496
+			false,
497
+			true,
498
+			$load_only
499
+		);
500
+	}
501
+
502
+
503
+	/**
504
+	 * loads core classes - must be singletons
505
+	 *
506
+	 * @param string $class_name - simple class name ie: session
507
+	 * @param mixed  $arguments
508
+	 * @param bool   $load_only
509
+	 * @param bool   $cache      whether to cache the object or not.
510
+	 * @return bool|null|object
511
+	 * @throws InvalidInterfaceException
512
+	 * @throws InvalidDataTypeException
513
+	 * @throws EE_Error
514
+	 * @throws ReflectionException
515
+	 * @throws InvalidArgumentException
516
+	 */
517
+	public function load_lib(string $class_name, $arguments = [], bool $load_only = false, bool $cache = true)
518
+	{
519
+		$paths = [
520
+			EE_LIBRARIES,
521
+			EE_LIBRARIES . 'messages/',
522
+			EE_LIBRARIES . 'shortcodes/',
523
+			EE_LIBRARIES . 'qtips/',
524
+			EE_LIBRARIES . 'payment_methods/',
525
+		];
526
+		// retrieve instantiated class
527
+		return $this->_load(
528
+			$paths,
529
+			'EE_',
530
+			$class_name,
531
+			'lib',
532
+			$arguments,
533
+			false,
534
+			$cache,
535
+			$load_only
536
+		);
537
+	}
538
+
539
+
540
+	/**
541
+	 * loads model classes - must be singletons
542
+	 *
543
+	 * @param string $class_name - simple class name ie: price
544
+	 * @param mixed  $arguments
545
+	 * @param bool   $load_only
546
+	 * @return EEM_Base|null
547
+	 * @throws InvalidInterfaceException
548
+	 * @throws InvalidDataTypeException
549
+	 * @throws EE_Error
550
+	 * @throws ReflectionException
551
+	 * @throws InvalidArgumentException
552
+	 */
553
+	public function load_model(string $class_name, $arguments = [], bool $load_only = false): ?EEM_Base
554
+	{
555
+		// retrieve instantiated class
556
+		$model = $this->_load(
557
+			(array) apply_filters(
558
+				'FHEE__EE_Registry__load_model__paths',
559
+				[
560
+					EE_MODELS,
561
+					EE_CORE,
562
+				]
563
+			),
564
+			'EEM_',
565
+			$class_name,
566
+			'model',
567
+			$arguments,
568
+			false,
569
+			true,
570
+			$load_only
571
+		);
572
+		return $model instanceof EEM_Base ? $model : null;
573
+	}
574
+
575
+
576
+	/**
577
+	 * loads model classes - must be singletons
578
+	 *
579
+	 * @param string $class_name - simple class name ie: price
580
+	 * @param mixed  $arguments
581
+	 * @param bool   $load_only
582
+	 * @return bool|null|object
583
+	 * @throws InvalidInterfaceException
584
+	 * @throws InvalidDataTypeException
585
+	 * @throws EE_Error
586
+	 * @throws ReflectionException
587
+	 * @throws InvalidArgumentException
588
+	 * @deprecated  4.10.33.p
589
+	 */
590
+	public function load_model_class(string $class_name, $arguments = [], bool $load_only = true)
591
+	{
592
+		$paths = [
593
+			EE_MODELS . 'fields/',
594
+			EE_MODELS . 'helpers/',
595
+			EE_MODELS . 'relations/',
596
+			EE_MODELS . 'strategies/',
597
+		];
598
+		// retrieve instantiated class
599
+		return $this->_load(
600
+			$paths,
601
+			'EE_',
602
+			$class_name,
603
+			'',
604
+			$arguments,
605
+			false,
606
+			true,
607
+			$load_only
608
+		);
609
+	}
610
+
611
+
612
+	/**
613
+	 * Determines if $model_name is the name of an actual EE model.
614
+	 *
615
+	 * @param string $model_name like Event, Attendee, Question_Group_Question, etc.
616
+	 * @return boolean
617
+	 */
618
+	public function is_model_name(string $model_name): bool
619
+	{
620
+		return isset($this->models[ $model_name ]);
621
+	}
622
+
623
+
624
+	/**
625
+	 * generic class loader
626
+	 *
627
+	 * @param string $path_to_file - directory path to file location, not including filename
628
+	 * @param string $file_name    - file name  ie:  my_file.php, including extension
629
+	 * @param string $type         - file type - core? class? helper? model?
630
+	 * @param mixed  $arguments
631
+	 * @param bool   $load_only
632
+	 * @return bool|null|object
633
+	 * @throws InvalidInterfaceException
634
+	 * @throws InvalidDataTypeException
635
+	 * @throws EE_Error
636
+	 * @throws ReflectionException
637
+	 * @throws InvalidArgumentException
638
+	 */
639
+	public function load_file(
640
+		string $path_to_file,
641
+		string $file_name,
642
+		string $type = '',
643
+		$arguments = [],
644
+		bool $load_only = true
645
+	) {
646
+		// retrieve instantiated class
647
+		return $this->_load(
648
+			(array) $path_to_file,
649
+			'',
650
+			$file_name,
651
+			$type,
652
+			$arguments,
653
+			false,
654
+			true,
655
+			$load_only
656
+		);
657
+	}
658
+
659
+
660
+	/**
661
+	 * @param string $path_to_file - directory path to file location, not including filename
662
+	 * @param string $class_name   - full class name  ie:  My_Class
663
+	 * @param string $type         - file type - core? class? helper? model?
664
+	 * @param mixed  $arguments
665
+	 * @param bool   $load_only
666
+	 * @return bool|null|object
667
+	 * @throws InvalidInterfaceException
668
+	 * @throws InvalidDataTypeException
669
+	 * @throws EE_Error
670
+	 * @throws ReflectionException
671
+	 * @throws InvalidArgumentException
672
+	 * @deprecated  4.10.33.p
673
+	 */
674
+	public function load_addon(
675
+		string $path_to_file,
676
+		string $class_name,
677
+		string $type = 'class',
678
+		$arguments = [],
679
+		bool $load_only = false
680
+	) {
681
+		// retrieve instantiated class
682
+		return $this->_load(
683
+			(array) $path_to_file,
684
+			'addon',
685
+			$class_name,
686
+			$type,
687
+			$arguments,
688
+			false,
689
+			true,
690
+			$load_only
691
+		);
692
+	}
693
+
694
+
695
+	/**
696
+	 * instantiates, caches, and automatically resolves dependencies
697
+	 * for classes that use a Fully Qualified Class Name.
698
+	 * if the class is not capable of being loaded using PSR-4 autoloading,
699
+	 * then you need to use one of the existing load_*() methods
700
+	 * which can resolve the classname and filepath from the passed arguments
701
+	 *
702
+	 * @param string      $class_name Fully Qualified Class Name
703
+	 * @param array       $arguments  an argument, or array of arguments to pass to the class upon instantiation
704
+	 * @param bool        $cache      whether to cache the instantiated object for reuse
705
+	 * @param bool        $from_db    some classes are instantiated from the db
706
+	 *                                and thus call a different method to instantiate
707
+	 * @param bool        $load_only  if true, will only load the file, but will NOT instantiate an object
708
+	 * @param bool|string $addon      if true, will cache the object in the EE_Registry->$addons array
709
+	 * @return bool|null|mixed     null = failure to load or instantiate class object.
710
+	 *                                object = class loaded and instantiated successfully.
711
+	 *                                bool = fail or success when $load_only is true
712
+	 * @throws InvalidInterfaceException
713
+	 * @throws InvalidDataTypeException
714
+	 * @throws EE_Error
715
+	 * @throws ReflectionException
716
+	 * @throws InvalidArgumentException
717
+	 */
718
+	public function create(
719
+		string $class_name = '',
720
+		array $arguments = [],
721
+		bool $cache = false,
722
+		bool $from_db = false,
723
+		bool $load_only = false,
724
+		bool $addon = false
725
+	) {
726
+		$class_name   = ltrim($class_name, '\\');
727
+		$class_name   = $this->class_cache->getFqnForAlias($class_name);
728
+		$class_exists = $this->loadOrVerifyClassExists($class_name, $arguments);
729
+		// if a non-FQCN was passed, then
730
+		// verifyClassExists() might return an object
731
+		// or it could return null if the class just could not be found anywhere
732
+		if ($class_exists instanceof $class_name || $class_exists === null) {
733
+			// either way, return the results
734
+			return $class_exists;
735
+		}
736
+		$class_name = $class_exists;
737
+		// if we're only loading the class and it already exists, then let's just return true immediately
738
+		if ($load_only) {
739
+			return true;
740
+		}
741
+		$addon = $addon ? 'addon' : '';
742
+		// $this->_cache_on is toggled during the recursive loading that can occur with dependency injection
743
+		// $cache is controlled by individual calls to separate Registry loader methods like load_class()
744
+		// $load_only is also controlled by individual calls to separate Registry loader methods like load_file()
745
+		if ($this->_cache_on && $cache) {
746
+			// return object if it's already cached
747
+			$cached_class = $this->_get_cached_class($class_name, $addon, $arguments);
748
+			if ($cached_class !== null) {
749
+				return $cached_class;
750
+			}
751
+		}                                                           // obtain the loader method from the dependency map
752
+		$loader = $this->_dependency_map->class_loader($class_name);// instantiate the requested object
753
+		if ($loader instanceof Closure) {
754
+			$class_obj = $loader($arguments);
755
+		} else {
756
+			if ($loader && method_exists($this, $loader)) {
757
+				$class_obj = $this->{$loader}($class_name, $arguments);
758
+			} else {
759
+				$class_obj = $this->_create_object($class_name, $arguments, $addon, $from_db);
760
+			}
761
+		}
762
+		if (($this->_cache_on && $cache) || $this->get_class_abbreviation($class_name, '')) {
763
+			// save it for later... kinda like gum  { : $
764
+			$this->_set_cached_class(
765
+				$class_obj,
766
+				$class_name,
767
+				$addon,
768
+				$from_db,
769
+				$arguments
770
+			);
771
+		}
772
+		$this->_cache_on = true;
773
+		return $class_obj;
774
+	}
775
+
776
+
777
+	/**
778
+	 * Recursively checks that a class exists and potentially attempts to load classes with non-FQCNs
779
+	 *
780
+	 * @param string|mixed $class_name
781
+	 * @param array        $arguments
782
+	 * @param int          $attempt
783
+	 * @return mixed
784
+	 */
785
+	private function loadOrVerifyClassExists($class_name, array $arguments, int $attempt = 1)
786
+	{
787
+		if (is_object($class_name) || class_exists($class_name)) {
788
+			return $class_name;
789
+		}
790
+		switch ($attempt) {
791
+			case 1:
792
+				// if it's a FQCN then maybe the class is registered with a preceding \
793
+				$class_name = strpos($class_name, '\\') !== false
794
+					? '\\' . ltrim($class_name, '\\')
795
+					: $class_name;
796
+				break;
797
+			case 2:
798
+				//
799
+				$loader = $this->_dependency_map->class_loader($class_name);
800
+				if ($loader && method_exists($this, $loader)) {
801
+					return $this->{$loader}($class_name, $arguments);
802
+				}
803
+				break;
804
+			case 3:
805
+			default:
806
+				return null;
807
+		}
808
+		$attempt++;
809
+		return $this->loadOrVerifyClassExists($class_name, $arguments, $attempt);
810
+	}
811
+
812
+
813
+	/**
814
+	 * instantiates, caches, and injects dependencies for classes
815
+	 *
816
+	 * @param array  $file_paths         an array of paths to folders to look in
817
+	 * @param string $class_prefix       EE  or EEM or... ???
818
+	 * @param string $class_name         $class name
819
+	 * @param string $type               file type - core? class? helper? model?
820
+	 * @param mixed  $arguments          an argument or array of arguments to pass to the class upon instantiation
821
+	 * @param bool   $from_db            some classes are instantiated from the db
822
+	 *                                   and thus call a different method to instantiate
823
+	 * @param bool   $cache              whether to cache the instantiated object for reuse
824
+	 * @param bool   $load_only          if true, will only load the file, but will NOT instantiate an object
825
+	 * @return bool|null|object          null   = failure to load or instantiate class object.
826
+	 *                                   object = class loaded and instantiated successfully.
827
+	 *                                   bool   = fail or success when $load_only is true
828
+	 * @throws EE_Error
829
+	 * @throws ReflectionException
830
+	 * @throws InvalidInterfaceException
831
+	 * @throws InvalidDataTypeException
832
+	 * @throws InvalidArgumentException
833
+	 */
834
+	protected function _load(
835
+		array $file_paths = [],
836
+		string $class_prefix = 'EE_',
837
+		string $class_name = '',
838
+		string $type = 'class',
839
+		array $arguments = [],
840
+		bool $from_db = false,
841
+		bool $cache = true,
842
+		bool $load_only = false
843
+	) {
844
+		$class_name = ltrim($class_name, '\\');
845
+		// strip php file extension
846
+		$class_name = str_replace('.php', '', trim($class_name));
847
+		// does the class have a prefix ?
848
+		if (! empty($class_prefix) && $class_prefix !== 'addon') {
849
+			// make sure $class_prefix is uppercase
850
+			$class_prefix = strtoupper(trim($class_prefix));
851
+			// add class prefix ONCE!!!
852
+			$class_name = $class_prefix . str_replace($class_prefix, '', $class_name);
853
+		}
854
+		$class_name   = $this->class_cache->getFqnForAlias($class_name);
855
+		$class_exists = class_exists($class_name, false);
856
+		// if we're only loading the class and it already exists, then let's just return true immediately
857
+		if ($load_only && $class_exists) {
858
+			return true;
859
+		}
860
+		$arguments = is_array($arguments) ? $arguments : [$arguments];
861
+		// $this->_cache_on is toggled during the recursive loading that can occur with dependency injection
862
+		// $cache is controlled by individual calls to separate Registry loader methods like load_class()
863
+		// $load_only is also controlled by individual calls to separate Registry loader methods like load_file()
864
+		if ($this->_cache_on && $cache && ! $load_only) {
865
+			// return object if it's already cached
866
+			$cached_class = $this->_get_cached_class($class_name, $class_prefix, $arguments);
867
+			if ($cached_class !== null) {
868
+				return $cached_class;
869
+			}
870
+		}
871
+		// if the class doesn't already exist.. then we need to try and find the file and load it
872
+		if (! $class_exists) {
873
+			// get full path to file
874
+			$path = $this->_resolve_path($class_name, $type, $file_paths);
875
+			// load the file
876
+			$loaded = $this->_require_file($path, $class_name, $type, $file_paths);
877
+			// if we are only loading a file but NOT instantiating an object
878
+			// then return boolean for whether class was loaded or not
879
+			if ($load_only) {
880
+				return $loaded;
881
+			}
882
+			// if an object was expected but loading failed, then return nothing
883
+			if (! $loaded) {
884
+				return null;
885
+			}
886
+		}
887
+		// instantiate the requested object
888
+		$class_obj = $this->_create_object($class_name, $arguments, $type, $from_db);
889
+		if ($this->_cache_on && $cache) {
890
+			// save it for later... kinda like gum  { : $
891
+			$this->_set_cached_class(
892
+				$class_obj,
893
+				$class_name,
894
+				$class_prefix,
895
+				$from_db,
896
+				$arguments
897
+			);
898
+		}
899
+		$this->_cache_on = true;
900
+		return $class_obj;
901
+	}
902
+
903
+
904
+	/**
905
+	 * @param string $class_name
906
+	 * @param string $default have to specify something, but not anything that will conflict
907
+	 * @return mixed|string
908
+	 */
909
+	protected function get_class_abbreviation(string $class_name, string $default = 'FANCY_BATMAN_PANTS')
910
+	{
911
+		return $this->_class_abbreviations[ $class_name ] ?? $default;
912
+	}
913
+
914
+
915
+	/**
916
+	 * attempts to find a cached version of the requested class
917
+	 * by looking in the following places:
918
+	 *        $this->{$class_abbreviation}            ie:    $this->CART
919
+	 *        $this->{$class_name}                        ie:    $this->Some_Class
920
+	 *        $this->LIB->{$class_name}                ie:    $this->LIB->Some_Class
921
+	 *        $this->addon->{$class_name}    ie:    $this->addon->Some_Addon_Class
922
+	 *
923
+	 * @param string $class_name
924
+	 * @param string $class_prefix
925
+	 * @param array  $arguments
926
+	 * @return mixed
927
+	 */
928
+	protected function _get_cached_class(
929
+		string $class_name,
930
+		string $class_prefix = '',
931
+		array $arguments = []
932
+	) {
933
+		if ($class_name === 'EE_Registry') {
934
+			return $this;
935
+		}
936
+		$class_abbreviation = $this->get_class_abbreviation($class_name);
937
+		// check if class has already been loaded, and return it if it has been
938
+		if (isset($this->{$class_abbreviation})) {
939
+			return $this->{$class_abbreviation};
940
+		}
941
+		$class_name = str_replace('\\', '_', $class_name);
942
+		if (isset($this->{$class_name})) {
943
+			return $this->{$class_name};
944
+		}
945
+		if ($class_prefix === 'addon' && $this->addons->has($class_name)) {
946
+			return $this->addons->get($class_name);
947
+		}
948
+		$object_identifier = $this->object_identifier->getIdentifier($class_name, $arguments);
949
+		if ($this->LIB->has($object_identifier)) {
950
+			return $this->LIB->get($object_identifier);
951
+		}
952
+		foreach ($this->LIB as $key => $object) {
953
+			if (
954
+				// request does not contain new arguments and therefore no args identifier
955
+				! $this->object_identifier->hasArguments($object_identifier)
956
+				// but previously cached class with args was found
957
+				&& $this->object_identifier->fqcnMatchesObjectIdentifier($class_name, $key)
958
+			) {
959
+				return $object;
960
+			}
961
+		}
962
+		return null;
963
+	}
964
+
965
+
966
+	/**
967
+	 * removes a cached version of the requested class
968
+	 *
969
+	 * @param string  $class_name
970
+	 * @param boolean $addon
971
+	 * @param array   $arguments
972
+	 * @return boolean
973
+	 */
974
+	public function clear_cached_class(
975
+		string $class_name,
976
+		bool $addon = false,
977
+		array $arguments = []
978
+	): bool {
979
+		$class_abbreviation = $this->get_class_abbreviation($class_name);
980
+		// check if class has already been loaded, and return it if it has been
981
+		if (isset($this->{$class_abbreviation})) {
982
+			$this->{$class_abbreviation} = null;
983
+			return true;
984
+		}
985
+		$class_name = str_replace('\\', '_', $class_name);
986
+		if (isset($this->{$class_name})) {
987
+			$this->{$class_name} = null;
988
+			return true;
989
+		}
990
+		if ($addon && $this->addons->has($class_name)) {
991
+			$this->addons->remove($class_name);
992
+			return true;
993
+		}
994
+		$class_name = $this->object_identifier->getIdentifier($class_name, $arguments);
995
+		if ($this->LIB->has($class_name)) {
996
+			$this->LIB->remove($class_name);
997
+			return true;
998
+		}
999
+		return false;
1000
+	}
1001
+
1002
+
1003
+	/**
1004
+	 * _set_cached_class
1005
+	 * attempts to cache the instantiated class locally
1006
+	 * in one of the following places, in the following order:
1007
+	 *        $this->{class_abbreviation}   ie:    $this->CART
1008
+	 *        $this->{$class_name}          ie:    $this->Some_Class
1009
+	 *        $this->addon->{$$class_name}    ie:    $this->addon->Some_Addon_Class
1010
+	 *        $this->LIB->{$class_name}     ie:    $this->LIB->Some_Class
1011
+	 *
1012
+	 * @param object $class_obj
1013
+	 * @param string $class_name
1014
+	 * @param string $class_prefix
1015
+	 * @param bool   $from_db
1016
+	 * @param array  $arguments
1017
+	 * @return void
1018
+	 */
1019
+	protected function _set_cached_class(
1020
+		$class_obj,
1021
+		string $class_name,
1022
+		string $class_prefix = '',
1023
+		bool $from_db = false,
1024
+		array $arguments = []
1025
+	) {
1026
+		if ($class_name === 'EE_Registry' || empty($class_obj)) {
1027
+			return;
1028
+		}
1029
+		// return newly instantiated class
1030
+		$class_abbreviation = $this->get_class_abbreviation($class_name, '');
1031
+		if ($class_abbreviation) {
1032
+			$this->{$class_abbreviation} = $class_obj;
1033
+			return;
1034
+		}
1035
+		$class_name = str_replace('\\', '_', $class_name);
1036
+		if (property_exists($this, $class_name)) {
1037
+			$this->{$class_name} = $class_obj;
1038
+			return;
1039
+		}
1040
+		if ($class_prefix === 'addon') {
1041
+			$this->addons->add($class_name, $class_obj);
1042
+			return;
1043
+		}
1044
+		if (! $from_db) {
1045
+			$class_name = $this->object_identifier->getIdentifier($class_name, $arguments);
1046
+			$this->LIB->add($class_name, $class_obj);
1047
+		}
1048
+	}
1049
+
1050
+
1051
+	/**
1052
+	 * attempts to find a full valid filepath for the requested class.
1053
+	 * loops thru each of the base paths in the $file_paths array and appends : "{classname} . {file type} . php"
1054
+	 * then returns that path if the target file has been found and is readable
1055
+	 *
1056
+	 * @param string $class_name
1057
+	 * @param string $type
1058
+	 * @param array  $file_paths
1059
+	 * @return string | bool
1060
+	 */
1061
+	protected function _resolve_path(string $class_name, string $type = '', array $file_paths = [])
1062
+	{
1063
+		// make sure $file_paths is an array
1064
+		$file_paths = is_array($file_paths)
1065
+			? $file_paths
1066
+			: [$file_paths];
1067
+		// cycle thru paths
1068
+		foreach ($file_paths as $key => $file_path) {
1069
+			// convert all separators to proper /, if no filepath, then use EE_CLASSES
1070
+			$file_path = $file_path
1071
+				? str_replace(['/', '\\'], '/', $file_path)
1072
+				: EE_CLASSES;
1073
+			// prep file type
1074
+			$type = ! empty($type)
1075
+				? trim($type, '.') . '.'
1076
+				: '';
1077
+			// build full file path
1078
+			$file_paths[ $key ] = rtrim($file_path, '/') . '/' . $class_name . '.' . $type . 'php';
1079
+			// does the file exist and can be read ?
1080
+			if (is_readable($file_paths[ $key ])) {
1081
+				return $file_paths[ $key ];
1082
+			}
1083
+		}
1084
+		return false;
1085
+	}
1086
+
1087
+
1088
+	/**
1089
+	 * basically just performs a require_once()
1090
+	 * but with some error handling
1091
+	 *
1092
+	 * @param string $path
1093
+	 * @param string $class_name
1094
+	 * @param string $type
1095
+	 * @param array  $file_paths
1096
+	 * @return bool
1097
+	 * @throws EE_Error
1098
+	 * @throws ReflectionException
1099
+	 */
1100
+	protected function _require_file(string $path, string $class_name, string $type = '', array $file_paths = []): bool
1101
+	{
1102
+		$this->resolve_legacy_class_parent($class_name);
1103
+		// don't give up! you gotta...
1104
+		try {
1105
+			// does the file exist and can it be read ?
1106
+			if (! $path) {
1107
+				// just in case the file has already been autoloaded,
1108
+				// but discrepancies in the naming schema are preventing it from
1109
+				// being loaded via one of the EE_Registry::load_*() methods,
1110
+				// then let's try one last hail mary before throwing an exception
1111
+				// and call class_exists() again, but with autoloading turned ON
1112
+				if (class_exists($class_name)) {
1113
+					return true;
1114
+				}
1115
+				// so sorry, can't find the file
1116
+				throw new EE_Error(
1117
+					sprintf(
1118
+						esc_html__(
1119
+							'The %1$s file %2$s could not be located or is not readable due to file permissions. Please ensure that the following filepath(s) are correct: %3$s',
1120
+							'event_espresso'
1121
+						),
1122
+						trim($type, '.'),
1123
+						$class_name,
1124
+						'<br />' . implode(',<br />', $file_paths)
1125
+					)
1126
+				);
1127
+			}
1128
+			// get the file
1129
+			require_once($path);
1130
+			// if the class isn't already declared somewhere
1131
+			if (class_exists($class_name, false) === false) {
1132
+				// so sorry, not a class
1133
+				throw new EE_Error(
1134
+					sprintf(
1135
+						esc_html__(
1136
+							'The %s file %s does not appear to contain the %s Class.',
1137
+							'event_espresso'
1138
+						),
1139
+						$type,
1140
+						$path,
1141
+						$class_name
1142
+					)
1143
+				);
1144
+			}
1145
+		} catch (EE_Error $e) {
1146
+			$e->get_error();
1147
+			return false;
1148
+		}
1149
+		return true;
1150
+	}
1151
+
1152
+
1153
+	/**
1154
+	 * Some of our legacy classes that extended a parent class would simply use a require() statement
1155
+	 * before their class declaration in order to ensure that the parent class was loaded.
1156
+	 * This is not ideal, but it's nearly impossible to determine the parent class of a non-namespaced class,
1157
+	 * without triggering a fatal error because the parent class has yet to be loaded and therefore doesn't exist.
1158
+	 *
1159
+	 * @param string $class_name
1160
+	 */
1161
+	protected function resolve_legacy_class_parent(string $class_name = '')
1162
+	{
1163
+		try {
1164
+			$legacy_parent_class_map = [
1165
+				'EE_Payment_Processor' => 'core/business/EE_Processor_Base.class.php',
1166
+			];
1167
+			if (isset($legacy_parent_class_map[ $class_name ])) {
1168
+				require_once EE_PLUGIN_DIR_PATH . $legacy_parent_class_map[ $class_name ];
1169
+			}
1170
+		} catch (Exception $exception) {
1171
+		}
1172
+	}
1173
+
1174
+
1175
+	/**
1176
+	 * _create_object
1177
+	 * Attempts to instantiate the requested class via any of the
1178
+	 * commonly used instantiation methods employed throughout EE.
1179
+	 * The priority for instantiation is as follows:
1180
+	 *        - abstract classes or any class flagged as "load only" (no instantiation occurs)
1181
+	 *        - model objects via their 'new_instance_from_db' method
1182
+	 *        - model objects via their 'new_instance' method
1183
+	 *        - "singleton" classes" via their 'instance' method
1184
+	 *    - standard instantiable classes via their __constructor
1185
+	 * Prior to instantiation, if the classname exists in the dependency_map,
1186
+	 * then the constructor for the requested class will be examined to determine
1187
+	 * if any dependencies exist, and if they can be injected.
1188
+	 * If so, then those classes will be added to the array of arguments passed to the constructor
1189
+	 *
1190
+	 * @param string $class_name
1191
+	 * @param array  $arguments
1192
+	 * @param string $type
1193
+	 * @param bool   $from_db
1194
+	 * @return null|object|bool
1195
+	 * @throws InvalidArgumentException
1196
+	 * @throws InvalidInterfaceException
1197
+	 * @throws EE_Error
1198
+	 * @throws ReflectionException
1199
+	 * @throws InvalidDataTypeException
1200
+	 */
1201
+	protected function _create_object(
1202
+		string $class_name,
1203
+		array $arguments = [],
1204
+		string $type = '',
1205
+		bool $from_db = false
1206
+	) {
1207
+		// create reflection
1208
+		$reflector = $this->mirror->getReflectionClass($class_name);
1209
+		// make sure arguments are an array
1210
+		$arguments = is_array($arguments)
1211
+			? $arguments
1212
+			: [$arguments];
1213
+		// and if arguments array is numerically and sequentially indexed, then we want it to remain as is,
1214
+		// else wrap it in an additional array so that it doesn't get split into multiple parameters
1215
+		$arguments = $this->_array_is_numerically_and_sequentially_indexed($arguments)
1216
+			? $arguments
1217
+			: [$arguments];
1218
+		// attempt to inject dependencies ?
1219
+		if ($this->_dependency_map->has($class_name)) {
1220
+			$arguments = $this->_resolve_dependencies($reflector, $class_name, $arguments);
1221
+		}
1222
+		// instantiate the class if possible
1223
+		if ($reflector->isAbstract()) {
1224
+			// nothing to instantiate, loading file was enough
1225
+			// does not throw an exception so $instantiation_mode is unused
1226
+			// $instantiation_mode = "1) no constructor abstract class";
1227
+			return true;
1228
+		}
1229
+		if (
1230
+			empty($arguments)
1231
+			&& $this->mirror->getConstructorFromReflection($reflector) === null
1232
+			&& $reflector->isInstantiable()
1233
+		) {
1234
+			// no constructor = static methods only... nothing to instantiate, loading file was enough
1235
+			// $instantiation_mode = "2) no constructor but instantiable";
1236
+			return $reflector->newInstance();
1237
+		}
1238
+		if ($from_db && method_exists($class_name, 'new_instance_from_db')) {
1239
+			// $instantiation_mode = "3) new_instance_from_db()";
1240
+			return call_user_func_array([$class_name, 'new_instance_from_db'], $arguments);
1241
+		}
1242
+		if (method_exists($class_name, 'new_instance')) {
1243
+			// $instantiation_mode = "4) new_instance()";
1244
+			return call_user_func_array([$class_name, 'new_instance'], $arguments);
1245
+		}
1246
+		if (method_exists($class_name, 'instance')) {
1247
+			// $instantiation_mode = "5) instance()";
1248
+			return call_user_func_array([$class_name, 'instance'], $arguments);
1249
+		}
1250
+		if ($reflector->isInstantiable()) {
1251
+			$args_passed_count = count($arguments);
1252
+			$args_required_count = count($this->mirror->getRequiredParameters($class_name));
1253
+			if ($args_passed_count < $args_required_count) {
1254
+				throw new RuntimeException(
1255
+					sprintf(
1256
+						__(
1257
+							'Invalid arguments supplied for the %1$s class, %2$s were required but %3$s were passed.',
1258
+							'event_espresso'
1259
+						),
1260
+						$class_name,
1261
+						$args_required_count,
1262
+						$args_passed_count
1263
+					)
1264
+				);
1265
+			}
1266
+			// $instantiation_mode = "6) constructor";
1267
+			return $reflector->newInstanceArgs($arguments);
1268
+		}
1269
+		// heh ? something's not right !
1270
+		throw new EE_Error(
1271
+			sprintf(
1272
+				esc_html__('The %s file %s could not be instantiated.', 'event_espresso'),
1273
+				$type,
1274
+				$class_name
1275
+			)
1276
+		);
1277
+	}
1278
+
1279
+
1280
+	/**
1281
+	 * @see http://stackoverflow.com/questions/173400/how-to-check-if-php-array-is-associative-or-sequential
1282
+	 * @param array $array
1283
+	 * @return bool
1284
+	 */
1285
+	protected function _array_is_numerically_and_sequentially_indexed(array $array): bool
1286
+	{
1287
+		return empty($array) || array_keys($array) === range(0, count($array) - 1);
1288
+	}
1289
+
1290
+
1291
+	/**
1292
+	 * _resolve_dependencies
1293
+	 * examines the constructor for the requested class to determine
1294
+	 * if any dependencies exist, and if they can be injected.
1295
+	 * If so, then those classes will be added to the array of arguments passed to the constructor
1296
+	 * PLZ NOTE: this is achieved by type hinting the constructor params
1297
+	 * For example:
1298
+	 *        if attempting to load a class "Foo" with the following constructor:
1299
+	 *        __construct( Bar $bar_class, Fighter $grohl_class )
1300
+	 *        then $bar_class and $grohl_class will be added to the $arguments array,
1301
+	 *        but only IF they are NOT already present in the incoming arguments array,
1302
+	 *        and the correct classes can be loaded
1303
+	 *
1304
+	 * @param ReflectionClass $reflector
1305
+	 * @param string          $class_name
1306
+	 * @param array           $arguments
1307
+	 * @return array
1308
+	 * @throws InvalidArgumentException
1309
+	 * @throws InvalidDataTypeException
1310
+	 * @throws InvalidInterfaceException
1311
+	 * @throws ReflectionException
1312
+	 */
1313
+	protected function _resolve_dependencies(
1314
+		ReflectionClass $reflector,
1315
+		string $class_name,
1316
+		array $arguments = []
1317
+	): array {
1318
+		// let's examine the constructor
1319
+		$constructor = $this->mirror->getConstructorFromReflection($reflector);
1320
+		// whu? huh? nothing?
1321
+		if (! $constructor) {
1322
+			return $arguments;
1323
+		}
1324
+		// get constructor parameters
1325
+		$params = $this->mirror->getParametersFromReflection($reflector);
1326
+		// and the keys for the incoming arguments array so that we can compare existing arguments with what is expected
1327
+		$argument_keys = array_keys($arguments);
1328
+		// now loop thru all the constructors' expected parameters
1329
+		foreach ($params as $index => $param) {
1330
+			try {
1331
+				// is this a dependency for a specific class ?
1332
+				$param_class = $this->mirror->getParameterClassName($param, $class_name, $index);
1333
+			} catch (ReflectionException $exception) {
1334
+				// uh-oh... most likely a legacy class that has not been autoloaded
1335
+				// let's try to derive the classname from what we have now
1336
+				// and hope that the property var name is close to the class name
1337
+				$param_class = $param->getName();
1338
+				$param_class = str_replace('_', ' ', $param_class);
1339
+				$param_class = ucwords($param_class);
1340
+				$param_class = str_replace(' ', '_', $param_class);
1341
+			}
1342
+			// BUT WAIT !!! This class may be an alias for something else (or getting replaced at runtime)
1343
+			$param_class = $this->class_cache->isAlias($param_class, $class_name)
1344
+				? $this->class_cache->getFqnForAlias($param_class, $class_name)
1345
+				: $param_class;
1346
+			if (
1347
+				// param is not even a class
1348
+				($param_class === null || $this->parameterIsPrimitive($param_class))
1349
+				// and something already exists in the incoming arguments for this param
1350
+				&& array_key_exists($index, $argument_keys)
1351
+				&& isset($arguments[ $argument_keys[ $index ] ])
1352
+			) {
1353
+				// so let's skip this argument and move on to the next
1354
+				continue;
1355
+			}
1356
+			// parameter is type hinted as a class
1357
+			if ($param_class !== null) {
1358
+				// parameter exists as an incoming argument, AND it's the correct class
1359
+				if (
1360
+					array_key_exists($index, $argument_keys)
1361
+					&& isset($arguments[ $argument_keys[ $index ] ])
1362
+					&& $arguments[ $argument_keys[ $index ] ] instanceof $param_class
1363
+				) {
1364
+					// skip this argument and move on to the next
1365
+					continue;
1366
+				}
1367
+				// parameter should be injected
1368
+				if ($this->_dependency_map->has_dependency_for_class($class_name, $param_class)) {
1369
+					$arguments = $this->_resolve_dependency(
1370
+						$class_name,
1371
+						$param_class,
1372
+						$arguments,
1373
+						$index
1374
+					);
1375
+				}
1376
+			}
1377
+			if (empty($arguments[ $index ])) {
1378
+				$default_value = $this->mirror->getParameterDefaultValue(
1379
+					$param,
1380
+					$class_name,
1381
+					$index
1382
+				);
1383
+				// if there's no default value, and the incoming argument is an array (albeit empty), then use that
1384
+				$arguments[ $index ] = $default_value === null
1385
+									   && isset($arguments[ $index ])
1386
+									   && is_array($arguments[ $index ])
1387
+					? $arguments[ $index ]
1388
+					: $default_value;
1389
+			}
1390
+		}
1391
+		return $arguments;
1392
+	}
1393
+
1394
+
1395
+	/**
1396
+	 * @param string $class_name
1397
+	 * @param string $param_class
1398
+	 * @param array  $arguments
1399
+	 * @param mixed  $index
1400
+	 * @return array
1401
+	 * @throws InvalidArgumentException
1402
+	 * @throws InvalidInterfaceException
1403
+	 * @throws InvalidDataTypeException
1404
+	 */
1405
+	protected function _resolve_dependency(string $class_name, string $param_class, array $arguments, $index): array
1406
+	{
1407
+		$dependency = null;
1408
+		// should dependency be loaded from cache ?
1409
+		$cache_on = $this->_dependency_map->loading_strategy_for_class_dependency(
1410
+			$class_name,
1411
+			$param_class
1412
+		);
1413
+		$cache_on = $cache_on !== EE_Dependency_Map::load_new_object;
1414
+		// we might have a dependency...
1415
+		// let's MAYBE try and find it in our cache if that's what's been requested
1416
+		$cached_class = $cache_on
1417
+			? $this->_get_cached_class($param_class)
1418
+			: null;
1419
+		// and grab it if it exists
1420
+		if ($cached_class instanceof $param_class) {
1421
+			$dependency = $cached_class;
1422
+		} elseif ($param_class !== $class_name) {
1423
+			// obtain the loader method from the dependency map
1424
+			$loader = $this->_dependency_map->class_loader($param_class);
1425
+			// is loader a custom closure ?
1426
+			if ($loader instanceof Closure) {
1427
+				$dependency = $loader($arguments);
1428
+			} else {
1429
+				// set the cache on property for the recursive loading call
1430
+				$this->_cache_on = $cache_on;
1431
+				// if not, then let's try and load it via the registry
1432
+				if ($loader && method_exists($this, $loader)) {
1433
+					$dependency = $this->{$loader}($param_class);
1434
+				} else {
1435
+					$dependency = LoaderFactory::getLoader()->load(
1436
+						$param_class,
1437
+						[],
1438
+						$cache_on
1439
+					);
1440
+				}
1441
+			}
1442
+		}
1443
+		// did we successfully find the correct dependency ?
1444
+		if ($dependency instanceof $param_class) {
1445
+			// then let's inject it into the incoming array of arguments at the correct location
1446
+			$arguments[ $index ] = $dependency;
1447
+		}
1448
+		return $arguments;
1449
+	}
1450
+
1451
+
1452
+	/**
1453
+	 * call any loader that's been registered in the EE_Dependency_Map::$_class_loaders array
1454
+	 *
1455
+	 * @param string $classname PLEASE NOTE: the class name needs to match what's registered
1456
+	 *                          in the EE_Dependency_Map::$_class_loaders array,
1457
+	 *                          including the class prefix, ie: "EE_", "EEM_", "EEH_", etc
1458
+	 * @param array  $arguments
1459
+	 * @return object
1460
+	 */
1461
+	public static function factory(string $classname, array $arguments = [])
1462
+	{
1463
+		$loader = self::instance()->_dependency_map->class_loader($classname);
1464
+		if ($loader instanceof Closure) {
1465
+			return $loader($arguments);
1466
+		}
1467
+		if (method_exists(self::instance(), $loader)) {
1468
+			return self::instance()->{$loader}($classname, $arguments);
1469
+		}
1470
+		return null;
1471
+	}
1472
+
1473
+
1474
+	/**
1475
+	 * Gets the addon by its class name
1476
+	 *
1477
+	 * @param string $class_name
1478
+	 * @return EE_Addon
1479
+	 */
1480
+	public function getAddon(string $class_name): ?EE_Addon
1481
+	{
1482
+		$class_name = str_replace('\\', '_', $class_name);
1483
+		return $this->addons->{$class_name} ?? null;
1484
+	}
1485
+
1486
+
1487
+	/**
1488
+	 * removes the addon from the internal cache
1489
+	 *
1490
+	 * @param string $class_name
1491
+	 * @return void
1492
+	 */
1493
+	public function removeAddon(string $class_name)
1494
+	{
1495
+		$class_name = str_replace('\\', '_', $class_name);
1496
+		$this->addons->remove($class_name);
1497
+	}
1498
+
1499
+
1500
+	/**
1501
+	 * Gets the addon by its name/slug (not classname. For that, just
1502
+	 * use the get_addon() method above
1503
+	 *
1504
+	 * @param string $name
1505
+	 * @return EE_Addon
1506
+	 */
1507
+	public function get_addon_by_name(string $name): ?EE_Addon
1508
+	{
1509
+		foreach ($this->addons as $addon) {
1510
+			if ($addon->name() === $name) {
1511
+				return $addon;
1512
+			}
1513
+		}
1514
+		return null;
1515
+	}
1516
+
1517
+
1518
+	/**
1519
+	 * Gets an array of all the registered addons, where the keys are their names.
1520
+	 * (ie, what each returns for their name() function)
1521
+	 * They're already available on EE_Registry::instance()->addons as properties,
1522
+	 * where each property's name is the addon's classname,
1523
+	 * So if you just want to get the addon by classname,
1524
+	 * OR use the get_addon() method above.
1525
+	 * PLEASE  NOTE:
1526
+	 * addons with Fully Qualified Class Names
1527
+	 * have had the namespace separators converted to underscores,
1528
+	 * so a classname like Fully\Qualified\ClassName
1529
+	 * would have been converted to Fully_Qualified_ClassName
1530
+	 *
1531
+	 * @return EE_Addon[] where the KEYS are the addon's name()
1532
+	 */
1533
+	public function get_addons_by_name(): array
1534
+	{
1535
+		$addons = [];
1536
+		foreach ($this->addons as $addon) {
1537
+			$addons[ $addon->name() ] = $addon;
1538
+		}
1539
+		return $addons;
1540
+	}
1541
+
1542
+
1543
+	/**
1544
+	 * Resets the specified model's instance AND makes sure EE_Registry doesn't keep
1545
+	 * a stale copy of it around
1546
+	 *
1547
+	 * @param string $model_name
1548
+	 * @return EEM_Base
1549
+	 * @throws EE_Error
1550
+	 * @throws ReflectionException
1551
+	 */
1552
+	public function reset_model(string $model_name): ?EEM_Base
1553
+	{
1554
+		$model_class_name = strpos($model_name, 'EEM_') !== 0
1555
+			? "EEM_$model_name"
1556
+			: $model_name;
1557
+		if (! $this->LIB->has($model_class_name)) {
1558
+			return null;
1559
+		}
1560
+		$model = $this->LIB->get($model_class_name);
1561
+		if (! $model instanceof EEM_Base) {
1562
+			return null;
1563
+		}
1564
+		// get that model reset it and make sure we nuke the old reference to it
1565
+		if ($model instanceof $model_class_name && is_callable([$model_class_name, 'reset'])) {
1566
+			$this->LIB->remove($model_class_name);
1567
+			$this->LIB->add($model_class_name, $model->reset());
1568
+		} else {
1569
+			throw new EE_Error(
1570
+				sprintf(
1571
+					esc_html__('Model %s does not have a method "reset"', 'event_espresso'),
1572
+					$model_name
1573
+				)
1574
+			);
1575
+		}
1576
+		return $model;
1577
+	}
1578
+
1579
+
1580
+	/**
1581
+	 * Resets the registry.
1582
+	 * The criteria for what gets reset is based on what can be shared between sites on the same request when
1583
+	 * switch_to_blog is used in a multisite install.  Here is a list of things that are NOT reset.
1584
+	 * - $_dependency_map
1585
+	 * - $_class_abbreviations
1586
+	 * - $NET_CFG (EE_Network_Config): The config is shared network wide so no need to reset.
1587
+	 * - $REQ:  Still on the same request so no need to change.
1588
+	 * - $CAP: There is no site specific state in the EE_Capability class.
1589
+	 * - $SSN: Although ideally, the session should not be shared between site switches, we can't reset it because only
1590
+	 * one Session can be active in a single request.  Resetting could resolve in "headers already sent" errors.
1591
+	 * - $addons:  In multisite, the state of the addons is something controlled via hooks etc in a normal request.  So
1592
+	 *             for now, we won't reset the addons because it could break calls to an add-ons class/methods in the
1593
+	 *             switch or on the restore.
1594
+	 * - $modules
1595
+	 * - $shortcodes
1596
+	 * - $widgets
1597
+	 *
1598
+	 * @param boolean $hard             [deprecated]
1599
+	 * @param boolean $reinstantiate    whether to create new instances of EE_Registry's singletons too,
1600
+	 *                                  or just reset without re-instantiating (handy to set to FALSE if you're not
1601
+	 *                                  sure if you CAN currently reinstantiate the singletons at the moment)
1602
+	 * @param bool    $reset_models     Defaults to true.  When false, then the models are not reset.  This is so
1603
+	 *                                  client
1604
+	 *                                  code instead can just change the model context to a different blog id if
1605
+	 *                                  necessary
1606
+	 * @return EE_Registry
1607
+	 * @throws EE_Error
1608
+	 * @throws ReflectionException
1609
+	 */
1610
+	public static function reset(bool $hard = false, bool $reinstantiate = true, bool $reset_models = true): EE_Registry
1611
+	{
1612
+		$instance            = self::instance();
1613
+		$instance->_cache_on = true;
1614
+		// reset some "special" classes
1615
+		EEH_Activation::reset();
1616
+		$hard = apply_filters('FHEE__EE_Registry__reset__hard', $hard);
1617
+		$instance->CFG = EE_Config::reset($hard, $reinstantiate);
1618
+		$instance->CART = null;
1619
+		$instance->MRM = null;
1620
+		// messages reset
1621
+		EED_Messages::reset();
1622
+		// handle of objects cached on LIB
1623
+		foreach (['LIB', 'modules'] as $cache) {
1624
+			foreach ($instance->{$cache} as $class_name => $class) {
1625
+				if (self::_reset_and_unset_object($class, $reset_models)) {
1626
+					unset($instance->{$cache}->{$class_name});
1627
+				}
1628
+			}
1629
+		}
1630
+		return $instance;
1631
+	}
1632
+
1633
+
1634
+	/**
1635
+	 * if passed object implements ResettableInterface, then call it's reset() method
1636
+	 * if passed object implements InterminableInterface, then return false,
1637
+	 * to indicate that it should NOT be cleared from the Registry cache
1638
+	 *
1639
+	 * @param      $object
1640
+	 * @param bool $reset_models
1641
+	 * @return bool returns true if cached object should be unset
1642
+	 * @throws EE_Error
1643
+	 * @throws ReflectionException
1644
+	 */
1645
+	private static function _reset_and_unset_object($object, bool $reset_models): bool
1646
+	{
1647
+		if (! is_object($object)) {
1648
+			// don't unset anything that's not an object
1649
+			return false;
1650
+		}
1651
+		if ($object instanceof EED_Module) {
1652
+			$object::reset();
1653
+			// don't unset modules
1654
+			return false;
1655
+		}
1656
+		if ($object instanceof ResettableInterface) {
1657
+			if ($object instanceof EEM_Base) {
1658
+				if ($reset_models) {
1659
+					$object->reset();
1660
+					return true;
1661
+				}
1662
+				return false;
1663
+			}
1664
+			$object->reset();
1665
+			return true;
1666
+		}
1667
+		if (! $object instanceof InterminableInterface) {
1668
+			return true;
1669
+		}
1670
+		return false;
1671
+	}
1672
+
1673
+
1674
+	/**
1675
+	 * Gets all the custom post type models defined
1676
+	 *
1677
+	 * @return array keys are model "short names" (Eg "Event") and keys are classnames (eg "EEM_Event")
1678
+	 */
1679
+	public function cpt_models(): array
1680
+	{
1681
+		$cpt_models = [];
1682
+		foreach ($this->non_abstract_db_models as $short_name => $classname) {
1683
+			if (is_subclass_of($classname, 'EEM_CPT_Base')) {
1684
+				$cpt_models[ $short_name ] = $classname;
1685
+			}
1686
+		}
1687
+		return $cpt_models;
1688
+	}
1689
+
1690
+
1691
+	/**
1692
+	 * @return EE_Config
1693
+	 */
1694
+	public static function CFG(): EE_Config
1695
+	{
1696
+		return self::instance()->CFG;
1697
+	}
1698
+
1699
+
1700
+	/**
1701
+	 * @param string $class_name
1702
+	 * @return ReflectionClass
1703
+	 * @throws ReflectionException
1704
+	 * @throws InvalidDataTypeException
1705
+	 * @deprecated 4.9.62.p
1706
+	 */
1707
+	public function get_ReflectionClass(string $class_name): ReflectionClass
1708
+	{
1709
+		return $this->mirror->getReflectionClass($class_name);
1710
+	}
1711
+
1712
+	private function parameterIsPrimitive(?string $param_class): bool
1713
+	{
1714
+		return in_array(
1715
+			$param_class,
1716
+			[
1717
+				'array',
1718
+				'bool',
1719
+				'float',
1720
+				'int',
1721
+				'string',
1722
+			]
1723
+		);
1724
+	}
1725 1725
 }
Please login to merge, or discard this patch.
core/EE_Cart.core.php 1 patch
Indentation   +393 added lines, -393 removed lines patch added patch discarded remove patch
@@ -16,397 +16,397 @@
 block discarded – undo
16 16
  */
17 17
 class EE_Cart implements ResettableInterface
18 18
 {
19
-    /**
20
-     * instance of the EE_Cart object
21
-     *
22
-     * @var EE_Cart $_instance
23
-     */
24
-    private static $_instance;
25
-
26
-    /**
27
-     * instance of the EE_Session object
28
-     *
29
-     * @var EE_Session $_session
30
-     */
31
-    protected $_session;
32
-
33
-    /**
34
-     * The total Line item which comprises all the children line-item subtotals,
35
-     * which in turn each have their line items.
36
-     * Typically, the line item structure will look like:
37
-     * grand total
38
-     * -tickets-sub-total
39
-     * --ticket1
40
-     * --ticket2
41
-     * --...
42
-     * -taxes-sub-total
43
-     * --tax1
44
-     * --tax2
45
-     *
46
-     * @var EE_Line_Item
47
-     */
48
-    private $_grand_total;
49
-
50
-
51
-    /**
52
-     * singleton method used to instantiate class object
53
-     * @param EE_Line_Item $grand_total
54
-     * @param EE_Session   $session
55
-     * @return EE_Cart
56
-     * @throws EE_Error
57
-     * @throws ReflectionException
58
-     */
59
-    public static function instance(EE_Line_Item $grand_total = null, EE_Session $session = null)
60
-    {
61
-        if ($grand_total instanceof EE_Line_Item && $grand_total->is_total()) {
62
-            self::$_instance = new self($grand_total, $session);
63
-        }
64
-        // or maybe retrieve an existing one ?
65
-        if (! self::$_instance instanceof EE_Cart) {
66
-            // try getting the cart out of the session
67
-            $saved_cart = $session instanceof EE_Session ? $session->cart() : null;
68
-            self::$_instance = $saved_cart instanceof EE_Cart ? $saved_cart : new self($grand_total, $session);
69
-            unset($saved_cart);
70
-        }
71
-        // verify that cart is ok and grand total line item exists
72
-        if (! self::$_instance instanceof EE_Cart || ! self::$_instance->_grand_total instanceof EE_Line_Item) {
73
-            self::$_instance = new self($grand_total, $session);
74
-        }
75
-        self::$_instance->get_grand_total();
76
-        // once everything is all said and done, save the cart to the EE_Session
77
-        add_action('shutdown', array(self::$_instance, 'save_cart'), 90);
78
-        return self::$_instance;
79
-    }
80
-
81
-
82
-    /**
83
-     * private constructor to prevent direct creation
84
-     *
85
-     * @param EE_Line_Item $grand_total
86
-     * @param EE_Session   $session
87
-     */
88
-    private function __construct(EE_Line_Item $grand_total = null, EE_Session $session = null)
89
-    {
90
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
91
-        $this->set_session($session);
92
-        if ($grand_total instanceof EE_Line_Item && $grand_total->is_total()) {
93
-            $this->set_grand_total_line_item($grand_total);
94
-        }
95
-    }
96
-
97
-
98
-    /**
99
-     * Resets the cart completely (whereas empty_cart
100
-     *
101
-     * @param EE_Line_Item $grand_total
102
-     * @param EE_Session   $session
103
-     * @return EE_Cart
104
-     * @throws EE_Error
105
-     * @throws ReflectionException
106
-     */
107
-    public static function reset(EE_Line_Item $grand_total = null, EE_Session $session = null)
108
-    {
109
-        remove_action('shutdown', array(self::$_instance, 'save_cart'), 90);
110
-        if ($session instanceof EE_Session) {
111
-            $session->reset_cart();
112
-        }
113
-        self::$_instance = null;
114
-        return self::instance($grand_total, $session);
115
-    }
116
-
117
-
118
-    /**
119
-     * @return EE_Session
120
-     */
121
-    public function session()
122
-    {
123
-        if (! $this->_session instanceof EE_Session) {
124
-            $this->set_session();
125
-        }
126
-        return $this->_session;
127
-    }
128
-
129
-
130
-    /**
131
-     * @param EE_Session $session
132
-     */
133
-    public function set_session(EE_Session $session = null)
134
-    {
135
-        $this->_session = $session instanceof EE_Session ? $session : EE_Registry::instance()->load_core('Session');
136
-    }
137
-
138
-
139
-    /**
140
-     * Sets the cart to match the line item. Especially handy for loading an old cart where you
141
-     *  know the grand total line item on it
142
-     *
143
-     * @param EE_Line_Item $line_item
144
-     */
145
-    public function set_grand_total_line_item(EE_Line_Item $line_item)
146
-    {
147
-        $this->_grand_total = $line_item;
148
-    }
149
-
150
-
151
-    /**
152
-     * get_cart_from_reg_url_link
153
-     *
154
-     * @param EE_Transaction $transaction
155
-     * @param EE_Session     $session
156
-     * @return EE_Cart
157
-     * @throws EE_Error
158
-     * @throws ReflectionException
159
-     */
160
-    public static function get_cart_from_txn(EE_Transaction $transaction, EE_Session $session = null)
161
-    {
162
-        $grand_total = $transaction->total_line_item();
163
-        $grand_total->get_items();
164
-        $grand_total->tax_descendants();
165
-        return EE_Cart::instance($grand_total, $session);
166
-    }
167
-
168
-
169
-    /**
170
-     * Creates the total line item, and ensures it has its 'tickets' and 'taxes' sub-items
171
-     *
172
-     * @return EE_Line_Item
173
-     * @throws EE_Error
174
-     * @throws ReflectionException
175
-     */
176
-    private function _create_grand_total()
177
-    {
178
-        $this->_grand_total = EEH_Line_Item::create_total_line_item();
179
-        return $this->_grand_total;
180
-    }
181
-
182
-
183
-    /**
184
-     * Gets all the line items of object type Ticket
185
-     *
186
-     * @return EE_Line_Item[]
187
-     * @throws EE_Error
188
-     * @throws ReflectionException
189
-     */
190
-    public function get_tickets(): array
191
-    {
192
-        if ($this->_grand_total === null) {
193
-            return array();
194
-        }
195
-        return EEH_Line_Item::get_ticket_line_items($this->_grand_total);
196
-    }
197
-
198
-
199
-    /**
200
-     * returns the total quantity of tickets in the cart
201
-     *
202
-     * @return int
203
-     * @throws EE_Error
204
-     * @throws ReflectionException
205
-     */
206
-    public function all_ticket_quantity_count(): int
207
-    {
208
-        $tickets = $this->get_tickets();
209
-        if (empty($tickets)) {
210
-            return 0;
211
-        }
212
-        $count = 0;
213
-        foreach ($tickets as $ticket) {
214
-            $count += $ticket->quantity();
215
-        }
216
-        return $count;
217
-    }
218
-
219
-
220
-    /**
221
-     * Gets all the tax line items
222
-     *
223
-     * @return EE_Line_Item[]
224
-     * @throws EE_Error
225
-     * @throws ReflectionException
226
-     */
227
-    public function get_taxes()
228
-    {
229
-        return EEH_Line_Item::get_taxes_subtotal($this->_grand_total)->children();
230
-    }
231
-
232
-
233
-    /**
234
-     * Gets the total line item (which is a parent of all other line items) on this cart
235
-     *
236
-     * @return EE_Line_Item
237
-     * @throws EE_Error
238
-     * @throws ReflectionException
239
-     */
240
-    public function get_grand_total()
241
-    {
242
-        return $this->_grand_total instanceof EE_Line_Item ? $this->_grand_total : $this->_create_grand_total();
243
-    }
244
-
245
-
246
-    /**
247
-     * process items for adding to cart
248
-     * @param EE_Ticket $ticket
249
-     * @param int       $qty
250
-     * @return bool TRUE on success, FALSE on fail
251
-     * @throws EE_Error
252
-     * @throws ReflectionException
253
-     */
254
-    public function add_ticket_to_cart(EE_Ticket $ticket, $qty = 1)
255
-    {
256
-        EEH_Line_Item::add_ticket_purchase($this->get_grand_total(), $ticket, $qty, false);
257
-        return $this->save_cart();
258
-    }
259
-
260
-
261
-    /**
262
-     * get_cart_total_before_tax
263
-     *
264
-     * @return float
265
-     * @throws EE_Error
266
-     * @throws ReflectionException
267
-     */
268
-    public function get_cart_total_before_tax()
269
-    {
270
-        return $this->get_grand_total()->recalculate_pre_tax_total();
271
-    }
272
-
273
-
274
-    /**
275
-     * gets the total amount of tax paid for items in this cart
276
-     *
277
-     * @return float
278
-     * @throws EE_Error
279
-     * @throws ReflectionException
280
-     */
281
-    public function get_applied_taxes()
282
-    {
283
-        return EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
284
-    }
285
-
286
-
287
-    /**
288
-     * Gets the total amount to be paid for the items in the cart, including taxes and other modifiers
289
-     *
290
-     * @return float
291
-     * @throws EE_Error
292
-     * @throws ReflectionException
293
-     */
294
-    public function get_cart_grand_total()
295
-    {
296
-        EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
297
-        return $this->get_grand_total()->total();
298
-    }
299
-
300
-
301
-    /**
302
-     * Gets the total amount to be paid for the items in the cart, including taxes and other modifiers
303
-     *
304
-     * @return float
305
-     * @throws EE_Error
306
-     * @throws ReflectionException
307
-     */
308
-    public function recalculate_all_cart_totals()
309
-    {
310
-        $pre_tax_total = $this->get_cart_total_before_tax();
311
-        $taxes_total = EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
312
-        $this->_grand_total->set_total($pre_tax_total + $taxes_total);
313
-        $this->_grand_total->save_this_and_descendants_to_txn();
314
-        return $this->get_grand_total()->total();
315
-    }
316
-
317
-
318
-    /**
319
-     * deletes an item from the cart
320
-     *
321
-     * @param array|bool|string $line_item_codes
322
-     * @return int on success, FALSE on fail
323
-     * @throws EE_Error
324
-     * @throws ReflectionException
325
-     */
326
-    public function delete_items($line_item_codes = false)
327
-    {
328
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
329
-        return EEH_Line_Item::delete_items($this->get_grand_total(), $line_item_codes);
330
-    }
331
-
332
-
333
-    /**
334
-     * @remove ALL items from cart and zero ALL totals
335
-     * @return bool
336
-     * @throws EE_Error
337
-     * @throws ReflectionException
338
-     */
339
-    public function empty_cart()
340
-    {
341
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
342
-        $this->_grand_total = $this->_create_grand_total();
343
-        return $this->save_cart(true);
344
-    }
345
-
346
-
347
-    /**
348
-     * remove ALL items from cart and delete total as well
349
-     * @return bool
350
-     * @throws EE_Error
351
-     * @throws ReflectionException
352
-     */
353
-    public function delete_cart()
354
-    {
355
-        if ($this->_grand_total instanceof EE_Line_Item) {
356
-            $deleted = EEH_Line_Item::delete_all_child_items($this->_grand_total);
357
-            if ($deleted) {
358
-                $deleted += $this->_grand_total->delete();
359
-                $this->_grand_total = null;
360
-                return true;
361
-            }
362
-        }
363
-        return false;
364
-    }
365
-
366
-
367
-    /**
368
-     * save   cart to session
369
-     * @param bool $apply_taxes
370
-     * @return bool TRUE on success, FALSE on fail
371
-     * @throws EE_Error
372
-     * @throws ReflectionException
373
-     */
374
-    public function save_cart($apply_taxes = true)
375
-    {
376
-        if ($apply_taxes && $this->_grand_total instanceof EE_Line_Item) {
377
-            EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
378
-            // make sure we don't cache the transaction because it can get stale
379
-            if (
380
-                $this->_grand_total->get_one_from_cache('Transaction') instanceof EE_Transaction
381
-                && $this->_grand_total->get_one_from_cache('Transaction')->ID()
382
-            ) {
383
-                $this->_grand_total->clear_cache('Transaction', null, true);
384
-            }
385
-        }
386
-        if ($this->session() instanceof EE_Session) {
387
-            return $this->session()->set_cart($this);
388
-        }
389
-        return false;
390
-    }
391
-
392
-
393
-    public function __wakeup()
394
-    {
395
-        if (! $this->_grand_total instanceof EE_Line_Item && absint($this->_grand_total) !== 0) {
396
-            // $this->_grand_total is actually just an ID, so use it to get the object from the db
397
-            $this->_grand_total = EEM_Line_Item::instance()->get_one_by_ID($this->_grand_total);
398
-        }
399
-    }
400
-
401
-
402
-    /**
403
-     * @return array
404
-     */
405
-    public function __sleep()
406
-    {
407
-        if ($this->_grand_total instanceof EE_Line_Item && $this->_grand_total->ID()) {
408
-            $this->_grand_total = $this->_grand_total->ID();
409
-        }
410
-        return array('_grand_total');
411
-    }
19
+	/**
20
+	 * instance of the EE_Cart object
21
+	 *
22
+	 * @var EE_Cart $_instance
23
+	 */
24
+	private static $_instance;
25
+
26
+	/**
27
+	 * instance of the EE_Session object
28
+	 *
29
+	 * @var EE_Session $_session
30
+	 */
31
+	protected $_session;
32
+
33
+	/**
34
+	 * The total Line item which comprises all the children line-item subtotals,
35
+	 * which in turn each have their line items.
36
+	 * Typically, the line item structure will look like:
37
+	 * grand total
38
+	 * -tickets-sub-total
39
+	 * --ticket1
40
+	 * --ticket2
41
+	 * --...
42
+	 * -taxes-sub-total
43
+	 * --tax1
44
+	 * --tax2
45
+	 *
46
+	 * @var EE_Line_Item
47
+	 */
48
+	private $_grand_total;
49
+
50
+
51
+	/**
52
+	 * singleton method used to instantiate class object
53
+	 * @param EE_Line_Item $grand_total
54
+	 * @param EE_Session   $session
55
+	 * @return EE_Cart
56
+	 * @throws EE_Error
57
+	 * @throws ReflectionException
58
+	 */
59
+	public static function instance(EE_Line_Item $grand_total = null, EE_Session $session = null)
60
+	{
61
+		if ($grand_total instanceof EE_Line_Item && $grand_total->is_total()) {
62
+			self::$_instance = new self($grand_total, $session);
63
+		}
64
+		// or maybe retrieve an existing one ?
65
+		if (! self::$_instance instanceof EE_Cart) {
66
+			// try getting the cart out of the session
67
+			$saved_cart = $session instanceof EE_Session ? $session->cart() : null;
68
+			self::$_instance = $saved_cart instanceof EE_Cart ? $saved_cart : new self($grand_total, $session);
69
+			unset($saved_cart);
70
+		}
71
+		// verify that cart is ok and grand total line item exists
72
+		if (! self::$_instance instanceof EE_Cart || ! self::$_instance->_grand_total instanceof EE_Line_Item) {
73
+			self::$_instance = new self($grand_total, $session);
74
+		}
75
+		self::$_instance->get_grand_total();
76
+		// once everything is all said and done, save the cart to the EE_Session
77
+		add_action('shutdown', array(self::$_instance, 'save_cart'), 90);
78
+		return self::$_instance;
79
+	}
80
+
81
+
82
+	/**
83
+	 * private constructor to prevent direct creation
84
+	 *
85
+	 * @param EE_Line_Item $grand_total
86
+	 * @param EE_Session   $session
87
+	 */
88
+	private function __construct(EE_Line_Item $grand_total = null, EE_Session $session = null)
89
+	{
90
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
91
+		$this->set_session($session);
92
+		if ($grand_total instanceof EE_Line_Item && $grand_total->is_total()) {
93
+			$this->set_grand_total_line_item($grand_total);
94
+		}
95
+	}
96
+
97
+
98
+	/**
99
+	 * Resets the cart completely (whereas empty_cart
100
+	 *
101
+	 * @param EE_Line_Item $grand_total
102
+	 * @param EE_Session   $session
103
+	 * @return EE_Cart
104
+	 * @throws EE_Error
105
+	 * @throws ReflectionException
106
+	 */
107
+	public static function reset(EE_Line_Item $grand_total = null, EE_Session $session = null)
108
+	{
109
+		remove_action('shutdown', array(self::$_instance, 'save_cart'), 90);
110
+		if ($session instanceof EE_Session) {
111
+			$session->reset_cart();
112
+		}
113
+		self::$_instance = null;
114
+		return self::instance($grand_total, $session);
115
+	}
116
+
117
+
118
+	/**
119
+	 * @return EE_Session
120
+	 */
121
+	public function session()
122
+	{
123
+		if (! $this->_session instanceof EE_Session) {
124
+			$this->set_session();
125
+		}
126
+		return $this->_session;
127
+	}
128
+
129
+
130
+	/**
131
+	 * @param EE_Session $session
132
+	 */
133
+	public function set_session(EE_Session $session = null)
134
+	{
135
+		$this->_session = $session instanceof EE_Session ? $session : EE_Registry::instance()->load_core('Session');
136
+	}
137
+
138
+
139
+	/**
140
+	 * Sets the cart to match the line item. Especially handy for loading an old cart where you
141
+	 *  know the grand total line item on it
142
+	 *
143
+	 * @param EE_Line_Item $line_item
144
+	 */
145
+	public function set_grand_total_line_item(EE_Line_Item $line_item)
146
+	{
147
+		$this->_grand_total = $line_item;
148
+	}
149
+
150
+
151
+	/**
152
+	 * get_cart_from_reg_url_link
153
+	 *
154
+	 * @param EE_Transaction $transaction
155
+	 * @param EE_Session     $session
156
+	 * @return EE_Cart
157
+	 * @throws EE_Error
158
+	 * @throws ReflectionException
159
+	 */
160
+	public static function get_cart_from_txn(EE_Transaction $transaction, EE_Session $session = null)
161
+	{
162
+		$grand_total = $transaction->total_line_item();
163
+		$grand_total->get_items();
164
+		$grand_total->tax_descendants();
165
+		return EE_Cart::instance($grand_total, $session);
166
+	}
167
+
168
+
169
+	/**
170
+	 * Creates the total line item, and ensures it has its 'tickets' and 'taxes' sub-items
171
+	 *
172
+	 * @return EE_Line_Item
173
+	 * @throws EE_Error
174
+	 * @throws ReflectionException
175
+	 */
176
+	private function _create_grand_total()
177
+	{
178
+		$this->_grand_total = EEH_Line_Item::create_total_line_item();
179
+		return $this->_grand_total;
180
+	}
181
+
182
+
183
+	/**
184
+	 * Gets all the line items of object type Ticket
185
+	 *
186
+	 * @return EE_Line_Item[]
187
+	 * @throws EE_Error
188
+	 * @throws ReflectionException
189
+	 */
190
+	public function get_tickets(): array
191
+	{
192
+		if ($this->_grand_total === null) {
193
+			return array();
194
+		}
195
+		return EEH_Line_Item::get_ticket_line_items($this->_grand_total);
196
+	}
197
+
198
+
199
+	/**
200
+	 * returns the total quantity of tickets in the cart
201
+	 *
202
+	 * @return int
203
+	 * @throws EE_Error
204
+	 * @throws ReflectionException
205
+	 */
206
+	public function all_ticket_quantity_count(): int
207
+	{
208
+		$tickets = $this->get_tickets();
209
+		if (empty($tickets)) {
210
+			return 0;
211
+		}
212
+		$count = 0;
213
+		foreach ($tickets as $ticket) {
214
+			$count += $ticket->quantity();
215
+		}
216
+		return $count;
217
+	}
218
+
219
+
220
+	/**
221
+	 * Gets all the tax line items
222
+	 *
223
+	 * @return EE_Line_Item[]
224
+	 * @throws EE_Error
225
+	 * @throws ReflectionException
226
+	 */
227
+	public function get_taxes()
228
+	{
229
+		return EEH_Line_Item::get_taxes_subtotal($this->_grand_total)->children();
230
+	}
231
+
232
+
233
+	/**
234
+	 * Gets the total line item (which is a parent of all other line items) on this cart
235
+	 *
236
+	 * @return EE_Line_Item
237
+	 * @throws EE_Error
238
+	 * @throws ReflectionException
239
+	 */
240
+	public function get_grand_total()
241
+	{
242
+		return $this->_grand_total instanceof EE_Line_Item ? $this->_grand_total : $this->_create_grand_total();
243
+	}
244
+
245
+
246
+	/**
247
+	 * process items for adding to cart
248
+	 * @param EE_Ticket $ticket
249
+	 * @param int       $qty
250
+	 * @return bool TRUE on success, FALSE on fail
251
+	 * @throws EE_Error
252
+	 * @throws ReflectionException
253
+	 */
254
+	public function add_ticket_to_cart(EE_Ticket $ticket, $qty = 1)
255
+	{
256
+		EEH_Line_Item::add_ticket_purchase($this->get_grand_total(), $ticket, $qty, false);
257
+		return $this->save_cart();
258
+	}
259
+
260
+
261
+	/**
262
+	 * get_cart_total_before_tax
263
+	 *
264
+	 * @return float
265
+	 * @throws EE_Error
266
+	 * @throws ReflectionException
267
+	 */
268
+	public function get_cart_total_before_tax()
269
+	{
270
+		return $this->get_grand_total()->recalculate_pre_tax_total();
271
+	}
272
+
273
+
274
+	/**
275
+	 * gets the total amount of tax paid for items in this cart
276
+	 *
277
+	 * @return float
278
+	 * @throws EE_Error
279
+	 * @throws ReflectionException
280
+	 */
281
+	public function get_applied_taxes()
282
+	{
283
+		return EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
284
+	}
285
+
286
+
287
+	/**
288
+	 * Gets the total amount to be paid for the items in the cart, including taxes and other modifiers
289
+	 *
290
+	 * @return float
291
+	 * @throws EE_Error
292
+	 * @throws ReflectionException
293
+	 */
294
+	public function get_cart_grand_total()
295
+	{
296
+		EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
297
+		return $this->get_grand_total()->total();
298
+	}
299
+
300
+
301
+	/**
302
+	 * Gets the total amount to be paid for the items in the cart, including taxes and other modifiers
303
+	 *
304
+	 * @return float
305
+	 * @throws EE_Error
306
+	 * @throws ReflectionException
307
+	 */
308
+	public function recalculate_all_cart_totals()
309
+	{
310
+		$pre_tax_total = $this->get_cart_total_before_tax();
311
+		$taxes_total = EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
312
+		$this->_grand_total->set_total($pre_tax_total + $taxes_total);
313
+		$this->_grand_total->save_this_and_descendants_to_txn();
314
+		return $this->get_grand_total()->total();
315
+	}
316
+
317
+
318
+	/**
319
+	 * deletes an item from the cart
320
+	 *
321
+	 * @param array|bool|string $line_item_codes
322
+	 * @return int on success, FALSE on fail
323
+	 * @throws EE_Error
324
+	 * @throws ReflectionException
325
+	 */
326
+	public function delete_items($line_item_codes = false)
327
+	{
328
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
329
+		return EEH_Line_Item::delete_items($this->get_grand_total(), $line_item_codes);
330
+	}
331
+
332
+
333
+	/**
334
+	 * @remove ALL items from cart and zero ALL totals
335
+	 * @return bool
336
+	 * @throws EE_Error
337
+	 * @throws ReflectionException
338
+	 */
339
+	public function empty_cart()
340
+	{
341
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
342
+		$this->_grand_total = $this->_create_grand_total();
343
+		return $this->save_cart(true);
344
+	}
345
+
346
+
347
+	/**
348
+	 * remove ALL items from cart and delete total as well
349
+	 * @return bool
350
+	 * @throws EE_Error
351
+	 * @throws ReflectionException
352
+	 */
353
+	public function delete_cart()
354
+	{
355
+		if ($this->_grand_total instanceof EE_Line_Item) {
356
+			$deleted = EEH_Line_Item::delete_all_child_items($this->_grand_total);
357
+			if ($deleted) {
358
+				$deleted += $this->_grand_total->delete();
359
+				$this->_grand_total = null;
360
+				return true;
361
+			}
362
+		}
363
+		return false;
364
+	}
365
+
366
+
367
+	/**
368
+	 * save   cart to session
369
+	 * @param bool $apply_taxes
370
+	 * @return bool TRUE on success, FALSE on fail
371
+	 * @throws EE_Error
372
+	 * @throws ReflectionException
373
+	 */
374
+	public function save_cart($apply_taxes = true)
375
+	{
376
+		if ($apply_taxes && $this->_grand_total instanceof EE_Line_Item) {
377
+			EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
378
+			// make sure we don't cache the transaction because it can get stale
379
+			if (
380
+				$this->_grand_total->get_one_from_cache('Transaction') instanceof EE_Transaction
381
+				&& $this->_grand_total->get_one_from_cache('Transaction')->ID()
382
+			) {
383
+				$this->_grand_total->clear_cache('Transaction', null, true);
384
+			}
385
+		}
386
+		if ($this->session() instanceof EE_Session) {
387
+			return $this->session()->set_cart($this);
388
+		}
389
+		return false;
390
+	}
391
+
392
+
393
+	public function __wakeup()
394
+	{
395
+		if (! $this->_grand_total instanceof EE_Line_Item && absint($this->_grand_total) !== 0) {
396
+			// $this->_grand_total is actually just an ID, so use it to get the object from the db
397
+			$this->_grand_total = EEM_Line_Item::instance()->get_one_by_ID($this->_grand_total);
398
+		}
399
+	}
400
+
401
+
402
+	/**
403
+	 * @return array
404
+	 */
405
+	public function __sleep()
406
+	{
407
+		if ($this->_grand_total instanceof EE_Line_Item && $this->_grand_total->ID()) {
408
+			$this->_grand_total = $this->_grand_total->ID();
409
+		}
410
+		return array('_grand_total');
411
+	}
412 412
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Export.class.php 1 patch
Indentation   +771 added lines, -771 removed lines patch added patch discarded remove patch
@@ -18,775 +18,775 @@
 block discarded – undo
18 18
  */
19 19
 class EE_Export
20 20
 {
21
-    const option_prefix = 'ee_report_job_';
22
-
23
-    /**
24
-     * instance of the EE_Export object
25
-     */
26
-    private static $_instance = null;
27
-
28
-    /**
29
-     * instance of the EE_CSV object
30
-     * @var EE_CSV
31
-     */
32
-    public $EE_CSV = null;
33
-
34
-
35
-    private $_req_data = array();
36
-
37
-
38
-    /**
39
-     * private constructor to prevent direct creation
40
-     *
41
-     * @param array $request_data
42
-     */
43
-    private function __construct($request_data = array())
44
-    {
45
-        $this->_req_data = $request_data;
46
-        $this->today = date("Y-m-d", time());
47
-        require_once(EE_CLASSES . 'EE_CSV.class.php');
48
-        $this->EE_CSV = EE_CSV::instance();
49
-    }
50
-
51
-
52
-    /**
53
-     * singleton method used to instantiate class object
54
-     *
55
-     * @param array $request_data
56
-     * @return EE_Export
57
-     */
58
-    public static function instance($request_data = array())
59
-    {
60
-        // check if class object is instantiated
61
-        if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Export)) {
62
-            self::$_instance = new self($request_data);
63
-        }
64
-        return self::$_instance;
65
-    }
66
-
67
-
68
-    /**
69
-     * Export Event Espresso data - routes export requests
70
-     * @return void | bool
71
-     */
72
-    public function export()
73
-    {
74
-        // in case of bulk exports, the "actual" action will be in action2, but first check regular action for "export" keyword
75
-        if (isset($this->_req_data['action']) && strpos($this->_req_data['action'], 'export') === false) {
76
-            // check if action2 has export action
77
-            if (isset($this->_req_data['action2']) && strpos($this->_req_data['action2'], 'export') !== false) {
78
-                // whoop! there it is!
79
-                $this->_req_data['action'] = $this->_req_data['action2'];
80
-            }
81
-        }
82
-
83
-        $this->_req_data['export'] = isset($this->_req_data['export']) ? $this->_req_data['export'] : '';
84
-
85
-        switch ($this->_req_data['export']) {
86
-            case 'report':
87
-                switch ($this->_req_data['action']) {
88
-                    case "event":
89
-                    case "export_events":
90
-                    case 'all_event_data':
91
-                        $this->export_all_event_data();
92
-                        break;
93
-
94
-                    case 'registrations_report_for_event':
95
-                        $this->report_registrations_for_event($this->_req_data['EVT_ID']);
96
-                        break;
97
-
98
-                    case 'attendees':
99
-                        $this->export_attendees();
100
-                        break;
101
-
102
-                    case 'categories':
103
-                        $this->export_categories();
104
-                        break;
105
-
106
-                    default:
107
-                        EE_Error::add_error(
108
-                            esc_html__('An error occurred! The requested export report could not be found.', 'event_espresso'),
109
-                            __FILE__,
110
-                            __FUNCTION__,
111
-                            __LINE__
112
-                        );
113
-                        return false;
114
-                        break;
115
-                }
116
-                break; // end of switch export : report
117
-            default:
118
-                break;
119
-        } // end of switch export
120
-
121
-        exit;
122
-    }
123
-
124
-    /**
125
-     * Downloads a CSV file with all the columns, but no data. This should be used for importing
126
-     *
127
-     * @return void kills execution
128
-     * @throws EE_Error
129
-     * @throws ReflectionException
130
-     */
131
-    public function export_sample()
132
-    {
133
-        $event = EEM_Event::instance()->get_one();
134
-        $this->_req_data['EVT_ID'] = $event->ID();
135
-        $this->export_all_event_data();
136
-    }
137
-
138
-
139
-    /**
140
-     * Export data for ALL events
141
-     * @return void
142
-     * @throws EE_Error
143
-     * @throws ReflectionException
144
-     */
145
-    public function export_all_event_data()
146
-    {
147
-        // are any Event IDs set?
148
-        $event_query_params = array();
149
-        $related_models_query_params = array();
150
-        $related_through_reg_query_params = array();
151
-        $datetime_ticket_query_params = array();
152
-        $price_query_params = array();
153
-        $price_type_query_params = array();
154
-        $term_query_params = array();
155
-        $state_country_query_params = array();
156
-        $question_group_query_params = array();
157
-        $question_query_params = array();
158
-        if (isset($this->_req_data['EVT_ID'])) {
159
-            // do we have an array of IDs ?
160
-
161
-            if (is_array($this->_req_data['EVT_ID'])) {
162
-                $EVT_IDs = array_map('sanitize_text_field', $this->_req_data['EVT_ID']);
163
-                $value_to_equal = array('IN', $EVT_IDs);
164
-                $filename = 'events';
165
-            } else {
166
-                // generate regular where = clause
167
-                $EVT_ID = absint($this->_req_data['EVT_ID']);
168
-                $value_to_equal = $EVT_ID;
169
-                $event = EE_Registry::instance()->load_model('Event')->get_one_by_ID($EVT_ID);
170
-
171
-                $filename = 'event-' . ($event instanceof EE_Event ? $event->slug() : esc_html__('unknown', 'event_espresso'));
172
-            }
173
-            $event_query_params[0]['EVT_ID'] = $value_to_equal;
174
-            $related_models_query_params[0]['Event.EVT_ID'] = $value_to_equal;
175
-            $related_through_reg_query_params[0]['Registration.EVT_ID'] = $value_to_equal;
176
-            $datetime_ticket_query_params[0]['Datetime.EVT_ID'] = $value_to_equal;
177
-            $price_query_params[0]['Ticket.Datetime.EVT_ID'] = $value_to_equal;
178
-            $price_type_query_params[0]['Price.Ticket.Datetime.EVT_ID'] = $value_to_equal;
179
-            $term_query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $value_to_equal;
180
-            $state_country_query_params[0]['Venue.Event.EVT_ID'] = $value_to_equal;
181
-            $question_group_query_params[0]['Event.EVT_ID'] = $value_to_equal;
182
-            $question_query_params[0]['Question_Group.Event.EVT_ID'] = $value_to_equal;
183
-        } else {
184
-            $filename = 'all-events';
185
-        }
186
-
187
-
188
-        // array in the format:  table name =>  query where clause
189
-        $models_to_export = array(
190
-            'Event'                   => $event_query_params,
191
-            'Datetime'                => $related_models_query_params,
192
-            'Ticket_Template'         => $price_query_params,
193
-            'Ticket'                  => $datetime_ticket_query_params,
194
-            'Datetime_Ticket'         => $datetime_ticket_query_params,
195
-            'Price_Type'              => $price_type_query_params,
196
-            'Price'                   => $price_query_params,
197
-            'Ticket_Price'            => $price_query_params,
198
-            'Term'                    => $term_query_params,
199
-            'Term_Taxonomy'           => $related_models_query_params,
200
-            'Term_Relationship'       => $related_models_query_params, // model has NO primary key...
201
-            'Country'                 => $state_country_query_params,
202
-            'State'                   => $state_country_query_params,
203
-            'Venue'                   => $related_models_query_params,
204
-            'Event_Venue'             => $related_models_query_params,
205
-            'Question_Group'          => $question_group_query_params,
206
-            'Event_Question_Group'    => $question_group_query_params,
207
-            'Question'                => $question_query_params,
208
-            'Question_Group_Question' => $question_query_params,
209
-            // 'Transaction'=>$related_through_reg_query_params,
210
-            // 'Registration'=>$related_models_query_params,
211
-            // 'Attendee'=>$related_through_reg_query_params,
212
-            // 'Line_Item'=>
213
-
214
-        );
215
-
216
-        $model_data = $this->_get_export_data_for_models($models_to_export);
217
-
218
-        $filename = $this->generate_filename($filename);
219
-
220
-        if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $model_data)) {
221
-            EE_Error::add_error(
222
-                esc_html__(
223
-                    "'An error occurred and the Event details could not be exported from the database.'",
224
-                    "event_espresso"
225
-                ),
226
-                __FILE__,
227
-                __FUNCTION__,
228
-                __LINE__
229
-            );
230
-        }
231
-    }
232
-
233
-    public function report_attendees()
234
-    {
235
-        $attendee_rows = EEM_Attendee::instance()->get_all_wpdb_results(
236
-            array(
237
-                'force_join' => array('State', 'Country'),
238
-                'caps'       => EEM_Base::caps_read_admin,
239
-            )
240
-        );
241
-        $csv_data = array();
242
-        foreach ($attendee_rows as $attendee_row) {
243
-            $csv_row = array();
244
-            foreach (EEM_Attendee::instance()->field_settings() as $field_name => $field_obj) {
245
-                if ($field_name == 'STA_ID') {
246
-                    $state_name_field = EEM_State::instance()->field_settings_for('STA_name');
247
-                    $csv_row[ esc_html__('State', 'event_espresso') ] = $attendee_row[ $state_name_field->get_qualified_column(
248
-                    ) ];
249
-                } elseif ($field_name == 'CNT_ISO') {
250
-                    $country_name_field = EEM_Country::instance()->field_settings_for('CNT_name');
251
-                    $csv_row[ esc_html__(
252
-                        'Country',
253
-                        'event_espresso'
254
-                    ) ] = $attendee_row[ $country_name_field->get_qualified_column() ];
255
-                } else {
256
-                    $csv_row[ $field_obj->get_nicename() ] = $attendee_row[ $field_obj->get_qualified_column() ];
257
-                }
258
-            }
259
-            $csv_data[] = $csv_row;
260
-        }
261
-
262
-        $filename = $this->generate_filename('contact-list-report');
263
-
264
-        $handle = $this->EE_CSV->begin_sending_csv($filename);
265
-        $this->EE_CSV->write_data_array_to_csv($handle, $csv_data);
266
-        $this->EE_CSV->end_sending_csv($handle);
267
-    }
268
-
269
-
270
-    /**
271
-     * Export data for ALL attendees
272
-     * @return void
273
-     * @throws EE_Error
274
-     */
275
-    public function export_attendees()
276
-    {
277
-
278
-        $states_that_have_an_attendee = EEM_State::instance()->get_all(
279
-            array(0 => array('Attendee.ATT_ID' => array('IS NOT NULL')))
280
-        );
281
-        $countries_that_have_an_attendee = EEM_Country::instance()->get_all(
282
-            array(0 => array('Attendee.ATT_ID' => array('IS NOT NULL')))
283
-        );
284
-        // $states_to_export_query_params
285
-        $models_to_export = array(
286
-            'Country'  => array(array('CNT_ISO' => array('IN', array_keys($countries_that_have_an_attendee)))),
287
-            'State'    => array(array('STA_ID' => array('IN', array_keys($states_that_have_an_attendee)))),
288
-            'Attendee' => array(),
289
-        );
290
-
291
-
292
-        $model_data = $this->_get_export_data_for_models($models_to_export);
293
-        $filename = $this->generate_filename('all-attendees');
294
-
295
-        if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $model_data)) {
296
-            EE_Error::add_error(
297
-                esc_html__(
298
-                    'An error occurred and the Attendee data could not be exported from the database.',
299
-                    'event_espresso'
300
-                ),
301
-                __FILE__,
302
-                __FUNCTION__,
303
-                __LINE__
304
-            );
305
-        }
306
-    }
307
-
308
-    /**
309
-     * Shortcut for preparing a database result for display
310
-     *
311
-     * @param EEM_Base $model
312
-     * @param string $field_name
313
-     * @param string $raw_db_value
314
-     * @param bool|string $pretty_schema true to display pretty, a string to use a specific "Schema", or false to
315
-     *                                      NOT display pretty
316
-     * @return string
317
-     * @throws EE_Error
318
-     */
319
-    protected function _prepare_value_from_db_for_display($model, $field_name, $raw_db_value, $pretty_schema = true)
320
-    {
321
-        $field_obj = $model->field_settings_for($field_name);
322
-        $value_on_model_obj = $field_obj->prepare_for_set_from_db($raw_db_value);
323
-        if ($field_obj instanceof EE_Datetime_Field) {
324
-            $field_obj->set_date_format(
325
-                EE_CSV::instance()->get_date_format_for_csv($field_obj->get_date_format($pretty_schema)),
326
-                $pretty_schema
327
-            );
328
-            $field_obj->set_time_format(
329
-                EE_CSV::instance()->get_time_format_for_csv($field_obj->get_time_format($pretty_schema)),
330
-                $pretty_schema
331
-            );
332
-        }
333
-        if ($pretty_schema === true) {
334
-            return $field_obj->prepare_for_pretty_echoing($value_on_model_obj);
335
-        } elseif (is_string($pretty_schema)) {
336
-            return $field_obj->prepare_for_pretty_echoing($value_on_model_obj, $pretty_schema);
337
-        } else {
338
-            return $field_obj->prepare_for_get($value_on_model_obj);
339
-        }
340
-    }
341
-
342
-    /**
343
-     * Export a custom CSV of registration info including: A bunch of the reg fields, the time of the event, the price
344
-     * name, and the questions associated with the registrations
345
-     *
346
-     * @param int $event_id
347
-     * @throws EE_Error
348
-     * @throws ReflectionException
349
-     */
350
-    public function report_registrations_for_event($event_id = null)
351
-    {
352
-        $reg_fields_to_include = array(
353
-            'TXN_ID',
354
-            'ATT_ID',
355
-            'REG_ID',
356
-            'REG_date',
357
-            'REG_code',
358
-            'REG_count',
359
-            'REG_final_price',
360
-
361
-        );
362
-        $att_fields_to_include = array(
363
-            'ATT_fname',
364
-            'ATT_lname',
365
-            'ATT_email',
366
-            'ATT_address',
367
-            'ATT_address2',
368
-            'ATT_city',
369
-            'STA_ID',
370
-            'CNT_ISO',
371
-            'ATT_zip',
372
-            'ATT_phone',
373
-        );
374
-
375
-        $registrations_csv_ready_array = array();
376
-        $reg_model = EE_Registry::instance()->load_model('Registration');
377
-        $query_params = apply_filters(
378
-            'FHEE__EE_Export__report_registration_for_event',
379
-            array(
380
-                array(
381
-                    'OR'                 => array(
382
-                        // don't include registrations from failed or abandoned transactions...
383
-                        'Transaction.STS_ID' => array(
384
-                            'NOT IN',
385
-                            array(EEM_Transaction::failed_status_code, EEM_Transaction::abandoned_status_code),
386
-                        ),
387
-                        // unless the registration is approved, in which case include it regardless of transaction status
388
-                        'STS_ID'             => RegStatus::APPROVED,
389
-                    ),
390
-                    'Ticket.TKT_deleted' => array('IN', array(true, false)),
391
-                ),
392
-                'order_by'   => array('Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'),
393
-                'force_join' => array('Transaction', 'Ticket', 'Attendee'),
394
-                'caps'       => EEM_Base::caps_read_admin,
395
-            ),
396
-            $event_id
397
-        );
398
-        if ($event_id) {
399
-            $query_params[0]['EVT_ID'] = $event_id;
400
-        } else {
401
-            $query_params['force_join'][] = 'Event';
402
-        }
403
-        $registration_rows = $reg_model->get_all_wpdb_results($query_params);
404
-        // get all questions which relate to someone in this group
405
-        $registration_ids = array();
406
-        foreach ($registration_rows as $reg_row) {
407
-            $registration_ids[] = intval($reg_row['Registration.REG_ID']);
408
-        }
409
-        // EEM_Question::instance()->show_next_x_db_queries();
410
-        $questions_for_these_regs_rows = EEM_Question::instance()->get_all_wpdb_results(
411
-            array(array('Answer.REG_ID' => array('IN', $registration_ids)))
412
-        );
413
-        foreach ($registration_rows as $reg_row) {
414
-            if (is_array($reg_row)) {
415
-                $reg_csv_array = array();
416
-                if (! $event_id) {
417
-                    // get the event's name and Id
418
-                    $reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
419
-                        esc_html__('%1$s (%2$s)', 'event_espresso'),
420
-                        $this->_prepare_value_from_db_for_display(
421
-                            EEM_Event::instance(),
422
-                            'EVT_name',
423
-                            $reg_row['Event_CPT.post_title']
424
-                        ),
425
-                        $reg_row['Event_CPT.ID']
426
-                    );
427
-                }
428
-                $is_primary_reg = $reg_row['Registration.REG_count'] == '1' ? true : false;
429
-                /*@var $reg_row EE_Registration */
430
-                foreach ($reg_fields_to_include as $field_name) {
431
-                    $field = $reg_model->field_settings_for($field_name);
432
-                    if ($field_name == 'REG_final_price') {
433
-                        $value = $this->_prepare_value_from_db_for_display(
434
-                            $reg_model,
435
-                            $field_name,
436
-                            $reg_row['Registration.REG_final_price'],
437
-                            'localized_float'
438
-                        );
439
-                    } elseif ($field_name == 'REG_count') {
440
-                        $value = sprintf(
441
-                            esc_html__('%s of %s', 'event_espresso'),
442
-                            $this->_prepare_value_from_db_for_display(
443
-                                $reg_model,
444
-                                'REG_count',
445
-                                $reg_row['Registration.REG_count']
446
-                            ),
447
-                            $this->_prepare_value_from_db_for_display(
448
-                                $reg_model,
449
-                                'REG_group_size',
450
-                                $reg_row['Registration.REG_group_size']
451
-                            )
452
-                        );
453
-                    } elseif ($field_name == 'REG_date') {
454
-                        $value = $this->_prepare_value_from_db_for_display(
455
-                            $reg_model,
456
-                            $field_name,
457
-                            $reg_row['Registration.REG_date'],
458
-                            'no_html'
459
-                        );
460
-                    } else {
461
-                        $value = $this->_prepare_value_from_db_for_display(
462
-                            $reg_model,
463
-                            $field_name,
464
-                            $reg_row[ $field->get_qualified_column() ]
465
-                        );
466
-                    }
467
-                    $reg_csv_array[ $this->_get_column_name_for_field($field) ] = $value;
468
-                    if ($field_name == 'REG_final_price') {
469
-                        // add a column named Currency after the final price
470
-                        $reg_csv_array[ esc_html__("Currency", "event_espresso") ] = EE_Config::instance()->currency->code;
471
-                    }
472
-                }
473
-                // get pretty status
474
-                $stati = EEM_Status::instance()->localized_status(
475
-                    array(
476
-                        $reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
477
-                        $reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
478
-                    ),
479
-                    false,
480
-                    'sentence'
481
-                );
482
-                $reg_csv_array[ esc_html__(
483
-                    "Registration Status",
484
-                    'event_espresso'
485
-                ) ] = $stati[ $reg_row['Registration.STS_ID'] ];
486
-                // get pretty trnasaction status
487
-                $reg_csv_array[ esc_html__(
488
-                    "Transaction Status",
489
-                    'event_espresso'
490
-                ) ] = $stati[ $reg_row['TransactionTable.STS_ID'] ];
491
-                $reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
492
-                    ? $this->_prepare_value_from_db_for_display(
493
-                        EEM_Transaction::instance(),
494
-                        'TXN_total',
495
-                        $reg_row['TransactionTable.TXN_total'],
496
-                        'localized_float'
497
-                    ) : '0.00';
498
-                $reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ] = $is_primary_reg
499
-                    ? $this->_prepare_value_from_db_for_display(
500
-                        EEM_Transaction::instance(),
501
-                        'TXN_paid',
502
-                        $reg_row['TransactionTable.TXN_paid'],
503
-                        'localized_float'
504
-                    ) : '0.00';
505
-                $payment_methods = array();
506
-                $gateway_txn_ids_etc = array();
507
-                $payment_times = array();
508
-                if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
509
-                    $payments_info = EEM_Payment::instance()->get_all_wpdb_results(
510
-                        array(
511
-                            array(
512
-                                'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
513
-                                'STS_ID' => EEM_Payment::status_id_approved,
514
-                            ),
515
-                            'force_join' => array('Payment_Method'),
516
-                        ),
517
-                        ARRAY_A,
518
-                        'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
519
-                    );
520
-                    list($payment_methods, $gateway_txn_ids_etc, $payment_times) = PaymentsInfoCSV::extractPaymentInfo($payments_info);
521
-                }
522
-                $reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(',', $payment_times);
523
-                $reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(",", $payment_methods);
524
-                $reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
525
-                    ',',
526
-                    $gateway_txn_ids_etc
527
-                );
528
-
529
-                // get whether or not the user has checked in
530
-                $reg_csv_array[ esc_html__("Check-Ins", "event_espresso") ] = $reg_model->count_related(
531
-                    $reg_row['Registration.REG_ID'],
532
-                    'Checkin'
533
-                );
534
-                // get ticket of registration and its price
535
-                $ticket_model = EE_Registry::instance()->load_model('Ticket');
536
-                if ($reg_row['Ticket.TKT_ID']) {
537
-                    $ticket_name = $this->_prepare_value_from_db_for_display(
538
-                        $ticket_model,
539
-                        'TKT_name',
540
-                        $reg_row['Ticket.TKT_name']
541
-                    );
542
-                    $datetimes_strings = array();
543
-                    foreach (
544
-                        EEM_Datetime::instance()->get_all_wpdb_results(
545
-                            array(
546
-                            array('Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']),
547
-                            'order_by'                 => array('DTT_EVT_start' => 'ASC'),
548
-                            'default_where_conditions' => 'none',
549
-                            )
550
-                        ) as $datetime
551
-                    ) {
552
-                        $datetimes_strings[] = $this->_prepare_value_from_db_for_display(
553
-                            EEM_Datetime::instance(),
554
-                            'DTT_EVT_start',
555
-                            $datetime['Datetime.DTT_EVT_start']
556
-                        );
557
-                    }
558
-                } else {
559
-                    $ticket_name = esc_html__('Unknown', 'event_espresso');
560
-                    $datetimes_strings = array(esc_html__('Unknown', 'event_espresso'));
561
-                }
562
-                $reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
563
-                $reg_csv_array[ esc_html__("Datetimes of Ticket", "event_espresso") ] = implode(", ", $datetimes_strings);
564
-                // get datetime(s) of registration
565
-
566
-                // add attendee columns
567
-                foreach ($att_fields_to_include as $att_field_name) {
568
-                    $field_obj = EEM_Attendee::instance()->field_settings_for($att_field_name);
569
-                    if ($reg_row['Attendee_CPT.ID']) {
570
-                        if ($att_field_name == 'STA_ID') {
571
-                            $value = EEM_State::instance()->get_var(
572
-                                array(array('STA_ID' => $reg_row['Attendee_Meta.STA_ID'])),
573
-                                'STA_name'
574
-                            );
575
-                        } elseif ($att_field_name == 'CNT_ISO') {
576
-                            $value = EEM_Country::instance()->get_var(
577
-                                array(array('CNT_ISO' => $reg_row['Attendee_Meta.CNT_ISO'])),
578
-                                'CNT_name'
579
-                            );
580
-                        } else {
581
-                            $value = $this->_prepare_value_from_db_for_display(
582
-                                EEM_Attendee::instance(),
583
-                                $att_field_name,
584
-                                $reg_row[ $field_obj->get_qualified_column() ]
585
-                            );
586
-                        }
587
-                    } else {
588
-                        $value = '';
589
-                    }
590
-
591
-                    $reg_csv_array[ $this->_get_column_name_for_field($field_obj) ] = $value;
592
-                }
593
-
594
-                // make sure each registration has the same questions in the same order
595
-                foreach ($questions_for_these_regs_rows as $question_row) {
596
-                    if (! isset($reg_csv_array[ $question_row['Question.QST_admin_label'] ])) {
597
-                        $reg_csv_array[ $question_row['Question.QST_admin_label'] ] = null;
598
-                    }
599
-                }
600
-                // now fill out the questions THEY answered
601
-                foreach (
602
-                    EEM_Answer::instance()->get_all_wpdb_results(
603
-                        array(array('REG_ID' => $reg_row['Registration.REG_ID']), 'force_join' => array('Question'))
604
-                    ) as $answer_row
605
-                ) {
606
-                    /* @var $answer EE_Answer */
607
-                    if ($answer_row['Question.QST_ID']) {
608
-                        $question_label = $this->_prepare_value_from_db_for_display(
609
-                            EEM_Question::instance(),
610
-                            'QST_admin_label',
611
-                            $answer_row['Question.QST_admin_label']
612
-                        );
613
-                    } else {
614
-                        $question_label = sprintf(esc_html__('Question $s', 'event_espresso'), $answer_row['Answer.QST_ID']);
615
-                    }
616
-                    if (isset($answer_row['Question.QST_type']) && $answer_row['Question.QST_type'] == EEM_Question::QST_type_state) {
617
-                        $reg_csv_array[ $question_label ] = EEM_State::instance()->get_state_name_by_ID(
618
-                            $answer_row['Answer.ANS_value']
619
-                        );
620
-                    } else {
621
-                        $reg_csv_array[ $question_label ] = $this->_prepare_value_from_db_for_display(
622
-                            EEM_Answer::instance(),
623
-                            'ANS_value',
624
-                            $answer_row['Answer.ANS_value']
625
-                        );
626
-                    }
627
-                }
628
-                $registrations_csv_ready_array[] = apply_filters(
629
-                    'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
630
-                    $reg_csv_array,
631
-                    $reg_row
632
-                );
633
-            }
634
-        }
635
-
636
-        // if we couldn't export anything, we want to at least show the column headers
637
-        if (empty($registrations_csv_ready_array)) {
638
-            $reg_csv_array = array();
639
-            $model_and_fields_to_include = array(
640
-                'Registration' => $reg_fields_to_include,
641
-                'Attendee'     => $att_fields_to_include,
642
-            );
643
-            foreach ($model_and_fields_to_include as $model_name => $field_list) {
644
-                $model = EE_Registry::instance()->load_model($model_name);
645
-                foreach ($field_list as $field_name) {
646
-                    $field = $model->field_settings_for($field_name);
647
-                    $reg_csv_array[ $this->_get_column_name_for_field(
648
-                        $field
649
-                    ) ] = null;// $registration->get($field->get_name());
650
-                }
651
-            }
652
-            $registrations_csv_ready_array [] = $reg_csv_array;
653
-        }
654
-        if ($event_id) {
655
-            $event_slug = EEM_Event::instance()->get_var(array(array('EVT_ID' => $event_id)), 'EVT_slug');
656
-            if (! $event_slug) {
657
-                $event_slug = esc_html__('unknown', 'event_espresso');
658
-            }
659
-        } else {
660
-            $event_slug = esc_html__('all', 'event_espresso');
661
-        }
662
-        $filename = sprintf("registrations-for-%s", $event_slug);
663
-
664
-        $handle = $this->EE_CSV->begin_sending_csv($filename);
665
-        $this->EE_CSV->write_data_array_to_csv($handle, $registrations_csv_ready_array);
666
-        $this->EE_CSV->end_sending_csv($handle);
667
-    }
668
-
669
-    /**
670
-     * Gets the 'normal' column named for fields
671
-     *
672
-     * @param EE_Model_Field_Base $field
673
-     * @return string
674
-     * @throws EE_Error
675
-     */
676
-    protected function _get_column_name_for_field(EE_Model_Field_Base $field)
677
-    {
678
-        return $field->get_nicename() . "[" . $field->get_name() . "]";
679
-    }
680
-
681
-
682
-    /**
683
-     * Export data for ALL events
684
-     * @return void
685
-     */
686
-    public function export_categories()
687
-    {
688
-        // are any Event IDs set?
689
-        $query_params = array();
690
-        if (isset($this->_req_data['EVT_CAT_ID'])) {
691
-            // do we have an array of IDs ?
692
-            if (is_array($this->_req_data['EVT_CAT_ID'])) {
693
-                // generate an "IN (CSV)" where clause
694
-                $EVT_CAT_IDs = array_map('sanitize_text_field', $this->_req_data['EVT_CAT_ID']);
695
-                $filename = 'event-categories';
696
-                $query_params[0]['term_taxonomy_id'] = array('IN', $EVT_CAT_IDs);
697
-            } else {
698
-                // generate regular where = clause
699
-                $EVT_CAT_ID = absint($this->_req_data['EVT_CAT_ID']);
700
-                $filename = 'event-category#' . $EVT_CAT_ID;
701
-                $query_params[0]['term_taxonomy_id'] = $EVT_CAT_ID;
702
-            }
703
-        } else {
704
-            // no IDs means we will d/l the entire table
705
-            $filename = 'all-categories';
706
-        }
707
-
708
-        $tables_to_export = array(
709
-            'Term_Taxonomy' => $query_params,
710
-        );
711
-
712
-        $table_data = $this->_get_export_data_for_models($tables_to_export);
713
-        $filename = $this->generate_filename($filename);
714
-
715
-        if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $table_data)) {
716
-            EE_Error::add_error(
717
-                esc_html__(
718
-                    'An error occurred and the Category details could not be exported from the database.',
719
-                    'event_espresso'
720
-                ),
721
-                __FILE__,
722
-                __FUNCTION__,
723
-                __LINE__
724
-            );
725
-        }
726
-    }
727
-
728
-
729
-    /**
730
-     * process export name to create a suitable filename
731
-     * @param string - export_name
732
-     * @return string on success, FALSE on fail
733
-     */
734
-    private function generate_filename($export_name = '')
735
-    {
736
-        if ($export_name != '') {
737
-            $filename = get_bloginfo('name') . '-' . $export_name;
738
-            $filename = sanitize_key($filename) . '-' . $this->today;
739
-            return $filename;
740
-        } else {
741
-            EE_Error::add_error(esc_html__("No filename was provided", "event_espresso"), __FILE__, __FUNCTION__, __LINE__);
742
-        }
743
-        return false;
744
-    }
745
-
746
-
747
-    /**
748
-     * recursive function for exporting table data and merging the results with the next results
749
-     * @param array $models_to_export keys are model names (eg 'Event', 'Attendee', etc.) and values are arrays of
750
-     *                                query params @return bool on success, FALSE on fail
751
-     * @throws EE_Error
752
-     * @throws ReflectionException
753
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
754
-     */
755
-    private function _get_export_data_for_models($models_to_export = array())
756
-    {
757
-        $table_data = false;
758
-        if (is_array($models_to_export)) {
759
-            foreach ($models_to_export as $model_name => $query_params) {
760
-                // check for a numerically-indexed array. in that case, $model_name is the value!!
761
-                if (is_int($model_name)) {
762
-                    $model_name = $query_params;
763
-                    $query_params = array();
764
-                }
765
-                $model = EE_Registry::instance()->load_model($model_name);
766
-                $model_objects = $model->get_all($query_params);
767
-
768
-                $table_data[ $model_name ] = array();
769
-                foreach ($model_objects as $model_object) {
770
-                    $model_data_array = array();
771
-                    $fields = $model->field_settings();
772
-                    foreach ($fields as $field) {
773
-                        $column_name = $field->get_nicename() . "[" . $field->get_name() . "]";
774
-                        if ($field instanceof EE_Datetime_Field) {
775
-                            // $field->set_date_format('Y-m-d');
776
-                            // $field->set_time_format('H:i:s');
777
-                            $model_data_array[ $column_name ] = $model_object->get_datetime(
778
-                                $field->get_name(),
779
-                                'Y-m-d',
780
-                                'H:i:s'
781
-                            );
782
-                        } else {
783
-                            $model_data_array[ $column_name ] = $model_object->get($field->get_name());
784
-                        }
785
-                    }
786
-                    $table_data[ $model_name ][] = $model_data_array;
787
-                }
788
-            }
789
-        }
790
-        return $table_data;
791
-    }
21
+	const option_prefix = 'ee_report_job_';
22
+
23
+	/**
24
+	 * instance of the EE_Export object
25
+	 */
26
+	private static $_instance = null;
27
+
28
+	/**
29
+	 * instance of the EE_CSV object
30
+	 * @var EE_CSV
31
+	 */
32
+	public $EE_CSV = null;
33
+
34
+
35
+	private $_req_data = array();
36
+
37
+
38
+	/**
39
+	 * private constructor to prevent direct creation
40
+	 *
41
+	 * @param array $request_data
42
+	 */
43
+	private function __construct($request_data = array())
44
+	{
45
+		$this->_req_data = $request_data;
46
+		$this->today = date("Y-m-d", time());
47
+		require_once(EE_CLASSES . 'EE_CSV.class.php');
48
+		$this->EE_CSV = EE_CSV::instance();
49
+	}
50
+
51
+
52
+	/**
53
+	 * singleton method used to instantiate class object
54
+	 *
55
+	 * @param array $request_data
56
+	 * @return EE_Export
57
+	 */
58
+	public static function instance($request_data = array())
59
+	{
60
+		// check if class object is instantiated
61
+		if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Export)) {
62
+			self::$_instance = new self($request_data);
63
+		}
64
+		return self::$_instance;
65
+	}
66
+
67
+
68
+	/**
69
+	 * Export Event Espresso data - routes export requests
70
+	 * @return void | bool
71
+	 */
72
+	public function export()
73
+	{
74
+		// in case of bulk exports, the "actual" action will be in action2, but first check regular action for "export" keyword
75
+		if (isset($this->_req_data['action']) && strpos($this->_req_data['action'], 'export') === false) {
76
+			// check if action2 has export action
77
+			if (isset($this->_req_data['action2']) && strpos($this->_req_data['action2'], 'export') !== false) {
78
+				// whoop! there it is!
79
+				$this->_req_data['action'] = $this->_req_data['action2'];
80
+			}
81
+		}
82
+
83
+		$this->_req_data['export'] = isset($this->_req_data['export']) ? $this->_req_data['export'] : '';
84
+
85
+		switch ($this->_req_data['export']) {
86
+			case 'report':
87
+				switch ($this->_req_data['action']) {
88
+					case "event":
89
+					case "export_events":
90
+					case 'all_event_data':
91
+						$this->export_all_event_data();
92
+						break;
93
+
94
+					case 'registrations_report_for_event':
95
+						$this->report_registrations_for_event($this->_req_data['EVT_ID']);
96
+						break;
97
+
98
+					case 'attendees':
99
+						$this->export_attendees();
100
+						break;
101
+
102
+					case 'categories':
103
+						$this->export_categories();
104
+						break;
105
+
106
+					default:
107
+						EE_Error::add_error(
108
+							esc_html__('An error occurred! The requested export report could not be found.', 'event_espresso'),
109
+							__FILE__,
110
+							__FUNCTION__,
111
+							__LINE__
112
+						);
113
+						return false;
114
+						break;
115
+				}
116
+				break; // end of switch export : report
117
+			default:
118
+				break;
119
+		} // end of switch export
120
+
121
+		exit;
122
+	}
123
+
124
+	/**
125
+	 * Downloads a CSV file with all the columns, but no data. This should be used for importing
126
+	 *
127
+	 * @return void kills execution
128
+	 * @throws EE_Error
129
+	 * @throws ReflectionException
130
+	 */
131
+	public function export_sample()
132
+	{
133
+		$event = EEM_Event::instance()->get_one();
134
+		$this->_req_data['EVT_ID'] = $event->ID();
135
+		$this->export_all_event_data();
136
+	}
137
+
138
+
139
+	/**
140
+	 * Export data for ALL events
141
+	 * @return void
142
+	 * @throws EE_Error
143
+	 * @throws ReflectionException
144
+	 */
145
+	public function export_all_event_data()
146
+	{
147
+		// are any Event IDs set?
148
+		$event_query_params = array();
149
+		$related_models_query_params = array();
150
+		$related_through_reg_query_params = array();
151
+		$datetime_ticket_query_params = array();
152
+		$price_query_params = array();
153
+		$price_type_query_params = array();
154
+		$term_query_params = array();
155
+		$state_country_query_params = array();
156
+		$question_group_query_params = array();
157
+		$question_query_params = array();
158
+		if (isset($this->_req_data['EVT_ID'])) {
159
+			// do we have an array of IDs ?
160
+
161
+			if (is_array($this->_req_data['EVT_ID'])) {
162
+				$EVT_IDs = array_map('sanitize_text_field', $this->_req_data['EVT_ID']);
163
+				$value_to_equal = array('IN', $EVT_IDs);
164
+				$filename = 'events';
165
+			} else {
166
+				// generate regular where = clause
167
+				$EVT_ID = absint($this->_req_data['EVT_ID']);
168
+				$value_to_equal = $EVT_ID;
169
+				$event = EE_Registry::instance()->load_model('Event')->get_one_by_ID($EVT_ID);
170
+
171
+				$filename = 'event-' . ($event instanceof EE_Event ? $event->slug() : esc_html__('unknown', 'event_espresso'));
172
+			}
173
+			$event_query_params[0]['EVT_ID'] = $value_to_equal;
174
+			$related_models_query_params[0]['Event.EVT_ID'] = $value_to_equal;
175
+			$related_through_reg_query_params[0]['Registration.EVT_ID'] = $value_to_equal;
176
+			$datetime_ticket_query_params[0]['Datetime.EVT_ID'] = $value_to_equal;
177
+			$price_query_params[0]['Ticket.Datetime.EVT_ID'] = $value_to_equal;
178
+			$price_type_query_params[0]['Price.Ticket.Datetime.EVT_ID'] = $value_to_equal;
179
+			$term_query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $value_to_equal;
180
+			$state_country_query_params[0]['Venue.Event.EVT_ID'] = $value_to_equal;
181
+			$question_group_query_params[0]['Event.EVT_ID'] = $value_to_equal;
182
+			$question_query_params[0]['Question_Group.Event.EVT_ID'] = $value_to_equal;
183
+		} else {
184
+			$filename = 'all-events';
185
+		}
186
+
187
+
188
+		// array in the format:  table name =>  query where clause
189
+		$models_to_export = array(
190
+			'Event'                   => $event_query_params,
191
+			'Datetime'                => $related_models_query_params,
192
+			'Ticket_Template'         => $price_query_params,
193
+			'Ticket'                  => $datetime_ticket_query_params,
194
+			'Datetime_Ticket'         => $datetime_ticket_query_params,
195
+			'Price_Type'              => $price_type_query_params,
196
+			'Price'                   => $price_query_params,
197
+			'Ticket_Price'            => $price_query_params,
198
+			'Term'                    => $term_query_params,
199
+			'Term_Taxonomy'           => $related_models_query_params,
200
+			'Term_Relationship'       => $related_models_query_params, // model has NO primary key...
201
+			'Country'                 => $state_country_query_params,
202
+			'State'                   => $state_country_query_params,
203
+			'Venue'                   => $related_models_query_params,
204
+			'Event_Venue'             => $related_models_query_params,
205
+			'Question_Group'          => $question_group_query_params,
206
+			'Event_Question_Group'    => $question_group_query_params,
207
+			'Question'                => $question_query_params,
208
+			'Question_Group_Question' => $question_query_params,
209
+			// 'Transaction'=>$related_through_reg_query_params,
210
+			// 'Registration'=>$related_models_query_params,
211
+			// 'Attendee'=>$related_through_reg_query_params,
212
+			// 'Line_Item'=>
213
+
214
+		);
215
+
216
+		$model_data = $this->_get_export_data_for_models($models_to_export);
217
+
218
+		$filename = $this->generate_filename($filename);
219
+
220
+		if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $model_data)) {
221
+			EE_Error::add_error(
222
+				esc_html__(
223
+					"'An error occurred and the Event details could not be exported from the database.'",
224
+					"event_espresso"
225
+				),
226
+				__FILE__,
227
+				__FUNCTION__,
228
+				__LINE__
229
+			);
230
+		}
231
+	}
232
+
233
+	public function report_attendees()
234
+	{
235
+		$attendee_rows = EEM_Attendee::instance()->get_all_wpdb_results(
236
+			array(
237
+				'force_join' => array('State', 'Country'),
238
+				'caps'       => EEM_Base::caps_read_admin,
239
+			)
240
+		);
241
+		$csv_data = array();
242
+		foreach ($attendee_rows as $attendee_row) {
243
+			$csv_row = array();
244
+			foreach (EEM_Attendee::instance()->field_settings() as $field_name => $field_obj) {
245
+				if ($field_name == 'STA_ID') {
246
+					$state_name_field = EEM_State::instance()->field_settings_for('STA_name');
247
+					$csv_row[ esc_html__('State', 'event_espresso') ] = $attendee_row[ $state_name_field->get_qualified_column(
248
+					) ];
249
+				} elseif ($field_name == 'CNT_ISO') {
250
+					$country_name_field = EEM_Country::instance()->field_settings_for('CNT_name');
251
+					$csv_row[ esc_html__(
252
+						'Country',
253
+						'event_espresso'
254
+					) ] = $attendee_row[ $country_name_field->get_qualified_column() ];
255
+				} else {
256
+					$csv_row[ $field_obj->get_nicename() ] = $attendee_row[ $field_obj->get_qualified_column() ];
257
+				}
258
+			}
259
+			$csv_data[] = $csv_row;
260
+		}
261
+
262
+		$filename = $this->generate_filename('contact-list-report');
263
+
264
+		$handle = $this->EE_CSV->begin_sending_csv($filename);
265
+		$this->EE_CSV->write_data_array_to_csv($handle, $csv_data);
266
+		$this->EE_CSV->end_sending_csv($handle);
267
+	}
268
+
269
+
270
+	/**
271
+	 * Export data for ALL attendees
272
+	 * @return void
273
+	 * @throws EE_Error
274
+	 */
275
+	public function export_attendees()
276
+	{
277
+
278
+		$states_that_have_an_attendee = EEM_State::instance()->get_all(
279
+			array(0 => array('Attendee.ATT_ID' => array('IS NOT NULL')))
280
+		);
281
+		$countries_that_have_an_attendee = EEM_Country::instance()->get_all(
282
+			array(0 => array('Attendee.ATT_ID' => array('IS NOT NULL')))
283
+		);
284
+		// $states_to_export_query_params
285
+		$models_to_export = array(
286
+			'Country'  => array(array('CNT_ISO' => array('IN', array_keys($countries_that_have_an_attendee)))),
287
+			'State'    => array(array('STA_ID' => array('IN', array_keys($states_that_have_an_attendee)))),
288
+			'Attendee' => array(),
289
+		);
290
+
291
+
292
+		$model_data = $this->_get_export_data_for_models($models_to_export);
293
+		$filename = $this->generate_filename('all-attendees');
294
+
295
+		if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $model_data)) {
296
+			EE_Error::add_error(
297
+				esc_html__(
298
+					'An error occurred and the Attendee data could not be exported from the database.',
299
+					'event_espresso'
300
+				),
301
+				__FILE__,
302
+				__FUNCTION__,
303
+				__LINE__
304
+			);
305
+		}
306
+	}
307
+
308
+	/**
309
+	 * Shortcut for preparing a database result for display
310
+	 *
311
+	 * @param EEM_Base $model
312
+	 * @param string $field_name
313
+	 * @param string $raw_db_value
314
+	 * @param bool|string $pretty_schema true to display pretty, a string to use a specific "Schema", or false to
315
+	 *                                      NOT display pretty
316
+	 * @return string
317
+	 * @throws EE_Error
318
+	 */
319
+	protected function _prepare_value_from_db_for_display($model, $field_name, $raw_db_value, $pretty_schema = true)
320
+	{
321
+		$field_obj = $model->field_settings_for($field_name);
322
+		$value_on_model_obj = $field_obj->prepare_for_set_from_db($raw_db_value);
323
+		if ($field_obj instanceof EE_Datetime_Field) {
324
+			$field_obj->set_date_format(
325
+				EE_CSV::instance()->get_date_format_for_csv($field_obj->get_date_format($pretty_schema)),
326
+				$pretty_schema
327
+			);
328
+			$field_obj->set_time_format(
329
+				EE_CSV::instance()->get_time_format_for_csv($field_obj->get_time_format($pretty_schema)),
330
+				$pretty_schema
331
+			);
332
+		}
333
+		if ($pretty_schema === true) {
334
+			return $field_obj->prepare_for_pretty_echoing($value_on_model_obj);
335
+		} elseif (is_string($pretty_schema)) {
336
+			return $field_obj->prepare_for_pretty_echoing($value_on_model_obj, $pretty_schema);
337
+		} else {
338
+			return $field_obj->prepare_for_get($value_on_model_obj);
339
+		}
340
+	}
341
+
342
+	/**
343
+	 * Export a custom CSV of registration info including: A bunch of the reg fields, the time of the event, the price
344
+	 * name, and the questions associated with the registrations
345
+	 *
346
+	 * @param int $event_id
347
+	 * @throws EE_Error
348
+	 * @throws ReflectionException
349
+	 */
350
+	public function report_registrations_for_event($event_id = null)
351
+	{
352
+		$reg_fields_to_include = array(
353
+			'TXN_ID',
354
+			'ATT_ID',
355
+			'REG_ID',
356
+			'REG_date',
357
+			'REG_code',
358
+			'REG_count',
359
+			'REG_final_price',
360
+
361
+		);
362
+		$att_fields_to_include = array(
363
+			'ATT_fname',
364
+			'ATT_lname',
365
+			'ATT_email',
366
+			'ATT_address',
367
+			'ATT_address2',
368
+			'ATT_city',
369
+			'STA_ID',
370
+			'CNT_ISO',
371
+			'ATT_zip',
372
+			'ATT_phone',
373
+		);
374
+
375
+		$registrations_csv_ready_array = array();
376
+		$reg_model = EE_Registry::instance()->load_model('Registration');
377
+		$query_params = apply_filters(
378
+			'FHEE__EE_Export__report_registration_for_event',
379
+			array(
380
+				array(
381
+					'OR'                 => array(
382
+						// don't include registrations from failed or abandoned transactions...
383
+						'Transaction.STS_ID' => array(
384
+							'NOT IN',
385
+							array(EEM_Transaction::failed_status_code, EEM_Transaction::abandoned_status_code),
386
+						),
387
+						// unless the registration is approved, in which case include it regardless of transaction status
388
+						'STS_ID'             => RegStatus::APPROVED,
389
+					),
390
+					'Ticket.TKT_deleted' => array('IN', array(true, false)),
391
+				),
392
+				'order_by'   => array('Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'),
393
+				'force_join' => array('Transaction', 'Ticket', 'Attendee'),
394
+				'caps'       => EEM_Base::caps_read_admin,
395
+			),
396
+			$event_id
397
+		);
398
+		if ($event_id) {
399
+			$query_params[0]['EVT_ID'] = $event_id;
400
+		} else {
401
+			$query_params['force_join'][] = 'Event';
402
+		}
403
+		$registration_rows = $reg_model->get_all_wpdb_results($query_params);
404
+		// get all questions which relate to someone in this group
405
+		$registration_ids = array();
406
+		foreach ($registration_rows as $reg_row) {
407
+			$registration_ids[] = intval($reg_row['Registration.REG_ID']);
408
+		}
409
+		// EEM_Question::instance()->show_next_x_db_queries();
410
+		$questions_for_these_regs_rows = EEM_Question::instance()->get_all_wpdb_results(
411
+			array(array('Answer.REG_ID' => array('IN', $registration_ids)))
412
+		);
413
+		foreach ($registration_rows as $reg_row) {
414
+			if (is_array($reg_row)) {
415
+				$reg_csv_array = array();
416
+				if (! $event_id) {
417
+					// get the event's name and Id
418
+					$reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
419
+						esc_html__('%1$s (%2$s)', 'event_espresso'),
420
+						$this->_prepare_value_from_db_for_display(
421
+							EEM_Event::instance(),
422
+							'EVT_name',
423
+							$reg_row['Event_CPT.post_title']
424
+						),
425
+						$reg_row['Event_CPT.ID']
426
+					);
427
+				}
428
+				$is_primary_reg = $reg_row['Registration.REG_count'] == '1' ? true : false;
429
+				/*@var $reg_row EE_Registration */
430
+				foreach ($reg_fields_to_include as $field_name) {
431
+					$field = $reg_model->field_settings_for($field_name);
432
+					if ($field_name == 'REG_final_price') {
433
+						$value = $this->_prepare_value_from_db_for_display(
434
+							$reg_model,
435
+							$field_name,
436
+							$reg_row['Registration.REG_final_price'],
437
+							'localized_float'
438
+						);
439
+					} elseif ($field_name == 'REG_count') {
440
+						$value = sprintf(
441
+							esc_html__('%s of %s', 'event_espresso'),
442
+							$this->_prepare_value_from_db_for_display(
443
+								$reg_model,
444
+								'REG_count',
445
+								$reg_row['Registration.REG_count']
446
+							),
447
+							$this->_prepare_value_from_db_for_display(
448
+								$reg_model,
449
+								'REG_group_size',
450
+								$reg_row['Registration.REG_group_size']
451
+							)
452
+						);
453
+					} elseif ($field_name == 'REG_date') {
454
+						$value = $this->_prepare_value_from_db_for_display(
455
+							$reg_model,
456
+							$field_name,
457
+							$reg_row['Registration.REG_date'],
458
+							'no_html'
459
+						);
460
+					} else {
461
+						$value = $this->_prepare_value_from_db_for_display(
462
+							$reg_model,
463
+							$field_name,
464
+							$reg_row[ $field->get_qualified_column() ]
465
+						);
466
+					}
467
+					$reg_csv_array[ $this->_get_column_name_for_field($field) ] = $value;
468
+					if ($field_name == 'REG_final_price') {
469
+						// add a column named Currency after the final price
470
+						$reg_csv_array[ esc_html__("Currency", "event_espresso") ] = EE_Config::instance()->currency->code;
471
+					}
472
+				}
473
+				// get pretty status
474
+				$stati = EEM_Status::instance()->localized_status(
475
+					array(
476
+						$reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
477
+						$reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
478
+					),
479
+					false,
480
+					'sentence'
481
+				);
482
+				$reg_csv_array[ esc_html__(
483
+					"Registration Status",
484
+					'event_espresso'
485
+				) ] = $stati[ $reg_row['Registration.STS_ID'] ];
486
+				// get pretty trnasaction status
487
+				$reg_csv_array[ esc_html__(
488
+					"Transaction Status",
489
+					'event_espresso'
490
+				) ] = $stati[ $reg_row['TransactionTable.STS_ID'] ];
491
+				$reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
492
+					? $this->_prepare_value_from_db_for_display(
493
+						EEM_Transaction::instance(),
494
+						'TXN_total',
495
+						$reg_row['TransactionTable.TXN_total'],
496
+						'localized_float'
497
+					) : '0.00';
498
+				$reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ] = $is_primary_reg
499
+					? $this->_prepare_value_from_db_for_display(
500
+						EEM_Transaction::instance(),
501
+						'TXN_paid',
502
+						$reg_row['TransactionTable.TXN_paid'],
503
+						'localized_float'
504
+					) : '0.00';
505
+				$payment_methods = array();
506
+				$gateway_txn_ids_etc = array();
507
+				$payment_times = array();
508
+				if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
509
+					$payments_info = EEM_Payment::instance()->get_all_wpdb_results(
510
+						array(
511
+							array(
512
+								'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
513
+								'STS_ID' => EEM_Payment::status_id_approved,
514
+							),
515
+							'force_join' => array('Payment_Method'),
516
+						),
517
+						ARRAY_A,
518
+						'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
519
+					);
520
+					list($payment_methods, $gateway_txn_ids_etc, $payment_times) = PaymentsInfoCSV::extractPaymentInfo($payments_info);
521
+				}
522
+				$reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(',', $payment_times);
523
+				$reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(",", $payment_methods);
524
+				$reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
525
+					',',
526
+					$gateway_txn_ids_etc
527
+				);
528
+
529
+				// get whether or not the user has checked in
530
+				$reg_csv_array[ esc_html__("Check-Ins", "event_espresso") ] = $reg_model->count_related(
531
+					$reg_row['Registration.REG_ID'],
532
+					'Checkin'
533
+				);
534
+				// get ticket of registration and its price
535
+				$ticket_model = EE_Registry::instance()->load_model('Ticket');
536
+				if ($reg_row['Ticket.TKT_ID']) {
537
+					$ticket_name = $this->_prepare_value_from_db_for_display(
538
+						$ticket_model,
539
+						'TKT_name',
540
+						$reg_row['Ticket.TKT_name']
541
+					);
542
+					$datetimes_strings = array();
543
+					foreach (
544
+						EEM_Datetime::instance()->get_all_wpdb_results(
545
+							array(
546
+							array('Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']),
547
+							'order_by'                 => array('DTT_EVT_start' => 'ASC'),
548
+							'default_where_conditions' => 'none',
549
+							)
550
+						) as $datetime
551
+					) {
552
+						$datetimes_strings[] = $this->_prepare_value_from_db_for_display(
553
+							EEM_Datetime::instance(),
554
+							'DTT_EVT_start',
555
+							$datetime['Datetime.DTT_EVT_start']
556
+						);
557
+					}
558
+				} else {
559
+					$ticket_name = esc_html__('Unknown', 'event_espresso');
560
+					$datetimes_strings = array(esc_html__('Unknown', 'event_espresso'));
561
+				}
562
+				$reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
563
+				$reg_csv_array[ esc_html__("Datetimes of Ticket", "event_espresso") ] = implode(", ", $datetimes_strings);
564
+				// get datetime(s) of registration
565
+
566
+				// add attendee columns
567
+				foreach ($att_fields_to_include as $att_field_name) {
568
+					$field_obj = EEM_Attendee::instance()->field_settings_for($att_field_name);
569
+					if ($reg_row['Attendee_CPT.ID']) {
570
+						if ($att_field_name == 'STA_ID') {
571
+							$value = EEM_State::instance()->get_var(
572
+								array(array('STA_ID' => $reg_row['Attendee_Meta.STA_ID'])),
573
+								'STA_name'
574
+							);
575
+						} elseif ($att_field_name == 'CNT_ISO') {
576
+							$value = EEM_Country::instance()->get_var(
577
+								array(array('CNT_ISO' => $reg_row['Attendee_Meta.CNT_ISO'])),
578
+								'CNT_name'
579
+							);
580
+						} else {
581
+							$value = $this->_prepare_value_from_db_for_display(
582
+								EEM_Attendee::instance(),
583
+								$att_field_name,
584
+								$reg_row[ $field_obj->get_qualified_column() ]
585
+							);
586
+						}
587
+					} else {
588
+						$value = '';
589
+					}
590
+
591
+					$reg_csv_array[ $this->_get_column_name_for_field($field_obj) ] = $value;
592
+				}
593
+
594
+				// make sure each registration has the same questions in the same order
595
+				foreach ($questions_for_these_regs_rows as $question_row) {
596
+					if (! isset($reg_csv_array[ $question_row['Question.QST_admin_label'] ])) {
597
+						$reg_csv_array[ $question_row['Question.QST_admin_label'] ] = null;
598
+					}
599
+				}
600
+				// now fill out the questions THEY answered
601
+				foreach (
602
+					EEM_Answer::instance()->get_all_wpdb_results(
603
+						array(array('REG_ID' => $reg_row['Registration.REG_ID']), 'force_join' => array('Question'))
604
+					) as $answer_row
605
+				) {
606
+					/* @var $answer EE_Answer */
607
+					if ($answer_row['Question.QST_ID']) {
608
+						$question_label = $this->_prepare_value_from_db_for_display(
609
+							EEM_Question::instance(),
610
+							'QST_admin_label',
611
+							$answer_row['Question.QST_admin_label']
612
+						);
613
+					} else {
614
+						$question_label = sprintf(esc_html__('Question $s', 'event_espresso'), $answer_row['Answer.QST_ID']);
615
+					}
616
+					if (isset($answer_row['Question.QST_type']) && $answer_row['Question.QST_type'] == EEM_Question::QST_type_state) {
617
+						$reg_csv_array[ $question_label ] = EEM_State::instance()->get_state_name_by_ID(
618
+							$answer_row['Answer.ANS_value']
619
+						);
620
+					} else {
621
+						$reg_csv_array[ $question_label ] = $this->_prepare_value_from_db_for_display(
622
+							EEM_Answer::instance(),
623
+							'ANS_value',
624
+							$answer_row['Answer.ANS_value']
625
+						);
626
+					}
627
+				}
628
+				$registrations_csv_ready_array[] = apply_filters(
629
+					'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
630
+					$reg_csv_array,
631
+					$reg_row
632
+				);
633
+			}
634
+		}
635
+
636
+		// if we couldn't export anything, we want to at least show the column headers
637
+		if (empty($registrations_csv_ready_array)) {
638
+			$reg_csv_array = array();
639
+			$model_and_fields_to_include = array(
640
+				'Registration' => $reg_fields_to_include,
641
+				'Attendee'     => $att_fields_to_include,
642
+			);
643
+			foreach ($model_and_fields_to_include as $model_name => $field_list) {
644
+				$model = EE_Registry::instance()->load_model($model_name);
645
+				foreach ($field_list as $field_name) {
646
+					$field = $model->field_settings_for($field_name);
647
+					$reg_csv_array[ $this->_get_column_name_for_field(
648
+						$field
649
+					) ] = null;// $registration->get($field->get_name());
650
+				}
651
+			}
652
+			$registrations_csv_ready_array [] = $reg_csv_array;
653
+		}
654
+		if ($event_id) {
655
+			$event_slug = EEM_Event::instance()->get_var(array(array('EVT_ID' => $event_id)), 'EVT_slug');
656
+			if (! $event_slug) {
657
+				$event_slug = esc_html__('unknown', 'event_espresso');
658
+			}
659
+		} else {
660
+			$event_slug = esc_html__('all', 'event_espresso');
661
+		}
662
+		$filename = sprintf("registrations-for-%s", $event_slug);
663
+
664
+		$handle = $this->EE_CSV->begin_sending_csv($filename);
665
+		$this->EE_CSV->write_data_array_to_csv($handle, $registrations_csv_ready_array);
666
+		$this->EE_CSV->end_sending_csv($handle);
667
+	}
668
+
669
+	/**
670
+	 * Gets the 'normal' column named for fields
671
+	 *
672
+	 * @param EE_Model_Field_Base $field
673
+	 * @return string
674
+	 * @throws EE_Error
675
+	 */
676
+	protected function _get_column_name_for_field(EE_Model_Field_Base $field)
677
+	{
678
+		return $field->get_nicename() . "[" . $field->get_name() . "]";
679
+	}
680
+
681
+
682
+	/**
683
+	 * Export data for ALL events
684
+	 * @return void
685
+	 */
686
+	public function export_categories()
687
+	{
688
+		// are any Event IDs set?
689
+		$query_params = array();
690
+		if (isset($this->_req_data['EVT_CAT_ID'])) {
691
+			// do we have an array of IDs ?
692
+			if (is_array($this->_req_data['EVT_CAT_ID'])) {
693
+				// generate an "IN (CSV)" where clause
694
+				$EVT_CAT_IDs = array_map('sanitize_text_field', $this->_req_data['EVT_CAT_ID']);
695
+				$filename = 'event-categories';
696
+				$query_params[0]['term_taxonomy_id'] = array('IN', $EVT_CAT_IDs);
697
+			} else {
698
+				// generate regular where = clause
699
+				$EVT_CAT_ID = absint($this->_req_data['EVT_CAT_ID']);
700
+				$filename = 'event-category#' . $EVT_CAT_ID;
701
+				$query_params[0]['term_taxonomy_id'] = $EVT_CAT_ID;
702
+			}
703
+		} else {
704
+			// no IDs means we will d/l the entire table
705
+			$filename = 'all-categories';
706
+		}
707
+
708
+		$tables_to_export = array(
709
+			'Term_Taxonomy' => $query_params,
710
+		);
711
+
712
+		$table_data = $this->_get_export_data_for_models($tables_to_export);
713
+		$filename = $this->generate_filename($filename);
714
+
715
+		if (! $this->EE_CSV->export_multiple_model_data_to_csv($filename, $table_data)) {
716
+			EE_Error::add_error(
717
+				esc_html__(
718
+					'An error occurred and the Category details could not be exported from the database.',
719
+					'event_espresso'
720
+				),
721
+				__FILE__,
722
+				__FUNCTION__,
723
+				__LINE__
724
+			);
725
+		}
726
+	}
727
+
728
+
729
+	/**
730
+	 * process export name to create a suitable filename
731
+	 * @param string - export_name
732
+	 * @return string on success, FALSE on fail
733
+	 */
734
+	private function generate_filename($export_name = '')
735
+	{
736
+		if ($export_name != '') {
737
+			$filename = get_bloginfo('name') . '-' . $export_name;
738
+			$filename = sanitize_key($filename) . '-' . $this->today;
739
+			return $filename;
740
+		} else {
741
+			EE_Error::add_error(esc_html__("No filename was provided", "event_espresso"), __FILE__, __FUNCTION__, __LINE__);
742
+		}
743
+		return false;
744
+	}
745
+
746
+
747
+	/**
748
+	 * recursive function for exporting table data and merging the results with the next results
749
+	 * @param array $models_to_export keys are model names (eg 'Event', 'Attendee', etc.) and values are arrays of
750
+	 *                                query params @return bool on success, FALSE on fail
751
+	 * @throws EE_Error
752
+	 * @throws ReflectionException
753
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
754
+	 */
755
+	private function _get_export_data_for_models($models_to_export = array())
756
+	{
757
+		$table_data = false;
758
+		if (is_array($models_to_export)) {
759
+			foreach ($models_to_export as $model_name => $query_params) {
760
+				// check for a numerically-indexed array. in that case, $model_name is the value!!
761
+				if (is_int($model_name)) {
762
+					$model_name = $query_params;
763
+					$query_params = array();
764
+				}
765
+				$model = EE_Registry::instance()->load_model($model_name);
766
+				$model_objects = $model->get_all($query_params);
767
+
768
+				$table_data[ $model_name ] = array();
769
+				foreach ($model_objects as $model_object) {
770
+					$model_data_array = array();
771
+					$fields = $model->field_settings();
772
+					foreach ($fields as $field) {
773
+						$column_name = $field->get_nicename() . "[" . $field->get_name() . "]";
774
+						if ($field instanceof EE_Datetime_Field) {
775
+							// $field->set_date_format('Y-m-d');
776
+							// $field->set_time_format('H:i:s');
777
+							$model_data_array[ $column_name ] = $model_object->get_datetime(
778
+								$field->get_name(),
779
+								'Y-m-d',
780
+								'H:i:s'
781
+							);
782
+						} else {
783
+							$model_data_array[ $column_name ] = $model_object->get($field->get_name());
784
+						}
785
+					}
786
+					$table_data[ $model_name ][] = $model_data_array;
787
+				}
788
+			}
789
+		}
790
+		return $table_data;
791
+	}
792 792
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Event.class.php 1 patch
Indentation   +1634 added lines, -1634 removed lines patch added patch discarded remove patch
@@ -16,1641 +16,1641 @@
 block discarded – undo
16 16
  */
17 17
 class EE_Event extends EE_CPT_Base implements EEI_Line_Item_Object, EEI_Admin_Links, EEI_Has_Icon, EEI_Event
18 18
 {
19
-    /**
20
-     * cached value for the the logical active status for the event
21
-     *
22
-     * @see get_active_status()
23
-     * @var string
24
-     */
25
-    protected $_active_status = '';
26
-
27
-    /**
28
-     * This is just used for caching the Primary Datetime for the Event on initial retrieval
29
-     *
30
-     * @var EE_Datetime
31
-     */
32
-    protected $_Primary_Datetime;
33
-
34
-    /**
35
-     * @var EventSpacesCalculator $available_spaces_calculator
36
-     */
37
-    protected $available_spaces_calculator;
38
-
39
-
40
-    /**
41
-     * @param array  $props_n_values          incoming values
42
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
43
-     *                                        used.)
44
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
45
-     *                                        date_format and the second value is the time format
46
-     * @return EE_Event
47
-     * @throws EE_Error
48
-     * @throws ReflectionException
49
-     */
50
-    public static function new_instance($props_n_values = [], $timezone = '', $date_formats = []): EE_Event
51
-    {
52
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
53
-        return $has_object ?: new self($props_n_values, false, $timezone, $date_formats);
54
-    }
55
-
56
-
57
-    /**
58
-     * @param array  $props_n_values  incoming values from the database
59
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
60
-     *                                the website will be used.
61
-     * @return EE_Event
62
-     * @throws EE_Error
63
-     * @throws ReflectionException
64
-     */
65
-    public static function new_instance_from_db($props_n_values = [], $timezone = ''): EE_Event
66
-    {
67
-        return new self($props_n_values, true, $timezone);
68
-    }
69
-
70
-
71
-    /**
72
-     * @return EventSpacesCalculator
73
-     * @throws EE_Error
74
-     * @throws ReflectionException
75
-     */
76
-    public function getAvailableSpacesCalculator(): EventSpacesCalculator
77
-    {
78
-        if (! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
79
-            $this->available_spaces_calculator = new EventSpacesCalculator($this);
80
-        }
81
-        return $this->available_spaces_calculator;
82
-    }
83
-
84
-
85
-    /**
86
-     * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
87
-     *
88
-     * @param string $field_name
89
-     * @param mixed  $field_value
90
-     * @param bool   $use_default
91
-     * @throws EE_Error
92
-     * @throws ReflectionException
93
-     */
94
-    public function set($field_name, $field_value, $use_default = false)
95
-    {
96
-        switch ($field_name) {
97
-            case 'status':
98
-                $this->set_status($field_value, $use_default);
99
-                break;
100
-            default:
101
-                parent::set($field_name, $field_value, $use_default);
102
-        }
103
-    }
104
-
105
-
106
-    /**
107
-     *    set_status
108
-     * Checks if event status is being changed to SOLD OUT
109
-     * and updates event meta data with previous event status
110
-     * so that we can revert things if/when the event is no longer sold out
111
-     *
112
-     * @param string $status
113
-     * @param bool   $use_default
114
-     * @return void
115
-     * @throws EE_Error
116
-     * @throws ReflectionException
117
-     */
118
-    public function set_status($status = '', $use_default = false)
119
-    {
120
-        // if nothing is set, and we aren't explicitly wanting to reset the status, then just leave
121
-        if (empty($status) && ! $use_default) {
122
-            return;
123
-        }
124
-        // get current Event status
125
-        $old_status = $this->status();
126
-        // if status has changed
127
-        if ($old_status !== $status) {
128
-            // TO sold_out
129
-            if ($status === EEM_Event::sold_out) {
130
-                // save the previous event status so that we can revert if the event is no longer sold out
131
-                $this->add_post_meta('_previous_event_status', $old_status);
132
-                do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $status);
133
-                // OR FROM  sold_out
134
-            } elseif ($old_status === EEM_Event::sold_out) {
135
-                $this->delete_post_meta('_previous_event_status');
136
-                do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $status);
137
-            }
138
-            // clear out the active status so that it gets reset the next time it is requested
139
-            $this->_active_status = null;
140
-            // update status
141
-            parent::set('status', $status, $use_default);
142
-            do_action('AHEE__EE_Event__set_status__after_update', $this);
143
-            return;
144
-        }
145
-        // even though the old value matches the new value, it's still good to
146
-        // allow the parent set method to have a say
147
-        parent::set('status', $status, $use_default);
148
-    }
149
-
150
-
151
-    /**
152
-     * Gets all the datetimes for this event
153
-     *
154
-     * @param array|null $query_params
155
-     * @return EE_Base_Class[]|EE_Datetime[]
156
-     * @throws EE_Error
157
-     * @throws ReflectionException
158
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
159
-     */
160
-    public function datetimes(?array $query_params = []): array
161
-    {
162
-        return $this->get_many_related('Datetime', $query_params);
163
-    }
164
-
165
-
166
-    /**
167
-     * Gets all the datetimes for this event that are currently ACTIVE,
168
-     * meaning the datetime has started and has not yet ended.
169
-     *
170
-     * @param int|null   $start_date   timestamp to use for event date start time, defaults to NOW unless set to 0
171
-     * @param array|null $query_params will recursively replace default values
172
-     * @throws EE_Error
173
-     * @throws ReflectionException
174
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
175
-     */
176
-    public function activeDatetimes(?int $start_date, ?array $query_params = []): array
177
-    {
178
-        // if start date is null, then use current time
179
-        $start_date = $start_date ?? time();
180
-        $where      = [];
181
-        if ($start_date) {
182
-            $where['DTT_EVT_start'] = ['<', $start_date];
183
-            $where['DTT_EVT_end']   = ['>', time()];
184
-        }
185
-        $query_params = array_replace_recursive(
186
-            [
187
-                $where,
188
-                'order_by' => ['DTT_EVT_start' => 'ASC'],
189
-            ],
190
-            $query_params
191
-        );
192
-        return $this->get_many_related('Datetime', $query_params);
193
-    }
194
-
195
-
196
-    /**
197
-     * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
198
-     *
199
-     * @return EE_Base_Class[]|EE_Datetime[]
200
-     * @throws EE_Error
201
-     * @throws ReflectionException
202
-     */
203
-    public function datetimes_in_chronological_order(): array
204
-    {
205
-        return $this->get_many_related('Datetime', ['order_by' => ['DTT_EVT_start' => 'ASC']]);
206
-    }
207
-
208
-
209
-    /**
210
-     * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
211
-     * @todo We should probably UNSET timezone on the EEM_Datetime model
212
-     * after running our query, so that this timezone isn't set for EVERY query
213
-     * on EEM_Datetime for the rest of the request, no?
214
-     *
215
-     * @param bool     $show_expired whether or not to include expired events
216
-     * @param bool     $show_deleted whether or not to include deleted events
217
-     * @param int|null $limit
218
-     * @return EE_Datetime[]
219
-     * @throws EE_Error
220
-     * @throws ReflectionException
221
-     */
222
-    public function datetimes_ordered(bool $show_expired = true, bool $show_deleted = false, ?int $limit = null): array
223
-    {
224
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
225
-            $this->ID(),
226
-            $show_expired,
227
-            $show_deleted,
228
-            $limit
229
-        );
230
-    }
231
-
232
-
233
-    /**
234
-     * Returns one related datetime. Mostly only used by some legacy code.
235
-     *
236
-     * @return EE_Base_Class|EE_Datetime
237
-     * @throws EE_Error
238
-     * @throws ReflectionException
239
-     */
240
-    public function first_datetime(): EE_Datetime
241
-    {
242
-        return $this->get_first_related('Datetime');
243
-    }
244
-
245
-
246
-    /**
247
-     * Returns the 'primary' datetime for the event
248
-     *
249
-     * @param bool $try_to_exclude_expired
250
-     * @param bool $try_to_exclude_deleted
251
-     * @return EE_Datetime|null
252
-     * @throws EE_Error
253
-     * @throws ReflectionException
254
-     */
255
-    public function primary_datetime(
256
-        bool $try_to_exclude_expired = true,
257
-        bool $try_to_exclude_deleted = true
258
-    ): ?EE_Datetime {
259
-        if (! empty($this->_Primary_Datetime)) {
260
-            return $this->_Primary_Datetime;
261
-        }
262
-        $this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
263
-            $this->ID(),
264
-            $try_to_exclude_expired,
265
-            $try_to_exclude_deleted
266
-        );
267
-        return $this->_Primary_Datetime;
268
-    }
269
-
270
-
271
-    /**
272
-     * Gets all the tickets available for purchase of this event
273
-     *
274
-     * @param array|null $query_params
275
-     * @return EE_Base_Class[]|EE_Ticket[]
276
-     * @throws EE_Error
277
-     * @throws ReflectionException
278
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
279
-     */
280
-    public function tickets(?array $query_params = []): array
281
-    {
282
-        // first get all datetimes
283
-        $datetimes = $this->datetimes_ordered();
284
-        if (! $datetimes) {
285
-            return [];
286
-        }
287
-        $datetime_ids = [];
288
-        foreach ($datetimes as $datetime) {
289
-            $datetime_ids[] = $datetime->ID();
290
-        }
291
-        $where_params = ['Datetime.DTT_ID' => ['IN', $datetime_ids]];
292
-        // if incoming $query_params has where conditions let's merge but not override existing.
293
-        if (is_array($query_params) && isset($query_params[0])) {
294
-            $where_params = array_merge($query_params[0], $where_params);
295
-            unset($query_params[0]);
296
-        }
297
-        // now add $where_params to $query_params
298
-        $query_params[0] = $where_params;
299
-        return EEM_Ticket::instance()->get_all($query_params);
300
-    }
301
-
302
-
303
-    /**
304
-     * get all unexpired not-trashed tickets
305
-     *
306
-     * @return EE_Ticket[]
307
-     * @throws EE_Error
308
-     * @throws ReflectionException
309
-     */
310
-    public function active_tickets(): array
311
-    {
312
-        return $this->tickets(
313
-            [
314
-                [
315
-                    'TKT_end_date' => ['>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')],
316
-                    'TKT_deleted'  => false,
317
-                ],
318
-            ]
319
-        );
320
-    }
321
-
322
-
323
-    /**
324
-     * @return int
325
-     * @throws EE_Error
326
-     * @throws ReflectionException
327
-     */
328
-    public function additional_limit(): int
329
-    {
330
-        return $this->get('EVT_additional_limit');
331
-    }
332
-
333
-
334
-    /**
335
-     * @return bool
336
-     * @throws EE_Error
337
-     * @throws ReflectionException
338
-     */
339
-    public function allow_overflow(): bool
340
-    {
341
-        return $this->get('EVT_allow_overflow');
342
-    }
343
-
344
-
345
-    /**
346
-     * @return string
347
-     * @throws EE_Error
348
-     * @throws ReflectionException
349
-     */
350
-    public function created(): string
351
-    {
352
-        return $this->get('EVT_created');
353
-    }
354
-
355
-
356
-    /**
357
-     * @return string
358
-     * @throws EE_Error
359
-     * @throws ReflectionException
360
-     */
361
-    public function description(): string
362
-    {
363
-        return $this->get('EVT_desc');
364
-    }
365
-
366
-
367
-    /**
368
-     * Runs do_shortcode and wpautop on the description
369
-     *
370
-     * @return string of html
371
-     * @throws EE_Error
372
-     * @throws ReflectionException
373
-     */
374
-    public function description_filtered(): string
375
-    {
376
-        return $this->get_pretty('EVT_desc');
377
-    }
378
-
379
-
380
-    /**
381
-     * @return bool
382
-     * @throws EE_Error
383
-     * @throws ReflectionException
384
-     */
385
-    public function display_description(): bool
386
-    {
387
-        return $this->get('EVT_display_desc');
388
-    }
389
-
390
-
391
-    /**
392
-     * @return bool
393
-     * @throws EE_Error
394
-     * @throws ReflectionException
395
-     */
396
-    public function display_ticket_selector(): bool
397
-    {
398
-        return (bool) $this->get('EVT_display_ticket_selector');
399
-    }
400
-
401
-
402
-    /**
403
-     * @return string
404
-     * @throws EE_Error
405
-     * @throws ReflectionException
406
-     */
407
-    public function external_url(): ?string
408
-    {
409
-        return $this->get('EVT_external_URL') ?? '';
410
-    }
411
-
412
-
413
-    /**
414
-     * @return bool
415
-     * @throws EE_Error
416
-     * @throws ReflectionException
417
-     */
418
-    public function member_only(): bool
419
-    {
420
-        return $this->get('EVT_member_only');
421
-    }
422
-
423
-
424
-    /**
425
-     * @return string
426
-     * @throws EE_Error
427
-     * @throws ReflectionException
428
-     */
429
-    public function phone(): string
430
-    {
431
-        return $this->get('EVT_phone');
432
-    }
433
-
434
-
435
-    /**
436
-     * @return string
437
-     * @throws EE_Error
438
-     * @throws ReflectionException
439
-     */
440
-    public function modified(): string
441
-    {
442
-        return $this->get('EVT_modified');
443
-    }
444
-
445
-
446
-    /**
447
-     * @return string
448
-     * @throws EE_Error
449
-     * @throws ReflectionException
450
-     */
451
-    public function name(): string
452
-    {
453
-        return $this->get('EVT_name');
454
-    }
455
-
456
-
457
-    /**
458
-     * @return int
459
-     * @throws EE_Error
460
-     * @throws ReflectionException
461
-     */
462
-    public function order(): int
463
-    {
464
-        return $this->get('EVT_order');
465
-    }
466
-
467
-
468
-    /**
469
-     * @return string
470
-     * @throws EE_Error
471
-     * @throws ReflectionException
472
-     */
473
-    public function default_registration_status(): string
474
-    {
475
-        $event_default_registration_status = $this->get('EVT_default_registration_status');
476
-        return ! empty($event_default_registration_status)
477
-            ? $event_default_registration_status
478
-            : EE_Registry::instance()->CFG->registration->default_STS_ID;
479
-    }
480
-
481
-
482
-    /**
483
-     * @param int|null    $num_words
484
-     * @param string|null $more
485
-     * @param bool        $not_full_desc
486
-     * @return string
487
-     * @throws EE_Error
488
-     * @throws ReflectionException
489
-     */
490
-    public function short_description(?int $num_words = 55, string $more = null, bool $not_full_desc = false): string
491
-    {
492
-        $short_desc = $this->get('EVT_short_desc');
493
-        if (! empty($short_desc) || $not_full_desc) {
494
-            return $short_desc;
495
-        }
496
-        $full_desc = $this->get('EVT_desc');
497
-        return wp_trim_words($full_desc, $num_words, $more);
498
-    }
499
-
500
-
501
-    /**
502
-     * @return string
503
-     * @throws EE_Error
504
-     * @throws ReflectionException
505
-     */
506
-    public function slug(): string
507
-    {
508
-        return $this->get('EVT_slug');
509
-    }
510
-
511
-
512
-    /**
513
-     * @return string
514
-     * @throws EE_Error
515
-     * @throws ReflectionException
516
-     */
517
-    public function timezone_string(): string
518
-    {
519
-        return $this->get('EVT_timezone_string');
520
-    }
521
-
522
-
523
-    /**
524
-     * @return string
525
-     * @throws EE_Error
526
-     * @throws ReflectionException
527
-     * @deprecated
528
-     */
529
-    public function visible_on(): string
530
-    {
531
-        EE_Error::doing_it_wrong(
532
-            __METHOD__,
533
-            esc_html__(
534
-                'This method has been deprecated and there is no replacement for it.',
535
-                'event_espresso'
536
-            ),
537
-            '5.0.0.rc.002'
538
-        );
539
-        return $this->get('EVT_visible_on');
540
-    }
541
-
542
-
543
-    /**
544
-     * @return int
545
-     * @throws EE_Error
546
-     * @throws ReflectionException
547
-     */
548
-    public function wp_user(): int
549
-    {
550
-        return (int) $this->get('EVT_wp_user');
551
-    }
552
-
553
-
554
-    /**
555
-     * @return bool
556
-     * @throws EE_Error
557
-     * @throws ReflectionException
558
-     */
559
-    public function donations(): bool
560
-    {
561
-        return $this->get('EVT_donations');
562
-    }
563
-
564
-
565
-    /**
566
-     * @param int $limit
567
-     * @throws EE_Error
568
-     * @throws ReflectionException
569
-     */
570
-    public function set_additional_limit(int $limit)
571
-    {
572
-        $this->set('EVT_additional_limit', $limit);
573
-    }
574
-
575
-
576
-    /**
577
-     * @param $created
578
-     * @throws EE_Error
579
-     * @throws ReflectionException
580
-     */
581
-    public function set_created($created)
582
-    {
583
-        $this->set('EVT_created', $created);
584
-    }
585
-
586
-
587
-    /**
588
-     * @param $desc
589
-     * @throws EE_Error
590
-     * @throws ReflectionException
591
-     */
592
-    public function set_description($desc)
593
-    {
594
-        $this->set('EVT_desc', $desc);
595
-    }
596
-
597
-
598
-    /**
599
-     * @param $display_desc
600
-     * @throws EE_Error
601
-     * @throws ReflectionException
602
-     */
603
-    public function set_display_description($display_desc)
604
-    {
605
-        $this->set('EVT_display_desc', $display_desc);
606
-    }
607
-
608
-
609
-    /**
610
-     * @param $display_ticket_selector
611
-     * @throws EE_Error
612
-     * @throws ReflectionException
613
-     */
614
-    public function set_display_ticket_selector($display_ticket_selector)
615
-    {
616
-        $this->set('EVT_display_ticket_selector', $display_ticket_selector);
617
-    }
618
-
619
-
620
-    /**
621
-     * @param $external_url
622
-     * @throws EE_Error
623
-     * @throws ReflectionException
624
-     */
625
-    public function set_external_url($external_url)
626
-    {
627
-        $this->set('EVT_external_URL', $external_url);
628
-    }
629
-
630
-
631
-    /**
632
-     * @param $member_only
633
-     * @throws EE_Error
634
-     * @throws ReflectionException
635
-     */
636
-    public function set_member_only($member_only)
637
-    {
638
-        $this->set('EVT_member_only', $member_only);
639
-    }
640
-
641
-
642
-    /**
643
-     * @param $event_phone
644
-     * @throws EE_Error
645
-     * @throws ReflectionException
646
-     */
647
-    public function set_event_phone($event_phone)
648
-    {
649
-        $this->set('EVT_phone', $event_phone);
650
-    }
651
-
652
-
653
-    /**
654
-     * @param $modified
655
-     * @throws EE_Error
656
-     * @throws ReflectionException
657
-     */
658
-    public function set_modified($modified)
659
-    {
660
-        $this->set('EVT_modified', $modified);
661
-    }
662
-
663
-
664
-    /**
665
-     * @param $name
666
-     * @throws EE_Error
667
-     * @throws ReflectionException
668
-     */
669
-    public function set_name($name)
670
-    {
671
-        $this->set('EVT_name', $name);
672
-    }
673
-
674
-
675
-    /**
676
-     * @param $order
677
-     * @throws EE_Error
678
-     * @throws ReflectionException
679
-     */
680
-    public function set_order($order)
681
-    {
682
-        $this->set('EVT_order', $order);
683
-    }
684
-
685
-
686
-    /**
687
-     * @param $short_desc
688
-     * @throws EE_Error
689
-     * @throws ReflectionException
690
-     */
691
-    public function set_short_description($short_desc)
692
-    {
693
-        $this->set('EVT_short_desc', $short_desc);
694
-    }
695
-
696
-
697
-    /**
698
-     * @param $slug
699
-     * @throws EE_Error
700
-     * @throws ReflectionException
701
-     */
702
-    public function set_slug($slug)
703
-    {
704
-        $this->set('EVT_slug', $slug);
705
-    }
706
-
707
-
708
-    /**
709
-     * @param $timezone_string
710
-     * @throws EE_Error
711
-     * @throws ReflectionException
712
-     */
713
-    public function set_timezone_string($timezone_string)
714
-    {
715
-        $this->set('EVT_timezone_string', $timezone_string);
716
-    }
717
-
718
-
719
-    /**
720
-     * @param $visible_on
721
-     * @throws EE_Error
722
-     * @throws ReflectionException
723
-     * @deprecated
724
-     */
725
-    public function set_visible_on($visible_on)
726
-    {
727
-        EE_Error::doing_it_wrong(
728
-            __METHOD__,
729
-            esc_html__(
730
-                'This method has been deprecated and there is no replacement for it.',
731
-                'event_espresso'
732
-            ),
733
-            '5.0.0.rc.002'
734
-        );
735
-        $this->set('EVT_visible_on', $visible_on);
736
-    }
737
-
738
-
739
-    /**
740
-     * @param $wp_user
741
-     * @throws EE_Error
742
-     * @throws ReflectionException
743
-     */
744
-    public function set_wp_user($wp_user)
745
-    {
746
-        $this->set('EVT_wp_user', $wp_user);
747
-    }
748
-
749
-
750
-    /**
751
-     * @param $default_registration_status
752
-     * @throws EE_Error
753
-     * @throws ReflectionException
754
-     */
755
-    public function set_default_registration_status($default_registration_status)
756
-    {
757
-        $this->set('EVT_default_registration_status', $default_registration_status);
758
-    }
759
-
760
-
761
-    /**
762
-     * @param $donations
763
-     * @throws EE_Error
764
-     * @throws ReflectionException
765
-     */
766
-    public function set_donations($donations)
767
-    {
768
-        $this->set('EVT_donations', $donations);
769
-    }
770
-
771
-
772
-    /**
773
-     * Adds a venue to this event
774
-     *
775
-     * @param int|EE_Venue /int $venue_id_or_obj
776
-     * @return EE_Base_Class|EE_Venue
777
-     * @throws EE_Error
778
-     * @throws ReflectionException
779
-     */
780
-    public function add_venue($venue_id_or_obj): EE_Venue
781
-    {
782
-        return $this->_add_relation_to($venue_id_or_obj, 'Venue');
783
-    }
784
-
785
-
786
-    /**
787
-     * Removes a venue from the event
788
-     *
789
-     * @param EE_Venue /int $venue_id_or_obj
790
-     * @return EE_Base_Class|EE_Venue
791
-     * @throws EE_Error
792
-     * @throws ReflectionException
793
-     */
794
-    public function remove_venue($venue_id_or_obj): EE_Venue
795
-    {
796
-        $venue_id_or_obj = ! empty($venue_id_or_obj) ? $venue_id_or_obj : $this->venue();
797
-        return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
798
-    }
799
-
800
-
801
-    /**
802
-     * Gets the venue related to the event. May provide additional $query_params if desired
803
-     *
804
-     * @param array $query_params
805
-     * @return int
806
-     * @throws EE_Error
807
-     * @throws ReflectionException
808
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
809
-     */
810
-    public function venue_ID(array $query_params = []): int
811
-    {
812
-        $venue = $this->get_first_related('Venue', $query_params);
813
-        return $venue instanceof EE_Venue ? $venue->ID() : 0;
814
-    }
815
-
816
-
817
-    /**
818
-     * Gets the venue related to the event. May provide additional $query_params if desired
819
-     *
820
-     * @param array $query_params
821
-     * @return EE_Base_Class|EE_Venue|null
822
-     * @throws EE_Error
823
-     * @throws ReflectionException
824
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
825
-     */
826
-    public function venue(array $query_params = []): ?EE_Venue
827
-    {
828
-        return $this->get_first_related('Venue', $query_params);
829
-    }
830
-
831
-
832
-    /**
833
-     * @param array $query_params
834
-     * @return EE_Base_Class[]|EE_Venue[]
835
-     * @throws EE_Error
836
-     * @throws ReflectionException
837
-     * @deprecated 5.0.0.p
838
-     */
839
-    public function venues(array $query_params = []): array
840
-    {
841
-        $venue = $this->venue($query_params);
842
-        return $venue instanceof EE_Venue ? [$venue] : [];
843
-    }
844
-
845
-
846
-    /**
847
-     * check if event id is present and if event is published
848
-     *
849
-     * @return boolean true yes, false no
850
-     * @throws EE_Error
851
-     * @throws ReflectionException
852
-     */
853
-    private function _has_ID_and_is_published(): bool
854
-    {
855
-        // first check if event id is present and not NULL,
856
-        // then check if this event is published (or any of the equivalent "published" statuses)
857
-        return
858
-            $this->ID() && $this->ID() !== null
859
-            && (
860
-                $this->status() === 'publish'
861
-                || $this->status() === EEM_Event::sold_out
862
-                || $this->status() === EEM_Event::postponed
863
-                || $this->status() === EEM_Event::cancelled
864
-            );
865
-    }
866
-
867
-
868
-    /**
869
-     * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
870
-     *
871
-     * @return boolean true yes, false no
872
-     * @throws EE_Error
873
-     * @throws ReflectionException
874
-     */
875
-    public function is_upcoming(): bool
876
-    {
877
-        // check if event id is present and if this event is published
878
-        if ($this->is_inactive()) {
879
-            return false;
880
-        }
881
-        // set initial value
882
-        $upcoming = false;
883
-        // next let's get all datetimes and loop through them
884
-        $datetimes = $this->datetimes_in_chronological_order();
885
-        foreach ($datetimes as $datetime) {
886
-            if ($datetime instanceof EE_Datetime) {
887
-                // if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
888
-                if ($datetime->is_expired()) {
889
-                    continue;
890
-                }
891
-                // if this dtt is active then we return false.
892
-                if ($datetime->is_active()) {
893
-                    return false;
894
-                }
895
-                // otherwise let's check upcoming status
896
-                $upcoming = $datetime->is_upcoming();
897
-            }
898
-        }
899
-        return $upcoming;
900
-    }
901
-
902
-
903
-    /**
904
-     * @return bool
905
-     * @throws EE_Error
906
-     * @throws ReflectionException
907
-     */
908
-    public function is_active(): bool
909
-    {
910
-        // check if event id is present and if this event is published
911
-        if ($this->is_inactive()) {
912
-            return false;
913
-        }
914
-        // set initial value
915
-        $active = false;
916
-        // next let's get all datetimes and loop through them
917
-        $datetimes = $this->datetimes_in_chronological_order();
918
-        foreach ($datetimes as $datetime) {
919
-            if ($datetime instanceof EE_Datetime) {
920
-                // if this dtt is expired then we continue cause one of the other datetimes might be active.
921
-                if ($datetime->is_expired()) {
922
-                    continue;
923
-                }
924
-                // if this dtt is upcoming then we return false.
925
-                if ($datetime->is_upcoming()) {
926
-                    return false;
927
-                }
928
-                // otherwise let's check active status
929
-                $active = $datetime->is_active();
930
-            }
931
-        }
932
-        return $active;
933
-    }
934
-
935
-
936
-    /**
937
-     * @return bool
938
-     * @throws EE_Error
939
-     * @throws ReflectionException
940
-     */
941
-    public function is_expired(): bool
942
-    {
943
-        // check if event id is present and if this event is published
944
-        if ($this->is_inactive()) {
945
-            return false;
946
-        }
947
-        // set initial value
948
-        $expired = false;
949
-        // first let's get all datetimes and loop through them
950
-        $datetimes = $this->datetimes_in_chronological_order();
951
-        foreach ($datetimes as $datetime) {
952
-            if ($datetime instanceof EE_Datetime) {
953
-                // if this dtt is upcoming or active then we return false.
954
-                if ($datetime->is_upcoming() || $datetime->is_active()) {
955
-                    return false;
956
-                }
957
-                // otherwise let's check active status
958
-                $expired = $datetime->is_expired();
959
-            }
960
-        }
961
-        return $expired;
962
-    }
963
-
964
-
965
-    /**
966
-     * @return bool
967
-     * @throws EE_Error
968
-     * @throws ReflectionException
969
-     */
970
-    public function is_inactive(): bool
971
-    {
972
-        // check if event id is present and if this event is published
973
-        if ($this->_has_ID_and_is_published()) {
974
-            return false;
975
-        }
976
-        return true;
977
-    }
978
-
979
-
980
-    /**
981
-     * calculate spaces remaining based on "saleable" tickets
982
-     *
983
-     * @param array|null $tickets
984
-     * @param bool       $filtered
985
-     * @return int|float
986
-     * @throws EE_Error
987
-     * @throws DomainException
988
-     * @throws UnexpectedEntityException
989
-     * @throws ReflectionException
990
-     */
991
-    public function spaces_remaining(?array $tickets = [], ?bool $filtered = true)
992
-    {
993
-        $this->getAvailableSpacesCalculator()->setActiveTickets($tickets);
994
-        $spaces_remaining = $this->getAvailableSpacesCalculator()->spacesRemaining();
995
-        return $filtered
996
-            ? apply_filters(
997
-                'FHEE_EE_Event__spaces_remaining',
998
-                $spaces_remaining,
999
-                $this,
1000
-                $tickets
1001
-            )
1002
-            : $spaces_remaining;
1003
-    }
1004
-
1005
-
1006
-    /**
1007
-     *    perform_sold_out_status_check
1008
-     *    checks all of this event's datetime  reg_limit - sold values to determine if ANY datetimes have spaces
1009
-     *    available... if NOT, then the event status will get toggled to 'sold_out'
1010
-     *
1011
-     * @return bool    return the ACTUAL sold out state.
1012
-     * @throws EE_Error
1013
-     * @throws DomainException
1014
-     * @throws UnexpectedEntityException
1015
-     * @throws ReflectionException
1016
-     */
1017
-    public function perform_sold_out_status_check(): bool
1018
-    {
1019
-        // get all tickets
1020
-        $tickets     = $this->tickets(
1021
-            [
1022
-                'default_where_conditions' => 'none',
1023
-                'order_by'                 => ['TKT_qty' => 'ASC'],
1024
-            ]
1025
-        );
1026
-        $all_expired = true;
1027
-        foreach ($tickets as $ticket) {
1028
-            if (! $ticket->is_expired()) {
1029
-                $all_expired = false;
1030
-                break;
1031
-            }
1032
-        }
1033
-        // if all the tickets are just expired, then don't update the event status to sold out
1034
-        if ($all_expired) {
1035
-            return true;
1036
-        }
1037
-        $spaces_remaining = $this->spaces_remaining($tickets);
1038
-        if ($spaces_remaining < 1) {
1039
-            if ($this->status() !== EEM_CPT_Base::post_status_private) {
1040
-                $this->set_status(EEM_Event::sold_out);
1041
-                $this->save();
1042
-            }
1043
-            $sold_out = true;
1044
-        } else {
1045
-            $sold_out = false;
1046
-            // was event previously marked as sold out ?
1047
-            if ($this->status() === EEM_Event::sold_out) {
1048
-                // revert status to previous value, if it was set
1049
-                $previous_event_status = $this->get_post_meta('_previous_event_status', true);
1050
-                if ($previous_event_status) {
1051
-                    $this->set_status($previous_event_status);
1052
-                    $this->save();
1053
-                }
1054
-            }
1055
-        }
1056
-        do_action('AHEE__EE_Event__perform_sold_out_status_check__end', $this, $sold_out, $spaces_remaining, $tickets);
1057
-        return $sold_out;
1058
-    }
1059
-
1060
-
1061
-    /**
1062
-     * This returns the total remaining spaces for sale on this event.
1063
-     *
1064
-     * @return int|float
1065
-     * @throws EE_Error
1066
-     * @throws DomainException
1067
-     * @throws UnexpectedEntityException
1068
-     * @throws ReflectionException
1069
-     * @uses EE_Event::total_available_spaces()
1070
-     */
1071
-    public function spaces_remaining_for_sale()
1072
-    {
1073
-        return $this->total_available_spaces(true);
1074
-    }
1075
-
1076
-
1077
-    /**
1078
-     * This returns the total spaces available for an event
1079
-     * while considering all the quantities on the tickets and the reg limits
1080
-     * on the datetimes attached to this event.
1081
-     *
1082
-     * @param bool $consider_sold   Whether to consider any tickets that have already sold in our calculation.
1083
-     *                              If this is false, then we return the most tickets that could ever be sold
1084
-     *                              for this event with the datetime and tickets setup on the event under optimal
1085
-     *                              selling conditions.  Otherwise we return a live calculation of spaces available
1086
-     *                              based on tickets sold.  Depending on setup and stage of sales, this
1087
-     *                              may appear to equal remaining tickets.  However, the more tickets are
1088
-     *                              sold out, the more accurate the "live" total is.
1089
-     * @return int|float
1090
-     * @throws EE_Error
1091
-     * @throws DomainException
1092
-     * @throws UnexpectedEntityException
1093
-     * @throws ReflectionException
1094
-     */
1095
-    public function total_available_spaces(bool $consider_sold = false)
1096
-    {
1097
-        $spaces_available = $consider_sold
1098
-            ? $this->getAvailableSpacesCalculator()->spacesRemaining()
1099
-            : $this->getAvailableSpacesCalculator()->totalSpacesAvailable();
1100
-        return apply_filters(
1101
-            'FHEE_EE_Event__total_available_spaces__spaces_available',
1102
-            $spaces_available,
1103
-            $this,
1104
-            $this->getAvailableSpacesCalculator()->getDatetimes(),
1105
-            $this->getAvailableSpacesCalculator()->getActiveTickets()
1106
-        );
1107
-    }
1108
-
1109
-
1110
-    /**
1111
-     * Checks if the event is set to sold out
1112
-     *
1113
-     * @param bool $actual  whether or not to perform calculations to not only figure the
1114
-     *                      actual status but also to flip the status if necessary to sold
1115
-     *                      out If false, we just check the existing status of the event
1116
-     * @return boolean
1117
-     * @throws EE_Error
1118
-     * @throws ReflectionException
1119
-     */
1120
-    public function is_sold_out(bool $actual = false): bool
1121
-    {
1122
-        if (! $actual) {
1123
-            return $this->status() === EEM_Event::sold_out;
1124
-        }
1125
-        return $this->perform_sold_out_status_check();
1126
-    }
1127
-
1128
-
1129
-    /**
1130
-     * Checks if the event is marked as postponed
1131
-     *
1132
-     * @return boolean
1133
-     */
1134
-    public function is_postponed(): bool
1135
-    {
1136
-        return $this->status() === EEM_Event::postponed;
1137
-    }
1138
-
1139
-
1140
-    /**
1141
-     * Checks if the event is marked as cancelled
1142
-     *
1143
-     * @return boolean
1144
-     */
1145
-    public function is_cancelled(): bool
1146
-    {
1147
-        return $this->status() === EEM_Event::cancelled;
1148
-    }
1149
-
1150
-
1151
-    /**
1152
-     * Get the logical active status in a hierarchical order for all the datetimes.  Note
1153
-     * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1154
-     * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1155
-     * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1156
-     * the event is considered expired.
1157
-     * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a
1158
-     * status set on the EVENT when it is not published and thus is done
1159
-     *
1160
-     * @param bool $reset
1161
-     * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1162
-     * @throws EE_Error
1163
-     * @throws ReflectionException
1164
-     */
1165
-    public function get_active_status(bool $reset = false)
1166
-    {
1167
-        // if the active status has already been set, then just use that value (unless we are resetting it)
1168
-        if (! empty($this->_active_status) && ! $reset) {
1169
-            return $this->_active_status;
1170
-        }
1171
-        // first check if event id is present on this object
1172
-        if (! $this->ID()) {
1173
-            return false;
1174
-        }
1175
-        $where_params_for_event = [['EVT_ID' => $this->ID()]];
1176
-        // if event is published:
1177
-        if (
1178
-            $this->status() === EEM_CPT_Base::post_status_publish
1179
-            || $this->status() === EEM_CPT_Base::post_status_private
1180
-        ) {
1181
-            // active?
1182
-            if (
1183
-                EEM_Datetime::instance()->get_datetime_count_for_status(
1184
-                    EE_Datetime::active,
1185
-                    $where_params_for_event
1186
-                ) > 0
1187
-            ) {
1188
-                $this->_active_status = EE_Datetime::active;
1189
-            } else {
1190
-                // upcoming?
1191
-                if (
1192
-                    EEM_Datetime::instance()->get_datetime_count_for_status(
1193
-                        EE_Datetime::upcoming,
1194
-                        $where_params_for_event
1195
-                    ) > 0
1196
-                ) {
1197
-                    $this->_active_status = EE_Datetime::upcoming;
1198
-                } else {
1199
-                    // expired?
1200
-                    if (
1201
-                        EEM_Datetime::instance()->get_datetime_count_for_status(
1202
-                            EE_Datetime::expired,
1203
-                            $where_params_for_event
1204
-                        ) > 0
1205
-                    ) {
1206
-                        $this->_active_status = EE_Datetime::expired;
1207
-                    } else {
1208
-                        // it would be odd if things make it this far
1209
-                        // because it basically means there are no datetimes attached to the event.
1210
-                        // So in this case it will just be considered inactive.
1211
-                        $this->_active_status = EE_Datetime::inactive;
1212
-                    }
1213
-                }
1214
-            }
1215
-        } else {
1216
-            // the event is not published, so let's just set it's active status according to its' post status
1217
-            switch ($this->status()) {
1218
-                case EEM_Event::sold_out:
1219
-                    $this->_active_status = EE_Datetime::sold_out;
1220
-                    break;
1221
-                case EEM_Event::cancelled:
1222
-                    $this->_active_status = EE_Datetime::cancelled;
1223
-                    break;
1224
-                case EEM_Event::postponed:
1225
-                    $this->_active_status = EE_Datetime::postponed;
1226
-                    break;
1227
-                default:
1228
-                    $this->_active_status = EE_Datetime::inactive;
1229
-            }
1230
-        }
1231
-        return $this->_active_status;
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     *    pretty_active_status
1237
-     *
1238
-     * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1239
-     * @return string
1240
-     * @throws EE_Error
1241
-     * @throws ReflectionException
1242
-     */
1243
-    public function pretty_active_status(bool $echo = true): string
1244
-    {
1245
-        $active_status = $this->get_active_status();
1246
-        $status        = "
19
+	/**
20
+	 * cached value for the the logical active status for the event
21
+	 *
22
+	 * @see get_active_status()
23
+	 * @var string
24
+	 */
25
+	protected $_active_status = '';
26
+
27
+	/**
28
+	 * This is just used for caching the Primary Datetime for the Event on initial retrieval
29
+	 *
30
+	 * @var EE_Datetime
31
+	 */
32
+	protected $_Primary_Datetime;
33
+
34
+	/**
35
+	 * @var EventSpacesCalculator $available_spaces_calculator
36
+	 */
37
+	protected $available_spaces_calculator;
38
+
39
+
40
+	/**
41
+	 * @param array  $props_n_values          incoming values
42
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
43
+	 *                                        used.)
44
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
45
+	 *                                        date_format and the second value is the time format
46
+	 * @return EE_Event
47
+	 * @throws EE_Error
48
+	 * @throws ReflectionException
49
+	 */
50
+	public static function new_instance($props_n_values = [], $timezone = '', $date_formats = []): EE_Event
51
+	{
52
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
53
+		return $has_object ?: new self($props_n_values, false, $timezone, $date_formats);
54
+	}
55
+
56
+
57
+	/**
58
+	 * @param array  $props_n_values  incoming values from the database
59
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
60
+	 *                                the website will be used.
61
+	 * @return EE_Event
62
+	 * @throws EE_Error
63
+	 * @throws ReflectionException
64
+	 */
65
+	public static function new_instance_from_db($props_n_values = [], $timezone = ''): EE_Event
66
+	{
67
+		return new self($props_n_values, true, $timezone);
68
+	}
69
+
70
+
71
+	/**
72
+	 * @return EventSpacesCalculator
73
+	 * @throws EE_Error
74
+	 * @throws ReflectionException
75
+	 */
76
+	public function getAvailableSpacesCalculator(): EventSpacesCalculator
77
+	{
78
+		if (! $this->available_spaces_calculator instanceof EventSpacesCalculator) {
79
+			$this->available_spaces_calculator = new EventSpacesCalculator($this);
80
+		}
81
+		return $this->available_spaces_calculator;
82
+	}
83
+
84
+
85
+	/**
86
+	 * Overrides parent set() method so that all calls to set( 'status', $status ) can be routed to internal methods
87
+	 *
88
+	 * @param string $field_name
89
+	 * @param mixed  $field_value
90
+	 * @param bool   $use_default
91
+	 * @throws EE_Error
92
+	 * @throws ReflectionException
93
+	 */
94
+	public function set($field_name, $field_value, $use_default = false)
95
+	{
96
+		switch ($field_name) {
97
+			case 'status':
98
+				$this->set_status($field_value, $use_default);
99
+				break;
100
+			default:
101
+				parent::set($field_name, $field_value, $use_default);
102
+		}
103
+	}
104
+
105
+
106
+	/**
107
+	 *    set_status
108
+	 * Checks if event status is being changed to SOLD OUT
109
+	 * and updates event meta data with previous event status
110
+	 * so that we can revert things if/when the event is no longer sold out
111
+	 *
112
+	 * @param string $status
113
+	 * @param bool   $use_default
114
+	 * @return void
115
+	 * @throws EE_Error
116
+	 * @throws ReflectionException
117
+	 */
118
+	public function set_status($status = '', $use_default = false)
119
+	{
120
+		// if nothing is set, and we aren't explicitly wanting to reset the status, then just leave
121
+		if (empty($status) && ! $use_default) {
122
+			return;
123
+		}
124
+		// get current Event status
125
+		$old_status = $this->status();
126
+		// if status has changed
127
+		if ($old_status !== $status) {
128
+			// TO sold_out
129
+			if ($status === EEM_Event::sold_out) {
130
+				// save the previous event status so that we can revert if the event is no longer sold out
131
+				$this->add_post_meta('_previous_event_status', $old_status);
132
+				do_action('AHEE__EE_Event__set_status__to_sold_out', $this, $old_status, $status);
133
+				// OR FROM  sold_out
134
+			} elseif ($old_status === EEM_Event::sold_out) {
135
+				$this->delete_post_meta('_previous_event_status');
136
+				do_action('AHEE__EE_Event__set_status__from_sold_out', $this, $old_status, $status);
137
+			}
138
+			// clear out the active status so that it gets reset the next time it is requested
139
+			$this->_active_status = null;
140
+			// update status
141
+			parent::set('status', $status, $use_default);
142
+			do_action('AHEE__EE_Event__set_status__after_update', $this);
143
+			return;
144
+		}
145
+		// even though the old value matches the new value, it's still good to
146
+		// allow the parent set method to have a say
147
+		parent::set('status', $status, $use_default);
148
+	}
149
+
150
+
151
+	/**
152
+	 * Gets all the datetimes for this event
153
+	 *
154
+	 * @param array|null $query_params
155
+	 * @return EE_Base_Class[]|EE_Datetime[]
156
+	 * @throws EE_Error
157
+	 * @throws ReflectionException
158
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
159
+	 */
160
+	public function datetimes(?array $query_params = []): array
161
+	{
162
+		return $this->get_many_related('Datetime', $query_params);
163
+	}
164
+
165
+
166
+	/**
167
+	 * Gets all the datetimes for this event that are currently ACTIVE,
168
+	 * meaning the datetime has started and has not yet ended.
169
+	 *
170
+	 * @param int|null   $start_date   timestamp to use for event date start time, defaults to NOW unless set to 0
171
+	 * @param array|null $query_params will recursively replace default values
172
+	 * @throws EE_Error
173
+	 * @throws ReflectionException
174
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
175
+	 */
176
+	public function activeDatetimes(?int $start_date, ?array $query_params = []): array
177
+	{
178
+		// if start date is null, then use current time
179
+		$start_date = $start_date ?? time();
180
+		$where      = [];
181
+		if ($start_date) {
182
+			$where['DTT_EVT_start'] = ['<', $start_date];
183
+			$where['DTT_EVT_end']   = ['>', time()];
184
+		}
185
+		$query_params = array_replace_recursive(
186
+			[
187
+				$where,
188
+				'order_by' => ['DTT_EVT_start' => 'ASC'],
189
+			],
190
+			$query_params
191
+		);
192
+		return $this->get_many_related('Datetime', $query_params);
193
+	}
194
+
195
+
196
+	/**
197
+	 * Gets all the datetimes for this event, ordered by DTT_EVT_start in ascending order
198
+	 *
199
+	 * @return EE_Base_Class[]|EE_Datetime[]
200
+	 * @throws EE_Error
201
+	 * @throws ReflectionException
202
+	 */
203
+	public function datetimes_in_chronological_order(): array
204
+	{
205
+		return $this->get_many_related('Datetime', ['order_by' => ['DTT_EVT_start' => 'ASC']]);
206
+	}
207
+
208
+
209
+	/**
210
+	 * Gets all the datetimes for this event, ordered by the DTT_order on the datetime.
211
+	 * @todo We should probably UNSET timezone on the EEM_Datetime model
212
+	 * after running our query, so that this timezone isn't set for EVERY query
213
+	 * on EEM_Datetime for the rest of the request, no?
214
+	 *
215
+	 * @param bool     $show_expired whether or not to include expired events
216
+	 * @param bool     $show_deleted whether or not to include deleted events
217
+	 * @param int|null $limit
218
+	 * @return EE_Datetime[]
219
+	 * @throws EE_Error
220
+	 * @throws ReflectionException
221
+	 */
222
+	public function datetimes_ordered(bool $show_expired = true, bool $show_deleted = false, ?int $limit = null): array
223
+	{
224
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_event_ordered_by_DTT_order(
225
+			$this->ID(),
226
+			$show_expired,
227
+			$show_deleted,
228
+			$limit
229
+		);
230
+	}
231
+
232
+
233
+	/**
234
+	 * Returns one related datetime. Mostly only used by some legacy code.
235
+	 *
236
+	 * @return EE_Base_Class|EE_Datetime
237
+	 * @throws EE_Error
238
+	 * @throws ReflectionException
239
+	 */
240
+	public function first_datetime(): EE_Datetime
241
+	{
242
+		return $this->get_first_related('Datetime');
243
+	}
244
+
245
+
246
+	/**
247
+	 * Returns the 'primary' datetime for the event
248
+	 *
249
+	 * @param bool $try_to_exclude_expired
250
+	 * @param bool $try_to_exclude_deleted
251
+	 * @return EE_Datetime|null
252
+	 * @throws EE_Error
253
+	 * @throws ReflectionException
254
+	 */
255
+	public function primary_datetime(
256
+		bool $try_to_exclude_expired = true,
257
+		bool $try_to_exclude_deleted = true
258
+	): ?EE_Datetime {
259
+		if (! empty($this->_Primary_Datetime)) {
260
+			return $this->_Primary_Datetime;
261
+		}
262
+		$this->_Primary_Datetime = EEM_Datetime::instance($this->_timezone)->get_primary_datetime_for_event(
263
+			$this->ID(),
264
+			$try_to_exclude_expired,
265
+			$try_to_exclude_deleted
266
+		);
267
+		return $this->_Primary_Datetime;
268
+	}
269
+
270
+
271
+	/**
272
+	 * Gets all the tickets available for purchase of this event
273
+	 *
274
+	 * @param array|null $query_params
275
+	 * @return EE_Base_Class[]|EE_Ticket[]
276
+	 * @throws EE_Error
277
+	 * @throws ReflectionException
278
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
279
+	 */
280
+	public function tickets(?array $query_params = []): array
281
+	{
282
+		// first get all datetimes
283
+		$datetimes = $this->datetimes_ordered();
284
+		if (! $datetimes) {
285
+			return [];
286
+		}
287
+		$datetime_ids = [];
288
+		foreach ($datetimes as $datetime) {
289
+			$datetime_ids[] = $datetime->ID();
290
+		}
291
+		$where_params = ['Datetime.DTT_ID' => ['IN', $datetime_ids]];
292
+		// if incoming $query_params has where conditions let's merge but not override existing.
293
+		if (is_array($query_params) && isset($query_params[0])) {
294
+			$where_params = array_merge($query_params[0], $where_params);
295
+			unset($query_params[0]);
296
+		}
297
+		// now add $where_params to $query_params
298
+		$query_params[0] = $where_params;
299
+		return EEM_Ticket::instance()->get_all($query_params);
300
+	}
301
+
302
+
303
+	/**
304
+	 * get all unexpired not-trashed tickets
305
+	 *
306
+	 * @return EE_Ticket[]
307
+	 * @throws EE_Error
308
+	 * @throws ReflectionException
309
+	 */
310
+	public function active_tickets(): array
311
+	{
312
+		return $this->tickets(
313
+			[
314
+				[
315
+					'TKT_end_date' => ['>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')],
316
+					'TKT_deleted'  => false,
317
+				],
318
+			]
319
+		);
320
+	}
321
+
322
+
323
+	/**
324
+	 * @return int
325
+	 * @throws EE_Error
326
+	 * @throws ReflectionException
327
+	 */
328
+	public function additional_limit(): int
329
+	{
330
+		return $this->get('EVT_additional_limit');
331
+	}
332
+
333
+
334
+	/**
335
+	 * @return bool
336
+	 * @throws EE_Error
337
+	 * @throws ReflectionException
338
+	 */
339
+	public function allow_overflow(): bool
340
+	{
341
+		return $this->get('EVT_allow_overflow');
342
+	}
343
+
344
+
345
+	/**
346
+	 * @return string
347
+	 * @throws EE_Error
348
+	 * @throws ReflectionException
349
+	 */
350
+	public function created(): string
351
+	{
352
+		return $this->get('EVT_created');
353
+	}
354
+
355
+
356
+	/**
357
+	 * @return string
358
+	 * @throws EE_Error
359
+	 * @throws ReflectionException
360
+	 */
361
+	public function description(): string
362
+	{
363
+		return $this->get('EVT_desc');
364
+	}
365
+
366
+
367
+	/**
368
+	 * Runs do_shortcode and wpautop on the description
369
+	 *
370
+	 * @return string of html
371
+	 * @throws EE_Error
372
+	 * @throws ReflectionException
373
+	 */
374
+	public function description_filtered(): string
375
+	{
376
+		return $this->get_pretty('EVT_desc');
377
+	}
378
+
379
+
380
+	/**
381
+	 * @return bool
382
+	 * @throws EE_Error
383
+	 * @throws ReflectionException
384
+	 */
385
+	public function display_description(): bool
386
+	{
387
+		return $this->get('EVT_display_desc');
388
+	}
389
+
390
+
391
+	/**
392
+	 * @return bool
393
+	 * @throws EE_Error
394
+	 * @throws ReflectionException
395
+	 */
396
+	public function display_ticket_selector(): bool
397
+	{
398
+		return (bool) $this->get('EVT_display_ticket_selector');
399
+	}
400
+
401
+
402
+	/**
403
+	 * @return string
404
+	 * @throws EE_Error
405
+	 * @throws ReflectionException
406
+	 */
407
+	public function external_url(): ?string
408
+	{
409
+		return $this->get('EVT_external_URL') ?? '';
410
+	}
411
+
412
+
413
+	/**
414
+	 * @return bool
415
+	 * @throws EE_Error
416
+	 * @throws ReflectionException
417
+	 */
418
+	public function member_only(): bool
419
+	{
420
+		return $this->get('EVT_member_only');
421
+	}
422
+
423
+
424
+	/**
425
+	 * @return string
426
+	 * @throws EE_Error
427
+	 * @throws ReflectionException
428
+	 */
429
+	public function phone(): string
430
+	{
431
+		return $this->get('EVT_phone');
432
+	}
433
+
434
+
435
+	/**
436
+	 * @return string
437
+	 * @throws EE_Error
438
+	 * @throws ReflectionException
439
+	 */
440
+	public function modified(): string
441
+	{
442
+		return $this->get('EVT_modified');
443
+	}
444
+
445
+
446
+	/**
447
+	 * @return string
448
+	 * @throws EE_Error
449
+	 * @throws ReflectionException
450
+	 */
451
+	public function name(): string
452
+	{
453
+		return $this->get('EVT_name');
454
+	}
455
+
456
+
457
+	/**
458
+	 * @return int
459
+	 * @throws EE_Error
460
+	 * @throws ReflectionException
461
+	 */
462
+	public function order(): int
463
+	{
464
+		return $this->get('EVT_order');
465
+	}
466
+
467
+
468
+	/**
469
+	 * @return string
470
+	 * @throws EE_Error
471
+	 * @throws ReflectionException
472
+	 */
473
+	public function default_registration_status(): string
474
+	{
475
+		$event_default_registration_status = $this->get('EVT_default_registration_status');
476
+		return ! empty($event_default_registration_status)
477
+			? $event_default_registration_status
478
+			: EE_Registry::instance()->CFG->registration->default_STS_ID;
479
+	}
480
+
481
+
482
+	/**
483
+	 * @param int|null    $num_words
484
+	 * @param string|null $more
485
+	 * @param bool        $not_full_desc
486
+	 * @return string
487
+	 * @throws EE_Error
488
+	 * @throws ReflectionException
489
+	 */
490
+	public function short_description(?int $num_words = 55, string $more = null, bool $not_full_desc = false): string
491
+	{
492
+		$short_desc = $this->get('EVT_short_desc');
493
+		if (! empty($short_desc) || $not_full_desc) {
494
+			return $short_desc;
495
+		}
496
+		$full_desc = $this->get('EVT_desc');
497
+		return wp_trim_words($full_desc, $num_words, $more);
498
+	}
499
+
500
+
501
+	/**
502
+	 * @return string
503
+	 * @throws EE_Error
504
+	 * @throws ReflectionException
505
+	 */
506
+	public function slug(): string
507
+	{
508
+		return $this->get('EVT_slug');
509
+	}
510
+
511
+
512
+	/**
513
+	 * @return string
514
+	 * @throws EE_Error
515
+	 * @throws ReflectionException
516
+	 */
517
+	public function timezone_string(): string
518
+	{
519
+		return $this->get('EVT_timezone_string');
520
+	}
521
+
522
+
523
+	/**
524
+	 * @return string
525
+	 * @throws EE_Error
526
+	 * @throws ReflectionException
527
+	 * @deprecated
528
+	 */
529
+	public function visible_on(): string
530
+	{
531
+		EE_Error::doing_it_wrong(
532
+			__METHOD__,
533
+			esc_html__(
534
+				'This method has been deprecated and there is no replacement for it.',
535
+				'event_espresso'
536
+			),
537
+			'5.0.0.rc.002'
538
+		);
539
+		return $this->get('EVT_visible_on');
540
+	}
541
+
542
+
543
+	/**
544
+	 * @return int
545
+	 * @throws EE_Error
546
+	 * @throws ReflectionException
547
+	 */
548
+	public function wp_user(): int
549
+	{
550
+		return (int) $this->get('EVT_wp_user');
551
+	}
552
+
553
+
554
+	/**
555
+	 * @return bool
556
+	 * @throws EE_Error
557
+	 * @throws ReflectionException
558
+	 */
559
+	public function donations(): bool
560
+	{
561
+		return $this->get('EVT_donations');
562
+	}
563
+
564
+
565
+	/**
566
+	 * @param int $limit
567
+	 * @throws EE_Error
568
+	 * @throws ReflectionException
569
+	 */
570
+	public function set_additional_limit(int $limit)
571
+	{
572
+		$this->set('EVT_additional_limit', $limit);
573
+	}
574
+
575
+
576
+	/**
577
+	 * @param $created
578
+	 * @throws EE_Error
579
+	 * @throws ReflectionException
580
+	 */
581
+	public function set_created($created)
582
+	{
583
+		$this->set('EVT_created', $created);
584
+	}
585
+
586
+
587
+	/**
588
+	 * @param $desc
589
+	 * @throws EE_Error
590
+	 * @throws ReflectionException
591
+	 */
592
+	public function set_description($desc)
593
+	{
594
+		$this->set('EVT_desc', $desc);
595
+	}
596
+
597
+
598
+	/**
599
+	 * @param $display_desc
600
+	 * @throws EE_Error
601
+	 * @throws ReflectionException
602
+	 */
603
+	public function set_display_description($display_desc)
604
+	{
605
+		$this->set('EVT_display_desc', $display_desc);
606
+	}
607
+
608
+
609
+	/**
610
+	 * @param $display_ticket_selector
611
+	 * @throws EE_Error
612
+	 * @throws ReflectionException
613
+	 */
614
+	public function set_display_ticket_selector($display_ticket_selector)
615
+	{
616
+		$this->set('EVT_display_ticket_selector', $display_ticket_selector);
617
+	}
618
+
619
+
620
+	/**
621
+	 * @param $external_url
622
+	 * @throws EE_Error
623
+	 * @throws ReflectionException
624
+	 */
625
+	public function set_external_url($external_url)
626
+	{
627
+		$this->set('EVT_external_URL', $external_url);
628
+	}
629
+
630
+
631
+	/**
632
+	 * @param $member_only
633
+	 * @throws EE_Error
634
+	 * @throws ReflectionException
635
+	 */
636
+	public function set_member_only($member_only)
637
+	{
638
+		$this->set('EVT_member_only', $member_only);
639
+	}
640
+
641
+
642
+	/**
643
+	 * @param $event_phone
644
+	 * @throws EE_Error
645
+	 * @throws ReflectionException
646
+	 */
647
+	public function set_event_phone($event_phone)
648
+	{
649
+		$this->set('EVT_phone', $event_phone);
650
+	}
651
+
652
+
653
+	/**
654
+	 * @param $modified
655
+	 * @throws EE_Error
656
+	 * @throws ReflectionException
657
+	 */
658
+	public function set_modified($modified)
659
+	{
660
+		$this->set('EVT_modified', $modified);
661
+	}
662
+
663
+
664
+	/**
665
+	 * @param $name
666
+	 * @throws EE_Error
667
+	 * @throws ReflectionException
668
+	 */
669
+	public function set_name($name)
670
+	{
671
+		$this->set('EVT_name', $name);
672
+	}
673
+
674
+
675
+	/**
676
+	 * @param $order
677
+	 * @throws EE_Error
678
+	 * @throws ReflectionException
679
+	 */
680
+	public function set_order($order)
681
+	{
682
+		$this->set('EVT_order', $order);
683
+	}
684
+
685
+
686
+	/**
687
+	 * @param $short_desc
688
+	 * @throws EE_Error
689
+	 * @throws ReflectionException
690
+	 */
691
+	public function set_short_description($short_desc)
692
+	{
693
+		$this->set('EVT_short_desc', $short_desc);
694
+	}
695
+
696
+
697
+	/**
698
+	 * @param $slug
699
+	 * @throws EE_Error
700
+	 * @throws ReflectionException
701
+	 */
702
+	public function set_slug($slug)
703
+	{
704
+		$this->set('EVT_slug', $slug);
705
+	}
706
+
707
+
708
+	/**
709
+	 * @param $timezone_string
710
+	 * @throws EE_Error
711
+	 * @throws ReflectionException
712
+	 */
713
+	public function set_timezone_string($timezone_string)
714
+	{
715
+		$this->set('EVT_timezone_string', $timezone_string);
716
+	}
717
+
718
+
719
+	/**
720
+	 * @param $visible_on
721
+	 * @throws EE_Error
722
+	 * @throws ReflectionException
723
+	 * @deprecated
724
+	 */
725
+	public function set_visible_on($visible_on)
726
+	{
727
+		EE_Error::doing_it_wrong(
728
+			__METHOD__,
729
+			esc_html__(
730
+				'This method has been deprecated and there is no replacement for it.',
731
+				'event_espresso'
732
+			),
733
+			'5.0.0.rc.002'
734
+		);
735
+		$this->set('EVT_visible_on', $visible_on);
736
+	}
737
+
738
+
739
+	/**
740
+	 * @param $wp_user
741
+	 * @throws EE_Error
742
+	 * @throws ReflectionException
743
+	 */
744
+	public function set_wp_user($wp_user)
745
+	{
746
+		$this->set('EVT_wp_user', $wp_user);
747
+	}
748
+
749
+
750
+	/**
751
+	 * @param $default_registration_status
752
+	 * @throws EE_Error
753
+	 * @throws ReflectionException
754
+	 */
755
+	public function set_default_registration_status($default_registration_status)
756
+	{
757
+		$this->set('EVT_default_registration_status', $default_registration_status);
758
+	}
759
+
760
+
761
+	/**
762
+	 * @param $donations
763
+	 * @throws EE_Error
764
+	 * @throws ReflectionException
765
+	 */
766
+	public function set_donations($donations)
767
+	{
768
+		$this->set('EVT_donations', $donations);
769
+	}
770
+
771
+
772
+	/**
773
+	 * Adds a venue to this event
774
+	 *
775
+	 * @param int|EE_Venue /int $venue_id_or_obj
776
+	 * @return EE_Base_Class|EE_Venue
777
+	 * @throws EE_Error
778
+	 * @throws ReflectionException
779
+	 */
780
+	public function add_venue($venue_id_or_obj): EE_Venue
781
+	{
782
+		return $this->_add_relation_to($venue_id_or_obj, 'Venue');
783
+	}
784
+
785
+
786
+	/**
787
+	 * Removes a venue from the event
788
+	 *
789
+	 * @param EE_Venue /int $venue_id_or_obj
790
+	 * @return EE_Base_Class|EE_Venue
791
+	 * @throws EE_Error
792
+	 * @throws ReflectionException
793
+	 */
794
+	public function remove_venue($venue_id_or_obj): EE_Venue
795
+	{
796
+		$venue_id_or_obj = ! empty($venue_id_or_obj) ? $venue_id_or_obj : $this->venue();
797
+		return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
798
+	}
799
+
800
+
801
+	/**
802
+	 * Gets the venue related to the event. May provide additional $query_params if desired
803
+	 *
804
+	 * @param array $query_params
805
+	 * @return int
806
+	 * @throws EE_Error
807
+	 * @throws ReflectionException
808
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
809
+	 */
810
+	public function venue_ID(array $query_params = []): int
811
+	{
812
+		$venue = $this->get_first_related('Venue', $query_params);
813
+		return $venue instanceof EE_Venue ? $venue->ID() : 0;
814
+	}
815
+
816
+
817
+	/**
818
+	 * Gets the venue related to the event. May provide additional $query_params if desired
819
+	 *
820
+	 * @param array $query_params
821
+	 * @return EE_Base_Class|EE_Venue|null
822
+	 * @throws EE_Error
823
+	 * @throws ReflectionException
824
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
825
+	 */
826
+	public function venue(array $query_params = []): ?EE_Venue
827
+	{
828
+		return $this->get_first_related('Venue', $query_params);
829
+	}
830
+
831
+
832
+	/**
833
+	 * @param array $query_params
834
+	 * @return EE_Base_Class[]|EE_Venue[]
835
+	 * @throws EE_Error
836
+	 * @throws ReflectionException
837
+	 * @deprecated 5.0.0.p
838
+	 */
839
+	public function venues(array $query_params = []): array
840
+	{
841
+		$venue = $this->venue($query_params);
842
+		return $venue instanceof EE_Venue ? [$venue] : [];
843
+	}
844
+
845
+
846
+	/**
847
+	 * check if event id is present and if event is published
848
+	 *
849
+	 * @return boolean true yes, false no
850
+	 * @throws EE_Error
851
+	 * @throws ReflectionException
852
+	 */
853
+	private function _has_ID_and_is_published(): bool
854
+	{
855
+		// first check if event id is present and not NULL,
856
+		// then check if this event is published (or any of the equivalent "published" statuses)
857
+		return
858
+			$this->ID() && $this->ID() !== null
859
+			&& (
860
+				$this->status() === 'publish'
861
+				|| $this->status() === EEM_Event::sold_out
862
+				|| $this->status() === EEM_Event::postponed
863
+				|| $this->status() === EEM_Event::cancelled
864
+			);
865
+	}
866
+
867
+
868
+	/**
869
+	 * This simply compares the internal dates with NOW and determines if the event is upcoming or not.
870
+	 *
871
+	 * @return boolean true yes, false no
872
+	 * @throws EE_Error
873
+	 * @throws ReflectionException
874
+	 */
875
+	public function is_upcoming(): bool
876
+	{
877
+		// check if event id is present and if this event is published
878
+		if ($this->is_inactive()) {
879
+			return false;
880
+		}
881
+		// set initial value
882
+		$upcoming = false;
883
+		// next let's get all datetimes and loop through them
884
+		$datetimes = $this->datetimes_in_chronological_order();
885
+		foreach ($datetimes as $datetime) {
886
+			if ($datetime instanceof EE_Datetime) {
887
+				// if this dtt is expired then we continue cause one of the other datetimes might be upcoming.
888
+				if ($datetime->is_expired()) {
889
+					continue;
890
+				}
891
+				// if this dtt is active then we return false.
892
+				if ($datetime->is_active()) {
893
+					return false;
894
+				}
895
+				// otherwise let's check upcoming status
896
+				$upcoming = $datetime->is_upcoming();
897
+			}
898
+		}
899
+		return $upcoming;
900
+	}
901
+
902
+
903
+	/**
904
+	 * @return bool
905
+	 * @throws EE_Error
906
+	 * @throws ReflectionException
907
+	 */
908
+	public function is_active(): bool
909
+	{
910
+		// check if event id is present and if this event is published
911
+		if ($this->is_inactive()) {
912
+			return false;
913
+		}
914
+		// set initial value
915
+		$active = false;
916
+		// next let's get all datetimes and loop through them
917
+		$datetimes = $this->datetimes_in_chronological_order();
918
+		foreach ($datetimes as $datetime) {
919
+			if ($datetime instanceof EE_Datetime) {
920
+				// if this dtt is expired then we continue cause one of the other datetimes might be active.
921
+				if ($datetime->is_expired()) {
922
+					continue;
923
+				}
924
+				// if this dtt is upcoming then we return false.
925
+				if ($datetime->is_upcoming()) {
926
+					return false;
927
+				}
928
+				// otherwise let's check active status
929
+				$active = $datetime->is_active();
930
+			}
931
+		}
932
+		return $active;
933
+	}
934
+
935
+
936
+	/**
937
+	 * @return bool
938
+	 * @throws EE_Error
939
+	 * @throws ReflectionException
940
+	 */
941
+	public function is_expired(): bool
942
+	{
943
+		// check if event id is present and if this event is published
944
+		if ($this->is_inactive()) {
945
+			return false;
946
+		}
947
+		// set initial value
948
+		$expired = false;
949
+		// first let's get all datetimes and loop through them
950
+		$datetimes = $this->datetimes_in_chronological_order();
951
+		foreach ($datetimes as $datetime) {
952
+			if ($datetime instanceof EE_Datetime) {
953
+				// if this dtt is upcoming or active then we return false.
954
+				if ($datetime->is_upcoming() || $datetime->is_active()) {
955
+					return false;
956
+				}
957
+				// otherwise let's check active status
958
+				$expired = $datetime->is_expired();
959
+			}
960
+		}
961
+		return $expired;
962
+	}
963
+
964
+
965
+	/**
966
+	 * @return bool
967
+	 * @throws EE_Error
968
+	 * @throws ReflectionException
969
+	 */
970
+	public function is_inactive(): bool
971
+	{
972
+		// check if event id is present and if this event is published
973
+		if ($this->_has_ID_and_is_published()) {
974
+			return false;
975
+		}
976
+		return true;
977
+	}
978
+
979
+
980
+	/**
981
+	 * calculate spaces remaining based on "saleable" tickets
982
+	 *
983
+	 * @param array|null $tickets
984
+	 * @param bool       $filtered
985
+	 * @return int|float
986
+	 * @throws EE_Error
987
+	 * @throws DomainException
988
+	 * @throws UnexpectedEntityException
989
+	 * @throws ReflectionException
990
+	 */
991
+	public function spaces_remaining(?array $tickets = [], ?bool $filtered = true)
992
+	{
993
+		$this->getAvailableSpacesCalculator()->setActiveTickets($tickets);
994
+		$spaces_remaining = $this->getAvailableSpacesCalculator()->spacesRemaining();
995
+		return $filtered
996
+			? apply_filters(
997
+				'FHEE_EE_Event__spaces_remaining',
998
+				$spaces_remaining,
999
+				$this,
1000
+				$tickets
1001
+			)
1002
+			: $spaces_remaining;
1003
+	}
1004
+
1005
+
1006
+	/**
1007
+	 *    perform_sold_out_status_check
1008
+	 *    checks all of this event's datetime  reg_limit - sold values to determine if ANY datetimes have spaces
1009
+	 *    available... if NOT, then the event status will get toggled to 'sold_out'
1010
+	 *
1011
+	 * @return bool    return the ACTUAL sold out state.
1012
+	 * @throws EE_Error
1013
+	 * @throws DomainException
1014
+	 * @throws UnexpectedEntityException
1015
+	 * @throws ReflectionException
1016
+	 */
1017
+	public function perform_sold_out_status_check(): bool
1018
+	{
1019
+		// get all tickets
1020
+		$tickets     = $this->tickets(
1021
+			[
1022
+				'default_where_conditions' => 'none',
1023
+				'order_by'                 => ['TKT_qty' => 'ASC'],
1024
+			]
1025
+		);
1026
+		$all_expired = true;
1027
+		foreach ($tickets as $ticket) {
1028
+			if (! $ticket->is_expired()) {
1029
+				$all_expired = false;
1030
+				break;
1031
+			}
1032
+		}
1033
+		// if all the tickets are just expired, then don't update the event status to sold out
1034
+		if ($all_expired) {
1035
+			return true;
1036
+		}
1037
+		$spaces_remaining = $this->spaces_remaining($tickets);
1038
+		if ($spaces_remaining < 1) {
1039
+			if ($this->status() !== EEM_CPT_Base::post_status_private) {
1040
+				$this->set_status(EEM_Event::sold_out);
1041
+				$this->save();
1042
+			}
1043
+			$sold_out = true;
1044
+		} else {
1045
+			$sold_out = false;
1046
+			// was event previously marked as sold out ?
1047
+			if ($this->status() === EEM_Event::sold_out) {
1048
+				// revert status to previous value, if it was set
1049
+				$previous_event_status = $this->get_post_meta('_previous_event_status', true);
1050
+				if ($previous_event_status) {
1051
+					$this->set_status($previous_event_status);
1052
+					$this->save();
1053
+				}
1054
+			}
1055
+		}
1056
+		do_action('AHEE__EE_Event__perform_sold_out_status_check__end', $this, $sold_out, $spaces_remaining, $tickets);
1057
+		return $sold_out;
1058
+	}
1059
+
1060
+
1061
+	/**
1062
+	 * This returns the total remaining spaces for sale on this event.
1063
+	 *
1064
+	 * @return int|float
1065
+	 * @throws EE_Error
1066
+	 * @throws DomainException
1067
+	 * @throws UnexpectedEntityException
1068
+	 * @throws ReflectionException
1069
+	 * @uses EE_Event::total_available_spaces()
1070
+	 */
1071
+	public function spaces_remaining_for_sale()
1072
+	{
1073
+		return $this->total_available_spaces(true);
1074
+	}
1075
+
1076
+
1077
+	/**
1078
+	 * This returns the total spaces available for an event
1079
+	 * while considering all the quantities on the tickets and the reg limits
1080
+	 * on the datetimes attached to this event.
1081
+	 *
1082
+	 * @param bool $consider_sold   Whether to consider any tickets that have already sold in our calculation.
1083
+	 *                              If this is false, then we return the most tickets that could ever be sold
1084
+	 *                              for this event with the datetime and tickets setup on the event under optimal
1085
+	 *                              selling conditions.  Otherwise we return a live calculation of spaces available
1086
+	 *                              based on tickets sold.  Depending on setup and stage of sales, this
1087
+	 *                              may appear to equal remaining tickets.  However, the more tickets are
1088
+	 *                              sold out, the more accurate the "live" total is.
1089
+	 * @return int|float
1090
+	 * @throws EE_Error
1091
+	 * @throws DomainException
1092
+	 * @throws UnexpectedEntityException
1093
+	 * @throws ReflectionException
1094
+	 */
1095
+	public function total_available_spaces(bool $consider_sold = false)
1096
+	{
1097
+		$spaces_available = $consider_sold
1098
+			? $this->getAvailableSpacesCalculator()->spacesRemaining()
1099
+			: $this->getAvailableSpacesCalculator()->totalSpacesAvailable();
1100
+		return apply_filters(
1101
+			'FHEE_EE_Event__total_available_spaces__spaces_available',
1102
+			$spaces_available,
1103
+			$this,
1104
+			$this->getAvailableSpacesCalculator()->getDatetimes(),
1105
+			$this->getAvailableSpacesCalculator()->getActiveTickets()
1106
+		);
1107
+	}
1108
+
1109
+
1110
+	/**
1111
+	 * Checks if the event is set to sold out
1112
+	 *
1113
+	 * @param bool $actual  whether or not to perform calculations to not only figure the
1114
+	 *                      actual status but also to flip the status if necessary to sold
1115
+	 *                      out If false, we just check the existing status of the event
1116
+	 * @return boolean
1117
+	 * @throws EE_Error
1118
+	 * @throws ReflectionException
1119
+	 */
1120
+	public function is_sold_out(bool $actual = false): bool
1121
+	{
1122
+		if (! $actual) {
1123
+			return $this->status() === EEM_Event::sold_out;
1124
+		}
1125
+		return $this->perform_sold_out_status_check();
1126
+	}
1127
+
1128
+
1129
+	/**
1130
+	 * Checks if the event is marked as postponed
1131
+	 *
1132
+	 * @return boolean
1133
+	 */
1134
+	public function is_postponed(): bool
1135
+	{
1136
+		return $this->status() === EEM_Event::postponed;
1137
+	}
1138
+
1139
+
1140
+	/**
1141
+	 * Checks if the event is marked as cancelled
1142
+	 *
1143
+	 * @return boolean
1144
+	 */
1145
+	public function is_cancelled(): bool
1146
+	{
1147
+		return $this->status() === EEM_Event::cancelled;
1148
+	}
1149
+
1150
+
1151
+	/**
1152
+	 * Get the logical active status in a hierarchical order for all the datetimes.  Note
1153
+	 * Basically, we order the datetimes by EVT_start_date.  Then first test on whether the event is published.  If its
1154
+	 * NOT published then we test for whether its expired or not.  IF it IS published then we test first on whether an
1155
+	 * event has any active dates.  If no active dates then we check for any upcoming dates.  If no upcoming dates then
1156
+	 * the event is considered expired.
1157
+	 * NOTE: this method does NOT calculate whether the datetimes are sold out when event is published.  Sold Out is a
1158
+	 * status set on the EVENT when it is not published and thus is done
1159
+	 *
1160
+	 * @param bool $reset
1161
+	 * @return bool | string - based on EE_Datetime active constants or FALSE if error.
1162
+	 * @throws EE_Error
1163
+	 * @throws ReflectionException
1164
+	 */
1165
+	public function get_active_status(bool $reset = false)
1166
+	{
1167
+		// if the active status has already been set, then just use that value (unless we are resetting it)
1168
+		if (! empty($this->_active_status) && ! $reset) {
1169
+			return $this->_active_status;
1170
+		}
1171
+		// first check if event id is present on this object
1172
+		if (! $this->ID()) {
1173
+			return false;
1174
+		}
1175
+		$where_params_for_event = [['EVT_ID' => $this->ID()]];
1176
+		// if event is published:
1177
+		if (
1178
+			$this->status() === EEM_CPT_Base::post_status_publish
1179
+			|| $this->status() === EEM_CPT_Base::post_status_private
1180
+		) {
1181
+			// active?
1182
+			if (
1183
+				EEM_Datetime::instance()->get_datetime_count_for_status(
1184
+					EE_Datetime::active,
1185
+					$where_params_for_event
1186
+				) > 0
1187
+			) {
1188
+				$this->_active_status = EE_Datetime::active;
1189
+			} else {
1190
+				// upcoming?
1191
+				if (
1192
+					EEM_Datetime::instance()->get_datetime_count_for_status(
1193
+						EE_Datetime::upcoming,
1194
+						$where_params_for_event
1195
+					) > 0
1196
+				) {
1197
+					$this->_active_status = EE_Datetime::upcoming;
1198
+				} else {
1199
+					// expired?
1200
+					if (
1201
+						EEM_Datetime::instance()->get_datetime_count_for_status(
1202
+							EE_Datetime::expired,
1203
+							$where_params_for_event
1204
+						) > 0
1205
+					) {
1206
+						$this->_active_status = EE_Datetime::expired;
1207
+					} else {
1208
+						// it would be odd if things make it this far
1209
+						// because it basically means there are no datetimes attached to the event.
1210
+						// So in this case it will just be considered inactive.
1211
+						$this->_active_status = EE_Datetime::inactive;
1212
+					}
1213
+				}
1214
+			}
1215
+		} else {
1216
+			// the event is not published, so let's just set it's active status according to its' post status
1217
+			switch ($this->status()) {
1218
+				case EEM_Event::sold_out:
1219
+					$this->_active_status = EE_Datetime::sold_out;
1220
+					break;
1221
+				case EEM_Event::cancelled:
1222
+					$this->_active_status = EE_Datetime::cancelled;
1223
+					break;
1224
+				case EEM_Event::postponed:
1225
+					$this->_active_status = EE_Datetime::postponed;
1226
+					break;
1227
+				default:
1228
+					$this->_active_status = EE_Datetime::inactive;
1229
+			}
1230
+		}
1231
+		return $this->_active_status;
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 *    pretty_active_status
1237
+	 *
1238
+	 * @param boolean $echo whether to return (FALSE), or echo out the result (TRUE)
1239
+	 * @return string
1240
+	 * @throws EE_Error
1241
+	 * @throws ReflectionException
1242
+	 */
1243
+	public function pretty_active_status(bool $echo = true): string
1244
+	{
1245
+		$active_status = $this->get_active_status();
1246
+		$status        = "
1247 1247
         <span class='event-active-status event-active-status-$active_status ee-status ee-status-bg--$active_status'>
1248 1248
             " . EEH_Template::pretty_status($active_status, false, 'sentence') . "
1249 1249
         </span >";
1250
-        if ($echo) {
1251
-            echo wp_kses($status, AllowedTags::getAllowedTags());
1252
-            return '';
1253
-        }
1254
-        return $status;
1255
-    }
1256
-
1257
-
1258
-    /**
1259
-     * @return bool|int
1260
-     * @throws EE_Error
1261
-     * @throws ReflectionException
1262
-     */
1263
-    public function get_number_of_tickets_sold()
1264
-    {
1265
-        $tkt_sold = 0;
1266
-        if (! $this->ID()) {
1267
-            return 0;
1268
-        }
1269
-        $datetimes = $this->datetimes();
1270
-        foreach ($datetimes as $datetime) {
1271
-            if ($datetime instanceof EE_Datetime) {
1272
-                $tkt_sold += $datetime->sold();
1273
-            }
1274
-        }
1275
-        return $tkt_sold;
1276
-    }
1277
-
1278
-
1279
-    /**
1280
-     * This just returns a count of all the registrations for this event
1281
-     *
1282
-     * @return int
1283
-     * @throws EE_Error
1284
-     * @throws ReflectionException
1285
-     */
1286
-    public function get_count_of_all_registrations(): int
1287
-    {
1288
-        return EEM_Event::instance()->count_related($this, 'Registration');
1289
-    }
1290
-
1291
-
1292
-    /**
1293
-     * This returns the ticket with the earliest start time that is
1294
-     * available for this event (across all datetimes attached to the event)
1295
-     *
1296
-     * @return EE_Base_Class|EE_Ticket|null
1297
-     * @throws EE_Error
1298
-     * @throws ReflectionException
1299
-     */
1300
-    public function get_ticket_with_earliest_start_time()
1301
-    {
1302
-        $where['Datetime.EVT_ID'] = $this->ID();
1303
-        $query_params             = [$where, 'order_by' => ['TKT_start_date' => 'ASC']];
1304
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1305
-    }
1306
-
1307
-
1308
-    /**
1309
-     * This returns the ticket with the latest end time that is available
1310
-     * for this event (across all datetimes attached to the event)
1311
-     *
1312
-     * @return EE_Base_Class|EE_Ticket|null
1313
-     * @throws EE_Error
1314
-     * @throws ReflectionException
1315
-     */
1316
-    public function get_ticket_with_latest_end_time()
1317
-    {
1318
-        $where['Datetime.EVT_ID'] = $this->ID();
1319
-        $query_params             = [$where, 'order_by' => ['TKT_end_date' => 'DESC']];
1320
-        return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1321
-    }
1322
-
1323
-
1324
-    /**
1325
-     * This returns the number of different ticket types currently on sale for this event.
1326
-     *
1327
-     * @return int
1328
-     * @throws EE_Error
1329
-     * @throws ReflectionException
1330
-     */
1331
-    public function countTicketsOnSale(): int
1332
-    {
1333
-        $where = [
1334
-            'Datetime.EVT_ID' => $this->ID(),
1335
-            'TKT_start_date'  => ['<', time()],
1336
-            'TKT_end_date'    => ['>', time()],
1337
-        ];
1338
-        return EEM_Ticket::instance()->count([$where]);
1339
-    }
1340
-
1341
-
1342
-    /**
1343
-     * This returns whether there are any tickets on sale for this event.
1344
-     *
1345
-     * @return bool true = YES tickets on sale.
1346
-     * @throws EE_Error
1347
-     * @throws ReflectionException
1348
-     */
1349
-    public function tickets_on_sale(): bool
1350
-    {
1351
-        return $this->countTicketsOnSale() > 0;
1352
-    }
1353
-
1354
-
1355
-    /**
1356
-     * Gets the URL for viewing this event on the front-end. Overrides parent
1357
-     * to check for an external URL first
1358
-     *
1359
-     * @return string
1360
-     * @throws EE_Error
1361
-     * @throws ReflectionException
1362
-     */
1363
-    public function get_permalink(): string
1364
-    {
1365
-        if ($this->external_url()) {
1366
-            return $this->external_url();
1367
-        }
1368
-        return parent::get_permalink();
1369
-    }
1370
-
1371
-
1372
-    /**
1373
-     * Gets the first term for 'espresso_event_categories' we can find
1374
-     *
1375
-     * @param array $query_params
1376
-     * @return EE_Base_Class|EE_Term|null
1377
-     * @throws EE_Error
1378
-     * @throws ReflectionException
1379
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1380
-     */
1381
-    public function first_event_category(array $query_params = []): ?EE_Term
1382
-    {
1383
-        $query_params[0]['Term_Taxonomy.taxonomy']     = 'espresso_event_categories';
1384
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1385
-        return EEM_Term::instance()->get_one($query_params);
1386
-    }
1387
-
1388
-
1389
-    /**
1390
-     * Gets all terms for 'espresso_event_categories' we can find
1391
-     *
1392
-     * @param array $query_params
1393
-     * @return EE_Base_Class[]|EE_Term[]
1394
-     * @throws EE_Error
1395
-     * @throws ReflectionException
1396
-     */
1397
-    public function get_all_event_categories(array $query_params = []): array
1398
-    {
1399
-        $query_params[0]['Term_Taxonomy.taxonomy']     = 'espresso_event_categories';
1400
-        $query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1401
-        return EEM_Term::instance()->get_all($query_params);
1402
-    }
1403
-
1404
-
1405
-    /**
1406
-     * Adds a question group to this event
1407
-     *
1408
-     * @param EE_Question_Group|int $question_group_id_or_obj
1409
-     * @param bool                  $for_primary if true, the question group will be added for the primary
1410
-     *                                           registrant, if false will be added for others. default: false
1411
-     * @return EE_Base_Class|EE_Question_Group
1412
-     * @throws EE_Error
1413
-     * @throws InvalidArgumentException
1414
-     * @throws InvalidDataTypeException
1415
-     * @throws InvalidInterfaceException
1416
-     * @throws ReflectionException
1417
-     */
1418
-    public function add_question_group($question_group_id_or_obj, bool $for_primary = false): EE_Question_Group
1419
-    {
1420
-        // If the row already exists, it will be updated. If it doesn't, it will be inserted.
1421
-        // That's in EE_HABTM_Relation::add_relation_to().
1422
-        return $this->_add_relation_to(
1423
-            $question_group_id_or_obj,
1424
-            'Question_Group',
1425
-            [
1426
-                EEM_Event_Question_Group::instance()->fieldNameForContext($for_primary) => true,
1427
-            ]
1428
-        );
1429
-    }
1430
-
1431
-
1432
-    /**
1433
-     * Removes a question group from the event
1434
-     *
1435
-     * @param EE_Question_Group|int $question_group_id_or_obj
1436
-     * @param bool                  $for_primary if true, the question group will be removed from the primary
1437
-     *                                           registrant, if false will be removed from others. default: false
1438
-     * @return EE_Base_Class|EE_Question_Group|int
1439
-     * @throws EE_Error
1440
-     * @throws InvalidArgumentException
1441
-     * @throws ReflectionException
1442
-     * @throws InvalidDataTypeException
1443
-     * @throws InvalidInterfaceException
1444
-     */
1445
-    public function remove_question_group($question_group_id_or_obj, bool $for_primary = false)
1446
-    {
1447
-        // If the question group is used for the other type (primary or additional)
1448
-        // then just update it. If not, delete it outright.
1449
-        $existing_relation = $this->get_first_related(
1450
-            'Event_Question_Group',
1451
-            [
1452
-                [
1453
-                    'QSG_ID' => EEM_Question_Group::instance()->ensure_is_ID($question_group_id_or_obj),
1454
-                ],
1455
-            ]
1456
-        );
1457
-        $field_to_update   = EEM_Event_Question_Group::instance()->fieldNameForContext($for_primary);
1458
-        $other_field       = EEM_Event_Question_Group::instance()->fieldNameForContext(! $for_primary);
1459
-        if ($existing_relation->get($other_field) === false) {
1460
-            // Delete it. It's now no longer for primary or additional question groups.
1461
-            return $this->_remove_relation_to($question_group_id_or_obj, 'Question_Group');
1462
-        }
1463
-        // Just update it. They'll still use this question group for the other category
1464
-        $existing_relation->save(
1465
-            [
1466
-                $field_to_update => false,
1467
-            ]
1468
-        );
1469
-        return $question_group_id_or_obj;
1470
-    }
1471
-
1472
-
1473
-    /**
1474
-     * Gets all the question groups, ordering them by QSG_order ascending
1475
-     *
1476
-     * @param array $query_params
1477
-     * @return EE_Base_Class[]|EE_Question_Group[]
1478
-     * @throws EE_Error
1479
-     * @throws ReflectionException
1480
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1481
-     */
1482
-    public function question_groups(array $query_params = []): array
1483
-    {
1484
-        $query_params = ! empty($query_params) ? $query_params : ['order_by' => ['QSG_order' => 'ASC']];
1485
-        return $this->get_many_related('Question_Group', $query_params);
1486
-    }
1487
-
1488
-
1489
-    /**
1490
-     * Implementation for EEI_Has_Icon interface method.
1491
-     *
1492
-     * @return string
1493
-     * @see EEI_Visual_Representation for comments
1494
-     */
1495
-    public function get_icon(): string
1496
-    {
1497
-        return '<span class="dashicons dashicons-flag"></span>';
1498
-    }
1499
-
1500
-
1501
-    /**
1502
-     * Implementation for EEI_Admin_Links interface method.
1503
-     *
1504
-     * @return string
1505
-     * @throws EE_Error
1506
-     * @throws ReflectionException
1507
-     * @see EEI_Admin_Links for comments
1508
-     */
1509
-    public function get_admin_details_link(): string
1510
-    {
1511
-        return $this->get_admin_edit_link();
1512
-    }
1513
-
1514
-
1515
-    /**
1516
-     * Implementation for EEI_Admin_Links interface method.
1517
-     *
1518
-     * @return string
1519
-     * @throws EE_Error
1520
-     * @throws ReflectionException
1521
-     * @see EEI_Admin_Links for comments
1522
-     */
1523
-    public function get_admin_edit_link(): string
1524
-    {
1525
-        return EEH_URL::add_query_args_and_nonce(
1526
-            [
1527
-                'page'   => 'espresso_events',
1528
-                'action' => 'edit',
1529
-                'post'   => $this->ID(),
1530
-            ],
1531
-            admin_url('admin.php')
1532
-        );
1533
-    }
1534
-
1535
-
1536
-    /**
1537
-     * Implementation for EEI_Admin_Links interface method.
1538
-     *
1539
-     * @return string
1540
-     * @see EEI_Admin_Links for comments
1541
-     */
1542
-    public function get_admin_settings_link(): string
1543
-    {
1544
-        return EEH_URL::add_query_args_and_nonce(
1545
-            [
1546
-                'page'   => 'espresso_events',
1547
-                'action' => 'default_event_settings',
1548
-            ],
1549
-            admin_url('admin.php')
1550
-        );
1551
-    }
1552
-
1553
-
1554
-    /**
1555
-     * Implementation for EEI_Admin_Links interface method.
1556
-     *
1557
-     * @return string
1558
-     * @see EEI_Admin_Links for comments
1559
-     */
1560
-    public function get_admin_overview_link(): string
1561
-    {
1562
-        return EEH_URL::add_query_args_and_nonce(
1563
-            [
1564
-                'page'   => 'espresso_events',
1565
-                'action' => 'default',
1566
-            ],
1567
-            admin_url('admin.php')
1568
-        );
1569
-    }
1570
-
1571
-
1572
-    /**
1573
-     * @return string|null
1574
-     * @throws EE_Error
1575
-     * @throws ReflectionException
1576
-     */
1577
-    public function registrationFormUuid(): ?string
1578
-    {
1579
-        return $this->get('FSC_UUID') ?? '';
1580
-    }
1581
-
1582
-
1583
-    /**
1584
-     * Gets all the form sections for this event
1585
-     *
1586
-     * @return EE_Base_Class[]|EE_Form_Section[]
1587
-     * @throws EE_Error
1588
-     * @throws ReflectionException
1589
-     */
1590
-    public function registrationForm(): array
1591
-    {
1592
-        $FSC_UUID = $this->registrationFormUuid();
1593
-
1594
-        if (empty($FSC_UUID)) {
1595
-            return [];
1596
-        }
1597
-
1598
-        return EEM_Form_Section::instance()->get_all(
1599
-            [
1600
-                [
1601
-                    'OR' => [
1602
-                        'FSC_UUID'      => $FSC_UUID, // top level form
1603
-                        'FSC_belongsTo' => $FSC_UUID, // child form sections
1604
-                    ],
1605
-                ],
1606
-                'order_by' => ['FSC_order' => 'ASC'],
1607
-            ]
1608
-        );
1609
-    }
1610
-
1611
-
1612
-    /**
1613
-     * @param string $UUID
1614
-     * @throws EE_Error
1615
-     * @throws ReflectionException
1616
-     */
1617
-    public function setRegistrationFormUuid(string $UUID): void
1618
-    {
1619
-        if (! Cuid::isCuid($UUID)) {
1620
-            throw new InvalidArgumentException(
1621
-                sprintf(
1622
-                /* translators: 1: UUID value, 2: UUID generator function. */
1623
-                    esc_html__(
1624
-                        'The supplied UUID "%1$s" is invalid or missing. Please use %2$s to generate a valid one.',
1625
-                        'event_espresso'
1626
-                    ),
1627
-                    $UUID,
1628
-                    '`Cuid::cuid()`'
1629
-                )
1630
-            );
1631
-        }
1632
-        $this->set('FSC_UUID', $UUID);
1633
-    }
1634
-
1635
-
1636
-    /**
1637
-     * Get visibility status of event
1638
-     *
1639
-     * @param bool $hide_public
1640
-     * @return string
1641
-     */
1642
-    public function get_visibility_status(bool $hide_public = true): string
1643
-    {
1644
-        if ($this->status() === 'private') {
1645
-            return esc_html__('Private', 'event_espresso');
1646
-        }
1647
-        if (! empty($this->wp_post()->post_password)) {
1648
-            return esc_html__('Password Protected', 'event_espresso');
1649
-        }
1650
-        if (! $hide_public) {
1651
-            return esc_html__('Public', 'event_espresso');
1652
-        }
1653
-
1654
-        return '';
1655
-    }
1250
+		if ($echo) {
1251
+			echo wp_kses($status, AllowedTags::getAllowedTags());
1252
+			return '';
1253
+		}
1254
+		return $status;
1255
+	}
1256
+
1257
+
1258
+	/**
1259
+	 * @return bool|int
1260
+	 * @throws EE_Error
1261
+	 * @throws ReflectionException
1262
+	 */
1263
+	public function get_number_of_tickets_sold()
1264
+	{
1265
+		$tkt_sold = 0;
1266
+		if (! $this->ID()) {
1267
+			return 0;
1268
+		}
1269
+		$datetimes = $this->datetimes();
1270
+		foreach ($datetimes as $datetime) {
1271
+			if ($datetime instanceof EE_Datetime) {
1272
+				$tkt_sold += $datetime->sold();
1273
+			}
1274
+		}
1275
+		return $tkt_sold;
1276
+	}
1277
+
1278
+
1279
+	/**
1280
+	 * This just returns a count of all the registrations for this event
1281
+	 *
1282
+	 * @return int
1283
+	 * @throws EE_Error
1284
+	 * @throws ReflectionException
1285
+	 */
1286
+	public function get_count_of_all_registrations(): int
1287
+	{
1288
+		return EEM_Event::instance()->count_related($this, 'Registration');
1289
+	}
1290
+
1291
+
1292
+	/**
1293
+	 * This returns the ticket with the earliest start time that is
1294
+	 * available for this event (across all datetimes attached to the event)
1295
+	 *
1296
+	 * @return EE_Base_Class|EE_Ticket|null
1297
+	 * @throws EE_Error
1298
+	 * @throws ReflectionException
1299
+	 */
1300
+	public function get_ticket_with_earliest_start_time()
1301
+	{
1302
+		$where['Datetime.EVT_ID'] = $this->ID();
1303
+		$query_params             = [$where, 'order_by' => ['TKT_start_date' => 'ASC']];
1304
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1305
+	}
1306
+
1307
+
1308
+	/**
1309
+	 * This returns the ticket with the latest end time that is available
1310
+	 * for this event (across all datetimes attached to the event)
1311
+	 *
1312
+	 * @return EE_Base_Class|EE_Ticket|null
1313
+	 * @throws EE_Error
1314
+	 * @throws ReflectionException
1315
+	 */
1316
+	public function get_ticket_with_latest_end_time()
1317
+	{
1318
+		$where['Datetime.EVT_ID'] = $this->ID();
1319
+		$query_params             = [$where, 'order_by' => ['TKT_end_date' => 'DESC']];
1320
+		return EE_Registry::instance()->load_model('Ticket')->get_one($query_params);
1321
+	}
1322
+
1323
+
1324
+	/**
1325
+	 * This returns the number of different ticket types currently on sale for this event.
1326
+	 *
1327
+	 * @return int
1328
+	 * @throws EE_Error
1329
+	 * @throws ReflectionException
1330
+	 */
1331
+	public function countTicketsOnSale(): int
1332
+	{
1333
+		$where = [
1334
+			'Datetime.EVT_ID' => $this->ID(),
1335
+			'TKT_start_date'  => ['<', time()],
1336
+			'TKT_end_date'    => ['>', time()],
1337
+		];
1338
+		return EEM_Ticket::instance()->count([$where]);
1339
+	}
1340
+
1341
+
1342
+	/**
1343
+	 * This returns whether there are any tickets on sale for this event.
1344
+	 *
1345
+	 * @return bool true = YES tickets on sale.
1346
+	 * @throws EE_Error
1347
+	 * @throws ReflectionException
1348
+	 */
1349
+	public function tickets_on_sale(): bool
1350
+	{
1351
+		return $this->countTicketsOnSale() > 0;
1352
+	}
1353
+
1354
+
1355
+	/**
1356
+	 * Gets the URL for viewing this event on the front-end. Overrides parent
1357
+	 * to check for an external URL first
1358
+	 *
1359
+	 * @return string
1360
+	 * @throws EE_Error
1361
+	 * @throws ReflectionException
1362
+	 */
1363
+	public function get_permalink(): string
1364
+	{
1365
+		if ($this->external_url()) {
1366
+			return $this->external_url();
1367
+		}
1368
+		return parent::get_permalink();
1369
+	}
1370
+
1371
+
1372
+	/**
1373
+	 * Gets the first term for 'espresso_event_categories' we can find
1374
+	 *
1375
+	 * @param array $query_params
1376
+	 * @return EE_Base_Class|EE_Term|null
1377
+	 * @throws EE_Error
1378
+	 * @throws ReflectionException
1379
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1380
+	 */
1381
+	public function first_event_category(array $query_params = []): ?EE_Term
1382
+	{
1383
+		$query_params[0]['Term_Taxonomy.taxonomy']     = 'espresso_event_categories';
1384
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1385
+		return EEM_Term::instance()->get_one($query_params);
1386
+	}
1387
+
1388
+
1389
+	/**
1390
+	 * Gets all terms for 'espresso_event_categories' we can find
1391
+	 *
1392
+	 * @param array $query_params
1393
+	 * @return EE_Base_Class[]|EE_Term[]
1394
+	 * @throws EE_Error
1395
+	 * @throws ReflectionException
1396
+	 */
1397
+	public function get_all_event_categories(array $query_params = []): array
1398
+	{
1399
+		$query_params[0]['Term_Taxonomy.taxonomy']     = 'espresso_event_categories';
1400
+		$query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $this->ID();
1401
+		return EEM_Term::instance()->get_all($query_params);
1402
+	}
1403
+
1404
+
1405
+	/**
1406
+	 * Adds a question group to this event
1407
+	 *
1408
+	 * @param EE_Question_Group|int $question_group_id_or_obj
1409
+	 * @param bool                  $for_primary if true, the question group will be added for the primary
1410
+	 *                                           registrant, if false will be added for others. default: false
1411
+	 * @return EE_Base_Class|EE_Question_Group
1412
+	 * @throws EE_Error
1413
+	 * @throws InvalidArgumentException
1414
+	 * @throws InvalidDataTypeException
1415
+	 * @throws InvalidInterfaceException
1416
+	 * @throws ReflectionException
1417
+	 */
1418
+	public function add_question_group($question_group_id_or_obj, bool $for_primary = false): EE_Question_Group
1419
+	{
1420
+		// If the row already exists, it will be updated. If it doesn't, it will be inserted.
1421
+		// That's in EE_HABTM_Relation::add_relation_to().
1422
+		return $this->_add_relation_to(
1423
+			$question_group_id_or_obj,
1424
+			'Question_Group',
1425
+			[
1426
+				EEM_Event_Question_Group::instance()->fieldNameForContext($for_primary) => true,
1427
+			]
1428
+		);
1429
+	}
1430
+
1431
+
1432
+	/**
1433
+	 * Removes a question group from the event
1434
+	 *
1435
+	 * @param EE_Question_Group|int $question_group_id_or_obj
1436
+	 * @param bool                  $for_primary if true, the question group will be removed from the primary
1437
+	 *                                           registrant, if false will be removed from others. default: false
1438
+	 * @return EE_Base_Class|EE_Question_Group|int
1439
+	 * @throws EE_Error
1440
+	 * @throws InvalidArgumentException
1441
+	 * @throws ReflectionException
1442
+	 * @throws InvalidDataTypeException
1443
+	 * @throws InvalidInterfaceException
1444
+	 */
1445
+	public function remove_question_group($question_group_id_or_obj, bool $for_primary = false)
1446
+	{
1447
+		// If the question group is used for the other type (primary or additional)
1448
+		// then just update it. If not, delete it outright.
1449
+		$existing_relation = $this->get_first_related(
1450
+			'Event_Question_Group',
1451
+			[
1452
+				[
1453
+					'QSG_ID' => EEM_Question_Group::instance()->ensure_is_ID($question_group_id_or_obj),
1454
+				],
1455
+			]
1456
+		);
1457
+		$field_to_update   = EEM_Event_Question_Group::instance()->fieldNameForContext($for_primary);
1458
+		$other_field       = EEM_Event_Question_Group::instance()->fieldNameForContext(! $for_primary);
1459
+		if ($existing_relation->get($other_field) === false) {
1460
+			// Delete it. It's now no longer for primary or additional question groups.
1461
+			return $this->_remove_relation_to($question_group_id_or_obj, 'Question_Group');
1462
+		}
1463
+		// Just update it. They'll still use this question group for the other category
1464
+		$existing_relation->save(
1465
+			[
1466
+				$field_to_update => false,
1467
+			]
1468
+		);
1469
+		return $question_group_id_or_obj;
1470
+	}
1471
+
1472
+
1473
+	/**
1474
+	 * Gets all the question groups, ordering them by QSG_order ascending
1475
+	 *
1476
+	 * @param array $query_params
1477
+	 * @return EE_Base_Class[]|EE_Question_Group[]
1478
+	 * @throws EE_Error
1479
+	 * @throws ReflectionException
1480
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1481
+	 */
1482
+	public function question_groups(array $query_params = []): array
1483
+	{
1484
+		$query_params = ! empty($query_params) ? $query_params : ['order_by' => ['QSG_order' => 'ASC']];
1485
+		return $this->get_many_related('Question_Group', $query_params);
1486
+	}
1487
+
1488
+
1489
+	/**
1490
+	 * Implementation for EEI_Has_Icon interface method.
1491
+	 *
1492
+	 * @return string
1493
+	 * @see EEI_Visual_Representation for comments
1494
+	 */
1495
+	public function get_icon(): string
1496
+	{
1497
+		return '<span class="dashicons dashicons-flag"></span>';
1498
+	}
1499
+
1500
+
1501
+	/**
1502
+	 * Implementation for EEI_Admin_Links interface method.
1503
+	 *
1504
+	 * @return string
1505
+	 * @throws EE_Error
1506
+	 * @throws ReflectionException
1507
+	 * @see EEI_Admin_Links for comments
1508
+	 */
1509
+	public function get_admin_details_link(): string
1510
+	{
1511
+		return $this->get_admin_edit_link();
1512
+	}
1513
+
1514
+
1515
+	/**
1516
+	 * Implementation for EEI_Admin_Links interface method.
1517
+	 *
1518
+	 * @return string
1519
+	 * @throws EE_Error
1520
+	 * @throws ReflectionException
1521
+	 * @see EEI_Admin_Links for comments
1522
+	 */
1523
+	public function get_admin_edit_link(): string
1524
+	{
1525
+		return EEH_URL::add_query_args_and_nonce(
1526
+			[
1527
+				'page'   => 'espresso_events',
1528
+				'action' => 'edit',
1529
+				'post'   => $this->ID(),
1530
+			],
1531
+			admin_url('admin.php')
1532
+		);
1533
+	}
1534
+
1535
+
1536
+	/**
1537
+	 * Implementation for EEI_Admin_Links interface method.
1538
+	 *
1539
+	 * @return string
1540
+	 * @see EEI_Admin_Links for comments
1541
+	 */
1542
+	public function get_admin_settings_link(): string
1543
+	{
1544
+		return EEH_URL::add_query_args_and_nonce(
1545
+			[
1546
+				'page'   => 'espresso_events',
1547
+				'action' => 'default_event_settings',
1548
+			],
1549
+			admin_url('admin.php')
1550
+		);
1551
+	}
1552
+
1553
+
1554
+	/**
1555
+	 * Implementation for EEI_Admin_Links interface method.
1556
+	 *
1557
+	 * @return string
1558
+	 * @see EEI_Admin_Links for comments
1559
+	 */
1560
+	public function get_admin_overview_link(): string
1561
+	{
1562
+		return EEH_URL::add_query_args_and_nonce(
1563
+			[
1564
+				'page'   => 'espresso_events',
1565
+				'action' => 'default',
1566
+			],
1567
+			admin_url('admin.php')
1568
+		);
1569
+	}
1570
+
1571
+
1572
+	/**
1573
+	 * @return string|null
1574
+	 * @throws EE_Error
1575
+	 * @throws ReflectionException
1576
+	 */
1577
+	public function registrationFormUuid(): ?string
1578
+	{
1579
+		return $this->get('FSC_UUID') ?? '';
1580
+	}
1581
+
1582
+
1583
+	/**
1584
+	 * Gets all the form sections for this event
1585
+	 *
1586
+	 * @return EE_Base_Class[]|EE_Form_Section[]
1587
+	 * @throws EE_Error
1588
+	 * @throws ReflectionException
1589
+	 */
1590
+	public function registrationForm(): array
1591
+	{
1592
+		$FSC_UUID = $this->registrationFormUuid();
1593
+
1594
+		if (empty($FSC_UUID)) {
1595
+			return [];
1596
+		}
1597
+
1598
+		return EEM_Form_Section::instance()->get_all(
1599
+			[
1600
+				[
1601
+					'OR' => [
1602
+						'FSC_UUID'      => $FSC_UUID, // top level form
1603
+						'FSC_belongsTo' => $FSC_UUID, // child form sections
1604
+					],
1605
+				],
1606
+				'order_by' => ['FSC_order' => 'ASC'],
1607
+			]
1608
+		);
1609
+	}
1610
+
1611
+
1612
+	/**
1613
+	 * @param string $UUID
1614
+	 * @throws EE_Error
1615
+	 * @throws ReflectionException
1616
+	 */
1617
+	public function setRegistrationFormUuid(string $UUID): void
1618
+	{
1619
+		if (! Cuid::isCuid($UUID)) {
1620
+			throw new InvalidArgumentException(
1621
+				sprintf(
1622
+				/* translators: 1: UUID value, 2: UUID generator function. */
1623
+					esc_html__(
1624
+						'The supplied UUID "%1$s" is invalid or missing. Please use %2$s to generate a valid one.',
1625
+						'event_espresso'
1626
+					),
1627
+					$UUID,
1628
+					'`Cuid::cuid()`'
1629
+				)
1630
+			);
1631
+		}
1632
+		$this->set('FSC_UUID', $UUID);
1633
+	}
1634
+
1635
+
1636
+	/**
1637
+	 * Get visibility status of event
1638
+	 *
1639
+	 * @param bool $hide_public
1640
+	 * @return string
1641
+	 */
1642
+	public function get_visibility_status(bool $hide_public = true): string
1643
+	{
1644
+		if ($this->status() === 'private') {
1645
+			return esc_html__('Private', 'event_espresso');
1646
+		}
1647
+		if (! empty($this->wp_post()->post_password)) {
1648
+			return esc_html__('Password Protected', 'event_espresso');
1649
+		}
1650
+		if (! $hide_public) {
1651
+			return esc_html__('Public', 'event_espresso');
1652
+		}
1653
+
1654
+		return '';
1655
+	}
1656 1656
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Transaction.class.php 2 patches
Indentation   +1749 added lines, -1749 removed lines patch added patch discarded remove patch
@@ -13,1753 +13,1753 @@
 block discarded – undo
13 13
  */
14 14
 class EE_Transaction extends EE_Base_Class implements EEI_Transaction
15 15
 {
16
-    /**
17
-     * The length of time in seconds that a lock is applied before being considered expired.
18
-     * It is not long because a transaction should only be locked for the duration of the request that locked it
19
-     */
20
-    const LOCK_EXPIRATION = 2;
21
-
22
-    /**
23
-     * txn status upon initial construction.
24
-     *
25
-     * @var string
26
-     */
27
-    protected $_old_txn_status;
28
-
29
-    private ?EE_Registration $_primary_registrant = null;
30
-
31
-    /**
32
-     * @param array  $props_n_values          incoming values
33
-     * @param string $timezone                incoming timezone
34
-     *                                        (if not set the timezone set for the website will be used.)
35
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
36
-     *                                        date_format and the second value is the time format
37
-     * @return EE_Transaction
38
-     * @throws EE_Error
39
-     * @throws InvalidArgumentException
40
-     * @throws InvalidDataTypeException
41
-     * @throws InvalidInterfaceException
42
-     * @throws ReflectionException
43
-     */
44
-    public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
45
-    {
46
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
47
-        $txn        = $has_object
48
-            ? $has_object
49
-            : new self($props_n_values, false, $timezone, $date_formats);
50
-        if (! $has_object) {
51
-            $txn->set_old_txn_status($txn->status_ID());
52
-        }
53
-        return $txn;
54
-    }
55
-
56
-
57
-    /**
58
-     * @param array  $props_n_values  incoming values from the database
59
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
60
-     *                                the website will be used.
61
-     * @return EE_Transaction
62
-     * @throws EE_Error
63
-     * @throws InvalidArgumentException
64
-     * @throws InvalidDataTypeException
65
-     * @throws InvalidInterfaceException
66
-     * @throws ReflectionException
67
-     */
68
-    public static function new_instance_from_db($props_n_values = [], $timezone = '')
69
-    {
70
-        $txn = new self($props_n_values, true, $timezone);
71
-        $txn->set_old_txn_status($txn->status_ID());
72
-        return $txn;
73
-    }
74
-
75
-
76
-    /**
77
-     * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
78
-     * If a lock has already been set, then we will attempt to remove it in case it has expired.
79
-     * If that also fails, then an exception is thrown.
80
-     *
81
-     * @throws EE_Error
82
-     * @throws InvalidArgumentException
83
-     * @throws InvalidDataTypeException
84
-     * @throws InvalidInterfaceException
85
-     * @throws ReflectionException
86
-     */
87
-    public function lock()
88
-    {
89
-        // attempt to set lock, but if that fails...
90
-        if (! $this->add_extra_meta('lock', time(), true)) {
91
-            // then attempt to remove the lock in case it is expired
92
-            if ($this->_remove_expired_lock()) {
93
-                // if removal was successful, then try setting lock again
94
-                $this->lock();
95
-            } else {
96
-                // but if the lock can not be removed, then throw an exception
97
-                throw new EE_Error(
98
-                    sprintf(
99
-                        esc_html__(
100
-                            'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.',
101
-                            'event_espresso'
102
-                        ),
103
-                        $this->ID()
104
-                    )
105
-                );
106
-            }
107
-        }
108
-    }
109
-
110
-
111
-    /**
112
-     * removes transaction lock applied in EE_Transaction::lock()
113
-     *
114
-     * @return int
115
-     * @throws EE_Error
116
-     * @throws InvalidArgumentException
117
-     * @throws InvalidDataTypeException
118
-     * @throws InvalidInterfaceException
119
-     * @throws ReflectionException
120
-     */
121
-    public function unlock()
122
-    {
123
-        return $this->delete_extra_meta('lock');
124
-    }
125
-
126
-
127
-    /**
128
-     * Decides whether or not now is the right time to update the transaction.
129
-     * This is useful because we don't always know if it is safe to update the transaction
130
-     * and its related data. why?
131
-     * because it's possible that the transaction is being used in another
132
-     * request and could overwrite anything we save.
133
-     * So we want to only update the txn once we know that won't happen.
134
-     * We also check that the lock isn't expired, and remove it if it is
135
-     *
136
-     * @return boolean
137
-     * @throws EE_Error
138
-     * @throws InvalidArgumentException
139
-     * @throws InvalidDataTypeException
140
-     * @throws InvalidInterfaceException
141
-     * @throws ReflectionException
142
-     */
143
-    public function is_locked()
144
-    {
145
-        // if TXN is not locked, then return false immediately
146
-        if (! $this->_get_lock()) {
147
-            return false;
148
-        }
149
-        // if not, then let's try and remove the lock in case it's expired...
150
-        // _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
151
-        // and a positive number if the lock was removed (ie: number of locks deleted),
152
-        // so we need to return the opposite
153
-        return ! $this->_remove_expired_lock();
154
-    }
155
-
156
-
157
-    /**
158
-     * Gets the meta field indicating that this TXN is locked
159
-     *
160
-     * @return int
161
-     * @throws EE_Error
162
-     * @throws InvalidArgumentException
163
-     * @throws InvalidDataTypeException
164
-     * @throws InvalidInterfaceException
165
-     * @throws ReflectionException
166
-     */
167
-    protected function _get_lock()
168
-    {
169
-        return (int) $this->get_extra_meta('lock', true, 0);
170
-    }
171
-
172
-
173
-    /**
174
-     * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
175
-     *
176
-     * @return int
177
-     * @throws EE_Error
178
-     * @throws InvalidArgumentException
179
-     * @throws InvalidDataTypeException
180
-     * @throws InvalidInterfaceException
181
-     * @throws ReflectionException
182
-     */
183
-    protected function _remove_expired_lock()
184
-    {
185
-        $locked = $this->_get_lock();
186
-        if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
187
-            return $this->unlock();
188
-        }
189
-        return 0;
190
-    }
191
-
192
-
193
-    /**
194
-     * Set transaction total
195
-     *
196
-     * @param float $total total value of transaction
197
-     * @throws EE_Error
198
-     * @throws InvalidArgumentException
199
-     * @throws InvalidDataTypeException
200
-     * @throws InvalidInterfaceException
201
-     * @throws ReflectionException
202
-     */
203
-    public function set_total($total = 0.00)
204
-    {
205
-        $this->set('TXN_total', (float) $total);
206
-    }
207
-
208
-
209
-    /**
210
-     * Set Total Amount Paid to Date
211
-     *
212
-     * @param float $total_paid total amount paid to date (sum of all payments)
213
-     * @throws EE_Error
214
-     * @throws InvalidArgumentException
215
-     * @throws InvalidDataTypeException
216
-     * @throws InvalidInterfaceException
217
-     * @throws ReflectionException
218
-     */
219
-    public function set_paid($total_paid = 0.00)
220
-    {
221
-        $this->set('TXN_paid', (float) $total_paid);
222
-    }
223
-
224
-
225
-    /**
226
-     * Set transaction status
227
-     *
228
-     * @param string $status        whether the transaction is open, declined, accepted,
229
-     *                              or any number of custom values that can be set
230
-     * @throws EE_Error
231
-     * @throws InvalidArgumentException
232
-     * @throws InvalidDataTypeException
233
-     * @throws InvalidInterfaceException
234
-     * @throws ReflectionException
235
-     */
236
-    public function set_status($status = '')
237
-    {
238
-        $this->set('STS_ID', $status);
239
-    }
240
-
241
-
242
-    /**
243
-     * Set hash salt
244
-     *
245
-     * @param string $hash_salt required for some payment gateways
246
-     * @throws EE_Error
247
-     * @throws InvalidArgumentException
248
-     * @throws InvalidDataTypeException
249
-     * @throws InvalidInterfaceException
250
-     * @throws ReflectionException
251
-     */
252
-    public function set_hash_salt($hash_salt = '')
253
-    {
254
-        $this->set('TXN_hash_salt', $hash_salt);
255
-    }
256
-
257
-
258
-    /**
259
-     * Sets TXN_reg_steps array
260
-     *
261
-     * @param array $txn_reg_steps
262
-     * @throws EE_Error
263
-     * @throws InvalidArgumentException
264
-     * @throws InvalidDataTypeException
265
-     * @throws InvalidInterfaceException
266
-     * @throws ReflectionException
267
-     */
268
-    public function set_reg_steps(array $txn_reg_steps)
269
-    {
270
-        $this->set('TXN_reg_steps', $txn_reg_steps);
271
-    }
272
-
273
-
274
-    /**
275
-     * Gets TXN_reg_steps
276
-     *
277
-     * @return array
278
-     * @throws EE_Error
279
-     * @throws InvalidArgumentException
280
-     * @throws InvalidDataTypeException
281
-     * @throws InvalidInterfaceException
282
-     * @throws ReflectionException
283
-     */
284
-    public function reg_steps()
285
-    {
286
-        $TXN_reg_steps = $this->get('TXN_reg_steps');
287
-        return is_array($TXN_reg_steps)
288
-            ? $TXN_reg_steps
289
-            : [];
290
-    }
291
-
292
-
293
-    /**
294
-     * @param string|null $schema
295
-     *     Schemas:
296
-     *     'localized_float': "3,023.00"
297
-     *     'no_currency_code': "$3,023.00"
298
-     *     null: "$3,023.00<span>USD</span>"
299
-     * @return string of transaction's total cost, with currency symbol and decimal
300
-     * @throws EE_Error
301
-     * @throws InvalidArgumentException
302
-     * @throws InvalidDataTypeException
303
-     * @throws InvalidInterfaceException
304
-     * @throws ReflectionException
305
-     */
306
-    public function pretty_total(?string $schema = null)
307
-    {
308
-        return $this->get_pretty('TXN_total', $schema);
309
-    }
310
-
311
-
312
-    /**
313
-     * Gets the amount paid in a pretty string (formatted and with currency symbol)
314
-     *
315
-     * @param string|null $schema
316
-     *     Schemas:
317
-     *     'localized_float': "3,023.00"
318
-     *     'no_currency_code': "$3,023.00"
319
-     *     null: "$3,023.00<span>USD</span>"
320
-     * @return string
321
-     * @throws EE_Error
322
-     * @throws InvalidArgumentException
323
-     * @throws InvalidDataTypeException
324
-     * @throws InvalidInterfaceException
325
-     * @throws ReflectionException
326
-     */
327
-    public function pretty_paid(?string $schema = null)
328
-    {
329
-        return $this->get_pretty('TXN_paid', $schema);
330
-    }
331
-
332
-
333
-    /**
334
-     * calculate the amount remaining for this transaction and return;
335
-     *
336
-     * @return float amount remaining
337
-     * @throws EE_Error
338
-     * @throws InvalidArgumentException
339
-     * @throws InvalidDataTypeException
340
-     * @throws InvalidInterfaceException
341
-     * @throws ReflectionException
342
-     */
343
-    public function remaining()
344
-    {
345
-        return $this->total() - $this->paid();
346
-    }
347
-
348
-
349
-    /**
350
-     * get Transaction Total
351
-     *
352
-     * @return float
353
-     * @throws EE_Error
354
-     * @throws InvalidArgumentException
355
-     * @throws InvalidDataTypeException
356
-     * @throws InvalidInterfaceException
357
-     * @throws ReflectionException
358
-     */
359
-    public function total()
360
-    {
361
-        return (float) $this->get('TXN_total');
362
-    }
363
-
364
-
365
-    /**
366
-     * get Total Amount Paid to Date
367
-     *
368
-     * @return float
369
-     * @throws EE_Error
370
-     * @throws InvalidArgumentException
371
-     * @throws InvalidDataTypeException
372
-     * @throws InvalidInterfaceException
373
-     * @throws ReflectionException
374
-     */
375
-    public function paid()
376
-    {
377
-        return (float) $this->get('TXN_paid');
378
-    }
379
-
380
-
381
-    /**
382
-     * @return mixed|null
383
-     * @throws EE_Error
384
-     * @throws InvalidArgumentException
385
-     * @throws InvalidDataTypeException
386
-     * @throws InvalidInterfaceException
387
-     * @throws ReflectionException
388
-     */
389
-    public function get_cart_session()
390
-    {
391
-        $session_data = (array) $this->get('TXN_session_data');
392
-        return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
393
-            ? $session_data['cart']
394
-            : null;
395
-    }
396
-
397
-
398
-    /**
399
-     * get Transaction session data
400
-     *
401
-     * @return array|mixed
402
-     * @throws EE_Error
403
-     * @throws InvalidArgumentException
404
-     * @throws InvalidDataTypeException
405
-     * @throws InvalidInterfaceException
406
-     * @throws ReflectionException
407
-     */
408
-    public function session_data()
409
-    {
410
-        $session_data = $this->get('TXN_session_data');
411
-        if (empty($session_data)) {
412
-            $session_data = [
413
-                'id'            => null,
414
-                'user_id'       => null,
415
-                'ip_address'    => null,
416
-                'user_agent'    => null,
417
-                'init_access'   => null,
418
-                'last_access'   => null,
419
-                'pages_visited' => [],
420
-            ];
421
-        }
422
-        return $session_data;
423
-    }
424
-
425
-
426
-    /**
427
-     * Set session data within the TXN object
428
-     *
429
-     * @param EE_Session|array|null $session_data
430
-     * @throws EE_Error
431
-     * @throws InvalidArgumentException
432
-     * @throws InvalidDataTypeException
433
-     * @throws InvalidInterfaceException
434
-     * @throws ReflectionException
435
-     */
436
-    public function set_txn_session_data($session_data)
437
-    {
438
-        if ($session_data instanceof EE_Session) {
439
-            $this->set('TXN_session_data', $session_data->get_session_data(null, true));
440
-        } else {
441
-            $this->set('TXN_session_data', $session_data);
442
-        }
443
-    }
444
-
445
-
446
-    /**
447
-     * get Transaction hash salt
448
-     *
449
-     * @return mixed
450
-     * @throws EE_Error
451
-     * @throws InvalidArgumentException
452
-     * @throws InvalidDataTypeException
453
-     * @throws InvalidInterfaceException
454
-     * @throws ReflectionException
455
-     */
456
-    public function hash_salt_()
457
-    {
458
-        return $this->get('TXN_hash_salt');
459
-    }
460
-
461
-
462
-    /**
463
-     * Returns the transaction datetime as either:
464
-     *            - unix timestamp format ($format = false, $gmt = true)
465
-     *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
466
-     *              has no affect with this option)), this also may include a timezone abbreviation if the
467
-     *              set timezone in this class differs from what the timezone is on the blog.
468
-     *            - formatted date string including the UTC (timezone) offset (default).
469
-     *
470
-     * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
471
-     * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
472
-     *                          or no UTC offset applied
473
-     * @return string | int
474
-     * @throws EE_Error
475
-     * @throws InvalidArgumentException
476
-     * @throws InvalidDataTypeException
477
-     * @throws InvalidInterfaceException
478
-     * @throws ReflectionException
479
-     */
480
-    public function datetime($format = false, $gmt = false)
481
-    {
482
-        if ($format) {
483
-            return $this->get_pretty('TXN_timestamp');
484
-        }
485
-        if ($gmt) {
486
-            return $this->get_raw('TXN_timestamp');
487
-        }
488
-        return $this->get('TXN_timestamp');
489
-    }
490
-
491
-
492
-    /**
493
-     * Gets registrations on this transaction
494
-     *
495
-     * @param array   $query_params array of query parameters
496
-     * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
497
-     * @return EE_Base_Class[]|EE_Registration[]
498
-     * @throws EE_Error
499
-     * @throws InvalidArgumentException
500
-     * @throws InvalidDataTypeException
501
-     * @throws InvalidInterfaceException
502
-     * @throws ReflectionException
503
-     */
504
-    public function registrations($query_params = [], $get_cached = false)
505
-    {
506
-        $query_params = (empty($query_params) || ! is_array($query_params))
507
-            ? [
508
-                'order_by' => [
509
-                    'Event.EVT_name'     => 'ASC',
510
-                    'Attendee.ATT_lname' => 'ASC',
511
-                    'Attendee.ATT_fname' => 'ASC',
512
-                    'REG_ID'             => 'ASC',
513
-                ],
514
-            ]
515
-            : $query_params;
516
-        $query_params = $get_cached
517
-            ? []
518
-            : $query_params;
519
-        return $this->get_many_related('Registration', $query_params);
520
-    }
521
-
522
-
523
-    /**
524
-     * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
525
-     * function for getting attendees and how many registrations they each have for an event)
526
-     *
527
-     * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
528
-     * @throws EE_Error
529
-     * @throws InvalidArgumentException
530
-     * @throws InvalidDataTypeException
531
-     * @throws InvalidInterfaceException
532
-     * @throws ReflectionException
533
-     */
534
-    public function attendees()
535
-    {
536
-        return $this->get_many_related('Attendee', [['Registration.Transaction.TXN_ID' => $this->ID()]]);
537
-    }
538
-
539
-
540
-    /**
541
-     * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
542
-     *
543
-     * @param array $query_params @see
544
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
545
-     * @return EE_Base_Class[]|EE_Payment[]
546
-     * @throws EE_Error
547
-     * @throws InvalidArgumentException
548
-     * @throws InvalidDataTypeException
549
-     * @throws InvalidInterfaceException
550
-     * @throws ReflectionException
551
-     */
552
-    public function payments($query_params = [])
553
-    {
554
-        return $this->get_many_related('Payment', $query_params);
555
-    }
556
-
557
-
558
-    /**
559
-     * gets only approved payments for this transaction
560
-     *
561
-     * @return EE_Base_Class[]|EE_Payment[]
562
-     * @throws EE_Error
563
-     * @throws InvalidArgumentException
564
-     * @throws ReflectionException
565
-     * @throws InvalidDataTypeException
566
-     * @throws InvalidInterfaceException
567
-     */
568
-    public function approved_payments()
569
-    {
570
-        EE_Registry::instance()->load_model('Payment');
571
-        return $this->get_many_related(
572
-            'Payment',
573
-            [
574
-                ['STS_ID' => EEM_Payment::status_id_approved],
575
-                'order_by' => ['PAY_timestamp' => 'DESC'],
576
-            ]
577
-        );
578
-    }
579
-
580
-
581
-    /**
582
-     * Gets all payments which have not been approved
583
-     *
584
-     * @return EE_Base_Class[]|EEI_Payment[]
585
-     * @throws EE_Error if a model is misconfigured somehow
586
-     * @throws InvalidArgumentException
587
-     * @throws InvalidDataTypeException
588
-     * @throws InvalidInterfaceException
589
-     * @throws ReflectionException
590
-     */
591
-    public function pending_payments()
592
-    {
593
-        return $this->get_many_related(
594
-            'Payment',
595
-            [
596
-                [
597
-                    'STS_ID' => EEM_Payment::status_id_pending,
598
-                ],
599
-                'order_by' => [
600
-                    'PAY_timestamp' => 'DESC',
601
-                ],
602
-            ]
603
-        );
604
-    }
605
-
606
-
607
-    /**
608
-     * echoes $this->pretty_status()
609
-     *
610
-     * @param bool $show_icons
611
-     * @throws EE_Error
612
-     * @throws InvalidArgumentException
613
-     * @throws InvalidDataTypeException
614
-     * @throws InvalidInterfaceException
615
-     * @throws ReflectionException
616
-     */
617
-    public function e_pretty_status($show_icons = false)
618
-    {
619
-        echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
620
-    }
621
-
622
-
623
-    /**
624
-     * returns a pretty version of the status, good for displaying to users
625
-     *
626
-     * @param bool $show_icons
627
-     * @return string
628
-     * @throws EE_Error
629
-     * @throws InvalidArgumentException
630
-     * @throws InvalidDataTypeException
631
-     * @throws InvalidInterfaceException
632
-     * @throws ReflectionException
633
-     */
634
-    public function pretty_status($show_icons = false)
635
-    {
636
-        $status = EEM_Status::instance()->localized_status(
637
-            [$this->status_ID() => esc_html__('unknown', 'event_espresso')],
638
-            false,
639
-            'sentence'
640
-        );
641
-        $icon   = '';
642
-        switch ($this->status_ID()) {
643
-            case EEM_Transaction::complete_status_code:
644
-                $icon = $show_icons
645
-                    ? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>'
646
-                    : '';
647
-                break;
648
-            case EEM_Transaction::incomplete_status_code:
649
-                $icon = $show_icons
650
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
651
-                    : '';
652
-                break;
653
-            case EEM_Transaction::abandoned_status_code:
654
-                $icon = $show_icons
655
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>'
656
-                    : '';
657
-                break;
658
-            case EEM_Transaction::failed_status_code:
659
-                $icon = $show_icons
660
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
661
-                    : '';
662
-                break;
663
-            case EEM_Transaction::overpaid_status_code:
664
-                $icon = $show_icons
665
-                    ? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>'
666
-                    : '';
667
-                break;
668
-        }
669
-        return $icon . $status[ $this->status_ID() ];
670
-    }
671
-
672
-
673
-    /**
674
-     * get Transaction Status
675
-     *
676
-     * @return mixed
677
-     * @throws EE_Error
678
-     * @throws InvalidArgumentException
679
-     * @throws InvalidDataTypeException
680
-     * @throws InvalidInterfaceException
681
-     * @throws ReflectionException
682
-     */
683
-    public function status_ID()
684
-    {
685
-        return $this->get('STS_ID');
686
-    }
687
-
688
-
689
-    /**
690
-     * Returns TRUE or FALSE for whether or not this transaction cost any money
691
-     *
692
-     * @return boolean
693
-     * @throws EE_Error
694
-     * @throws InvalidArgumentException
695
-     * @throws InvalidDataTypeException
696
-     * @throws InvalidInterfaceException
697
-     * @throws ReflectionException
698
-     */
699
-    public function is_free()
700
-    {
701
-        return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
702
-    }
703
-
704
-
705
-    /**
706
-     * Returns whether this transaction is complete
707
-     * Useful in templates and other logic for deciding if we should ask for another payment...
708
-     *
709
-     * @return boolean
710
-     * @throws EE_Error
711
-     * @throws InvalidArgumentException
712
-     * @throws InvalidDataTypeException
713
-     * @throws InvalidInterfaceException
714
-     * @throws ReflectionException
715
-     */
716
-    public function is_completed()
717
-    {
718
-        return $this->status_ID() === EEM_Transaction::complete_status_code;
719
-    }
720
-
721
-
722
-    /**
723
-     * Returns whether this transaction is incomplete
724
-     * Useful in templates and other logic for deciding if we should ask for another payment...
725
-     *
726
-     * @return boolean
727
-     * @throws EE_Error
728
-     * @throws InvalidArgumentException
729
-     * @throws InvalidDataTypeException
730
-     * @throws InvalidInterfaceException
731
-     * @throws ReflectionException
732
-     */
733
-    public function is_incomplete()
734
-    {
735
-        return $this->status_ID() === EEM_Transaction::incomplete_status_code;
736
-    }
737
-
738
-
739
-    /**
740
-     * Returns whether this transaction is overpaid
741
-     * Useful in templates and other logic for deciding if monies need to be refunded
742
-     *
743
-     * @return boolean
744
-     * @throws EE_Error
745
-     * @throws InvalidArgumentException
746
-     * @throws InvalidDataTypeException
747
-     * @throws InvalidInterfaceException
748
-     * @throws ReflectionException
749
-     */
750
-    public function is_overpaid()
751
-    {
752
-        return $this->status_ID() === EEM_Transaction::overpaid_status_code;
753
-    }
754
-
755
-
756
-    /**
757
-     * Returns whether this transaction was abandoned
758
-     * meaning that the transaction/registration process was somehow interrupted and never completed
759
-     * but that contact information exists for at least one registrant
760
-     *
761
-     * @return boolean
762
-     * @throws EE_Error
763
-     * @throws InvalidArgumentException
764
-     * @throws InvalidDataTypeException
765
-     * @throws InvalidInterfaceException
766
-     * @throws ReflectionException
767
-     */
768
-    public function is_abandoned()
769
-    {
770
-        return $this->status_ID() === EEM_Transaction::abandoned_status_code;
771
-    }
772
-
773
-
774
-    /**
775
-     * Returns whether this transaction failed
776
-     * meaning that the transaction/registration process was somehow interrupted and never completed
777
-     * and that NO contact information exists for any registrants
778
-     *
779
-     * @return boolean
780
-     * @throws EE_Error
781
-     * @throws InvalidArgumentException
782
-     * @throws InvalidDataTypeException
783
-     * @throws InvalidInterfaceException
784
-     * @throws ReflectionException
785
-     */
786
-    public function failed()
787
-    {
788
-        return $this->status_ID() === EEM_Transaction::failed_status_code;
789
-    }
790
-
791
-
792
-    /**
793
-     * This returns the url for the invoice of this transaction
794
-     *
795
-     * @param string $type 'html' or 'pdf' (default is pdf)
796
-     * @return string
797
-     * @throws DomainException
798
-     * @throws EE_Error
799
-     * @throws InvalidArgumentException
800
-     * @throws InvalidDataTypeException
801
-     * @throws InvalidInterfaceException
802
-     * @throws ReflectionException
803
-     */
804
-    public function invoice_url($type = 'html')
805
-    {
806
-        $REG = $this->primary_registration();
807
-        if (! $REG instanceof EE_Registration) {
808
-            return '';
809
-        }
810
-        return $REG->invoice_url($type);
811
-    }
812
-
813
-
814
-    /**
815
-     * Gets the primary registration only
816
-     *
817
-     * @return EE_Base_Class|EE_Registration
818
-     * @throws EE_Error
819
-     * @throws InvalidArgumentException
820
-     * @throws InvalidDataTypeException
821
-     * @throws InvalidInterfaceException
822
-     * @throws ReflectionException
823
-     */
824
-    public function primary_registration()
825
-    {
826
-        if ($this->_primary_registrant instanceof EE_Registration) {
827
-            return $this->_primary_registrant;
828
-        }
829
-
830
-        $registrations = (array) $this->get_many_related(
831
-            'Registration',
832
-            [['REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT]]
833
-        );
834
-        foreach ($registrations as $registration) {
835
-            // valid registration that is NOT cancelled or declined ?
836
-            if (
837
-                $registration instanceof EE_Registration
838
-                && ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
839
-            ) {
840
-                $this->_primary_registrant = $registration;
841
-                return $registration;
842
-            }
843
-        }
844
-        // nothing valid found, so just return first thing from array of results
845
-        $primary_registrant = reset($registrations);
846
-        $this->_primary_registrant = $primary_registrant instanceof EE_Registration ? $primary_registrant : null;
847
-        return $this->_primary_registrant;
848
-    }
849
-
850
-
851
-    /**
852
-     * Gets the URL for viewing the receipt
853
-     *
854
-     * @param string $type 'pdf' or 'html' (default is 'html')
855
-     * @return string
856
-     * @throws DomainException
857
-     * @throws EE_Error
858
-     * @throws InvalidArgumentException
859
-     * @throws InvalidDataTypeException
860
-     * @throws InvalidInterfaceException
861
-     * @throws ReflectionException
862
-     */
863
-    public function receipt_url($type = 'html')
864
-    {
865
-        $REG = $this->primary_registration();
866
-        if (! $REG instanceof EE_Registration) {
867
-            return '';
868
-        }
869
-        return $REG->receipt_url($type);
870
-    }
871
-
872
-
873
-    /**
874
-     * Gets the URL of the thank you page with this registration REG_url_link added as
875
-     * a query parameter
876
-     *
877
-     * @return string
878
-     * @throws EE_Error
879
-     * @throws InvalidArgumentException
880
-     * @throws InvalidDataTypeException
881
-     * @throws InvalidInterfaceException
882
-     * @throws ReflectionException
883
-     */
884
-    public function payment_overview_url()
885
-    {
886
-        $primary_registration = $this->primary_registration();
887
-        return $primary_registration instanceof EE_Registration
888
-            ? $primary_registration->payment_overview_url()
889
-            : false;
890
-    }
891
-
892
-
893
-    /**
894
-     * @return string
895
-     * @throws EE_Error
896
-     * @throws InvalidArgumentException
897
-     * @throws InvalidDataTypeException
898
-     * @throws InvalidInterfaceException
899
-     * @throws ReflectionException
900
-     */
901
-    public function gateway_response_on_transaction()
902
-    {
903
-        $payment = $this->get_first_related('Payment');
904
-        return $payment instanceof EE_Payment
905
-            ? $payment->gateway_response()
906
-            : '';
907
-    }
908
-
909
-
910
-    /**
911
-     * Get the status object of this object
912
-     *
913
-     * @return EE_Base_Class|EE_Status
914
-     * @throws EE_Error
915
-     * @throws InvalidArgumentException
916
-     * @throws InvalidDataTypeException
917
-     * @throws InvalidInterfaceException
918
-     * @throws ReflectionException
919
-     */
920
-    public function status_obj()
921
-    {
922
-        return $this->get_first_related('Status');
923
-    }
924
-
925
-
926
-    /**
927
-     * Gets all the extra meta info on this payment
928
-     *
929
-     * @param array $query_params
930
-     * @return EE_Base_Class[]|EE_Extra_Meta
931
-     * @throws EE_Error
932
-     * @throws InvalidArgumentException
933
-     * @throws InvalidDataTypeException
934
-     * @throws InvalidInterfaceException
935
-     * @throws ReflectionException
936
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
937
-     */
938
-    public function extra_meta($query_params = [])
939
-    {
940
-        return $this->get_many_related('Extra_Meta', $query_params);
941
-    }
942
-
943
-
944
-    /**
945
-     * Wrapper for _add_relation_to
946
-     *
947
-     * @param EE_Registration $registration
948
-     * @return EE_Base_Class the relation was added to
949
-     * @throws EE_Error
950
-     * @throws InvalidArgumentException
951
-     * @throws InvalidDataTypeException
952
-     * @throws InvalidInterfaceException
953
-     * @throws ReflectionException
954
-     */
955
-    public function add_registration(EE_Registration $registration)
956
-    {
957
-        return $this->_add_relation_to($registration, 'Registration');
958
-    }
959
-
960
-
961
-    /**
962
-     * Removes the given registration from being related (even before saving this transaction).
963
-     * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
964
-     *
965
-     * @param int $registration_or_id
966
-     * @return EE_Base_Class that was removed from being related
967
-     * @throws EE_Error
968
-     * @throws InvalidArgumentException
969
-     * @throws InvalidDataTypeException
970
-     * @throws InvalidInterfaceException
971
-     * @throws ReflectionException
972
-     */
973
-    public function remove_registration_with_id($registration_or_id)
974
-    {
975
-        return $this->_remove_relation_to($registration_or_id, 'Registration');
976
-    }
977
-
978
-
979
-    /**
980
-     * Gets all the line items which are for ACTUAL items
981
-     *
982
-     * @return EE_Line_Item[]
983
-     * @throws EE_Error
984
-     * @throws InvalidArgumentException
985
-     * @throws InvalidDataTypeException
986
-     * @throws InvalidInterfaceException
987
-     * @throws ReflectionException
988
-     */
989
-    public function items_purchased()
990
-    {
991
-        return $this->line_items([['LIN_type' => EEM_Line_Item::type_line_item]]);
992
-    }
993
-
994
-
995
-    /**
996
-     * Wrapper for _add_relation_to
997
-     *
998
-     * @param EE_Line_Item $line_item
999
-     * @return EE_Base_Class the relation was added to
1000
-     * @throws EE_Error
1001
-     * @throws InvalidArgumentException
1002
-     * @throws InvalidDataTypeException
1003
-     * @throws InvalidInterfaceException
1004
-     * @throws ReflectionException
1005
-     */
1006
-    public function add_line_item(EE_Line_Item $line_item)
1007
-    {
1008
-        return $this->_add_relation_to($line_item, 'Line_Item');
1009
-    }
1010
-
1011
-
1012
-    /**
1013
-     * Gets ALL the line items related to this transaction (unstructured)
1014
-     *
1015
-     * @param array $query_params
1016
-     * @return EE_Base_Class[]|EE_Line_Item[]
1017
-     * @throws EE_Error
1018
-     * @throws InvalidArgumentException
1019
-     * @throws InvalidDataTypeException
1020
-     * @throws InvalidInterfaceException
1021
-     * @throws ReflectionException
1022
-     */
1023
-    public function line_items($query_params = [])
1024
-    {
1025
-        return $this->get_many_related('Line_Item', $query_params);
1026
-    }
1027
-
1028
-
1029
-    /**
1030
-     * Gets all the line items which are taxes on the total
1031
-     *
1032
-     * @return EE_Line_Item[]
1033
-     * @throws EE_Error
1034
-     * @throws InvalidArgumentException
1035
-     * @throws InvalidDataTypeException
1036
-     * @throws InvalidInterfaceException
1037
-     * @throws ReflectionException
1038
-     */
1039
-    public function tax_items()
1040
-    {
1041
-        return $this->line_items([['LIN_type' => EEM_Line_Item::type_tax]]);
1042
-    }
1043
-
1044
-
1045
-    /**
1046
-     * Gets the total line item (which is a parent of all other related line items,
1047
-     * meaning it takes them all into account on its total)
1048
-     *
1049
-     * @param bool $create_if_not_found
1050
-     * @return EE_Line_Item|null
1051
-     * @throws EE_Error
1052
-     * @throws InvalidArgumentException
1053
-     * @throws InvalidDataTypeException
1054
-     * @throws InvalidInterfaceException
1055
-     * @throws ReflectionException
1056
-     */
1057
-    public function total_line_item(bool $create_if_not_found = true): ?EE_Line_Item
1058
-    {
1059
-        $item = $this->get_first_related('Line_Item', [['LIN_type' => EEM_Line_Item::type_total]]);
1060
-        if ($item instanceof EE_Line_Item) {
1061
-            return $item;
1062
-        }
1063
-        return $create_if_not_found
1064
-            ? EEH_Line_Item::create_total_line_item($this)
1065
-            : null;
1066
-    }
1067
-
1068
-
1069
-    /**
1070
-     * Returns the total amount of tax on this transaction
1071
-     * (assumes there's only one tax subtotal line item)
1072
-     *
1073
-     * @return float
1074
-     * @throws EE_Error
1075
-     * @throws InvalidArgumentException
1076
-     * @throws InvalidDataTypeException
1077
-     * @throws InvalidInterfaceException
1078
-     * @throws ReflectionException
1079
-     */
1080
-    public function tax_total()
1081
-    {
1082
-        $tax_line_item = $this->tax_total_line_item();
1083
-        if ($tax_line_item) {
1084
-            return (float) $tax_line_item->total();
1085
-        }
1086
-        return (float) 0;
1087
-    }
1088
-
1089
-
1090
-    /**
1091
-     * Gets the tax subtotal line item (assumes there's only one)
1092
-     *
1093
-     * @return EE_Line_Item
1094
-     * @throws EE_Error
1095
-     * @throws InvalidArgumentException
1096
-     * @throws InvalidDataTypeException
1097
-     * @throws InvalidInterfaceException
1098
-     * @throws ReflectionException
1099
-     */
1100
-    public function tax_total_line_item()
1101
-    {
1102
-        return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1103
-    }
1104
-
1105
-
1106
-    /**
1107
-     * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1108
-     *
1109
-     * @return EE_Form_Section_Proper|null
1110
-     * @throws EE_Error
1111
-     * @throws InvalidArgumentException
1112
-     * @throws InvalidDataTypeException
1113
-     * @throws InvalidInterfaceException
1114
-     * @throws ReflectionException
1115
-     */
1116
-    public function billing_info(): ?EE_Form_Section_Proper
1117
-    {
1118
-        $payment_method = $this->payment_method();
1119
-        if (! $payment_method) {
1120
-            EE_Error::add_error(
1121
-                esc_html__(
1122
-                    'Could not find billing info for transaction because no gateway has been used for it yet',
1123
-                    'event_espresso'
1124
-                ),
1125
-                __FILE__,
1126
-                __FUNCTION__,
1127
-                __LINE__
1128
-            );
1129
-            return null;
1130
-        }
1131
-        $primary_reg = $this->primary_registration();
1132
-        if (! $primary_reg) {
1133
-            EE_Error::add_error(
1134
-                esc_html__(
1135
-                    'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1136
-                    'event_espresso'
1137
-                ),
1138
-                __FILE__,
1139
-                __FUNCTION__,
1140
-                __LINE__
1141
-            );
1142
-            return null;
1143
-        }
1144
-        $attendee = $primary_reg->attendee();
1145
-        if (! $attendee) {
1146
-            EE_Error::add_error(
1147
-                esc_html__(
1148
-                    'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1149
-                    'event_espresso'
1150
-                ),
1151
-                __FILE__,
1152
-                __FUNCTION__,
1153
-                __LINE__
1154
-            );
1155
-            return null;
1156
-        }
1157
-        return $attendee->billing_info_for_payment_method($payment_method);
1158
-    }
1159
-
1160
-
1161
-    /**
1162
-     * Gets PMD_ID
1163
-     *
1164
-     * @return int
1165
-     * @throws EE_Error
1166
-     * @throws InvalidArgumentException
1167
-     * @throws InvalidDataTypeException
1168
-     * @throws InvalidInterfaceException
1169
-     * @throws ReflectionException
1170
-     */
1171
-    public function payment_method_ID()
1172
-    {
1173
-        return $this->get('PMD_ID');
1174
-    }
1175
-
1176
-
1177
-    /**
1178
-     * Sets PMD_ID
1179
-     *
1180
-     * @param int $PMD_ID
1181
-     * @throws EE_Error
1182
-     * @throws InvalidArgumentException
1183
-     * @throws InvalidDataTypeException
1184
-     * @throws InvalidInterfaceException
1185
-     * @throws ReflectionException
1186
-     */
1187
-    public function set_payment_method_ID($PMD_ID)
1188
-    {
1189
-        $this->set('PMD_ID', $PMD_ID);
1190
-    }
1191
-
1192
-
1193
-    /**
1194
-     * Gets the last-used payment method on this transaction
1195
-     * (we COULD just use the last-made payment, but some payment methods, namely
1196
-     * offline ones, dont' create payments)
1197
-     *
1198
-     * @return EE_Payment_Method
1199
-     * @throws EE_Error
1200
-     * @throws InvalidArgumentException
1201
-     * @throws InvalidDataTypeException
1202
-     * @throws InvalidInterfaceException
1203
-     * @throws ReflectionException
1204
-     */
1205
-    public function payment_method()
1206
-    {
1207
-        $PMD_ID = $this->payment_method_ID();
1208
-        if ($PMD_ID) {
1209
-            $pm = EEM_Payment_Method::instance()->get_one_by_ID($this->payment_method_ID());
1210
-            if ($pm instanceof EE_Payment_Method) {
1211
-                return $pm;
1212
-            }
1213
-        }
1214
-        $last_payment = $this->last_payment();
1215
-        if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1216
-            return $last_payment->payment_method();
1217
-        }
1218
-        return null;
1219
-    }
1220
-
1221
-
1222
-    /**
1223
-     * Gets the last payment made
1224
-     *
1225
-     * @return EE_Base_Class|EE_Payment|null
1226
-     * @throws EE_Error
1227
-     * @throws InvalidArgumentException
1228
-     * @throws InvalidDataTypeException
1229
-     * @throws InvalidInterfaceException
1230
-     * @throws ReflectionException
1231
-     */
1232
-    public function last_payment(): ?EE_Payment
1233
-    {
1234
-        return $this->get_first_related('Payment', ['order_by' => ['PAY_ID' => 'desc']]);
1235
-    }
1236
-
1237
-
1238
-    /**
1239
-     * Gets all the line items which are unrelated to tickets on this transaction
1240
-     *
1241
-     * @return EE_Line_Item[]
1242
-     * @throws EE_Error
1243
-     * @throws InvalidArgumentException
1244
-     * @throws InvalidDataTypeException
1245
-     * @throws InvalidInterfaceException
1246
-     * @throws ReflectionException
1247
-     */
1248
-    public function non_ticket_line_items()
1249
-    {
1250
-        return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1251
-    }
1252
-
1253
-
1254
-    /**
1255
-     * possibly toggles TXN status
1256
-     *
1257
-     * @param boolean $update whether to save the TXN
1258
-     * @return bool whether the TXN was saved
1259
-     * @throws EE_Error
1260
-     * @throws InvalidArgumentException
1261
-     * @throws InvalidDataTypeException
1262
-     * @throws InvalidInterfaceException
1263
-     * @throws ReflectionException
1264
-     * @throws RuntimeException
1265
-     */
1266
-    public function update_status_based_on_total_paid($update = true)
1267
-    {
1268
-        // set transaction status based on comparison of TXN_paid vs TXN_total
1269
-        if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1270
-            $new_txn_status = EEM_Transaction::overpaid_status_code;
1271
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1272
-            $new_txn_status = EEM_Transaction::complete_status_code;
1273
-        } elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1274
-            $new_txn_status = EEM_Transaction::incomplete_status_code;
1275
-        } else {
1276
-            throw new RuntimeException(
1277
-                esc_html__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1278
-            );
1279
-        }
1280
-        if ($new_txn_status !== $this->status_ID()) {
1281
-            $this->set_status($new_txn_status);
1282
-            if ($update) {
1283
-                return (bool) $this->save();
1284
-            }
1285
-        }
1286
-        return false;
1287
-    }
1288
-
1289
-
1290
-    /**
1291
-     * Updates the transaction's status and total_paid based on all the payments
1292
-     * that apply to it
1293
-     *
1294
-     * @return array|bool
1295
-     * @throws EE_Error
1296
-     * @throws InvalidArgumentException
1297
-     * @throws ReflectionException
1298
-     * @throws InvalidDataTypeException
1299
-     * @throws InvalidInterfaceException
1300
-     * @deprecated
1301
-     */
1302
-    public function update_based_on_payments()
1303
-    {
1304
-        EE_Error::doing_it_wrong(
1305
-            __CLASS__ . '::' . __FUNCTION__,
1306
-            sprintf(
1307
-                esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1308
-                'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1309
-            ),
1310
-            '4.6.0'
1311
-        );
1312
-        /** @type EE_Transaction_Processor $transaction_processor */
1313
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1314
-        return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1315
-    }
1316
-
1317
-
1318
-    /**
1319
-     * @return string
1320
-     */
1321
-    public function old_txn_status()
1322
-    {
1323
-        return $this->_old_txn_status;
1324
-    }
1325
-
1326
-
1327
-    /**
1328
-     * @param string $old_txn_status
1329
-     */
1330
-    public function set_old_txn_status($old_txn_status)
1331
-    {
1332
-        // only set the first time
1333
-        if ($this->_old_txn_status === null) {
1334
-            $this->_old_txn_status = $old_txn_status;
1335
-        }
1336
-    }
1337
-
1338
-
1339
-    /**
1340
-     * reg_status_updated
1341
-     *
1342
-     * @return bool
1343
-     * @throws EE_Error
1344
-     * @throws InvalidArgumentException
1345
-     * @throws InvalidDataTypeException
1346
-     * @throws InvalidInterfaceException
1347
-     * @throws ReflectionException
1348
-     */
1349
-    public function txn_status_updated()
1350
-    {
1351
-        return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1352
-    }
1353
-
1354
-
1355
-    /**
1356
-     * _reg_steps_completed
1357
-     * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1358
-     * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1359
-     * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1360
-     *
1361
-     * @param string $reg_step_slug
1362
-     * @param bool   $check_all
1363
-     * @return bool|int
1364
-     * @throws EE_Error
1365
-     * @throws InvalidArgumentException
1366
-     * @throws InvalidDataTypeException
1367
-     * @throws InvalidInterfaceException
1368
-     * @throws ReflectionException
1369
-     */
1370
-    private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1371
-    {
1372
-        $reg_steps = $this->reg_steps();
1373
-        if (! is_array($reg_steps) || empty($reg_steps)) {
1374
-            return false;
1375
-        }
1376
-        // loop thru reg steps array)
1377
-        foreach ($reg_steps as $slug => $reg_step_completed) {
1378
-            // if NOT checking ALL steps (only checking one step)
1379
-            if (! $check_all) {
1380
-                // and this is the one
1381
-                if ($slug === $reg_step_slug) {
1382
-                    return $reg_step_completed;
1383
-                }
1384
-                // skip to next reg step in loop
1385
-                continue;
1386
-            }
1387
-            // $check_all must be true, else we would never have gotten to this point
1388
-            if ($slug === $reg_step_slug) {
1389
-                // if we reach this point, then we are testing either:
1390
-                // all_reg_steps_completed_except() or
1391
-                // all_reg_steps_completed_except_final_step(),
1392
-                // and since this is the reg step EXCEPTION being tested
1393
-                // we want to return true (yes true) if this reg step is NOT completed
1394
-                // ie: "is everything completed except the final step?"
1395
-                // "that is correct... the final step is not completed, but all others are."
1396
-                return $reg_step_completed !== true;
1397
-            }
1398
-            if ($reg_step_completed !== true) {
1399
-                // if any reg step is NOT completed, then ALL steps are not completed
1400
-                return false;
1401
-            }
1402
-        }
1403
-        return true;
1404
-    }
1405
-
1406
-
1407
-    /**
1408
-     * all_reg_steps_completed
1409
-     * returns:
1410
-     *    true if ALL reg steps have been marked as completed
1411
-     *        or false if any step is not completed
1412
-     *
1413
-     * @return bool
1414
-     * @throws EE_Error
1415
-     * @throws InvalidArgumentException
1416
-     * @throws InvalidDataTypeException
1417
-     * @throws InvalidInterfaceException
1418
-     * @throws ReflectionException
1419
-     */
1420
-    public function all_reg_steps_completed()
1421
-    {
1422
-        return $this->_reg_steps_completed();
1423
-    }
1424
-
1425
-
1426
-    /**
1427
-     * all_reg_steps_completed_except
1428
-     * returns:
1429
-     *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1430
-     *        or false if any other step is not completed
1431
-     *        or false if ALL steps are completed including the exception you are testing !!!
1432
-     *
1433
-     * @param string $exception
1434
-     * @return bool
1435
-     * @throws EE_Error
1436
-     * @throws InvalidArgumentException
1437
-     * @throws InvalidDataTypeException
1438
-     * @throws InvalidInterfaceException
1439
-     * @throws ReflectionException
1440
-     */
1441
-    public function all_reg_steps_completed_except($exception = '')
1442
-    {
1443
-        return $this->_reg_steps_completed($exception);
1444
-    }
1445
-
1446
-
1447
-    /**
1448
-     * all_reg_steps_completed_except
1449
-     * returns:
1450
-     *        true if ALL reg steps, except the final step, have been marked as completed
1451
-     *        or false if any step is not completed
1452
-     *    or false if ALL steps are completed including the final step !!!
1453
-     *
1454
-     * @return bool
1455
-     * @throws EE_Error
1456
-     * @throws InvalidArgumentException
1457
-     * @throws InvalidDataTypeException
1458
-     * @throws InvalidInterfaceException
1459
-     * @throws ReflectionException
1460
-     */
1461
-    public function all_reg_steps_completed_except_final_step()
1462
-    {
1463
-        return $this->_reg_steps_completed('finalize_registration');
1464
-    }
1465
-
1466
-
1467
-    /**
1468
-     * reg_step_completed
1469
-     * returns:
1470
-     *    true if a specific reg step has been marked as completed
1471
-     *    a Unix timestamp if it has been initialized but not yet completed,
1472
-     *    or false if it has not yet been initialized
1473
-     *
1474
-     * @param string $reg_step_slug
1475
-     * @return bool|int
1476
-     * @throws EE_Error
1477
-     * @throws InvalidArgumentException
1478
-     * @throws InvalidDataTypeException
1479
-     * @throws InvalidInterfaceException
1480
-     * @throws ReflectionException
1481
-     */
1482
-    public function reg_step_completed($reg_step_slug)
1483
-    {
1484
-        return $this->_reg_steps_completed($reg_step_slug, false);
1485
-    }
1486
-
1487
-
1488
-    /**
1489
-     * completed_final_reg_step
1490
-     * returns:
1491
-     *    true if the finalize_registration reg step has been marked as completed
1492
-     *    a Unix timestamp if it has been initialized but not yet completed,
1493
-     *    or false if it has not yet been initialized
1494
-     *
1495
-     * @return bool|int
1496
-     * @throws EE_Error
1497
-     * @throws InvalidArgumentException
1498
-     * @throws InvalidDataTypeException
1499
-     * @throws InvalidInterfaceException
1500
-     * @throws ReflectionException
1501
-     */
1502
-    public function final_reg_step_completed()
1503
-    {
1504
-        return $this->_reg_steps_completed('finalize_registration', false);
1505
-    }
1506
-
1507
-
1508
-    /**
1509
-     * set_reg_step_initiated
1510
-     * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1511
-     *
1512
-     * @param string $reg_step_slug
1513
-     * @return boolean
1514
-     * @throws EE_Error
1515
-     * @throws InvalidArgumentException
1516
-     * @throws InvalidDataTypeException
1517
-     * @throws InvalidInterfaceException
1518
-     * @throws ReflectionException
1519
-     */
1520
-    public function set_reg_step_initiated($reg_step_slug)
1521
-    {
1522
-        return $this->_set_reg_step_completed_status($reg_step_slug, time());
1523
-    }
1524
-
1525
-
1526
-    /**
1527
-     * set_reg_step_completed
1528
-     * given a valid TXN_reg_step, this sets the step as completed
1529
-     *
1530
-     * @param string $reg_step_slug
1531
-     * @return boolean
1532
-     * @throws EE_Error
1533
-     * @throws InvalidArgumentException
1534
-     * @throws InvalidDataTypeException
1535
-     * @throws InvalidInterfaceException
1536
-     * @throws ReflectionException
1537
-     */
1538
-    public function set_reg_step_completed($reg_step_slug)
1539
-    {
1540
-        return $this->_set_reg_step_completed_status($reg_step_slug, true);
1541
-    }
1542
-
1543
-
1544
-    /**
1545
-     * set_reg_step_completed
1546
-     * given a valid TXN_reg_step slug, this sets the step as NOT completed
1547
-     *
1548
-     * @param string $reg_step_slug
1549
-     * @return boolean
1550
-     * @throws EE_Error
1551
-     * @throws InvalidArgumentException
1552
-     * @throws InvalidDataTypeException
1553
-     * @throws InvalidInterfaceException
1554
-     * @throws ReflectionException
1555
-     */
1556
-    public function set_reg_step_not_completed($reg_step_slug)
1557
-    {
1558
-        return $this->_set_reg_step_completed_status($reg_step_slug, false);
1559
-    }
1560
-
1561
-
1562
-    /**
1563
-     * set_reg_step_completed
1564
-     * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1565
-     *
1566
-     * @param string      $reg_step_slug
1567
-     * @param boolean|int $status
1568
-     * @return boolean
1569
-     * @throws EE_Error
1570
-     * @throws InvalidArgumentException
1571
-     * @throws InvalidDataTypeException
1572
-     * @throws InvalidInterfaceException
1573
-     * @throws ReflectionException
1574
-     */
1575
-    private function _set_reg_step_completed_status($reg_step_slug, $status)
1576
-    {
1577
-        // validate status
1578
-        $status = is_bool($status) || is_int($status)
1579
-            ? $status
1580
-            : false;
1581
-        // get reg steps array
1582
-        $txn_reg_steps = $this->reg_steps();
1583
-        // if reg step does NOT exist
1584
-        if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1585
-            return false;
1586
-        }
1587
-        // if  we're trying to complete a step that is already completed
1588
-        if ($txn_reg_steps[ $reg_step_slug ] === true) {
1589
-            return true;
1590
-        }
1591
-        // if  we're trying to complete a step that hasn't even started
1592
-        if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1593
-            return false;
1594
-        }
1595
-        // if current status value matches the incoming value (no change)
1596
-        // type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1597
-        if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1598
-            // this will happen in cases where multiple AJAX requests occur during the same step
1599
-            return true;
1600
-        }
1601
-        // if we're trying to set a start time, but it has already been set...
1602
-        if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1603
-            // skip the update below, but don't return FALSE so that errors won't be displayed
1604
-            return true;
1605
-        }
1606
-        // update completed status
1607
-        $txn_reg_steps[ $reg_step_slug ] = $status;
1608
-        $this->set_reg_steps($txn_reg_steps);
1609
-        $this->save();
1610
-        return true;
1611
-    }
1612
-
1613
-
1614
-    /**
1615
-     * remove_reg_step
1616
-     * given a valid TXN_reg_step slug, this will remove (unset)
1617
-     * the reg step from the TXN reg step array
1618
-     *
1619
-     * @param string $reg_step_slug
1620
-     * @return void
1621
-     * @throws EE_Error
1622
-     * @throws InvalidArgumentException
1623
-     * @throws InvalidDataTypeException
1624
-     * @throws InvalidInterfaceException
1625
-     * @throws ReflectionException
1626
-     */
1627
-    public function remove_reg_step($reg_step_slug)
1628
-    {
1629
-        // get reg steps array
1630
-        $txn_reg_steps = $this->reg_steps();
1631
-        unset($txn_reg_steps[ $reg_step_slug ]);
1632
-        $this->set_reg_steps($txn_reg_steps);
1633
-    }
1634
-
1635
-
1636
-    /**
1637
-     * toggle_failed_transaction_status
1638
-     * upgrades a TXNs status from failed to abandoned,
1639
-     * meaning that contact information has been captured for at least one registrant
1640
-     *
1641
-     * @param bool $save
1642
-     * @return bool
1643
-     * @throws EE_Error
1644
-     * @throws InvalidArgumentException
1645
-     * @throws InvalidDataTypeException
1646
-     * @throws InvalidInterfaceException
1647
-     * @throws ReflectionException
1648
-     */
1649
-    public function toggle_failed_transaction_status($save = true)
1650
-    {
1651
-        // if TXN status is still set as "failed"...
1652
-        if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1653
-            $this->set_status(EEM_Transaction::abandoned_status_code);
1654
-            if ($save) {
1655
-                $this->save();
1656
-            }
1657
-            return true;
1658
-        }
1659
-        return false;
1660
-    }
1661
-
1662
-
1663
-    /**
1664
-     * toggle_abandoned_transaction_status
1665
-     * upgrades a TXNs status from failed or abandoned to incomplete
1666
-     *
1667
-     * @return bool
1668
-     * @throws EE_Error
1669
-     * @throws InvalidArgumentException
1670
-     * @throws InvalidDataTypeException
1671
-     * @throws InvalidInterfaceException
1672
-     * @throws ReflectionException
1673
-     */
1674
-    public function toggle_abandoned_transaction_status()
1675
-    {
1676
-        // if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1677
-        $txn_status = $this->status_ID();
1678
-        if (
1679
-            $txn_status === EEM_Transaction::failed_status_code
1680
-            || $txn_status === EEM_Transaction::abandoned_status_code
1681
-        ) {
1682
-            // if a contact record for the primary registrant has been created
1683
-            if (
1684
-                $this->primary_registration() instanceof EE_Registration
1685
-                && $this->primary_registration()->attendee() instanceof EE_Attendee
1686
-            ) {
1687
-                $this->set_status(EEM_Transaction::incomplete_status_code);
1688
-            } else {
1689
-                // no contact record? yer abandoned!
1690
-                $this->set_status(EEM_Transaction::abandoned_status_code);
1691
-            }
1692
-            return true;
1693
-        }
1694
-        return false;
1695
-    }
1696
-
1697
-
1698
-    /**
1699
-     * checks if an Abandoned TXN has any related payments, and if so,
1700
-     * updates the TXN status based on the amount paid
1701
-     *
1702
-     * @throws EE_Error
1703
-     * @throws InvalidArgumentException
1704
-     * @throws InvalidDataTypeException
1705
-     * @throws InvalidInterfaceException
1706
-     * @throws ReflectionException
1707
-     * @throws RuntimeException
1708
-     * @throws ReflectionException
1709
-     */
1710
-    public function verify_abandoned_transaction_status()
1711
-    {
1712
-        if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1713
-            return;
1714
-        }
1715
-        $payments = $this->get_many_related('Payment');
1716
-        if (! empty($payments)) {
1717
-            foreach ($payments as $payment) {
1718
-                if ($payment instanceof EE_Payment) {
1719
-                    // kk this TXN should NOT be abandoned
1720
-                    $this->update_status_based_on_total_paid();
1721
-                    if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1722
-                        EE_Error::add_attention(
1723
-                            sprintf(
1724
-                                esc_html__(
1725
-                                    'The status for Transaction #%1$d has been updated from "Abandoned" to "%2$s", because at least one payment has been made towards it. If the payment appears in the "Payment Details" table below, you may need to edit its status and/or other details as well.',
1726
-                                    'event_espresso'
1727
-                                ),
1728
-                                $this->ID(),
1729
-                                $this->pretty_status()
1730
-                            )
1731
-                        );
1732
-                    }
1733
-                    // get final reg step status
1734
-                    $finalized = $this->final_reg_step_completed();
1735
-                    // if the 'finalize_registration' step has been initiated (has a timestamp)
1736
-                    // but has not yet been fully completed (TRUE)
1737
-                    if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1738
-                        $this->set_reg_step_completed('finalize_registration');
1739
-                        $this->save();
1740
-                    }
1741
-                }
1742
-            }
1743
-        }
1744
-    }
1745
-
1746
-
1747
-    /**
1748
-     * @throws EE_Error
1749
-     * @throws InvalidArgumentException
1750
-     * @throws InvalidDataTypeException
1751
-     * @throws InvalidInterfaceException
1752
-     * @throws ReflectionException
1753
-     * @throws RuntimeException
1754
-     * @since 4.10.4.p
1755
-     */
1756
-    public function recalculateLineItems()
1757
-    {
1758
-        $total_line_item = $this->total_line_item(false);
1759
-        if ($total_line_item instanceof EE_Line_Item) {
1760
-            EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1761
-            return EEH_Line_Item::apply_taxes($total_line_item, true);
1762
-        }
1763
-        return false;
1764
-    }
16
+	/**
17
+	 * The length of time in seconds that a lock is applied before being considered expired.
18
+	 * It is not long because a transaction should only be locked for the duration of the request that locked it
19
+	 */
20
+	const LOCK_EXPIRATION = 2;
21
+
22
+	/**
23
+	 * txn status upon initial construction.
24
+	 *
25
+	 * @var string
26
+	 */
27
+	protected $_old_txn_status;
28
+
29
+	private ?EE_Registration $_primary_registrant = null;
30
+
31
+	/**
32
+	 * @param array  $props_n_values          incoming values
33
+	 * @param string $timezone                incoming timezone
34
+	 *                                        (if not set the timezone set for the website will be used.)
35
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
36
+	 *                                        date_format and the second value is the time format
37
+	 * @return EE_Transaction
38
+	 * @throws EE_Error
39
+	 * @throws InvalidArgumentException
40
+	 * @throws InvalidDataTypeException
41
+	 * @throws InvalidInterfaceException
42
+	 * @throws ReflectionException
43
+	 */
44
+	public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
45
+	{
46
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
47
+		$txn        = $has_object
48
+			? $has_object
49
+			: new self($props_n_values, false, $timezone, $date_formats);
50
+		if (! $has_object) {
51
+			$txn->set_old_txn_status($txn->status_ID());
52
+		}
53
+		return $txn;
54
+	}
55
+
56
+
57
+	/**
58
+	 * @param array  $props_n_values  incoming values from the database
59
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
60
+	 *                                the website will be used.
61
+	 * @return EE_Transaction
62
+	 * @throws EE_Error
63
+	 * @throws InvalidArgumentException
64
+	 * @throws InvalidDataTypeException
65
+	 * @throws InvalidInterfaceException
66
+	 * @throws ReflectionException
67
+	 */
68
+	public static function new_instance_from_db($props_n_values = [], $timezone = '')
69
+	{
70
+		$txn = new self($props_n_values, true, $timezone);
71
+		$txn->set_old_txn_status($txn->status_ID());
72
+		return $txn;
73
+	}
74
+
75
+
76
+	/**
77
+	 * Sets a meta field indicating that this TXN is locked and should not be updated in the db.
78
+	 * If a lock has already been set, then we will attempt to remove it in case it has expired.
79
+	 * If that also fails, then an exception is thrown.
80
+	 *
81
+	 * @throws EE_Error
82
+	 * @throws InvalidArgumentException
83
+	 * @throws InvalidDataTypeException
84
+	 * @throws InvalidInterfaceException
85
+	 * @throws ReflectionException
86
+	 */
87
+	public function lock()
88
+	{
89
+		// attempt to set lock, but if that fails...
90
+		if (! $this->add_extra_meta('lock', time(), true)) {
91
+			// then attempt to remove the lock in case it is expired
92
+			if ($this->_remove_expired_lock()) {
93
+				// if removal was successful, then try setting lock again
94
+				$this->lock();
95
+			} else {
96
+				// but if the lock can not be removed, then throw an exception
97
+				throw new EE_Error(
98
+					sprintf(
99
+						esc_html__(
100
+							'Could not lock Transaction %1$d because it is already locked, meaning another part of the system is currently editing it. It should already be unlocked by the time you read this, so please refresh the page and try again.',
101
+							'event_espresso'
102
+						),
103
+						$this->ID()
104
+					)
105
+				);
106
+			}
107
+		}
108
+	}
109
+
110
+
111
+	/**
112
+	 * removes transaction lock applied in EE_Transaction::lock()
113
+	 *
114
+	 * @return int
115
+	 * @throws EE_Error
116
+	 * @throws InvalidArgumentException
117
+	 * @throws InvalidDataTypeException
118
+	 * @throws InvalidInterfaceException
119
+	 * @throws ReflectionException
120
+	 */
121
+	public function unlock()
122
+	{
123
+		return $this->delete_extra_meta('lock');
124
+	}
125
+
126
+
127
+	/**
128
+	 * Decides whether or not now is the right time to update the transaction.
129
+	 * This is useful because we don't always know if it is safe to update the transaction
130
+	 * and its related data. why?
131
+	 * because it's possible that the transaction is being used in another
132
+	 * request and could overwrite anything we save.
133
+	 * So we want to only update the txn once we know that won't happen.
134
+	 * We also check that the lock isn't expired, and remove it if it is
135
+	 *
136
+	 * @return boolean
137
+	 * @throws EE_Error
138
+	 * @throws InvalidArgumentException
139
+	 * @throws InvalidDataTypeException
140
+	 * @throws InvalidInterfaceException
141
+	 * @throws ReflectionException
142
+	 */
143
+	public function is_locked()
144
+	{
145
+		// if TXN is not locked, then return false immediately
146
+		if (! $this->_get_lock()) {
147
+			return false;
148
+		}
149
+		// if not, then let's try and remove the lock in case it's expired...
150
+		// _remove_expired_lock() returns 0 when lock is valid (ie: removed = false)
151
+		// and a positive number if the lock was removed (ie: number of locks deleted),
152
+		// so we need to return the opposite
153
+		return ! $this->_remove_expired_lock();
154
+	}
155
+
156
+
157
+	/**
158
+	 * Gets the meta field indicating that this TXN is locked
159
+	 *
160
+	 * @return int
161
+	 * @throws EE_Error
162
+	 * @throws InvalidArgumentException
163
+	 * @throws InvalidDataTypeException
164
+	 * @throws InvalidInterfaceException
165
+	 * @throws ReflectionException
166
+	 */
167
+	protected function _get_lock()
168
+	{
169
+		return (int) $this->get_extra_meta('lock', true, 0);
170
+	}
171
+
172
+
173
+	/**
174
+	 * If the lock on this transaction is expired, then we want to remove it so that the transaction can be updated
175
+	 *
176
+	 * @return int
177
+	 * @throws EE_Error
178
+	 * @throws InvalidArgumentException
179
+	 * @throws InvalidDataTypeException
180
+	 * @throws InvalidInterfaceException
181
+	 * @throws ReflectionException
182
+	 */
183
+	protected function _remove_expired_lock()
184
+	{
185
+		$locked = $this->_get_lock();
186
+		if ($locked && time() - EE_Transaction::LOCK_EXPIRATION > $locked) {
187
+			return $this->unlock();
188
+		}
189
+		return 0;
190
+	}
191
+
192
+
193
+	/**
194
+	 * Set transaction total
195
+	 *
196
+	 * @param float $total total value of transaction
197
+	 * @throws EE_Error
198
+	 * @throws InvalidArgumentException
199
+	 * @throws InvalidDataTypeException
200
+	 * @throws InvalidInterfaceException
201
+	 * @throws ReflectionException
202
+	 */
203
+	public function set_total($total = 0.00)
204
+	{
205
+		$this->set('TXN_total', (float) $total);
206
+	}
207
+
208
+
209
+	/**
210
+	 * Set Total Amount Paid to Date
211
+	 *
212
+	 * @param float $total_paid total amount paid to date (sum of all payments)
213
+	 * @throws EE_Error
214
+	 * @throws InvalidArgumentException
215
+	 * @throws InvalidDataTypeException
216
+	 * @throws InvalidInterfaceException
217
+	 * @throws ReflectionException
218
+	 */
219
+	public function set_paid($total_paid = 0.00)
220
+	{
221
+		$this->set('TXN_paid', (float) $total_paid);
222
+	}
223
+
224
+
225
+	/**
226
+	 * Set transaction status
227
+	 *
228
+	 * @param string $status        whether the transaction is open, declined, accepted,
229
+	 *                              or any number of custom values that can be set
230
+	 * @throws EE_Error
231
+	 * @throws InvalidArgumentException
232
+	 * @throws InvalidDataTypeException
233
+	 * @throws InvalidInterfaceException
234
+	 * @throws ReflectionException
235
+	 */
236
+	public function set_status($status = '')
237
+	{
238
+		$this->set('STS_ID', $status);
239
+	}
240
+
241
+
242
+	/**
243
+	 * Set hash salt
244
+	 *
245
+	 * @param string $hash_salt required for some payment gateways
246
+	 * @throws EE_Error
247
+	 * @throws InvalidArgumentException
248
+	 * @throws InvalidDataTypeException
249
+	 * @throws InvalidInterfaceException
250
+	 * @throws ReflectionException
251
+	 */
252
+	public function set_hash_salt($hash_salt = '')
253
+	{
254
+		$this->set('TXN_hash_salt', $hash_salt);
255
+	}
256
+
257
+
258
+	/**
259
+	 * Sets TXN_reg_steps array
260
+	 *
261
+	 * @param array $txn_reg_steps
262
+	 * @throws EE_Error
263
+	 * @throws InvalidArgumentException
264
+	 * @throws InvalidDataTypeException
265
+	 * @throws InvalidInterfaceException
266
+	 * @throws ReflectionException
267
+	 */
268
+	public function set_reg_steps(array $txn_reg_steps)
269
+	{
270
+		$this->set('TXN_reg_steps', $txn_reg_steps);
271
+	}
272
+
273
+
274
+	/**
275
+	 * Gets TXN_reg_steps
276
+	 *
277
+	 * @return array
278
+	 * @throws EE_Error
279
+	 * @throws InvalidArgumentException
280
+	 * @throws InvalidDataTypeException
281
+	 * @throws InvalidInterfaceException
282
+	 * @throws ReflectionException
283
+	 */
284
+	public function reg_steps()
285
+	{
286
+		$TXN_reg_steps = $this->get('TXN_reg_steps');
287
+		return is_array($TXN_reg_steps)
288
+			? $TXN_reg_steps
289
+			: [];
290
+	}
291
+
292
+
293
+	/**
294
+	 * @param string|null $schema
295
+	 *     Schemas:
296
+	 *     'localized_float': "3,023.00"
297
+	 *     'no_currency_code': "$3,023.00"
298
+	 *     null: "$3,023.00<span>USD</span>"
299
+	 * @return string of transaction's total cost, with currency symbol and decimal
300
+	 * @throws EE_Error
301
+	 * @throws InvalidArgumentException
302
+	 * @throws InvalidDataTypeException
303
+	 * @throws InvalidInterfaceException
304
+	 * @throws ReflectionException
305
+	 */
306
+	public function pretty_total(?string $schema = null)
307
+	{
308
+		return $this->get_pretty('TXN_total', $schema);
309
+	}
310
+
311
+
312
+	/**
313
+	 * Gets the amount paid in a pretty string (formatted and with currency symbol)
314
+	 *
315
+	 * @param string|null $schema
316
+	 *     Schemas:
317
+	 *     'localized_float': "3,023.00"
318
+	 *     'no_currency_code': "$3,023.00"
319
+	 *     null: "$3,023.00<span>USD</span>"
320
+	 * @return string
321
+	 * @throws EE_Error
322
+	 * @throws InvalidArgumentException
323
+	 * @throws InvalidDataTypeException
324
+	 * @throws InvalidInterfaceException
325
+	 * @throws ReflectionException
326
+	 */
327
+	public function pretty_paid(?string $schema = null)
328
+	{
329
+		return $this->get_pretty('TXN_paid', $schema);
330
+	}
331
+
332
+
333
+	/**
334
+	 * calculate the amount remaining for this transaction and return;
335
+	 *
336
+	 * @return float amount remaining
337
+	 * @throws EE_Error
338
+	 * @throws InvalidArgumentException
339
+	 * @throws InvalidDataTypeException
340
+	 * @throws InvalidInterfaceException
341
+	 * @throws ReflectionException
342
+	 */
343
+	public function remaining()
344
+	{
345
+		return $this->total() - $this->paid();
346
+	}
347
+
348
+
349
+	/**
350
+	 * get Transaction Total
351
+	 *
352
+	 * @return float
353
+	 * @throws EE_Error
354
+	 * @throws InvalidArgumentException
355
+	 * @throws InvalidDataTypeException
356
+	 * @throws InvalidInterfaceException
357
+	 * @throws ReflectionException
358
+	 */
359
+	public function total()
360
+	{
361
+		return (float) $this->get('TXN_total');
362
+	}
363
+
364
+
365
+	/**
366
+	 * get Total Amount Paid to Date
367
+	 *
368
+	 * @return float
369
+	 * @throws EE_Error
370
+	 * @throws InvalidArgumentException
371
+	 * @throws InvalidDataTypeException
372
+	 * @throws InvalidInterfaceException
373
+	 * @throws ReflectionException
374
+	 */
375
+	public function paid()
376
+	{
377
+		return (float) $this->get('TXN_paid');
378
+	}
379
+
380
+
381
+	/**
382
+	 * @return mixed|null
383
+	 * @throws EE_Error
384
+	 * @throws InvalidArgumentException
385
+	 * @throws InvalidDataTypeException
386
+	 * @throws InvalidInterfaceException
387
+	 * @throws ReflectionException
388
+	 */
389
+	public function get_cart_session()
390
+	{
391
+		$session_data = (array) $this->get('TXN_session_data');
392
+		return isset($session_data['cart']) && $session_data['cart'] instanceof EE_Cart
393
+			? $session_data['cart']
394
+			: null;
395
+	}
396
+
397
+
398
+	/**
399
+	 * get Transaction session data
400
+	 *
401
+	 * @return array|mixed
402
+	 * @throws EE_Error
403
+	 * @throws InvalidArgumentException
404
+	 * @throws InvalidDataTypeException
405
+	 * @throws InvalidInterfaceException
406
+	 * @throws ReflectionException
407
+	 */
408
+	public function session_data()
409
+	{
410
+		$session_data = $this->get('TXN_session_data');
411
+		if (empty($session_data)) {
412
+			$session_data = [
413
+				'id'            => null,
414
+				'user_id'       => null,
415
+				'ip_address'    => null,
416
+				'user_agent'    => null,
417
+				'init_access'   => null,
418
+				'last_access'   => null,
419
+				'pages_visited' => [],
420
+			];
421
+		}
422
+		return $session_data;
423
+	}
424
+
425
+
426
+	/**
427
+	 * Set session data within the TXN object
428
+	 *
429
+	 * @param EE_Session|array|null $session_data
430
+	 * @throws EE_Error
431
+	 * @throws InvalidArgumentException
432
+	 * @throws InvalidDataTypeException
433
+	 * @throws InvalidInterfaceException
434
+	 * @throws ReflectionException
435
+	 */
436
+	public function set_txn_session_data($session_data)
437
+	{
438
+		if ($session_data instanceof EE_Session) {
439
+			$this->set('TXN_session_data', $session_data->get_session_data(null, true));
440
+		} else {
441
+			$this->set('TXN_session_data', $session_data);
442
+		}
443
+	}
444
+
445
+
446
+	/**
447
+	 * get Transaction hash salt
448
+	 *
449
+	 * @return mixed
450
+	 * @throws EE_Error
451
+	 * @throws InvalidArgumentException
452
+	 * @throws InvalidDataTypeException
453
+	 * @throws InvalidInterfaceException
454
+	 * @throws ReflectionException
455
+	 */
456
+	public function hash_salt_()
457
+	{
458
+		return $this->get('TXN_hash_salt');
459
+	}
460
+
461
+
462
+	/**
463
+	 * Returns the transaction datetime as either:
464
+	 *            - unix timestamp format ($format = false, $gmt = true)
465
+	 *            - formatted date string including the UTC (timezone) offset ($format = true ($gmt
466
+	 *              has no affect with this option)), this also may include a timezone abbreviation if the
467
+	 *              set timezone in this class differs from what the timezone is on the blog.
468
+	 *            - formatted date string including the UTC (timezone) offset (default).
469
+	 *
470
+	 * @param boolean $format   - whether to return a unix timestamp (default) or formatted date string
471
+	 * @param boolean $gmt      - whether to return a unix timestamp with UTC offset applied (default)
472
+	 *                          or no UTC offset applied
473
+	 * @return string | int
474
+	 * @throws EE_Error
475
+	 * @throws InvalidArgumentException
476
+	 * @throws InvalidDataTypeException
477
+	 * @throws InvalidInterfaceException
478
+	 * @throws ReflectionException
479
+	 */
480
+	public function datetime($format = false, $gmt = false)
481
+	{
482
+		if ($format) {
483
+			return $this->get_pretty('TXN_timestamp');
484
+		}
485
+		if ($gmt) {
486
+			return $this->get_raw('TXN_timestamp');
487
+		}
488
+		return $this->get('TXN_timestamp');
489
+	}
490
+
491
+
492
+	/**
493
+	 * Gets registrations on this transaction
494
+	 *
495
+	 * @param array   $query_params array of query parameters
496
+	 * @param boolean $get_cached   TRUE to retrieve cached registrations or FALSE to pull from the db
497
+	 * @return EE_Base_Class[]|EE_Registration[]
498
+	 * @throws EE_Error
499
+	 * @throws InvalidArgumentException
500
+	 * @throws InvalidDataTypeException
501
+	 * @throws InvalidInterfaceException
502
+	 * @throws ReflectionException
503
+	 */
504
+	public function registrations($query_params = [], $get_cached = false)
505
+	{
506
+		$query_params = (empty($query_params) || ! is_array($query_params))
507
+			? [
508
+				'order_by' => [
509
+					'Event.EVT_name'     => 'ASC',
510
+					'Attendee.ATT_lname' => 'ASC',
511
+					'Attendee.ATT_fname' => 'ASC',
512
+					'REG_ID'             => 'ASC',
513
+				],
514
+			]
515
+			: $query_params;
516
+		$query_params = $get_cached
517
+			? []
518
+			: $query_params;
519
+		return $this->get_many_related('Registration', $query_params);
520
+	}
521
+
522
+
523
+	/**
524
+	 * Gets all the attendees for this transaction (handy for use with EE_Attendee's get_registrations_for_event
525
+	 * function for getting attendees and how many registrations they each have for an event)
526
+	 *
527
+	 * @return mixed EE_Attendee[] by default, int if $output is set to 'COUNT'
528
+	 * @throws EE_Error
529
+	 * @throws InvalidArgumentException
530
+	 * @throws InvalidDataTypeException
531
+	 * @throws InvalidInterfaceException
532
+	 * @throws ReflectionException
533
+	 */
534
+	public function attendees()
535
+	{
536
+		return $this->get_many_related('Attendee', [['Registration.Transaction.TXN_ID' => $this->ID()]]);
537
+	}
538
+
539
+
540
+	/**
541
+	 * Gets payments for this transaction. Unlike other such functions, order by 'DESC' by default
542
+	 *
543
+	 * @param array $query_params @see
544
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
545
+	 * @return EE_Base_Class[]|EE_Payment[]
546
+	 * @throws EE_Error
547
+	 * @throws InvalidArgumentException
548
+	 * @throws InvalidDataTypeException
549
+	 * @throws InvalidInterfaceException
550
+	 * @throws ReflectionException
551
+	 */
552
+	public function payments($query_params = [])
553
+	{
554
+		return $this->get_many_related('Payment', $query_params);
555
+	}
556
+
557
+
558
+	/**
559
+	 * gets only approved payments for this transaction
560
+	 *
561
+	 * @return EE_Base_Class[]|EE_Payment[]
562
+	 * @throws EE_Error
563
+	 * @throws InvalidArgumentException
564
+	 * @throws ReflectionException
565
+	 * @throws InvalidDataTypeException
566
+	 * @throws InvalidInterfaceException
567
+	 */
568
+	public function approved_payments()
569
+	{
570
+		EE_Registry::instance()->load_model('Payment');
571
+		return $this->get_many_related(
572
+			'Payment',
573
+			[
574
+				['STS_ID' => EEM_Payment::status_id_approved],
575
+				'order_by' => ['PAY_timestamp' => 'DESC'],
576
+			]
577
+		);
578
+	}
579
+
580
+
581
+	/**
582
+	 * Gets all payments which have not been approved
583
+	 *
584
+	 * @return EE_Base_Class[]|EEI_Payment[]
585
+	 * @throws EE_Error if a model is misconfigured somehow
586
+	 * @throws InvalidArgumentException
587
+	 * @throws InvalidDataTypeException
588
+	 * @throws InvalidInterfaceException
589
+	 * @throws ReflectionException
590
+	 */
591
+	public function pending_payments()
592
+	{
593
+		return $this->get_many_related(
594
+			'Payment',
595
+			[
596
+				[
597
+					'STS_ID' => EEM_Payment::status_id_pending,
598
+				],
599
+				'order_by' => [
600
+					'PAY_timestamp' => 'DESC',
601
+				],
602
+			]
603
+		);
604
+	}
605
+
606
+
607
+	/**
608
+	 * echoes $this->pretty_status()
609
+	 *
610
+	 * @param bool $show_icons
611
+	 * @throws EE_Error
612
+	 * @throws InvalidArgumentException
613
+	 * @throws InvalidDataTypeException
614
+	 * @throws InvalidInterfaceException
615
+	 * @throws ReflectionException
616
+	 */
617
+	public function e_pretty_status($show_icons = false)
618
+	{
619
+		echo wp_kses($this->pretty_status($show_icons), AllowedTags::getAllowedTags());
620
+	}
621
+
622
+
623
+	/**
624
+	 * returns a pretty version of the status, good for displaying to users
625
+	 *
626
+	 * @param bool $show_icons
627
+	 * @return string
628
+	 * @throws EE_Error
629
+	 * @throws InvalidArgumentException
630
+	 * @throws InvalidDataTypeException
631
+	 * @throws InvalidInterfaceException
632
+	 * @throws ReflectionException
633
+	 */
634
+	public function pretty_status($show_icons = false)
635
+	{
636
+		$status = EEM_Status::instance()->localized_status(
637
+			[$this->status_ID() => esc_html__('unknown', 'event_espresso')],
638
+			false,
639
+			'sentence'
640
+		);
641
+		$icon   = '';
642
+		switch ($this->status_ID()) {
643
+			case EEM_Transaction::complete_status_code:
644
+				$icon = $show_icons
645
+					? '<span class="dashicons dashicons-yes ee-icon-size-24 green-text"></span>'
646
+					: '';
647
+				break;
648
+			case EEM_Transaction::incomplete_status_code:
649
+				$icon = $show_icons
650
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 lt-blue-text"></span>'
651
+					: '';
652
+				break;
653
+			case EEM_Transaction::abandoned_status_code:
654
+				$icon = $show_icons
655
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 red-text"></span>'
656
+					: '';
657
+				break;
658
+			case EEM_Transaction::failed_status_code:
659
+				$icon = $show_icons
660
+					? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
661
+					: '';
662
+				break;
663
+			case EEM_Transaction::overpaid_status_code:
664
+				$icon = $show_icons
665
+					? '<span class="dashicons dashicons-plus ee-icon-size-16 orange-text"></span>'
666
+					: '';
667
+				break;
668
+		}
669
+		return $icon . $status[ $this->status_ID() ];
670
+	}
671
+
672
+
673
+	/**
674
+	 * get Transaction Status
675
+	 *
676
+	 * @return mixed
677
+	 * @throws EE_Error
678
+	 * @throws InvalidArgumentException
679
+	 * @throws InvalidDataTypeException
680
+	 * @throws InvalidInterfaceException
681
+	 * @throws ReflectionException
682
+	 */
683
+	public function status_ID()
684
+	{
685
+		return $this->get('STS_ID');
686
+	}
687
+
688
+
689
+	/**
690
+	 * Returns TRUE or FALSE for whether or not this transaction cost any money
691
+	 *
692
+	 * @return boolean
693
+	 * @throws EE_Error
694
+	 * @throws InvalidArgumentException
695
+	 * @throws InvalidDataTypeException
696
+	 * @throws InvalidInterfaceException
697
+	 * @throws ReflectionException
698
+	 */
699
+	public function is_free()
700
+	{
701
+		return EEH_Money::compare_floats($this->get('TXN_total'), 0, '==');
702
+	}
703
+
704
+
705
+	/**
706
+	 * Returns whether this transaction is complete
707
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
708
+	 *
709
+	 * @return boolean
710
+	 * @throws EE_Error
711
+	 * @throws InvalidArgumentException
712
+	 * @throws InvalidDataTypeException
713
+	 * @throws InvalidInterfaceException
714
+	 * @throws ReflectionException
715
+	 */
716
+	public function is_completed()
717
+	{
718
+		return $this->status_ID() === EEM_Transaction::complete_status_code;
719
+	}
720
+
721
+
722
+	/**
723
+	 * Returns whether this transaction is incomplete
724
+	 * Useful in templates and other logic for deciding if we should ask for another payment...
725
+	 *
726
+	 * @return boolean
727
+	 * @throws EE_Error
728
+	 * @throws InvalidArgumentException
729
+	 * @throws InvalidDataTypeException
730
+	 * @throws InvalidInterfaceException
731
+	 * @throws ReflectionException
732
+	 */
733
+	public function is_incomplete()
734
+	{
735
+		return $this->status_ID() === EEM_Transaction::incomplete_status_code;
736
+	}
737
+
738
+
739
+	/**
740
+	 * Returns whether this transaction is overpaid
741
+	 * Useful in templates and other logic for deciding if monies need to be refunded
742
+	 *
743
+	 * @return boolean
744
+	 * @throws EE_Error
745
+	 * @throws InvalidArgumentException
746
+	 * @throws InvalidDataTypeException
747
+	 * @throws InvalidInterfaceException
748
+	 * @throws ReflectionException
749
+	 */
750
+	public function is_overpaid()
751
+	{
752
+		return $this->status_ID() === EEM_Transaction::overpaid_status_code;
753
+	}
754
+
755
+
756
+	/**
757
+	 * Returns whether this transaction was abandoned
758
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
759
+	 * but that contact information exists for at least one registrant
760
+	 *
761
+	 * @return boolean
762
+	 * @throws EE_Error
763
+	 * @throws InvalidArgumentException
764
+	 * @throws InvalidDataTypeException
765
+	 * @throws InvalidInterfaceException
766
+	 * @throws ReflectionException
767
+	 */
768
+	public function is_abandoned()
769
+	{
770
+		return $this->status_ID() === EEM_Transaction::abandoned_status_code;
771
+	}
772
+
773
+
774
+	/**
775
+	 * Returns whether this transaction failed
776
+	 * meaning that the transaction/registration process was somehow interrupted and never completed
777
+	 * and that NO contact information exists for any registrants
778
+	 *
779
+	 * @return boolean
780
+	 * @throws EE_Error
781
+	 * @throws InvalidArgumentException
782
+	 * @throws InvalidDataTypeException
783
+	 * @throws InvalidInterfaceException
784
+	 * @throws ReflectionException
785
+	 */
786
+	public function failed()
787
+	{
788
+		return $this->status_ID() === EEM_Transaction::failed_status_code;
789
+	}
790
+
791
+
792
+	/**
793
+	 * This returns the url for the invoice of this transaction
794
+	 *
795
+	 * @param string $type 'html' or 'pdf' (default is pdf)
796
+	 * @return string
797
+	 * @throws DomainException
798
+	 * @throws EE_Error
799
+	 * @throws InvalidArgumentException
800
+	 * @throws InvalidDataTypeException
801
+	 * @throws InvalidInterfaceException
802
+	 * @throws ReflectionException
803
+	 */
804
+	public function invoice_url($type = 'html')
805
+	{
806
+		$REG = $this->primary_registration();
807
+		if (! $REG instanceof EE_Registration) {
808
+			return '';
809
+		}
810
+		return $REG->invoice_url($type);
811
+	}
812
+
813
+
814
+	/**
815
+	 * Gets the primary registration only
816
+	 *
817
+	 * @return EE_Base_Class|EE_Registration
818
+	 * @throws EE_Error
819
+	 * @throws InvalidArgumentException
820
+	 * @throws InvalidDataTypeException
821
+	 * @throws InvalidInterfaceException
822
+	 * @throws ReflectionException
823
+	 */
824
+	public function primary_registration()
825
+	{
826
+		if ($this->_primary_registrant instanceof EE_Registration) {
827
+			return $this->_primary_registrant;
828
+		}
829
+
830
+		$registrations = (array) $this->get_many_related(
831
+			'Registration',
832
+			[['REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT]]
833
+		);
834
+		foreach ($registrations as $registration) {
835
+			// valid registration that is NOT cancelled or declined ?
836
+			if (
837
+				$registration instanceof EE_Registration
838
+				&& ! in_array($registration->status_ID(), EEM_Registration::closed_reg_statuses(), true)
839
+			) {
840
+				$this->_primary_registrant = $registration;
841
+				return $registration;
842
+			}
843
+		}
844
+		// nothing valid found, so just return first thing from array of results
845
+		$primary_registrant = reset($registrations);
846
+		$this->_primary_registrant = $primary_registrant instanceof EE_Registration ? $primary_registrant : null;
847
+		return $this->_primary_registrant;
848
+	}
849
+
850
+
851
+	/**
852
+	 * Gets the URL for viewing the receipt
853
+	 *
854
+	 * @param string $type 'pdf' or 'html' (default is 'html')
855
+	 * @return string
856
+	 * @throws DomainException
857
+	 * @throws EE_Error
858
+	 * @throws InvalidArgumentException
859
+	 * @throws InvalidDataTypeException
860
+	 * @throws InvalidInterfaceException
861
+	 * @throws ReflectionException
862
+	 */
863
+	public function receipt_url($type = 'html')
864
+	{
865
+		$REG = $this->primary_registration();
866
+		if (! $REG instanceof EE_Registration) {
867
+			return '';
868
+		}
869
+		return $REG->receipt_url($type);
870
+	}
871
+
872
+
873
+	/**
874
+	 * Gets the URL of the thank you page with this registration REG_url_link added as
875
+	 * a query parameter
876
+	 *
877
+	 * @return string
878
+	 * @throws EE_Error
879
+	 * @throws InvalidArgumentException
880
+	 * @throws InvalidDataTypeException
881
+	 * @throws InvalidInterfaceException
882
+	 * @throws ReflectionException
883
+	 */
884
+	public function payment_overview_url()
885
+	{
886
+		$primary_registration = $this->primary_registration();
887
+		return $primary_registration instanceof EE_Registration
888
+			? $primary_registration->payment_overview_url()
889
+			: false;
890
+	}
891
+
892
+
893
+	/**
894
+	 * @return string
895
+	 * @throws EE_Error
896
+	 * @throws InvalidArgumentException
897
+	 * @throws InvalidDataTypeException
898
+	 * @throws InvalidInterfaceException
899
+	 * @throws ReflectionException
900
+	 */
901
+	public function gateway_response_on_transaction()
902
+	{
903
+		$payment = $this->get_first_related('Payment');
904
+		return $payment instanceof EE_Payment
905
+			? $payment->gateway_response()
906
+			: '';
907
+	}
908
+
909
+
910
+	/**
911
+	 * Get the status object of this object
912
+	 *
913
+	 * @return EE_Base_Class|EE_Status
914
+	 * @throws EE_Error
915
+	 * @throws InvalidArgumentException
916
+	 * @throws InvalidDataTypeException
917
+	 * @throws InvalidInterfaceException
918
+	 * @throws ReflectionException
919
+	 */
920
+	public function status_obj()
921
+	{
922
+		return $this->get_first_related('Status');
923
+	}
924
+
925
+
926
+	/**
927
+	 * Gets all the extra meta info on this payment
928
+	 *
929
+	 * @param array $query_params
930
+	 * @return EE_Base_Class[]|EE_Extra_Meta
931
+	 * @throws EE_Error
932
+	 * @throws InvalidArgumentException
933
+	 * @throws InvalidDataTypeException
934
+	 * @throws InvalidInterfaceException
935
+	 * @throws ReflectionException
936
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
937
+	 */
938
+	public function extra_meta($query_params = [])
939
+	{
940
+		return $this->get_many_related('Extra_Meta', $query_params);
941
+	}
942
+
943
+
944
+	/**
945
+	 * Wrapper for _add_relation_to
946
+	 *
947
+	 * @param EE_Registration $registration
948
+	 * @return EE_Base_Class the relation was added to
949
+	 * @throws EE_Error
950
+	 * @throws InvalidArgumentException
951
+	 * @throws InvalidDataTypeException
952
+	 * @throws InvalidInterfaceException
953
+	 * @throws ReflectionException
954
+	 */
955
+	public function add_registration(EE_Registration $registration)
956
+	{
957
+		return $this->_add_relation_to($registration, 'Registration');
958
+	}
959
+
960
+
961
+	/**
962
+	 * Removes the given registration from being related (even before saving this transaction).
963
+	 * If an ID/index is provided and this transaction isn't saved yet, removes it from list of cached relations
964
+	 *
965
+	 * @param int $registration_or_id
966
+	 * @return EE_Base_Class that was removed from being related
967
+	 * @throws EE_Error
968
+	 * @throws InvalidArgumentException
969
+	 * @throws InvalidDataTypeException
970
+	 * @throws InvalidInterfaceException
971
+	 * @throws ReflectionException
972
+	 */
973
+	public function remove_registration_with_id($registration_or_id)
974
+	{
975
+		return $this->_remove_relation_to($registration_or_id, 'Registration');
976
+	}
977
+
978
+
979
+	/**
980
+	 * Gets all the line items which are for ACTUAL items
981
+	 *
982
+	 * @return EE_Line_Item[]
983
+	 * @throws EE_Error
984
+	 * @throws InvalidArgumentException
985
+	 * @throws InvalidDataTypeException
986
+	 * @throws InvalidInterfaceException
987
+	 * @throws ReflectionException
988
+	 */
989
+	public function items_purchased()
990
+	{
991
+		return $this->line_items([['LIN_type' => EEM_Line_Item::type_line_item]]);
992
+	}
993
+
994
+
995
+	/**
996
+	 * Wrapper for _add_relation_to
997
+	 *
998
+	 * @param EE_Line_Item $line_item
999
+	 * @return EE_Base_Class the relation was added to
1000
+	 * @throws EE_Error
1001
+	 * @throws InvalidArgumentException
1002
+	 * @throws InvalidDataTypeException
1003
+	 * @throws InvalidInterfaceException
1004
+	 * @throws ReflectionException
1005
+	 */
1006
+	public function add_line_item(EE_Line_Item $line_item)
1007
+	{
1008
+		return $this->_add_relation_to($line_item, 'Line_Item');
1009
+	}
1010
+
1011
+
1012
+	/**
1013
+	 * Gets ALL the line items related to this transaction (unstructured)
1014
+	 *
1015
+	 * @param array $query_params
1016
+	 * @return EE_Base_Class[]|EE_Line_Item[]
1017
+	 * @throws EE_Error
1018
+	 * @throws InvalidArgumentException
1019
+	 * @throws InvalidDataTypeException
1020
+	 * @throws InvalidInterfaceException
1021
+	 * @throws ReflectionException
1022
+	 */
1023
+	public function line_items($query_params = [])
1024
+	{
1025
+		return $this->get_many_related('Line_Item', $query_params);
1026
+	}
1027
+
1028
+
1029
+	/**
1030
+	 * Gets all the line items which are taxes on the total
1031
+	 *
1032
+	 * @return EE_Line_Item[]
1033
+	 * @throws EE_Error
1034
+	 * @throws InvalidArgumentException
1035
+	 * @throws InvalidDataTypeException
1036
+	 * @throws InvalidInterfaceException
1037
+	 * @throws ReflectionException
1038
+	 */
1039
+	public function tax_items()
1040
+	{
1041
+		return $this->line_items([['LIN_type' => EEM_Line_Item::type_tax]]);
1042
+	}
1043
+
1044
+
1045
+	/**
1046
+	 * Gets the total line item (which is a parent of all other related line items,
1047
+	 * meaning it takes them all into account on its total)
1048
+	 *
1049
+	 * @param bool $create_if_not_found
1050
+	 * @return EE_Line_Item|null
1051
+	 * @throws EE_Error
1052
+	 * @throws InvalidArgumentException
1053
+	 * @throws InvalidDataTypeException
1054
+	 * @throws InvalidInterfaceException
1055
+	 * @throws ReflectionException
1056
+	 */
1057
+	public function total_line_item(bool $create_if_not_found = true): ?EE_Line_Item
1058
+	{
1059
+		$item = $this->get_first_related('Line_Item', [['LIN_type' => EEM_Line_Item::type_total]]);
1060
+		if ($item instanceof EE_Line_Item) {
1061
+			return $item;
1062
+		}
1063
+		return $create_if_not_found
1064
+			? EEH_Line_Item::create_total_line_item($this)
1065
+			: null;
1066
+	}
1067
+
1068
+
1069
+	/**
1070
+	 * Returns the total amount of tax on this transaction
1071
+	 * (assumes there's only one tax subtotal line item)
1072
+	 *
1073
+	 * @return float
1074
+	 * @throws EE_Error
1075
+	 * @throws InvalidArgumentException
1076
+	 * @throws InvalidDataTypeException
1077
+	 * @throws InvalidInterfaceException
1078
+	 * @throws ReflectionException
1079
+	 */
1080
+	public function tax_total()
1081
+	{
1082
+		$tax_line_item = $this->tax_total_line_item();
1083
+		if ($tax_line_item) {
1084
+			return (float) $tax_line_item->total();
1085
+		}
1086
+		return (float) 0;
1087
+	}
1088
+
1089
+
1090
+	/**
1091
+	 * Gets the tax subtotal line item (assumes there's only one)
1092
+	 *
1093
+	 * @return EE_Line_Item
1094
+	 * @throws EE_Error
1095
+	 * @throws InvalidArgumentException
1096
+	 * @throws InvalidDataTypeException
1097
+	 * @throws InvalidInterfaceException
1098
+	 * @throws ReflectionException
1099
+	 */
1100
+	public function tax_total_line_item()
1101
+	{
1102
+		return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
1103
+	}
1104
+
1105
+
1106
+	/**
1107
+	 * Gets the array of billing info for the gateway and for this transaction's primary registration's attendee.
1108
+	 *
1109
+	 * @return EE_Form_Section_Proper|null
1110
+	 * @throws EE_Error
1111
+	 * @throws InvalidArgumentException
1112
+	 * @throws InvalidDataTypeException
1113
+	 * @throws InvalidInterfaceException
1114
+	 * @throws ReflectionException
1115
+	 */
1116
+	public function billing_info(): ?EE_Form_Section_Proper
1117
+	{
1118
+		$payment_method = $this->payment_method();
1119
+		if (! $payment_method) {
1120
+			EE_Error::add_error(
1121
+				esc_html__(
1122
+					'Could not find billing info for transaction because no gateway has been used for it yet',
1123
+					'event_espresso'
1124
+				),
1125
+				__FILE__,
1126
+				__FUNCTION__,
1127
+				__LINE__
1128
+			);
1129
+			return null;
1130
+		}
1131
+		$primary_reg = $this->primary_registration();
1132
+		if (! $primary_reg) {
1133
+			EE_Error::add_error(
1134
+				esc_html__(
1135
+					'Cannot get billing info for gateway %s on transaction because no primary registration exists',
1136
+					'event_espresso'
1137
+				),
1138
+				__FILE__,
1139
+				__FUNCTION__,
1140
+				__LINE__
1141
+			);
1142
+			return null;
1143
+		}
1144
+		$attendee = $primary_reg->attendee();
1145
+		if (! $attendee) {
1146
+			EE_Error::add_error(
1147
+				esc_html__(
1148
+					'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
1149
+					'event_espresso'
1150
+				),
1151
+				__FILE__,
1152
+				__FUNCTION__,
1153
+				__LINE__
1154
+			);
1155
+			return null;
1156
+		}
1157
+		return $attendee->billing_info_for_payment_method($payment_method);
1158
+	}
1159
+
1160
+
1161
+	/**
1162
+	 * Gets PMD_ID
1163
+	 *
1164
+	 * @return int
1165
+	 * @throws EE_Error
1166
+	 * @throws InvalidArgumentException
1167
+	 * @throws InvalidDataTypeException
1168
+	 * @throws InvalidInterfaceException
1169
+	 * @throws ReflectionException
1170
+	 */
1171
+	public function payment_method_ID()
1172
+	{
1173
+		return $this->get('PMD_ID');
1174
+	}
1175
+
1176
+
1177
+	/**
1178
+	 * Sets PMD_ID
1179
+	 *
1180
+	 * @param int $PMD_ID
1181
+	 * @throws EE_Error
1182
+	 * @throws InvalidArgumentException
1183
+	 * @throws InvalidDataTypeException
1184
+	 * @throws InvalidInterfaceException
1185
+	 * @throws ReflectionException
1186
+	 */
1187
+	public function set_payment_method_ID($PMD_ID)
1188
+	{
1189
+		$this->set('PMD_ID', $PMD_ID);
1190
+	}
1191
+
1192
+
1193
+	/**
1194
+	 * Gets the last-used payment method on this transaction
1195
+	 * (we COULD just use the last-made payment, but some payment methods, namely
1196
+	 * offline ones, dont' create payments)
1197
+	 *
1198
+	 * @return EE_Payment_Method
1199
+	 * @throws EE_Error
1200
+	 * @throws InvalidArgumentException
1201
+	 * @throws InvalidDataTypeException
1202
+	 * @throws InvalidInterfaceException
1203
+	 * @throws ReflectionException
1204
+	 */
1205
+	public function payment_method()
1206
+	{
1207
+		$PMD_ID = $this->payment_method_ID();
1208
+		if ($PMD_ID) {
1209
+			$pm = EEM_Payment_Method::instance()->get_one_by_ID($this->payment_method_ID());
1210
+			if ($pm instanceof EE_Payment_Method) {
1211
+				return $pm;
1212
+			}
1213
+		}
1214
+		$last_payment = $this->last_payment();
1215
+		if ($last_payment instanceof EE_Payment && $last_payment->payment_method()) {
1216
+			return $last_payment->payment_method();
1217
+		}
1218
+		return null;
1219
+	}
1220
+
1221
+
1222
+	/**
1223
+	 * Gets the last payment made
1224
+	 *
1225
+	 * @return EE_Base_Class|EE_Payment|null
1226
+	 * @throws EE_Error
1227
+	 * @throws InvalidArgumentException
1228
+	 * @throws InvalidDataTypeException
1229
+	 * @throws InvalidInterfaceException
1230
+	 * @throws ReflectionException
1231
+	 */
1232
+	public function last_payment(): ?EE_Payment
1233
+	{
1234
+		return $this->get_first_related('Payment', ['order_by' => ['PAY_ID' => 'desc']]);
1235
+	}
1236
+
1237
+
1238
+	/**
1239
+	 * Gets all the line items which are unrelated to tickets on this transaction
1240
+	 *
1241
+	 * @return EE_Line_Item[]
1242
+	 * @throws EE_Error
1243
+	 * @throws InvalidArgumentException
1244
+	 * @throws InvalidDataTypeException
1245
+	 * @throws InvalidInterfaceException
1246
+	 * @throws ReflectionException
1247
+	 */
1248
+	public function non_ticket_line_items()
1249
+	{
1250
+		return EEM_Line_Item::instance()->get_all_non_ticket_line_items_for_transaction($this->ID());
1251
+	}
1252
+
1253
+
1254
+	/**
1255
+	 * possibly toggles TXN status
1256
+	 *
1257
+	 * @param boolean $update whether to save the TXN
1258
+	 * @return bool whether the TXN was saved
1259
+	 * @throws EE_Error
1260
+	 * @throws InvalidArgumentException
1261
+	 * @throws InvalidDataTypeException
1262
+	 * @throws InvalidInterfaceException
1263
+	 * @throws ReflectionException
1264
+	 * @throws RuntimeException
1265
+	 */
1266
+	public function update_status_based_on_total_paid($update = true)
1267
+	{
1268
+		// set transaction status based on comparison of TXN_paid vs TXN_total
1269
+		if (EEH_Money::compare_floats($this->paid(), $this->total(), '>')) {
1270
+			$new_txn_status = EEM_Transaction::overpaid_status_code;
1271
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total())) {
1272
+			$new_txn_status = EEM_Transaction::complete_status_code;
1273
+		} elseif (EEH_Money::compare_floats($this->paid(), $this->total(), '<')) {
1274
+			$new_txn_status = EEM_Transaction::incomplete_status_code;
1275
+		} else {
1276
+			throw new RuntimeException(
1277
+				esc_html__('The total paid calculation for this transaction is inaccurate.', 'event_espresso')
1278
+			);
1279
+		}
1280
+		if ($new_txn_status !== $this->status_ID()) {
1281
+			$this->set_status($new_txn_status);
1282
+			if ($update) {
1283
+				return (bool) $this->save();
1284
+			}
1285
+		}
1286
+		return false;
1287
+	}
1288
+
1289
+
1290
+	/**
1291
+	 * Updates the transaction's status and total_paid based on all the payments
1292
+	 * that apply to it
1293
+	 *
1294
+	 * @return array|bool
1295
+	 * @throws EE_Error
1296
+	 * @throws InvalidArgumentException
1297
+	 * @throws ReflectionException
1298
+	 * @throws InvalidDataTypeException
1299
+	 * @throws InvalidInterfaceException
1300
+	 * @deprecated
1301
+	 */
1302
+	public function update_based_on_payments()
1303
+	{
1304
+		EE_Error::doing_it_wrong(
1305
+			__CLASS__ . '::' . __FUNCTION__,
1306
+			sprintf(
1307
+				esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1308
+				'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
1309
+			),
1310
+			'4.6.0'
1311
+		);
1312
+		/** @type EE_Transaction_Processor $transaction_processor */
1313
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
1314
+		return $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment($this);
1315
+	}
1316
+
1317
+
1318
+	/**
1319
+	 * @return string
1320
+	 */
1321
+	public function old_txn_status()
1322
+	{
1323
+		return $this->_old_txn_status;
1324
+	}
1325
+
1326
+
1327
+	/**
1328
+	 * @param string $old_txn_status
1329
+	 */
1330
+	public function set_old_txn_status($old_txn_status)
1331
+	{
1332
+		// only set the first time
1333
+		if ($this->_old_txn_status === null) {
1334
+			$this->_old_txn_status = $old_txn_status;
1335
+		}
1336
+	}
1337
+
1338
+
1339
+	/**
1340
+	 * reg_status_updated
1341
+	 *
1342
+	 * @return bool
1343
+	 * @throws EE_Error
1344
+	 * @throws InvalidArgumentException
1345
+	 * @throws InvalidDataTypeException
1346
+	 * @throws InvalidInterfaceException
1347
+	 * @throws ReflectionException
1348
+	 */
1349
+	public function txn_status_updated()
1350
+	{
1351
+		return $this->status_ID() !== $this->_old_txn_status && $this->_old_txn_status !== null;
1352
+	}
1353
+
1354
+
1355
+	/**
1356
+	 * _reg_steps_completed
1357
+	 * if $check_all is TRUE, then returns TRUE if ALL reg steps have been marked as completed,
1358
+	 * if a $reg_step_slug is provided, then this step will be skipped when testing for completion
1359
+	 * if $check_all is FALSE and a $reg_step_slug is provided, then ONLY that reg step will be tested for completion
1360
+	 *
1361
+	 * @param string $reg_step_slug
1362
+	 * @param bool   $check_all
1363
+	 * @return bool|int
1364
+	 * @throws EE_Error
1365
+	 * @throws InvalidArgumentException
1366
+	 * @throws InvalidDataTypeException
1367
+	 * @throws InvalidInterfaceException
1368
+	 * @throws ReflectionException
1369
+	 */
1370
+	private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1371
+	{
1372
+		$reg_steps = $this->reg_steps();
1373
+		if (! is_array($reg_steps) || empty($reg_steps)) {
1374
+			return false;
1375
+		}
1376
+		// loop thru reg steps array)
1377
+		foreach ($reg_steps as $slug => $reg_step_completed) {
1378
+			// if NOT checking ALL steps (only checking one step)
1379
+			if (! $check_all) {
1380
+				// and this is the one
1381
+				if ($slug === $reg_step_slug) {
1382
+					return $reg_step_completed;
1383
+				}
1384
+				// skip to next reg step in loop
1385
+				continue;
1386
+			}
1387
+			// $check_all must be true, else we would never have gotten to this point
1388
+			if ($slug === $reg_step_slug) {
1389
+				// if we reach this point, then we are testing either:
1390
+				// all_reg_steps_completed_except() or
1391
+				// all_reg_steps_completed_except_final_step(),
1392
+				// and since this is the reg step EXCEPTION being tested
1393
+				// we want to return true (yes true) if this reg step is NOT completed
1394
+				// ie: "is everything completed except the final step?"
1395
+				// "that is correct... the final step is not completed, but all others are."
1396
+				return $reg_step_completed !== true;
1397
+			}
1398
+			if ($reg_step_completed !== true) {
1399
+				// if any reg step is NOT completed, then ALL steps are not completed
1400
+				return false;
1401
+			}
1402
+		}
1403
+		return true;
1404
+	}
1405
+
1406
+
1407
+	/**
1408
+	 * all_reg_steps_completed
1409
+	 * returns:
1410
+	 *    true if ALL reg steps have been marked as completed
1411
+	 *        or false if any step is not completed
1412
+	 *
1413
+	 * @return bool
1414
+	 * @throws EE_Error
1415
+	 * @throws InvalidArgumentException
1416
+	 * @throws InvalidDataTypeException
1417
+	 * @throws InvalidInterfaceException
1418
+	 * @throws ReflectionException
1419
+	 */
1420
+	public function all_reg_steps_completed()
1421
+	{
1422
+		return $this->_reg_steps_completed();
1423
+	}
1424
+
1425
+
1426
+	/**
1427
+	 * all_reg_steps_completed_except
1428
+	 * returns:
1429
+	 *        true if ALL reg steps, except a particular step that you wish to skip over, have been marked as completed
1430
+	 *        or false if any other step is not completed
1431
+	 *        or false if ALL steps are completed including the exception you are testing !!!
1432
+	 *
1433
+	 * @param string $exception
1434
+	 * @return bool
1435
+	 * @throws EE_Error
1436
+	 * @throws InvalidArgumentException
1437
+	 * @throws InvalidDataTypeException
1438
+	 * @throws InvalidInterfaceException
1439
+	 * @throws ReflectionException
1440
+	 */
1441
+	public function all_reg_steps_completed_except($exception = '')
1442
+	{
1443
+		return $this->_reg_steps_completed($exception);
1444
+	}
1445
+
1446
+
1447
+	/**
1448
+	 * all_reg_steps_completed_except
1449
+	 * returns:
1450
+	 *        true if ALL reg steps, except the final step, have been marked as completed
1451
+	 *        or false if any step is not completed
1452
+	 *    or false if ALL steps are completed including the final step !!!
1453
+	 *
1454
+	 * @return bool
1455
+	 * @throws EE_Error
1456
+	 * @throws InvalidArgumentException
1457
+	 * @throws InvalidDataTypeException
1458
+	 * @throws InvalidInterfaceException
1459
+	 * @throws ReflectionException
1460
+	 */
1461
+	public function all_reg_steps_completed_except_final_step()
1462
+	{
1463
+		return $this->_reg_steps_completed('finalize_registration');
1464
+	}
1465
+
1466
+
1467
+	/**
1468
+	 * reg_step_completed
1469
+	 * returns:
1470
+	 *    true if a specific reg step has been marked as completed
1471
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1472
+	 *    or false if it has not yet been initialized
1473
+	 *
1474
+	 * @param string $reg_step_slug
1475
+	 * @return bool|int
1476
+	 * @throws EE_Error
1477
+	 * @throws InvalidArgumentException
1478
+	 * @throws InvalidDataTypeException
1479
+	 * @throws InvalidInterfaceException
1480
+	 * @throws ReflectionException
1481
+	 */
1482
+	public function reg_step_completed($reg_step_slug)
1483
+	{
1484
+		return $this->_reg_steps_completed($reg_step_slug, false);
1485
+	}
1486
+
1487
+
1488
+	/**
1489
+	 * completed_final_reg_step
1490
+	 * returns:
1491
+	 *    true if the finalize_registration reg step has been marked as completed
1492
+	 *    a Unix timestamp if it has been initialized but not yet completed,
1493
+	 *    or false if it has not yet been initialized
1494
+	 *
1495
+	 * @return bool|int
1496
+	 * @throws EE_Error
1497
+	 * @throws InvalidArgumentException
1498
+	 * @throws InvalidDataTypeException
1499
+	 * @throws InvalidInterfaceException
1500
+	 * @throws ReflectionException
1501
+	 */
1502
+	public function final_reg_step_completed()
1503
+	{
1504
+		return $this->_reg_steps_completed('finalize_registration', false);
1505
+	}
1506
+
1507
+
1508
+	/**
1509
+	 * set_reg_step_initiated
1510
+	 * given a valid TXN_reg_step, this sets it's value to a unix timestamp
1511
+	 *
1512
+	 * @param string $reg_step_slug
1513
+	 * @return boolean
1514
+	 * @throws EE_Error
1515
+	 * @throws InvalidArgumentException
1516
+	 * @throws InvalidDataTypeException
1517
+	 * @throws InvalidInterfaceException
1518
+	 * @throws ReflectionException
1519
+	 */
1520
+	public function set_reg_step_initiated($reg_step_slug)
1521
+	{
1522
+		return $this->_set_reg_step_completed_status($reg_step_slug, time());
1523
+	}
1524
+
1525
+
1526
+	/**
1527
+	 * set_reg_step_completed
1528
+	 * given a valid TXN_reg_step, this sets the step as completed
1529
+	 *
1530
+	 * @param string $reg_step_slug
1531
+	 * @return boolean
1532
+	 * @throws EE_Error
1533
+	 * @throws InvalidArgumentException
1534
+	 * @throws InvalidDataTypeException
1535
+	 * @throws InvalidInterfaceException
1536
+	 * @throws ReflectionException
1537
+	 */
1538
+	public function set_reg_step_completed($reg_step_slug)
1539
+	{
1540
+		return $this->_set_reg_step_completed_status($reg_step_slug, true);
1541
+	}
1542
+
1543
+
1544
+	/**
1545
+	 * set_reg_step_completed
1546
+	 * given a valid TXN_reg_step slug, this sets the step as NOT completed
1547
+	 *
1548
+	 * @param string $reg_step_slug
1549
+	 * @return boolean
1550
+	 * @throws EE_Error
1551
+	 * @throws InvalidArgumentException
1552
+	 * @throws InvalidDataTypeException
1553
+	 * @throws InvalidInterfaceException
1554
+	 * @throws ReflectionException
1555
+	 */
1556
+	public function set_reg_step_not_completed($reg_step_slug)
1557
+	{
1558
+		return $this->_set_reg_step_completed_status($reg_step_slug, false);
1559
+	}
1560
+
1561
+
1562
+	/**
1563
+	 * set_reg_step_completed
1564
+	 * given a valid reg step slug, this sets the TXN_reg_step completed status which is either:
1565
+	 *
1566
+	 * @param string      $reg_step_slug
1567
+	 * @param boolean|int $status
1568
+	 * @return boolean
1569
+	 * @throws EE_Error
1570
+	 * @throws InvalidArgumentException
1571
+	 * @throws InvalidDataTypeException
1572
+	 * @throws InvalidInterfaceException
1573
+	 * @throws ReflectionException
1574
+	 */
1575
+	private function _set_reg_step_completed_status($reg_step_slug, $status)
1576
+	{
1577
+		// validate status
1578
+		$status = is_bool($status) || is_int($status)
1579
+			? $status
1580
+			: false;
1581
+		// get reg steps array
1582
+		$txn_reg_steps = $this->reg_steps();
1583
+		// if reg step does NOT exist
1584
+		if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1585
+			return false;
1586
+		}
1587
+		// if  we're trying to complete a step that is already completed
1588
+		if ($txn_reg_steps[ $reg_step_slug ] === true) {
1589
+			return true;
1590
+		}
1591
+		// if  we're trying to complete a step that hasn't even started
1592
+		if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1593
+			return false;
1594
+		}
1595
+		// if current status value matches the incoming value (no change)
1596
+		// type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1597
+		if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1598
+			// this will happen in cases where multiple AJAX requests occur during the same step
1599
+			return true;
1600
+		}
1601
+		// if we're trying to set a start time, but it has already been set...
1602
+		if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1603
+			// skip the update below, but don't return FALSE so that errors won't be displayed
1604
+			return true;
1605
+		}
1606
+		// update completed status
1607
+		$txn_reg_steps[ $reg_step_slug ] = $status;
1608
+		$this->set_reg_steps($txn_reg_steps);
1609
+		$this->save();
1610
+		return true;
1611
+	}
1612
+
1613
+
1614
+	/**
1615
+	 * remove_reg_step
1616
+	 * given a valid TXN_reg_step slug, this will remove (unset)
1617
+	 * the reg step from the TXN reg step array
1618
+	 *
1619
+	 * @param string $reg_step_slug
1620
+	 * @return void
1621
+	 * @throws EE_Error
1622
+	 * @throws InvalidArgumentException
1623
+	 * @throws InvalidDataTypeException
1624
+	 * @throws InvalidInterfaceException
1625
+	 * @throws ReflectionException
1626
+	 */
1627
+	public function remove_reg_step($reg_step_slug)
1628
+	{
1629
+		// get reg steps array
1630
+		$txn_reg_steps = $this->reg_steps();
1631
+		unset($txn_reg_steps[ $reg_step_slug ]);
1632
+		$this->set_reg_steps($txn_reg_steps);
1633
+	}
1634
+
1635
+
1636
+	/**
1637
+	 * toggle_failed_transaction_status
1638
+	 * upgrades a TXNs status from failed to abandoned,
1639
+	 * meaning that contact information has been captured for at least one registrant
1640
+	 *
1641
+	 * @param bool $save
1642
+	 * @return bool
1643
+	 * @throws EE_Error
1644
+	 * @throws InvalidArgumentException
1645
+	 * @throws InvalidDataTypeException
1646
+	 * @throws InvalidInterfaceException
1647
+	 * @throws ReflectionException
1648
+	 */
1649
+	public function toggle_failed_transaction_status($save = true)
1650
+	{
1651
+		// if TXN status is still set as "failed"...
1652
+		if ($this->status_ID() === EEM_Transaction::failed_status_code) {
1653
+			$this->set_status(EEM_Transaction::abandoned_status_code);
1654
+			if ($save) {
1655
+				$this->save();
1656
+			}
1657
+			return true;
1658
+		}
1659
+		return false;
1660
+	}
1661
+
1662
+
1663
+	/**
1664
+	 * toggle_abandoned_transaction_status
1665
+	 * upgrades a TXNs status from failed or abandoned to incomplete
1666
+	 *
1667
+	 * @return bool
1668
+	 * @throws EE_Error
1669
+	 * @throws InvalidArgumentException
1670
+	 * @throws InvalidDataTypeException
1671
+	 * @throws InvalidInterfaceException
1672
+	 * @throws ReflectionException
1673
+	 */
1674
+	public function toggle_abandoned_transaction_status()
1675
+	{
1676
+		// if TXN status has not been updated already due to a payment, and is still set as "failed" or "abandoned"...
1677
+		$txn_status = $this->status_ID();
1678
+		if (
1679
+			$txn_status === EEM_Transaction::failed_status_code
1680
+			|| $txn_status === EEM_Transaction::abandoned_status_code
1681
+		) {
1682
+			// if a contact record for the primary registrant has been created
1683
+			if (
1684
+				$this->primary_registration() instanceof EE_Registration
1685
+				&& $this->primary_registration()->attendee() instanceof EE_Attendee
1686
+			) {
1687
+				$this->set_status(EEM_Transaction::incomplete_status_code);
1688
+			} else {
1689
+				// no contact record? yer abandoned!
1690
+				$this->set_status(EEM_Transaction::abandoned_status_code);
1691
+			}
1692
+			return true;
1693
+		}
1694
+		return false;
1695
+	}
1696
+
1697
+
1698
+	/**
1699
+	 * checks if an Abandoned TXN has any related payments, and if so,
1700
+	 * updates the TXN status based on the amount paid
1701
+	 *
1702
+	 * @throws EE_Error
1703
+	 * @throws InvalidArgumentException
1704
+	 * @throws InvalidDataTypeException
1705
+	 * @throws InvalidInterfaceException
1706
+	 * @throws ReflectionException
1707
+	 * @throws RuntimeException
1708
+	 * @throws ReflectionException
1709
+	 */
1710
+	public function verify_abandoned_transaction_status()
1711
+	{
1712
+		if ($this->status_ID() !== EEM_Transaction::abandoned_status_code) {
1713
+			return;
1714
+		}
1715
+		$payments = $this->get_many_related('Payment');
1716
+		if (! empty($payments)) {
1717
+			foreach ($payments as $payment) {
1718
+				if ($payment instanceof EE_Payment) {
1719
+					// kk this TXN should NOT be abandoned
1720
+					$this->update_status_based_on_total_paid();
1721
+					if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1722
+						EE_Error::add_attention(
1723
+							sprintf(
1724
+								esc_html__(
1725
+									'The status for Transaction #%1$d has been updated from "Abandoned" to "%2$s", because at least one payment has been made towards it. If the payment appears in the "Payment Details" table below, you may need to edit its status and/or other details as well.',
1726
+									'event_espresso'
1727
+								),
1728
+								$this->ID(),
1729
+								$this->pretty_status()
1730
+							)
1731
+						);
1732
+					}
1733
+					// get final reg step status
1734
+					$finalized = $this->final_reg_step_completed();
1735
+					// if the 'finalize_registration' step has been initiated (has a timestamp)
1736
+					// but has not yet been fully completed (TRUE)
1737
+					if (is_int($finalized) && $finalized !== false && $finalized !== true) {
1738
+						$this->set_reg_step_completed('finalize_registration');
1739
+						$this->save();
1740
+					}
1741
+				}
1742
+			}
1743
+		}
1744
+	}
1745
+
1746
+
1747
+	/**
1748
+	 * @throws EE_Error
1749
+	 * @throws InvalidArgumentException
1750
+	 * @throws InvalidDataTypeException
1751
+	 * @throws InvalidInterfaceException
1752
+	 * @throws ReflectionException
1753
+	 * @throws RuntimeException
1754
+	 * @since 4.10.4.p
1755
+	 */
1756
+	public function recalculateLineItems()
1757
+	{
1758
+		$total_line_item = $this->total_line_item(false);
1759
+		if ($total_line_item instanceof EE_Line_Item) {
1760
+			EEH_Line_Item::resetIsTaxableForTickets($total_line_item);
1761
+			return EEH_Line_Item::apply_taxes($total_line_item, true);
1762
+		}
1763
+		return false;
1764
+	}
1765 1765
 }
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -47,7 +47,7 @@  discard block
 block discarded – undo
47 47
         $txn        = $has_object
48 48
             ? $has_object
49 49
             : new self($props_n_values, false, $timezone, $date_formats);
50
-        if (! $has_object) {
50
+        if ( ! $has_object) {
51 51
             $txn->set_old_txn_status($txn->status_ID());
52 52
         }
53 53
         return $txn;
@@ -87,7 +87,7 @@  discard block
 block discarded – undo
87 87
     public function lock()
88 88
     {
89 89
         // attempt to set lock, but if that fails...
90
-        if (! $this->add_extra_meta('lock', time(), true)) {
90
+        if ( ! $this->add_extra_meta('lock', time(), true)) {
91 91
             // then attempt to remove the lock in case it is expired
92 92
             if ($this->_remove_expired_lock()) {
93 93
                 // if removal was successful, then try setting lock again
@@ -143,7 +143,7 @@  discard block
 block discarded – undo
143 143
     public function is_locked()
144 144
     {
145 145
         // if TXN is not locked, then return false immediately
146
-        if (! $this->_get_lock()) {
146
+        if ( ! $this->_get_lock()) {
147 147
             return false;
148 148
         }
149 149
         // if not, then let's try and remove the lock in case it's expired...
@@ -638,7 +638,7 @@  discard block
 block discarded – undo
638 638
             false,
639 639
             'sentence'
640 640
         );
641
-        $icon   = '';
641
+        $icon = '';
642 642
         switch ($this->status_ID()) {
643 643
             case EEM_Transaction::complete_status_code:
644 644
                 $icon = $show_icons
@@ -666,7 +666,7 @@  discard block
 block discarded – undo
666 666
                     : '';
667 667
                 break;
668 668
         }
669
-        return $icon . $status[ $this->status_ID() ];
669
+        return $icon.$status[$this->status_ID()];
670 670
     }
671 671
 
672 672
 
@@ -804,7 +804,7 @@  discard block
 block discarded – undo
804 804
     public function invoice_url($type = 'html')
805 805
     {
806 806
         $REG = $this->primary_registration();
807
-        if (! $REG instanceof EE_Registration) {
807
+        if ( ! $REG instanceof EE_Registration) {
808 808
             return '';
809 809
         }
810 810
         return $REG->invoice_url($type);
@@ -863,7 +863,7 @@  discard block
 block discarded – undo
863 863
     public function receipt_url($type = 'html')
864 864
     {
865 865
         $REG = $this->primary_registration();
866
-        if (! $REG instanceof EE_Registration) {
866
+        if ( ! $REG instanceof EE_Registration) {
867 867
             return '';
868 868
         }
869 869
         return $REG->receipt_url($type);
@@ -1116,7 +1116,7 @@  discard block
 block discarded – undo
1116 1116
     public function billing_info(): ?EE_Form_Section_Proper
1117 1117
     {
1118 1118
         $payment_method = $this->payment_method();
1119
-        if (! $payment_method) {
1119
+        if ( ! $payment_method) {
1120 1120
             EE_Error::add_error(
1121 1121
                 esc_html__(
1122 1122
                     'Could not find billing info for transaction because no gateway has been used for it yet',
@@ -1129,7 +1129,7 @@  discard block
 block discarded – undo
1129 1129
             return null;
1130 1130
         }
1131 1131
         $primary_reg = $this->primary_registration();
1132
-        if (! $primary_reg) {
1132
+        if ( ! $primary_reg) {
1133 1133
             EE_Error::add_error(
1134 1134
                 esc_html__(
1135 1135
                     'Cannot get billing info for gateway %s on transaction because no primary registration exists',
@@ -1142,7 +1142,7 @@  discard block
 block discarded – undo
1142 1142
             return null;
1143 1143
         }
1144 1144
         $attendee = $primary_reg->attendee();
1145
-        if (! $attendee) {
1145
+        if ( ! $attendee) {
1146 1146
             EE_Error::add_error(
1147 1147
                 esc_html__(
1148 1148
                     'Cannot get billing info for gateway %s on transaction because the primary registration has no attendee exists',
@@ -1302,7 +1302,7 @@  discard block
 block discarded – undo
1302 1302
     public function update_based_on_payments()
1303 1303
     {
1304 1304
         EE_Error::doing_it_wrong(
1305
-            __CLASS__ . '::' . __FUNCTION__,
1305
+            __CLASS__.'::'.__FUNCTION__,
1306 1306
             sprintf(
1307 1307
                 esc_html__('This method is deprecated. Please use "%s" instead', 'event_espresso'),
1308 1308
                 'EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()'
@@ -1370,13 +1370,13 @@  discard block
 block discarded – undo
1370 1370
     private function _reg_steps_completed($reg_step_slug = '', $check_all = true)
1371 1371
     {
1372 1372
         $reg_steps = $this->reg_steps();
1373
-        if (! is_array($reg_steps) || empty($reg_steps)) {
1373
+        if ( ! is_array($reg_steps) || empty($reg_steps)) {
1374 1374
             return false;
1375 1375
         }
1376 1376
         // loop thru reg steps array)
1377 1377
         foreach ($reg_steps as $slug => $reg_step_completed) {
1378 1378
             // if NOT checking ALL steps (only checking one step)
1379
-            if (! $check_all) {
1379
+            if ( ! $check_all) {
1380 1380
                 // and this is the one
1381 1381
                 if ($slug === $reg_step_slug) {
1382 1382
                     return $reg_step_completed;
@@ -1581,30 +1581,30 @@  discard block
 block discarded – undo
1581 1581
         // get reg steps array
1582 1582
         $txn_reg_steps = $this->reg_steps();
1583 1583
         // if reg step does NOT exist
1584
-        if (! isset($txn_reg_steps[ $reg_step_slug ])) {
1584
+        if ( ! isset($txn_reg_steps[$reg_step_slug])) {
1585 1585
             return false;
1586 1586
         }
1587 1587
         // if  we're trying to complete a step that is already completed
1588
-        if ($txn_reg_steps[ $reg_step_slug ] === true) {
1588
+        if ($txn_reg_steps[$reg_step_slug] === true) {
1589 1589
             return true;
1590 1590
         }
1591 1591
         // if  we're trying to complete a step that hasn't even started
1592
-        if ($status === true && $txn_reg_steps[ $reg_step_slug ] === false) {
1592
+        if ($status === true && $txn_reg_steps[$reg_step_slug] === false) {
1593 1593
             return false;
1594 1594
         }
1595 1595
         // if current status value matches the incoming value (no change)
1596 1596
         // type casting as int means values should collapse to either 0, 1, or a timestamp like 1234567890
1597
-        if ((int) $txn_reg_steps[ $reg_step_slug ] === (int) $status) {
1597
+        if ((int) $txn_reg_steps[$reg_step_slug] === (int) $status) {
1598 1598
             // this will happen in cases where multiple AJAX requests occur during the same step
1599 1599
             return true;
1600 1600
         }
1601 1601
         // if we're trying to set a start time, but it has already been set...
1602
-        if (is_numeric($status) && is_numeric($txn_reg_steps[ $reg_step_slug ])) {
1602
+        if (is_numeric($status) && is_numeric($txn_reg_steps[$reg_step_slug])) {
1603 1603
             // skip the update below, but don't return FALSE so that errors won't be displayed
1604 1604
             return true;
1605 1605
         }
1606 1606
         // update completed status
1607
-        $txn_reg_steps[ $reg_step_slug ] = $status;
1607
+        $txn_reg_steps[$reg_step_slug] = $status;
1608 1608
         $this->set_reg_steps($txn_reg_steps);
1609 1609
         $this->save();
1610 1610
         return true;
@@ -1628,7 +1628,7 @@  discard block
 block discarded – undo
1628 1628
     {
1629 1629
         // get reg steps array
1630 1630
         $txn_reg_steps = $this->reg_steps();
1631
-        unset($txn_reg_steps[ $reg_step_slug ]);
1631
+        unset($txn_reg_steps[$reg_step_slug]);
1632 1632
         $this->set_reg_steps($txn_reg_steps);
1633 1633
     }
1634 1634
 
@@ -1713,12 +1713,12 @@  discard block
 block discarded – undo
1713 1713
             return;
1714 1714
         }
1715 1715
         $payments = $this->get_many_related('Payment');
1716
-        if (! empty($payments)) {
1716
+        if ( ! empty($payments)) {
1717 1717
             foreach ($payments as $payment) {
1718 1718
                 if ($payment instanceof EE_Payment) {
1719 1719
                     // kk this TXN should NOT be abandoned
1720 1720
                     $this->update_status_based_on_total_paid();
1721
-                    if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1721
+                    if ( ! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1722 1722
                         EE_Error::add_attention(
1723 1723
                             sprintf(
1724 1724
                                 esc_html__(
Please login to merge, or discard this patch.
core/db_classes/EE_Import.class.php 1 patch
Indentation   +959 added lines, -959 removed lines patch added patch discarded remove patch
@@ -13,94 +13,94 @@  discard block
 block discarded – undo
13 13
  */
14 14
 class EE_Import implements ResettableInterface
15 15
 {
16
-    const do_insert = 'insert';
17
-    const do_update = 'update';
18
-    const do_nothing = 'nothing';
19
-
20
-
21
-    /**
22
-     * instance of the EE_Import object
23
-     */
24
-    private static $_instance;
25
-
26
-    private static $_csv_array = array();
27
-
28
-    /**
29
-     * @var array of model names
30
-     */
31
-    private static $_model_list = array();
32
-
33
-    private static $_columns_to_save = array();
34
-
35
-    protected $_total_inserts = 0;
36
-    protected $_total_updates = 0;
37
-    protected $_total_insert_errors = 0;
38
-    protected $_total_update_errors = 0;
39
-
40
-    /**
41
-     * @var EE_CSV
42
-     * @since 4.10.14.p
43
-     */
44
-    private $EE_CSV;
45
-
46
-
47
-    /**
48
-     * private constructor to prevent direct creation
49
-     *
50
-     * @return void
51
-     */
52
-    private function __construct()
53
-    {
54
-        $this->_total_inserts = 0;
55
-        $this->_total_updates = 0;
56
-        $this->_total_insert_errors = 0;
57
-        $this->_total_update_errors = 0;
58
-    }
59
-
60
-
61
-    /**
62
-     * singleton method used to instantiate class object
63
-     *
64
-     * @return EE_Import
65
-     */
66
-    public static function instance()
67
-    {
68
-        // check if class object is instantiated
69
-        if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Import)) {
70
-            self::$_instance = new self();
71
-        }
72
-        return self::$_instance;
73
-    }
74
-
75
-    /**
76
-     * Resets the importer
77
-     *
78
-     * @return EE_Import
79
-     */
80
-    public static function reset()
81
-    {
82
-        self::$_instance = null;
83
-        return self::instance();
84
-    }
85
-
86
-
87
-    /**
88
-     * generates HTML for a file upload input and form
89
-     *
90
-     * @param    string $title  - heading for the form
91
-     * @param    string $intro  - additional text explaing what to do
92
-     * @param    string $page   - EE Admin page to direct form to - in the form "espresso_{pageslug}"
93
-     * @param    string $action - EE Admin page route array "action" that form will direct to
94
-     * @param    string $type   - type of file to import
95
-     * @return    string
96
-     */
97
-    public function upload_form($title, $intro, $form_url, $action, $type)
98
-    {
99
-
100
-        $form_url = EE_Admin_Page::add_query_args_and_nonce(array('action' => $action), $form_url);
101
-
102
-        ob_start();
103
-        ?>
16
+	const do_insert = 'insert';
17
+	const do_update = 'update';
18
+	const do_nothing = 'nothing';
19
+
20
+
21
+	/**
22
+	 * instance of the EE_Import object
23
+	 */
24
+	private static $_instance;
25
+
26
+	private static $_csv_array = array();
27
+
28
+	/**
29
+	 * @var array of model names
30
+	 */
31
+	private static $_model_list = array();
32
+
33
+	private static $_columns_to_save = array();
34
+
35
+	protected $_total_inserts = 0;
36
+	protected $_total_updates = 0;
37
+	protected $_total_insert_errors = 0;
38
+	protected $_total_update_errors = 0;
39
+
40
+	/**
41
+	 * @var EE_CSV
42
+	 * @since 4.10.14.p
43
+	 */
44
+	private $EE_CSV;
45
+
46
+
47
+	/**
48
+	 * private constructor to prevent direct creation
49
+	 *
50
+	 * @return void
51
+	 */
52
+	private function __construct()
53
+	{
54
+		$this->_total_inserts = 0;
55
+		$this->_total_updates = 0;
56
+		$this->_total_insert_errors = 0;
57
+		$this->_total_update_errors = 0;
58
+	}
59
+
60
+
61
+	/**
62
+	 * singleton method used to instantiate class object
63
+	 *
64
+	 * @return EE_Import
65
+	 */
66
+	public static function instance()
67
+	{
68
+		// check if class object is instantiated
69
+		if (self::$_instance === null or ! is_object(self::$_instance) or ! (self::$_instance instanceof EE_Import)) {
70
+			self::$_instance = new self();
71
+		}
72
+		return self::$_instance;
73
+	}
74
+
75
+	/**
76
+	 * Resets the importer
77
+	 *
78
+	 * @return EE_Import
79
+	 */
80
+	public static function reset()
81
+	{
82
+		self::$_instance = null;
83
+		return self::instance();
84
+	}
85
+
86
+
87
+	/**
88
+	 * generates HTML for a file upload input and form
89
+	 *
90
+	 * @param    string $title  - heading for the form
91
+	 * @param    string $intro  - additional text explaing what to do
92
+	 * @param    string $page   - EE Admin page to direct form to - in the form "espresso_{pageslug}"
93
+	 * @param    string $action - EE Admin page route array "action" that form will direct to
94
+	 * @param    string $type   - type of file to import
95
+	 * @return    string
96
+	 */
97
+	public function upload_form($title, $intro, $form_url, $action, $type)
98
+	{
99
+
100
+		$form_url = EE_Admin_Page::add_query_args_and_nonce(array('action' => $action), $form_url);
101
+
102
+		ob_start();
103
+		?>
104 104
         <div class="ee-upload-form-dv">
105 105
             <h3><?php echo esc_html($title); ?></h3>
106 106
             <p><?php echo esc_html($intro); ?></p>
@@ -116,880 +116,880 @@  discard block
 block discarded – undo
116 116
                 <b><?php esc_html_e('Attention', 'event_espresso'); ?></b><br/>
117 117
                 <?php echo sprintf(esc_html__('Accepts .%s file types only.', 'event_espresso'), $type); ?>
118 118
                 <?php echo esc_html__(
119
-                    'Please only import CSV files exported from Event Espresso, or compatible 3rd-party software.',
120
-                    'event_espresso'
121
-                ); ?>
119
+					'Please only import CSV files exported from Event Espresso, or compatible 3rd-party software.',
120
+					'event_espresso'
121
+				); ?>
122 122
             </p>
123 123
 
124 124
         </div>
125 125
 
126 126
         <?php
127
-        $uploader = ob_get_clean();
128
-        return $uploader;
129
-    }
130
-
131
-
132
-    /**
133
-     * Import Event Espresso data - some code "borrowed" from event espresso csv_import.php
134
-     * @return boolean success
135
-     */
136
-    public function import()
137
-    {
138
-
139
-        require_once(EE_CLASSES . 'EE_CSV.class.php');
140
-        $this->EE_CSV = EE_CSV::instance();
141
-
142
-        /** @var RequestInterface $request */
143
-        $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
144
-
145
-        if ($request->requestParamIsSet('import') && $request->requestParamIsSet('csv_submitted')) {
146
-            $files = $request->filesParams();
147
-            switch ($files['file']['error'][0]) {
148
-                case UPLOAD_ERR_OK:
149
-                    $error_msg = false;
150
-                    break;
151
-                case UPLOAD_ERR_INI_SIZE:
152
-                    $error_msg = esc_html__(
153
-                        "'The uploaded file exceeds the upload_max_filesize directive in php.ini.'",
154
-                        "event_espresso"
155
-                    );
156
-                    break;
157
-                case UPLOAD_ERR_FORM_SIZE:
158
-                    $error_msg = esc_html__(
159
-                        'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
160
-                        "event_espresso"
161
-                    );
162
-                    break;
163
-                case UPLOAD_ERR_PARTIAL:
164
-                    $error_msg = esc_html__('The uploaded file was only partially uploaded.', "event_espresso");
165
-                    break;
166
-                case UPLOAD_ERR_NO_FILE:
167
-                    $error_msg = esc_html__('No file was uploaded.', "event_espresso");
168
-                    break;
169
-                case UPLOAD_ERR_NO_TMP_DIR:
170
-                    $error_msg = esc_html__('Missing a temporary folder.', "event_espresso");
171
-                    break;
172
-                case UPLOAD_ERR_CANT_WRITE:
173
-                    $error_msg = esc_html__('Failed to write file to disk.', "event_espresso");
174
-                    break;
175
-                case UPLOAD_ERR_EXTENSION:
176
-                    $error_msg = esc_html__('File upload stopped by extension.', "event_espresso");
177
-                    break;
178
-                default:
179
-                    $error_msg = esc_html__(
180
-                        'An unknown error occurred and the file could not be uploaded',
181
-                        "event_espresso"
182
-                    );
183
-                    break;
184
-            }
185
-
186
-            if (! $error_msg) {
187
-                $filename = $files['file']['name'][0];
188
-                $file_ext = substr(strrchr($filename, '.'), 1);
189
-                $file_type = $files['file']['type'][0];
190
-                $temp_file = $files['file']['tmp_name'][0];
191
-                $filesize = $files['file']['size'][0] / 1024;// convert from bytes to KB
192
-
193
-                if ($file_ext == 'csv') {
194
-                    $max_upload = $this->EE_CSV->get_max_upload_size();// max upload size in KB
195
-                    if ($filesize < $max_upload || true) {
196
-                        $wp_upload_dir = str_replace(array('\\', '/'), '/', wp_upload_dir());
197
-                        $path_to_file = $wp_upload_dir['basedir'] . '/espresso/' . $filename;
198
-
199
-                        if (move_uploaded_file($temp_file, $path_to_file)) {
200
-                            // convert csv to array
201
-                            $this->csv_array = $this->EE_CSV->import_csv_to_model_data_array($path_to_file);
202
-
203
-                            $action = $request->getRequestParam('action');
204
-
205
-                            // was data successfully stored in an array?
206
-                            if (is_array($this->csv_array)) {
207
-                                $import_what = str_replace('csv_import_', '', $action);
208
-                                $import_what = str_replace('_', ' ', ucwords($import_what));
209
-                                $processed_data = $this->csv_array;
210
-                                $this->columns_to_save = false;
211
-
212
-                                // if any imports require funky processing, we'll catch them in the switch
213
-                                switch ($action) {
214
-                                    case "import_events":
215
-                                    case "event_list":
216
-                                        $import_what = 'Event Details';
217
-                                        break;
218
-
219
-                                    case 'groupon_import_csv':
220
-                                        $import_what = 'Groupon Codes';
221
-                                        $processed_data = $this->process_groupon_codes();
222
-                                        break;
223
-                                }
224
-                                // save processed codes to db
225
-                                if ($this->save_csv_data_array_to_db($processed_data, $this->columns_to_save)) {
226
-                                    return true;
227
-                                }
228
-                            } else {
229
-                                // no array? must be an error
230
-                                EE_Error::add_error(
231
-                                    sprintf(esc_html__("No file seems to have been uploaded", "event_espresso")),
232
-                                    __FILE__,
233
-                                    __FUNCTION__,
234
-                                    __LINE__
235
-                                );
236
-                                return false;
237
-                            }
238
-                        } else {
239
-                            EE_Error::add_error(
240
-                                sprintf(esc_html__("%s was not successfully uploaded", "event_espresso"), $filename),
241
-                                __FILE__,
242
-                                __FUNCTION__,
243
-                                __LINE__
244
-                            );
245
-                            return false;
246
-                        }
247
-                    } else {
248
-                        EE_Error::add_error(
249
-                            sprintf(
250
-                                esc_html__(
251
-                                    "%s was too large of a file and could not be uploaded. The max filesize is %s' KB.",
252
-                                    "event_espresso"
253
-                                ),
254
-                                $filename,
255
-                                $max_upload
256
-                            ),
257
-                            __FILE__,
258
-                            __FUNCTION__,
259
-                            __LINE__
260
-                        );
261
-                        return false;
262
-                    }
263
-                } else {
264
-                    EE_Error::add_error(
265
-                        sprintf(esc_html__("%s  had an invalid file extension, not uploaded", "event_espresso"), $filename),
266
-                        __FILE__,
267
-                        __FUNCTION__,
268
-                        __LINE__
269
-                    );
270
-                    return false;
271
-                }
272
-            } else {
273
-                EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
274
-                return false;
275
-            }
276
-        }
277
-        return false;
278
-    }
279
-
280
-
281
-    /**
282
-     *    Given an array of data (usually from a CSV import) attempts to save that data to the db.
283
-     *    If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names,
284
-     *    next level being numeric indexes adn each value representing a model object, and the last layer down
285
-     *    being keys of model fields and their proposed values.
286
-     *    If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned.
287
-     *    If the CSV data says (in the metadata row) that it's from the SAME database,
288
-     *    we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those
289
-     *    IDs DON'T exist in the database, they're treated as temporary IDs,
290
-     *    which can used elsewhere to refer to the same object. Once an item
291
-     *    with a temporary ID gets inserted, we record its mapping from temporary
292
-     *    ID to real ID, and use the real ID in place of the temporary ID
293
-     *    when that temporary ID was used as a foreign key.
294
-     *    If the CSV data says (in the metadata again) that it's from a DIFFERENT database,
295
-     *    we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with
296
-     *    ID 1, and the database already has an event with ID 1, we assume that's just a coincidence,
297
-     *    and insert a new event, and map it's temporary ID of 1 over to its new real ID.
298
-     *    An important exception are non-auto-increment primary keys. If one entry in the
299
-     *    CSV file has the same ID as one in the DB, we assume they are meant to be
300
-     *    the same item, and instead update the item in the DB with that same ID.
301
-     *    Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th
302
-     *    time you import a CSV from a different site, we remember their mappings, and
303
-     *    will try to update the item in the DB instead of inserting another item (eg
304
-     *    if we previously imported an event with temporary ID 1, and then it got a
305
-     *    real ID of 123, we remember that. So the next time we import an event with
306
-     *    temporary ID, from the same site, we know that it's real ID is 123, and will
307
-     *    update that event, instead of adding a new event).
308
-     *
309
-     * @param array $csv_data_array - the array containing the csv data produced from
310
-     *                              EE_CSV::import_csv_to_model_data_array()
311
-     * @param array $fields_to_save - an array containing the csv column names as keys with the corresponding db table
312
-     *                              fields they will be saved to
313
-     * @return TRUE on success, FALSE on fail
314
-     * @throws EE_Error
315
-     */
316
-    public function save_csv_data_array_to_db($csv_data_array, $model_name = false)
317
-    {
318
-        $success = false;
319
-        $error = false;
320
-        // whther to treat this import as if it's data froma different database or not
321
-        // ie, if it IS from a different database, ignore foreign keys whihf
322
-        $export_from_site_a_to_b = true;
323
-        // first level of array is not table information but a table name was passed to the function
324
-        // array is only two levels deep, so let's fix that by adding a level, else the next steps will fail
325
-        if ($model_name) {
326
-            $csv_data_array = array($csv_data_array);
327
-        }
328
-        // begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
329
-        $old_site_url = 'none-specified';
330
-        // hanlde metadata
331
-        if (isset($csv_data_array[ EE_CSV::metadata_header ])) {
332
-            $csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]);
333
-            // ok so its metadata, dont try to save it to ehte db obviously...
334
-            if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) {
335
-                EE_Error::add_attention(
336
-                    sprintf(
337
-                        esc_html__(
338
-                            "CSV Data appears to be from the same database, so attempting to update data",
339
-                            "event_espresso"
340
-                        )
341
-                    )
342
-                );
343
-                $export_from_site_a_to_b = false;
344
-            } else {
345
-                $old_site_url = isset($csv_metadata['site_url']) ? $csv_metadata['site_url'] : $old_site_url;
346
-                EE_Error::add_attention(
347
-                    sprintf(
348
-                        esc_html__(
349
-                            "CSV Data appears to be from a different database (%s instead of %s), so we assume IDs in the CSV data DO NOT correspond to IDs in this database",
350
-                            "event_espresso"
351
-                        ),
352
-                        $old_site_url,
353
-                        site_url()
354
-                    )
355
-                );
356
-            };
357
-            unset($csv_data_array[ EE_CSV::metadata_header ]);
358
-        }
359
-        /**
360
-         * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
361
-         * the value will be the newly-inserted ID.
362
-         * If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
363
-         */
364
-        $old_db_to_new_db_mapping = get_option('ee_id_mapping_from' . sanitize_title($old_site_url), array());
365
-        if ($old_db_to_new_db_mapping) {
366
-            EE_Error::add_attention(
367
-                sprintf(
368
-                    esc_html__(
369
-                        "We noticed you have imported data via CSV from %s before. Because of this, IDs in your CSV have been mapped to their new IDs in %s",
370
-                        "event_espresso"
371
-                    ),
372
-                    $old_site_url,
373
-                    site_url()
374
-                )
375
-            );
376
-        }
377
-        $old_db_to_new_db_mapping = $this->save_data_rows_to_db(
378
-            $csv_data_array,
379
-            $export_from_site_a_to_b,
380
-            $old_db_to_new_db_mapping
381
-        );
382
-
383
-        // save the mapping from old db to new db in case they try re-importing the same data from the same website again
384
-        update_option('ee_id_mapping_from' . sanitize_title($old_site_url), $old_db_to_new_db_mapping);
385
-
386
-        if ($this->_total_updates > 0) {
387
-            EE_Error::add_success(
388
-                sprintf(
389
-                    esc_html__("%s existing records in the database were updated.", "event_espresso"),
390
-                    $this->_total_updates
391
-                )
392
-            );
393
-            $success = true;
394
-        }
395
-        if ($this->_total_inserts > 0) {
396
-            EE_Error::add_success(
397
-                sprintf(esc_html__("%s new records were added to the database.", "event_espresso"), $this->_total_inserts)
398
-            );
399
-            $success = true;
400
-        }
401
-
402
-        if ($this->_total_update_errors > 0) {
403
-            EE_Error::add_error(
404
-                sprintf(
405
-                    esc_html__(
406
-                        "'One or more errors occurred, and a total of %s existing records in the database were <strong>not</strong> updated.'",
407
-                        "event_espresso"
408
-                    ),
409
-                    $this->_total_update_errors
410
-                ),
411
-                __FILE__,
412
-                __FUNCTION__,
413
-                __LINE__
414
-            );
415
-            $error = true;
416
-        }
417
-        if ($this->_total_insert_errors > 0) {
418
-            EE_Error::add_error(
419
-                sprintf(
420
-                    esc_html__(
421
-                        "One or more errors occurred, and a total of %s new records were <strong>not</strong> added to the database.'",
422
-                        "event_espresso"
423
-                    ),
424
-                    $this->_total_insert_errors
425
-                ),
426
-                __FILE__,
427
-                __FUNCTION__,
428
-                __LINE__
429
-            );
430
-            $error = true;
431
-        }
432
-
433
-        // lastly, we need to update the datetime and ticket sold amounts
434
-        // as those may have been affected by this
435
-        EEM_Ticket::instance()->update_tickets_sold(EEM_Ticket::instance()->get_all());
436
-
437
-        // if there was at least one success and absolutely no errors
438
-        if ($success && ! $error) {
439
-            return true;
440
-        } else {
441
-            return false;
442
-        }
443
-    }
444
-
445
-
446
-    /**
447
-     * Processes the array of data, given the knowledge that it's from the same database or a different one,
448
-     * and the mapping from temporary IDs to real IDs.
449
-     * If the data is from a different database, we treat the primary keys and their corresponding
450
-     * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys
451
-     * in the real target database. As items are inserted, their temporary primary keys
452
-     * are mapped to the real IDs in the target database. Also, before doing any update or
453
-     * insert, we replace all the temp ID which are foreign keys with their mapped real IDs.
454
-     * An exception: string primary keys are treated as real IDs, or else we'd need to
455
-     * dynamically generate new string primary keys which would be very awkard for the country table etc.
456
-     * Also, models with no primary key are strange too. We combine use their primar key INDEX (a
457
-     * combination of fields) to create a unique string identifying the row and store
458
-     * those in the mapping.
459
-     *
460
-     * If the data is from the same database, we usually treat primary keys as real IDs.
461
-     * An exception is if there is nothing in the database for that ID. If that's the case,
462
-     * we need to insert a new row for that ID, and then map from the non-existent ID
463
-     * to the newly-inserted real ID.
464
-     *
465
-     * @param mixed $csv_data_array
466
-     * @param mixed $export_from_site_a_to_b
467
-     * @param mixed $old_db_to_new_db_mapping
468
-     * @return array updated $old_db_to_new_db_mapping
469
-     */
470
-    public function save_data_rows_to_db($csv_data_array, $export_from_site_a_to_b, $old_db_to_new_db_mapping)
471
-    {
472
-        foreach ($csv_data_array as $model_name_in_csv_data => $model_data_from_import) {
473
-            // now check that assumption was correct. If
474
-            if (EE_Registry::instance()->is_model_name($model_name_in_csv_data)) {
475
-                $model_name = $model_name_in_csv_data;
476
-            } else {
477
-                // no table info in the array and no table name passed to the function?? FAIL
478
-                EE_Error::add_error(
479
-                    esc_html__(
480
-                        'No table information was specified and/or found, therefore the import could not be completed',
481
-                        'event_espresso'
482
-                    ),
483
-                    __FILE__,
484
-                    __FUNCTION__,
485
-                    __LINE__
486
-                );
487
-                return false;
488
-            }
489
-            /* @var $model EEM_Base */
490
-            $model = EE_Registry::instance()->load_model($model_name);
491
-
492
-            // so without further ado, scanning all the data provided for primary keys and their inital values
493
-            foreach ($model_data_from_import as $model_object_data) {
494
-                // before we do ANYTHING, make sure the csv row wasn't just completely blank
495
-                $row_is_completely_empty = true;
496
-                foreach ($model_object_data as $field) {
497
-                    if ($field) {
498
-                        $row_is_completely_empty = false;
499
-                    }
500
-                }
501
-                if ($row_is_completely_empty) {
502
-                    continue;
503
-                }
504
-                // find the PK in the row of data (or a combined key if
505
-                // there is no primary key)
506
-                if ($model->has_primary_key_field()) {
507
-                    $id_in_csv = $model_object_data[ $model->primary_key_name() ];
508
-                } else {
509
-                    $id_in_csv = $model->get_index_primary_key_string($model_object_data);
510
-                }
511
-
512
-
513
-                $model_object_data = $this->_replace_temp_ids_with_mappings(
514
-                    $model_object_data,
515
-                    $model,
516
-                    $old_db_to_new_db_mapping,
517
-                    $export_from_site_a_to_b
518
-                );
519
-                // now we need to decide if we're going to add a new model object given the $model_object_data,
520
-                // or just update.
521
-                if ($export_from_site_a_to_b) {
522
-                    $what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_other_db(
523
-                        $id_in_csv,
524
-                        $model_object_data,
525
-                        $model,
526
-                        $old_db_to_new_db_mapping
527
-                    );
528
-                } else {// this is just a re-import
529
-                    $what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_same_db(
530
-                        $id_in_csv,
531
-                        $model_object_data,
532
-                        $model,
533
-                        $old_db_to_new_db_mapping
534
-                    );
535
-                }
536
-                if ($what_to_do == self::do_nothing) {
537
-                    continue;
538
-                }
539
-
540
-                // double-check we actually want to insert, if that's what we're planning
541
-                // based on whether this item would be unique in the DB or not
542
-                if ($what_to_do == self::do_insert) {
543
-                    // we're supposed to be inserting. But wait, will this thing
544
-                    // be acceptable if inserted?
545
-                    $conflicting = $model->get_one_conflicting($model_object_data, false);
546
-                    if ($conflicting) {
547
-                        // ok, this item would conflict if inserted. Just update the item that it conflicts with.
548
-                        $what_to_do = self::do_update;
549
-                        // and if this model has a primary key, remember its mapping
550
-                        if ($model->has_primary_key_field()) {
551
-                            $old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID();
552
-                            $model_object_data[ $model->primary_key_name() ] = $conflicting->ID();
553
-                        } else {
554
-                            // we want to update this conflicting item, instead of inserting a conflicting item
555
-                            // so we need to make sure they match entirely (its possible that they only conflicted on one field, but we need them to match on other fields
556
-                            // for the WHERE conditions in the update). At the time of this comment, there were no models like this
557
-                            foreach ($model->get_combined_primary_key_fields() as $key_field) {
558
-                                $model_object_data[ $key_field->get_name() ] = $conflicting->get(
559
-                                    $key_field->get_name()
560
-                                );
561
-                            }
562
-                        }
563
-                    }
564
-                }
565
-                if ($what_to_do == self::do_insert) {
566
-                    $old_db_to_new_db_mapping = $this->_insert_from_data_array(
567
-                        $id_in_csv,
568
-                        $model_object_data,
569
-                        $model,
570
-                        $old_db_to_new_db_mapping
571
-                    );
572
-                } elseif ($what_to_do == self::do_update) {
573
-                    $old_db_to_new_db_mapping = $this->_update_from_data_array(
574
-                        $id_in_csv,
575
-                        $model_object_data,
576
-                        $model,
577
-                        $old_db_to_new_db_mapping
578
-                    );
579
-                } else {
580
-                    throw new EE_Error(
581
-                        sprintf(
582
-                            esc_html__(
583
-                                'Programming error. We shoudl be inserting or updating, but instead we are being told to "%s", whifh is invalid',
584
-                                'event_espresso'
585
-                            ),
586
-                            $what_to_do
587
-                        )
588
-                    );
589
-                }
590
-            }
591
-        }
592
-        return $old_db_to_new_db_mapping;
593
-    }
594
-
595
-
596
-    /**
597
-     * Decides whether or not to insert, given that this data is from another database.
598
-     * So, if the primary key of this $model_object_data already exists in the database,
599
-     * it's just a coincidence and we should still insert. The only time we should
600
-     * update is when we know what it maps to, or there's something that would
601
-     * conflict (and we should instead just update that conflicting thing)
602
-     *
603
-     * @param string   $id_in_csv
604
-     * @param array    $model_object_data        by reference so it can be modified
605
-     * @param EEM_Base $model
606
-     * @param array    $old_db_to_new_db_mapping by reference so it can be modified
607
-     * @return string one of the consts on this class that starts with do_*
608
-     */
609
-    protected function _decide_whether_to_insert_or_update_given_data_from_other_db(
610
-        $id_in_csv,
611
-        $model_object_data,
612
-        $model,
613
-        $old_db_to_new_db_mapping
614
-    ) {
615
-        $model_name = $model->get_this_model_name();
616
-        // if it's a site-to-site export-and-import, see if this modelobject's id
617
-        // in the old data that we know of
618
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) {
619
-            return self::do_update;
620
-        } else {
621
-            return self::do_insert;
622
-        }
623
-    }
624
-
625
-    /**
626
-     * If this thing basically already exists in the database, we want to update it;
627
-     * otherwise insert it (ie, someone tweaked the CSV file, or the item was
628
-     * deleted in the database so it should be re-inserted)
629
-     *
630
-     * @param mixed     $id_in_csv
631
-     * @param mixed     $model_object_data
632
-     * @param EEM_Base $model
633
-     * @param mixed     $old_db_to_new_db_mapping
634
-     * @return
635
-     */
636
-    protected function _decide_whether_to_insert_or_update_given_data_from_same_db(
637
-        $id_in_csv,
638
-        $model_object_data,
639
-        $model
640
-    ) {
641
-        // in this case, check if this thing ACTUALLY exists in the database
642
-        if ($model->get_one_conflicting($model_object_data)) {
643
-            return self::do_update;
644
-        } else {
645
-            return self::do_insert;
646
-        }
647
-    }
648
-
649
-    /**
650
-     * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs
651
-     * with their mapped real IDs. Eg, if importing from site A to B, the mapping
652
-     * file may indicate that the ID "my_event_id" maps to an actual event ID of 123.
653
-     * So this function searches for any event temp Ids called "my_event_id" and
654
-     * replaces them with 123.
655
-     * Also, if there is no temp ID for the INT foreign keys from another database,
656
-     * replaces them with 0 or the field's default.
657
-     *
658
-     * @param mixed     $model_object_data
659
-     * @param EEM_Base $model
660
-     * @param mixed     $old_db_to_new_db_mapping
661
-     * @param boolean  $export_from_site_a_to_b
662
-     * @return array updated model object data with temp IDs removed
663
-     */
664
-    protected function _replace_temp_ids_with_mappings(
665
-        $model_object_data,
666
-        $model,
667
-        $old_db_to_new_db_mapping,
668
-        $export_from_site_a_to_b
669
-    ) {
670
-        // if this model object's primary key is in the mapping, replace it
671
-        if (
672
-            $model->has_primary_key_field() &&
673
-            $model->get_primary_key_field()->is_auto_increment() &&
674
-            isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) &&
675
-            isset(
676
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ]
677
-            )
678
-        ) {
679
-            $model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name(
680
-            ) ][ $model_object_data[ $model->primary_key_name() ] ];
681
-        }
682
-
683
-        try {
684
-            $model_name_field = $model->get_field_containing_related_model_name();
685
-            $models_pointed_to_by_model_name_field = $model_name_field->get_model_names_pointed_to();
686
-        } catch (EE_Error $e) {
687
-            $model_name_field = null;
688
-            $models_pointed_to_by_model_name_field = array();
689
-        }
690
-        foreach ($model->field_settings(true) as $field_obj) {
691
-            if ($field_obj instanceof EE_Foreign_Key_Int_Field) {
692
-                $models_pointed_to = $field_obj->get_model_names_pointed_to();
693
-                $found_a_mapping = false;
694
-                foreach ($models_pointed_to as $model_pointed_to_by_fk) {
695
-                    if ($model_name_field) {
696
-                        $value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
697
-                        if ($value_of_model_name_field == $model_pointed_to_by_fk) {
698
-                            $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
699
-                                $model_object_data[ $field_obj->get_name() ],
700
-                                $model_pointed_to_by_fk,
701
-                                $old_db_to_new_db_mapping,
702
-                                $export_from_site_a_to_b
703
-                            );
704
-                            $found_a_mapping = true;
705
-                            break;
706
-                        }
707
-                    } else {
708
-                        $model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
709
-                            $model_object_data[ $field_obj->get_name() ],
710
-                            $model_pointed_to_by_fk,
711
-                            $old_db_to_new_db_mapping,
712
-                            $export_from_site_a_to_b
713
-                        );
714
-                        $found_a_mapping = true;
715
-                    }
716
-                    // once we've found a mapping for this field no need to continue
717
-                    if ($found_a_mapping) {
718
-                        break;
719
-                    }
720
-                }
721
-            } else {
722
-                // it's a string foreign key (which we leave alone, because those are things
723
-                // like country names, which we'd really rather not make 2 USAs etc (we'd actually
724
-                // prefer to just update one)
725
-                // or it's just a regular value that ought to be replaced
726
-            }
727
-        }
728
-        //
729
-        if ($model instanceof EEM_Term_Taxonomy) {
730
-            $model_object_data = $this->_handle_split_term_ids($model_object_data);
731
-        }
732
-        return $model_object_data;
733
-    }
734
-
735
-    /**
736
-     * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id
737
-     * this term-taxonomy refers to may be out-of-date so we need to update it.
738
-     * @see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/
739
-     *
740
-     * @param mixed $model_object_data
741
-     * @return array new model object data
742
-     */
743
-    protected function _handle_split_term_ids($model_object_data)
744
-    {
745
-        if (
746
-            isset($model_object_data['term_id'])
747
-            && isset($model_object_data['taxonomy'])
748
-            && apply_filters(
749
-                'FHEE__EE_Import__handle_split_term_ids__function_exists',
750
-                function_exists('wp_get_split_term'),
751
-                $model_object_data
752
-            )
753
-        ) {
754
-            $new_term_id = wp_get_split_term($model_object_data['term_id'], $model_object_data['taxonomy']);
755
-            if ($new_term_id) {
756
-                $model_object_data['term_id'] = $new_term_id;
757
-            }
758
-        }
759
-        return $model_object_data;
760
-    }
761
-
762
-    /**
763
-     * Given the object's ID and its model's name, find it int he mapping data,
764
-     * bearing in mind where it came from
765
-     *
766
-     * @param mixed   $object_id
767
-     * @param string $model_name
768
-     * @param array  $old_db_to_new_db_mapping
769
-     * @param mixed   $export_from_site_a_to_b
770
-     * @return int
771
-     */
772
-    protected function _find_mapping_in($object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b)
773
-    {
774
-        if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) {
775
-            return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
776
-        } elseif ($object_id == '0' || $object_id == '') {
777
-            // leave as-is
778
-            return $object_id;
779
-        } elseif ($export_from_site_a_to_b) {
780
-            // we couldn't find a mapping for this, and it's from a different site,
781
-            // so blank it out
782
-            return null;
783
-        } elseif (! $export_from_site_a_to_b) {
784
-            // we coudln't find a mapping for this, but it's from thsi DB anyway
785
-            // so let's just leave it as-is
786
-            return $object_id;
787
-        }
788
-    }
789
-
790
-    /**
791
-     *
792
-     * @param mixed     $id_in_csv
793
-     * @param mixed     $model_object_data
794
-     * @param EEM_Base $model
795
-     * @param mixed     $old_db_to_new_db_mapping
796
-     * @return array updated $old_db_to_new_db_mapping
797
-     */
798
-    protected function _insert_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
799
-    {
800
-        // remove the primary key, if there is one (we don't want it for inserts OR updates)
801
-        // we'll put it back in if we need it
802
-        if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) {
803
-            $effective_id = $model_object_data[ $model->primary_key_name() ];
804
-            unset($model_object_data[ $model->primary_key_name() ]);
805
-        } else {
806
-            $effective_id = $model->get_index_primary_key_string($model_object_data);
807
-        }
808
-        // the model takes care of validating the CSV's input
809
-        try {
810
-            $new_id = $model->insert($model_object_data);
811
-            if ($new_id) {
812
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id;
813
-                $this->_total_inserts++;
814
-                EE_Error::add_success(
815
-                    sprintf(
816
-                        esc_html__("Successfully added new %s (with id %s) with csv data %s", "event_espresso"),
817
-                        $model->get_this_model_name(),
818
-                        $new_id,
819
-                        implode(",", $model_object_data)
820
-                    )
821
-                );
822
-            } else {
823
-                $this->_total_insert_errors++;
824
-                // put the ID used back in there for the error message
825
-                if ($model->has_primary_key_field()) {
826
-                    $model_object_data[ $model->primary_key_name() ] = $effective_id;
827
-                }
828
-                EE_Error::add_error(
829
-                    sprintf(
830
-                        esc_html__("Could not insert new %s with the csv data: %s", "event_espresso"),
831
-                        $model->get_this_model_name(),
832
-                        http_build_query($model_object_data)
833
-                    ),
834
-                    __FILE__,
835
-                    __FUNCTION__,
836
-                    __LINE__
837
-                );
838
-            }
839
-        } catch (EE_Error $e) {
840
-            $this->_total_insert_errors++;
841
-            if ($model->has_primary_key_field()) {
842
-                $model_object_data[ $model->primary_key_name() ] = $effective_id;
843
-            }
844
-            EE_Error::add_error(
845
-                sprintf(
846
-                    esc_html__("Could not insert new %s with the csv data: %s because %s", "event_espresso"),
847
-                    $model->get_this_model_name(),
848
-                    implode(",", $model_object_data),
849
-                    $e->getMessage()
850
-                ),
851
-                __FILE__,
852
-                __FUNCTION__,
853
-                __LINE__
854
-            );
855
-        }
856
-        return $old_db_to_new_db_mapping;
857
-    }
858
-
859
-    /**
860
-     * Given the model object data, finds the row to update and updates it
861
-     *
862
-     * @param string|int $id_in_csv
863
-     * @param array      $model_object_data
864
-     * @param EEM_Base   $model
865
-     * @param array      $old_db_to_new_db_mapping
866
-     * @return array updated $old_db_to_new_db_mapping
867
-     */
868
-    protected function _update_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
869
-    {
870
-        try {
871
-            // let's keep two copies of the model object data:
872
-            // one for performing an update, one for everthing else
873
-            $model_object_data_for_update = $model_object_data;
874
-            if ($model->has_primary_key_field()) {
875
-                $conditions = array($model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]);
876
-                // remove the primary key because we shouldn't use it for updating
877
-                unset($model_object_data_for_update[ $model->primary_key_name() ]);
878
-            } elseif ($model->get_combined_primary_key_fields() > 1) {
879
-                $conditions = array();
880
-                foreach ($model->get_combined_primary_key_fields() as $key_field) {
881
-                    $conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ];
882
-                }
883
-            } else {
884
-                $model->primary_key_name(
885
-                );// this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
886
-            }
887
-
888
-            $success = $model->update($model_object_data_for_update, array($conditions));
889
-            if ($success) {
890
-                $this->_total_updates++;
891
-                EE_Error::add_success(
892
-                    sprintf(
893
-                        esc_html__("Successfully updated %s with csv data %s", "event_espresso"),
894
-                        $model->get_this_model_name(),
895
-                        implode(",", $model_object_data_for_update)
896
-                    )
897
-                );
898
-                // we should still record the mapping even though it was an update
899
-                // because if we were going to insert somethign but it was going to conflict
900
-                // we would have last-minute decided to update. So we'd like to know what we updated
901
-                // and so we record what record ended up being updated using the mapping
902
-                if ($model->has_primary_key_field()) {
903
-                    $new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
904
-                } else {
905
-                    // no primary key just a combined key
906
-                    $new_key_for_mapping = $model->get_index_primary_key_string($model_object_data);
907
-                }
908
-                $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
909
-            } else {
910
-                $matched_items = $model->get_all(array($conditions));
911
-                if (! $matched_items) {
912
-                    // no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
913
-                    $this->_total_update_errors++;
914
-                    EE_Error::add_error(
915
-                        sprintf(
916
-                            esc_html__(
917
-                                "Could not update %s with the csv data: '%s' for an unknown reason (using WHERE conditions %s)",
918
-                                "event_espresso"
919
-                            ),
920
-                            $model->get_this_model_name(),
921
-                            http_build_query($model_object_data),
922
-                            http_build_query($conditions)
923
-                        ),
924
-                        __FILE__,
925
-                        __FUNCTION__,
926
-                        __LINE__
927
-                    );
928
-                } else {
929
-                    $this->_total_updates++;
930
-                    EE_Error::add_success(
931
-                        sprintf(
932
-                            esc_html__(
933
-                                "%s with csv data '%s' was found in the database and didn't need updating because all the data is identical.",
934
-                                "event_espresso"
935
-                            ),
936
-                            $model->get_this_model_name(),
937
-                            implode(",", $model_object_data)
938
-                        )
939
-                    );
940
-                }
941
-            }
942
-        } catch (EE_Error $e) {
943
-            $this->_total_update_errors++;
944
-            $basic_message = sprintf(
945
-                esc_html__("Could not update %s with the csv data: %s because %s", "event_espresso"),
946
-                $model->get_this_model_name(),
947
-                implode(",", $model_object_data),
948
-                $e->getMessage()
949
-            );
950
-            $debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
951
-            EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__);
952
-        }
953
-        return $old_db_to_new_db_mapping;
954
-    }
955
-
956
-    /**
957
-     * Gets the number of inserts performed since importer was instantiated or reset
958
-     *
959
-     * @return int
960
-     */
961
-    public function get_total_inserts()
962
-    {
963
-        return $this->_total_inserts;
964
-    }
965
-
966
-    /**
967
-     *  Gets the number of insert errors since importer was instantiated or reset
968
-     *
969
-     * @return int
970
-     */
971
-    public function get_total_insert_errors()
972
-    {
973
-        return $this->_total_insert_errors;
974
-    }
975
-
976
-    /**
977
-     *  Gets the number of updates performed since importer was instantiated or reset
978
-     *
979
-     * @return int
980
-     */
981
-    public function get_total_updates()
982
-    {
983
-        return $this->_total_updates;
984
-    }
985
-
986
-    /**
987
-     *  Gets the number of update errors since importer was instantiated or reset
988
-     *
989
-     * @return int
990
-     */
991
-    public function get_total_update_errors()
992
-    {
993
-        return $this->_total_update_errors;
994
-    }
127
+		$uploader = ob_get_clean();
128
+		return $uploader;
129
+	}
130
+
131
+
132
+	/**
133
+	 * Import Event Espresso data - some code "borrowed" from event espresso csv_import.php
134
+	 * @return boolean success
135
+	 */
136
+	public function import()
137
+	{
138
+
139
+		require_once(EE_CLASSES . 'EE_CSV.class.php');
140
+		$this->EE_CSV = EE_CSV::instance();
141
+
142
+		/** @var RequestInterface $request */
143
+		$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
144
+
145
+		if ($request->requestParamIsSet('import') && $request->requestParamIsSet('csv_submitted')) {
146
+			$files = $request->filesParams();
147
+			switch ($files['file']['error'][0]) {
148
+				case UPLOAD_ERR_OK:
149
+					$error_msg = false;
150
+					break;
151
+				case UPLOAD_ERR_INI_SIZE:
152
+					$error_msg = esc_html__(
153
+						"'The uploaded file exceeds the upload_max_filesize directive in php.ini.'",
154
+						"event_espresso"
155
+					);
156
+					break;
157
+				case UPLOAD_ERR_FORM_SIZE:
158
+					$error_msg = esc_html__(
159
+						'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
160
+						"event_espresso"
161
+					);
162
+					break;
163
+				case UPLOAD_ERR_PARTIAL:
164
+					$error_msg = esc_html__('The uploaded file was only partially uploaded.', "event_espresso");
165
+					break;
166
+				case UPLOAD_ERR_NO_FILE:
167
+					$error_msg = esc_html__('No file was uploaded.', "event_espresso");
168
+					break;
169
+				case UPLOAD_ERR_NO_TMP_DIR:
170
+					$error_msg = esc_html__('Missing a temporary folder.', "event_espresso");
171
+					break;
172
+				case UPLOAD_ERR_CANT_WRITE:
173
+					$error_msg = esc_html__('Failed to write file to disk.', "event_espresso");
174
+					break;
175
+				case UPLOAD_ERR_EXTENSION:
176
+					$error_msg = esc_html__('File upload stopped by extension.', "event_espresso");
177
+					break;
178
+				default:
179
+					$error_msg = esc_html__(
180
+						'An unknown error occurred and the file could not be uploaded',
181
+						"event_espresso"
182
+					);
183
+					break;
184
+			}
185
+
186
+			if (! $error_msg) {
187
+				$filename = $files['file']['name'][0];
188
+				$file_ext = substr(strrchr($filename, '.'), 1);
189
+				$file_type = $files['file']['type'][0];
190
+				$temp_file = $files['file']['tmp_name'][0];
191
+				$filesize = $files['file']['size'][0] / 1024;// convert from bytes to KB
192
+
193
+				if ($file_ext == 'csv') {
194
+					$max_upload = $this->EE_CSV->get_max_upload_size();// max upload size in KB
195
+					if ($filesize < $max_upload || true) {
196
+						$wp_upload_dir = str_replace(array('\\', '/'), '/', wp_upload_dir());
197
+						$path_to_file = $wp_upload_dir['basedir'] . '/espresso/' . $filename;
198
+
199
+						if (move_uploaded_file($temp_file, $path_to_file)) {
200
+							// convert csv to array
201
+							$this->csv_array = $this->EE_CSV->import_csv_to_model_data_array($path_to_file);
202
+
203
+							$action = $request->getRequestParam('action');
204
+
205
+							// was data successfully stored in an array?
206
+							if (is_array($this->csv_array)) {
207
+								$import_what = str_replace('csv_import_', '', $action);
208
+								$import_what = str_replace('_', ' ', ucwords($import_what));
209
+								$processed_data = $this->csv_array;
210
+								$this->columns_to_save = false;
211
+
212
+								// if any imports require funky processing, we'll catch them in the switch
213
+								switch ($action) {
214
+									case "import_events":
215
+									case "event_list":
216
+										$import_what = 'Event Details';
217
+										break;
218
+
219
+									case 'groupon_import_csv':
220
+										$import_what = 'Groupon Codes';
221
+										$processed_data = $this->process_groupon_codes();
222
+										break;
223
+								}
224
+								// save processed codes to db
225
+								if ($this->save_csv_data_array_to_db($processed_data, $this->columns_to_save)) {
226
+									return true;
227
+								}
228
+							} else {
229
+								// no array? must be an error
230
+								EE_Error::add_error(
231
+									sprintf(esc_html__("No file seems to have been uploaded", "event_espresso")),
232
+									__FILE__,
233
+									__FUNCTION__,
234
+									__LINE__
235
+								);
236
+								return false;
237
+							}
238
+						} else {
239
+							EE_Error::add_error(
240
+								sprintf(esc_html__("%s was not successfully uploaded", "event_espresso"), $filename),
241
+								__FILE__,
242
+								__FUNCTION__,
243
+								__LINE__
244
+							);
245
+							return false;
246
+						}
247
+					} else {
248
+						EE_Error::add_error(
249
+							sprintf(
250
+								esc_html__(
251
+									"%s was too large of a file and could not be uploaded. The max filesize is %s' KB.",
252
+									"event_espresso"
253
+								),
254
+								$filename,
255
+								$max_upload
256
+							),
257
+							__FILE__,
258
+							__FUNCTION__,
259
+							__LINE__
260
+						);
261
+						return false;
262
+					}
263
+				} else {
264
+					EE_Error::add_error(
265
+						sprintf(esc_html__("%s  had an invalid file extension, not uploaded", "event_espresso"), $filename),
266
+						__FILE__,
267
+						__FUNCTION__,
268
+						__LINE__
269
+					);
270
+					return false;
271
+				}
272
+			} else {
273
+				EE_Error::add_error($error_msg, __FILE__, __FUNCTION__, __LINE__);
274
+				return false;
275
+			}
276
+		}
277
+		return false;
278
+	}
279
+
280
+
281
+	/**
282
+	 *    Given an array of data (usually from a CSV import) attempts to save that data to the db.
283
+	 *    If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names,
284
+	 *    next level being numeric indexes adn each value representing a model object, and the last layer down
285
+	 *    being keys of model fields and their proposed values.
286
+	 *    If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned.
287
+	 *    If the CSV data says (in the metadata row) that it's from the SAME database,
288
+	 *    we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those
289
+	 *    IDs DON'T exist in the database, they're treated as temporary IDs,
290
+	 *    which can used elsewhere to refer to the same object. Once an item
291
+	 *    with a temporary ID gets inserted, we record its mapping from temporary
292
+	 *    ID to real ID, and use the real ID in place of the temporary ID
293
+	 *    when that temporary ID was used as a foreign key.
294
+	 *    If the CSV data says (in the metadata again) that it's from a DIFFERENT database,
295
+	 *    we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with
296
+	 *    ID 1, and the database already has an event with ID 1, we assume that's just a coincidence,
297
+	 *    and insert a new event, and map it's temporary ID of 1 over to its new real ID.
298
+	 *    An important exception are non-auto-increment primary keys. If one entry in the
299
+	 *    CSV file has the same ID as one in the DB, we assume they are meant to be
300
+	 *    the same item, and instead update the item in the DB with that same ID.
301
+	 *    Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th
302
+	 *    time you import a CSV from a different site, we remember their mappings, and
303
+	 *    will try to update the item in the DB instead of inserting another item (eg
304
+	 *    if we previously imported an event with temporary ID 1, and then it got a
305
+	 *    real ID of 123, we remember that. So the next time we import an event with
306
+	 *    temporary ID, from the same site, we know that it's real ID is 123, and will
307
+	 *    update that event, instead of adding a new event).
308
+	 *
309
+	 * @param array $csv_data_array - the array containing the csv data produced from
310
+	 *                              EE_CSV::import_csv_to_model_data_array()
311
+	 * @param array $fields_to_save - an array containing the csv column names as keys with the corresponding db table
312
+	 *                              fields they will be saved to
313
+	 * @return TRUE on success, FALSE on fail
314
+	 * @throws EE_Error
315
+	 */
316
+	public function save_csv_data_array_to_db($csv_data_array, $model_name = false)
317
+	{
318
+		$success = false;
319
+		$error = false;
320
+		// whther to treat this import as if it's data froma different database or not
321
+		// ie, if it IS from a different database, ignore foreign keys whihf
322
+		$export_from_site_a_to_b = true;
323
+		// first level of array is not table information but a table name was passed to the function
324
+		// array is only two levels deep, so let's fix that by adding a level, else the next steps will fail
325
+		if ($model_name) {
326
+			$csv_data_array = array($csv_data_array);
327
+		}
328
+		// begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
329
+		$old_site_url = 'none-specified';
330
+		// hanlde metadata
331
+		if (isset($csv_data_array[ EE_CSV::metadata_header ])) {
332
+			$csv_metadata = array_shift($csv_data_array[ EE_CSV::metadata_header ]);
333
+			// ok so its metadata, dont try to save it to ehte db obviously...
334
+			if (isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()) {
335
+				EE_Error::add_attention(
336
+					sprintf(
337
+						esc_html__(
338
+							"CSV Data appears to be from the same database, so attempting to update data",
339
+							"event_espresso"
340
+						)
341
+					)
342
+				);
343
+				$export_from_site_a_to_b = false;
344
+			} else {
345
+				$old_site_url = isset($csv_metadata['site_url']) ? $csv_metadata['site_url'] : $old_site_url;
346
+				EE_Error::add_attention(
347
+					sprintf(
348
+						esc_html__(
349
+							"CSV Data appears to be from a different database (%s instead of %s), so we assume IDs in the CSV data DO NOT correspond to IDs in this database",
350
+							"event_espresso"
351
+						),
352
+						$old_site_url,
353
+						site_url()
354
+					)
355
+				);
356
+			};
357
+			unset($csv_data_array[ EE_CSV::metadata_header ]);
358
+		}
359
+		/**
360
+		 * @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
361
+		 * the value will be the newly-inserted ID.
362
+		 * If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
363
+		 */
364
+		$old_db_to_new_db_mapping = get_option('ee_id_mapping_from' . sanitize_title($old_site_url), array());
365
+		if ($old_db_to_new_db_mapping) {
366
+			EE_Error::add_attention(
367
+				sprintf(
368
+					esc_html__(
369
+						"We noticed you have imported data via CSV from %s before. Because of this, IDs in your CSV have been mapped to their new IDs in %s",
370
+						"event_espresso"
371
+					),
372
+					$old_site_url,
373
+					site_url()
374
+				)
375
+			);
376
+		}
377
+		$old_db_to_new_db_mapping = $this->save_data_rows_to_db(
378
+			$csv_data_array,
379
+			$export_from_site_a_to_b,
380
+			$old_db_to_new_db_mapping
381
+		);
382
+
383
+		// save the mapping from old db to new db in case they try re-importing the same data from the same website again
384
+		update_option('ee_id_mapping_from' . sanitize_title($old_site_url), $old_db_to_new_db_mapping);
385
+
386
+		if ($this->_total_updates > 0) {
387
+			EE_Error::add_success(
388
+				sprintf(
389
+					esc_html__("%s existing records in the database were updated.", "event_espresso"),
390
+					$this->_total_updates
391
+				)
392
+			);
393
+			$success = true;
394
+		}
395
+		if ($this->_total_inserts > 0) {
396
+			EE_Error::add_success(
397
+				sprintf(esc_html__("%s new records were added to the database.", "event_espresso"), $this->_total_inserts)
398
+			);
399
+			$success = true;
400
+		}
401
+
402
+		if ($this->_total_update_errors > 0) {
403
+			EE_Error::add_error(
404
+				sprintf(
405
+					esc_html__(
406
+						"'One or more errors occurred, and a total of %s existing records in the database were <strong>not</strong> updated.'",
407
+						"event_espresso"
408
+					),
409
+					$this->_total_update_errors
410
+				),
411
+				__FILE__,
412
+				__FUNCTION__,
413
+				__LINE__
414
+			);
415
+			$error = true;
416
+		}
417
+		if ($this->_total_insert_errors > 0) {
418
+			EE_Error::add_error(
419
+				sprintf(
420
+					esc_html__(
421
+						"One or more errors occurred, and a total of %s new records were <strong>not</strong> added to the database.'",
422
+						"event_espresso"
423
+					),
424
+					$this->_total_insert_errors
425
+				),
426
+				__FILE__,
427
+				__FUNCTION__,
428
+				__LINE__
429
+			);
430
+			$error = true;
431
+		}
432
+
433
+		// lastly, we need to update the datetime and ticket sold amounts
434
+		// as those may have been affected by this
435
+		EEM_Ticket::instance()->update_tickets_sold(EEM_Ticket::instance()->get_all());
436
+
437
+		// if there was at least one success and absolutely no errors
438
+		if ($success && ! $error) {
439
+			return true;
440
+		} else {
441
+			return false;
442
+		}
443
+	}
444
+
445
+
446
+	/**
447
+	 * Processes the array of data, given the knowledge that it's from the same database or a different one,
448
+	 * and the mapping from temporary IDs to real IDs.
449
+	 * If the data is from a different database, we treat the primary keys and their corresponding
450
+	 * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys
451
+	 * in the real target database. As items are inserted, their temporary primary keys
452
+	 * are mapped to the real IDs in the target database. Also, before doing any update or
453
+	 * insert, we replace all the temp ID which are foreign keys with their mapped real IDs.
454
+	 * An exception: string primary keys are treated as real IDs, or else we'd need to
455
+	 * dynamically generate new string primary keys which would be very awkard for the country table etc.
456
+	 * Also, models with no primary key are strange too. We combine use their primar key INDEX (a
457
+	 * combination of fields) to create a unique string identifying the row and store
458
+	 * those in the mapping.
459
+	 *
460
+	 * If the data is from the same database, we usually treat primary keys as real IDs.
461
+	 * An exception is if there is nothing in the database for that ID. If that's the case,
462
+	 * we need to insert a new row for that ID, and then map from the non-existent ID
463
+	 * to the newly-inserted real ID.
464
+	 *
465
+	 * @param mixed $csv_data_array
466
+	 * @param mixed $export_from_site_a_to_b
467
+	 * @param mixed $old_db_to_new_db_mapping
468
+	 * @return array updated $old_db_to_new_db_mapping
469
+	 */
470
+	public function save_data_rows_to_db($csv_data_array, $export_from_site_a_to_b, $old_db_to_new_db_mapping)
471
+	{
472
+		foreach ($csv_data_array as $model_name_in_csv_data => $model_data_from_import) {
473
+			// now check that assumption was correct. If
474
+			if (EE_Registry::instance()->is_model_name($model_name_in_csv_data)) {
475
+				$model_name = $model_name_in_csv_data;
476
+			} else {
477
+				// no table info in the array and no table name passed to the function?? FAIL
478
+				EE_Error::add_error(
479
+					esc_html__(
480
+						'No table information was specified and/or found, therefore the import could not be completed',
481
+						'event_espresso'
482
+					),
483
+					__FILE__,
484
+					__FUNCTION__,
485
+					__LINE__
486
+				);
487
+				return false;
488
+			}
489
+			/* @var $model EEM_Base */
490
+			$model = EE_Registry::instance()->load_model($model_name);
491
+
492
+			// so without further ado, scanning all the data provided for primary keys and their inital values
493
+			foreach ($model_data_from_import as $model_object_data) {
494
+				// before we do ANYTHING, make sure the csv row wasn't just completely blank
495
+				$row_is_completely_empty = true;
496
+				foreach ($model_object_data as $field) {
497
+					if ($field) {
498
+						$row_is_completely_empty = false;
499
+					}
500
+				}
501
+				if ($row_is_completely_empty) {
502
+					continue;
503
+				}
504
+				// find the PK in the row of data (or a combined key if
505
+				// there is no primary key)
506
+				if ($model->has_primary_key_field()) {
507
+					$id_in_csv = $model_object_data[ $model->primary_key_name() ];
508
+				} else {
509
+					$id_in_csv = $model->get_index_primary_key_string($model_object_data);
510
+				}
511
+
512
+
513
+				$model_object_data = $this->_replace_temp_ids_with_mappings(
514
+					$model_object_data,
515
+					$model,
516
+					$old_db_to_new_db_mapping,
517
+					$export_from_site_a_to_b
518
+				);
519
+				// now we need to decide if we're going to add a new model object given the $model_object_data,
520
+				// or just update.
521
+				if ($export_from_site_a_to_b) {
522
+					$what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_other_db(
523
+						$id_in_csv,
524
+						$model_object_data,
525
+						$model,
526
+						$old_db_to_new_db_mapping
527
+					);
528
+				} else {// this is just a re-import
529
+					$what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_same_db(
530
+						$id_in_csv,
531
+						$model_object_data,
532
+						$model,
533
+						$old_db_to_new_db_mapping
534
+					);
535
+				}
536
+				if ($what_to_do == self::do_nothing) {
537
+					continue;
538
+				}
539
+
540
+				// double-check we actually want to insert, if that's what we're planning
541
+				// based on whether this item would be unique in the DB or not
542
+				if ($what_to_do == self::do_insert) {
543
+					// we're supposed to be inserting. But wait, will this thing
544
+					// be acceptable if inserted?
545
+					$conflicting = $model->get_one_conflicting($model_object_data, false);
546
+					if ($conflicting) {
547
+						// ok, this item would conflict if inserted. Just update the item that it conflicts with.
548
+						$what_to_do = self::do_update;
549
+						// and if this model has a primary key, remember its mapping
550
+						if ($model->has_primary_key_field()) {
551
+							$old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ] = $conflicting->ID();
552
+							$model_object_data[ $model->primary_key_name() ] = $conflicting->ID();
553
+						} else {
554
+							// we want to update this conflicting item, instead of inserting a conflicting item
555
+							// so we need to make sure they match entirely (its possible that they only conflicted on one field, but we need them to match on other fields
556
+							// for the WHERE conditions in the update). At the time of this comment, there were no models like this
557
+							foreach ($model->get_combined_primary_key_fields() as $key_field) {
558
+								$model_object_data[ $key_field->get_name() ] = $conflicting->get(
559
+									$key_field->get_name()
560
+								);
561
+							}
562
+						}
563
+					}
564
+				}
565
+				if ($what_to_do == self::do_insert) {
566
+					$old_db_to_new_db_mapping = $this->_insert_from_data_array(
567
+						$id_in_csv,
568
+						$model_object_data,
569
+						$model,
570
+						$old_db_to_new_db_mapping
571
+					);
572
+				} elseif ($what_to_do == self::do_update) {
573
+					$old_db_to_new_db_mapping = $this->_update_from_data_array(
574
+						$id_in_csv,
575
+						$model_object_data,
576
+						$model,
577
+						$old_db_to_new_db_mapping
578
+					);
579
+				} else {
580
+					throw new EE_Error(
581
+						sprintf(
582
+							esc_html__(
583
+								'Programming error. We shoudl be inserting or updating, but instead we are being told to "%s", whifh is invalid',
584
+								'event_espresso'
585
+							),
586
+							$what_to_do
587
+						)
588
+					);
589
+				}
590
+			}
591
+		}
592
+		return $old_db_to_new_db_mapping;
593
+	}
594
+
595
+
596
+	/**
597
+	 * Decides whether or not to insert, given that this data is from another database.
598
+	 * So, if the primary key of this $model_object_data already exists in the database,
599
+	 * it's just a coincidence and we should still insert. The only time we should
600
+	 * update is when we know what it maps to, or there's something that would
601
+	 * conflict (and we should instead just update that conflicting thing)
602
+	 *
603
+	 * @param string   $id_in_csv
604
+	 * @param array    $model_object_data        by reference so it can be modified
605
+	 * @param EEM_Base $model
606
+	 * @param array    $old_db_to_new_db_mapping by reference so it can be modified
607
+	 * @return string one of the consts on this class that starts with do_*
608
+	 */
609
+	protected function _decide_whether_to_insert_or_update_given_data_from_other_db(
610
+		$id_in_csv,
611
+		$model_object_data,
612
+		$model,
613
+		$old_db_to_new_db_mapping
614
+	) {
615
+		$model_name = $model->get_this_model_name();
616
+		// if it's a site-to-site export-and-import, see if this modelobject's id
617
+		// in the old data that we know of
618
+		if (isset($old_db_to_new_db_mapping[ $model_name ][ $id_in_csv ])) {
619
+			return self::do_update;
620
+		} else {
621
+			return self::do_insert;
622
+		}
623
+	}
624
+
625
+	/**
626
+	 * If this thing basically already exists in the database, we want to update it;
627
+	 * otherwise insert it (ie, someone tweaked the CSV file, or the item was
628
+	 * deleted in the database so it should be re-inserted)
629
+	 *
630
+	 * @param mixed     $id_in_csv
631
+	 * @param mixed     $model_object_data
632
+	 * @param EEM_Base $model
633
+	 * @param mixed     $old_db_to_new_db_mapping
634
+	 * @return
635
+	 */
636
+	protected function _decide_whether_to_insert_or_update_given_data_from_same_db(
637
+		$id_in_csv,
638
+		$model_object_data,
639
+		$model
640
+	) {
641
+		// in this case, check if this thing ACTUALLY exists in the database
642
+		if ($model->get_one_conflicting($model_object_data)) {
643
+			return self::do_update;
644
+		} else {
645
+			return self::do_insert;
646
+		}
647
+	}
648
+
649
+	/**
650
+	 * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs
651
+	 * with their mapped real IDs. Eg, if importing from site A to B, the mapping
652
+	 * file may indicate that the ID "my_event_id" maps to an actual event ID of 123.
653
+	 * So this function searches for any event temp Ids called "my_event_id" and
654
+	 * replaces them with 123.
655
+	 * Also, if there is no temp ID for the INT foreign keys from another database,
656
+	 * replaces them with 0 or the field's default.
657
+	 *
658
+	 * @param mixed     $model_object_data
659
+	 * @param EEM_Base $model
660
+	 * @param mixed     $old_db_to_new_db_mapping
661
+	 * @param boolean  $export_from_site_a_to_b
662
+	 * @return array updated model object data with temp IDs removed
663
+	 */
664
+	protected function _replace_temp_ids_with_mappings(
665
+		$model_object_data,
666
+		$model,
667
+		$old_db_to_new_db_mapping,
668
+		$export_from_site_a_to_b
669
+	) {
670
+		// if this model object's primary key is in the mapping, replace it
671
+		if (
672
+			$model->has_primary_key_field() &&
673
+			$model->get_primary_key_field()->is_auto_increment() &&
674
+			isset($old_db_to_new_db_mapping[ $model->get_this_model_name() ]) &&
675
+			isset(
676
+				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ]
677
+			)
678
+		) {
679
+			$model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name(
680
+			) ][ $model_object_data[ $model->primary_key_name() ] ];
681
+		}
682
+
683
+		try {
684
+			$model_name_field = $model->get_field_containing_related_model_name();
685
+			$models_pointed_to_by_model_name_field = $model_name_field->get_model_names_pointed_to();
686
+		} catch (EE_Error $e) {
687
+			$model_name_field = null;
688
+			$models_pointed_to_by_model_name_field = array();
689
+		}
690
+		foreach ($model->field_settings(true) as $field_obj) {
691
+			if ($field_obj instanceof EE_Foreign_Key_Int_Field) {
692
+				$models_pointed_to = $field_obj->get_model_names_pointed_to();
693
+				$found_a_mapping = false;
694
+				foreach ($models_pointed_to as $model_pointed_to_by_fk) {
695
+					if ($model_name_field) {
696
+						$value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
697
+						if ($value_of_model_name_field == $model_pointed_to_by_fk) {
698
+							$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
699
+								$model_object_data[ $field_obj->get_name() ],
700
+								$model_pointed_to_by_fk,
701
+								$old_db_to_new_db_mapping,
702
+								$export_from_site_a_to_b
703
+							);
704
+							$found_a_mapping = true;
705
+							break;
706
+						}
707
+					} else {
708
+						$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
709
+							$model_object_data[ $field_obj->get_name() ],
710
+							$model_pointed_to_by_fk,
711
+							$old_db_to_new_db_mapping,
712
+							$export_from_site_a_to_b
713
+						);
714
+						$found_a_mapping = true;
715
+					}
716
+					// once we've found a mapping for this field no need to continue
717
+					if ($found_a_mapping) {
718
+						break;
719
+					}
720
+				}
721
+			} else {
722
+				// it's a string foreign key (which we leave alone, because those are things
723
+				// like country names, which we'd really rather not make 2 USAs etc (we'd actually
724
+				// prefer to just update one)
725
+				// or it's just a regular value that ought to be replaced
726
+			}
727
+		}
728
+		//
729
+		if ($model instanceof EEM_Term_Taxonomy) {
730
+			$model_object_data = $this->_handle_split_term_ids($model_object_data);
731
+		}
732
+		return $model_object_data;
733
+	}
734
+
735
+	/**
736
+	 * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id
737
+	 * this term-taxonomy refers to may be out-of-date so we need to update it.
738
+	 * @see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/
739
+	 *
740
+	 * @param mixed $model_object_data
741
+	 * @return array new model object data
742
+	 */
743
+	protected function _handle_split_term_ids($model_object_data)
744
+	{
745
+		if (
746
+			isset($model_object_data['term_id'])
747
+			&& isset($model_object_data['taxonomy'])
748
+			&& apply_filters(
749
+				'FHEE__EE_Import__handle_split_term_ids__function_exists',
750
+				function_exists('wp_get_split_term'),
751
+				$model_object_data
752
+			)
753
+		) {
754
+			$new_term_id = wp_get_split_term($model_object_data['term_id'], $model_object_data['taxonomy']);
755
+			if ($new_term_id) {
756
+				$model_object_data['term_id'] = $new_term_id;
757
+			}
758
+		}
759
+		return $model_object_data;
760
+	}
761
+
762
+	/**
763
+	 * Given the object's ID and its model's name, find it int he mapping data,
764
+	 * bearing in mind where it came from
765
+	 *
766
+	 * @param mixed   $object_id
767
+	 * @param string $model_name
768
+	 * @param array  $old_db_to_new_db_mapping
769
+	 * @param mixed   $export_from_site_a_to_b
770
+	 * @return int
771
+	 */
772
+	protected function _find_mapping_in($object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b)
773
+	{
774
+		if (isset($old_db_to_new_db_mapping[ $model_name ][ $object_id ])) {
775
+			return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
776
+		} elseif ($object_id == '0' || $object_id == '') {
777
+			// leave as-is
778
+			return $object_id;
779
+		} elseif ($export_from_site_a_to_b) {
780
+			// we couldn't find a mapping for this, and it's from a different site,
781
+			// so blank it out
782
+			return null;
783
+		} elseif (! $export_from_site_a_to_b) {
784
+			// we coudln't find a mapping for this, but it's from thsi DB anyway
785
+			// so let's just leave it as-is
786
+			return $object_id;
787
+		}
788
+	}
789
+
790
+	/**
791
+	 *
792
+	 * @param mixed     $id_in_csv
793
+	 * @param mixed     $model_object_data
794
+	 * @param EEM_Base $model
795
+	 * @param mixed     $old_db_to_new_db_mapping
796
+	 * @return array updated $old_db_to_new_db_mapping
797
+	 */
798
+	protected function _insert_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
799
+	{
800
+		// remove the primary key, if there is one (we don't want it for inserts OR updates)
801
+		// we'll put it back in if we need it
802
+		if ($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()) {
803
+			$effective_id = $model_object_data[ $model->primary_key_name() ];
804
+			unset($model_object_data[ $model->primary_key_name() ]);
805
+		} else {
806
+			$effective_id = $model->get_index_primary_key_string($model_object_data);
807
+		}
808
+		// the model takes care of validating the CSV's input
809
+		try {
810
+			$new_id = $model->insert($model_object_data);
811
+			if ($new_id) {
812
+				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_id;
813
+				$this->_total_inserts++;
814
+				EE_Error::add_success(
815
+					sprintf(
816
+						esc_html__("Successfully added new %s (with id %s) with csv data %s", "event_espresso"),
817
+						$model->get_this_model_name(),
818
+						$new_id,
819
+						implode(",", $model_object_data)
820
+					)
821
+				);
822
+			} else {
823
+				$this->_total_insert_errors++;
824
+				// put the ID used back in there for the error message
825
+				if ($model->has_primary_key_field()) {
826
+					$model_object_data[ $model->primary_key_name() ] = $effective_id;
827
+				}
828
+				EE_Error::add_error(
829
+					sprintf(
830
+						esc_html__("Could not insert new %s with the csv data: %s", "event_espresso"),
831
+						$model->get_this_model_name(),
832
+						http_build_query($model_object_data)
833
+					),
834
+					__FILE__,
835
+					__FUNCTION__,
836
+					__LINE__
837
+				);
838
+			}
839
+		} catch (EE_Error $e) {
840
+			$this->_total_insert_errors++;
841
+			if ($model->has_primary_key_field()) {
842
+				$model_object_data[ $model->primary_key_name() ] = $effective_id;
843
+			}
844
+			EE_Error::add_error(
845
+				sprintf(
846
+					esc_html__("Could not insert new %s with the csv data: %s because %s", "event_espresso"),
847
+					$model->get_this_model_name(),
848
+					implode(",", $model_object_data),
849
+					$e->getMessage()
850
+				),
851
+				__FILE__,
852
+				__FUNCTION__,
853
+				__LINE__
854
+			);
855
+		}
856
+		return $old_db_to_new_db_mapping;
857
+	}
858
+
859
+	/**
860
+	 * Given the model object data, finds the row to update and updates it
861
+	 *
862
+	 * @param string|int $id_in_csv
863
+	 * @param array      $model_object_data
864
+	 * @param EEM_Base   $model
865
+	 * @param array      $old_db_to_new_db_mapping
866
+	 * @return array updated $old_db_to_new_db_mapping
867
+	 */
868
+	protected function _update_from_data_array($id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping)
869
+	{
870
+		try {
871
+			// let's keep two copies of the model object data:
872
+			// one for performing an update, one for everthing else
873
+			$model_object_data_for_update = $model_object_data;
874
+			if ($model->has_primary_key_field()) {
875
+				$conditions = array($model->primary_key_name() => $model_object_data[ $model->primary_key_name() ]);
876
+				// remove the primary key because we shouldn't use it for updating
877
+				unset($model_object_data_for_update[ $model->primary_key_name() ]);
878
+			} elseif ($model->get_combined_primary_key_fields() > 1) {
879
+				$conditions = array();
880
+				foreach ($model->get_combined_primary_key_fields() as $key_field) {
881
+					$conditions[ $key_field->get_name() ] = $model_object_data[ $key_field->get_name() ];
882
+				}
883
+			} else {
884
+				$model->primary_key_name(
885
+				);// this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
886
+			}
887
+
888
+			$success = $model->update($model_object_data_for_update, array($conditions));
889
+			if ($success) {
890
+				$this->_total_updates++;
891
+				EE_Error::add_success(
892
+					sprintf(
893
+						esc_html__("Successfully updated %s with csv data %s", "event_espresso"),
894
+						$model->get_this_model_name(),
895
+						implode(",", $model_object_data_for_update)
896
+					)
897
+				);
898
+				// we should still record the mapping even though it was an update
899
+				// because if we were going to insert somethign but it was going to conflict
900
+				// we would have last-minute decided to update. So we'd like to know what we updated
901
+				// and so we record what record ended up being updated using the mapping
902
+				if ($model->has_primary_key_field()) {
903
+					$new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
904
+				} else {
905
+					// no primary key just a combined key
906
+					$new_key_for_mapping = $model->get_index_primary_key_string($model_object_data);
907
+				}
908
+				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
909
+			} else {
910
+				$matched_items = $model->get_all(array($conditions));
911
+				if (! $matched_items) {
912
+					// no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
913
+					$this->_total_update_errors++;
914
+					EE_Error::add_error(
915
+						sprintf(
916
+							esc_html__(
917
+								"Could not update %s with the csv data: '%s' for an unknown reason (using WHERE conditions %s)",
918
+								"event_espresso"
919
+							),
920
+							$model->get_this_model_name(),
921
+							http_build_query($model_object_data),
922
+							http_build_query($conditions)
923
+						),
924
+						__FILE__,
925
+						__FUNCTION__,
926
+						__LINE__
927
+					);
928
+				} else {
929
+					$this->_total_updates++;
930
+					EE_Error::add_success(
931
+						sprintf(
932
+							esc_html__(
933
+								"%s with csv data '%s' was found in the database and didn't need updating because all the data is identical.",
934
+								"event_espresso"
935
+							),
936
+							$model->get_this_model_name(),
937
+							implode(",", $model_object_data)
938
+						)
939
+					);
940
+				}
941
+			}
942
+		} catch (EE_Error $e) {
943
+			$this->_total_update_errors++;
944
+			$basic_message = sprintf(
945
+				esc_html__("Could not update %s with the csv data: %s because %s", "event_espresso"),
946
+				$model->get_this_model_name(),
947
+				implode(",", $model_object_data),
948
+				$e->getMessage()
949
+			);
950
+			$debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
951
+			EE_Error::add_error("$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__);
952
+		}
953
+		return $old_db_to_new_db_mapping;
954
+	}
955
+
956
+	/**
957
+	 * Gets the number of inserts performed since importer was instantiated or reset
958
+	 *
959
+	 * @return int
960
+	 */
961
+	public function get_total_inserts()
962
+	{
963
+		return $this->_total_inserts;
964
+	}
965
+
966
+	/**
967
+	 *  Gets the number of insert errors since importer was instantiated or reset
968
+	 *
969
+	 * @return int
970
+	 */
971
+	public function get_total_insert_errors()
972
+	{
973
+		return $this->_total_insert_errors;
974
+	}
975
+
976
+	/**
977
+	 *  Gets the number of updates performed since importer was instantiated or reset
978
+	 *
979
+	 * @return int
980
+	 */
981
+	public function get_total_updates()
982
+	{
983
+		return $this->_total_updates;
984
+	}
985
+
986
+	/**
987
+	 *  Gets the number of update errors since importer was instantiated or reset
988
+	 *
989
+	 * @return int
990
+	 */
991
+	public function get_total_update_errors()
992
+	{
993
+		return $this->_total_update_errors;
994
+	}
995 995
 }
Please login to merge, or discard this patch.