Total Complexity | 72 |
Total Lines | 545 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like MetaSlider often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MetaSlider, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
9 | class MetaSlider |
||
10 | { |
||
11 | public $id = 0; // slider ID |
||
12 | public $identifier = 0; // unique identifier |
||
13 | public $slides = []; //slides belonging to this slider |
||
14 | public $settings = []; // slider settings |
||
15 | |||
16 | /** |
||
17 | * Constructor |
||
18 | * @param $id |
||
19 | * @param $shortcode_settings |
||
20 | */ |
||
21 | public function __construct($id, $shortcode_settings) |
||
22 | { |
||
23 | $this->id = $id; |
||
24 | $this->settings = array_merge($shortcode_settings, $this->get_settings()); |
||
25 | $this->identifier = 'metaslider_' . $this->id; |
||
26 | $this->save(); |
||
27 | $this->populate_slides(); |
||
28 | } |
||
29 | |||
30 | /** |
||
31 | * Return the unique identifier for the slider (used to avoid javascript conflicts) |
||
32 | * |
||
33 | * @return string unique identifier for slider |
||
34 | */ |
||
35 | protected function get_identifier() |
||
36 | { |
||
37 | return $this->identifier; |
||
38 | } |
||
39 | |||
40 | /** |
||
41 | * Get settings for the current slider |
||
42 | * |
||
43 | * @return array slider settings |
||
44 | */ |
||
45 | private function get_settings() |
||
46 | { |
||
47 | $settings = get_post_meta($this->id, 'ml-slider_settings', true); |
||
|
|||
48 | |||
49 | if (is_array($settings) && isset($settings['type']) && in_array($settings['type'], ['flex', 'coin', 'nivo', 'responsive'])) { |
||
50 | return $settings; |
||
51 | } |
||
52 | |||
53 | return $this->get_default_parameters(); |
||
54 | } |
||
55 | |||
56 | /** |
||
57 | * Return an individual setting |
||
58 | * |
||
59 | * @param string $name Name of the setting |
||
60 | * @return string setting value or 'false' |
||
61 | */ |
||
62 | public function get_setting($name) |
||
63 | { |
||
64 | if (!isset($this->settings[$name])) { |
||
65 | $defaults = $this->get_default_parameters(); |
||
66 | |||
67 | if (isset($defaults[$name])) { |
||
68 | return $defaults[$name] ?: 'false'; |
||
69 | } |
||
70 | } else { |
||
71 | if (mb_strlen($this->settings[$name]) > 0) { |
||
72 | return $this->settings[$name]; |
||
73 | } |
||
74 | } |
||
75 | |||
76 | return 'false'; |
||
77 | } |
||
78 | |||
79 | /** |
||
80 | * Get the slider libary parameters, this lists all possible parameters and their |
||
81 | * default values. Slider subclasses override this and disable/rename parameters |
||
82 | * appropriately. |
||
83 | * |
||
84 | * @return string javascript options |
||
85 | */ |
||
86 | public function get_default_parameters() |
||
87 | { |
||
88 | $params = [ |
||
89 | 'type' => 'flex', |
||
90 | 'random' => false, |
||
91 | 'cssClass' => '', |
||
92 | 'printCss' => true, |
||
93 | 'printJs' => true, |
||
94 | 'width' => 700, |
||
95 | 'height' => 300, |
||
96 | 'spw' => 7, |
||
97 | 'sph' => 5, |
||
98 | 'delay' => 3000, |
||
99 | 'sDelay' => 30, |
||
100 | 'opacity' => 0.7, |
||
101 | 'titleSpeed' => 500, |
||
102 | 'effect' => 'random', |
||
103 | 'navigation' => true, |
||
104 | 'links' => true, |
||
105 | 'hoverPause' => true, |
||
106 | 'theme' => 'default', |
||
107 | 'direction' => 'horizontal', |
||
108 | 'reverse' => false, |
||
109 | 'animationSpeed' => 600, |
||
110 | 'prevText' => '<', |
||
111 | 'nextText' => '>', |
||
112 | 'slices' => 15, |
||
113 | 'center' => false, |
||
114 | 'smartCrop' => true, |
||
115 | 'carouselMode' => false, |
||
116 | 'easing' => 'linear', |
||
117 | 'autoPlay' => true, |
||
118 | 'thumb_width' => 150, |
||
119 | 'thumb_height' => 100, |
||
120 | 'fullWidth' => false, |
||
121 | 'noConflict' => false, |
||
122 | ]; |
||
123 | |||
124 | $params = apply_filters('metaslider_default_parameters', $params); |
||
125 | |||
126 | return $params; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Save the slider details and initiate the update of all slides associated with slider. |
||
131 | */ |
||
132 | private function save() |
||
133 | { |
||
134 | if (!is_admin()) { |
||
135 | return; |
||
136 | } |
||
137 | |||
138 | // make changes to slider |
||
139 | if (\Xmf\Request::hasVar('settings', 'POST')) { |
||
140 | check_admin_referer('metaslider_save_' . $this->id); |
||
141 | $this->update_settings($_POST['settings']); |
||
142 | } |
||
143 | if (\Xmf\Request::hasVar('title', 'POST')) { |
||
144 | check_admin_referer('metaslider_save_' . $this->id); |
||
145 | $this->update_title($_POST['title']); |
||
146 | } |
||
147 | if (\Xmf\Request::hasVar('deleteSlide', 'GET')) { |
||
148 | $this->delete_slide((int)$_GET['deleteSlide']); |
||
149 | } |
||
150 | |||
151 | // make changes to slides |
||
152 | if (\Xmf\Request::hasVar('attachment', 'POST')) { |
||
153 | check_admin_referer('metaslider_save_' . $this->id); |
||
154 | $this->update_slides($_POST['attachment']); |
||
155 | } |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * The main query for extracting the slides for the slideshow |
||
160 | */ |
||
161 | public function get_slides() |
||
162 | { |
||
163 | $args = [ |
||
164 | 'force_no_custom_order' => true, |
||
165 | 'orderby' => 'menu_order', |
||
166 | 'order' => 'ASC', |
||
167 | 'post_type' => 'attachment', |
||
168 | 'post_status' => 'inherit', |
||
169 | 'lang' => '', // polylang, ingore language filter |
||
170 | 'suppress_filters' => 1, // wpml, ignore language filter |
||
171 | 'posts_per_page' => -1, |
||
172 | 'tax_query' => [ |
||
173 | [ |
||
174 | 'taxonomy' => 'ml-slider', |
||
175 | 'field' => 'slug', |
||
176 | 'terms' => $this->id, |
||
177 | ], |
||
178 | ], |
||
179 | ]; |
||
180 | |||
181 | $args = apply_filters('metaslider_populate_slides_args', $args, $this->id, $this->settings); |
||
182 | |||
183 | $query = new WP_Query($args); |
||
184 | |||
185 | return $query; |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Return slides for the current slider |
||
190 | * |
||
191 | * @return array collection of slides belonging to the current slider |
||
192 | */ |
||
193 | private function populate_slides() |
||
194 | { |
||
195 | $slides = []; |
||
196 | |||
197 | $query = $this->get_slides(); |
||
198 | |||
199 | while ($query->have_posts()) { |
||
200 | $query->next_post(); |
||
201 | |||
202 | $type = get_post_meta($query->post->ID, 'ml-slider_type', true); |
||
203 | $type = $type ?: 'image'; // backwards compatibility, fall back to 'image' |
||
204 | |||
205 | if (has_filter("metaslider_get_{$type}_slide")) { |
||
206 | $return = apply_filters("metaslider_get_{$type}_slide", $query->post->ID, $this->id); |
||
207 | |||
208 | if (is_array($return)) { |
||
209 | $slides = array_merge($slides, $return); |
||
210 | } else { |
||
211 | $slides[] = $return; |
||
212 | } |
||
213 | } |
||
214 | } |
||
215 | |||
216 | // apply random setting |
||
217 | if ('true' === $this->get_setting('random') && !is_admin()) { |
||
218 | shuffle($slides); |
||
219 | } |
||
220 | |||
221 | $this->slides = $slides; |
||
222 | |||
223 | return $this->slides; |
||
224 | } |
||
225 | |||
226 | /** |
||
227 | * Render each slide belonging to the slider out to the screen |
||
228 | */ |
||
229 | public function render_admin_slides() |
||
233 | } |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * Output the HTML and Javascript for this slider |
||
238 | * |
||
239 | * @return string HTML & Javascrpt |
||
240 | */ |
||
241 | public function render_public_slides() |
||
242 | { |
||
243 | $html[] = '<!-- meta slider -->'; |
||
244 | $html[] = '<div style="' . $this->get_container_style() . '" class="' . $this->get_container_class() . '">'; |
||
245 | $html[] = ' ' . $this->get_inline_css(); |
||
246 | $html[] = ' <div id="' . $this->get_container_id() . '">'; |
||
247 | $html[] = ' ' . $this->get_html(); |
||
248 | $html[] = ' ' . $this->get_html_after(); |
||
249 | $html[] = ' </div>'; |
||
250 | $html[] = ' <script type="text/javascript">'; |
||
251 | $html[] = ' ' . $this->get_inline_javascript(); |
||
252 | $html[] = ' </script>'; |
||
253 | $html[] = '</div>'; |
||
254 | $html[] = '<!--// meta slider-->'; |
||
255 | |||
256 | $slideshow = implode("\n", $html); |
||
257 | |||
258 | $slideshow = apply_filters('metaslider_slideshow_output', $slideshow, $this->id, $this->settings); |
||
259 | |||
260 | return $slideshow; |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Return the ID to use for the container |
||
265 | */ |
||
266 | private function get_container_id() |
||
267 | { |
||
268 | $container_id = 'metaslider_container_' . $this->id; |
||
269 | |||
270 | $id = apply_filters('metaslider_container_id', $container_id, $this->id, $this->settings); |
||
271 | |||
272 | return $id; |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Return the classes to use for the slidehsow container |
||
277 | */ |
||
278 | private function get_container_class() |
||
279 | { |
||
280 | $class = "metaslider metaslider-{$this->get_setting('type')} metaslider-{$this->id} ml-slider"; |
||
281 | |||
282 | // apply the css class setting |
||
283 | if ('false' !== $this->get_setting('cssClass')) { |
||
284 | $class .= ' ' . $this->get_setting('cssClass'); |
||
285 | } |
||
286 | |||
287 | // handle any custom classes |
||
288 | $class = apply_filters('metaslider_css_classes', $class, $this->id, $this->settings); |
||
289 | |||
290 | return $class; |
||
291 | } |
||
292 | |||
293 | /** |
||
294 | * Return the inline CSS style for the slideshow container. |
||
295 | */ |
||
296 | private function get_container_style() |
||
297 | { |
||
298 | // default |
||
299 | $style = "max-width: {$this->get_setting('width')}px;"; |
||
300 | |||
301 | // carousels are always 100% wide |
||
302 | if (('true' === $this->get_setting('carouselMode') || 'true' === $this->get_setting('fullWidth')) && ('coin' !== $this->get_setting('type'))) { |
||
303 | $style = 'width: 100%;'; |
||
304 | } |
||
305 | |||
306 | // percentWidth showcode parameter takes precedence |
||
307 | if ('false' !== $this->get_setting('percentwidth') && $this->get_setting('percentwidth') > 0) { |
||
308 | $style = "width: {$this->get_setting('percentwidth')}%;"; |
||
309 | } |
||
310 | |||
311 | // center align the slideshow |
||
312 | if ('false' !== $this->get_setting('center')) { |
||
313 | $style .= ' margin: 0 auto;'; |
||
314 | } |
||
315 | |||
316 | // handle any custom container styles |
||
317 | $style = apply_filters('metaslider_container_style', $style, $this->id, $this->settings); |
||
318 | |||
319 | return $style; |
||
320 | } |
||
321 | |||
322 | /** |
||
323 | * Return the Javascript to kick off the slider. Code is wrapped in a timer |
||
324 | * to allow for themes that load jQuery at the bottom of the page. |
||
325 | * |
||
326 | * Delay execution of slider code until jQuery is ready (supports themes where |
||
327 | * jQuery is loaded at the bottom of the page) |
||
328 | * |
||
329 | * @return string javascript |
||
330 | */ |
||
331 | private function get_inline_javascript() |
||
332 | { |
||
333 | $custom_js_before = $this->get_custom_javascript_before(); |
||
334 | $custom_js_after = $this->get_custom_javascript_after(); |
||
335 | |||
336 | $script = 'var ' . $this->identifier . ' = function($) {'; |
||
337 | $script .= $custom_js_before; |
||
338 | $script .= "\n $('#" . $this->identifier . "')." . $this->js_function . '({ '; |
||
339 | $script .= "\n " . $this->get_javascript_parameters(); |
||
340 | $script .= "\n });"; |
||
341 | $script .= $custom_js_after; |
||
342 | $script .= "\n };"; |
||
343 | $script .= "\n var timer_" . $this->identifier . ' = function() {'; |
||
344 | $script .= "\n var slider = !window.jQuery ? window.setTimeout(timer_{$this->identifier}, 100) : !jQuery.isReady ? window.setTimeout(timer_{$this->identifier}, 100) : {$this->identifier}(window.jQuery);"; |
||
345 | $script .= "\n };"; |
||
346 | $script .= "\n timer_" . $this->identifier . '();'; |
||
347 | |||
348 | return $script; |
||
349 | } |
||
350 | |||
351 | /** |
||
352 | * Custom HTML to add immediately below the markup |
||
353 | */ |
||
354 | private function get_html_after() |
||
355 | { |
||
356 | $type = $this->get_setting('type'); |
||
357 | |||
358 | $html = apply_filters("metaslider_{$type}_slider_html_after", '', $this->id, $this->settings); |
||
359 | |||
360 | if (mb_strlen($html)) { |
||
361 | return " {$html}"; |
||
362 | } |
||
363 | |||
364 | return ''; |
||
365 | } |
||
366 | |||
367 | /** |
||
368 | * Custom JavaScript to execute immediately before the slideshow is initialized |
||
369 | */ |
||
370 | private function get_custom_javascript_before() |
||
387 | } |
||
388 | |||
389 | /** |
||
390 | * Custom Javascript to execute immediately after the slideshow is initialized |
||
391 | */ |
||
392 | private function get_custom_javascript_after() |
||
393 | { |
||
394 | $type = $this->get_setting('type'); |
||
395 | |||
403 | } |
||
404 | |||
405 | /** |
||
406 | * Build the javascript parameter arguments for the slider. |
||
407 | * |
||
408 | * @return string parameters |
||
409 | */ |
||
410 | private function get_javascript_parameters() |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * Apply any custom inline styling |
||
447 | * |
||
448 | * @return string |
||
449 | */ |
||
450 | private function get_inline_css() |
||
451 | { |
||
452 | $css = apply_filters('metaslider_css', '', $this->settings, $this->id); |
||
453 | |||
454 | // use this to add the scoped attribute for HTML5 validation (if needed) |
||
455 | $attributes = apply_filters('metaslider_style_attributes', '', $this->settings, $this->id); |
||
456 | |||
457 | if (mb_strlen($css)) { |
||
458 | return "<style type=\"text/css\"{$attributes}>{$css}\n </style>"; |
||
459 | } |
||
460 | |||
461 | return ''; |
||
462 | } |
||
463 | |||
464 | /** |
||
465 | * Include slider assets, JS and CSS paths are specified by child classes. |
||
466 | */ |
||
467 | public function enqueue_scripts() |
||
468 | { |
||
469 | if ('true' === $this->get_setting('printJs')) { |
||
470 | wp_enqueue_script('metaslider-' . $this->get_setting('type') . '-slider', METASLIDER_ASSETS_URL . $this->js_path, ['jquery'], METASLIDER_VERSION); |
||
471 | } |
||
472 | |||
473 | if ('true' === $this->get_setting('printCss')) { |
||
474 | // this will be added to the bottom of the page as <head> has already been processed by WordPress. |
||
475 | // For HTML5 compatibility, use a minification plugin to move the CSS to the <head> |
||
476 | wp_enqueue_style('metaslider-' . $this->get_setting('type') . '-slider', METASLIDER_ASSETS_URL . $this->css_path, false, METASLIDER_VERSION); |
||
477 | wp_enqueue_style('metaslider-public', METASLIDER_ASSETS_URL . 'metaslider/public.css', false, METASLIDER_VERSION); |
||
478 | } |
||
479 | |||
480 | do_action('metaslider_register_public_styles'); |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * Update the slider settings, converting checkbox values (on/off) to true or false. |
||
485 | * @param $new_settings |
||
486 | */ |
||
487 | public function update_settings($new_settings) |
||
488 | { |
||
489 | $old_settings = $this->get_settings(); |
||
490 | |||
491 | // convert submitted checkbox values from 'on' or 'off' to boolean values |
||
492 | $checkboxes = ['noConflict', 'fullWidth', 'hoverPause', 'links', 'reverse', 'random', 'printCss', 'printJs', 'smoothHeight', 'center', 'smartCrop', 'carouselMode', 'autoPlay']; |
||
493 | |||
494 | foreach ($checkboxes as $checkbox) { |
||
495 | if (isset($new_settings[$checkbox]) && 'on' === $new_settings[$checkbox]) { |
||
496 | $new_settings[$checkbox] = 'true'; |
||
497 | } else { |
||
498 | $new_settings[$checkbox] = 'false'; |
||
499 | } |
||
500 | } |
||
501 | |||
502 | // update the slider settings |
||
503 | update_post_meta($this->id, 'ml-slider_settings', array_merge($old_settings, $new_settings)); |
||
504 | |||
505 | $this->settings = $this->get_settings(); |
||
506 | } |
||
507 | |||
508 | /** |
||
509 | * Update the title of the slider |
||
510 | * @param $title |
||
511 | */ |
||
512 | private function update_title($title) |
||
513 | { |
||
514 | $slide = [ |
||
515 | 'ID' => $this->id, |
||
516 | 'post_title' => $title, |
||
517 | ]; |
||
518 | |||
519 | wp_update_post($slide); |
||
520 | } |
||
521 | |||
522 | /** |
||
523 | * Delete a slide. This doesn't actually remove the slide from WordPress, simply untags |
||
524 | * it from the slide taxonomy. |
||
525 | * |
||
526 | * @param int $slide_id |
||
527 | * @return |
||
528 | */ |
||
529 | private function delete_slide($slide_id) |
||
530 | { |
||
531 | // Get the existing terms and only keep the ones we don't want removed |
||
532 | $new_terms = []; |
||
533 | $current_terms = wp_get_object_terms($slide_id, 'ml-slider', ['fields' => 'ids']); |
||
534 | $term = get_term_by('name', $this->id, 'ml-slider'); |
||
535 | |||
536 | foreach ($current_terms as $current_term) { |
||
537 | if ($current_term != $term->term_id) { |
||
538 | $new_terms[] = (int)$current_term; |
||
539 | } |
||
540 | } |
||
541 | |||
542 | return wp_set_object_terms($slide_id, $new_terms, 'ml-slider'); |
||
543 | } |
||
544 | |||
545 | /** |
||
546 | * Loop over each slide and call the save action on each |
||
547 | * |
||
548 | * @param array $data - posted form data. |
||
549 | */ |
||
550 | private function update_slides($data) |
||
554 | } |
||
555 | } |
||
556 | } |
||
557 |