@@ -130,7 +130,7 @@ discard block |
||
130 | 130 | */ |
131 | 131 | private function setName($name) |
132 | 132 | { |
133 | - if (! is_string($name)) { |
|
133 | + if ( ! is_string($name)) { |
|
134 | 134 | throw new InvalidDataTypeException('$name', $name, 'string'); |
135 | 135 | } |
136 | 136 | $this->name = sanitize_key($name); |
@@ -154,7 +154,7 @@ discard block |
||
154 | 154 | */ |
155 | 155 | private function setMessage($message) |
156 | 156 | { |
157 | - if (! is_string($message)) { |
|
157 | + if ( ! is_string($message)) { |
|
158 | 158 | throw new InvalidDataTypeException('$message', $message, 'string'); |
159 | 159 | } |
160 | 160 | global $allowedtags; |
@@ -200,7 +200,7 @@ discard block |
||
200 | 200 | */ |
201 | 201 | private function setCapability($capability) |
202 | 202 | { |
203 | - if (! is_string($capability)) { |
|
203 | + if ( ! is_string($capability)) { |
|
204 | 204 | throw new InvalidDataTypeException('$capability', $capability, 'string'); |
205 | 205 | } |
206 | 206 | $this->capability = ! empty($capability) ? $capability : 'manage_options'; |
@@ -224,7 +224,7 @@ discard block |
||
224 | 224 | */ |
225 | 225 | private function setCapContext($cap_context) |
226 | 226 | { |
227 | - if (! is_string($cap_context)) { |
|
227 | + if ( ! is_string($cap_context)) { |
|
228 | 228 | throw new InvalidDataTypeException('$cap_context', $cap_context, 'string'); |
229 | 229 | } |
230 | 230 | $this->cap_context = ! empty($cap_context) ? $cap_context : 'view persistent admin notice'; |
@@ -258,7 +258,7 @@ discard block |
||
258 | 258 | */ |
259 | 259 | public function getCapCheck() |
260 | 260 | { |
261 | - if (! $this->cap_check instanceof CapCheckInterface) { |
|
261 | + if ( ! $this->cap_check instanceof CapCheckInterface) { |
|
262 | 262 | $this->setCapCheck( |
263 | 263 | new CapCheck( |
264 | 264 | $this->capability, |
@@ -343,10 +343,10 @@ discard block |
||
343 | 343 | */ |
344 | 344 | public function confirmRegistered() |
345 | 345 | { |
346 | - if (! apply_filters('PersistentAdminNoticeManager__registerAndSaveNotices__complete', false)) { |
|
346 | + if ( ! apply_filters('PersistentAdminNoticeManager__registerAndSaveNotices__complete', false)) { |
|
347 | 347 | PersistentAdminNoticeManager::loadRegisterAndSaveNotices(); |
348 | 348 | } |
349 | - if (! $this->registered && WP_DEBUG) { |
|
349 | + if ( ! $this->registered && WP_DEBUG) { |
|
350 | 350 | throw new DomainException( |
351 | 351 | sprintf( |
352 | 352 | esc_html__( |
@@ -25,316 +25,316 @@ |
||
25 | 25 | class PersistentAdminNotice implements RequiresCapCheckInterface |
26 | 26 | { |
27 | 27 | |
28 | - /** |
|
29 | - * @var string $name |
|
30 | - */ |
|
31 | - protected $name = ''; |
|
32 | - |
|
33 | - /** |
|
34 | - * @var string $message |
|
35 | - */ |
|
36 | - protected $message = ''; |
|
37 | - |
|
38 | - /** |
|
39 | - * @var boolean $force_update |
|
40 | - */ |
|
41 | - protected $force_update = false; |
|
42 | - |
|
43 | - /** |
|
44 | - * @var string $capability |
|
45 | - */ |
|
46 | - protected $capability = 'manage_options'; |
|
47 | - |
|
48 | - /** |
|
49 | - * @var string $cap_context |
|
50 | - */ |
|
51 | - protected $cap_context = 'view persistent admin notice'; |
|
52 | - |
|
53 | - /** |
|
54 | - * @var boolean $dismissed |
|
55 | - */ |
|
56 | - protected $dismissed = false; |
|
57 | - |
|
58 | - /** |
|
59 | - * @var CapCheckInterface $cap_check |
|
60 | - */ |
|
61 | - protected $cap_check; |
|
62 | - |
|
63 | - /** |
|
64 | - * if true, then this notice will be deleted from the database |
|
65 | - * |
|
66 | - * @var boolean $purge |
|
67 | - */ |
|
68 | - protected $purge = false; |
|
69 | - |
|
70 | - /** |
|
71 | - * gets set to true if notice is successfully registered with the PersistentAdminNoticeManager |
|
72 | - * if false, and WP_DEBUG is on, then an exception will be thrown in the admin footer |
|
73 | - * |
|
74 | - * @var boolean $registered |
|
75 | - */ |
|
76 | - private $registered = false; |
|
77 | - |
|
78 | - |
|
79 | - /** |
|
80 | - * PersistentAdminNotice constructor |
|
81 | - * |
|
82 | - * @param string $name [required] the name, or key of the Persistent Admin Notice to be stored |
|
83 | - * @param string $message [required] the message to be stored persistently until dismissed |
|
84 | - * @param bool $force_update enforce the reappearance of a persistent message |
|
85 | - * @param string $capability user capability required to view this notice |
|
86 | - * @param string $cap_context description for why the cap check is being performed |
|
87 | - * @param bool $dismissed whether or not the user has already dismissed/viewed this notice |
|
88 | - * @throws InvalidDataTypeException |
|
89 | - */ |
|
90 | - public function __construct( |
|
91 | - $name, |
|
92 | - $message, |
|
93 | - $force_update = false, |
|
94 | - $capability = 'manage_options', |
|
95 | - $cap_context = 'view persistent admin notice', |
|
96 | - $dismissed = false |
|
97 | - ) { |
|
98 | - $this->setName($name); |
|
99 | - $this->setMessage($message); |
|
100 | - $this->setForceUpdate($force_update); |
|
101 | - $this->setCapability($capability); |
|
102 | - $this->setCapContext($cap_context); |
|
103 | - $this->setDismissed($dismissed); |
|
104 | - add_action( |
|
105 | - 'AHEE__EventEspresso_core_services_notifications_PersistentAdminNoticeManager__registerNotices', |
|
106 | - array($this, 'registerPersistentAdminNotice') |
|
107 | - ); |
|
108 | - add_action('shutdown', array($this, 'confirmRegistered'), 999); |
|
109 | - } |
|
110 | - |
|
111 | - |
|
112 | - /** |
|
113 | - * @return string |
|
114 | - */ |
|
115 | - public function getName() |
|
116 | - { |
|
117 | - return $this->name; |
|
118 | - } |
|
119 | - |
|
120 | - |
|
121 | - /** |
|
122 | - * @param string $name |
|
123 | - * @throws InvalidDataTypeException |
|
124 | - */ |
|
125 | - private function setName($name) |
|
126 | - { |
|
127 | - if (! is_string($name)) { |
|
128 | - throw new InvalidDataTypeException('$name', $name, 'string'); |
|
129 | - } |
|
130 | - $this->name = sanitize_key($name); |
|
131 | - } |
|
132 | - |
|
133 | - |
|
134 | - /** |
|
135 | - * @return string |
|
136 | - */ |
|
137 | - public function getMessage() |
|
138 | - { |
|
139 | - return $this->message; |
|
140 | - } |
|
141 | - |
|
142 | - |
|
143 | - /** |
|
144 | - * @param string $message |
|
145 | - * @throws InvalidDataTypeException |
|
146 | - */ |
|
147 | - private function setMessage($message) |
|
148 | - { |
|
149 | - if (! is_string($message)) { |
|
150 | - throw new InvalidDataTypeException('$message', $message, 'string'); |
|
151 | - } |
|
152 | - global $allowedtags; |
|
153 | - $allowedtags['br'] = array(); |
|
154 | - $this->message = wp_kses($message, $allowedtags); |
|
155 | - } |
|
156 | - |
|
157 | - |
|
158 | - /** |
|
159 | - * @return bool |
|
160 | - */ |
|
161 | - public function getForceUpdate() |
|
162 | - { |
|
163 | - return $this->force_update; |
|
164 | - } |
|
165 | - |
|
166 | - |
|
167 | - /** |
|
168 | - * @param bool $force_update |
|
169 | - */ |
|
170 | - private function setForceUpdate($force_update) |
|
171 | - { |
|
172 | - $this->force_update = filter_var($force_update, FILTER_VALIDATE_BOOLEAN); |
|
173 | - } |
|
174 | - |
|
175 | - |
|
176 | - /** |
|
177 | - * @return string |
|
178 | - */ |
|
179 | - public function getCapability() |
|
180 | - { |
|
181 | - return $this->capability; |
|
182 | - } |
|
183 | - |
|
184 | - |
|
185 | - /** |
|
186 | - * @param string $capability |
|
187 | - * @throws InvalidDataTypeException |
|
188 | - */ |
|
189 | - private function setCapability($capability) |
|
190 | - { |
|
191 | - if (! is_string($capability)) { |
|
192 | - throw new InvalidDataTypeException('$capability', $capability, 'string'); |
|
193 | - } |
|
194 | - $this->capability = ! empty($capability) ? $capability : 'manage_options'; |
|
195 | - } |
|
196 | - |
|
197 | - |
|
198 | - /** |
|
199 | - * @return string |
|
200 | - */ |
|
201 | - public function getCapContext() |
|
202 | - { |
|
203 | - return $this->cap_context; |
|
204 | - } |
|
205 | - |
|
206 | - |
|
207 | - /** |
|
208 | - * @param string $cap_context |
|
209 | - * @throws InvalidDataTypeException |
|
210 | - */ |
|
211 | - private function setCapContext($cap_context) |
|
212 | - { |
|
213 | - if (! is_string($cap_context)) { |
|
214 | - throw new InvalidDataTypeException('$cap_context', $cap_context, 'string'); |
|
215 | - } |
|
216 | - $this->cap_context = ! empty($cap_context) ? $cap_context : 'view persistent admin notice'; |
|
217 | - } |
|
218 | - |
|
219 | - |
|
220 | - /** |
|
221 | - * @return bool |
|
222 | - */ |
|
223 | - public function getDismissed() |
|
224 | - { |
|
225 | - return $this->dismissed; |
|
226 | - } |
|
227 | - |
|
228 | - |
|
229 | - /** |
|
230 | - * @param bool $dismissed |
|
231 | - */ |
|
232 | - public function setDismissed($dismissed) |
|
233 | - { |
|
234 | - $this->dismissed = filter_var($dismissed, FILTER_VALIDATE_BOOLEAN); |
|
235 | - } |
|
236 | - |
|
237 | - |
|
238 | - /** |
|
239 | - * @return CapCheckInterface |
|
240 | - * @throws InvalidDataTypeException |
|
241 | - */ |
|
242 | - public function getCapCheck() |
|
243 | - { |
|
244 | - if (! $this->cap_check instanceof CapCheckInterface) { |
|
245 | - $this->setCapCheck( |
|
246 | - new CapCheck( |
|
247 | - $this->capability, |
|
248 | - $this->cap_context |
|
249 | - ) |
|
250 | - ); |
|
251 | - } |
|
252 | - return $this->cap_check; |
|
253 | - } |
|
254 | - |
|
255 | - |
|
256 | - /** |
|
257 | - * @param CapCheckInterface $cap_check |
|
258 | - */ |
|
259 | - private function setCapCheck(CapCheckInterface $cap_check) |
|
260 | - { |
|
261 | - $this->cap_check = $cap_check; |
|
262 | - } |
|
263 | - |
|
264 | - |
|
265 | - /** |
|
266 | - * @return bool |
|
267 | - */ |
|
268 | - public function getPurge() |
|
269 | - { |
|
270 | - return $this->purge; |
|
271 | - } |
|
272 | - |
|
273 | - |
|
274 | - /** |
|
275 | - * @param bool $purge |
|
276 | - */ |
|
277 | - public function setPurge($purge) |
|
278 | - { |
|
279 | - $this->purge = filter_var($purge, FILTER_VALIDATE_BOOLEAN); |
|
280 | - } |
|
281 | - |
|
282 | - |
|
283 | - /** |
|
284 | - * given a valid PersistentAdminNotice Collection, |
|
285 | - * this notice will be added if it is not already found in the collection (using its name as the identifier) |
|
286 | - * if an existing notice is found that has already been dismissed, |
|
287 | - * but we are overriding with a forced update, then we will toggle its dismissed state, |
|
288 | - * so that the notice is displayed again |
|
289 | - * |
|
290 | - * @param Collection $persistent_admin_notice_collection |
|
291 | - * @throws InvalidEntityException |
|
292 | - * @throws InvalidDataTypeException |
|
293 | - * @throws DuplicateCollectionIdentifierException |
|
294 | - */ |
|
295 | - public function registerPersistentAdminNotice(Collection $persistent_admin_notice_collection) |
|
296 | - { |
|
297 | - if ($this->registered) { |
|
298 | - return; |
|
299 | - } |
|
300 | - // first check if this notice has already been added to the collection |
|
301 | - if ($persistent_admin_notice_collection->has($this->name)) { |
|
302 | - /** @var PersistentAdminNotice $existing */ |
|
303 | - $existing = $persistent_admin_notice_collection->get($this->name); |
|
304 | - // we don't need to add it again (we can't actually) |
|
305 | - // but if it has already been dismissed, and we are overriding with a forced update |
|
306 | - if ($existing->getDismissed() && $this->getForceUpdate()) { |
|
307 | - // then toggle the notice's dismissed state to true |
|
308 | - // so that it gets displayed again |
|
309 | - $existing->setDismissed(false); |
|
310 | - // and make sure the message is set |
|
311 | - $existing->setMessage($this->message); |
|
312 | - } |
|
313 | - } else { |
|
314 | - $persistent_admin_notice_collection->add($this, $this->name); |
|
315 | - } |
|
316 | - $this->registered = true; |
|
317 | - } |
|
318 | - |
|
319 | - |
|
320 | - /** |
|
321 | - * @throws Exception |
|
322 | - */ |
|
323 | - public function confirmRegistered() |
|
324 | - { |
|
325 | - if (! apply_filters('PersistentAdminNoticeManager__registerAndSaveNotices__complete', false)) { |
|
326 | - PersistentAdminNoticeManager::loadRegisterAndSaveNotices(); |
|
327 | - } |
|
328 | - if (! $this->registered && WP_DEBUG) { |
|
329 | - throw new DomainException( |
|
330 | - sprintf( |
|
331 | - esc_html__( |
|
332 | - 'The "%1$s" PersistentAdminNotice was not successfully registered. Please ensure that it is being created prior to either the "admin_notices" or "network_admin_notices" hooks being triggered.', |
|
333 | - 'event_espresso' |
|
334 | - ), |
|
335 | - $this->name |
|
336 | - ) |
|
337 | - ); |
|
338 | - } |
|
339 | - } |
|
28 | + /** |
|
29 | + * @var string $name |
|
30 | + */ |
|
31 | + protected $name = ''; |
|
32 | + |
|
33 | + /** |
|
34 | + * @var string $message |
|
35 | + */ |
|
36 | + protected $message = ''; |
|
37 | + |
|
38 | + /** |
|
39 | + * @var boolean $force_update |
|
40 | + */ |
|
41 | + protected $force_update = false; |
|
42 | + |
|
43 | + /** |
|
44 | + * @var string $capability |
|
45 | + */ |
|
46 | + protected $capability = 'manage_options'; |
|
47 | + |
|
48 | + /** |
|
49 | + * @var string $cap_context |
|
50 | + */ |
|
51 | + protected $cap_context = 'view persistent admin notice'; |
|
52 | + |
|
53 | + /** |
|
54 | + * @var boolean $dismissed |
|
55 | + */ |
|
56 | + protected $dismissed = false; |
|
57 | + |
|
58 | + /** |
|
59 | + * @var CapCheckInterface $cap_check |
|
60 | + */ |
|
61 | + protected $cap_check; |
|
62 | + |
|
63 | + /** |
|
64 | + * if true, then this notice will be deleted from the database |
|
65 | + * |
|
66 | + * @var boolean $purge |
|
67 | + */ |
|
68 | + protected $purge = false; |
|
69 | + |
|
70 | + /** |
|
71 | + * gets set to true if notice is successfully registered with the PersistentAdminNoticeManager |
|
72 | + * if false, and WP_DEBUG is on, then an exception will be thrown in the admin footer |
|
73 | + * |
|
74 | + * @var boolean $registered |
|
75 | + */ |
|
76 | + private $registered = false; |
|
77 | + |
|
78 | + |
|
79 | + /** |
|
80 | + * PersistentAdminNotice constructor |
|
81 | + * |
|
82 | + * @param string $name [required] the name, or key of the Persistent Admin Notice to be stored |
|
83 | + * @param string $message [required] the message to be stored persistently until dismissed |
|
84 | + * @param bool $force_update enforce the reappearance of a persistent message |
|
85 | + * @param string $capability user capability required to view this notice |
|
86 | + * @param string $cap_context description for why the cap check is being performed |
|
87 | + * @param bool $dismissed whether or not the user has already dismissed/viewed this notice |
|
88 | + * @throws InvalidDataTypeException |
|
89 | + */ |
|
90 | + public function __construct( |
|
91 | + $name, |
|
92 | + $message, |
|
93 | + $force_update = false, |
|
94 | + $capability = 'manage_options', |
|
95 | + $cap_context = 'view persistent admin notice', |
|
96 | + $dismissed = false |
|
97 | + ) { |
|
98 | + $this->setName($name); |
|
99 | + $this->setMessage($message); |
|
100 | + $this->setForceUpdate($force_update); |
|
101 | + $this->setCapability($capability); |
|
102 | + $this->setCapContext($cap_context); |
|
103 | + $this->setDismissed($dismissed); |
|
104 | + add_action( |
|
105 | + 'AHEE__EventEspresso_core_services_notifications_PersistentAdminNoticeManager__registerNotices', |
|
106 | + array($this, 'registerPersistentAdminNotice') |
|
107 | + ); |
|
108 | + add_action('shutdown', array($this, 'confirmRegistered'), 999); |
|
109 | + } |
|
110 | + |
|
111 | + |
|
112 | + /** |
|
113 | + * @return string |
|
114 | + */ |
|
115 | + public function getName() |
|
116 | + { |
|
117 | + return $this->name; |
|
118 | + } |
|
119 | + |
|
120 | + |
|
121 | + /** |
|
122 | + * @param string $name |
|
123 | + * @throws InvalidDataTypeException |
|
124 | + */ |
|
125 | + private function setName($name) |
|
126 | + { |
|
127 | + if (! is_string($name)) { |
|
128 | + throw new InvalidDataTypeException('$name', $name, 'string'); |
|
129 | + } |
|
130 | + $this->name = sanitize_key($name); |
|
131 | + } |
|
132 | + |
|
133 | + |
|
134 | + /** |
|
135 | + * @return string |
|
136 | + */ |
|
137 | + public function getMessage() |
|
138 | + { |
|
139 | + return $this->message; |
|
140 | + } |
|
141 | + |
|
142 | + |
|
143 | + /** |
|
144 | + * @param string $message |
|
145 | + * @throws InvalidDataTypeException |
|
146 | + */ |
|
147 | + private function setMessage($message) |
|
148 | + { |
|
149 | + if (! is_string($message)) { |
|
150 | + throw new InvalidDataTypeException('$message', $message, 'string'); |
|
151 | + } |
|
152 | + global $allowedtags; |
|
153 | + $allowedtags['br'] = array(); |
|
154 | + $this->message = wp_kses($message, $allowedtags); |
|
155 | + } |
|
156 | + |
|
157 | + |
|
158 | + /** |
|
159 | + * @return bool |
|
160 | + */ |
|
161 | + public function getForceUpdate() |
|
162 | + { |
|
163 | + return $this->force_update; |
|
164 | + } |
|
165 | + |
|
166 | + |
|
167 | + /** |
|
168 | + * @param bool $force_update |
|
169 | + */ |
|
170 | + private function setForceUpdate($force_update) |
|
171 | + { |
|
172 | + $this->force_update = filter_var($force_update, FILTER_VALIDATE_BOOLEAN); |
|
173 | + } |
|
174 | + |
|
175 | + |
|
176 | + /** |
|
177 | + * @return string |
|
178 | + */ |
|
179 | + public function getCapability() |
|
180 | + { |
|
181 | + return $this->capability; |
|
182 | + } |
|
183 | + |
|
184 | + |
|
185 | + /** |
|
186 | + * @param string $capability |
|
187 | + * @throws InvalidDataTypeException |
|
188 | + */ |
|
189 | + private function setCapability($capability) |
|
190 | + { |
|
191 | + if (! is_string($capability)) { |
|
192 | + throw new InvalidDataTypeException('$capability', $capability, 'string'); |
|
193 | + } |
|
194 | + $this->capability = ! empty($capability) ? $capability : 'manage_options'; |
|
195 | + } |
|
196 | + |
|
197 | + |
|
198 | + /** |
|
199 | + * @return string |
|
200 | + */ |
|
201 | + public function getCapContext() |
|
202 | + { |
|
203 | + return $this->cap_context; |
|
204 | + } |
|
205 | + |
|
206 | + |
|
207 | + /** |
|
208 | + * @param string $cap_context |
|
209 | + * @throws InvalidDataTypeException |
|
210 | + */ |
|
211 | + private function setCapContext($cap_context) |
|
212 | + { |
|
213 | + if (! is_string($cap_context)) { |
|
214 | + throw new InvalidDataTypeException('$cap_context', $cap_context, 'string'); |
|
215 | + } |
|
216 | + $this->cap_context = ! empty($cap_context) ? $cap_context : 'view persistent admin notice'; |
|
217 | + } |
|
218 | + |
|
219 | + |
|
220 | + /** |
|
221 | + * @return bool |
|
222 | + */ |
|
223 | + public function getDismissed() |
|
224 | + { |
|
225 | + return $this->dismissed; |
|
226 | + } |
|
227 | + |
|
228 | + |
|
229 | + /** |
|
230 | + * @param bool $dismissed |
|
231 | + */ |
|
232 | + public function setDismissed($dismissed) |
|
233 | + { |
|
234 | + $this->dismissed = filter_var($dismissed, FILTER_VALIDATE_BOOLEAN); |
|
235 | + } |
|
236 | + |
|
237 | + |
|
238 | + /** |
|
239 | + * @return CapCheckInterface |
|
240 | + * @throws InvalidDataTypeException |
|
241 | + */ |
|
242 | + public function getCapCheck() |
|
243 | + { |
|
244 | + if (! $this->cap_check instanceof CapCheckInterface) { |
|
245 | + $this->setCapCheck( |
|
246 | + new CapCheck( |
|
247 | + $this->capability, |
|
248 | + $this->cap_context |
|
249 | + ) |
|
250 | + ); |
|
251 | + } |
|
252 | + return $this->cap_check; |
|
253 | + } |
|
254 | + |
|
255 | + |
|
256 | + /** |
|
257 | + * @param CapCheckInterface $cap_check |
|
258 | + */ |
|
259 | + private function setCapCheck(CapCheckInterface $cap_check) |
|
260 | + { |
|
261 | + $this->cap_check = $cap_check; |
|
262 | + } |
|
263 | + |
|
264 | + |
|
265 | + /** |
|
266 | + * @return bool |
|
267 | + */ |
|
268 | + public function getPurge() |
|
269 | + { |
|
270 | + return $this->purge; |
|
271 | + } |
|
272 | + |
|
273 | + |
|
274 | + /** |
|
275 | + * @param bool $purge |
|
276 | + */ |
|
277 | + public function setPurge($purge) |
|
278 | + { |
|
279 | + $this->purge = filter_var($purge, FILTER_VALIDATE_BOOLEAN); |
|
280 | + } |
|
281 | + |
|
282 | + |
|
283 | + /** |
|
284 | + * given a valid PersistentAdminNotice Collection, |
|
285 | + * this notice will be added if it is not already found in the collection (using its name as the identifier) |
|
286 | + * if an existing notice is found that has already been dismissed, |
|
287 | + * but we are overriding with a forced update, then we will toggle its dismissed state, |
|
288 | + * so that the notice is displayed again |
|
289 | + * |
|
290 | + * @param Collection $persistent_admin_notice_collection |
|
291 | + * @throws InvalidEntityException |
|
292 | + * @throws InvalidDataTypeException |
|
293 | + * @throws DuplicateCollectionIdentifierException |
|
294 | + */ |
|
295 | + public function registerPersistentAdminNotice(Collection $persistent_admin_notice_collection) |
|
296 | + { |
|
297 | + if ($this->registered) { |
|
298 | + return; |
|
299 | + } |
|
300 | + // first check if this notice has already been added to the collection |
|
301 | + if ($persistent_admin_notice_collection->has($this->name)) { |
|
302 | + /** @var PersistentAdminNotice $existing */ |
|
303 | + $existing = $persistent_admin_notice_collection->get($this->name); |
|
304 | + // we don't need to add it again (we can't actually) |
|
305 | + // but if it has already been dismissed, and we are overriding with a forced update |
|
306 | + if ($existing->getDismissed() && $this->getForceUpdate()) { |
|
307 | + // then toggle the notice's dismissed state to true |
|
308 | + // so that it gets displayed again |
|
309 | + $existing->setDismissed(false); |
|
310 | + // and make sure the message is set |
|
311 | + $existing->setMessage($this->message); |
|
312 | + } |
|
313 | + } else { |
|
314 | + $persistent_admin_notice_collection->add($this, $this->name); |
|
315 | + } |
|
316 | + $this->registered = true; |
|
317 | + } |
|
318 | + |
|
319 | + |
|
320 | + /** |
|
321 | + * @throws Exception |
|
322 | + */ |
|
323 | + public function confirmRegistered() |
|
324 | + { |
|
325 | + if (! apply_filters('PersistentAdminNoticeManager__registerAndSaveNotices__complete', false)) { |
|
326 | + PersistentAdminNoticeManager::loadRegisterAndSaveNotices(); |
|
327 | + } |
|
328 | + if (! $this->registered && WP_DEBUG) { |
|
329 | + throw new DomainException( |
|
330 | + sprintf( |
|
331 | + esc_html__( |
|
332 | + 'The "%1$s" PersistentAdminNotice was not successfully registered. Please ensure that it is being created prior to either the "admin_notices" or "network_admin_notices" hooks being triggered.', |
|
333 | + 'event_espresso' |
|
334 | + ), |
|
335 | + $this->name |
|
336 | + ) |
|
337 | + ); |
|
338 | + } |
|
339 | + } |
|
340 | 340 | } |
@@ -14,133 +14,133 @@ |
||
14 | 14 | trait EventsAdmin |
15 | 15 | { |
16 | 16 | |
17 | - /** |
|
18 | - * @param string $additional_params |
|
19 | - */ |
|
20 | - public function amOnDefaultEventsListTablePage($additional_params = '') |
|
21 | - { |
|
22 | - $this->actor()->amOnAdminPage(EventsPage::defaultEventsListTableUrl($additional_params)); |
|
23 | - } |
|
24 | - |
|
25 | - |
|
26 | - /** |
|
27 | - * Triggers the publishing of the Event. |
|
28 | - */ |
|
29 | - public function publishEvent() |
|
30 | - { |
|
31 | - $this->actor()->scrollTo(EventsPage::EVENT_EDITOR_TITLE_FIELD_SELECTOR); |
|
32 | - $this->actor()->wait(3); |
|
33 | - $this->actor()->click(EventsPage::EVENT_EDITOR_PUBLISH_BUTTON_SELECTOR); |
|
34 | - $this->actor()->waitForText('Event published.', 30); |
|
35 | - } |
|
36 | - |
|
37 | - |
|
38 | - /** |
|
39 | - * Triggers saving the Event. |
|
40 | - */ |
|
41 | - public function saveEvent() |
|
42 | - { |
|
43 | - $this->actor()->scrollTo(EventsPage::EVENT_EDITOR_TITLE_FIELD_SELECTOR); |
|
44 | - $this->actor()->wait(2); |
|
45 | - $this->actor()->click(EventsPage::EVENT_EDITOR_SAVE_BUTTON_SELECTOR); |
|
46 | - } |
|
47 | - |
|
48 | - |
|
49 | - /** |
|
50 | - * Navigates the actor to the event list table page and will attempt to edit the event for the given title. |
|
51 | - * First this will search using the given title and then attempt to edit from the results of the search. |
|
52 | - * |
|
53 | - * Assumes actor is already logged in. |
|
54 | - * @param $event_title |
|
55 | - */ |
|
56 | - public function amEditingTheEventWithTitle($event_title) |
|
57 | - { |
|
58 | - $this->amOnDefaultEventsListTablePage(); |
|
59 | - $this->actor()->fillField(EventsPage::EVENT_LIST_TABLE_SEARCH_INPUT_SELECTOR, $event_title); |
|
60 | - $this->actor()->click(CoreAdmin::LIST_TABLE_SEARCH_SUBMIT_SELECTOR); |
|
61 | - $this->actor()->waitForText($event_title, 15); |
|
62 | - $this->actor()->click(EventsPage::eventListTableEventTitleEditLinkSelectorForTitle($event_title)); |
|
63 | - } |
|
64 | - |
|
65 | - |
|
66 | - /** |
|
67 | - * Navigates the user to the single event page (frontend view) for the given event title via clicking the "View" |
|
68 | - * link for the event in the event list table. |
|
69 | - * Assumes the actor is already logged in and on the Event list table page. |
|
70 | - * |
|
71 | - * @param string $event_title |
|
72 | - */ |
|
73 | - public function amOnEventPageAfterClickingViewLinkInListTableForEvent($event_title) |
|
74 | - { |
|
75 | - $this->actor()->moveMouseOver(EventsPage::eventListTableEventTitleEditLinkSelectorForTitle($event_title)); |
|
76 | - $this->actor()->click(EventsPage::eventListTableEventTitleViewLinkSelectorForTitle($event_title)); |
|
77 | - } |
|
78 | - |
|
79 | - |
|
80 | - /** |
|
81 | - * Used to retrieve the event id for the event via the list table and for the given event. |
|
82 | - * @param string $event_title |
|
83 | - */ |
|
84 | - public function observeEventIdInListTableForEvent($event_title) |
|
85 | - { |
|
86 | - return $this->actor()->observeValueFromInputAt(EventsPage::eventListTableEventIdSelectorForTitle($event_title)); |
|
87 | - } |
|
88 | - |
|
89 | - |
|
90 | - /** |
|
91 | - * This performs the click action on the gear icon that triggers the advanced settings view state. |
|
92 | - * Assumes the actor is already logged in and editing an event. |
|
93 | - * |
|
94 | - * @param int $row_number What ticket row to toggle open/close. |
|
95 | - */ |
|
96 | - public function toggleAdvancedSettingsViewForTicketRow($row_number = 1) |
|
97 | - { |
|
98 | - $this->actor()->click(EventsPage::eventEditorTicketAdvancedDetailsSelector($row_number)); |
|
99 | - } |
|
100 | - |
|
101 | - |
|
102 | - /** |
|
103 | - * Toggles the TKT_is_taxable checkbox for the ticket in the given row. |
|
104 | - * Assumes the actor is already logged in and editing an event and that the advanced settings view state for that |
|
105 | - * ticket is "open". |
|
106 | - * |
|
107 | - * @param int $row_number What ticket row to toggle the checkbox for. |
|
108 | - */ |
|
109 | - public function toggleTicketIsTaxableForTicketRow($row_number = 1) |
|
110 | - { |
|
111 | - $this->actor()->click(EventsPage::eventEditorTicketTaxableCheckboxSelector($row_number)); |
|
112 | - } |
|
113 | - |
|
114 | - |
|
115 | - /** |
|
116 | - * Use to change the default registration status for the event. |
|
117 | - * Assumes the view is already on the event editor. |
|
118 | - * @param $registration_status |
|
119 | - */ |
|
120 | - public function changeDefaultRegistrationStatusTo($registration_status) |
|
121 | - { |
|
122 | - $this->actor()->selectOption( |
|
123 | - EventsPage::EVENT_EDITOR_DEFAULT_REGISTRATION_STATUS_FIELD_SELECTOR, |
|
124 | - $registration_status |
|
125 | - ); |
|
126 | - } |
|
127 | - |
|
128 | - |
|
129 | - /** |
|
130 | - * Use this from the context of the event editor to select the given custom template for a given message type and |
|
131 | - * messenger. |
|
132 | - * |
|
133 | - * @param string $message_type_label The visible label for the message type (eg Registration Approved) |
|
134 | - * @param string $messenger_slug The slug for the messenger (eg 'email') |
|
135 | - * @param string $custom_template_label The visible label in the select input for the custom template you want |
|
136 | - * selected. |
|
137 | - */ |
|
138 | - public function selectCustomTemplateFor($message_type_label, $messenger_slug, $custom_template_label) |
|
139 | - { |
|
140 | - $this->actor()->click(EventsPage::eventEditorNotificationsMetaBoxMessengerTabSelector($messenger_slug)); |
|
141 | - $this->actor()->selectOption( |
|
142 | - EventsPage::eventEditorNotificationsMetaBoxSelectSelectorForMessageType($message_type_label), |
|
143 | - $custom_template_label |
|
144 | - ); |
|
145 | - } |
|
17 | + /** |
|
18 | + * @param string $additional_params |
|
19 | + */ |
|
20 | + public function amOnDefaultEventsListTablePage($additional_params = '') |
|
21 | + { |
|
22 | + $this->actor()->amOnAdminPage(EventsPage::defaultEventsListTableUrl($additional_params)); |
|
23 | + } |
|
24 | + |
|
25 | + |
|
26 | + /** |
|
27 | + * Triggers the publishing of the Event. |
|
28 | + */ |
|
29 | + public function publishEvent() |
|
30 | + { |
|
31 | + $this->actor()->scrollTo(EventsPage::EVENT_EDITOR_TITLE_FIELD_SELECTOR); |
|
32 | + $this->actor()->wait(3); |
|
33 | + $this->actor()->click(EventsPage::EVENT_EDITOR_PUBLISH_BUTTON_SELECTOR); |
|
34 | + $this->actor()->waitForText('Event published.', 30); |
|
35 | + } |
|
36 | + |
|
37 | + |
|
38 | + /** |
|
39 | + * Triggers saving the Event. |
|
40 | + */ |
|
41 | + public function saveEvent() |
|
42 | + { |
|
43 | + $this->actor()->scrollTo(EventsPage::EVENT_EDITOR_TITLE_FIELD_SELECTOR); |
|
44 | + $this->actor()->wait(2); |
|
45 | + $this->actor()->click(EventsPage::EVENT_EDITOR_SAVE_BUTTON_SELECTOR); |
|
46 | + } |
|
47 | + |
|
48 | + |
|
49 | + /** |
|
50 | + * Navigates the actor to the event list table page and will attempt to edit the event for the given title. |
|
51 | + * First this will search using the given title and then attempt to edit from the results of the search. |
|
52 | + * |
|
53 | + * Assumes actor is already logged in. |
|
54 | + * @param $event_title |
|
55 | + */ |
|
56 | + public function amEditingTheEventWithTitle($event_title) |
|
57 | + { |
|
58 | + $this->amOnDefaultEventsListTablePage(); |
|
59 | + $this->actor()->fillField(EventsPage::EVENT_LIST_TABLE_SEARCH_INPUT_SELECTOR, $event_title); |
|
60 | + $this->actor()->click(CoreAdmin::LIST_TABLE_SEARCH_SUBMIT_SELECTOR); |
|
61 | + $this->actor()->waitForText($event_title, 15); |
|
62 | + $this->actor()->click(EventsPage::eventListTableEventTitleEditLinkSelectorForTitle($event_title)); |
|
63 | + } |
|
64 | + |
|
65 | + |
|
66 | + /** |
|
67 | + * Navigates the user to the single event page (frontend view) for the given event title via clicking the "View" |
|
68 | + * link for the event in the event list table. |
|
69 | + * Assumes the actor is already logged in and on the Event list table page. |
|
70 | + * |
|
71 | + * @param string $event_title |
|
72 | + */ |
|
73 | + public function amOnEventPageAfterClickingViewLinkInListTableForEvent($event_title) |
|
74 | + { |
|
75 | + $this->actor()->moveMouseOver(EventsPage::eventListTableEventTitleEditLinkSelectorForTitle($event_title)); |
|
76 | + $this->actor()->click(EventsPage::eventListTableEventTitleViewLinkSelectorForTitle($event_title)); |
|
77 | + } |
|
78 | + |
|
79 | + |
|
80 | + /** |
|
81 | + * Used to retrieve the event id for the event via the list table and for the given event. |
|
82 | + * @param string $event_title |
|
83 | + */ |
|
84 | + public function observeEventIdInListTableForEvent($event_title) |
|
85 | + { |
|
86 | + return $this->actor()->observeValueFromInputAt(EventsPage::eventListTableEventIdSelectorForTitle($event_title)); |
|
87 | + } |
|
88 | + |
|
89 | + |
|
90 | + /** |
|
91 | + * This performs the click action on the gear icon that triggers the advanced settings view state. |
|
92 | + * Assumes the actor is already logged in and editing an event. |
|
93 | + * |
|
94 | + * @param int $row_number What ticket row to toggle open/close. |
|
95 | + */ |
|
96 | + public function toggleAdvancedSettingsViewForTicketRow($row_number = 1) |
|
97 | + { |
|
98 | + $this->actor()->click(EventsPage::eventEditorTicketAdvancedDetailsSelector($row_number)); |
|
99 | + } |
|
100 | + |
|
101 | + |
|
102 | + /** |
|
103 | + * Toggles the TKT_is_taxable checkbox for the ticket in the given row. |
|
104 | + * Assumes the actor is already logged in and editing an event and that the advanced settings view state for that |
|
105 | + * ticket is "open". |
|
106 | + * |
|
107 | + * @param int $row_number What ticket row to toggle the checkbox for. |
|
108 | + */ |
|
109 | + public function toggleTicketIsTaxableForTicketRow($row_number = 1) |
|
110 | + { |
|
111 | + $this->actor()->click(EventsPage::eventEditorTicketTaxableCheckboxSelector($row_number)); |
|
112 | + } |
|
113 | + |
|
114 | + |
|
115 | + /** |
|
116 | + * Use to change the default registration status for the event. |
|
117 | + * Assumes the view is already on the event editor. |
|
118 | + * @param $registration_status |
|
119 | + */ |
|
120 | + public function changeDefaultRegistrationStatusTo($registration_status) |
|
121 | + { |
|
122 | + $this->actor()->selectOption( |
|
123 | + EventsPage::EVENT_EDITOR_DEFAULT_REGISTRATION_STATUS_FIELD_SELECTOR, |
|
124 | + $registration_status |
|
125 | + ); |
|
126 | + } |
|
127 | + |
|
128 | + |
|
129 | + /** |
|
130 | + * Use this from the context of the event editor to select the given custom template for a given message type and |
|
131 | + * messenger. |
|
132 | + * |
|
133 | + * @param string $message_type_label The visible label for the message type (eg Registration Approved) |
|
134 | + * @param string $messenger_slug The slug for the messenger (eg 'email') |
|
135 | + * @param string $custom_template_label The visible label in the select input for the custom template you want |
|
136 | + * selected. |
|
137 | + */ |
|
138 | + public function selectCustomTemplateFor($message_type_label, $messenger_slug, $custom_template_label) |
|
139 | + { |
|
140 | + $this->actor()->click(EventsPage::eventEditorNotificationsMetaBoxMessengerTabSelector($messenger_slug)); |
|
141 | + $this->actor()->selectOption( |
|
142 | + EventsPage::eventEditorNotificationsMetaBoxSelectSelectorForMessageType($message_type_label), |
|
143 | + $custom_template_label |
|
144 | + ); |
|
145 | + } |
|
146 | 146 | } |
147 | 147 | \ No newline at end of file |
@@ -3,7 +3,6 @@ |
||
3 | 3 | use EventEspresso\core\exceptions\InvalidDataTypeException; |
4 | 4 | use EventEspresso\core\exceptions\InvalidInterfaceException; |
5 | 5 | use EventEspresso\core\interfaces\ResettableInterface; |
6 | - |
|
7 | 6 | use EventEspresso\core\services\database\TableManager; |
8 | 7 | use EventEspresso\core\services\database\TableAnalysis; |
9 | 8 |
@@ -32,1238 +32,1238 @@ |
||
32 | 32 | class EE_Data_Migration_Manager implements ResettableInterface |
33 | 33 | { |
34 | 34 | |
35 | - /** |
|
36 | - * |
|
37 | - * @var EE_Registry |
|
38 | - */ |
|
39 | - // protected $EE; |
|
40 | - /** |
|
41 | - * name of the wordpress option which stores an array of data about |
|
42 | - */ |
|
43 | - const data_migrations_option_name = 'ee_data_migration'; |
|
44 | - |
|
45 | - |
|
46 | - const data_migration_script_option_prefix = 'ee_data_migration_script_'; |
|
47 | - |
|
48 | - const data_migration_script_mapping_option_prefix = 'ee_dms_map_'; |
|
49 | - |
|
50 | - /** |
|
51 | - * name of the wordpress option which stores the database' current version. IE, the code may be at version 4.2.0, |
|
52 | - * but as migrations are performed the database will progress from 3.1.35 to 4.1.0 etc. |
|
53 | - */ |
|
54 | - const current_database_state = 'ee_data_migration_current_db_state'; |
|
55 | - |
|
56 | - /** |
|
57 | - * Special status string returned when we're positive there are no more data migration |
|
58 | - * scripts that can be run. |
|
59 | - */ |
|
60 | - const status_no_more_migration_scripts = 'no_more_migration_scripts'; |
|
61 | - /** |
|
62 | - * string indicating the migration should continue |
|
63 | - */ |
|
64 | - const status_continue = 'status_continue'; |
|
65 | - /** |
|
66 | - * string indicating the migration has completed and should be ended |
|
67 | - */ |
|
68 | - const status_completed = 'status_completed'; |
|
69 | - /** |
|
70 | - * string indicating a fatal error occurred and the data migration should be completely aborted |
|
71 | - */ |
|
72 | - const status_fatal_error = 'status_fatal_error'; |
|
73 | - |
|
74 | - /** |
|
75 | - * the number of 'items' (usually DB rows) to migrate on each 'step' (ajax request sent |
|
76 | - * during migration) |
|
77 | - */ |
|
78 | - const step_size = 50; |
|
79 | - |
|
80 | - /** |
|
81 | - * option name that stores the queue of ee plugins needing to have |
|
82 | - * their data initialized (or re-initialized) once we are done migrations |
|
83 | - */ |
|
84 | - const db_init_queue_option_name = 'ee_db_init_queue'; |
|
85 | - /** |
|
86 | - * Array of information concerning data migrations that have ran in the history |
|
87 | - * of this EE installation. Keys should be the name of the version the script upgraded to |
|
88 | - * |
|
89 | - * @var EE_Data_Migration_Script_Base[] |
|
90 | - */ |
|
91 | - private $_data_migrations_ran = null; |
|
92 | - /** |
|
93 | - * The last ran script. It's nice to store this somewhere accessible, as its easiest |
|
94 | - * to know which was the last run by which is the newest wp option; but in most of the code |
|
95 | - * we just use the local $_data_migration_ran array, which organized the scripts differently |
|
96 | - * |
|
97 | - * @var EE_Data_Migration_Script_Base |
|
98 | - */ |
|
99 | - private $_last_ran_script = null; |
|
100 | - |
|
101 | - /** |
|
102 | - * Similarly to _last_ran_script, but this is the last INCOMPLETE migration script. |
|
103 | - * |
|
104 | - * @var EE_Data_Migration_Script_Base |
|
105 | - */ |
|
106 | - private $_last_ran_incomplete_script = null; |
|
107 | - /** |
|
108 | - * array where keys are classnames, and values are filepaths of all the known migration scripts |
|
109 | - * |
|
110 | - * @var array |
|
111 | - */ |
|
112 | - private $_data_migration_class_to_filepath_map; |
|
113 | - |
|
114 | - /** |
|
115 | - * the following 4 properties are fully set on construction. |
|
116 | - * Note: the first two apply to whether to continue running ALL migration scripts (ie, even though we're finished |
|
117 | - * one, we may want to start the next one); whereas the last two indicate whether to continue running a single |
|
118 | - * data migration script |
|
119 | - * |
|
120 | - * @var array |
|
121 | - */ |
|
122 | - public $stati_that_indicate_to_continue_migrations = array(); |
|
123 | - |
|
124 | - public $stati_that_indicate_to_stop_migrations = array(); |
|
125 | - |
|
126 | - public $stati_that_indicate_to_continue_single_migration_script = array(); |
|
127 | - |
|
128 | - public $stati_that_indicate_to_stop_single_migration_script = array(); |
|
129 | - |
|
130 | - /** |
|
131 | - * @var \EventEspresso\core\services\database\TableManager $table_manager |
|
132 | - */ |
|
133 | - protected $_table_manager; |
|
134 | - |
|
135 | - /** |
|
136 | - * @var \EventEspresso\core\services\database\TableAnalysis $table_analysis |
|
137 | - */ |
|
138 | - protected $_table_analysis; |
|
139 | - |
|
140 | - /** |
|
141 | - * @var array $script_migration_versions |
|
142 | - */ |
|
143 | - protected $script_migration_versions; |
|
144 | - |
|
145 | - /** |
|
146 | - * @var EE_Data_Migration_Manager $_instance |
|
147 | - * @access private |
|
148 | - */ |
|
149 | - private static $_instance = null; |
|
150 | - |
|
151 | - |
|
152 | - /** |
|
153 | - * @singleton method used to instantiate class object |
|
154 | - * @access public |
|
155 | - * @return EE_Data_Migration_Manager instance |
|
156 | - */ |
|
157 | - public static function instance() |
|
158 | - { |
|
159 | - // check if class object is instantiated |
|
160 | - if (! self::$_instance instanceof EE_Data_Migration_Manager) { |
|
161 | - self::$_instance = new self(); |
|
162 | - } |
|
163 | - return self::$_instance; |
|
164 | - } |
|
165 | - |
|
166 | - /** |
|
167 | - * resets the singleton to its brand-new state (but does NOT delete old references to the old singleton. Meaning, |
|
168 | - * all new usages of the singleton should be made with Classname::instance()) and returns it |
|
169 | - * |
|
170 | - * @return EE_Data_Migration_Manager |
|
171 | - */ |
|
172 | - public static function reset() |
|
173 | - { |
|
174 | - self::$_instance = null; |
|
175 | - return self::instance(); |
|
176 | - } |
|
177 | - |
|
178 | - |
|
179 | - /** |
|
180 | - * constructor |
|
181 | - */ |
|
182 | - private function __construct() |
|
183 | - { |
|
184 | - $this->stati_that_indicate_to_continue_migrations = array( |
|
185 | - self::status_continue, |
|
186 | - self::status_completed, |
|
187 | - ); |
|
188 | - $this->stati_that_indicate_to_stop_migrations = array( |
|
189 | - self::status_fatal_error, |
|
190 | - self::status_no_more_migration_scripts, |
|
191 | - ); |
|
192 | - $this->stati_that_indicate_to_continue_single_migration_script = array( |
|
193 | - self::status_continue, |
|
194 | - ); |
|
195 | - $this->stati_that_indicate_to_stop_single_migration_script = array( |
|
196 | - self::status_completed, |
|
197 | - self::status_fatal_error |
|
198 | - // note: status_no_more_migration_scripts doesn't apply |
|
199 | - ); |
|
200 | - // make sure we've included the base migration script, because we may need the EE_DMS_Unknown_1_0_0 class |
|
201 | - // to be defined, because right now it doesn't get autoloaded on its own |
|
202 | - EE_Registry::instance()->load_core('Data_Migration_Class_Base', array(), true); |
|
203 | - EE_Registry::instance()->load_core('Data_Migration_Script_Base', array(), true); |
|
204 | - EE_Registry::instance()->load_core('DMS_Unknown_1_0_0', array(), true); |
|
205 | - EE_Registry::instance()->load_core('Data_Migration_Script_Stage', array(), true); |
|
206 | - EE_Registry::instance()->load_core('Data_Migration_Script_Stage_Table', array(), true); |
|
207 | - $this->_table_manager = EE_Registry::instance()->create('TableManager', array(), true); |
|
208 | - $this->_table_analysis = EE_Registry::instance()->create('TableAnalysis', array(), true); |
|
209 | - } |
|
210 | - |
|
211 | - |
|
212 | - /** |
|
213 | - * Deciphers, from an option's name, what plugin and version it relates to (see _save_migrations_ran to see what |
|
214 | - * the option names are like, but generally they're like |
|
215 | - * 'ee_data_migration_script_Core.4.1.0' in 4.2 or 'ee_data_migration_script_4.1.0' before that). |
|
216 | - * The option name shouldn't ever be like 'ee_data_migration_script_Core.4.1.0.reg' because it's derived, |
|
217 | - * indirectly, from the data migration's classname, which should always be like EE_DMS_%s_%d_%d_%d.dms.php (eg |
|
218 | - * EE_DMS_Core_4_1_0.dms.php) |
|
219 | - * |
|
220 | - * @param string $option_name (see EE_Data_Migration_Manage::_save_migrations_ran() where the option name is set) |
|
221 | - * @return array where the first item is the plugin slug (eg 'Core','Calendar',etc) and the 2nd is the version of |
|
222 | - * that plugin (eg '4.1.0') |
|
223 | - */ |
|
224 | - private function _get_plugin_slug_and_version_string_from_dms_option_name($option_name) |
|
225 | - { |
|
226 | - $plugin_slug_and_version_string = str_replace( |
|
227 | - EE_Data_Migration_Manager::data_migration_script_option_prefix, |
|
228 | - "", |
|
229 | - $option_name |
|
230 | - ); |
|
231 | - // check if $plugin_slug_and_version_string is like '4.1.0' (4.1-style) or 'Core.4.1.0' (4.2-style) |
|
232 | - $parts = explode(".", $plugin_slug_and_version_string); |
|
233 | - |
|
234 | - if (count($parts) == 4) { |
|
235 | - // it's 4.2-style.eg Core.4.1.0 |
|
236 | - $plugin_slug = $parts[0];// eg Core |
|
237 | - $version_string = $parts[1] . "." . $parts[2] . "." . $parts[3]; // eg 4.1.0 |
|
238 | - } else { |
|
239 | - // it's 4.1-style: eg 4.1.0 |
|
240 | - $plugin_slug = 'Core'; |
|
241 | - $version_string = $plugin_slug_and_version_string;// eg 4.1.0 |
|
242 | - } |
|
243 | - return array($plugin_slug, $version_string); |
|
244 | - } |
|
245 | - |
|
246 | - /** |
|
247 | - * Gets the DMS class from the wordpress option, otherwise throws an EE_Error if it's not |
|
248 | - * for a known DMS class. |
|
249 | - * |
|
250 | - * @param string $dms_option_name |
|
251 | - * @param string $dms_option_value (serialized) |
|
252 | - * @return EE_Data_Migration_Script_Base |
|
253 | - * @throws EE_Error |
|
254 | - */ |
|
255 | - private function _get_dms_class_from_wp_option($dms_option_name, $dms_option_value) |
|
256 | - { |
|
257 | - $data_migration_data = maybe_unserialize($dms_option_value); |
|
258 | - if (isset($data_migration_data['class']) && class_exists($data_migration_data['class'])) { |
|
259 | - $class = new $data_migration_data['class']; |
|
260 | - if ($class instanceof EE_Data_Migration_Script_Base) { |
|
261 | - $class->instantiate_from_array_of_properties($data_migration_data); |
|
262 | - return $class; |
|
263 | - } else { |
|
264 | - // huh, so its an object but not a data migration script?? that shouldn't happen |
|
265 | - // just leave it as an array (which will probably just get ignored) |
|
266 | - throw new EE_Error( |
|
267 | - sprintf( |
|
268 | - __( |
|
269 | - "Trying to retrieve DMS class from wp option. No DMS by the name '%s' exists", |
|
270 | - 'event_espresso' |
|
271 | - ), |
|
272 | - $data_migration_data['class'] |
|
273 | - ) |
|
274 | - ); |
|
275 | - } |
|
276 | - } else { |
|
277 | - // so the data doesn't specify a class. So it must either be a legacy array of info or some array (which we'll probably just ignore), or a class that no longer exists |
|
278 | - throw new EE_Error( |
|
279 | - sprintf(__("The wp option with key '%s' does not represent a DMS", 'event_espresso'), $dms_option_name) |
|
280 | - ); |
|
281 | - } |
|
282 | - } |
|
283 | - |
|
284 | - /** |
|
285 | - * Gets the array describing what data migrations have run. Also has a side-effect of recording which was the last |
|
286 | - * ran, and which was the last ran which hasn't finished yet |
|
287 | - * |
|
288 | - * @return array where each element should be an array of EE_Data_Migration_Script_Base (but also has a few legacy |
|
289 | - * arrays in there - which should probably be ignored) |
|
290 | - */ |
|
291 | - public function get_data_migrations_ran() |
|
292 | - { |
|
293 | - if (! $this->_data_migrations_ran) { |
|
294 | - // setup autoloaders for each of the scripts in there |
|
295 | - $this->get_all_data_migration_scripts_available(); |
|
296 | - $data_migrations_options = $this->get_all_migration_script_options( |
|
297 | - );// get_option(EE_Data_Migration_Manager::data_migrations_option_name,get_option('espresso_data_migrations',array())); |
|
298 | - |
|
299 | - $data_migrations_ran = array(); |
|
300 | - // convert into data migration script classes where possible |
|
301 | - foreach ($data_migrations_options as $data_migration_option) { |
|
302 | - list($plugin_slug, $version_string) = $this->_get_plugin_slug_and_version_string_from_dms_option_name( |
|
303 | - $data_migration_option['option_name'] |
|
304 | - ); |
|
305 | - |
|
306 | - try { |
|
307 | - $class = $this->_get_dms_class_from_wp_option( |
|
308 | - $data_migration_option['option_name'], |
|
309 | - $data_migration_option['option_value'] |
|
310 | - ); |
|
311 | - $data_migrations_ran[ $plugin_slug ][ $version_string ] = $class; |
|
312 | - // ok so far THIS is the 'last-ran-script'... unless we find another on next iteration |
|
313 | - $this->_last_ran_script = $class; |
|
314 | - if (! $class->is_completed()) { |
|
315 | - // sometimes we also like to know which was the last incomplete script (or if there are any at all) |
|
316 | - $this->_last_ran_incomplete_script = $class; |
|
317 | - } |
|
318 | - } catch (EE_Error $e) { |
|
319 | - // ok so its not a DMS. We'll just keep it, although other code will need to expect non-DMSs |
|
320 | - $data_migrations_ran[ $plugin_slug ][ $version_string ] = maybe_unserialize( |
|
321 | - $data_migration_option['option_value'] |
|
322 | - ); |
|
323 | - } |
|
324 | - } |
|
325 | - // so here the array of $data_migrations_ran is actually a mix of classes and a few legacy arrays |
|
326 | - $this->_data_migrations_ran = $data_migrations_ran; |
|
327 | - if (! $this->_data_migrations_ran || ! is_array($this->_data_migrations_ran)) { |
|
328 | - $this->_data_migrations_ran = array(); |
|
329 | - } |
|
330 | - } |
|
331 | - return $this->_data_migrations_ran; |
|
332 | - } |
|
333 | - |
|
334 | - |
|
335 | - /** |
|
336 | - * |
|
337 | - * @param string $script_name eg 'DMS_Core_4_1_0' |
|
338 | - * @param string $old_table eg 'wp_events_detail' |
|
339 | - * @param string $old_pk eg 'wp_esp_posts' |
|
340 | - * @param $new_table |
|
341 | - * @return mixed string or int |
|
342 | - */ |
|
343 | - public function get_mapping_new_pk($script_name, $old_table, $old_pk, $new_table) |
|
344 | - { |
|
345 | - $script = EE_Registry::instance()->load_dms($script_name); |
|
346 | - $mapping = $script->get_mapping_new_pk($old_table, $old_pk, $new_table); |
|
347 | - return $mapping; |
|
348 | - } |
|
349 | - |
|
350 | - /** |
|
351 | - * Gets all the options containing migration scripts that have been run. Ordering is important: it's assumed that |
|
352 | - * the last option returned in this array is the most-recently ran DMS option |
|
353 | - * |
|
354 | - * @return array |
|
355 | - */ |
|
356 | - public function get_all_migration_script_options() |
|
357 | - { |
|
358 | - global $wpdb; |
|
359 | - return $wpdb->get_results( |
|
360 | - "SELECT * FROM {$wpdb->options} WHERE option_name like '" . EE_Data_Migration_Manager::data_migration_script_option_prefix . "%' ORDER BY option_id ASC", |
|
361 | - ARRAY_A |
|
362 | - ); |
|
363 | - } |
|
364 | - |
|
365 | - /** |
|
366 | - * Gets the array of folders which contain data migration scripts. Also adds them to be auto-loaded |
|
367 | - * |
|
368 | - * @return array where each value is the full folder path of a folder containing data migration scripts, WITH |
|
369 | - * slashes at the end of the folder name. |
|
370 | - */ |
|
371 | - public function get_data_migration_script_folders() |
|
372 | - { |
|
373 | - return apply_filters( |
|
374 | - 'FHEE__EE_Data_Migration_Manager__get_data_migration_script_folders', |
|
375 | - array('Core' => EE_CORE . 'data_migration_scripts') |
|
376 | - ); |
|
377 | - } |
|
378 | - |
|
379 | - /** |
|
380 | - * Gets the version the migration script upgrades to |
|
381 | - * |
|
382 | - * @param string $migration_script_name eg 'EE_DMS_Core_4_1_0' |
|
383 | - * @return array { |
|
384 | - * @type string $slug like 'Core','Calendar',etc |
|
385 | - * @type string $version like 4.3.0 |
|
386 | - * } |
|
387 | - * @throws EE_Error |
|
388 | - */ |
|
389 | - public function script_migrates_to_version($migration_script_name, $eeAddonClass = '') |
|
390 | - { |
|
391 | - if (isset($this->script_migration_versions[ $migration_script_name ])) { |
|
392 | - return $this->script_migration_versions[ $migration_script_name ]; |
|
393 | - } |
|
394 | - $dms_info = $this->parse_dms_classname($migration_script_name); |
|
395 | - $this->script_migration_versions[ $migration_script_name ] = array( |
|
396 | - 'slug' => $eeAddonClass !== '' ? $eeAddonClass : $dms_info['slug'], |
|
397 | - 'version' => $dms_info['major_version'] . "." . $dms_info['minor_version'] . "." . $dms_info['micro_version'], |
|
398 | - ); |
|
399 | - return $this->script_migration_versions[ $migration_script_name ]; |
|
400 | - } |
|
401 | - |
|
402 | - /** |
|
403 | - * Gets the juicy details out of a dms filename like 'EE_DMS_Core_4_1_0' |
|
404 | - * |
|
405 | - * @param string $classname |
|
406 | - * @return array with keys 'slug','major_version','minor_version', and 'micro_version' (the last 3 are ints) |
|
407 | - * @throws EE_Error |
|
408 | - */ |
|
409 | - public function parse_dms_classname($classname) |
|
410 | - { |
|
411 | - $matches = array(); |
|
412 | - preg_match('~EE_DMS_(.*)_([0-9]*)_([0-9]*)_([0-9]*)~', $classname, $matches); |
|
413 | - if (! $matches || ! (isset($matches[1]) && isset($matches[2]) && isset($matches[3]))) { |
|
414 | - throw new EE_Error( |
|
415 | - sprintf( |
|
416 | - __( |
|
417 | - "%s is not a valid Data Migration Script. The classname should be like EE_DMS_w_x_y_z, where w is either 'Core' or the slug of an addon and x, y and z are numbers, ", |
|
418 | - "event_espresso" |
|
419 | - ), |
|
420 | - $classname |
|
421 | - ) |
|
422 | - ); |
|
423 | - } |
|
424 | - return array( |
|
425 | - 'slug' => $matches[1], |
|
426 | - 'major_version' => intval($matches[2]), |
|
427 | - 'minor_version' => intval($matches[3]), |
|
428 | - 'micro_version' => intval($matches[4]), |
|
429 | - ); |
|
430 | - } |
|
431 | - |
|
432 | - /** |
|
433 | - * Ensures that the option indicating the current DB version is set. This should only be |
|
434 | - * a concern when activating EE for the first time, THEORETICALLY. |
|
435 | - * If we detect that we're activating EE4 over top of EE3.1, then we set the current db state to 3.1.x, otherwise |
|
436 | - * to 4.1.x. |
|
437 | - * |
|
438 | - * @return string of current db state |
|
439 | - */ |
|
440 | - public function ensure_current_database_state_is_set() |
|
441 | - { |
|
442 | - $espresso_db_core_updates = get_option('espresso_db_update', array()); |
|
443 | - $db_state = get_option(EE_Data_Migration_Manager::current_database_state); |
|
444 | - if (! $db_state) { |
|
445 | - // mark the DB as being in the state as the last version in there. |
|
446 | - // this is done to trigger maintenance mode and do data migration scripts |
|
447 | - // if the admin installed this version of EE over 3.1.x or 4.0.x |
|
448 | - // otherwise, the normal maintenance mode code is fine |
|
449 | - $previous_versions_installed = array_keys($espresso_db_core_updates); |
|
450 | - $previous_version_installed = end($previous_versions_installed); |
|
451 | - if (version_compare('4.1.0', $previous_version_installed)) { |
|
452 | - // last installed version was less than 4.1 |
|
453 | - // so we want the data migrations to happen. SO, we're going to say the DB is at that state |
|
454 | - $db_state = array('Core' => $previous_version_installed); |
|
455 | - } else { |
|
456 | - $db_state = array('Core' => EVENT_ESPRESSO_VERSION); |
|
457 | - } |
|
458 | - update_option(EE_Data_Migration_Manager::current_database_state, $db_state); |
|
459 | - } |
|
460 | - // in 4.1, $db_state would have only been a simple string like '4.1.0', |
|
461 | - // but in 4.2+ it should be an array with at least key 'Core' and the value of that plugin's |
|
462 | - // db, and possibly other keys for other addons like 'Calendar','Permissions',etc |
|
463 | - if (! is_array($db_state)) { |
|
464 | - $db_state = array('Core' => $db_state); |
|
465 | - update_option(EE_Data_Migration_Manager::current_database_state, $db_state); |
|
466 | - } |
|
467 | - return $db_state; |
|
468 | - } |
|
469 | - |
|
470 | - /** |
|
471 | - * Checks if there are any data migration scripts that ought to be run. If found, |
|
472 | - * returns the instantiated classes. If none are found (ie, they've all already been run |
|
473 | - * or they don't apply), returns an empty array |
|
474 | - * |
|
475 | - * @return EE_Data_Migration_Script_Base[] |
|
476 | - */ |
|
477 | - public function check_for_applicable_data_migration_scripts() |
|
478 | - { |
|
479 | - // get the option describing what options have already run |
|
480 | - $scripts_ran = $this->get_data_migrations_ran(); |
|
481 | - // $scripts_ran = array('4.1.0.core'=>array('monkey'=>null)); |
|
482 | - $script_class_and_filepaths_available = $this->get_all_data_migration_scripts_available(); |
|
483 | - |
|
484 | - |
|
485 | - $current_database_state = $this->ensure_current_database_state_is_set(); |
|
486 | - // determine which have already been run |
|
487 | - $script_classes_that_should_run_per_iteration = array(); |
|
488 | - $iteration = 0; |
|
489 | - $next_database_state_to_consider = $current_database_state; |
|
490 | - $theoretical_database_state = null; |
|
491 | - do { |
|
492 | - // the next state after the currently-considered one will start off looking the same as the current, but we may make additions... |
|
493 | - $theoretical_database_state = $next_database_state_to_consider; |
|
494 | - // the next db state to consider is "what would the DB be like had we run all the scripts we found that applied last time?) |
|
495 | - foreach ($script_class_and_filepaths_available as $classname => $filepath) { |
|
496 | - $migrates_to_version = $this->script_migrates_to_version($classname); |
|
497 | - $script_converts_plugin_slug = $migrates_to_version['slug']; |
|
498 | - $script_converts_to_version = $migrates_to_version['version']; |
|
499 | - // check if this version script is DONE or not; or if it's never been ran |
|
500 | - if (! $scripts_ran || |
|
501 | - ! isset($scripts_ran[ $script_converts_plugin_slug ]) || |
|
502 | - ! isset($scripts_ran[ $script_converts_plugin_slug ][ $script_converts_to_version ])) { |
|
503 | - // we haven't ran this conversion script before |
|
504 | - // now check if it applies... note that we've added an autoloader for it on get_all_data_migration_scripts_available |
|
505 | - $script = new $classname($this->_get_table_manager(), $this->_get_table_analysis()); |
|
506 | - /* @var $script EE_Data_Migration_Script_Base */ |
|
507 | - $can_migrate = $script->can_migrate_from_version($theoretical_database_state); |
|
508 | - if ($can_migrate) { |
|
509 | - $script_classes_that_should_run_per_iteration[ $iteration ][ $script->priority() ][] = $script; |
|
510 | - $migrates_to_version = $script->migrates_to_version(); |
|
511 | - $next_database_state_to_consider[ $migrates_to_version['slug'] ] = $migrates_to_version['version']; |
|
512 | - unset($script_class_and_filepaths_available[ $classname ]); |
|
513 | - } |
|
514 | - } elseif ($scripts_ran[ $script_converts_plugin_slug ][ $script_converts_to_version ] instanceof EE_Data_Migration_Script_Base) { |
|
515 | - // this script has been ran, or at least started |
|
516 | - $script = $scripts_ran[ $script_converts_plugin_slug ][ $script_converts_to_version ]; |
|
517 | - if ($script->get_status() != self::status_completed) { |
|
518 | - // this script is already underway... keep going with it |
|
519 | - $script_classes_that_should_run_per_iteration[ $iteration ][ $script->priority() ][] = $script; |
|
520 | - $migrates_to_version = $script->migrates_to_version(); |
|
521 | - $next_database_state_to_consider[ $migrates_to_version['slug'] ] = $migrates_to_version['version']; |
|
522 | - unset($script_class_and_filepaths_available[ $classname ]); |
|
523 | - } else { |
|
524 | - // it must have a status that indicates it has finished, so we don't want to try and run it again |
|
525 | - } |
|
526 | - } else { |
|
527 | - // it exists but it's not a proper data migration script |
|
528 | - // maybe the script got renamed? or was simply removed from EE? |
|
529 | - // either way, its certainly not runnable! |
|
530 | - } |
|
531 | - } |
|
532 | - $iteration++; |
|
533 | - } while ($next_database_state_to_consider != $theoretical_database_state && $iteration < 6); |
|
534 | - // ok we have all the scripts that should run, now let's make them into flat array |
|
535 | - $scripts_that_should_run = array(); |
|
536 | - foreach ($script_classes_that_should_run_per_iteration as $scripts_at_priority) { |
|
537 | - ksort($scripts_at_priority); |
|
538 | - foreach ($scripts_at_priority as $scripts) { |
|
539 | - foreach ($scripts as $script) { |
|
540 | - $scripts_that_should_run[ get_class($script) ] = $script; |
|
541 | - } |
|
542 | - } |
|
543 | - } |
|
544 | - |
|
545 | - do_action( |
|
546 | - 'AHEE__EE_Data_Migration_Manager__check_for_applicable_data_migration_scripts__scripts_that_should_run', |
|
547 | - $scripts_that_should_run |
|
548 | - ); |
|
549 | - return $scripts_that_should_run; |
|
550 | - } |
|
551 | - |
|
552 | - |
|
553 | - /** |
|
554 | - * Gets the script which is currently being ran, if there is one. If $include_completed_scripts is set to TRUE |
|
555 | - * it will return the last ran script even if its complete. |
|
556 | - * This means: if you want to find the currently-executing script, leave it as FALSE. |
|
557 | - * If you really just want to find the script which ran most recently, regardless of status, leave it as TRUE. |
|
558 | - * |
|
559 | - * @param bool $include_completed_scripts |
|
560 | - * @return EE_Data_Migration_Script_Base |
|
561 | - */ |
|
562 | - public function get_last_ran_script($include_completed_scripts = false) |
|
563 | - { |
|
564 | - // make sure we've setup the class properties _last_ran_script and _last_ran_incomplete_script |
|
565 | - if (! $this->_data_migrations_ran) { |
|
566 | - $this->get_data_migrations_ran(); |
|
567 | - } |
|
568 | - if ($include_completed_scripts) { |
|
569 | - return $this->_last_ran_script; |
|
570 | - } else { |
|
571 | - return $this->_last_ran_incomplete_script; |
|
572 | - } |
|
573 | - } |
|
574 | - |
|
575 | - |
|
576 | - /** |
|
577 | - * Runs the data migration scripts (well, each request to this method calls one of the |
|
578 | - * data migration scripts' migration_step() functions). |
|
579 | - * |
|
580 | - * @param int $step_size |
|
581 | - * @throws EE_Error |
|
582 | - * @return array { |
|
583 | - * // where the first item is one EE_Data_Migration_Script_Base's stati, |
|
584 | - * //and the second item is a string describing what was done |
|
585 | - * @type int $records_to_migrate from the current migration script |
|
586 | - * @type int $records_migrated |
|
587 | - * @type string $status one of EE_Data_Migration_Manager::status_* |
|
588 | - * @type string $script verbose name of the current DMS |
|
589 | - * @type string $message string describing what was done during this step |
|
590 | - * } |
|
591 | - */ |
|
592 | - public function migration_step($step_size = 0) |
|
593 | - { |
|
594 | - |
|
595 | - // bandaid fix for issue https://events.codebasehq.com/projects/event-espresso/tickets/7535 |
|
596 | - if (class_exists('EE_CPT_Strategy')) { |
|
597 | - remove_action('pre_get_posts', array(EE_CPT_Strategy::instance(), 'pre_get_posts'), 5); |
|
598 | - } |
|
599 | - |
|
600 | - try { |
|
601 | - $currently_executing_script = $this->get_last_ran_script(); |
|
602 | - if (! $currently_executing_script) { |
|
603 | - // Find the next script that needs to execute |
|
604 | - $scripts = $this->check_for_applicable_data_migration_scripts(); |
|
605 | - if (! $scripts) { |
|
606 | - // huh, no more scripts to run... apparently we're done! |
|
607 | - // but dont forget to make sure initial data is there |
|
608 | - // we should be good to allow them to exit maintenance mode now |
|
609 | - EE_Maintenance_Mode::instance()->set_maintenance_level( |
|
610 | - intval(EE_Maintenance_Mode::level_0_not_in_maintenance) |
|
611 | - ); |
|
612 | - // saving migrations ran should actually be unnecessary, but leaving in place just in case |
|
613 | - // remember this migration was finished (even if we timeout initing db for core and plugins) |
|
614 | - $this->_save_migrations_ran(); |
|
615 | - // make sure DB was updated AFTER we've recorded the migration was done |
|
616 | - $this->initialize_db_for_enqueued_ee_plugins(); |
|
617 | - return array( |
|
618 | - 'records_to_migrate' => 1, |
|
619 | - 'records_migrated' => 1, |
|
620 | - 'status' => self::status_no_more_migration_scripts, |
|
621 | - 'script' => __("Data Migration Completed Successfully", "event_espresso"), |
|
622 | - 'message' => __("All done!", "event_espresso"), |
|
623 | - ); |
|
624 | - } |
|
625 | - $currently_executing_script = array_shift($scripts); |
|
626 | - // and add to the array/wp option showing the scripts ran |
|
627 | - |
|
628 | - $migrates_to = $this->script_migrates_to_version(get_class($currently_executing_script)); |
|
629 | - $plugin_slug = $migrates_to['slug']; |
|
630 | - $version = $migrates_to['version']; |
|
631 | - $this->_data_migrations_ran[ $plugin_slug ][ $version ] = $currently_executing_script; |
|
632 | - } |
|
633 | - $current_script_name = get_class($currently_executing_script); |
|
634 | - } catch (Exception $e) { |
|
635 | - // an exception occurred while trying to get migration scripts |
|
636 | - |
|
637 | - $message = sprintf( |
|
638 | - __("Error Message: %sStack Trace:%s", "event_espresso"), |
|
639 | - $e->getMessage() . '<br>', |
|
640 | - $e->getTraceAsString() |
|
641 | - ); |
|
642 | - // record it on the array of data migration scripts ran. This will be overwritten next time we try and try to run data migrations |
|
643 | - // but that's ok-- it's just an FYI to support that we couldn't even run any data migrations |
|
644 | - $this->add_error_to_migrations_ran( |
|
645 | - sprintf(__("Could not run data migrations because: %s", "event_espresso"), $message) |
|
646 | - ); |
|
647 | - return array( |
|
648 | - 'records_to_migrate' => 1, |
|
649 | - 'records_migrated' => 0, |
|
650 | - 'status' => self::status_fatal_error, |
|
651 | - 'script' => __("Error loading data migration scripts", "event_espresso"), |
|
652 | - 'message' => $message, |
|
653 | - ); |
|
654 | - } |
|
655 | - // ok so we definitely have a data migration script |
|
656 | - try { |
|
657 | - // how big of a bite do we want to take? Allow users to easily override via their wp-config |
|
658 | - if (absint($step_size) < 1) { |
|
659 | - $step_size = defined('EE_MIGRATION_STEP_SIZE') && absint(EE_MIGRATION_STEP_SIZE) |
|
660 | - ? EE_MIGRATION_STEP_SIZE : EE_Data_Migration_Manager::step_size; |
|
661 | - } |
|
662 | - // do what we came to do! |
|
663 | - $currently_executing_script->migration_step($step_size); |
|
664 | - // can we wrap it up and verify default data? |
|
665 | - $init_dbs = false; |
|
666 | - switch ($currently_executing_script->get_status()) { |
|
667 | - case EE_Data_Migration_Manager::status_continue: |
|
668 | - $response_array = array( |
|
669 | - 'records_to_migrate' => $currently_executing_script->count_records_to_migrate(), |
|
670 | - 'records_migrated' => $currently_executing_script->count_records_migrated(), |
|
671 | - 'status' => EE_Data_Migration_Manager::status_continue, |
|
672 | - 'message' => $currently_executing_script->get_feedback_message(), |
|
673 | - 'script' => $currently_executing_script->pretty_name(), |
|
674 | - ); |
|
675 | - break; |
|
676 | - case EE_Data_Migration_Manager::status_completed: |
|
677 | - // ok so THAT script has completed |
|
678 | - $this->update_current_database_state_to($this->script_migrates_to_version($current_script_name)); |
|
679 | - $response_array = array( |
|
680 | - 'records_to_migrate' => $currently_executing_script->count_records_to_migrate(), |
|
681 | - 'records_migrated' => $currently_executing_script->count_records_migrated(), |
|
682 | - 'status' => EE_Data_Migration_Manager::status_completed, |
|
683 | - 'message' => $currently_executing_script->get_feedback_message(), |
|
684 | - 'script' => sprintf( |
|
685 | - __("%s Completed", 'event_espresso'), |
|
686 | - $currently_executing_script->pretty_name() |
|
687 | - ), |
|
688 | - ); |
|
689 | - // check if there are any more after this one. |
|
690 | - $scripts_remaining = $this->check_for_applicable_data_migration_scripts(); |
|
691 | - if (! $scripts_remaining) { |
|
692 | - // we should be good to allow them to exit maintenance mode now |
|
693 | - EE_Maintenance_Mode::instance()->set_maintenance_level( |
|
694 | - intval(EE_Maintenance_Mode::level_0_not_in_maintenance) |
|
695 | - ); |
|
696 | - // huh, no more scripts to run... apparently we're done! |
|
697 | - // but dont forget to make sure initial data is there |
|
698 | - $init_dbs = true; |
|
699 | - $response_array['status'] = self::status_no_more_migration_scripts; |
|
700 | - } |
|
701 | - break; |
|
702 | - default: |
|
703 | - $response_array = array( |
|
704 | - 'records_to_migrate' => $currently_executing_script->count_records_to_migrate(), |
|
705 | - 'records_migrated' => $currently_executing_script->count_records_migrated(), |
|
706 | - 'status' => $currently_executing_script->get_status(), |
|
707 | - 'message' => sprintf( |
|
708 | - __("Minor errors occurred during %s: %s", "event_espresso"), |
|
709 | - $currently_executing_script->pretty_name(), |
|
710 | - implode(", ", $currently_executing_script->get_errors()) |
|
711 | - ), |
|
712 | - 'script' => $currently_executing_script->pretty_name(), |
|
713 | - ); |
|
714 | - break; |
|
715 | - } |
|
716 | - } catch (Exception $e) { |
|
717 | - // ok so some exception was thrown which killed the data migration script |
|
718 | - // double-check we have a real script |
|
719 | - if ($currently_executing_script instanceof EE_Data_Migration_Script_Base) { |
|
720 | - $script_name = $currently_executing_script->pretty_name(); |
|
721 | - $currently_executing_script->set_broken(); |
|
722 | - $currently_executing_script->add_error($e->getMessage()); |
|
723 | - } else { |
|
724 | - $script_name = __("Error getting Migration Script", "event_espresso"); |
|
725 | - } |
|
726 | - $response_array = array( |
|
727 | - 'records_to_migrate' => 1, |
|
728 | - 'records_migrated' => 0, |
|
729 | - 'status' => self::status_fatal_error, |
|
730 | - 'message' => sprintf( |
|
731 | - __("A fatal error occurred during the migration: %s", "event_espresso"), |
|
732 | - $e->getMessage() |
|
733 | - ), |
|
734 | - 'script' => $script_name, |
|
735 | - ); |
|
736 | - } |
|
737 | - $successful_save = $this->_save_migrations_ran(); |
|
738 | - if ($successful_save !== true) { |
|
739 | - // ok so the current wp option didn't save. that's tricky, because we'd like to update it |
|
740 | - // and mark it as having a fatal error, but remember- WE CAN'T SAVE THIS WP OPTION! |
|
741 | - // however, if we throw an exception, and return that, then the next request |
|
742 | - // won't have as much info in it, and it may be able to save |
|
743 | - throw new EE_Error( |
|
744 | - sprintf( |
|
745 | - __( |
|
746 | - "The error '%s' occurred updating the status of the migration. This is a FATAL ERROR, but the error is preventing the system from remembering that. Please contact event espresso support.", |
|
747 | - "event_espresso" |
|
748 | - ), |
|
749 | - $successful_save |
|
750 | - ) |
|
751 | - ); |
|
752 | - } |
|
753 | - // if we're all done, initialize EE plugins' default data etc. |
|
754 | - if ($init_dbs) { |
|
755 | - $this->initialize_db_for_enqueued_ee_plugins(); |
|
756 | - } |
|
757 | - return $response_array; |
|
758 | - } |
|
759 | - |
|
760 | - |
|
761 | - /** |
|
762 | - * Echo out JSON response to migration script AJAX requests. Takes precautions |
|
763 | - * to buffer output so that we don't throw junk into our json. |
|
764 | - * |
|
765 | - * @return array with keys: |
|
766 | - * 'records_to_migrate' which counts ALL the records for the current migration, and should remain constant. (ie, |
|
767 | - * it's NOT the count of hwo many remain) |
|
768 | - * 'records_migrated' which also counts ALL the records which have been migrated (ie, percent_complete = |
|
769 | - * records_migrated/records_to_migrate) |
|
770 | - * 'status'=>a string, one of EE_Data_migration_Manager::status_* |
|
771 | - * 'message'=>a string, containing any message you want to show to the user. We may decide to split this up into |
|
772 | - * errors, notifications, and successes |
|
773 | - * 'script'=>a pretty name of the script currently running |
|
774 | - */ |
|
775 | - public function response_to_migration_ajax_request() |
|
776 | - { |
|
777 | - ob_start(); |
|
778 | - try { |
|
779 | - $response = $this->migration_step(); |
|
780 | - } catch (Exception $e) { |
|
781 | - $response = array( |
|
782 | - 'records_to_migrate' => 0, |
|
783 | - 'records_migrated' => 0, |
|
784 | - 'status' => EE_Data_Migration_Manager::status_fatal_error, |
|
785 | - 'message' => sprintf( |
|
786 | - __("Unknown fatal error occurred: %s", "event_espresso"), |
|
787 | - $e->getMessage() |
|
788 | - ), |
|
789 | - 'script' => 'Unknown', |
|
790 | - ); |
|
791 | - $this->add_error_to_migrations_ran($e->getMessage() . "; Stack trace:" . $e->getTraceAsString()); |
|
792 | - } |
|
793 | - $warnings_etc = @ob_get_contents(); |
|
794 | - ob_end_clean(); |
|
795 | - $response['message'] .= $warnings_etc; |
|
796 | - return $response; |
|
797 | - } |
|
798 | - |
|
799 | - /** |
|
800 | - * Updates the wordpress option that keeps track of which which EE version the database |
|
801 | - * is at (ie, the code may be at 4.1.0, but the database is still at 3.1.35) |
|
802 | - * |
|
803 | - * @param array $slug_and_version { |
|
804 | - * @type string $slug like 'Core' or 'Calendar', |
|
805 | - * @type string $version like '4.1.0' |
|
806 | - * } |
|
807 | - * @return void |
|
808 | - */ |
|
809 | - public function update_current_database_state_to($slug_and_version = null) |
|
810 | - { |
|
811 | - if (! $slug_and_version) { |
|
812 | - // no version was provided, assume it should be at the current code version |
|
813 | - $slug_and_version = array('slug' => 'Core', 'version' => espresso_version()); |
|
814 | - } |
|
815 | - $current_database_state = get_option(self::current_database_state); |
|
816 | - $current_database_state[ $slug_and_version['slug'] ] = $slug_and_version['version']; |
|
817 | - update_option(self::current_database_state, $current_database_state); |
|
818 | - } |
|
819 | - |
|
820 | - /** |
|
821 | - * Determines if the database is currently at a state matching what's indicated in $slug and $version. |
|
822 | - * |
|
823 | - * @param array $slug_and_version { |
|
824 | - * @type string $slug like 'Core' or 'Calendar', |
|
825 | - * @type string $version like '4.1.0' |
|
826 | - * } |
|
827 | - * @return boolean |
|
828 | - */ |
|
829 | - public function database_needs_updating_to($slug_and_version) |
|
830 | - { |
|
831 | - |
|
832 | - $slug = $slug_and_version['slug']; |
|
833 | - $version = $slug_and_version['version']; |
|
834 | - $current_database_state = get_option(self::current_database_state); |
|
835 | - if (! isset($current_database_state[ $slug ])) { |
|
836 | - return true; |
|
837 | - } else { |
|
838 | - // just compare the first 3 parts of version string, eg "4.7.1", not "4.7.1.dev.032" because DBs shouldn't change on nano version changes |
|
839 | - $version_parts_current_db_state = array_slice(explode('.', $current_database_state[ $slug ]), 0, 3); |
|
840 | - $version_parts_of_provided_db_state = array_slice(explode('.', $version), 0, 3); |
|
841 | - $needs_updating = false; |
|
842 | - foreach ($version_parts_current_db_state as $offset => $version_part_in_current_db_state) { |
|
843 | - if ($version_part_in_current_db_state < $version_parts_of_provided_db_state[ $offset ]) { |
|
844 | - $needs_updating = true; |
|
845 | - break; |
|
846 | - } |
|
847 | - } |
|
848 | - return $needs_updating; |
|
849 | - } |
|
850 | - } |
|
851 | - |
|
852 | - |
|
853 | - /** |
|
854 | - * Gets all the data migration scripts available in the core folder and folders |
|
855 | - * in addons. Has the side effect of adding them for autoloading |
|
856 | - * |
|
857 | - * @return array keys are expected classnames, values are their filepaths |
|
858 | - * @throws InvalidInterfaceException |
|
859 | - * @throws InvalidDataTypeException |
|
860 | - * @throws EE_Error |
|
861 | - * @throws InvalidArgumentException |
|
862 | - */ |
|
863 | - public function get_all_data_migration_scripts_available() |
|
864 | - { |
|
865 | - if (! $this->_data_migration_class_to_filepath_map) { |
|
866 | - $this->_data_migration_class_to_filepath_map = array(); |
|
867 | - foreach ($this->get_data_migration_script_folders() as $eeAddonClass => $folder_path) { |
|
868 | - // strip any placeholders added to classname to make it a unique array key |
|
869 | - $eeAddonClass = trim($eeAddonClass, '*'); |
|
870 | - $eeAddonClass = $eeAddonClass === 'Core' || class_exists($eeAddonClass) |
|
871 | - ? $eeAddonClass |
|
872 | - : ''; |
|
873 | - $folder_path = EEH_File::end_with_directory_separator($folder_path); |
|
874 | - $files = glob($folder_path . '*.dms.php'); |
|
875 | - if (empty($files)) { |
|
876 | - continue; |
|
877 | - } |
|
878 | - foreach ($files as $file) { |
|
879 | - $pos_of_last_slash = strrpos($file, DS); |
|
880 | - $classname = str_replace('.dms.php', '', substr($file, $pos_of_last_slash + 1)); |
|
881 | - $migrates_to = $this->script_migrates_to_version($classname, $eeAddonClass); |
|
882 | - $slug = $migrates_to['slug']; |
|
883 | - // check that the slug as contained in the DMS is associated with |
|
884 | - // the slug of an addon or core |
|
885 | - if ($slug !== 'Core' && EE_Registry::instance()->get_addon_by_name($slug) === null) { |
|
886 | - EE_Error::doing_it_wrong( |
|
887 | - __FUNCTION__, |
|
888 | - sprintf( |
|
889 | - esc_html__( |
|
890 | - 'The data migration script "%s" migrates the "%s" data, but there is no EE addon with that name. There is only: %s. ', |
|
891 | - 'event_espresso' |
|
892 | - ), |
|
893 | - $classname, |
|
894 | - $slug, |
|
895 | - implode(', ', array_keys(EE_Registry::instance()->get_addons_by_name())) |
|
896 | - ), |
|
897 | - '4.3.0.alpha.019' |
|
898 | - ); |
|
899 | - } |
|
900 | - $this->_data_migration_class_to_filepath_map[ $classname ] = $file; |
|
901 | - } |
|
902 | - } |
|
903 | - EEH_Autoloader::register_autoloader($this->_data_migration_class_to_filepath_map); |
|
904 | - } |
|
905 | - return $this->_data_migration_class_to_filepath_map; |
|
906 | - } |
|
907 | - |
|
908 | - |
|
909 | - /** |
|
910 | - * Once we have an addon that works with EE4.1, we will actually want to fetch the PUE slugs |
|
911 | - * from each addon, and check if they need updating, |
|
912 | - * |
|
913 | - * @return boolean |
|
914 | - */ |
|
915 | - public function addons_need_updating() |
|
916 | - { |
|
917 | - return false; |
|
918 | - } |
|
919 | - |
|
920 | - /** |
|
921 | - * Adds this error string to the data_migrations_ran array, but we dont necessarily know |
|
922 | - * where to put it, so we just throw it in there... better than nothing... |
|
923 | - * |
|
924 | - * @param string $error_message |
|
925 | - * @throws EE_Error |
|
926 | - */ |
|
927 | - public function add_error_to_migrations_ran($error_message) |
|
928 | - { |
|
929 | - // get last-ran migration script |
|
930 | - global $wpdb; |
|
931 | - $last_migration_script_option = $wpdb->get_row( |
|
932 | - "SELECT * FROM $wpdb->options WHERE option_name like '" . EE_Data_Migration_Manager::data_migration_script_option_prefix . "%' ORDER BY option_id DESC LIMIT 1", |
|
933 | - ARRAY_A |
|
934 | - ); |
|
935 | - |
|
936 | - $last_ran_migration_script_properties = isset($last_migration_script_option['option_value']) |
|
937 | - ? maybe_unserialize($last_migration_script_option['option_value']) : null; |
|
938 | - // now, tread lightly because we're here because a FATAL non-catchable error |
|
939 | - // was thrown last time when we were trying to run a data migration script |
|
940 | - // so the fatal error could have happened while getting the migration script |
|
941 | - // or doing running it... |
|
942 | - $versions_migrated_to = isset($last_migration_script_option['option_name']) ? str_replace( |
|
943 | - EE_Data_Migration_Manager::data_migration_script_option_prefix, |
|
944 | - "", |
|
945 | - $last_migration_script_option['option_name'] |
|
946 | - ) : null; |
|
947 | - |
|
948 | - // check if it THINKS its a data migration script and especially if it's one that HASN'T finished yet |
|
949 | - // because if it has finished, then it obviously couldn't be the cause of this error, right? (because its all done) |
|
950 | - if (isset($last_ran_migration_script_properties['class']) && isset($last_ran_migration_script_properties['_status']) && $last_ran_migration_script_properties['_status'] != self::status_completed) { |
|
951 | - // ok then just add this error to its list of errors |
|
952 | - $last_ran_migration_script_properties['_errors'][] = $error_message; |
|
953 | - $last_ran_migration_script_properties['_status'] = self::status_fatal_error; |
|
954 | - } else { |
|
955 | - // so we don't even know which script was last running |
|
956 | - // use the data migration error stub, which is designed specifically for this type of thing |
|
957 | - $general_migration_error = new EE_DMS_Unknown_1_0_0(); |
|
958 | - $general_migration_error->add_error($error_message); |
|
959 | - $general_migration_error->set_broken(); |
|
960 | - $last_ran_migration_script_properties = $general_migration_error->properties_as_array(); |
|
961 | - $versions_migrated_to = 'Unknown.1.0.0'; |
|
962 | - // now just to make sure appears as last (in case the were previously a fatal error like this) |
|
963 | - // delete the old one |
|
964 | - delete_option(self::data_migration_script_option_prefix . $versions_migrated_to); |
|
965 | - } |
|
966 | - update_option( |
|
967 | - self::data_migration_script_option_prefix . $versions_migrated_to, |
|
968 | - $last_ran_migration_script_properties |
|
969 | - ); |
|
970 | - } |
|
971 | - |
|
972 | - /** |
|
973 | - * saves what data migrations have ran to the database |
|
974 | - * |
|
975 | - * @return mixed TRUE if successfully saved migrations ran, string if an error occurred |
|
976 | - */ |
|
977 | - protected function _save_migrations_ran() |
|
978 | - { |
|
979 | - if ($this->_data_migrations_ran == null) { |
|
980 | - $this->get_data_migrations_ran(); |
|
981 | - } |
|
982 | - // now, we don't want to save actual classes to the DB because that's messy |
|
983 | - $successful_updates = true; |
|
984 | - foreach ($this->_data_migrations_ran as $plugin_slug => $migrations_ran_for_plugin) { |
|
985 | - foreach ($migrations_ran_for_plugin as $version_string => $array_or_migration_obj) { |
|
986 | - $plugin_slug_for_use_in_option_name = $plugin_slug . "."; |
|
987 | - $option_name = self::data_migration_script_option_prefix . $plugin_slug_for_use_in_option_name . $version_string; |
|
988 | - $old_option_value = get_option($option_name); |
|
989 | - if ($array_or_migration_obj instanceof EE_Data_Migration_Script_Base) { |
|
990 | - $script_array_for_saving = $array_or_migration_obj->properties_as_array(); |
|
991 | - if ($old_option_value != $script_array_for_saving) { |
|
992 | - $successful_updates = update_option($option_name, $script_array_for_saving); |
|
993 | - } |
|
994 | - } else {// we don't know what this array-thing is. So just save it as-is |
|
995 | - if ($old_option_value != $array_or_migration_obj) { |
|
996 | - $successful_updates = update_option($option_name, $array_or_migration_obj); |
|
997 | - } |
|
998 | - } |
|
999 | - if (! $successful_updates) { |
|
1000 | - global $wpdb; |
|
1001 | - return $wpdb->last_error; |
|
1002 | - } |
|
1003 | - } |
|
1004 | - } |
|
1005 | - return true; |
|
1006 | - // $updated = update_option(self::data_migrations_option_name, $array_of_migrations); |
|
1007 | - // if ($updated !== true) { |
|
1008 | - // global $wpdb; |
|
1009 | - // return $wpdb->last_error; |
|
1010 | - // } else { |
|
1011 | - // return true; |
|
1012 | - // } |
|
1013 | - // wp_mail( |
|
1014 | - // "[email protected]", |
|
1015 | - // time() . " price debug info", |
|
1016 | - // "updated: $updated, last error: $last_error, byte length of option: " . strlen( |
|
1017 | - // serialize($array_of_migrations) |
|
1018 | - // ) |
|
1019 | - // ); |
|
1020 | - } |
|
1021 | - |
|
1022 | - /** |
|
1023 | - * Takes an array of data migration script properties and re-creates the class from |
|
1024 | - * them. The argument $properties_array is assumed to have been made by |
|
1025 | - * EE_Data_Migration_Script_Base::properties_as_array() |
|
1026 | - * |
|
1027 | - * @param array $properties_array |
|
1028 | - * @return EE_Data_Migration_Script_Base |
|
1029 | - * @throws EE_Error |
|
1030 | - */ |
|
1031 | - public function _instantiate_script_from_properties_array($properties_array) |
|
1032 | - { |
|
1033 | - if (! isset($properties_array['class'])) { |
|
1034 | - throw new EE_Error( |
|
1035 | - sprintf( |
|
1036 | - __("Properties array has no 'class' properties. Here's what it has: %s", "event_espresso"), |
|
1037 | - implode(",", $properties_array) |
|
1038 | - ) |
|
1039 | - ); |
|
1040 | - } |
|
1041 | - $class_name = $properties_array['class']; |
|
1042 | - if (! class_exists($class_name)) { |
|
1043 | - throw new EE_Error(sprintf(__("There is no migration script named %s", "event_espresso"), $class_name)); |
|
1044 | - } |
|
1045 | - $class = new $class_name; |
|
1046 | - if (! $class instanceof EE_Data_Migration_Script_Base) { |
|
1047 | - throw new EE_Error( |
|
1048 | - sprintf( |
|
1049 | - __("Class '%s' is supposed to be a migration script. Its not, its a '%s'", "event_espresso"), |
|
1050 | - $class_name, |
|
1051 | - get_class($class) |
|
1052 | - ) |
|
1053 | - ); |
|
1054 | - } |
|
1055 | - $class->instantiate_from_array_of_properties($properties_array); |
|
1056 | - return $class; |
|
1057 | - } |
|
1058 | - |
|
1059 | - /** |
|
1060 | - * Gets the classname for the most up-to-date DMS (ie, the one that will finally |
|
1061 | - * leave the DB in a state usable by the current plugin code). |
|
1062 | - * |
|
1063 | - * @param string $plugin_slug the slug for the ee plugin we are searching for. Default is 'Core' |
|
1064 | - * @return string |
|
1065 | - */ |
|
1066 | - public function get_most_up_to_date_dms($plugin_slug = 'Core') |
|
1067 | - { |
|
1068 | - $class_to_filepath_map = $this->get_all_data_migration_scripts_available(); |
|
1069 | - $most_up_to_date_dms_classname = null; |
|
1070 | - foreach ($class_to_filepath_map as $classname => $filepath) { |
|
1071 | - if ($most_up_to_date_dms_classname === null) { |
|
1072 | - $migrates_to = $this->script_migrates_to_version($classname); |
|
1073 | - $this_plugin_slug = $migrates_to['slug']; |
|
1074 | - if ($this_plugin_slug == $plugin_slug) { |
|
1075 | - // if it's for core, it wins |
|
1076 | - $most_up_to_date_dms_classname = $classname; |
|
1077 | - } |
|
1078 | - // if it wasn't for core, we must keep searching for one that is! |
|
1079 | - continue; |
|
1080 | - } else { |
|
1081 | - $champion_migrates_to = $this->script_migrates_to_version($most_up_to_date_dms_classname); |
|
1082 | - $contender_migrates_to = $this->script_migrates_to_version($classname); |
|
1083 | - if ($contender_migrates_to['slug'] == $plugin_slug |
|
1084 | - && version_compare( |
|
1085 | - $champion_migrates_to['version'], |
|
1086 | - $contender_migrates_to['version'], |
|
1087 | - '<' |
|
1088 | - )) { |
|
1089 | - // so the contenders version is higher and its for Core |
|
1090 | - $most_up_to_date_dms_classname = $classname; |
|
1091 | - } |
|
1092 | - } |
|
1093 | - } |
|
1094 | - return $most_up_to_date_dms_classname; |
|
1095 | - } |
|
1096 | - |
|
1097 | - /** |
|
1098 | - * Gets the migration script specified but ONLY if it has already ran. |
|
1099 | - * |
|
1100 | - * Eg, if you wanted to see if 'EE_DMS_Core_4_1_0' has ran, you would run the following code: |
|
1101 | - * <code> $core_4_1_0_dms_ran = EE_Data_Migration_Manager::instance()->get_migration_ran( '4.1.0', 'Core' ) !== |
|
1102 | - * NULL;</code> This is especially useful in addons' data migration scripts, this way they can tell if a core (or |
|
1103 | - * other addon) DMS has ran, in case the current DMS depends on it. |
|
1104 | - * |
|
1105 | - * @param string $version the version the DMS searched for migrates to. Usually just the content before the 3rd |
|
1106 | - * period. Eg '4.1.0' |
|
1107 | - * @param string $plugin_slug like 'Core', 'Mailchimp', 'Calendar', etc |
|
1108 | - * @return EE_Data_Migration_Script_Base |
|
1109 | - */ |
|
1110 | - public function get_migration_ran($version, $plugin_slug = 'Core') |
|
1111 | - { |
|
1112 | - $migrations_ran = $this->get_data_migrations_ran(); |
|
1113 | - if (isset($migrations_ran[ $plugin_slug ]) && isset($migrations_ran[ $plugin_slug ][ $version ])) { |
|
1114 | - return $migrations_ran[ $plugin_slug ][ $version ]; |
|
1115 | - } else { |
|
1116 | - return null; |
|
1117 | - } |
|
1118 | - } |
|
1119 | - |
|
1120 | - /** |
|
1121 | - * Resets the borked data migration scripts so they're no longer borked |
|
1122 | - * so we can again attempt to migrate |
|
1123 | - * |
|
1124 | - * @return bool |
|
1125 | - * @throws EE_Error |
|
1126 | - */ |
|
1127 | - public function reattempt() |
|
1128 | - { |
|
1129 | - // find if the last-ran script was borked |
|
1130 | - // set it as being non-borked (we shouldn't ever get DMSs that we don't recognize) |
|
1131 | - // add an 'error' saying that we attempted to reset |
|
1132 | - // does it have a stage that was borked too? if so make it no longer borked |
|
1133 | - // add an 'error' saying we attempted to reset |
|
1134 | - $last_ran_script = $this->get_last_ran_script(); |
|
1135 | - if ($last_ran_script instanceof EE_DMS_Unknown_1_0_0) { |
|
1136 | - // if it was an error DMS, just mark it as complete (if another error occurs it will overwrite it) |
|
1137 | - $last_ran_script->set_completed(); |
|
1138 | - } elseif ($last_ran_script instanceof EE_Data_Migration_Script_Base) { |
|
1139 | - $last_ran_script->reattempt(); |
|
1140 | - } else { |
|
1141 | - throw new EE_Error( |
|
1142 | - sprintf( |
|
1143 | - __( |
|
1144 | - 'Unable to reattempt the last ran migration script because it was not a valid migration script. || It was %s', |
|
1145 | - 'event_espresso' |
|
1146 | - ), |
|
1147 | - print_r($last_ran_script, true) |
|
1148 | - ) |
|
1149 | - ); |
|
1150 | - } |
|
1151 | - return $this->_save_migrations_ran(); |
|
1152 | - } |
|
1153 | - |
|
1154 | - /** |
|
1155 | - * Gets whether or not this particular migration has run or not |
|
1156 | - * |
|
1157 | - * @param string $version the version the DMS searched for migrates to. Usually just the content before the 3rd |
|
1158 | - * period. Eg '4.1.0' |
|
1159 | - * @param string $plugin_slug like 'Core', 'Mailchimp', 'Calendar', etc |
|
1160 | - * @return boolean |
|
1161 | - */ |
|
1162 | - public function migration_has_ran($version, $plugin_slug = 'Core') |
|
1163 | - { |
|
1164 | - return $this->get_migration_ran($version, $plugin_slug) !== null; |
|
1165 | - } |
|
1166 | - |
|
1167 | - /** |
|
1168 | - * Enqueues this ee plugin to have its data initialized |
|
1169 | - * |
|
1170 | - * @param string $plugin_slug either 'Core' or EE_Addon::name()'s return value |
|
1171 | - */ |
|
1172 | - public function enqueue_db_initialization_for($plugin_slug) |
|
1173 | - { |
|
1174 | - $queue = $this->get_db_initialization_queue(); |
|
1175 | - if (! in_array($plugin_slug, $queue)) { |
|
1176 | - $queue[] = $plugin_slug; |
|
1177 | - } |
|
1178 | - update_option(self::db_init_queue_option_name, $queue); |
|
1179 | - } |
|
1180 | - |
|
1181 | - /** |
|
1182 | - * Calls EE_Addon::initialize_db_if_no_migrations_required() on each addon |
|
1183 | - * specified in EE_Data_Migration_Manager::get_db_init_queue(), and if 'Core' is |
|
1184 | - * in the queue, calls EE_System::initialize_db_if_no_migrations_required(). |
|
1185 | - */ |
|
1186 | - public function initialize_db_for_enqueued_ee_plugins() |
|
1187 | - { |
|
1188 | - $queue = $this->get_db_initialization_queue(); |
|
1189 | - foreach ($queue as $plugin_slug) { |
|
1190 | - $most_up_to_date_dms = $this->get_most_up_to_date_dms($plugin_slug); |
|
1191 | - if (! $most_up_to_date_dms) { |
|
1192 | - // if there is NO DMS for this plugin, obviously there's no schema to verify anyways |
|
1193 | - $verify_db = false; |
|
1194 | - } else { |
|
1195 | - $most_up_to_date_dms_migrates_to = $this->script_migrates_to_version($most_up_to_date_dms); |
|
1196 | - $verify_db = $this->database_needs_updating_to($most_up_to_date_dms_migrates_to); |
|
1197 | - } |
|
1198 | - if ($plugin_slug == 'Core') { |
|
1199 | - EE_System::instance()->initialize_db_if_no_migrations_required( |
|
1200 | - false, |
|
1201 | - $verify_db |
|
1202 | - ); |
|
1203 | - } else { |
|
1204 | - // just loop through the addons to make sure their database is setup |
|
1205 | - foreach (EE_Registry::instance()->addons as $addon) { |
|
1206 | - if ($addon->name() == $plugin_slug) { |
|
1207 | - $addon->initialize_db_if_no_migrations_required($verify_db); |
|
1208 | - break; |
|
1209 | - } |
|
1210 | - } |
|
1211 | - } |
|
1212 | - } |
|
1213 | - // because we just initialized the DBs for the enqueued ee plugins |
|
1214 | - // we don't need to keep remembering which ones needed to be initialized |
|
1215 | - delete_option(self::db_init_queue_option_name); |
|
1216 | - } |
|
1217 | - |
|
1218 | - /** |
|
1219 | - * Gets a numerically-indexed array of plugin slugs that need to have their databases |
|
1220 | - * (re-)initialized after migrations are complete. ie, each element should be either |
|
1221 | - * 'Core', or the return value of EE_Addon::name() for an addon |
|
1222 | - * |
|
1223 | - * @return array |
|
1224 | - */ |
|
1225 | - public function get_db_initialization_queue() |
|
1226 | - { |
|
1227 | - return get_option(self::db_init_queue_option_name, array()); |
|
1228 | - } |
|
1229 | - |
|
1230 | - /** |
|
1231 | - * Gets the injected table analyzer, or throws an exception |
|
1232 | - * |
|
1233 | - * @return TableAnalysis |
|
1234 | - * @throws EE_Error |
|
1235 | - */ |
|
1236 | - protected function _get_table_analysis() |
|
1237 | - { |
|
1238 | - if ($this->_table_analysis instanceof TableAnalysis) { |
|
1239 | - return $this->_table_analysis; |
|
1240 | - } else { |
|
1241 | - throw new EE_Error( |
|
1242 | - sprintf( |
|
1243 | - __('Table analysis class on class %1$s is not set properly.', 'event_espresso'), |
|
1244 | - get_class($this) |
|
1245 | - ) |
|
1246 | - ); |
|
1247 | - } |
|
1248 | - } |
|
1249 | - |
|
1250 | - /** |
|
1251 | - * Gets the injected table manager, or throws an exception |
|
1252 | - * |
|
1253 | - * @return TableManager |
|
1254 | - * @throws EE_Error |
|
1255 | - */ |
|
1256 | - protected function _get_table_manager() |
|
1257 | - { |
|
1258 | - if ($this->_table_manager instanceof TableManager) { |
|
1259 | - return $this->_table_manager; |
|
1260 | - } else { |
|
1261 | - throw new EE_Error( |
|
1262 | - sprintf( |
|
1263 | - __('Table manager class on class %1$s is not set properly.', 'event_espresso'), |
|
1264 | - get_class($this) |
|
1265 | - ) |
|
1266 | - ); |
|
1267 | - } |
|
1268 | - } |
|
35 | + /** |
|
36 | + * |
|
37 | + * @var EE_Registry |
|
38 | + */ |
|
39 | + // protected $EE; |
|
40 | + /** |
|
41 | + * name of the wordpress option which stores an array of data about |
|
42 | + */ |
|
43 | + const data_migrations_option_name = 'ee_data_migration'; |
|
44 | + |
|
45 | + |
|
46 | + const data_migration_script_option_prefix = 'ee_data_migration_script_'; |
|
47 | + |
|
48 | + const data_migration_script_mapping_option_prefix = 'ee_dms_map_'; |
|
49 | + |
|
50 | + /** |
|
51 | + * name of the wordpress option which stores the database' current version. IE, the code may be at version 4.2.0, |
|
52 | + * but as migrations are performed the database will progress from 3.1.35 to 4.1.0 etc. |
|
53 | + */ |
|
54 | + const current_database_state = 'ee_data_migration_current_db_state'; |
|
55 | + |
|
56 | + /** |
|
57 | + * Special status string returned when we're positive there are no more data migration |
|
58 | + * scripts that can be run. |
|
59 | + */ |
|
60 | + const status_no_more_migration_scripts = 'no_more_migration_scripts'; |
|
61 | + /** |
|
62 | + * string indicating the migration should continue |
|
63 | + */ |
|
64 | + const status_continue = 'status_continue'; |
|
65 | + /** |
|
66 | + * string indicating the migration has completed and should be ended |
|
67 | + */ |
|
68 | + const status_completed = 'status_completed'; |
|
69 | + /** |
|
70 | + * string indicating a fatal error occurred and the data migration should be completely aborted |
|
71 | + */ |
|
72 | + const status_fatal_error = 'status_fatal_error'; |
|
73 | + |
|
74 | + /** |
|
75 | + * the number of 'items' (usually DB rows) to migrate on each 'step' (ajax request sent |
|
76 | + * during migration) |
|
77 | + */ |
|
78 | + const step_size = 50; |
|
79 | + |
|
80 | + /** |
|
81 | + * option name that stores the queue of ee plugins needing to have |
|
82 | + * their data initialized (or re-initialized) once we are done migrations |
|
83 | + */ |
|
84 | + const db_init_queue_option_name = 'ee_db_init_queue'; |
|
85 | + /** |
|
86 | + * Array of information concerning data migrations that have ran in the history |
|
87 | + * of this EE installation. Keys should be the name of the version the script upgraded to |
|
88 | + * |
|
89 | + * @var EE_Data_Migration_Script_Base[] |
|
90 | + */ |
|
91 | + private $_data_migrations_ran = null; |
|
92 | + /** |
|
93 | + * The last ran script. It's nice to store this somewhere accessible, as its easiest |
|
94 | + * to know which was the last run by which is the newest wp option; but in most of the code |
|
95 | + * we just use the local $_data_migration_ran array, which organized the scripts differently |
|
96 | + * |
|
97 | + * @var EE_Data_Migration_Script_Base |
|
98 | + */ |
|
99 | + private $_last_ran_script = null; |
|
100 | + |
|
101 | + /** |
|
102 | + * Similarly to _last_ran_script, but this is the last INCOMPLETE migration script. |
|
103 | + * |
|
104 | + * @var EE_Data_Migration_Script_Base |
|
105 | + */ |
|
106 | + private $_last_ran_incomplete_script = null; |
|
107 | + /** |
|
108 | + * array where keys are classnames, and values are filepaths of all the known migration scripts |
|
109 | + * |
|
110 | + * @var array |
|
111 | + */ |
|
112 | + private $_data_migration_class_to_filepath_map; |
|
113 | + |
|
114 | + /** |
|
115 | + * the following 4 properties are fully set on construction. |
|
116 | + * Note: the first two apply to whether to continue running ALL migration scripts (ie, even though we're finished |
|
117 | + * one, we may want to start the next one); whereas the last two indicate whether to continue running a single |
|
118 | + * data migration script |
|
119 | + * |
|
120 | + * @var array |
|
121 | + */ |
|
122 | + public $stati_that_indicate_to_continue_migrations = array(); |
|
123 | + |
|
124 | + public $stati_that_indicate_to_stop_migrations = array(); |
|
125 | + |
|
126 | + public $stati_that_indicate_to_continue_single_migration_script = array(); |
|
127 | + |
|
128 | + public $stati_that_indicate_to_stop_single_migration_script = array(); |
|
129 | + |
|
130 | + /** |
|
131 | + * @var \EventEspresso\core\services\database\TableManager $table_manager |
|
132 | + */ |
|
133 | + protected $_table_manager; |
|
134 | + |
|
135 | + /** |
|
136 | + * @var \EventEspresso\core\services\database\TableAnalysis $table_analysis |
|
137 | + */ |
|
138 | + protected $_table_analysis; |
|
139 | + |
|
140 | + /** |
|
141 | + * @var array $script_migration_versions |
|
142 | + */ |
|
143 | + protected $script_migration_versions; |
|
144 | + |
|
145 | + /** |
|
146 | + * @var EE_Data_Migration_Manager $_instance |
|
147 | + * @access private |
|
148 | + */ |
|
149 | + private static $_instance = null; |
|
150 | + |
|
151 | + |
|
152 | + /** |
|
153 | + * @singleton method used to instantiate class object |
|
154 | + * @access public |
|
155 | + * @return EE_Data_Migration_Manager instance |
|
156 | + */ |
|
157 | + public static function instance() |
|
158 | + { |
|
159 | + // check if class object is instantiated |
|
160 | + if (! self::$_instance instanceof EE_Data_Migration_Manager) { |
|
161 | + self::$_instance = new self(); |
|
162 | + } |
|
163 | + return self::$_instance; |
|
164 | + } |
|
165 | + |
|
166 | + /** |
|
167 | + * resets the singleton to its brand-new state (but does NOT delete old references to the old singleton. Meaning, |
|
168 | + * all new usages of the singleton should be made with Classname::instance()) and returns it |
|
169 | + * |
|
170 | + * @return EE_Data_Migration_Manager |
|
171 | + */ |
|
172 | + public static function reset() |
|
173 | + { |
|
174 | + self::$_instance = null; |
|
175 | + return self::instance(); |
|
176 | + } |
|
177 | + |
|
178 | + |
|
179 | + /** |
|
180 | + * constructor |
|
181 | + */ |
|
182 | + private function __construct() |
|
183 | + { |
|
184 | + $this->stati_that_indicate_to_continue_migrations = array( |
|
185 | + self::status_continue, |
|
186 | + self::status_completed, |
|
187 | + ); |
|
188 | + $this->stati_that_indicate_to_stop_migrations = array( |
|
189 | + self::status_fatal_error, |
|
190 | + self::status_no_more_migration_scripts, |
|
191 | + ); |
|
192 | + $this->stati_that_indicate_to_continue_single_migration_script = array( |
|
193 | + self::status_continue, |
|
194 | + ); |
|
195 | + $this->stati_that_indicate_to_stop_single_migration_script = array( |
|
196 | + self::status_completed, |
|
197 | + self::status_fatal_error |
|
198 | + // note: status_no_more_migration_scripts doesn't apply |
|
199 | + ); |
|
200 | + // make sure we've included the base migration script, because we may need the EE_DMS_Unknown_1_0_0 class |
|
201 | + // to be defined, because right now it doesn't get autoloaded on its own |
|
202 | + EE_Registry::instance()->load_core('Data_Migration_Class_Base', array(), true); |
|
203 | + EE_Registry::instance()->load_core('Data_Migration_Script_Base', array(), true); |
|
204 | + EE_Registry::instance()->load_core('DMS_Unknown_1_0_0', array(), true); |
|
205 | + EE_Registry::instance()->load_core('Data_Migration_Script_Stage', array(), true); |
|
206 | + EE_Registry::instance()->load_core('Data_Migration_Script_Stage_Table', array(), true); |
|
207 | + $this->_table_manager = EE_Registry::instance()->create('TableManager', array(), true); |
|
208 | + $this->_table_analysis = EE_Registry::instance()->create('TableAnalysis', array(), true); |
|
209 | + } |
|
210 | + |
|
211 | + |
|
212 | + /** |
|
213 | + * Deciphers, from an option's name, what plugin and version it relates to (see _save_migrations_ran to see what |
|
214 | + * the option names are like, but generally they're like |
|
215 | + * 'ee_data_migration_script_Core.4.1.0' in 4.2 or 'ee_data_migration_script_4.1.0' before that). |
|
216 | + * The option name shouldn't ever be like 'ee_data_migration_script_Core.4.1.0.reg' because it's derived, |
|
217 | + * indirectly, from the data migration's classname, which should always be like EE_DMS_%s_%d_%d_%d.dms.php (eg |
|
218 | + * EE_DMS_Core_4_1_0.dms.php) |
|
219 | + * |
|
220 | + * @param string $option_name (see EE_Data_Migration_Manage::_save_migrations_ran() where the option name is set) |
|
221 | + * @return array where the first item is the plugin slug (eg 'Core','Calendar',etc) and the 2nd is the version of |
|
222 | + * that plugin (eg '4.1.0') |
|
223 | + */ |
|
224 | + private function _get_plugin_slug_and_version_string_from_dms_option_name($option_name) |
|
225 | + { |
|
226 | + $plugin_slug_and_version_string = str_replace( |
|
227 | + EE_Data_Migration_Manager::data_migration_script_option_prefix, |
|
228 | + "", |
|
229 | + $option_name |
|
230 | + ); |
|
231 | + // check if $plugin_slug_and_version_string is like '4.1.0' (4.1-style) or 'Core.4.1.0' (4.2-style) |
|
232 | + $parts = explode(".", $plugin_slug_and_version_string); |
|
233 | + |
|
234 | + if (count($parts) == 4) { |
|
235 | + // it's 4.2-style.eg Core.4.1.0 |
|
236 | + $plugin_slug = $parts[0];// eg Core |
|
237 | + $version_string = $parts[1] . "." . $parts[2] . "." . $parts[3]; // eg 4.1.0 |
|
238 | + } else { |
|
239 | + // it's 4.1-style: eg 4.1.0 |
|
240 | + $plugin_slug = 'Core'; |
|
241 | + $version_string = $plugin_slug_and_version_string;// eg 4.1.0 |
|
242 | + } |
|
243 | + return array($plugin_slug, $version_string); |
|
244 | + } |
|
245 | + |
|
246 | + /** |
|
247 | + * Gets the DMS class from the wordpress option, otherwise throws an EE_Error if it's not |
|
248 | + * for a known DMS class. |
|
249 | + * |
|
250 | + * @param string $dms_option_name |
|
251 | + * @param string $dms_option_value (serialized) |
|
252 | + * @return EE_Data_Migration_Script_Base |
|
253 | + * @throws EE_Error |
|
254 | + */ |
|
255 | + private function _get_dms_class_from_wp_option($dms_option_name, $dms_option_value) |
|
256 | + { |
|
257 | + $data_migration_data = maybe_unserialize($dms_option_value); |
|
258 | + if (isset($data_migration_data['class']) && class_exists($data_migration_data['class'])) { |
|
259 | + $class = new $data_migration_data['class']; |
|
260 | + if ($class instanceof EE_Data_Migration_Script_Base) { |
|
261 | + $class->instantiate_from_array_of_properties($data_migration_data); |
|
262 | + return $class; |
|
263 | + } else { |
|
264 | + // huh, so its an object but not a data migration script?? that shouldn't happen |
|
265 | + // just leave it as an array (which will probably just get ignored) |
|
266 | + throw new EE_Error( |
|
267 | + sprintf( |
|
268 | + __( |
|
269 | + "Trying to retrieve DMS class from wp option. No DMS by the name '%s' exists", |
|
270 | + 'event_espresso' |
|
271 | + ), |
|
272 | + $data_migration_data['class'] |
|
273 | + ) |
|
274 | + ); |
|
275 | + } |
|
276 | + } else { |
|
277 | + // so the data doesn't specify a class. So it must either be a legacy array of info or some array (which we'll probably just ignore), or a class that no longer exists |
|
278 | + throw new EE_Error( |
|
279 | + sprintf(__("The wp option with key '%s' does not represent a DMS", 'event_espresso'), $dms_option_name) |
|
280 | + ); |
|
281 | + } |
|
282 | + } |
|
283 | + |
|
284 | + /** |
|
285 | + * Gets the array describing what data migrations have run. Also has a side-effect of recording which was the last |
|
286 | + * ran, and which was the last ran which hasn't finished yet |
|
287 | + * |
|
288 | + * @return array where each element should be an array of EE_Data_Migration_Script_Base (but also has a few legacy |
|
289 | + * arrays in there - which should probably be ignored) |
|
290 | + */ |
|
291 | + public function get_data_migrations_ran() |
|
292 | + { |
|
293 | + if (! $this->_data_migrations_ran) { |
|
294 | + // setup autoloaders for each of the scripts in there |
|
295 | + $this->get_all_data_migration_scripts_available(); |
|
296 | + $data_migrations_options = $this->get_all_migration_script_options( |
|
297 | + );// get_option(EE_Data_Migration_Manager::data_migrations_option_name,get_option('espresso_data_migrations',array())); |
|
298 | + |
|
299 | + $data_migrations_ran = array(); |
|
300 | + // convert into data migration script classes where possible |
|
301 | + foreach ($data_migrations_options as $data_migration_option) { |
|
302 | + list($plugin_slug, $version_string) = $this->_get_plugin_slug_and_version_string_from_dms_option_name( |
|
303 | + $data_migration_option['option_name'] |
|
304 | + ); |
|
305 | + |
|
306 | + try { |
|
307 | + $class = $this->_get_dms_class_from_wp_option( |
|
308 | + $data_migration_option['option_name'], |
|
309 | + $data_migration_option['option_value'] |
|
310 | + ); |
|
311 | + $data_migrations_ran[ $plugin_slug ][ $version_string ] = $class; |
|
312 | + // ok so far THIS is the 'last-ran-script'... unless we find another on next iteration |
|
313 | + $this->_last_ran_script = $class; |
|
314 | + if (! $class->is_completed()) { |
|
315 | + // sometimes we also like to know which was the last incomplete script (or if there are any at all) |
|
316 | + $this->_last_ran_incomplete_script = $class; |
|
317 | + } |
|
318 | + } catch (EE_Error $e) { |
|
319 | + // ok so its not a DMS. We'll just keep it, although other code will need to expect non-DMSs |
|
320 | + $data_migrations_ran[ $plugin_slug ][ $version_string ] = maybe_unserialize( |
|
321 | + $data_migration_option['option_value'] |
|
322 | + ); |
|
323 | + } |
|
324 | + } |
|
325 | + // so here the array of $data_migrations_ran is actually a mix of classes and a few legacy arrays |
|
326 | + $this->_data_migrations_ran = $data_migrations_ran; |
|
327 | + if (! $this->_data_migrations_ran || ! is_array($this->_data_migrations_ran)) { |
|
328 | + $this->_data_migrations_ran = array(); |
|
329 | + } |
|
330 | + } |
|
331 | + return $this->_data_migrations_ran; |
|
332 | + } |
|
333 | + |
|
334 | + |
|
335 | + /** |
|
336 | + * |
|
337 | + * @param string $script_name eg 'DMS_Core_4_1_0' |
|
338 | + * @param string $old_table eg 'wp_events_detail' |
|
339 | + * @param string $old_pk eg 'wp_esp_posts' |
|
340 | + * @param $new_table |
|
341 | + * @return mixed string or int |
|
342 | + */ |
|
343 | + public function get_mapping_new_pk($script_name, $old_table, $old_pk, $new_table) |
|
344 | + { |
|
345 | + $script = EE_Registry::instance()->load_dms($script_name); |
|
346 | + $mapping = $script->get_mapping_new_pk($old_table, $old_pk, $new_table); |
|
347 | + return $mapping; |
|
348 | + } |
|
349 | + |
|
350 | + /** |
|
351 | + * Gets all the options containing migration scripts that have been run. Ordering is important: it's assumed that |
|
352 | + * the last option returned in this array is the most-recently ran DMS option |
|
353 | + * |
|
354 | + * @return array |
|
355 | + */ |
|
356 | + public function get_all_migration_script_options() |
|
357 | + { |
|
358 | + global $wpdb; |
|
359 | + return $wpdb->get_results( |
|
360 | + "SELECT * FROM {$wpdb->options} WHERE option_name like '" . EE_Data_Migration_Manager::data_migration_script_option_prefix . "%' ORDER BY option_id ASC", |
|
361 | + ARRAY_A |
|
362 | + ); |
|
363 | + } |
|
364 | + |
|
365 | + /** |
|
366 | + * Gets the array of folders which contain data migration scripts. Also adds them to be auto-loaded |
|
367 | + * |
|
368 | + * @return array where each value is the full folder path of a folder containing data migration scripts, WITH |
|
369 | + * slashes at the end of the folder name. |
|
370 | + */ |
|
371 | + public function get_data_migration_script_folders() |
|
372 | + { |
|
373 | + return apply_filters( |
|
374 | + 'FHEE__EE_Data_Migration_Manager__get_data_migration_script_folders', |
|
375 | + array('Core' => EE_CORE . 'data_migration_scripts') |
|
376 | + ); |
|
377 | + } |
|
378 | + |
|
379 | + /** |
|
380 | + * Gets the version the migration script upgrades to |
|
381 | + * |
|
382 | + * @param string $migration_script_name eg 'EE_DMS_Core_4_1_0' |
|
383 | + * @return array { |
|
384 | + * @type string $slug like 'Core','Calendar',etc |
|
385 | + * @type string $version like 4.3.0 |
|
386 | + * } |
|
387 | + * @throws EE_Error |
|
388 | + */ |
|
389 | + public function script_migrates_to_version($migration_script_name, $eeAddonClass = '') |
|
390 | + { |
|
391 | + if (isset($this->script_migration_versions[ $migration_script_name ])) { |
|
392 | + return $this->script_migration_versions[ $migration_script_name ]; |
|
393 | + } |
|
394 | + $dms_info = $this->parse_dms_classname($migration_script_name); |
|
395 | + $this->script_migration_versions[ $migration_script_name ] = array( |
|
396 | + 'slug' => $eeAddonClass !== '' ? $eeAddonClass : $dms_info['slug'], |
|
397 | + 'version' => $dms_info['major_version'] . "." . $dms_info['minor_version'] . "." . $dms_info['micro_version'], |
|
398 | + ); |
|
399 | + return $this->script_migration_versions[ $migration_script_name ]; |
|
400 | + } |
|
401 | + |
|
402 | + /** |
|
403 | + * Gets the juicy details out of a dms filename like 'EE_DMS_Core_4_1_0' |
|
404 | + * |
|
405 | + * @param string $classname |
|
406 | + * @return array with keys 'slug','major_version','minor_version', and 'micro_version' (the last 3 are ints) |
|
407 | + * @throws EE_Error |
|
408 | + */ |
|
409 | + public function parse_dms_classname($classname) |
|
410 | + { |
|
411 | + $matches = array(); |
|
412 | + preg_match('~EE_DMS_(.*)_([0-9]*)_([0-9]*)_([0-9]*)~', $classname, $matches); |
|
413 | + if (! $matches || ! (isset($matches[1]) && isset($matches[2]) && isset($matches[3]))) { |
|
414 | + throw new EE_Error( |
|
415 | + sprintf( |
|
416 | + __( |
|
417 | + "%s is not a valid Data Migration Script. The classname should be like EE_DMS_w_x_y_z, where w is either 'Core' or the slug of an addon and x, y and z are numbers, ", |
|
418 | + "event_espresso" |
|
419 | + ), |
|
420 | + $classname |
|
421 | + ) |
|
422 | + ); |
|
423 | + } |
|
424 | + return array( |
|
425 | + 'slug' => $matches[1], |
|
426 | + 'major_version' => intval($matches[2]), |
|
427 | + 'minor_version' => intval($matches[3]), |
|
428 | + 'micro_version' => intval($matches[4]), |
|
429 | + ); |
|
430 | + } |
|
431 | + |
|
432 | + /** |
|
433 | + * Ensures that the option indicating the current DB version is set. This should only be |
|
434 | + * a concern when activating EE for the first time, THEORETICALLY. |
|
435 | + * If we detect that we're activating EE4 over top of EE3.1, then we set the current db state to 3.1.x, otherwise |
|
436 | + * to 4.1.x. |
|
437 | + * |
|
438 | + * @return string of current db state |
|
439 | + */ |
|
440 | + public function ensure_current_database_state_is_set() |
|
441 | + { |
|
442 | + $espresso_db_core_updates = get_option('espresso_db_update', array()); |
|
443 | + $db_state = get_option(EE_Data_Migration_Manager::current_database_state); |
|
444 | + if (! $db_state) { |
|
445 | + // mark the DB as being in the state as the last version in there. |
|
446 | + // this is done to trigger maintenance mode and do data migration scripts |
|
447 | + // if the admin installed this version of EE over 3.1.x or 4.0.x |
|
448 | + // otherwise, the normal maintenance mode code is fine |
|
449 | + $previous_versions_installed = array_keys($espresso_db_core_updates); |
|
450 | + $previous_version_installed = end($previous_versions_installed); |
|
451 | + if (version_compare('4.1.0', $previous_version_installed)) { |
|
452 | + // last installed version was less than 4.1 |
|
453 | + // so we want the data migrations to happen. SO, we're going to say the DB is at that state |
|
454 | + $db_state = array('Core' => $previous_version_installed); |
|
455 | + } else { |
|
456 | + $db_state = array('Core' => EVENT_ESPRESSO_VERSION); |
|
457 | + } |
|
458 | + update_option(EE_Data_Migration_Manager::current_database_state, $db_state); |
|
459 | + } |
|
460 | + // in 4.1, $db_state would have only been a simple string like '4.1.0', |
|
461 | + // but in 4.2+ it should be an array with at least key 'Core' and the value of that plugin's |
|
462 | + // db, and possibly other keys for other addons like 'Calendar','Permissions',etc |
|
463 | + if (! is_array($db_state)) { |
|
464 | + $db_state = array('Core' => $db_state); |
|
465 | + update_option(EE_Data_Migration_Manager::current_database_state, $db_state); |
|
466 | + } |
|
467 | + return $db_state; |
|
468 | + } |
|
469 | + |
|
470 | + /** |
|
471 | + * Checks if there are any data migration scripts that ought to be run. If found, |
|
472 | + * returns the instantiated classes. If none are found (ie, they've all already been run |
|
473 | + * or they don't apply), returns an empty array |
|
474 | + * |
|
475 | + * @return EE_Data_Migration_Script_Base[] |
|
476 | + */ |
|
477 | + public function check_for_applicable_data_migration_scripts() |
|
478 | + { |
|
479 | + // get the option describing what options have already run |
|
480 | + $scripts_ran = $this->get_data_migrations_ran(); |
|
481 | + // $scripts_ran = array('4.1.0.core'=>array('monkey'=>null)); |
|
482 | + $script_class_and_filepaths_available = $this->get_all_data_migration_scripts_available(); |
|
483 | + |
|
484 | + |
|
485 | + $current_database_state = $this->ensure_current_database_state_is_set(); |
|
486 | + // determine which have already been run |
|
487 | + $script_classes_that_should_run_per_iteration = array(); |
|
488 | + $iteration = 0; |
|
489 | + $next_database_state_to_consider = $current_database_state; |
|
490 | + $theoretical_database_state = null; |
|
491 | + do { |
|
492 | + // the next state after the currently-considered one will start off looking the same as the current, but we may make additions... |
|
493 | + $theoretical_database_state = $next_database_state_to_consider; |
|
494 | + // the next db state to consider is "what would the DB be like had we run all the scripts we found that applied last time?) |
|
495 | + foreach ($script_class_and_filepaths_available as $classname => $filepath) { |
|
496 | + $migrates_to_version = $this->script_migrates_to_version($classname); |
|
497 | + $script_converts_plugin_slug = $migrates_to_version['slug']; |
|
498 | + $script_converts_to_version = $migrates_to_version['version']; |
|
499 | + // check if this version script is DONE or not; or if it's never been ran |
|
500 | + if (! $scripts_ran || |
|
501 | + ! isset($scripts_ran[ $script_converts_plugin_slug ]) || |
|
502 | + ! isset($scripts_ran[ $script_converts_plugin_slug ][ $script_converts_to_version ])) { |
|
503 | + // we haven't ran this conversion script before |
|
504 | + // now check if it applies... note that we've added an autoloader for it on get_all_data_migration_scripts_available |
|
505 | + $script = new $classname($this->_get_table_manager(), $this->_get_table_analysis()); |
|
506 | + /* @var $script EE_Data_Migration_Script_Base */ |
|
507 | + $can_migrate = $script->can_migrate_from_version($theoretical_database_state); |
|
508 | + if ($can_migrate) { |
|
509 | + $script_classes_that_should_run_per_iteration[ $iteration ][ $script->priority() ][] = $script; |
|
510 | + $migrates_to_version = $script->migrates_to_version(); |
|
511 | + $next_database_state_to_consider[ $migrates_to_version['slug'] ] = $migrates_to_version['version']; |
|
512 | + unset($script_class_and_filepaths_available[ $classname ]); |
|
513 | + } |
|
514 | + } elseif ($scripts_ran[ $script_converts_plugin_slug ][ $script_converts_to_version ] instanceof EE_Data_Migration_Script_Base) { |
|
515 | + // this script has been ran, or at least started |
|
516 | + $script = $scripts_ran[ $script_converts_plugin_slug ][ $script_converts_to_version ]; |
|
517 | + if ($script->get_status() != self::status_completed) { |
|
518 | + // this script is already underway... keep going with it |
|
519 | + $script_classes_that_should_run_per_iteration[ $iteration ][ $script->priority() ][] = $script; |
|
520 | + $migrates_to_version = $script->migrates_to_version(); |
|
521 | + $next_database_state_to_consider[ $migrates_to_version['slug'] ] = $migrates_to_version['version']; |
|
522 | + unset($script_class_and_filepaths_available[ $classname ]); |
|
523 | + } else { |
|
524 | + // it must have a status that indicates it has finished, so we don't want to try and run it again |
|
525 | + } |
|
526 | + } else { |
|
527 | + // it exists but it's not a proper data migration script |
|
528 | + // maybe the script got renamed? or was simply removed from EE? |
|
529 | + // either way, its certainly not runnable! |
|
530 | + } |
|
531 | + } |
|
532 | + $iteration++; |
|
533 | + } while ($next_database_state_to_consider != $theoretical_database_state && $iteration < 6); |
|
534 | + // ok we have all the scripts that should run, now let's make them into flat array |
|
535 | + $scripts_that_should_run = array(); |
|
536 | + foreach ($script_classes_that_should_run_per_iteration as $scripts_at_priority) { |
|
537 | + ksort($scripts_at_priority); |
|
538 | + foreach ($scripts_at_priority as $scripts) { |
|
539 | + foreach ($scripts as $script) { |
|
540 | + $scripts_that_should_run[ get_class($script) ] = $script; |
|
541 | + } |
|
542 | + } |
|
543 | + } |
|
544 | + |
|
545 | + do_action( |
|
546 | + 'AHEE__EE_Data_Migration_Manager__check_for_applicable_data_migration_scripts__scripts_that_should_run', |
|
547 | + $scripts_that_should_run |
|
548 | + ); |
|
549 | + return $scripts_that_should_run; |
|
550 | + } |
|
551 | + |
|
552 | + |
|
553 | + /** |
|
554 | + * Gets the script which is currently being ran, if there is one. If $include_completed_scripts is set to TRUE |
|
555 | + * it will return the last ran script even if its complete. |
|
556 | + * This means: if you want to find the currently-executing script, leave it as FALSE. |
|
557 | + * If you really just want to find the script which ran most recently, regardless of status, leave it as TRUE. |
|
558 | + * |
|
559 | + * @param bool $include_completed_scripts |
|
560 | + * @return EE_Data_Migration_Script_Base |
|
561 | + */ |
|
562 | + public function get_last_ran_script($include_completed_scripts = false) |
|
563 | + { |
|
564 | + // make sure we've setup the class properties _last_ran_script and _last_ran_incomplete_script |
|
565 | + if (! $this->_data_migrations_ran) { |
|
566 | + $this->get_data_migrations_ran(); |
|
567 | + } |
|
568 | + if ($include_completed_scripts) { |
|
569 | + return $this->_last_ran_script; |
|
570 | + } else { |
|
571 | + return $this->_last_ran_incomplete_script; |
|
572 | + } |
|
573 | + } |
|
574 | + |
|
575 | + |
|
576 | + /** |
|
577 | + * Runs the data migration scripts (well, each request to this method calls one of the |
|
578 | + * data migration scripts' migration_step() functions). |
|
579 | + * |
|
580 | + * @param int $step_size |
|
581 | + * @throws EE_Error |
|
582 | + * @return array { |
|
583 | + * // where the first item is one EE_Data_Migration_Script_Base's stati, |
|
584 | + * //and the second item is a string describing what was done |
|
585 | + * @type int $records_to_migrate from the current migration script |
|
586 | + * @type int $records_migrated |
|
587 | + * @type string $status one of EE_Data_Migration_Manager::status_* |
|
588 | + * @type string $script verbose name of the current DMS |
|
589 | + * @type string $message string describing what was done during this step |
|
590 | + * } |
|
591 | + */ |
|
592 | + public function migration_step($step_size = 0) |
|
593 | + { |
|
594 | + |
|
595 | + // bandaid fix for issue https://events.codebasehq.com/projects/event-espresso/tickets/7535 |
|
596 | + if (class_exists('EE_CPT_Strategy')) { |
|
597 | + remove_action('pre_get_posts', array(EE_CPT_Strategy::instance(), 'pre_get_posts'), 5); |
|
598 | + } |
|
599 | + |
|
600 | + try { |
|
601 | + $currently_executing_script = $this->get_last_ran_script(); |
|
602 | + if (! $currently_executing_script) { |
|
603 | + // Find the next script that needs to execute |
|
604 | + $scripts = $this->check_for_applicable_data_migration_scripts(); |
|
605 | + if (! $scripts) { |
|
606 | + // huh, no more scripts to run... apparently we're done! |
|
607 | + // but dont forget to make sure initial data is there |
|
608 | + // we should be good to allow them to exit maintenance mode now |
|
609 | + EE_Maintenance_Mode::instance()->set_maintenance_level( |
|
610 | + intval(EE_Maintenance_Mode::level_0_not_in_maintenance) |
|
611 | + ); |
|
612 | + // saving migrations ran should actually be unnecessary, but leaving in place just in case |
|
613 | + // remember this migration was finished (even if we timeout initing db for core and plugins) |
|
614 | + $this->_save_migrations_ran(); |
|
615 | + // make sure DB was updated AFTER we've recorded the migration was done |
|
616 | + $this->initialize_db_for_enqueued_ee_plugins(); |
|
617 | + return array( |
|
618 | + 'records_to_migrate' => 1, |
|
619 | + 'records_migrated' => 1, |
|
620 | + 'status' => self::status_no_more_migration_scripts, |
|
621 | + 'script' => __("Data Migration Completed Successfully", "event_espresso"), |
|
622 | + 'message' => __("All done!", "event_espresso"), |
|
623 | + ); |
|
624 | + } |
|
625 | + $currently_executing_script = array_shift($scripts); |
|
626 | + // and add to the array/wp option showing the scripts ran |
|
627 | + |
|
628 | + $migrates_to = $this->script_migrates_to_version(get_class($currently_executing_script)); |
|
629 | + $plugin_slug = $migrates_to['slug']; |
|
630 | + $version = $migrates_to['version']; |
|
631 | + $this->_data_migrations_ran[ $plugin_slug ][ $version ] = $currently_executing_script; |
|
632 | + } |
|
633 | + $current_script_name = get_class($currently_executing_script); |
|
634 | + } catch (Exception $e) { |
|
635 | + // an exception occurred while trying to get migration scripts |
|
636 | + |
|
637 | + $message = sprintf( |
|
638 | + __("Error Message: %sStack Trace:%s", "event_espresso"), |
|
639 | + $e->getMessage() . '<br>', |
|
640 | + $e->getTraceAsString() |
|
641 | + ); |
|
642 | + // record it on the array of data migration scripts ran. This will be overwritten next time we try and try to run data migrations |
|
643 | + // but that's ok-- it's just an FYI to support that we couldn't even run any data migrations |
|
644 | + $this->add_error_to_migrations_ran( |
|
645 | + sprintf(__("Could not run data migrations because: %s", "event_espresso"), $message) |
|
646 | + ); |
|
647 | + return array( |
|
648 | + 'records_to_migrate' => 1, |
|
649 | + 'records_migrated' => 0, |
|
650 | + 'status' => self::status_fatal_error, |
|
651 | + 'script' => __("Error loading data migration scripts", "event_espresso"), |
|
652 | + 'message' => $message, |
|
653 | + ); |
|
654 | + } |
|
655 | + // ok so we definitely have a data migration script |
|
656 | + try { |
|
657 | + // how big of a bite do we want to take? Allow users to easily override via their wp-config |
|
658 | + if (absint($step_size) < 1) { |
|
659 | + $step_size = defined('EE_MIGRATION_STEP_SIZE') && absint(EE_MIGRATION_STEP_SIZE) |
|
660 | + ? EE_MIGRATION_STEP_SIZE : EE_Data_Migration_Manager::step_size; |
|
661 | + } |
|
662 | + // do what we came to do! |
|
663 | + $currently_executing_script->migration_step($step_size); |
|
664 | + // can we wrap it up and verify default data? |
|
665 | + $init_dbs = false; |
|
666 | + switch ($currently_executing_script->get_status()) { |
|
667 | + case EE_Data_Migration_Manager::status_continue: |
|
668 | + $response_array = array( |
|
669 | + 'records_to_migrate' => $currently_executing_script->count_records_to_migrate(), |
|
670 | + 'records_migrated' => $currently_executing_script->count_records_migrated(), |
|
671 | + 'status' => EE_Data_Migration_Manager::status_continue, |
|
672 | + 'message' => $currently_executing_script->get_feedback_message(), |
|
673 | + 'script' => $currently_executing_script->pretty_name(), |
|
674 | + ); |
|
675 | + break; |
|
676 | + case EE_Data_Migration_Manager::status_completed: |
|
677 | + // ok so THAT script has completed |
|
678 | + $this->update_current_database_state_to($this->script_migrates_to_version($current_script_name)); |
|
679 | + $response_array = array( |
|
680 | + 'records_to_migrate' => $currently_executing_script->count_records_to_migrate(), |
|
681 | + 'records_migrated' => $currently_executing_script->count_records_migrated(), |
|
682 | + 'status' => EE_Data_Migration_Manager::status_completed, |
|
683 | + 'message' => $currently_executing_script->get_feedback_message(), |
|
684 | + 'script' => sprintf( |
|
685 | + __("%s Completed", 'event_espresso'), |
|
686 | + $currently_executing_script->pretty_name() |
|
687 | + ), |
|
688 | + ); |
|
689 | + // check if there are any more after this one. |
|
690 | + $scripts_remaining = $this->check_for_applicable_data_migration_scripts(); |
|
691 | + if (! $scripts_remaining) { |
|
692 | + // we should be good to allow them to exit maintenance mode now |
|
693 | + EE_Maintenance_Mode::instance()->set_maintenance_level( |
|
694 | + intval(EE_Maintenance_Mode::level_0_not_in_maintenance) |
|
695 | + ); |
|
696 | + // huh, no more scripts to run... apparently we're done! |
|
697 | + // but dont forget to make sure initial data is there |
|
698 | + $init_dbs = true; |
|
699 | + $response_array['status'] = self::status_no_more_migration_scripts; |
|
700 | + } |
|
701 | + break; |
|
702 | + default: |
|
703 | + $response_array = array( |
|
704 | + 'records_to_migrate' => $currently_executing_script->count_records_to_migrate(), |
|
705 | + 'records_migrated' => $currently_executing_script->count_records_migrated(), |
|
706 | + 'status' => $currently_executing_script->get_status(), |
|
707 | + 'message' => sprintf( |
|
708 | + __("Minor errors occurred during %s: %s", "event_espresso"), |
|
709 | + $currently_executing_script->pretty_name(), |
|
710 | + implode(", ", $currently_executing_script->get_errors()) |
|
711 | + ), |
|
712 | + 'script' => $currently_executing_script->pretty_name(), |
|
713 | + ); |
|
714 | + break; |
|
715 | + } |
|
716 | + } catch (Exception $e) { |
|
717 | + // ok so some exception was thrown which killed the data migration script |
|
718 | + // double-check we have a real script |
|
719 | + if ($currently_executing_script instanceof EE_Data_Migration_Script_Base) { |
|
720 | + $script_name = $currently_executing_script->pretty_name(); |
|
721 | + $currently_executing_script->set_broken(); |
|
722 | + $currently_executing_script->add_error($e->getMessage()); |
|
723 | + } else { |
|
724 | + $script_name = __("Error getting Migration Script", "event_espresso"); |
|
725 | + } |
|
726 | + $response_array = array( |
|
727 | + 'records_to_migrate' => 1, |
|
728 | + 'records_migrated' => 0, |
|
729 | + 'status' => self::status_fatal_error, |
|
730 | + 'message' => sprintf( |
|
731 | + __("A fatal error occurred during the migration: %s", "event_espresso"), |
|
732 | + $e->getMessage() |
|
733 | + ), |
|
734 | + 'script' => $script_name, |
|
735 | + ); |
|
736 | + } |
|
737 | + $successful_save = $this->_save_migrations_ran(); |
|
738 | + if ($successful_save !== true) { |
|
739 | + // ok so the current wp option didn't save. that's tricky, because we'd like to update it |
|
740 | + // and mark it as having a fatal error, but remember- WE CAN'T SAVE THIS WP OPTION! |
|
741 | + // however, if we throw an exception, and return that, then the next request |
|
742 | + // won't have as much info in it, and it may be able to save |
|
743 | + throw new EE_Error( |
|
744 | + sprintf( |
|
745 | + __( |
|
746 | + "The error '%s' occurred updating the status of the migration. This is a FATAL ERROR, but the error is preventing the system from remembering that. Please contact event espresso support.", |
|
747 | + "event_espresso" |
|
748 | + ), |
|
749 | + $successful_save |
|
750 | + ) |
|
751 | + ); |
|
752 | + } |
|
753 | + // if we're all done, initialize EE plugins' default data etc. |
|
754 | + if ($init_dbs) { |
|
755 | + $this->initialize_db_for_enqueued_ee_plugins(); |
|
756 | + } |
|
757 | + return $response_array; |
|
758 | + } |
|
759 | + |
|
760 | + |
|
761 | + /** |
|
762 | + * Echo out JSON response to migration script AJAX requests. Takes precautions |
|
763 | + * to buffer output so that we don't throw junk into our json. |
|
764 | + * |
|
765 | + * @return array with keys: |
|
766 | + * 'records_to_migrate' which counts ALL the records for the current migration, and should remain constant. (ie, |
|
767 | + * it's NOT the count of hwo many remain) |
|
768 | + * 'records_migrated' which also counts ALL the records which have been migrated (ie, percent_complete = |
|
769 | + * records_migrated/records_to_migrate) |
|
770 | + * 'status'=>a string, one of EE_Data_migration_Manager::status_* |
|
771 | + * 'message'=>a string, containing any message you want to show to the user. We may decide to split this up into |
|
772 | + * errors, notifications, and successes |
|
773 | + * 'script'=>a pretty name of the script currently running |
|
774 | + */ |
|
775 | + public function response_to_migration_ajax_request() |
|
776 | + { |
|
777 | + ob_start(); |
|
778 | + try { |
|
779 | + $response = $this->migration_step(); |
|
780 | + } catch (Exception $e) { |
|
781 | + $response = array( |
|
782 | + 'records_to_migrate' => 0, |
|
783 | + 'records_migrated' => 0, |
|
784 | + 'status' => EE_Data_Migration_Manager::status_fatal_error, |
|
785 | + 'message' => sprintf( |
|
786 | + __("Unknown fatal error occurred: %s", "event_espresso"), |
|
787 | + $e->getMessage() |
|
788 | + ), |
|
789 | + 'script' => 'Unknown', |
|
790 | + ); |
|
791 | + $this->add_error_to_migrations_ran($e->getMessage() . "; Stack trace:" . $e->getTraceAsString()); |
|
792 | + } |
|
793 | + $warnings_etc = @ob_get_contents(); |
|
794 | + ob_end_clean(); |
|
795 | + $response['message'] .= $warnings_etc; |
|
796 | + return $response; |
|
797 | + } |
|
798 | + |
|
799 | + /** |
|
800 | + * Updates the wordpress option that keeps track of which which EE version the database |
|
801 | + * is at (ie, the code may be at 4.1.0, but the database is still at 3.1.35) |
|
802 | + * |
|
803 | + * @param array $slug_and_version { |
|
804 | + * @type string $slug like 'Core' or 'Calendar', |
|
805 | + * @type string $version like '4.1.0' |
|
806 | + * } |
|
807 | + * @return void |
|
808 | + */ |
|
809 | + public function update_current_database_state_to($slug_and_version = null) |
|
810 | + { |
|
811 | + if (! $slug_and_version) { |
|
812 | + // no version was provided, assume it should be at the current code version |
|
813 | + $slug_and_version = array('slug' => 'Core', 'version' => espresso_version()); |
|
814 | + } |
|
815 | + $current_database_state = get_option(self::current_database_state); |
|
816 | + $current_database_state[ $slug_and_version['slug'] ] = $slug_and_version['version']; |
|
817 | + update_option(self::current_database_state, $current_database_state); |
|
818 | + } |
|
819 | + |
|
820 | + /** |
|
821 | + * Determines if the database is currently at a state matching what's indicated in $slug and $version. |
|
822 | + * |
|
823 | + * @param array $slug_and_version { |
|
824 | + * @type string $slug like 'Core' or 'Calendar', |
|
825 | + * @type string $version like '4.1.0' |
|
826 | + * } |
|
827 | + * @return boolean |
|
828 | + */ |
|
829 | + public function database_needs_updating_to($slug_and_version) |
|
830 | + { |
|
831 | + |
|
832 | + $slug = $slug_and_version['slug']; |
|
833 | + $version = $slug_and_version['version']; |
|
834 | + $current_database_state = get_option(self::current_database_state); |
|
835 | + if (! isset($current_database_state[ $slug ])) { |
|
836 | + return true; |
|
837 | + } else { |
|
838 | + // just compare the first 3 parts of version string, eg "4.7.1", not "4.7.1.dev.032" because DBs shouldn't change on nano version changes |
|
839 | + $version_parts_current_db_state = array_slice(explode('.', $current_database_state[ $slug ]), 0, 3); |
|
840 | + $version_parts_of_provided_db_state = array_slice(explode('.', $version), 0, 3); |
|
841 | + $needs_updating = false; |
|
842 | + foreach ($version_parts_current_db_state as $offset => $version_part_in_current_db_state) { |
|
843 | + if ($version_part_in_current_db_state < $version_parts_of_provided_db_state[ $offset ]) { |
|
844 | + $needs_updating = true; |
|
845 | + break; |
|
846 | + } |
|
847 | + } |
|
848 | + return $needs_updating; |
|
849 | + } |
|
850 | + } |
|
851 | + |
|
852 | + |
|
853 | + /** |
|
854 | + * Gets all the data migration scripts available in the core folder and folders |
|
855 | + * in addons. Has the side effect of adding them for autoloading |
|
856 | + * |
|
857 | + * @return array keys are expected classnames, values are their filepaths |
|
858 | + * @throws InvalidInterfaceException |
|
859 | + * @throws InvalidDataTypeException |
|
860 | + * @throws EE_Error |
|
861 | + * @throws InvalidArgumentException |
|
862 | + */ |
|
863 | + public function get_all_data_migration_scripts_available() |
|
864 | + { |
|
865 | + if (! $this->_data_migration_class_to_filepath_map) { |
|
866 | + $this->_data_migration_class_to_filepath_map = array(); |
|
867 | + foreach ($this->get_data_migration_script_folders() as $eeAddonClass => $folder_path) { |
|
868 | + // strip any placeholders added to classname to make it a unique array key |
|
869 | + $eeAddonClass = trim($eeAddonClass, '*'); |
|
870 | + $eeAddonClass = $eeAddonClass === 'Core' || class_exists($eeAddonClass) |
|
871 | + ? $eeAddonClass |
|
872 | + : ''; |
|
873 | + $folder_path = EEH_File::end_with_directory_separator($folder_path); |
|
874 | + $files = glob($folder_path . '*.dms.php'); |
|
875 | + if (empty($files)) { |
|
876 | + continue; |
|
877 | + } |
|
878 | + foreach ($files as $file) { |
|
879 | + $pos_of_last_slash = strrpos($file, DS); |
|
880 | + $classname = str_replace('.dms.php', '', substr($file, $pos_of_last_slash + 1)); |
|
881 | + $migrates_to = $this->script_migrates_to_version($classname, $eeAddonClass); |
|
882 | + $slug = $migrates_to['slug']; |
|
883 | + // check that the slug as contained in the DMS is associated with |
|
884 | + // the slug of an addon or core |
|
885 | + if ($slug !== 'Core' && EE_Registry::instance()->get_addon_by_name($slug) === null) { |
|
886 | + EE_Error::doing_it_wrong( |
|
887 | + __FUNCTION__, |
|
888 | + sprintf( |
|
889 | + esc_html__( |
|
890 | + 'The data migration script "%s" migrates the "%s" data, but there is no EE addon with that name. There is only: %s. ', |
|
891 | + 'event_espresso' |
|
892 | + ), |
|
893 | + $classname, |
|
894 | + $slug, |
|
895 | + implode(', ', array_keys(EE_Registry::instance()->get_addons_by_name())) |
|
896 | + ), |
|
897 | + '4.3.0.alpha.019' |
|
898 | + ); |
|
899 | + } |
|
900 | + $this->_data_migration_class_to_filepath_map[ $classname ] = $file; |
|
901 | + } |
|
902 | + } |
|
903 | + EEH_Autoloader::register_autoloader($this->_data_migration_class_to_filepath_map); |
|
904 | + } |
|
905 | + return $this->_data_migration_class_to_filepath_map; |
|
906 | + } |
|
907 | + |
|
908 | + |
|
909 | + /** |
|
910 | + * Once we have an addon that works with EE4.1, we will actually want to fetch the PUE slugs |
|
911 | + * from each addon, and check if they need updating, |
|
912 | + * |
|
913 | + * @return boolean |
|
914 | + */ |
|
915 | + public function addons_need_updating() |
|
916 | + { |
|
917 | + return false; |
|
918 | + } |
|
919 | + |
|
920 | + /** |
|
921 | + * Adds this error string to the data_migrations_ran array, but we dont necessarily know |
|
922 | + * where to put it, so we just throw it in there... better than nothing... |
|
923 | + * |
|
924 | + * @param string $error_message |
|
925 | + * @throws EE_Error |
|
926 | + */ |
|
927 | + public function add_error_to_migrations_ran($error_message) |
|
928 | + { |
|
929 | + // get last-ran migration script |
|
930 | + global $wpdb; |
|
931 | + $last_migration_script_option = $wpdb->get_row( |
|
932 | + "SELECT * FROM $wpdb->options WHERE option_name like '" . EE_Data_Migration_Manager::data_migration_script_option_prefix . "%' ORDER BY option_id DESC LIMIT 1", |
|
933 | + ARRAY_A |
|
934 | + ); |
|
935 | + |
|
936 | + $last_ran_migration_script_properties = isset($last_migration_script_option['option_value']) |
|
937 | + ? maybe_unserialize($last_migration_script_option['option_value']) : null; |
|
938 | + // now, tread lightly because we're here because a FATAL non-catchable error |
|
939 | + // was thrown last time when we were trying to run a data migration script |
|
940 | + // so the fatal error could have happened while getting the migration script |
|
941 | + // or doing running it... |
|
942 | + $versions_migrated_to = isset($last_migration_script_option['option_name']) ? str_replace( |
|
943 | + EE_Data_Migration_Manager::data_migration_script_option_prefix, |
|
944 | + "", |
|
945 | + $last_migration_script_option['option_name'] |
|
946 | + ) : null; |
|
947 | + |
|
948 | + // check if it THINKS its a data migration script and especially if it's one that HASN'T finished yet |
|
949 | + // because if it has finished, then it obviously couldn't be the cause of this error, right? (because its all done) |
|
950 | + if (isset($last_ran_migration_script_properties['class']) && isset($last_ran_migration_script_properties['_status']) && $last_ran_migration_script_properties['_status'] != self::status_completed) { |
|
951 | + // ok then just add this error to its list of errors |
|
952 | + $last_ran_migration_script_properties['_errors'][] = $error_message; |
|
953 | + $last_ran_migration_script_properties['_status'] = self::status_fatal_error; |
|
954 | + } else { |
|
955 | + // so we don't even know which script was last running |
|
956 | + // use the data migration error stub, which is designed specifically for this type of thing |
|
957 | + $general_migration_error = new EE_DMS_Unknown_1_0_0(); |
|
958 | + $general_migration_error->add_error($error_message); |
|
959 | + $general_migration_error->set_broken(); |
|
960 | + $last_ran_migration_script_properties = $general_migration_error->properties_as_array(); |
|
961 | + $versions_migrated_to = 'Unknown.1.0.0'; |
|
962 | + // now just to make sure appears as last (in case the were previously a fatal error like this) |
|
963 | + // delete the old one |
|
964 | + delete_option(self::data_migration_script_option_prefix . $versions_migrated_to); |
|
965 | + } |
|
966 | + update_option( |
|
967 | + self::data_migration_script_option_prefix . $versions_migrated_to, |
|
968 | + $last_ran_migration_script_properties |
|
969 | + ); |
|
970 | + } |
|
971 | + |
|
972 | + /** |
|
973 | + * saves what data migrations have ran to the database |
|
974 | + * |
|
975 | + * @return mixed TRUE if successfully saved migrations ran, string if an error occurred |
|
976 | + */ |
|
977 | + protected function _save_migrations_ran() |
|
978 | + { |
|
979 | + if ($this->_data_migrations_ran == null) { |
|
980 | + $this->get_data_migrations_ran(); |
|
981 | + } |
|
982 | + // now, we don't want to save actual classes to the DB because that's messy |
|
983 | + $successful_updates = true; |
|
984 | + foreach ($this->_data_migrations_ran as $plugin_slug => $migrations_ran_for_plugin) { |
|
985 | + foreach ($migrations_ran_for_plugin as $version_string => $array_or_migration_obj) { |
|
986 | + $plugin_slug_for_use_in_option_name = $plugin_slug . "."; |
|
987 | + $option_name = self::data_migration_script_option_prefix . $plugin_slug_for_use_in_option_name . $version_string; |
|
988 | + $old_option_value = get_option($option_name); |
|
989 | + if ($array_or_migration_obj instanceof EE_Data_Migration_Script_Base) { |
|
990 | + $script_array_for_saving = $array_or_migration_obj->properties_as_array(); |
|
991 | + if ($old_option_value != $script_array_for_saving) { |
|
992 | + $successful_updates = update_option($option_name, $script_array_for_saving); |
|
993 | + } |
|
994 | + } else {// we don't know what this array-thing is. So just save it as-is |
|
995 | + if ($old_option_value != $array_or_migration_obj) { |
|
996 | + $successful_updates = update_option($option_name, $array_or_migration_obj); |
|
997 | + } |
|
998 | + } |
|
999 | + if (! $successful_updates) { |
|
1000 | + global $wpdb; |
|
1001 | + return $wpdb->last_error; |
|
1002 | + } |
|
1003 | + } |
|
1004 | + } |
|
1005 | + return true; |
|
1006 | + // $updated = update_option(self::data_migrations_option_name, $array_of_migrations); |
|
1007 | + // if ($updated !== true) { |
|
1008 | + // global $wpdb; |
|
1009 | + // return $wpdb->last_error; |
|
1010 | + // } else { |
|
1011 | + // return true; |
|
1012 | + // } |
|
1013 | + // wp_mail( |
|
1014 | + // "[email protected]", |
|
1015 | + // time() . " price debug info", |
|
1016 | + // "updated: $updated, last error: $last_error, byte length of option: " . strlen( |
|
1017 | + // serialize($array_of_migrations) |
|
1018 | + // ) |
|
1019 | + // ); |
|
1020 | + } |
|
1021 | + |
|
1022 | + /** |
|
1023 | + * Takes an array of data migration script properties and re-creates the class from |
|
1024 | + * them. The argument $properties_array is assumed to have been made by |
|
1025 | + * EE_Data_Migration_Script_Base::properties_as_array() |
|
1026 | + * |
|
1027 | + * @param array $properties_array |
|
1028 | + * @return EE_Data_Migration_Script_Base |
|
1029 | + * @throws EE_Error |
|
1030 | + */ |
|
1031 | + public function _instantiate_script_from_properties_array($properties_array) |
|
1032 | + { |
|
1033 | + if (! isset($properties_array['class'])) { |
|
1034 | + throw new EE_Error( |
|
1035 | + sprintf( |
|
1036 | + __("Properties array has no 'class' properties. Here's what it has: %s", "event_espresso"), |
|
1037 | + implode(",", $properties_array) |
|
1038 | + ) |
|
1039 | + ); |
|
1040 | + } |
|
1041 | + $class_name = $properties_array['class']; |
|
1042 | + if (! class_exists($class_name)) { |
|
1043 | + throw new EE_Error(sprintf(__("There is no migration script named %s", "event_espresso"), $class_name)); |
|
1044 | + } |
|
1045 | + $class = new $class_name; |
|
1046 | + if (! $class instanceof EE_Data_Migration_Script_Base) { |
|
1047 | + throw new EE_Error( |
|
1048 | + sprintf( |
|
1049 | + __("Class '%s' is supposed to be a migration script. Its not, its a '%s'", "event_espresso"), |
|
1050 | + $class_name, |
|
1051 | + get_class($class) |
|
1052 | + ) |
|
1053 | + ); |
|
1054 | + } |
|
1055 | + $class->instantiate_from_array_of_properties($properties_array); |
|
1056 | + return $class; |
|
1057 | + } |
|
1058 | + |
|
1059 | + /** |
|
1060 | + * Gets the classname for the most up-to-date DMS (ie, the one that will finally |
|
1061 | + * leave the DB in a state usable by the current plugin code). |
|
1062 | + * |
|
1063 | + * @param string $plugin_slug the slug for the ee plugin we are searching for. Default is 'Core' |
|
1064 | + * @return string |
|
1065 | + */ |
|
1066 | + public function get_most_up_to_date_dms($plugin_slug = 'Core') |
|
1067 | + { |
|
1068 | + $class_to_filepath_map = $this->get_all_data_migration_scripts_available(); |
|
1069 | + $most_up_to_date_dms_classname = null; |
|
1070 | + foreach ($class_to_filepath_map as $classname => $filepath) { |
|
1071 | + if ($most_up_to_date_dms_classname === null) { |
|
1072 | + $migrates_to = $this->script_migrates_to_version($classname); |
|
1073 | + $this_plugin_slug = $migrates_to['slug']; |
|
1074 | + if ($this_plugin_slug == $plugin_slug) { |
|
1075 | + // if it's for core, it wins |
|
1076 | + $most_up_to_date_dms_classname = $classname; |
|
1077 | + } |
|
1078 | + // if it wasn't for core, we must keep searching for one that is! |
|
1079 | + continue; |
|
1080 | + } else { |
|
1081 | + $champion_migrates_to = $this->script_migrates_to_version($most_up_to_date_dms_classname); |
|
1082 | + $contender_migrates_to = $this->script_migrates_to_version($classname); |
|
1083 | + if ($contender_migrates_to['slug'] == $plugin_slug |
|
1084 | + && version_compare( |
|
1085 | + $champion_migrates_to['version'], |
|
1086 | + $contender_migrates_to['version'], |
|
1087 | + '<' |
|
1088 | + )) { |
|
1089 | + // so the contenders version is higher and its for Core |
|
1090 | + $most_up_to_date_dms_classname = $classname; |
|
1091 | + } |
|
1092 | + } |
|
1093 | + } |
|
1094 | + return $most_up_to_date_dms_classname; |
|
1095 | + } |
|
1096 | + |
|
1097 | + /** |
|
1098 | + * Gets the migration script specified but ONLY if it has already ran. |
|
1099 | + * |
|
1100 | + * Eg, if you wanted to see if 'EE_DMS_Core_4_1_0' has ran, you would run the following code: |
|
1101 | + * <code> $core_4_1_0_dms_ran = EE_Data_Migration_Manager::instance()->get_migration_ran( '4.1.0', 'Core' ) !== |
|
1102 | + * NULL;</code> This is especially useful in addons' data migration scripts, this way they can tell if a core (or |
|
1103 | + * other addon) DMS has ran, in case the current DMS depends on it. |
|
1104 | + * |
|
1105 | + * @param string $version the version the DMS searched for migrates to. Usually just the content before the 3rd |
|
1106 | + * period. Eg '4.1.0' |
|
1107 | + * @param string $plugin_slug like 'Core', 'Mailchimp', 'Calendar', etc |
|
1108 | + * @return EE_Data_Migration_Script_Base |
|
1109 | + */ |
|
1110 | + public function get_migration_ran($version, $plugin_slug = 'Core') |
|
1111 | + { |
|
1112 | + $migrations_ran = $this->get_data_migrations_ran(); |
|
1113 | + if (isset($migrations_ran[ $plugin_slug ]) && isset($migrations_ran[ $plugin_slug ][ $version ])) { |
|
1114 | + return $migrations_ran[ $plugin_slug ][ $version ]; |
|
1115 | + } else { |
|
1116 | + return null; |
|
1117 | + } |
|
1118 | + } |
|
1119 | + |
|
1120 | + /** |
|
1121 | + * Resets the borked data migration scripts so they're no longer borked |
|
1122 | + * so we can again attempt to migrate |
|
1123 | + * |
|
1124 | + * @return bool |
|
1125 | + * @throws EE_Error |
|
1126 | + */ |
|
1127 | + public function reattempt() |
|
1128 | + { |
|
1129 | + // find if the last-ran script was borked |
|
1130 | + // set it as being non-borked (we shouldn't ever get DMSs that we don't recognize) |
|
1131 | + // add an 'error' saying that we attempted to reset |
|
1132 | + // does it have a stage that was borked too? if so make it no longer borked |
|
1133 | + // add an 'error' saying we attempted to reset |
|
1134 | + $last_ran_script = $this->get_last_ran_script(); |
|
1135 | + if ($last_ran_script instanceof EE_DMS_Unknown_1_0_0) { |
|
1136 | + // if it was an error DMS, just mark it as complete (if another error occurs it will overwrite it) |
|
1137 | + $last_ran_script->set_completed(); |
|
1138 | + } elseif ($last_ran_script instanceof EE_Data_Migration_Script_Base) { |
|
1139 | + $last_ran_script->reattempt(); |
|
1140 | + } else { |
|
1141 | + throw new EE_Error( |
|
1142 | + sprintf( |
|
1143 | + __( |
|
1144 | + 'Unable to reattempt the last ran migration script because it was not a valid migration script. || It was %s', |
|
1145 | + 'event_espresso' |
|
1146 | + ), |
|
1147 | + print_r($last_ran_script, true) |
|
1148 | + ) |
|
1149 | + ); |
|
1150 | + } |
|
1151 | + return $this->_save_migrations_ran(); |
|
1152 | + } |
|
1153 | + |
|
1154 | + /** |
|
1155 | + * Gets whether or not this particular migration has run or not |
|
1156 | + * |
|
1157 | + * @param string $version the version the DMS searched for migrates to. Usually just the content before the 3rd |
|
1158 | + * period. Eg '4.1.0' |
|
1159 | + * @param string $plugin_slug like 'Core', 'Mailchimp', 'Calendar', etc |
|
1160 | + * @return boolean |
|
1161 | + */ |
|
1162 | + public function migration_has_ran($version, $plugin_slug = 'Core') |
|
1163 | + { |
|
1164 | + return $this->get_migration_ran($version, $plugin_slug) !== null; |
|
1165 | + } |
|
1166 | + |
|
1167 | + /** |
|
1168 | + * Enqueues this ee plugin to have its data initialized |
|
1169 | + * |
|
1170 | + * @param string $plugin_slug either 'Core' or EE_Addon::name()'s return value |
|
1171 | + */ |
|
1172 | + public function enqueue_db_initialization_for($plugin_slug) |
|
1173 | + { |
|
1174 | + $queue = $this->get_db_initialization_queue(); |
|
1175 | + if (! in_array($plugin_slug, $queue)) { |
|
1176 | + $queue[] = $plugin_slug; |
|
1177 | + } |
|
1178 | + update_option(self::db_init_queue_option_name, $queue); |
|
1179 | + } |
|
1180 | + |
|
1181 | + /** |
|
1182 | + * Calls EE_Addon::initialize_db_if_no_migrations_required() on each addon |
|
1183 | + * specified in EE_Data_Migration_Manager::get_db_init_queue(), and if 'Core' is |
|
1184 | + * in the queue, calls EE_System::initialize_db_if_no_migrations_required(). |
|
1185 | + */ |
|
1186 | + public function initialize_db_for_enqueued_ee_plugins() |
|
1187 | + { |
|
1188 | + $queue = $this->get_db_initialization_queue(); |
|
1189 | + foreach ($queue as $plugin_slug) { |
|
1190 | + $most_up_to_date_dms = $this->get_most_up_to_date_dms($plugin_slug); |
|
1191 | + if (! $most_up_to_date_dms) { |
|
1192 | + // if there is NO DMS for this plugin, obviously there's no schema to verify anyways |
|
1193 | + $verify_db = false; |
|
1194 | + } else { |
|
1195 | + $most_up_to_date_dms_migrates_to = $this->script_migrates_to_version($most_up_to_date_dms); |
|
1196 | + $verify_db = $this->database_needs_updating_to($most_up_to_date_dms_migrates_to); |
|
1197 | + } |
|
1198 | + if ($plugin_slug == 'Core') { |
|
1199 | + EE_System::instance()->initialize_db_if_no_migrations_required( |
|
1200 | + false, |
|
1201 | + $verify_db |
|
1202 | + ); |
|
1203 | + } else { |
|
1204 | + // just loop through the addons to make sure their database is setup |
|
1205 | + foreach (EE_Registry::instance()->addons as $addon) { |
|
1206 | + if ($addon->name() == $plugin_slug) { |
|
1207 | + $addon->initialize_db_if_no_migrations_required($verify_db); |
|
1208 | + break; |
|
1209 | + } |
|
1210 | + } |
|
1211 | + } |
|
1212 | + } |
|
1213 | + // because we just initialized the DBs for the enqueued ee plugins |
|
1214 | + // we don't need to keep remembering which ones needed to be initialized |
|
1215 | + delete_option(self::db_init_queue_option_name); |
|
1216 | + } |
|
1217 | + |
|
1218 | + /** |
|
1219 | + * Gets a numerically-indexed array of plugin slugs that need to have their databases |
|
1220 | + * (re-)initialized after migrations are complete. ie, each element should be either |
|
1221 | + * 'Core', or the return value of EE_Addon::name() for an addon |
|
1222 | + * |
|
1223 | + * @return array |
|
1224 | + */ |
|
1225 | + public function get_db_initialization_queue() |
|
1226 | + { |
|
1227 | + return get_option(self::db_init_queue_option_name, array()); |
|
1228 | + } |
|
1229 | + |
|
1230 | + /** |
|
1231 | + * Gets the injected table analyzer, or throws an exception |
|
1232 | + * |
|
1233 | + * @return TableAnalysis |
|
1234 | + * @throws EE_Error |
|
1235 | + */ |
|
1236 | + protected function _get_table_analysis() |
|
1237 | + { |
|
1238 | + if ($this->_table_analysis instanceof TableAnalysis) { |
|
1239 | + return $this->_table_analysis; |
|
1240 | + } else { |
|
1241 | + throw new EE_Error( |
|
1242 | + sprintf( |
|
1243 | + __('Table analysis class on class %1$s is not set properly.', 'event_espresso'), |
|
1244 | + get_class($this) |
|
1245 | + ) |
|
1246 | + ); |
|
1247 | + } |
|
1248 | + } |
|
1249 | + |
|
1250 | + /** |
|
1251 | + * Gets the injected table manager, or throws an exception |
|
1252 | + * |
|
1253 | + * @return TableManager |
|
1254 | + * @throws EE_Error |
|
1255 | + */ |
|
1256 | + protected function _get_table_manager() |
|
1257 | + { |
|
1258 | + if ($this->_table_manager instanceof TableManager) { |
|
1259 | + return $this->_table_manager; |
|
1260 | + } else { |
|
1261 | + throw new EE_Error( |
|
1262 | + sprintf( |
|
1263 | + __('Table manager class on class %1$s is not set properly.', 'event_espresso'), |
|
1264 | + get_class($this) |
|
1265 | + ) |
|
1266 | + ); |
|
1267 | + } |
|
1268 | + } |
|
1269 | 1269 | } |
@@ -157,7 +157,7 @@ discard block |
||
157 | 157 | public static function instance() |
158 | 158 | { |
159 | 159 | // check if class object is instantiated |
160 | - if (! self::$_instance instanceof EE_Data_Migration_Manager) { |
|
160 | + if ( ! self::$_instance instanceof EE_Data_Migration_Manager) { |
|
161 | 161 | self::$_instance = new self(); |
162 | 162 | } |
163 | 163 | return self::$_instance; |
@@ -233,12 +233,12 @@ discard block |
||
233 | 233 | |
234 | 234 | if (count($parts) == 4) { |
235 | 235 | // it's 4.2-style.eg Core.4.1.0 |
236 | - $plugin_slug = $parts[0];// eg Core |
|
237 | - $version_string = $parts[1] . "." . $parts[2] . "." . $parts[3]; // eg 4.1.0 |
|
236 | + $plugin_slug = $parts[0]; // eg Core |
|
237 | + $version_string = $parts[1].".".$parts[2].".".$parts[3]; // eg 4.1.0 |
|
238 | 238 | } else { |
239 | 239 | // it's 4.1-style: eg 4.1.0 |
240 | 240 | $plugin_slug = 'Core'; |
241 | - $version_string = $plugin_slug_and_version_string;// eg 4.1.0 |
|
241 | + $version_string = $plugin_slug_and_version_string; // eg 4.1.0 |
|
242 | 242 | } |
243 | 243 | return array($plugin_slug, $version_string); |
244 | 244 | } |
@@ -290,11 +290,11 @@ discard block |
||
290 | 290 | */ |
291 | 291 | public function get_data_migrations_ran() |
292 | 292 | { |
293 | - if (! $this->_data_migrations_ran) { |
|
293 | + if ( ! $this->_data_migrations_ran) { |
|
294 | 294 | // setup autoloaders for each of the scripts in there |
295 | 295 | $this->get_all_data_migration_scripts_available(); |
296 | 296 | $data_migrations_options = $this->get_all_migration_script_options( |
297 | - );// get_option(EE_Data_Migration_Manager::data_migrations_option_name,get_option('espresso_data_migrations',array())); |
|
297 | + ); // get_option(EE_Data_Migration_Manager::data_migrations_option_name,get_option('espresso_data_migrations',array())); |
|
298 | 298 | |
299 | 299 | $data_migrations_ran = array(); |
300 | 300 | // convert into data migration script classes where possible |
@@ -308,23 +308,23 @@ discard block |
||
308 | 308 | $data_migration_option['option_name'], |
309 | 309 | $data_migration_option['option_value'] |
310 | 310 | ); |
311 | - $data_migrations_ran[ $plugin_slug ][ $version_string ] = $class; |
|
311 | + $data_migrations_ran[$plugin_slug][$version_string] = $class; |
|
312 | 312 | // ok so far THIS is the 'last-ran-script'... unless we find another on next iteration |
313 | 313 | $this->_last_ran_script = $class; |
314 | - if (! $class->is_completed()) { |
|
314 | + if ( ! $class->is_completed()) { |
|
315 | 315 | // sometimes we also like to know which was the last incomplete script (or if there are any at all) |
316 | 316 | $this->_last_ran_incomplete_script = $class; |
317 | 317 | } |
318 | 318 | } catch (EE_Error $e) { |
319 | 319 | // ok so its not a DMS. We'll just keep it, although other code will need to expect non-DMSs |
320 | - $data_migrations_ran[ $plugin_slug ][ $version_string ] = maybe_unserialize( |
|
320 | + $data_migrations_ran[$plugin_slug][$version_string] = maybe_unserialize( |
|
321 | 321 | $data_migration_option['option_value'] |
322 | 322 | ); |
323 | 323 | } |
324 | 324 | } |
325 | 325 | // so here the array of $data_migrations_ran is actually a mix of classes and a few legacy arrays |
326 | 326 | $this->_data_migrations_ran = $data_migrations_ran; |
327 | - if (! $this->_data_migrations_ran || ! is_array($this->_data_migrations_ran)) { |
|
327 | + if ( ! $this->_data_migrations_ran || ! is_array($this->_data_migrations_ran)) { |
|
328 | 328 | $this->_data_migrations_ran = array(); |
329 | 329 | } |
330 | 330 | } |
@@ -357,7 +357,7 @@ discard block |
||
357 | 357 | { |
358 | 358 | global $wpdb; |
359 | 359 | return $wpdb->get_results( |
360 | - "SELECT * FROM {$wpdb->options} WHERE option_name like '" . EE_Data_Migration_Manager::data_migration_script_option_prefix . "%' ORDER BY option_id ASC", |
|
360 | + "SELECT * FROM {$wpdb->options} WHERE option_name like '".EE_Data_Migration_Manager::data_migration_script_option_prefix."%' ORDER BY option_id ASC", |
|
361 | 361 | ARRAY_A |
362 | 362 | ); |
363 | 363 | } |
@@ -372,7 +372,7 @@ discard block |
||
372 | 372 | { |
373 | 373 | return apply_filters( |
374 | 374 | 'FHEE__EE_Data_Migration_Manager__get_data_migration_script_folders', |
375 | - array('Core' => EE_CORE . 'data_migration_scripts') |
|
375 | + array('Core' => EE_CORE.'data_migration_scripts') |
|
376 | 376 | ); |
377 | 377 | } |
378 | 378 | |
@@ -388,15 +388,15 @@ discard block |
||
388 | 388 | */ |
389 | 389 | public function script_migrates_to_version($migration_script_name, $eeAddonClass = '') |
390 | 390 | { |
391 | - if (isset($this->script_migration_versions[ $migration_script_name ])) { |
|
392 | - return $this->script_migration_versions[ $migration_script_name ]; |
|
391 | + if (isset($this->script_migration_versions[$migration_script_name])) { |
|
392 | + return $this->script_migration_versions[$migration_script_name]; |
|
393 | 393 | } |
394 | 394 | $dms_info = $this->parse_dms_classname($migration_script_name); |
395 | - $this->script_migration_versions[ $migration_script_name ] = array( |
|
395 | + $this->script_migration_versions[$migration_script_name] = array( |
|
396 | 396 | 'slug' => $eeAddonClass !== '' ? $eeAddonClass : $dms_info['slug'], |
397 | - 'version' => $dms_info['major_version'] . "." . $dms_info['minor_version'] . "." . $dms_info['micro_version'], |
|
397 | + 'version' => $dms_info['major_version'].".".$dms_info['minor_version'].".".$dms_info['micro_version'], |
|
398 | 398 | ); |
399 | - return $this->script_migration_versions[ $migration_script_name ]; |
|
399 | + return $this->script_migration_versions[$migration_script_name]; |
|
400 | 400 | } |
401 | 401 | |
402 | 402 | /** |
@@ -410,7 +410,7 @@ discard block |
||
410 | 410 | { |
411 | 411 | $matches = array(); |
412 | 412 | preg_match('~EE_DMS_(.*)_([0-9]*)_([0-9]*)_([0-9]*)~', $classname, $matches); |
413 | - if (! $matches || ! (isset($matches[1]) && isset($matches[2]) && isset($matches[3]))) { |
|
413 | + if ( ! $matches || ! (isset($matches[1]) && isset($matches[2]) && isset($matches[3]))) { |
|
414 | 414 | throw new EE_Error( |
415 | 415 | sprintf( |
416 | 416 | __( |
@@ -441,7 +441,7 @@ discard block |
||
441 | 441 | { |
442 | 442 | $espresso_db_core_updates = get_option('espresso_db_update', array()); |
443 | 443 | $db_state = get_option(EE_Data_Migration_Manager::current_database_state); |
444 | - if (! $db_state) { |
|
444 | + if ( ! $db_state) { |
|
445 | 445 | // mark the DB as being in the state as the last version in there. |
446 | 446 | // this is done to trigger maintenance mode and do data migration scripts |
447 | 447 | // if the admin installed this version of EE over 3.1.x or 4.0.x |
@@ -460,7 +460,7 @@ discard block |
||
460 | 460 | // in 4.1, $db_state would have only been a simple string like '4.1.0', |
461 | 461 | // but in 4.2+ it should be an array with at least key 'Core' and the value of that plugin's |
462 | 462 | // db, and possibly other keys for other addons like 'Calendar','Permissions',etc |
463 | - if (! is_array($db_state)) { |
|
463 | + if ( ! is_array($db_state)) { |
|
464 | 464 | $db_state = array('Core' => $db_state); |
465 | 465 | update_option(EE_Data_Migration_Manager::current_database_state, $db_state); |
466 | 466 | } |
@@ -497,29 +497,29 @@ discard block |
||
497 | 497 | $script_converts_plugin_slug = $migrates_to_version['slug']; |
498 | 498 | $script_converts_to_version = $migrates_to_version['version']; |
499 | 499 | // check if this version script is DONE or not; or if it's never been ran |
500 | - if (! $scripts_ran || |
|
501 | - ! isset($scripts_ran[ $script_converts_plugin_slug ]) || |
|
502 | - ! isset($scripts_ran[ $script_converts_plugin_slug ][ $script_converts_to_version ])) { |
|
500 | + if ( ! $scripts_ran || |
|
501 | + ! isset($scripts_ran[$script_converts_plugin_slug]) || |
|
502 | + ! isset($scripts_ran[$script_converts_plugin_slug][$script_converts_to_version])) { |
|
503 | 503 | // we haven't ran this conversion script before |
504 | 504 | // now check if it applies... note that we've added an autoloader for it on get_all_data_migration_scripts_available |
505 | 505 | $script = new $classname($this->_get_table_manager(), $this->_get_table_analysis()); |
506 | 506 | /* @var $script EE_Data_Migration_Script_Base */ |
507 | 507 | $can_migrate = $script->can_migrate_from_version($theoretical_database_state); |
508 | 508 | if ($can_migrate) { |
509 | - $script_classes_that_should_run_per_iteration[ $iteration ][ $script->priority() ][] = $script; |
|
509 | + $script_classes_that_should_run_per_iteration[$iteration][$script->priority()][] = $script; |
|
510 | 510 | $migrates_to_version = $script->migrates_to_version(); |
511 | - $next_database_state_to_consider[ $migrates_to_version['slug'] ] = $migrates_to_version['version']; |
|
512 | - unset($script_class_and_filepaths_available[ $classname ]); |
|
511 | + $next_database_state_to_consider[$migrates_to_version['slug']] = $migrates_to_version['version']; |
|
512 | + unset($script_class_and_filepaths_available[$classname]); |
|
513 | 513 | } |
514 | - } elseif ($scripts_ran[ $script_converts_plugin_slug ][ $script_converts_to_version ] instanceof EE_Data_Migration_Script_Base) { |
|
514 | + } elseif ($scripts_ran[$script_converts_plugin_slug][$script_converts_to_version] instanceof EE_Data_Migration_Script_Base) { |
|
515 | 515 | // this script has been ran, or at least started |
516 | - $script = $scripts_ran[ $script_converts_plugin_slug ][ $script_converts_to_version ]; |
|
516 | + $script = $scripts_ran[$script_converts_plugin_slug][$script_converts_to_version]; |
|
517 | 517 | if ($script->get_status() != self::status_completed) { |
518 | 518 | // this script is already underway... keep going with it |
519 | - $script_classes_that_should_run_per_iteration[ $iteration ][ $script->priority() ][] = $script; |
|
519 | + $script_classes_that_should_run_per_iteration[$iteration][$script->priority()][] = $script; |
|
520 | 520 | $migrates_to_version = $script->migrates_to_version(); |
521 | - $next_database_state_to_consider[ $migrates_to_version['slug'] ] = $migrates_to_version['version']; |
|
522 | - unset($script_class_and_filepaths_available[ $classname ]); |
|
521 | + $next_database_state_to_consider[$migrates_to_version['slug']] = $migrates_to_version['version']; |
|
522 | + unset($script_class_and_filepaths_available[$classname]); |
|
523 | 523 | } else { |
524 | 524 | // it must have a status that indicates it has finished, so we don't want to try and run it again |
525 | 525 | } |
@@ -530,14 +530,14 @@ discard block |
||
530 | 530 | } |
531 | 531 | } |
532 | 532 | $iteration++; |
533 | - } while ($next_database_state_to_consider != $theoretical_database_state && $iteration < 6); |
|
533 | + }while ($next_database_state_to_consider != $theoretical_database_state && $iteration < 6); |
|
534 | 534 | // ok we have all the scripts that should run, now let's make them into flat array |
535 | 535 | $scripts_that_should_run = array(); |
536 | 536 | foreach ($script_classes_that_should_run_per_iteration as $scripts_at_priority) { |
537 | 537 | ksort($scripts_at_priority); |
538 | 538 | foreach ($scripts_at_priority as $scripts) { |
539 | 539 | foreach ($scripts as $script) { |
540 | - $scripts_that_should_run[ get_class($script) ] = $script; |
|
540 | + $scripts_that_should_run[get_class($script)] = $script; |
|
541 | 541 | } |
542 | 542 | } |
543 | 543 | } |
@@ -562,7 +562,7 @@ discard block |
||
562 | 562 | public function get_last_ran_script($include_completed_scripts = false) |
563 | 563 | { |
564 | 564 | // make sure we've setup the class properties _last_ran_script and _last_ran_incomplete_script |
565 | - if (! $this->_data_migrations_ran) { |
|
565 | + if ( ! $this->_data_migrations_ran) { |
|
566 | 566 | $this->get_data_migrations_ran(); |
567 | 567 | } |
568 | 568 | if ($include_completed_scripts) { |
@@ -599,10 +599,10 @@ discard block |
||
599 | 599 | |
600 | 600 | try { |
601 | 601 | $currently_executing_script = $this->get_last_ran_script(); |
602 | - if (! $currently_executing_script) { |
|
602 | + if ( ! $currently_executing_script) { |
|
603 | 603 | // Find the next script that needs to execute |
604 | 604 | $scripts = $this->check_for_applicable_data_migration_scripts(); |
605 | - if (! $scripts) { |
|
605 | + if ( ! $scripts) { |
|
606 | 606 | // huh, no more scripts to run... apparently we're done! |
607 | 607 | // but dont forget to make sure initial data is there |
608 | 608 | // we should be good to allow them to exit maintenance mode now |
@@ -628,7 +628,7 @@ discard block |
||
628 | 628 | $migrates_to = $this->script_migrates_to_version(get_class($currently_executing_script)); |
629 | 629 | $plugin_slug = $migrates_to['slug']; |
630 | 630 | $version = $migrates_to['version']; |
631 | - $this->_data_migrations_ran[ $plugin_slug ][ $version ] = $currently_executing_script; |
|
631 | + $this->_data_migrations_ran[$plugin_slug][$version] = $currently_executing_script; |
|
632 | 632 | } |
633 | 633 | $current_script_name = get_class($currently_executing_script); |
634 | 634 | } catch (Exception $e) { |
@@ -636,7 +636,7 @@ discard block |
||
636 | 636 | |
637 | 637 | $message = sprintf( |
638 | 638 | __("Error Message: %sStack Trace:%s", "event_espresso"), |
639 | - $e->getMessage() . '<br>', |
|
639 | + $e->getMessage().'<br>', |
|
640 | 640 | $e->getTraceAsString() |
641 | 641 | ); |
642 | 642 | // record it on the array of data migration scripts ran. This will be overwritten next time we try and try to run data migrations |
@@ -688,7 +688,7 @@ discard block |
||
688 | 688 | ); |
689 | 689 | // check if there are any more after this one. |
690 | 690 | $scripts_remaining = $this->check_for_applicable_data_migration_scripts(); |
691 | - if (! $scripts_remaining) { |
|
691 | + if ( ! $scripts_remaining) { |
|
692 | 692 | // we should be good to allow them to exit maintenance mode now |
693 | 693 | EE_Maintenance_Mode::instance()->set_maintenance_level( |
694 | 694 | intval(EE_Maintenance_Mode::level_0_not_in_maintenance) |
@@ -788,7 +788,7 @@ discard block |
||
788 | 788 | ), |
789 | 789 | 'script' => 'Unknown', |
790 | 790 | ); |
791 | - $this->add_error_to_migrations_ran($e->getMessage() . "; Stack trace:" . $e->getTraceAsString()); |
|
791 | + $this->add_error_to_migrations_ran($e->getMessage()."; Stack trace:".$e->getTraceAsString()); |
|
792 | 792 | } |
793 | 793 | $warnings_etc = @ob_get_contents(); |
794 | 794 | ob_end_clean(); |
@@ -808,12 +808,12 @@ discard block |
||
808 | 808 | */ |
809 | 809 | public function update_current_database_state_to($slug_and_version = null) |
810 | 810 | { |
811 | - if (! $slug_and_version) { |
|
811 | + if ( ! $slug_and_version) { |
|
812 | 812 | // no version was provided, assume it should be at the current code version |
813 | 813 | $slug_and_version = array('slug' => 'Core', 'version' => espresso_version()); |
814 | 814 | } |
815 | 815 | $current_database_state = get_option(self::current_database_state); |
816 | - $current_database_state[ $slug_and_version['slug'] ] = $slug_and_version['version']; |
|
816 | + $current_database_state[$slug_and_version['slug']] = $slug_and_version['version']; |
|
817 | 817 | update_option(self::current_database_state, $current_database_state); |
818 | 818 | } |
819 | 819 | |
@@ -832,15 +832,15 @@ discard block |
||
832 | 832 | $slug = $slug_and_version['slug']; |
833 | 833 | $version = $slug_and_version['version']; |
834 | 834 | $current_database_state = get_option(self::current_database_state); |
835 | - if (! isset($current_database_state[ $slug ])) { |
|
835 | + if ( ! isset($current_database_state[$slug])) { |
|
836 | 836 | return true; |
837 | 837 | } else { |
838 | 838 | // just compare the first 3 parts of version string, eg "4.7.1", not "4.7.1.dev.032" because DBs shouldn't change on nano version changes |
839 | - $version_parts_current_db_state = array_slice(explode('.', $current_database_state[ $slug ]), 0, 3); |
|
839 | + $version_parts_current_db_state = array_slice(explode('.', $current_database_state[$slug]), 0, 3); |
|
840 | 840 | $version_parts_of_provided_db_state = array_slice(explode('.', $version), 0, 3); |
841 | 841 | $needs_updating = false; |
842 | 842 | foreach ($version_parts_current_db_state as $offset => $version_part_in_current_db_state) { |
843 | - if ($version_part_in_current_db_state < $version_parts_of_provided_db_state[ $offset ]) { |
|
843 | + if ($version_part_in_current_db_state < $version_parts_of_provided_db_state[$offset]) { |
|
844 | 844 | $needs_updating = true; |
845 | 845 | break; |
846 | 846 | } |
@@ -862,7 +862,7 @@ discard block |
||
862 | 862 | */ |
863 | 863 | public function get_all_data_migration_scripts_available() |
864 | 864 | { |
865 | - if (! $this->_data_migration_class_to_filepath_map) { |
|
865 | + if ( ! $this->_data_migration_class_to_filepath_map) { |
|
866 | 866 | $this->_data_migration_class_to_filepath_map = array(); |
867 | 867 | foreach ($this->get_data_migration_script_folders() as $eeAddonClass => $folder_path) { |
868 | 868 | // strip any placeholders added to classname to make it a unique array key |
@@ -871,7 +871,7 @@ discard block |
||
871 | 871 | ? $eeAddonClass |
872 | 872 | : ''; |
873 | 873 | $folder_path = EEH_File::end_with_directory_separator($folder_path); |
874 | - $files = glob($folder_path . '*.dms.php'); |
|
874 | + $files = glob($folder_path.'*.dms.php'); |
|
875 | 875 | if (empty($files)) { |
876 | 876 | continue; |
877 | 877 | } |
@@ -897,7 +897,7 @@ discard block |
||
897 | 897 | '4.3.0.alpha.019' |
898 | 898 | ); |
899 | 899 | } |
900 | - $this->_data_migration_class_to_filepath_map[ $classname ] = $file; |
|
900 | + $this->_data_migration_class_to_filepath_map[$classname] = $file; |
|
901 | 901 | } |
902 | 902 | } |
903 | 903 | EEH_Autoloader::register_autoloader($this->_data_migration_class_to_filepath_map); |
@@ -929,7 +929,7 @@ discard block |
||
929 | 929 | // get last-ran migration script |
930 | 930 | global $wpdb; |
931 | 931 | $last_migration_script_option = $wpdb->get_row( |
932 | - "SELECT * FROM $wpdb->options WHERE option_name like '" . EE_Data_Migration_Manager::data_migration_script_option_prefix . "%' ORDER BY option_id DESC LIMIT 1", |
|
932 | + "SELECT * FROM $wpdb->options WHERE option_name like '".EE_Data_Migration_Manager::data_migration_script_option_prefix."%' ORDER BY option_id DESC LIMIT 1", |
|
933 | 933 | ARRAY_A |
934 | 934 | ); |
935 | 935 | |
@@ -961,10 +961,10 @@ discard block |
||
961 | 961 | $versions_migrated_to = 'Unknown.1.0.0'; |
962 | 962 | // now just to make sure appears as last (in case the were previously a fatal error like this) |
963 | 963 | // delete the old one |
964 | - delete_option(self::data_migration_script_option_prefix . $versions_migrated_to); |
|
964 | + delete_option(self::data_migration_script_option_prefix.$versions_migrated_to); |
|
965 | 965 | } |
966 | 966 | update_option( |
967 | - self::data_migration_script_option_prefix . $versions_migrated_to, |
|
967 | + self::data_migration_script_option_prefix.$versions_migrated_to, |
|
968 | 968 | $last_ran_migration_script_properties |
969 | 969 | ); |
970 | 970 | } |
@@ -983,8 +983,8 @@ discard block |
||
983 | 983 | $successful_updates = true; |
984 | 984 | foreach ($this->_data_migrations_ran as $plugin_slug => $migrations_ran_for_plugin) { |
985 | 985 | foreach ($migrations_ran_for_plugin as $version_string => $array_or_migration_obj) { |
986 | - $plugin_slug_for_use_in_option_name = $plugin_slug . "."; |
|
987 | - $option_name = self::data_migration_script_option_prefix . $plugin_slug_for_use_in_option_name . $version_string; |
|
986 | + $plugin_slug_for_use_in_option_name = $plugin_slug."."; |
|
987 | + $option_name = self::data_migration_script_option_prefix.$plugin_slug_for_use_in_option_name.$version_string; |
|
988 | 988 | $old_option_value = get_option($option_name); |
989 | 989 | if ($array_or_migration_obj instanceof EE_Data_Migration_Script_Base) { |
990 | 990 | $script_array_for_saving = $array_or_migration_obj->properties_as_array(); |
@@ -996,7 +996,7 @@ discard block |
||
996 | 996 | $successful_updates = update_option($option_name, $array_or_migration_obj); |
997 | 997 | } |
998 | 998 | } |
999 | - if (! $successful_updates) { |
|
999 | + if ( ! $successful_updates) { |
|
1000 | 1000 | global $wpdb; |
1001 | 1001 | return $wpdb->last_error; |
1002 | 1002 | } |
@@ -1030,7 +1030,7 @@ discard block |
||
1030 | 1030 | */ |
1031 | 1031 | public function _instantiate_script_from_properties_array($properties_array) |
1032 | 1032 | { |
1033 | - if (! isset($properties_array['class'])) { |
|
1033 | + if ( ! isset($properties_array['class'])) { |
|
1034 | 1034 | throw new EE_Error( |
1035 | 1035 | sprintf( |
1036 | 1036 | __("Properties array has no 'class' properties. Here's what it has: %s", "event_espresso"), |
@@ -1039,11 +1039,11 @@ discard block |
||
1039 | 1039 | ); |
1040 | 1040 | } |
1041 | 1041 | $class_name = $properties_array['class']; |
1042 | - if (! class_exists($class_name)) { |
|
1042 | + if ( ! class_exists($class_name)) { |
|
1043 | 1043 | throw new EE_Error(sprintf(__("There is no migration script named %s", "event_espresso"), $class_name)); |
1044 | 1044 | } |
1045 | 1045 | $class = new $class_name; |
1046 | - if (! $class instanceof EE_Data_Migration_Script_Base) { |
|
1046 | + if ( ! $class instanceof EE_Data_Migration_Script_Base) { |
|
1047 | 1047 | throw new EE_Error( |
1048 | 1048 | sprintf( |
1049 | 1049 | __("Class '%s' is supposed to be a migration script. Its not, its a '%s'", "event_espresso"), |
@@ -1110,8 +1110,8 @@ discard block |
||
1110 | 1110 | public function get_migration_ran($version, $plugin_slug = 'Core') |
1111 | 1111 | { |
1112 | 1112 | $migrations_ran = $this->get_data_migrations_ran(); |
1113 | - if (isset($migrations_ran[ $plugin_slug ]) && isset($migrations_ran[ $plugin_slug ][ $version ])) { |
|
1114 | - return $migrations_ran[ $plugin_slug ][ $version ]; |
|
1113 | + if (isset($migrations_ran[$plugin_slug]) && isset($migrations_ran[$plugin_slug][$version])) { |
|
1114 | + return $migrations_ran[$plugin_slug][$version]; |
|
1115 | 1115 | } else { |
1116 | 1116 | return null; |
1117 | 1117 | } |
@@ -1172,7 +1172,7 @@ discard block |
||
1172 | 1172 | public function enqueue_db_initialization_for($plugin_slug) |
1173 | 1173 | { |
1174 | 1174 | $queue = $this->get_db_initialization_queue(); |
1175 | - if (! in_array($plugin_slug, $queue)) { |
|
1175 | + if ( ! in_array($plugin_slug, $queue)) { |
|
1176 | 1176 | $queue[] = $plugin_slug; |
1177 | 1177 | } |
1178 | 1178 | update_option(self::db_init_queue_option_name, $queue); |
@@ -1188,7 +1188,7 @@ discard block |
||
1188 | 1188 | $queue = $this->get_db_initialization_queue(); |
1189 | 1189 | foreach ($queue as $plugin_slug) { |
1190 | 1190 | $most_up_to_date_dms = $this->get_most_up_to_date_dms($plugin_slug); |
1191 | - if (! $most_up_to_date_dms) { |
|
1191 | + if ( ! $most_up_to_date_dms) { |
|
1192 | 1192 | // if there is NO DMS for this plugin, obviously there's no schema to verify anyways |
1193 | 1193 | $verify_db = false; |
1194 | 1194 | } else { |
@@ -14,28 +14,28 @@ |
||
14 | 14 | class EE_Detect_File_Editor_Request extends EE_Middleware |
15 | 15 | { |
16 | 16 | |
17 | - /** |
|
18 | - * @deprecated |
|
19 | - * @param EE_Request $request |
|
20 | - * @param EE_Response $response |
|
21 | - * @return EE_Response |
|
22 | - */ |
|
23 | - public function handle_request(EE_Request $request, EE_Response $response) |
|
24 | - { |
|
25 | - EE_Error::doing_it_wrong( |
|
26 | - __METHOD__, |
|
27 | - sprintf( |
|
28 | - esc_html__( |
|
29 | - 'This class is deprecated. Please use %1$s instead. All Event Espresso request stack classes have been moved to %2$s and are now under the %3$s namespace', |
|
30 | - 'event_espresso' |
|
31 | - ), |
|
32 | - 'EventEspresso\core\services\request\middleware\DetectFileEditorRequest', |
|
33 | - '\core\services\request', |
|
34 | - 'EventEspresso\core\services\request' |
|
35 | - ), |
|
36 | - '4.9.52' |
|
37 | - ); |
|
38 | - return $response; |
|
39 | - } |
|
17 | + /** |
|
18 | + * @deprecated |
|
19 | + * @param EE_Request $request |
|
20 | + * @param EE_Response $response |
|
21 | + * @return EE_Response |
|
22 | + */ |
|
23 | + public function handle_request(EE_Request $request, EE_Response $response) |
|
24 | + { |
|
25 | + EE_Error::doing_it_wrong( |
|
26 | + __METHOD__, |
|
27 | + sprintf( |
|
28 | + esc_html__( |
|
29 | + 'This class is deprecated. Please use %1$s instead. All Event Espresso request stack classes have been moved to %2$s and are now under the %3$s namespace', |
|
30 | + 'event_espresso' |
|
31 | + ), |
|
32 | + 'EventEspresso\core\services\request\middleware\DetectFileEditorRequest', |
|
33 | + '\core\services\request', |
|
34 | + 'EventEspresso\core\services\request' |
|
35 | + ), |
|
36 | + '4.9.52' |
|
37 | + ); |
|
38 | + return $response; |
|
39 | + } |
|
40 | 40 | |
41 | 41 | } |
@@ -83,12 +83,12 @@ |
||
83 | 83 | <div class="notice inline notice-alt"> |
84 | 84 | <div class="ee-upsell-container"> |
85 | 85 | <div class="ee-upsell-inner-container"> |
86 | - <a href="' . $button_url . '"> |
|
87 | - ' . $button_text . ' |
|
86 | + <a href="' . $button_url.'"> |
|
87 | + ' . $button_text.' |
|
88 | 88 | </a> |
89 | 89 | </div> |
90 | 90 | <div class="ee-upsell-inner-container"> |
91 | - <p>' . $upsell_text . '</p> |
|
91 | + <p>' . $upsell_text.'</p> |
|
92 | 92 | </div> |
93 | 93 | <div style="clear:both"></div> |
94 | 94 | </div> |
@@ -17,47 +17,47 @@ discard block |
||
17 | 17 | class PluginUpsells |
18 | 18 | { |
19 | 19 | |
20 | - /** |
|
21 | - * @var DomainInterface |
|
22 | - */ |
|
23 | - private $domain; |
|
20 | + /** |
|
21 | + * @var DomainInterface |
|
22 | + */ |
|
23 | + private $domain; |
|
24 | 24 | |
25 | 25 | |
26 | - /** |
|
27 | - * PluginUpsells constructor. |
|
28 | - * |
|
29 | - * @param DomainInterface $domain |
|
30 | - */ |
|
31 | - public function __construct(DomainInterface $domain) |
|
32 | - { |
|
33 | - $this->domain = $domain; |
|
34 | - } |
|
26 | + /** |
|
27 | + * PluginUpsells constructor. |
|
28 | + * |
|
29 | + * @param DomainInterface $domain |
|
30 | + */ |
|
31 | + public function __construct(DomainInterface $domain) |
|
32 | + { |
|
33 | + $this->domain = $domain; |
|
34 | + } |
|
35 | 35 | |
36 | 36 | |
37 | - /** |
|
38 | - * Hook in various upsells for the decaf version of EE. |
|
39 | - */ |
|
40 | - public function decafUpsells() |
|
41 | - { |
|
42 | - if ($this->domain instanceof CaffeinatedInterface && ! $this->domain->isCaffeinated()) { |
|
43 | - add_action('after_plugin_row', array($this, 'doPremiumUpsell'), 10, 3); |
|
44 | - } |
|
45 | - } |
|
37 | + /** |
|
38 | + * Hook in various upsells for the decaf version of EE. |
|
39 | + */ |
|
40 | + public function decafUpsells() |
|
41 | + { |
|
42 | + if ($this->domain instanceof CaffeinatedInterface && ! $this->domain->isCaffeinated()) { |
|
43 | + add_action('after_plugin_row', array($this, 'doPremiumUpsell'), 10, 3); |
|
44 | + } |
|
45 | + } |
|
46 | 46 | |
47 | 47 | |
48 | - /** |
|
49 | - * Callback for `after_plugin_row` to add upsell info |
|
50 | - * |
|
51 | - * @param string $plugin_file |
|
52 | - * @param array $plugin_data |
|
53 | - * @param string $status |
|
54 | - * @throws DomainException |
|
55 | - */ |
|
56 | - public function doPremiumUpsell($plugin_file, $plugin_data, $status) |
|
57 | - { |
|
58 | - if ($plugin_file === $this->domain->pluginBasename()) { |
|
59 | - list($button_text, $button_url, $upsell_text) = $this->getAfterPluginRowDetails(); |
|
60 | - echo '<tr class="plugin-update-tr ee-upsell-plugin-list-table active"> |
|
48 | + /** |
|
49 | + * Callback for `after_plugin_row` to add upsell info |
|
50 | + * |
|
51 | + * @param string $plugin_file |
|
52 | + * @param array $plugin_data |
|
53 | + * @param string $status |
|
54 | + * @throws DomainException |
|
55 | + */ |
|
56 | + public function doPremiumUpsell($plugin_file, $plugin_data, $status) |
|
57 | + { |
|
58 | + if ($plugin_file === $this->domain->pluginBasename()) { |
|
59 | + list($button_text, $button_url, $upsell_text) = $this->getAfterPluginRowDetails(); |
|
60 | + echo '<tr class="plugin-update-tr ee-upsell-plugin-list-table active"> |
|
61 | 61 | <td colspan="3" class="plugin-update colspanchange"> |
62 | 62 | <div class="notice inline notice-alt"> |
63 | 63 | <div class="ee-upsell-container"> |
@@ -74,27 +74,27 @@ discard block |
||
74 | 74 | </div> |
75 | 75 | </td> |
76 | 76 | </tr>'; |
77 | - } |
|
78 | - } |
|
77 | + } |
|
78 | + } |
|
79 | 79 | |
80 | - /** |
|
81 | - * Provide the details used for the upsell container. |
|
82 | - * |
|
83 | - * @return array |
|
84 | - */ |
|
85 | - protected function getAfterPluginRowDetails() |
|
86 | - { |
|
87 | - return array( |
|
88 | - esc_html__('Upgrade for Support', 'event_espresso'), |
|
89 | - 'https://eventespresso.com/purchase/?slug=ee4-license-personal&utm_source=wp_admin_plugins_screen&utm_medium=link&utm_campaign=plugins_screen_upgrade_link" class="button button-primary', |
|
90 | - sprintf( |
|
91 | - esc_html__( |
|
92 | - 'You\'re missing out on %1$sexpert support%2$s and %1$sone-click updates%2$s! Don\'t have an Event Espresso support license key? Support the project and buy one today!', |
|
93 | - 'event_espresso' |
|
94 | - ), |
|
95 | - '<strong>', |
|
96 | - '</strong>' |
|
97 | - ), |
|
98 | - ); |
|
99 | - } |
|
80 | + /** |
|
81 | + * Provide the details used for the upsell container. |
|
82 | + * |
|
83 | + * @return array |
|
84 | + */ |
|
85 | + protected function getAfterPluginRowDetails() |
|
86 | + { |
|
87 | + return array( |
|
88 | + esc_html__('Upgrade for Support', 'event_espresso'), |
|
89 | + 'https://eventespresso.com/purchase/?slug=ee4-license-personal&utm_source=wp_admin_plugins_screen&utm_medium=link&utm_campaign=plugins_screen_upgrade_link" class="button button-primary', |
|
90 | + sprintf( |
|
91 | + esc_html__( |
|
92 | + 'You\'re missing out on %1$sexpert support%2$s and %1$sone-click updates%2$s! Don\'t have an Event Espresso support license key? Support the project and buy one today!', |
|
93 | + 'event_espresso' |
|
94 | + ), |
|
95 | + '<strong>', |
|
96 | + '</strong>' |
|
97 | + ), |
|
98 | + ); |
|
99 | + } |
|
100 | 100 | } |
@@ -78,13 +78,13 @@ |
||
78 | 78 | // EE_Dependency_Map: info about how to load classes required by other classes |
79 | 79 | espresso_load_required( |
80 | 80 | 'EE_Dependency_Map', |
81 | - EE_CORE . 'EE_Dependency_Map.core.php' |
|
81 | + EE_CORE.'EE_Dependency_Map.core.php' |
|
82 | 82 | ); |
83 | 83 | $this->dependency_map = EE_Dependency_Map::instance($this->class_cache); |
84 | 84 | // EE_Registry: central repository for classes (legacy) |
85 | 85 | espresso_load_required( |
86 | 86 | 'EE_Registry', |
87 | - EE_CORE . 'EE_Registry.core.php' |
|
87 | + EE_CORE.'EE_Registry.core.php' |
|
88 | 88 | ); |
89 | 89 | $this->registry = EE_Registry::instance( |
90 | 90 | $this->dependency_map, |
@@ -25,123 +25,123 @@ |
||
25 | 25 | class BootstrapDependencyInjectionContainer |
26 | 26 | { |
27 | 27 | |
28 | - /** |
|
29 | - * @var EE_Dependency_Map $dependency_map |
|
30 | - */ |
|
31 | - protected $dependency_map; |
|
32 | - |
|
33 | - /** |
|
34 | - * @type LoaderInterface $loader |
|
35 | - */ |
|
36 | - protected $loader; |
|
37 | - |
|
38 | - /** |
|
39 | - * @var EE_Registry $registry |
|
40 | - */ |
|
41 | - protected $registry; |
|
42 | - |
|
43 | - /** |
|
44 | - * @var ClassInterfaceCache $class_cache |
|
45 | - */ |
|
46 | - private $class_cache; |
|
47 | - |
|
48 | - /** |
|
49 | - * @var Mirror |
|
50 | - */ |
|
51 | - private $mirror; |
|
52 | - |
|
53 | - /** |
|
54 | - * @var ObjectIdentifier |
|
55 | - */ |
|
56 | - private $object_identifier; |
|
57 | - |
|
58 | - |
|
59 | - /** |
|
60 | - * Can't use this just yet until we exorcise some more of our singleton usage from core |
|
61 | - */ |
|
62 | - public function buildDependencyInjectionContainer() |
|
63 | - { |
|
64 | - // build DI container |
|
65 | - // $OpenCoffeeShop = new EventEspresso\core\services\container\OpenCoffeeShop(); |
|
66 | - // $OpenCoffeeShop->addRecipes(); |
|
67 | - // $CoffeeShop = $OpenCoffeeShop->CoffeeShop(); |
|
68 | - } |
|
69 | - |
|
70 | - |
|
71 | - /** |
|
72 | - * Setups EE_Registry and EE_Dependency_Map |
|
73 | - * |
|
74 | - * @throws EE_Error |
|
75 | - */ |
|
76 | - public function buildLegacyDependencyInjectionContainer() |
|
77 | - { |
|
78 | - $this->class_cache = new ClassInterfaceCache(); |
|
79 | - $this->object_identifier = new ObjectIdentifier($this->class_cache); |
|
80 | - $this->mirror = new Mirror(); |
|
81 | - // EE_Dependency_Map: info about how to load classes required by other classes |
|
82 | - espresso_load_required( |
|
83 | - 'EE_Dependency_Map', |
|
84 | - EE_CORE . 'EE_Dependency_Map.core.php' |
|
85 | - ); |
|
86 | - $this->dependency_map = EE_Dependency_Map::instance($this->class_cache); |
|
87 | - // EE_Registry: central repository for classes (legacy) |
|
88 | - espresso_load_required( |
|
89 | - 'EE_Registry', |
|
90 | - EE_CORE . 'EE_Registry.core.php' |
|
91 | - ); |
|
92 | - $this->registry = EE_Registry::instance( |
|
93 | - $this->dependency_map, |
|
94 | - $this->mirror, |
|
95 | - $this->class_cache, |
|
96 | - $this->object_identifier |
|
97 | - ); |
|
98 | - } |
|
99 | - |
|
100 | - |
|
101 | - /** |
|
102 | - * Performs initial setup for the generic Loader |
|
103 | - * |
|
104 | - * @throws InvalidDataTypeException |
|
105 | - * @throws InvalidInterfaceException |
|
106 | - * @throws InvalidArgumentException |
|
107 | - */ |
|
108 | - public function buildLoader() |
|
109 | - { |
|
110 | - $this->loader = LoaderFactory::getLoader( |
|
111 | - $this->registry, |
|
112 | - $this->class_cache, |
|
113 | - $this->object_identifier |
|
114 | - ); |
|
115 | - $this->loader->share('EventEspresso\core\services\loaders\ClassInterfaceCache', $this->class_cache); |
|
116 | - $this->loader->share('EventEspresso\core\services\loaders\ObjectIdentifier', $this->object_identifier); |
|
117 | - $this->loader->share('EventEspresso\core\services\container\Mirror', $this->mirror); |
|
118 | - $this->dependency_map->setLoader($this->loader); |
|
119 | - } |
|
120 | - |
|
121 | - |
|
122 | - /** |
|
123 | - * @return EE_Dependency_Map |
|
124 | - */ |
|
125 | - public function getDependencyMap() |
|
126 | - { |
|
127 | - return $this->dependency_map; |
|
128 | - } |
|
129 | - |
|
130 | - |
|
131 | - /** |
|
132 | - * @return EE_Registry |
|
133 | - */ |
|
134 | - public function getRegistry() |
|
135 | - { |
|
136 | - return $this->registry; |
|
137 | - } |
|
138 | - |
|
139 | - |
|
140 | - /** |
|
141 | - * @return LoaderInterface |
|
142 | - */ |
|
143 | - public function getLoader() |
|
144 | - { |
|
145 | - return $this->loader; |
|
146 | - } |
|
28 | + /** |
|
29 | + * @var EE_Dependency_Map $dependency_map |
|
30 | + */ |
|
31 | + protected $dependency_map; |
|
32 | + |
|
33 | + /** |
|
34 | + * @type LoaderInterface $loader |
|
35 | + */ |
|
36 | + protected $loader; |
|
37 | + |
|
38 | + /** |
|
39 | + * @var EE_Registry $registry |
|
40 | + */ |
|
41 | + protected $registry; |
|
42 | + |
|
43 | + /** |
|
44 | + * @var ClassInterfaceCache $class_cache |
|
45 | + */ |
|
46 | + private $class_cache; |
|
47 | + |
|
48 | + /** |
|
49 | + * @var Mirror |
|
50 | + */ |
|
51 | + private $mirror; |
|
52 | + |
|
53 | + /** |
|
54 | + * @var ObjectIdentifier |
|
55 | + */ |
|
56 | + private $object_identifier; |
|
57 | + |
|
58 | + |
|
59 | + /** |
|
60 | + * Can't use this just yet until we exorcise some more of our singleton usage from core |
|
61 | + */ |
|
62 | + public function buildDependencyInjectionContainer() |
|
63 | + { |
|
64 | + // build DI container |
|
65 | + // $OpenCoffeeShop = new EventEspresso\core\services\container\OpenCoffeeShop(); |
|
66 | + // $OpenCoffeeShop->addRecipes(); |
|
67 | + // $CoffeeShop = $OpenCoffeeShop->CoffeeShop(); |
|
68 | + } |
|
69 | + |
|
70 | + |
|
71 | + /** |
|
72 | + * Setups EE_Registry and EE_Dependency_Map |
|
73 | + * |
|
74 | + * @throws EE_Error |
|
75 | + */ |
|
76 | + public function buildLegacyDependencyInjectionContainer() |
|
77 | + { |
|
78 | + $this->class_cache = new ClassInterfaceCache(); |
|
79 | + $this->object_identifier = new ObjectIdentifier($this->class_cache); |
|
80 | + $this->mirror = new Mirror(); |
|
81 | + // EE_Dependency_Map: info about how to load classes required by other classes |
|
82 | + espresso_load_required( |
|
83 | + 'EE_Dependency_Map', |
|
84 | + EE_CORE . 'EE_Dependency_Map.core.php' |
|
85 | + ); |
|
86 | + $this->dependency_map = EE_Dependency_Map::instance($this->class_cache); |
|
87 | + // EE_Registry: central repository for classes (legacy) |
|
88 | + espresso_load_required( |
|
89 | + 'EE_Registry', |
|
90 | + EE_CORE . 'EE_Registry.core.php' |
|
91 | + ); |
|
92 | + $this->registry = EE_Registry::instance( |
|
93 | + $this->dependency_map, |
|
94 | + $this->mirror, |
|
95 | + $this->class_cache, |
|
96 | + $this->object_identifier |
|
97 | + ); |
|
98 | + } |
|
99 | + |
|
100 | + |
|
101 | + /** |
|
102 | + * Performs initial setup for the generic Loader |
|
103 | + * |
|
104 | + * @throws InvalidDataTypeException |
|
105 | + * @throws InvalidInterfaceException |
|
106 | + * @throws InvalidArgumentException |
|
107 | + */ |
|
108 | + public function buildLoader() |
|
109 | + { |
|
110 | + $this->loader = LoaderFactory::getLoader( |
|
111 | + $this->registry, |
|
112 | + $this->class_cache, |
|
113 | + $this->object_identifier |
|
114 | + ); |
|
115 | + $this->loader->share('EventEspresso\core\services\loaders\ClassInterfaceCache', $this->class_cache); |
|
116 | + $this->loader->share('EventEspresso\core\services\loaders\ObjectIdentifier', $this->object_identifier); |
|
117 | + $this->loader->share('EventEspresso\core\services\container\Mirror', $this->mirror); |
|
118 | + $this->dependency_map->setLoader($this->loader); |
|
119 | + } |
|
120 | + |
|
121 | + |
|
122 | + /** |
|
123 | + * @return EE_Dependency_Map |
|
124 | + */ |
|
125 | + public function getDependencyMap() |
|
126 | + { |
|
127 | + return $this->dependency_map; |
|
128 | + } |
|
129 | + |
|
130 | + |
|
131 | + /** |
|
132 | + * @return EE_Registry |
|
133 | + */ |
|
134 | + public function getRegistry() |
|
135 | + { |
|
136 | + return $this->registry; |
|
137 | + } |
|
138 | + |
|
139 | + |
|
140 | + /** |
|
141 | + * @return LoaderInterface |
|
142 | + */ |
|
143 | + public function getLoader() |
|
144 | + { |
|
145 | + return $this->loader; |
|
146 | + } |
|
147 | 147 | } |
@@ -25,226 +25,226 @@ |
||
25 | 25 | class Mirror |
26 | 26 | { |
27 | 27 | |
28 | - /** |
|
29 | - * @var ReflectionClass[] $classes |
|
30 | - */ |
|
31 | - private $classes = array(); |
|
32 | - |
|
33 | - /** |
|
34 | - * @var ReflectionMethod[] $constructors |
|
35 | - */ |
|
36 | - private $constructors = array(); |
|
37 | - |
|
38 | - /** |
|
39 | - * @var ReflectionParameter[][] $parameters |
|
40 | - */ |
|
41 | - private $parameters = array(); |
|
42 | - |
|
43 | - /** |
|
44 | - * @var ReflectionParameter[][] $parameters |
|
45 | - */ |
|
46 | - private $parameter_classes = array(); |
|
47 | - |
|
48 | - /** |
|
49 | - * @var ReflectionProperty[][] $properties |
|
50 | - */ |
|
51 | - private $properties = array(); |
|
52 | - |
|
53 | - /** |
|
54 | - * @var ReflectionMethod[][] $methods |
|
55 | - */ |
|
56 | - private $methods = array(); |
|
57 | - |
|
58 | - |
|
59 | - /** |
|
60 | - * @param string $class_name |
|
61 | - * @return ReflectionClass |
|
62 | - * @throws ReflectionException |
|
63 | - * @throws InvalidDataTypeException |
|
64 | - */ |
|
65 | - public function getReflectionClass($class_name) |
|
66 | - { |
|
67 | - if (! is_string($class_name)) { |
|
68 | - throw new InvalidDataTypeException($class_name, '$class_name', 'string (fully qualified class name)'); |
|
69 | - } |
|
70 | - if (! isset($this->classes[ $class_name ])) { |
|
71 | - $this->classes[ $class_name ] = new ReflectionClass($class_name); |
|
72 | - } |
|
73 | - return $this->classes[ $class_name ]; |
|
74 | - } |
|
75 | - |
|
76 | - |
|
77 | - /** |
|
78 | - * @param string $class_name |
|
79 | - * @return ReflectionMethod |
|
80 | - * @throws InvalidDataTypeException |
|
81 | - * @throws ReflectionException |
|
82 | - */ |
|
83 | - public function getConstructor($class_name) |
|
84 | - { |
|
85 | - if (! is_string($class_name)) { |
|
86 | - throw new InvalidDataTypeException($class_name, '$class_name', 'string (fully qualified class name)'); |
|
87 | - } |
|
88 | - if (! isset($this->constructors[ $class_name ])) { |
|
89 | - $reflection_class = $this->getReflectionClass($class_name); |
|
90 | - $this->constructors[ $class_name ] = $reflection_class->getConstructor(); |
|
91 | - } |
|
92 | - return $this->constructors[ $class_name ]; |
|
93 | - } |
|
94 | - |
|
95 | - |
|
96 | - /** |
|
97 | - * @param ReflectionClass $reflection_class |
|
98 | - * @return ReflectionMethod |
|
99 | - * @throws InvalidDataTypeException |
|
100 | - * @throws ReflectionException |
|
101 | - */ |
|
102 | - public function getConstructorFromReflection(ReflectionClass $reflection_class) |
|
103 | - { |
|
104 | - return $this->getConstructor($reflection_class->getName()); |
|
105 | - } |
|
106 | - |
|
107 | - |
|
108 | - /** |
|
109 | - * @param string $class_name |
|
110 | - * @return ReflectionParameter[] |
|
111 | - * @throws InvalidDataTypeException |
|
112 | - * @throws ReflectionException |
|
113 | - */ |
|
114 | - public function getParameters($class_name) |
|
115 | - { |
|
116 | - if (! isset($this->parameters[ $class_name ])) { |
|
117 | - $constructor = $this->getConstructor($class_name); |
|
118 | - $this->parameters[ $class_name ] = $constructor->getParameters(); |
|
119 | - } |
|
120 | - return $this->parameters[ $class_name ]; |
|
121 | - } |
|
122 | - |
|
123 | - |
|
124 | - /** |
|
125 | - * @param ReflectionClass $reflection_class |
|
126 | - * @return ReflectionParameter[] |
|
127 | - * @throws InvalidDataTypeException |
|
128 | - * @throws ReflectionException |
|
129 | - */ |
|
130 | - public function getParametersFromReflection(ReflectionClass $reflection_class) |
|
131 | - { |
|
132 | - return $this->getParameters($reflection_class->getName()); |
|
133 | - } |
|
134 | - |
|
135 | - |
|
136 | - /** |
|
137 | - * @param ReflectionMethod $constructor |
|
138 | - * @return ReflectionParameter[] |
|
139 | - * @throws InvalidDataTypeException |
|
140 | - * @throws ReflectionException |
|
141 | - */ |
|
142 | - public function getParametersFromReflectionConstructor(ReflectionMethod $constructor) |
|
143 | - { |
|
144 | - return $this->getParameters($constructor->getDeclaringClass()); |
|
145 | - } |
|
146 | - |
|
147 | - |
|
148 | - /** |
|
149 | - * @param ReflectionParameter $param |
|
150 | - * @param string $class_name |
|
151 | - * @param string $index |
|
152 | - * @return string|null |
|
153 | - */ |
|
154 | - public function getParameterClassName(ReflectionParameter $param, $class_name, $index) |
|
155 | - { |
|
156 | - if (isset($this->parameter_classes[ $class_name ][ $index ]['param_class_name'])) { |
|
157 | - return $this->parameter_classes[ $class_name ][ $index ]['param_class_name']; |
|
158 | - } |
|
159 | - if (! isset($this->parameter_classes[ $class_name ])) { |
|
160 | - $this->parameter_classes[ $class_name ] = array(); |
|
161 | - } |
|
162 | - if (! isset($this->parameter_classes[ $class_name ][ $index ])) { |
|
163 | - $this->parameter_classes[ $class_name ][ $index ] = array(); |
|
164 | - } |
|
165 | - $this->parameter_classes[ $class_name ][ $index ]['param_class_name'] = $param->getClass() |
|
166 | - ? $param->getClass()->name |
|
167 | - : null; |
|
168 | - return $this->parameter_classes[ $class_name ][ $index ]['param_class_name']; |
|
169 | - } |
|
170 | - |
|
171 | - |
|
172 | - /** |
|
173 | - * @param ReflectionParameter $param |
|
174 | - * @param string $class_name |
|
175 | - * @param string $index |
|
176 | - * @return string|null |
|
177 | - */ |
|
178 | - public function getParameterDefaultValue(ReflectionParameter $param, $class_name, $index) |
|
179 | - { |
|
180 | - if (isset($this->parameter_classes[ $class_name ][ $index ]['param_class_default'])) { |
|
181 | - return $this->parameter_classes[ $class_name ][ $index ]['param_class_default']; |
|
182 | - } |
|
183 | - if (! isset($this->parameter_classes[ $class_name ])) { |
|
184 | - $this->parameter_classes[ $class_name ] = array(); |
|
185 | - } |
|
186 | - if (! isset($this->parameter_classes[ $class_name ][ $index ])) { |
|
187 | - $this->parameter_classes[ $class_name ][ $index ] = array(); |
|
188 | - } |
|
189 | - $this->parameter_classes[ $class_name ][ $index ]['param_class_default'] = $param->isDefaultValueAvailable() |
|
190 | - ? $param->getDefaultValue() |
|
191 | - : null; |
|
192 | - return $this->parameter_classes[ $class_name ][ $index ]['param_class_default']; |
|
193 | - } |
|
194 | - |
|
195 | - |
|
196 | - /** |
|
197 | - * @param string $class_name |
|
198 | - * @return ReflectionProperty[] |
|
199 | - * @throws InvalidDataTypeException |
|
200 | - * @throws ReflectionException |
|
201 | - */ |
|
202 | - public function getProperties($class_name) |
|
203 | - { |
|
204 | - if (! isset($this->properties[ $class_name ])) { |
|
205 | - $reflection_class = $this->getReflectionClass($class_name); |
|
206 | - $this->properties[ $class_name ] = $reflection_class->getProperties(); |
|
207 | - } |
|
208 | - return $this->properties[ $class_name ]; |
|
209 | - } |
|
210 | - |
|
211 | - |
|
212 | - /** |
|
213 | - * @param ReflectionClass $reflection_class |
|
214 | - * @return ReflectionProperty[] |
|
215 | - * @throws InvalidDataTypeException |
|
216 | - * @throws ReflectionException |
|
217 | - */ |
|
218 | - public function getPropertiesFromReflection(ReflectionClass $reflection_class) |
|
219 | - { |
|
220 | - return $this->getProperties($reflection_class->getName()); |
|
221 | - } |
|
222 | - |
|
223 | - |
|
224 | - /** |
|
225 | - * @param string $class_name |
|
226 | - * @return ReflectionMethod[] |
|
227 | - * @throws InvalidDataTypeException |
|
228 | - * @throws ReflectionException |
|
229 | - */ |
|
230 | - public function getMethods($class_name) |
|
231 | - { |
|
232 | - if (! isset($this->methods[ $class_name ])) { |
|
233 | - $reflection_class = $this->getReflectionClass($class_name); |
|
234 | - $this->methods[ $class_name ] = $reflection_class->getMethods(); |
|
235 | - } |
|
236 | - return $this->methods[ $class_name ]; |
|
237 | - } |
|
238 | - |
|
239 | - |
|
240 | - /** |
|
241 | - * @param ReflectionClass $reflection_class ) |
|
242 | - * @return ReflectionMethod[] |
|
243 | - * @throws InvalidDataTypeException |
|
244 | - * @throws ReflectionException |
|
245 | - */ |
|
246 | - public function getMethodsFromReflection(ReflectionClass $reflection_class) |
|
247 | - { |
|
248 | - return $this->getMethods($reflection_class->getName()); |
|
249 | - } |
|
28 | + /** |
|
29 | + * @var ReflectionClass[] $classes |
|
30 | + */ |
|
31 | + private $classes = array(); |
|
32 | + |
|
33 | + /** |
|
34 | + * @var ReflectionMethod[] $constructors |
|
35 | + */ |
|
36 | + private $constructors = array(); |
|
37 | + |
|
38 | + /** |
|
39 | + * @var ReflectionParameter[][] $parameters |
|
40 | + */ |
|
41 | + private $parameters = array(); |
|
42 | + |
|
43 | + /** |
|
44 | + * @var ReflectionParameter[][] $parameters |
|
45 | + */ |
|
46 | + private $parameter_classes = array(); |
|
47 | + |
|
48 | + /** |
|
49 | + * @var ReflectionProperty[][] $properties |
|
50 | + */ |
|
51 | + private $properties = array(); |
|
52 | + |
|
53 | + /** |
|
54 | + * @var ReflectionMethod[][] $methods |
|
55 | + */ |
|
56 | + private $methods = array(); |
|
57 | + |
|
58 | + |
|
59 | + /** |
|
60 | + * @param string $class_name |
|
61 | + * @return ReflectionClass |
|
62 | + * @throws ReflectionException |
|
63 | + * @throws InvalidDataTypeException |
|
64 | + */ |
|
65 | + public function getReflectionClass($class_name) |
|
66 | + { |
|
67 | + if (! is_string($class_name)) { |
|
68 | + throw new InvalidDataTypeException($class_name, '$class_name', 'string (fully qualified class name)'); |
|
69 | + } |
|
70 | + if (! isset($this->classes[ $class_name ])) { |
|
71 | + $this->classes[ $class_name ] = new ReflectionClass($class_name); |
|
72 | + } |
|
73 | + return $this->classes[ $class_name ]; |
|
74 | + } |
|
75 | + |
|
76 | + |
|
77 | + /** |
|
78 | + * @param string $class_name |
|
79 | + * @return ReflectionMethod |
|
80 | + * @throws InvalidDataTypeException |
|
81 | + * @throws ReflectionException |
|
82 | + */ |
|
83 | + public function getConstructor($class_name) |
|
84 | + { |
|
85 | + if (! is_string($class_name)) { |
|
86 | + throw new InvalidDataTypeException($class_name, '$class_name', 'string (fully qualified class name)'); |
|
87 | + } |
|
88 | + if (! isset($this->constructors[ $class_name ])) { |
|
89 | + $reflection_class = $this->getReflectionClass($class_name); |
|
90 | + $this->constructors[ $class_name ] = $reflection_class->getConstructor(); |
|
91 | + } |
|
92 | + return $this->constructors[ $class_name ]; |
|
93 | + } |
|
94 | + |
|
95 | + |
|
96 | + /** |
|
97 | + * @param ReflectionClass $reflection_class |
|
98 | + * @return ReflectionMethod |
|
99 | + * @throws InvalidDataTypeException |
|
100 | + * @throws ReflectionException |
|
101 | + */ |
|
102 | + public function getConstructorFromReflection(ReflectionClass $reflection_class) |
|
103 | + { |
|
104 | + return $this->getConstructor($reflection_class->getName()); |
|
105 | + } |
|
106 | + |
|
107 | + |
|
108 | + /** |
|
109 | + * @param string $class_name |
|
110 | + * @return ReflectionParameter[] |
|
111 | + * @throws InvalidDataTypeException |
|
112 | + * @throws ReflectionException |
|
113 | + */ |
|
114 | + public function getParameters($class_name) |
|
115 | + { |
|
116 | + if (! isset($this->parameters[ $class_name ])) { |
|
117 | + $constructor = $this->getConstructor($class_name); |
|
118 | + $this->parameters[ $class_name ] = $constructor->getParameters(); |
|
119 | + } |
|
120 | + return $this->parameters[ $class_name ]; |
|
121 | + } |
|
122 | + |
|
123 | + |
|
124 | + /** |
|
125 | + * @param ReflectionClass $reflection_class |
|
126 | + * @return ReflectionParameter[] |
|
127 | + * @throws InvalidDataTypeException |
|
128 | + * @throws ReflectionException |
|
129 | + */ |
|
130 | + public function getParametersFromReflection(ReflectionClass $reflection_class) |
|
131 | + { |
|
132 | + return $this->getParameters($reflection_class->getName()); |
|
133 | + } |
|
134 | + |
|
135 | + |
|
136 | + /** |
|
137 | + * @param ReflectionMethod $constructor |
|
138 | + * @return ReflectionParameter[] |
|
139 | + * @throws InvalidDataTypeException |
|
140 | + * @throws ReflectionException |
|
141 | + */ |
|
142 | + public function getParametersFromReflectionConstructor(ReflectionMethod $constructor) |
|
143 | + { |
|
144 | + return $this->getParameters($constructor->getDeclaringClass()); |
|
145 | + } |
|
146 | + |
|
147 | + |
|
148 | + /** |
|
149 | + * @param ReflectionParameter $param |
|
150 | + * @param string $class_name |
|
151 | + * @param string $index |
|
152 | + * @return string|null |
|
153 | + */ |
|
154 | + public function getParameterClassName(ReflectionParameter $param, $class_name, $index) |
|
155 | + { |
|
156 | + if (isset($this->parameter_classes[ $class_name ][ $index ]['param_class_name'])) { |
|
157 | + return $this->parameter_classes[ $class_name ][ $index ]['param_class_name']; |
|
158 | + } |
|
159 | + if (! isset($this->parameter_classes[ $class_name ])) { |
|
160 | + $this->parameter_classes[ $class_name ] = array(); |
|
161 | + } |
|
162 | + if (! isset($this->parameter_classes[ $class_name ][ $index ])) { |
|
163 | + $this->parameter_classes[ $class_name ][ $index ] = array(); |
|
164 | + } |
|
165 | + $this->parameter_classes[ $class_name ][ $index ]['param_class_name'] = $param->getClass() |
|
166 | + ? $param->getClass()->name |
|
167 | + : null; |
|
168 | + return $this->parameter_classes[ $class_name ][ $index ]['param_class_name']; |
|
169 | + } |
|
170 | + |
|
171 | + |
|
172 | + /** |
|
173 | + * @param ReflectionParameter $param |
|
174 | + * @param string $class_name |
|
175 | + * @param string $index |
|
176 | + * @return string|null |
|
177 | + */ |
|
178 | + public function getParameterDefaultValue(ReflectionParameter $param, $class_name, $index) |
|
179 | + { |
|
180 | + if (isset($this->parameter_classes[ $class_name ][ $index ]['param_class_default'])) { |
|
181 | + return $this->parameter_classes[ $class_name ][ $index ]['param_class_default']; |
|
182 | + } |
|
183 | + if (! isset($this->parameter_classes[ $class_name ])) { |
|
184 | + $this->parameter_classes[ $class_name ] = array(); |
|
185 | + } |
|
186 | + if (! isset($this->parameter_classes[ $class_name ][ $index ])) { |
|
187 | + $this->parameter_classes[ $class_name ][ $index ] = array(); |
|
188 | + } |
|
189 | + $this->parameter_classes[ $class_name ][ $index ]['param_class_default'] = $param->isDefaultValueAvailable() |
|
190 | + ? $param->getDefaultValue() |
|
191 | + : null; |
|
192 | + return $this->parameter_classes[ $class_name ][ $index ]['param_class_default']; |
|
193 | + } |
|
194 | + |
|
195 | + |
|
196 | + /** |
|
197 | + * @param string $class_name |
|
198 | + * @return ReflectionProperty[] |
|
199 | + * @throws InvalidDataTypeException |
|
200 | + * @throws ReflectionException |
|
201 | + */ |
|
202 | + public function getProperties($class_name) |
|
203 | + { |
|
204 | + if (! isset($this->properties[ $class_name ])) { |
|
205 | + $reflection_class = $this->getReflectionClass($class_name); |
|
206 | + $this->properties[ $class_name ] = $reflection_class->getProperties(); |
|
207 | + } |
|
208 | + return $this->properties[ $class_name ]; |
|
209 | + } |
|
210 | + |
|
211 | + |
|
212 | + /** |
|
213 | + * @param ReflectionClass $reflection_class |
|
214 | + * @return ReflectionProperty[] |
|
215 | + * @throws InvalidDataTypeException |
|
216 | + * @throws ReflectionException |
|
217 | + */ |
|
218 | + public function getPropertiesFromReflection(ReflectionClass $reflection_class) |
|
219 | + { |
|
220 | + return $this->getProperties($reflection_class->getName()); |
|
221 | + } |
|
222 | + |
|
223 | + |
|
224 | + /** |
|
225 | + * @param string $class_name |
|
226 | + * @return ReflectionMethod[] |
|
227 | + * @throws InvalidDataTypeException |
|
228 | + * @throws ReflectionException |
|
229 | + */ |
|
230 | + public function getMethods($class_name) |
|
231 | + { |
|
232 | + if (! isset($this->methods[ $class_name ])) { |
|
233 | + $reflection_class = $this->getReflectionClass($class_name); |
|
234 | + $this->methods[ $class_name ] = $reflection_class->getMethods(); |
|
235 | + } |
|
236 | + return $this->methods[ $class_name ]; |
|
237 | + } |
|
238 | + |
|
239 | + |
|
240 | + /** |
|
241 | + * @param ReflectionClass $reflection_class ) |
|
242 | + * @return ReflectionMethod[] |
|
243 | + * @throws InvalidDataTypeException |
|
244 | + * @throws ReflectionException |
|
245 | + */ |
|
246 | + public function getMethodsFromReflection(ReflectionClass $reflection_class) |
|
247 | + { |
|
248 | + return $this->getMethods($reflection_class->getName()); |
|
249 | + } |
|
250 | 250 | } |
@@ -64,13 +64,13 @@ discard block |
||
64 | 64 | */ |
65 | 65 | public function getReflectionClass($class_name) |
66 | 66 | { |
67 | - if (! is_string($class_name)) { |
|
67 | + if ( ! is_string($class_name)) { |
|
68 | 68 | throw new InvalidDataTypeException($class_name, '$class_name', 'string (fully qualified class name)'); |
69 | 69 | } |
70 | - if (! isset($this->classes[ $class_name ])) { |
|
71 | - $this->classes[ $class_name ] = new ReflectionClass($class_name); |
|
70 | + if ( ! isset($this->classes[$class_name])) { |
|
71 | + $this->classes[$class_name] = new ReflectionClass($class_name); |
|
72 | 72 | } |
73 | - return $this->classes[ $class_name ]; |
|
73 | + return $this->classes[$class_name]; |
|
74 | 74 | } |
75 | 75 | |
76 | 76 | |
@@ -82,14 +82,14 @@ discard block |
||
82 | 82 | */ |
83 | 83 | public function getConstructor($class_name) |
84 | 84 | { |
85 | - if (! is_string($class_name)) { |
|
85 | + if ( ! is_string($class_name)) { |
|
86 | 86 | throw new InvalidDataTypeException($class_name, '$class_name', 'string (fully qualified class name)'); |
87 | 87 | } |
88 | - if (! isset($this->constructors[ $class_name ])) { |
|
88 | + if ( ! isset($this->constructors[$class_name])) { |
|
89 | 89 | $reflection_class = $this->getReflectionClass($class_name); |
90 | - $this->constructors[ $class_name ] = $reflection_class->getConstructor(); |
|
90 | + $this->constructors[$class_name] = $reflection_class->getConstructor(); |
|
91 | 91 | } |
92 | - return $this->constructors[ $class_name ]; |
|
92 | + return $this->constructors[$class_name]; |
|
93 | 93 | } |
94 | 94 | |
95 | 95 | |
@@ -113,11 +113,11 @@ discard block |
||
113 | 113 | */ |
114 | 114 | public function getParameters($class_name) |
115 | 115 | { |
116 | - if (! isset($this->parameters[ $class_name ])) { |
|
116 | + if ( ! isset($this->parameters[$class_name])) { |
|
117 | 117 | $constructor = $this->getConstructor($class_name); |
118 | - $this->parameters[ $class_name ] = $constructor->getParameters(); |
|
118 | + $this->parameters[$class_name] = $constructor->getParameters(); |
|
119 | 119 | } |
120 | - return $this->parameters[ $class_name ]; |
|
120 | + return $this->parameters[$class_name]; |
|
121 | 121 | } |
122 | 122 | |
123 | 123 | |
@@ -153,19 +153,19 @@ discard block |
||
153 | 153 | */ |
154 | 154 | public function getParameterClassName(ReflectionParameter $param, $class_name, $index) |
155 | 155 | { |
156 | - if (isset($this->parameter_classes[ $class_name ][ $index ]['param_class_name'])) { |
|
157 | - return $this->parameter_classes[ $class_name ][ $index ]['param_class_name']; |
|
156 | + if (isset($this->parameter_classes[$class_name][$index]['param_class_name'])) { |
|
157 | + return $this->parameter_classes[$class_name][$index]['param_class_name']; |
|
158 | 158 | } |
159 | - if (! isset($this->parameter_classes[ $class_name ])) { |
|
160 | - $this->parameter_classes[ $class_name ] = array(); |
|
159 | + if ( ! isset($this->parameter_classes[$class_name])) { |
|
160 | + $this->parameter_classes[$class_name] = array(); |
|
161 | 161 | } |
162 | - if (! isset($this->parameter_classes[ $class_name ][ $index ])) { |
|
163 | - $this->parameter_classes[ $class_name ][ $index ] = array(); |
|
162 | + if ( ! isset($this->parameter_classes[$class_name][$index])) { |
|
163 | + $this->parameter_classes[$class_name][$index] = array(); |
|
164 | 164 | } |
165 | - $this->parameter_classes[ $class_name ][ $index ]['param_class_name'] = $param->getClass() |
|
165 | + $this->parameter_classes[$class_name][$index]['param_class_name'] = $param->getClass() |
|
166 | 166 | ? $param->getClass()->name |
167 | 167 | : null; |
168 | - return $this->parameter_classes[ $class_name ][ $index ]['param_class_name']; |
|
168 | + return $this->parameter_classes[$class_name][$index]['param_class_name']; |
|
169 | 169 | } |
170 | 170 | |
171 | 171 | |
@@ -177,19 +177,19 @@ discard block |
||
177 | 177 | */ |
178 | 178 | public function getParameterDefaultValue(ReflectionParameter $param, $class_name, $index) |
179 | 179 | { |
180 | - if (isset($this->parameter_classes[ $class_name ][ $index ]['param_class_default'])) { |
|
181 | - return $this->parameter_classes[ $class_name ][ $index ]['param_class_default']; |
|
180 | + if (isset($this->parameter_classes[$class_name][$index]['param_class_default'])) { |
|
181 | + return $this->parameter_classes[$class_name][$index]['param_class_default']; |
|
182 | 182 | } |
183 | - if (! isset($this->parameter_classes[ $class_name ])) { |
|
184 | - $this->parameter_classes[ $class_name ] = array(); |
|
183 | + if ( ! isset($this->parameter_classes[$class_name])) { |
|
184 | + $this->parameter_classes[$class_name] = array(); |
|
185 | 185 | } |
186 | - if (! isset($this->parameter_classes[ $class_name ][ $index ])) { |
|
187 | - $this->parameter_classes[ $class_name ][ $index ] = array(); |
|
186 | + if ( ! isset($this->parameter_classes[$class_name][$index])) { |
|
187 | + $this->parameter_classes[$class_name][$index] = array(); |
|
188 | 188 | } |
189 | - $this->parameter_classes[ $class_name ][ $index ]['param_class_default'] = $param->isDefaultValueAvailable() |
|
189 | + $this->parameter_classes[$class_name][$index]['param_class_default'] = $param->isDefaultValueAvailable() |
|
190 | 190 | ? $param->getDefaultValue() |
191 | 191 | : null; |
192 | - return $this->parameter_classes[ $class_name ][ $index ]['param_class_default']; |
|
192 | + return $this->parameter_classes[$class_name][$index]['param_class_default']; |
|
193 | 193 | } |
194 | 194 | |
195 | 195 | |
@@ -201,11 +201,11 @@ discard block |
||
201 | 201 | */ |
202 | 202 | public function getProperties($class_name) |
203 | 203 | { |
204 | - if (! isset($this->properties[ $class_name ])) { |
|
204 | + if ( ! isset($this->properties[$class_name])) { |
|
205 | 205 | $reflection_class = $this->getReflectionClass($class_name); |
206 | - $this->properties[ $class_name ] = $reflection_class->getProperties(); |
|
206 | + $this->properties[$class_name] = $reflection_class->getProperties(); |
|
207 | 207 | } |
208 | - return $this->properties[ $class_name ]; |
|
208 | + return $this->properties[$class_name]; |
|
209 | 209 | } |
210 | 210 | |
211 | 211 | |
@@ -229,11 +229,11 @@ discard block |
||
229 | 229 | */ |
230 | 230 | public function getMethods($class_name) |
231 | 231 | { |
232 | - if (! isset($this->methods[ $class_name ])) { |
|
232 | + if ( ! isset($this->methods[$class_name])) { |
|
233 | 233 | $reflection_class = $this->getReflectionClass($class_name); |
234 | - $this->methods[ $class_name ] = $reflection_class->getMethods(); |
|
234 | + $this->methods[$class_name] = $reflection_class->getMethods(); |
|
235 | 235 | } |
236 | - return $this->methods[ $class_name ]; |
|
236 | + return $this->methods[$class_name]; |
|
237 | 237 | } |
238 | 238 | |
239 | 239 |
@@ -65,7 +65,7 @@ |
||
65 | 65 | */ |
66 | 66 | public function registerCustomTaxonomyTerm($taxonomy, $term_slug, array $cpt_slugs = array()) |
67 | 67 | { |
68 | - $this->custom_taxonomy_terms[][ $term_slug ] = new CustomTaxonomyTerm( |
|
68 | + $this->custom_taxonomy_terms[][$term_slug] = new CustomTaxonomyTerm( |
|
69 | 69 | $taxonomy, |
70 | 70 | $term_slug, |
71 | 71 | $cpt_slugs |
@@ -16,180 +16,180 @@ |
||
16 | 16 | class RegisterCustomTaxonomyTerms |
17 | 17 | { |
18 | 18 | |
19 | - /** |
|
20 | - * @var array[] $custom_taxonomy_terms |
|
21 | - */ |
|
22 | - public $custom_taxonomy_terms = array(); |
|
23 | - |
|
24 | - |
|
25 | - /** |
|
26 | - * RegisterCustomTaxonomyTerms constructor. |
|
27 | - */ |
|
28 | - public function __construct() |
|
29 | - { |
|
30 | - // hook into save_post so that we can make sure that the default terms get saved on publish of registered cpts |
|
31 | - // IF they don't have a term for that taxonomy set. |
|
32 | - add_action('save_post', array($this, 'saveDefaultTerm'), 100, 2); |
|
33 | - do_action( |
|
34 | - 'AHEE__EventEspresso_core_domain_services_custom_post_types_RegisterCustomTaxonomyTerms__construct_end', |
|
35 | - $this |
|
36 | - ); |
|
37 | - } |
|
38 | - |
|
39 | - |
|
40 | - public function registerCustomTaxonomyTerms() |
|
41 | - { |
|
42 | - // setup default terms in any of our taxonomies (but only if we're in admin). |
|
43 | - // Why not added via register_activation_hook? |
|
44 | - // Because it's possible that in future iterations of EE we may add new defaults for specialized taxonomies |
|
45 | - // (think event_types) and register_activation_hook only reliably runs when a user manually activates the plugin. |
|
46 | - // Keep in mind that this will READ these terms if they are deleted by the user. Hence MUST use terms. |
|
47 | - // if ( is_admin() ) { |
|
48 | - // $this->set_must_use_event_types(); |
|
49 | - // } |
|
50 | - // set default terms |
|
51 | - $this->registerCustomTaxonomyTerm( |
|
52 | - 'espresso_event_type', |
|
53 | - 'single-event', |
|
54 | - array('espresso_events') |
|
55 | - ); |
|
56 | - } |
|
57 | - |
|
58 | - |
|
59 | - /** |
|
60 | - * Allows us to set what the default will be for terms when a cpt is PUBLISHED. |
|
61 | - * |
|
62 | - * @param string $taxonomy The taxonomy we're using for the default term |
|
63 | - * @param string $term_slug The slug of the term that will be the default. |
|
64 | - * @param array $cpt_slugs An array of custom post types we want the default assigned to |
|
65 | - */ |
|
66 | - public function registerCustomTaxonomyTerm($taxonomy, $term_slug, array $cpt_slugs = array()) |
|
67 | - { |
|
68 | - $this->custom_taxonomy_terms[][ $term_slug ] = new CustomTaxonomyTerm( |
|
69 | - $taxonomy, |
|
70 | - $term_slug, |
|
71 | - $cpt_slugs |
|
72 | - ); |
|
73 | - } |
|
74 | - |
|
75 | - |
|
76 | - /** |
|
77 | - * hooked into the wp 'save_post' action hook for setting our default terms found in the $_default_terms property |
|
78 | - * |
|
79 | - * @param int $post_id ID of CPT being saved |
|
80 | - * @param WP_Post $post Post object |
|
81 | - * @return void |
|
82 | - */ |
|
83 | - public function saveDefaultTerm($post_id, WP_Post $post) |
|
84 | - { |
|
85 | - if (empty($this->custom_taxonomy_terms)) { |
|
86 | - return; |
|
87 | - } |
|
88 | - // no default terms set so lets just exit. |
|
89 | - foreach ($this->custom_taxonomy_terms as $custom_taxonomy_terms) { |
|
90 | - foreach ($custom_taxonomy_terms as $custom_taxonomy_term) { |
|
91 | - if ($post->post_status === 'publish' |
|
92 | - && $custom_taxonomy_term instanceof CustomTaxonomyTerm |
|
93 | - && in_array($post->post_type, $custom_taxonomy_term->customPostTypeSlugs(), true) |
|
94 | - ) { |
|
95 | - // note some error proofing going on here to save unnecessary db queries |
|
96 | - $taxonomies = get_object_taxonomies($post->post_type); |
|
97 | - foreach ($taxonomies as $taxonomy) { |
|
98 | - $terms = wp_get_post_terms($post_id, $taxonomy); |
|
99 | - if (empty($terms) && $taxonomy === $custom_taxonomy_term->taxonomySlug()) { |
|
100 | - wp_set_object_terms( |
|
101 | - $post_id, |
|
102 | - array($custom_taxonomy_term->termSlug()), |
|
103 | - $taxonomy |
|
104 | - ); |
|
105 | - } |
|
106 | - } |
|
107 | - } |
|
108 | - } |
|
109 | - } |
|
110 | - } |
|
111 | - |
|
112 | - |
|
113 | - /** |
|
114 | - * @return void |
|
115 | - */ |
|
116 | - public function setMustUseEventTypes() |
|
117 | - { |
|
118 | - $term_details = array( |
|
119 | - // Attendee's register for the first date-time only |
|
120 | - 'single-event' => array( |
|
121 | - 'term' => esc_html__('Single Event', 'event_espresso'), |
|
122 | - 'desc' => esc_html__( |
|
123 | - 'A single event that spans one or more consecutive days.', |
|
124 | - 'event_espresso' |
|
125 | - ), |
|
126 | - ), |
|
127 | - // example: a party or two-day long workshop |
|
128 | - // Attendee's can register for any of the date-times |
|
129 | - 'multi-event' => array( |
|
130 | - 'term' => esc_html__('Multi Event', 'event_espresso'), |
|
131 | - 'desc' => esc_html__( |
|
132 | - 'Multiple, separate, but related events that occur on consecutive days.', |
|
133 | - 'event_espresso' |
|
134 | - ), |
|
135 | - ), |
|
136 | - // example: a three day music festival or week long conference |
|
137 | - // Attendee's register for the first date-time only |
|
138 | - 'event-series' => array( |
|
139 | - 'term' => esc_html__('Event Series', 'event_espresso'), |
|
140 | - 'desc' => esc_html__( |
|
141 | - ' Multiple events that occur over multiple non-consecutive days.', |
|
142 | - 'event_espresso' |
|
143 | - ), |
|
144 | - ), |
|
145 | - // example: an 8 week introduction to basket weaving course |
|
146 | - // Attendee's can register for any of the date-times. |
|
147 | - 'recurring-event' => array( |
|
148 | - 'term' => esc_html__('Recurring Event', 'event_espresso'), |
|
149 | - 'desc' => esc_html__( |
|
150 | - 'Multiple events that occur over multiple non-consecutive days.', |
|
151 | - 'event_espresso' |
|
152 | - ), |
|
153 | - ), |
|
154 | - // example: a yoga class |
|
155 | - 'ongoing' => array( |
|
156 | - 'term' => esc_html__('Ongoing Event', 'event_espresso'), |
|
157 | - 'desc' => esc_html__( |
|
158 | - 'An "event" that people can purchase tickets to gain access for anytime for this event regardless of date times on the event', |
|
159 | - 'event_espresso' |
|
160 | - ), |
|
161 | - ) |
|
162 | - // example: access to a museum |
|
163 | - // 'walk-in' => array( esc_html__('Walk In', 'event_espresso'), esc_html__('Single datetime and single entry recurring events. Attendees register for one or multiple datetimes individually.', 'event_espresso') ), |
|
164 | - // 'reservation' => array( esc_html__('Reservation', 'event_espresso'), esc_html__('Reservations are created by specifying available datetimes and quantities. Attendees choose from the available datetimes and specify the quantity available (if the maximum is greater than 1)') ), //@TODO to avoid confusion we'll implement this in a later iteration > EE4.1 |
|
165 | - // 'multiple-session' => array( esc_html__('Multiple Session', 'event_espresso'), esc_html__('Multiple event, multiple datetime, hierarchically organized, custom entry events. Attendees may be required to register for a parent event before being allowed to register for child events. Attendees can register for any combination of child events as long as the datetimes do not conflict. Parent and child events may have additional fees or registration questions.') ), //@TODO to avoid confusion we'll implement this in a later iteration > EE4.1 |
|
166 | - // 'appointment' => array( esc_html__('Appointments', 'event_espresso'), esc_html__('Time slotted events where datetimes are generally in hours or minutes. For example, attendees can register for a single 15 minute or 1 hour time slot and this type of availability frequently reoccurs.', 'event_espresso') ) |
|
167 | - ); |
|
168 | - $this->setMustUseTerms('espresso_event_type', $term_details); |
|
169 | - } |
|
170 | - |
|
171 | - |
|
172 | - /** |
|
173 | - * wrapper method for handling the setting up of initial terms in the db (if they don't already exist). |
|
174 | - * Note this should ONLY be used for terms that always must be present. Be aware that if an initial term is |
|
175 | - * deleted then it WILL be recreated. |
|
176 | - * |
|
177 | - * @param string $taxonomy The name of the taxonomy |
|
178 | - * @param array $term_details An array of term details indexed by slug and containing Name of term, and |
|
179 | - * description as the elements in the array |
|
180 | - * @return void |
|
181 | - */ |
|
182 | - public function setMustUseTerms($taxonomy, $term_details) |
|
183 | - { |
|
184 | - $term_details = (array) $term_details; |
|
185 | - foreach ($term_details as $slug => $details) { |
|
186 | - if (isset($details['term'], $details['desc']) && ! term_exists($slug, $taxonomy)) { |
|
187 | - $insert_arr = array( |
|
188 | - 'slug' => $slug, |
|
189 | - 'description' => $details['desc'], |
|
190 | - ); |
|
191 | - wp_insert_term($details['term'], $taxonomy, $insert_arr); |
|
192 | - } |
|
193 | - } |
|
194 | - } |
|
19 | + /** |
|
20 | + * @var array[] $custom_taxonomy_terms |
|
21 | + */ |
|
22 | + public $custom_taxonomy_terms = array(); |
|
23 | + |
|
24 | + |
|
25 | + /** |
|
26 | + * RegisterCustomTaxonomyTerms constructor. |
|
27 | + */ |
|
28 | + public function __construct() |
|
29 | + { |
|
30 | + // hook into save_post so that we can make sure that the default terms get saved on publish of registered cpts |
|
31 | + // IF they don't have a term for that taxonomy set. |
|
32 | + add_action('save_post', array($this, 'saveDefaultTerm'), 100, 2); |
|
33 | + do_action( |
|
34 | + 'AHEE__EventEspresso_core_domain_services_custom_post_types_RegisterCustomTaxonomyTerms__construct_end', |
|
35 | + $this |
|
36 | + ); |
|
37 | + } |
|
38 | + |
|
39 | + |
|
40 | + public function registerCustomTaxonomyTerms() |
|
41 | + { |
|
42 | + // setup default terms in any of our taxonomies (but only if we're in admin). |
|
43 | + // Why not added via register_activation_hook? |
|
44 | + // Because it's possible that in future iterations of EE we may add new defaults for specialized taxonomies |
|
45 | + // (think event_types) and register_activation_hook only reliably runs when a user manually activates the plugin. |
|
46 | + // Keep in mind that this will READ these terms if they are deleted by the user. Hence MUST use terms. |
|
47 | + // if ( is_admin() ) { |
|
48 | + // $this->set_must_use_event_types(); |
|
49 | + // } |
|
50 | + // set default terms |
|
51 | + $this->registerCustomTaxonomyTerm( |
|
52 | + 'espresso_event_type', |
|
53 | + 'single-event', |
|
54 | + array('espresso_events') |
|
55 | + ); |
|
56 | + } |
|
57 | + |
|
58 | + |
|
59 | + /** |
|
60 | + * Allows us to set what the default will be for terms when a cpt is PUBLISHED. |
|
61 | + * |
|
62 | + * @param string $taxonomy The taxonomy we're using for the default term |
|
63 | + * @param string $term_slug The slug of the term that will be the default. |
|
64 | + * @param array $cpt_slugs An array of custom post types we want the default assigned to |
|
65 | + */ |
|
66 | + public function registerCustomTaxonomyTerm($taxonomy, $term_slug, array $cpt_slugs = array()) |
|
67 | + { |
|
68 | + $this->custom_taxonomy_terms[][ $term_slug ] = new CustomTaxonomyTerm( |
|
69 | + $taxonomy, |
|
70 | + $term_slug, |
|
71 | + $cpt_slugs |
|
72 | + ); |
|
73 | + } |
|
74 | + |
|
75 | + |
|
76 | + /** |
|
77 | + * hooked into the wp 'save_post' action hook for setting our default terms found in the $_default_terms property |
|
78 | + * |
|
79 | + * @param int $post_id ID of CPT being saved |
|
80 | + * @param WP_Post $post Post object |
|
81 | + * @return void |
|
82 | + */ |
|
83 | + public function saveDefaultTerm($post_id, WP_Post $post) |
|
84 | + { |
|
85 | + if (empty($this->custom_taxonomy_terms)) { |
|
86 | + return; |
|
87 | + } |
|
88 | + // no default terms set so lets just exit. |
|
89 | + foreach ($this->custom_taxonomy_terms as $custom_taxonomy_terms) { |
|
90 | + foreach ($custom_taxonomy_terms as $custom_taxonomy_term) { |
|
91 | + if ($post->post_status === 'publish' |
|
92 | + && $custom_taxonomy_term instanceof CustomTaxonomyTerm |
|
93 | + && in_array($post->post_type, $custom_taxonomy_term->customPostTypeSlugs(), true) |
|
94 | + ) { |
|
95 | + // note some error proofing going on here to save unnecessary db queries |
|
96 | + $taxonomies = get_object_taxonomies($post->post_type); |
|
97 | + foreach ($taxonomies as $taxonomy) { |
|
98 | + $terms = wp_get_post_terms($post_id, $taxonomy); |
|
99 | + if (empty($terms) && $taxonomy === $custom_taxonomy_term->taxonomySlug()) { |
|
100 | + wp_set_object_terms( |
|
101 | + $post_id, |
|
102 | + array($custom_taxonomy_term->termSlug()), |
|
103 | + $taxonomy |
|
104 | + ); |
|
105 | + } |
|
106 | + } |
|
107 | + } |
|
108 | + } |
|
109 | + } |
|
110 | + } |
|
111 | + |
|
112 | + |
|
113 | + /** |
|
114 | + * @return void |
|
115 | + */ |
|
116 | + public function setMustUseEventTypes() |
|
117 | + { |
|
118 | + $term_details = array( |
|
119 | + // Attendee's register for the first date-time only |
|
120 | + 'single-event' => array( |
|
121 | + 'term' => esc_html__('Single Event', 'event_espresso'), |
|
122 | + 'desc' => esc_html__( |
|
123 | + 'A single event that spans one or more consecutive days.', |
|
124 | + 'event_espresso' |
|
125 | + ), |
|
126 | + ), |
|
127 | + // example: a party or two-day long workshop |
|
128 | + // Attendee's can register for any of the date-times |
|
129 | + 'multi-event' => array( |
|
130 | + 'term' => esc_html__('Multi Event', 'event_espresso'), |
|
131 | + 'desc' => esc_html__( |
|
132 | + 'Multiple, separate, but related events that occur on consecutive days.', |
|
133 | + 'event_espresso' |
|
134 | + ), |
|
135 | + ), |
|
136 | + // example: a three day music festival or week long conference |
|
137 | + // Attendee's register for the first date-time only |
|
138 | + 'event-series' => array( |
|
139 | + 'term' => esc_html__('Event Series', 'event_espresso'), |
|
140 | + 'desc' => esc_html__( |
|
141 | + ' Multiple events that occur over multiple non-consecutive days.', |
|
142 | + 'event_espresso' |
|
143 | + ), |
|
144 | + ), |
|
145 | + // example: an 8 week introduction to basket weaving course |
|
146 | + // Attendee's can register for any of the date-times. |
|
147 | + 'recurring-event' => array( |
|
148 | + 'term' => esc_html__('Recurring Event', 'event_espresso'), |
|
149 | + 'desc' => esc_html__( |
|
150 | + 'Multiple events that occur over multiple non-consecutive days.', |
|
151 | + 'event_espresso' |
|
152 | + ), |
|
153 | + ), |
|
154 | + // example: a yoga class |
|
155 | + 'ongoing' => array( |
|
156 | + 'term' => esc_html__('Ongoing Event', 'event_espresso'), |
|
157 | + 'desc' => esc_html__( |
|
158 | + 'An "event" that people can purchase tickets to gain access for anytime for this event regardless of date times on the event', |
|
159 | + 'event_espresso' |
|
160 | + ), |
|
161 | + ) |
|
162 | + // example: access to a museum |
|
163 | + // 'walk-in' => array( esc_html__('Walk In', 'event_espresso'), esc_html__('Single datetime and single entry recurring events. Attendees register for one or multiple datetimes individually.', 'event_espresso') ), |
|
164 | + // 'reservation' => array( esc_html__('Reservation', 'event_espresso'), esc_html__('Reservations are created by specifying available datetimes and quantities. Attendees choose from the available datetimes and specify the quantity available (if the maximum is greater than 1)') ), //@TODO to avoid confusion we'll implement this in a later iteration > EE4.1 |
|
165 | + // 'multiple-session' => array( esc_html__('Multiple Session', 'event_espresso'), esc_html__('Multiple event, multiple datetime, hierarchically organized, custom entry events. Attendees may be required to register for a parent event before being allowed to register for child events. Attendees can register for any combination of child events as long as the datetimes do not conflict. Parent and child events may have additional fees or registration questions.') ), //@TODO to avoid confusion we'll implement this in a later iteration > EE4.1 |
|
166 | + // 'appointment' => array( esc_html__('Appointments', 'event_espresso'), esc_html__('Time slotted events where datetimes are generally in hours or minutes. For example, attendees can register for a single 15 minute or 1 hour time slot and this type of availability frequently reoccurs.', 'event_espresso') ) |
|
167 | + ); |
|
168 | + $this->setMustUseTerms('espresso_event_type', $term_details); |
|
169 | + } |
|
170 | + |
|
171 | + |
|
172 | + /** |
|
173 | + * wrapper method for handling the setting up of initial terms in the db (if they don't already exist). |
|
174 | + * Note this should ONLY be used for terms that always must be present. Be aware that if an initial term is |
|
175 | + * deleted then it WILL be recreated. |
|
176 | + * |
|
177 | + * @param string $taxonomy The name of the taxonomy |
|
178 | + * @param array $term_details An array of term details indexed by slug and containing Name of term, and |
|
179 | + * description as the elements in the array |
|
180 | + * @return void |
|
181 | + */ |
|
182 | + public function setMustUseTerms($taxonomy, $term_details) |
|
183 | + { |
|
184 | + $term_details = (array) $term_details; |
|
185 | + foreach ($term_details as $slug => $details) { |
|
186 | + if (isset($details['term'], $details['desc']) && ! term_exists($slug, $taxonomy)) { |
|
187 | + $insert_arr = array( |
|
188 | + 'slug' => $slug, |
|
189 | + 'description' => $details['desc'], |
|
190 | + ); |
|
191 | + wp_insert_term($details['term'], $taxonomy, $insert_arr); |
|
192 | + } |
|
193 | + } |
|
194 | + } |
|
195 | 195 | } |
@@ -47,7 +47,7 @@ |
||
47 | 47 | */ |
48 | 48 | public function __construct($generator) |
49 | 49 | { |
50 | - if (! ($generator instanceof EE_Registry || $generator instanceof CoffeeShop)) { |
|
50 | + if ( ! ($generator instanceof EE_Registry || $generator instanceof CoffeeShop)) { |
|
51 | 51 | throw new InvalidArgumentException( |
52 | 52 | esc_html__( |
53 | 53 | 'The CoreLoader class must receive an instance of EE_Registry or the CoffeeShop DI container.', |
@@ -29,108 +29,108 @@ |
||
29 | 29 | class CoreLoader implements LoaderDecoratorInterface |
30 | 30 | { |
31 | 31 | |
32 | - /** |
|
33 | - * @var EE_Registry|CoffeeShop $generator |
|
34 | - */ |
|
35 | - private $generator; |
|
32 | + /** |
|
33 | + * @var EE_Registry|CoffeeShop $generator |
|
34 | + */ |
|
35 | + private $generator; |
|
36 | 36 | |
37 | 37 | |
38 | - /** |
|
39 | - * CoreLoader constructor. |
|
40 | - * |
|
41 | - * @param EE_Registry|CoffeeShop $generator |
|
42 | - * @throws InvalidArgumentException |
|
43 | - */ |
|
44 | - public function __construct($generator) |
|
45 | - { |
|
46 | - if (! ($generator instanceof EE_Registry || $generator instanceof CoffeeShop)) { |
|
47 | - throw new InvalidArgumentException( |
|
48 | - esc_html__( |
|
49 | - 'The CoreLoader class must receive an instance of EE_Registry or the CoffeeShop DI container.', |
|
50 | - 'event_espresso' |
|
51 | - ) |
|
52 | - ); |
|
53 | - } |
|
54 | - $this->generator = $generator; |
|
55 | - } |
|
38 | + /** |
|
39 | + * CoreLoader constructor. |
|
40 | + * |
|
41 | + * @param EE_Registry|CoffeeShop $generator |
|
42 | + * @throws InvalidArgumentException |
|
43 | + */ |
|
44 | + public function __construct($generator) |
|
45 | + { |
|
46 | + if (! ($generator instanceof EE_Registry || $generator instanceof CoffeeShop)) { |
|
47 | + throw new InvalidArgumentException( |
|
48 | + esc_html__( |
|
49 | + 'The CoreLoader class must receive an instance of EE_Registry or the CoffeeShop DI container.', |
|
50 | + 'event_espresso' |
|
51 | + ) |
|
52 | + ); |
|
53 | + } |
|
54 | + $this->generator = $generator; |
|
55 | + } |
|
56 | 56 | |
57 | 57 | |
58 | - /** |
|
59 | - * Calls the appropriate loading method from the installed generator; |
|
60 | - * If EE_Registry is being used, then the additional parameters for the EE_Registry::create() method |
|
61 | - * can be added to the $arguments array and they will be extracted and passed to EE_Registry::create(), |
|
62 | - * but NOT to the class being instantiated. |
|
63 | - * This is done by adding the parameters to the $arguments array as follows: |
|
64 | - * array( |
|
65 | - * 'EE_Registry::create(from_db)' => true, // boolean value, default = false |
|
66 | - * 'EE_Registry::create(load_only)' => true, // boolean value, default = false |
|
67 | - * 'EE_Registry::create(addon)' => true, // boolean value, default = false |
|
68 | - * ) |
|
69 | - * |
|
70 | - * @param string $fqcn |
|
71 | - * @param array $arguments |
|
72 | - * @param bool $shared |
|
73 | - * @return mixed |
|
74 | - * @throws OutOfBoundsException |
|
75 | - * @throws ServiceExistsException |
|
76 | - * @throws InstantiationException |
|
77 | - * @throws InvalidIdentifierException |
|
78 | - * @throws InvalidDataTypeException |
|
79 | - * @throws InvalidClassException |
|
80 | - * @throws EE_Error |
|
81 | - * @throws ServiceNotFoundException |
|
82 | - * @throws ReflectionException |
|
83 | - * @throws InvalidInterfaceException |
|
84 | - * @throws InvalidArgumentException |
|
85 | - */ |
|
86 | - public function load($fqcn, $arguments = array(), $shared = true) |
|
87 | - { |
|
88 | - $shared = filter_var($shared, FILTER_VALIDATE_BOOLEAN); |
|
89 | - if ($this->generator instanceof EE_Registry) { |
|
90 | - // check if additional EE_Registry::create() arguments have been passed |
|
91 | - // from_db |
|
92 | - $from_db = isset($arguments['EE_Registry::create(from_db)']) |
|
93 | - ? filter_var($arguments['EE_Registry::create(from_db)'], FILTER_VALIDATE_BOOLEAN) |
|
94 | - : false; |
|
95 | - // load_only |
|
96 | - $load_only = isset($arguments['EE_Registry::create(load_only)']) |
|
97 | - ? filter_var($arguments['EE_Registry::create(load_only)'], FILTER_VALIDATE_BOOLEAN) |
|
98 | - : false; |
|
99 | - // addon |
|
100 | - $addon = isset($arguments['EE_Registry::create(addon)']) |
|
101 | - ? filter_var($arguments['EE_Registry::create(addon)'], FILTER_VALIDATE_BOOLEAN) |
|
102 | - : false; |
|
103 | - unset( |
|
104 | - $arguments['EE_Registry::create(from_db)'], |
|
105 | - $arguments['EE_Registry::create(load_only)'], |
|
106 | - $arguments['EE_Registry::create(addon)'] |
|
107 | - ); |
|
108 | - // addons need to be cached on EE_Registry |
|
109 | - $shared = $addon ? true : $shared; |
|
110 | - return $this->generator->create( |
|
111 | - $fqcn, |
|
112 | - $arguments, |
|
113 | - $shared, |
|
114 | - $from_db, |
|
115 | - $load_only, |
|
116 | - $addon |
|
117 | - ); |
|
118 | - } |
|
119 | - return $this->generator->brew( |
|
120 | - $fqcn, |
|
121 | - $arguments, |
|
122 | - $shared ? CoffeeMaker::BREW_SHARED : CoffeeMaker::BREW_NEW |
|
123 | - ); |
|
124 | - } |
|
58 | + /** |
|
59 | + * Calls the appropriate loading method from the installed generator; |
|
60 | + * If EE_Registry is being used, then the additional parameters for the EE_Registry::create() method |
|
61 | + * can be added to the $arguments array and they will be extracted and passed to EE_Registry::create(), |
|
62 | + * but NOT to the class being instantiated. |
|
63 | + * This is done by adding the parameters to the $arguments array as follows: |
|
64 | + * array( |
|
65 | + * 'EE_Registry::create(from_db)' => true, // boolean value, default = false |
|
66 | + * 'EE_Registry::create(load_only)' => true, // boolean value, default = false |
|
67 | + * 'EE_Registry::create(addon)' => true, // boolean value, default = false |
|
68 | + * ) |
|
69 | + * |
|
70 | + * @param string $fqcn |
|
71 | + * @param array $arguments |
|
72 | + * @param bool $shared |
|
73 | + * @return mixed |
|
74 | + * @throws OutOfBoundsException |
|
75 | + * @throws ServiceExistsException |
|
76 | + * @throws InstantiationException |
|
77 | + * @throws InvalidIdentifierException |
|
78 | + * @throws InvalidDataTypeException |
|
79 | + * @throws InvalidClassException |
|
80 | + * @throws EE_Error |
|
81 | + * @throws ServiceNotFoundException |
|
82 | + * @throws ReflectionException |
|
83 | + * @throws InvalidInterfaceException |
|
84 | + * @throws InvalidArgumentException |
|
85 | + */ |
|
86 | + public function load($fqcn, $arguments = array(), $shared = true) |
|
87 | + { |
|
88 | + $shared = filter_var($shared, FILTER_VALIDATE_BOOLEAN); |
|
89 | + if ($this->generator instanceof EE_Registry) { |
|
90 | + // check if additional EE_Registry::create() arguments have been passed |
|
91 | + // from_db |
|
92 | + $from_db = isset($arguments['EE_Registry::create(from_db)']) |
|
93 | + ? filter_var($arguments['EE_Registry::create(from_db)'], FILTER_VALIDATE_BOOLEAN) |
|
94 | + : false; |
|
95 | + // load_only |
|
96 | + $load_only = isset($arguments['EE_Registry::create(load_only)']) |
|
97 | + ? filter_var($arguments['EE_Registry::create(load_only)'], FILTER_VALIDATE_BOOLEAN) |
|
98 | + : false; |
|
99 | + // addon |
|
100 | + $addon = isset($arguments['EE_Registry::create(addon)']) |
|
101 | + ? filter_var($arguments['EE_Registry::create(addon)'], FILTER_VALIDATE_BOOLEAN) |
|
102 | + : false; |
|
103 | + unset( |
|
104 | + $arguments['EE_Registry::create(from_db)'], |
|
105 | + $arguments['EE_Registry::create(load_only)'], |
|
106 | + $arguments['EE_Registry::create(addon)'] |
|
107 | + ); |
|
108 | + // addons need to be cached on EE_Registry |
|
109 | + $shared = $addon ? true : $shared; |
|
110 | + return $this->generator->create( |
|
111 | + $fqcn, |
|
112 | + $arguments, |
|
113 | + $shared, |
|
114 | + $from_db, |
|
115 | + $load_only, |
|
116 | + $addon |
|
117 | + ); |
|
118 | + } |
|
119 | + return $this->generator->brew( |
|
120 | + $fqcn, |
|
121 | + $arguments, |
|
122 | + $shared ? CoffeeMaker::BREW_SHARED : CoffeeMaker::BREW_NEW |
|
123 | + ); |
|
124 | + } |
|
125 | 125 | |
126 | 126 | |
127 | - /** |
|
128 | - * calls reset() on generator if method exists |
|
129 | - */ |
|
130 | - public function reset() |
|
131 | - { |
|
132 | - if ($this->generator instanceof ResettableInterface) { |
|
133 | - $this->generator->reset(); |
|
134 | - } |
|
135 | - } |
|
127 | + /** |
|
128 | + * calls reset() on generator if method exists |
|
129 | + */ |
|
130 | + public function reset() |
|
131 | + { |
|
132 | + if ($this->generator instanceof ResettableInterface) { |
|
133 | + $this->generator->reset(); |
|
134 | + } |
|
135 | + } |
|
136 | 136 | } |