Completed
Push — add/e2e-mailchimp-block-test ( e217db...6066d0 )
by Yaroslav
98:30 queued 85:55
created

Jetpack_Gallery_Widget   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 430
Duplicated Lines 7.67 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 44
lcom 1
cbo 8
dl 33
loc 430
rs 8.8798
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 34 5
B widget() 0 85 7
A get_attachments() 0 24 3
A rectangular_widget() 11 11 3
A square_widget() 11 11 3
A circle_widget() 11 11 3
B slideshow_widget() 0 58 6
A tiled_gallery_content_width() 0 3 1
A form() 0 8 1
A update() 0 5 1
A sanitize() 0 16 5
A allowed_values() 0 22 1
A defaults() 0 9 1
A enqueue_frontend_scripts() 0 11 1
A enqueue_admin_scripts() 0 28 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Gallery_Widget 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Jetpack_Gallery_Widget, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
Plugin Name: Gallery
5
Description: Gallery widget
6
Author: Automattic Inc.
7
Version: 1.0
8
Author URI: http://automattic.com
9
*/
10
11
use Automattic\Jetpack\Assets;
12
13
class Jetpack_Gallery_Widget extends WP_Widget {
14
	const THUMB_SIZE    = 45;
15
	const DEFAULT_WIDTH = 265;
16
17
	protected $_instance_width;
18
19
	public function __construct() {
20
		$widget_ops = array(
21
			'classname'                   => 'widget-gallery',
22
			'description'                 => __( 'Display a photo gallery or slideshow', 'jetpack' ),
23
			'customize_selective_refresh' => true,
24
		);
25
26
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
27
28
		parent::__construct(
29
			'gallery',
30
			/** This filter is documented in modules/widgets/facebook-likebox.php */
31
			apply_filters( 'jetpack_widget_name', __( 'Gallery', 'jetpack' ) ),
32
			$widget_ops
33
		);
34
35
		if ( is_customize_preview() ) {
36
			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) );
37
38
			if ( class_exists( 'Jetpack_Tiled_Gallery' ) ) {
39
				add_action( 'wp_enqueue_scripts', array( 'Jetpack_Tiled_Gallery', 'default_scripts_and_styles' ) );
40
			}
41
42
			if ( class_exists( 'Jetpack_Slideshow_Shortcode' ) ) {
43
				$slideshow = new Jetpack_Slideshow_Shortcode();
44
				add_action( 'wp_enqueue_scripts', array( $slideshow, 'enqueue_scripts' ) );
45
			}
46
47
			if ( class_exists( 'Jetpack_Carousel' ) ) {
48
				$carousel = new Jetpack_Carousel();
49
				add_action( 'wp_enqueue_scripts', array( $carousel, 'enqueue_assets' ) );
50
			}
51
		}
52
	}
53
54
	/**
55
	 * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
56
	 * @param array $instance The settings for the particular instance of the widget.
57
	 */
58
	public function widget( $args, $instance ) {
59
		$instance = wp_parse_args( (array) $instance, $this->defaults() );
60
61
		$this->enqueue_frontend_scripts();
62
63
		extract( $args );
64
65
		$instance['attachments'] = $this->get_attachments( $instance );
66
67
		$classes = array();
68
69
		$classes[] = 'widget-gallery-' . $instance['type'];
70
71
		// Due to a bug in the carousel plugin, carousels will be triggered for all tiled galleries that exist on a page
72
		// with other tiled galleries, regardless of whether or not the widget was set to Carousel mode. The onClick selector
73
		// is simply too broad, since it was not written with widgets in mind. This special class prevents that behavior, via
74
		// an override handler in gallery.js
75
		if ( 'carousel' != $instance['link'] && 'slideshow' != $instance['type'] ) {
76
			$classes[] = 'no-carousel';
77
		} else {
78
			$classes[] = 'carousel';
79
		}
80
81
		$classes = implode( ' ', $classes );
82
83
		if ( 'carousel' == $instance['link'] ) {
84
			require_once plugin_dir_path( realpath( dirname( __FILE__ ) . '/../carousel/jetpack-carousel.php' ) ) . 'jetpack-carousel.php';
85
86
			if ( class_exists( 'Jetpack_Carousel' ) ) {
87
				// Create new carousel so we can use the enqueue_assets() method. Not ideal, but there is a decent amount
88
				// of logic in that method that shouldn't be duplicated.
89
				$carousel = new Jetpack_Carousel();
90
91
				// First parameter is $output, which comes from filters, and causes bypass of the asset enqueuing. Passing null is correct.
92
				$carousel->enqueue_assets( null );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Carousel::enqueue_assets() has too many arguments starting with null.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
93
			}
94
		}
95
96
		echo $before_widget . "\n";
97
98
		/** This filter is documented in core/src/wp-includes/default-widgets.php */
99
		$title = apply_filters( 'widget_title', $instance['title'] );
100
101
		if ( $title ) {
102
			echo $before_title . esc_html( $title ) . $after_title . "\n";
103
		}
104
105
		echo '<div class="' . esc_attr( $classes ) . '">' . "\n";
106
107
		$method = $instance['type'] . '_widget';
108
109
		/**
110
		 * Allow the width of a gallery to be altered by themes or other code.
111
		 *
112
		 * @module widgets
113
		 *
114
		 * @since 2.5.0
115
		 *
116
		 * @param int self::DEFAULT_WIDTH Default gallery width. Default is 265.
117
		 * @param string $args Display arguments including before_title, after_title, before_widget, and after_widget.
118
		 * @param array $instance The settings for the particular instance of the widget.
119
		 */
120
		$this->_instance_width = apply_filters( 'gallery_widget_content_width', self::DEFAULT_WIDTH, $args, $instance );
121
122
		// Register a filter to modify the tiled_gallery_content_width, so Jetpack_Tiled_Gallery
123
		// can appropriately size the tiles.
124
		add_filter( 'tiled_gallery_content_width', array( $this, 'tiled_gallery_content_width' ) );
125
126
		if ( method_exists( $this, $method ) ) {
127
			echo $this->$method( $args, $instance );
128
		}
129
130
		// Remove the stored $_instance_width, as it is no longer needed
131
		$this->_instance_width = null;
132
133
		// Remove the filter, so any Jetpack_Tiled_Gallery in a post is not affected
134
		remove_filter( 'tiled_gallery_content_width', array( $this, 'tiled_gallery_content_width' ) );
135
136
		echo "\n" . '</div>'; // .widget-gallery-$type
137
138
		echo "\n" . $after_widget;
139
140
		/** This action is documented in modules/widgets/gravatar-profile.php */
141
		do_action( 'jetpack_stats_extra', 'widget_view', 'gallery' );
142
	}
143
144
	/**
145
	 * Fetch the images attached to the gallery Widget
146
	 *
147
	 * @param array $instance The Widget instance for which you'd like attachments
148
	 * @return array Array of attachment objects for the Widget in $instance
149
	 */
150
	public function get_attachments( $instance ) {
151
		$ids = explode( ',', $instance['ids'] );
152
153
		if ( isset( $instance['random'] ) && 'on' == $instance['random'] ) {
154
			shuffle( $ids );
155
		}
156
157
		$attachments_query = new WP_Query(
158
			array(
159
				'post__in'       => $ids,
160
				'post_status'    => 'inherit',
161
				'post_type'      => 'attachment',
162
				'post_mime_type' => 'image',
163
				'posts_per_page' => -1,
164
				'orderby'        => 'post__in',
165
			)
166
		);
167
168
		$attachments = $attachments_query->get_posts();
169
170
		wp_reset_postdata();
171
172
		return $attachments;
173
	}
174
175
	/**
176
	 * Generate HTML for a rectangular, tiled Widget
177
	 *
178
	 * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
179
	 * @param array $instance The Widget instance to generate HTML for
180
	 * @return string String of HTML representing a rectangular gallery
181
	 */
182 View Code Duplication
	public function rectangular_widget( $args, $instance ) {
183
		if ( ! class_exists( 'Jetpack_Tiled_Gallery' )
184
			&& ! class_exists( 'Jetpack_Tiled_Gallery_Layout_Rectangular' ) ) {
185
			return;
186
		}
187
188
		Jetpack_Tiled_Gallery::default_scripts_and_styles();
189
190
		$layout = new Jetpack_Tiled_Gallery_Layout_Rectangular( $instance['attachments'], $instance['link'], false, 3 );
191
		return $layout->HTML();
192
	}
193
194
	/**
195
	 * Generate HTML for a square (grid style) Widget
196
	 *
197
	 * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
198
	 * @param array $instance The Widget instance to generate HTML for
199
	 * @return string String of HTML representing a square gallery
200
	 */
201 View Code Duplication
	public function square_widget( $args, $instance ) {
202
		if ( ! class_exists( 'Jetpack_Tiled_Gallery' )
203
			&& ! class_exists( 'Jetpack_Tiled_Gallery_Layout_Square' ) ) {
204
			return;
205
		}
206
207
		Jetpack_Tiled_Gallery::default_scripts_and_styles();
208
209
		$layout = new Jetpack_Tiled_Gallery_Layout_Square( $instance['attachments'], $instance['link'], false, 3 );
210
		return $layout->HTML();
211
	}
212
213
	/**
214
	 * Generate HTML for a circular (grid style) Widget
215
	 *
216
	 * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
217
	 * @param array $instance The Widget instance to generate HTML for
218
	 * @return string String of HTML representing a circular gallery
219
	 */
220 View Code Duplication
	public function circle_widget( $args, $instance ) {
221
		if ( ! class_exists( 'Jetpack_Tiled_Gallery' )
222
			&& ! class_exists( 'Jetpack_Tiled_Gallery_Layout_Circle' ) ) {
223
			return;
224
		}
225
226
		Jetpack_Tiled_Gallery::default_scripts_and_styles();
227
228
		$layout = new Jetpack_Tiled_Gallery_Layout_Circle( $instance['attachments'], $instance['link'], false, 3 );
229
		return $layout->HTML();
230
	}
231
232
	/**
233
	 * Generate HTML for a slideshow Widget
234
	 *
235
	 * @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
236
	 * @param array $instance The Widget instance to generate HTML for
237
	 * @return string String of HTML representing a slideshow gallery
238
	 */
239
	public function slideshow_widget( $args, $instance ) {
240
		global $content_width;
241
242
		require_once plugin_dir_path( realpath( dirname( __FILE__ ) . '/../shortcodes/slideshow.php' ) ) . 'slideshow.php';
243
244
		if ( ! class_exists( 'Jetpack_Slideshow_Shortcode' ) ) {
245
			return;
246
		}
247
248
		if ( count( $instance['attachments'] ) < 1 ) {
249
			return;
250
		}
251
252
		$slideshow = new Jetpack_Slideshow_Shortcode();
253
254
		$slideshow->enqueue_scripts();
255
256
		$gallery_instance = 'widget-' . $args['widget_id'];
257
258
		$gallery = array();
259
260
		foreach ( $instance['attachments'] as $attachment ) {
261
			$attachment_image_src = wp_get_attachment_image_src( $attachment->ID, 'full' );
262
			$attachment_image_src = jetpack_photon_url( $attachment_image_src[0], array( 'w' => $this->_instance_width ) ); // [url, width, height]
263
264
			$caption = wptexturize( strip_tags( $attachment->post_excerpt ) );
265
266
			$gallery[] = (object) array(
267
				'src'     => (string) esc_url_raw( $attachment_image_src ),
268
				'id'      => (string) $attachment->ID,
269
				'caption' => (string) $caption,
270
			);
271
		}
272
273
		$max_width  = intval( get_option( 'large_size_w' ) );
274
		$max_height = 175;
275
276
		if ( intval( $content_width ) > 0 ) {
277
			$max_width = min( intval( $content_width ), $max_width );
278
		}
279
280
		$color     = Jetpack_Options::get_option( 'slideshow_background_color', 'black' );
281
		$autostart = isset( $attr['autostart'] ) ? $attr['autostart'] : true;
0 ignored issues
show
Bug introduced by
The variable $attr seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
282
283
		$js_attr = array(
284
			'gallery'   => $gallery,
285
			'selector'  => $gallery_instance,
286
			'width'     => $max_width,
287
			'height'    => $max_height,
288
			'trans'     => 'fade',
289
			'color'     => $color,
290
			'autostart' => $autostart,
291
		);
292
293
		$html = $slideshow->slideshow_js( $js_attr );
294
295
		return $html;
296
	}
297
298
	/**
299
	 * tiled_gallery_content_width filter
300
	 *
301
	 * Used to adjust the content width of Jetpack_Tiled_Gallery's in sidebars
302
	 *
303
	 * $this->_instance_width is filtered in widget() and this filter is added then removed in widget()
304
	 *
305
	 * @param int $width int The original width value
306
	 * @return int The filtered width
307
	 */
308
	public function tiled_gallery_content_width( $width ) {
309
		return $this->_instance_width;
310
	}
311
312
	public function form( $instance ) {
313
		$defaults       = $this->defaults();
314
		$allowed_values = $this->allowed_values();
315
316
		$instance = wp_parse_args( (array) $instance, $defaults );
317
318
		include dirname( __FILE__ ) . '/gallery/templates/form.php';
319
	}
320
321
	public function update( $new_instance, $old_instance ) {
322
		$instance = $this->sanitize( $new_instance );
323
324
		return $instance;
325
	}
326
327
	/**
328
	 * Sanitize the $instance's values to the set of allowed values. If a value is not acceptable,
329
	 * it is set to its default.
330
	 *
331
	 * Helps keep things nice and secure by whitelisting only allowed values
332
	 *
333
	 * @param array $instance The Widget instance to sanitize values for
334
	 * @return array $instance The Widget instance with values sanitized
335
	 */
336
	public function sanitize( $instance ) {
337
		$allowed_values = $this->allowed_values();
338
		$defaults       = $this->defaults();
339
340
		foreach ( $instance as $key => $value ) {
341
			$value = trim( $value );
342
343
			if ( isset( $allowed_values[ $key ] ) && $allowed_values[ $key ] && ! array_key_exists( $value, $allowed_values[ $key ] ) ) {
344
				$instance[ $key ] = $defaults[ $key ];
345
			} else {
346
				$instance[ $key ] = sanitize_text_field( $value );
347
			}
348
		}
349
350
		return $instance;
351
	}
352
353
	/**
354
	 * Return a multi-dimensional array of allowed values (and their labels) for all widget form
355
	 * elements
356
	 *
357
	 * To allow all values on an input, omit it from the returned array
358
	 *
359
	 * @return array Array of allowed values for each option
360
	 */
361
	public function allowed_values() {
362
		$max_columns = 5;
363
364
		// Create an associative array of allowed column values. This just automates the generation of
365
		// column <option>s, from 1 to $max_columns
366
		$allowed_columns = array_combine( range( 1, $max_columns ), range( 1, $max_columns ) );
367
368
		return array(
369
			'type'    => array(
370
				'rectangular' => __( 'Tiles', 'jetpack' ),
371
				'square'      => __( 'Square Tiles', 'jetpack' ),
372
				'circle'      => __( 'Circles', 'jetpack' ),
373
				'slideshow'   => __( 'Slideshow', 'jetpack' ),
374
			),
375
			'columns' => $allowed_columns,
376
			'link'    => array(
377
				'carousel' => __( 'Carousel', 'jetpack' ),
378
				'post'     => __( 'Attachment Page', 'jetpack' ),
379
				'file'     => __( 'Media File', 'jetpack' ),
380
			),
381
		);
382
	}
383
384
	/**
385
	 * Return an associative array of default values
386
	 *
387
	 * These values are used in new widgets as well as when sanitizing input. If a given value is not allowed,
388
	 * as defined in allowed_values(), that input is set to the default value defined here.
389
	 *
390
	 * @return array Array of default values for the Widget's options
391
	 */
392
	public function defaults() {
393
		return array(
394
			'title'   => '',
395
			'type'    => 'rectangular',
396
			'ids'     => '',
397
			'columns' => 3,
398
			'link'    => 'carousel',
399
		);
400
	}
401
402
	public function enqueue_frontend_scripts() {
403
		wp_register_script(
404
			'gallery-widget',
405
			Assets::get_file_url_for_environment(
406
				'_inc/build/widgets/gallery/js/gallery.min.js',
407
				'modules/widgets/gallery/js/gallery.js'
408
			)
409
		);
410
411
		wp_enqueue_script( 'gallery-widget' );
412
	}
413
414
	public function enqueue_admin_scripts() {
415
		global $pagenow;
416
417
		if ( 'widgets.php' == $pagenow || 'customize.php' == $pagenow ) {
418
			wp_enqueue_media();
419
420
			wp_enqueue_script(
421
				'gallery-widget-admin',
422
				Assets::get_file_url_for_environment(
423
					'_inc/build/widgets/gallery/js/admin.min.js',
424
					'modules/widgets/gallery/js/admin.js'
425
				),
426
				array(
427
					'media-models',
428
					'media-views',
429
				),
430
				'20150501'
431
			);
432
433
			$js_settings = array(
434
				'thumbSize' => self::THUMB_SIZE,
435
			);
436
437
			wp_localize_script( 'gallery-widget-admin', '_wpGalleryWidgetAdminSettings', $js_settings );
438
			wp_enqueue_style( 'gallery-widget-admin', plugins_url( '/gallery/css/admin.css', __FILE__ ) );
439
			wp_style_add_data( 'gallery-widget-admin', 'rtl', 'replace' );
440
		}
441
	}
442
}
443
444
add_action( 'widgets_init', 'jetpack_gallery_widget_init' );
445
446
function jetpack_gallery_widget_init() {
447
	/**
448
	 * Allow the Gallery Widget to be enabled even when Core supports the Media Gallery Widget
449
	 *
450
	 * @module widgets
451
	 *
452
	 * @since 5.5.0
453
	 *
454
	 * @param bool false Whether to force-enable the gallery widget
455
	 */
456
	if (
457
		! apply_filters( 'jetpack_force_enable_gallery_widget', false )
458
		&& class_exists( 'WP_Widget_Media_Gallery' )
459
		&& Jetpack_Options::get_option( 'gallery_widget_migration' )
460
	) {
461
		return;
462
	}
463
	if ( ! method_exists( 'Jetpack', 'is_module_active' ) || Jetpack::is_module_active( 'tiled-gallery' ) ) {
464
		register_widget( 'Jetpack_Gallery_Widget' );
465
	}
466
}
467