Completed
Push — fix/e2e-tests ( c7de6e...e5f34f )
by Yaroslav
13:38 queued 06:23
created

Jetpack_Carousel::add_data_to_container()   C

Complexity

Conditions 11
Paths 4

Size

Total Lines 87

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
nc 4
nop 1
dl 0
loc 87
rs 6.1369
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
use Automattic\Jetpack\Assets;
3
/*
4
Plugin Name: Jetpack Carousel
5
Plugin URL: https://wordpress.com/
6
Description: Transform your standard image galleries into an immersive full-screen experience.
7
Version: 0.1
8
Author: Automattic
9
10
Released under the GPL v.2 license.
11
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
GNU General Public License for more details.
16
*/
17
class Jetpack_Carousel {
18
19
	public $prebuilt_widths = array( 370, 700, 1000, 1200, 1400, 2000 );
20
21
	public $first_run = true;
22
23
	public $in_gallery = false;
24
25
	public $in_jetpack = true;
26
27
	public $single_image_gallery_enabled = false;
28
29
	public $single_image_gallery_enabled_media_file = false;
30
31
	function __construct() {
32
		add_action( 'init', array( $this, 'init' ) );
33
	}
34
35
	function init() {
36
		if ( $this->maybe_disable_jp_carousel() ) {
37
			return;
38
		}
39
40
		$this->in_jetpack = ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'enable_module_configurable' ) ) ? true : false;
41
42
		$this->single_image_gallery_enabled            = ! $this->maybe_disable_jp_carousel_single_images();
43
		$this->single_image_gallery_enabled_media_file = $this->maybe_enable_jp_carousel_single_images_media_file();
44
45
		if ( is_admin() ) {
46
			// Register the Carousel-related related settings
47
			add_action( 'admin_init', array( $this, 'register_settings' ), 5 );
48
			if ( ! $this->in_jetpack ) {
49
				if ( 0 == $this->test_1or0_option( get_option( 'carousel_enable_it' ), true ) ) {
50
					return; // Carousel disabled, abort early, but still register setting so user can switch it back on
51
				}
52
			}
53
			// If in admin, register the ajax endpoints.
54
			add_action( 'wp_ajax_get_attachment_comments', array( $this, 'get_attachment_comments' ) );
55
			add_action( 'wp_ajax_nopriv_get_attachment_comments', array( $this, 'get_attachment_comments' ) );
56
			add_action( 'wp_ajax_post_attachment_comment', array( $this, 'post_attachment_comment' ) );
57
			add_action( 'wp_ajax_nopriv_post_attachment_comment', array( $this, 'post_attachment_comment' ) );
58
		} else {
59
			if ( ! $this->in_jetpack ) {
60
				if ( 0 == $this->test_1or0_option( get_option( 'carousel_enable_it' ), true ) ) {
61
					return; // Carousel disabled, abort early
62
				}
63
			}
64
			// If on front-end, do the Carousel thang.
65
			/**
66
			 * Filter the array of default prebuilt widths used in Carousel.
67
			 *
68
			 * @module carousel
69
			 *
70
			 * @since 1.6.0
71
			 *
72
			 * @param array $this->prebuilt_widths Array of default widths.
73
			 */
74
			$this->prebuilt_widths = apply_filters( 'jp_carousel_widths', $this->prebuilt_widths );
75
			// below: load later than other callbacks hooked it (e.g. 3rd party plugins handling gallery shortcode)
76
			add_filter( 'post_gallery', array( $this, 'check_if_shortcode_processed_and_enqueue_assets' ), 1000, 2 );
77
			add_filter( 'post_gallery', array( $this, 'set_in_gallery' ), -1000 );
78
			add_filter( 'gallery_style', array( $this, 'add_data_to_container' ) );
79
			add_filter( 'wp_get_attachment_image_attributes', array( $this, 'add_data_to_images' ), 10, 2 );
80
			add_filter( 'the_content', array( $this, 'check_content_for_blocks' ), 1 );
81
			add_filter( 'jetpack_tiled_galleries_block_content', array( $this, 'add_data_img_tags_and_enqueue_assets' ) );
82
			if ( $this->single_image_gallery_enabled ) {
83
				add_filter( 'the_content', array( $this, 'add_data_img_tags_and_enqueue_assets' ) );
84
			}
85
		}
86
87
		if ( $this->in_jetpack ) {
88
			Jetpack::enable_module_configurable( dirname( dirname( __FILE__ ) ) . '/carousel.php' );
89
		}
90
	}
91
92
	function maybe_disable_jp_carousel() {
93
		/**
94
		 * Allow third-party plugins or themes to disable Carousel.
95
		 *
96
		 * @module carousel
97
		 *
98
		 * @since 1.6.0
99
		 *
100
		 * @param bool false Should Carousel be disabled? Default to false.
101
		 */
102
		return apply_filters( 'jp_carousel_maybe_disable', false );
103
	}
104
105
	function maybe_disable_jp_carousel_single_images() {
106
		/**
107
		 * Allow third-party plugins or themes to disable Carousel for single images.
108
		 *
109
		 * @module carousel
110
		 *
111
		 * @since 4.5.0
112
		 *
113
		 * @param bool false Should Carousel be disabled for single images? Default to false.
114
		 */
115
		return apply_filters( 'jp_carousel_maybe_disable_single_images', false );
116
	}
117
118
	function maybe_enable_jp_carousel_single_images_media_file() {
119
		/**
120
		 * Allow third-party plugins or themes to enable Carousel
121
		 * for single images linking to 'Media File' (full size image).
122
		 *
123
		 * @module carousel
124
		 *
125
		 * @since 4.5.0
126
		 *
127
		 * @param bool false Should Carousel be enabled for single images linking to 'Media File'? Default to false.
128
		 */
129
		return apply_filters( 'jp_carousel_load_for_images_linked_to_file', false );
130
	}
131
132
	function asset_version( $version ) {
133
		/**
134
		 * Filter the version string used when enqueuing Carousel assets.
135
		 *
136
		 * @module carousel
137
		 *
138
		 * @since 1.6.0
139
		 *
140
		 * @param string $version Asset version.
141
		 */
142
		return apply_filters( 'jp_carousel_asset_version', $version );
143
	}
144
145
	function display_bail_message( $output = '' ) {
146
		// Displays a message on top of gallery if carousel has bailed
147
		$message  = '<div class="jp-carousel-msg"><p>';
148
		$message .= __( 'Jetpack\'s Carousel has been disabled, because another plugin or your theme is overriding the [gallery] shortcode.', 'jetpack' );
149
		$message .= '</p></div>';
150
		// put before gallery output
151
		$output = $message . $output;
152
		return $output;
153
	}
154
155
	function check_if_shortcode_processed_and_enqueue_assets( $output ) {
156
		if (
157
			class_exists( 'Jetpack_AMP_Support' )
158
			&& Jetpack_AMP_Support::is_amp_request()
159
		) {
160
			return $output;
161
		}
162
163
		if (
164
			! empty( $output ) &&
165
			/**
166
			 * Allow third-party plugins or themes to force-enable Carousel.
167
			 *
168
			 * @module carousel
169
			 *
170
			 * @since 1.9.0
171
			 *
172
			 * @param bool false Should we force enable Carousel? Default to false.
173
			 */
174
			! apply_filters( 'jp_carousel_force_enable', false )
175
		) {
176
			// Bail because someone is overriding the [gallery] shortcode.
177
			remove_filter( 'gallery_style', array( $this, 'add_data_to_container' ) );
178
			remove_filter( 'wp_get_attachment_image_attributes', array( $this, 'add_data_to_images' ) );
179
			remove_filter( 'the_content', array( $this, 'add_data_img_tags_and_enqueue_assets' ) );
180
			// Display message that carousel has bailed, if user is super_admin, and if we're not on WordPress.com.
181
			if (
182
				is_super_admin() &&
183
				! ( defined( 'IS_WPCOM' ) && IS_WPCOM )
184
			) {
185
				add_filter( 'post_gallery', array( $this, 'display_bail_message' ) );
186
			}
187
			return $output;
188
		}
189
190
		/**
191
		 * Fires when thumbnails are shown in Carousel.
192
		 *
193
		 * @module carousel
194
		 *
195
		 * @since 1.6.0
196
		 **/
197
		do_action( 'jp_carousel_thumbnails_shown' );
198
199
		$this->enqueue_assets();
200
201
		return $output;
202
	}
203
204
	/**
205
	 * Check if the content of a post uses gallery blocks. To be used by 'the_content' filter.
206
	 *
207
	 * @since 6.8.0
208
	 *
209
	 * @param string $content Post content.
210
	 *
211
	 * @return string $content Post content.
212
	 */
213
	function check_content_for_blocks( $content ) {
214
		if (
215
			class_exists( 'Jetpack_AMP_Support' )
216
			&& Jetpack_AMP_Support::is_amp_request()
217
		) {
218
			return $content;
219
		}
220
221
		if ( has_block( 'gallery', $content ) || has_block( 'jetpack/tiled-gallery', $content ) ) {
222
			$this->enqueue_assets();
223
			$content = $this->add_data_to_container( $content );
224
		}
225
		return $content;
226
	}
227
228
	function enqueue_assets() {
229
		if ( $this->first_run ) {
230
			wp_enqueue_script(
231
				'jetpack-carousel',
232
				Assets::get_file_url_for_environment(
233
					'_inc/build/carousel/jetpack-carousel.min.js',
234
					'modules/carousel/jetpack-carousel.js'
235
				),
236
				array( 'jquery.spin' ),
237
				$this->asset_version( '20190102' ),
238
				true
239
			);
240
241
			// Note: using  home_url() instead of admin_url() for ajaxurl to be sure  to get same domain on wpcom when using mapped domains (also works on self-hosted)
242
			// Also: not hardcoding path since there is no guarantee site is running on site root in self-hosted context.
243
			$is_logged_in         = is_user_logged_in();
244
			$current_user         = wp_get_current_user();
245
			$comment_registration = intval( get_option( 'comment_registration' ) );
246
			$require_name_email   = intval( get_option( 'require_name_email' ) );
247
			$localize_strings     = array(
248
				'widths'                          => $this->prebuilt_widths,
249
				'is_logged_in'                    => $is_logged_in,
250
				'lang'                            => strtolower( substr( get_locale(), 0, 2 ) ),
251
				'ajaxurl'                         => set_url_scheme( admin_url( 'admin-ajax.php' ) ),
252
				'nonce'                           => wp_create_nonce( 'carousel_nonce' ),
253
				'display_exif'                    => $this->test_1or0_option( Jetpack_Options::get_option_and_ensure_autoload( 'carousel_display_exif', true ) ),
254
				'display_geo'                     => $this->test_1or0_option( Jetpack_Options::get_option_and_ensure_autoload( 'carousel_display_geo', true ) ),
255
				'single_image_gallery'            => $this->single_image_gallery_enabled,
256
				'single_image_gallery_media_file' => $this->single_image_gallery_enabled_media_file,
257
				'background_color'                => $this->carousel_background_color_sanitize( Jetpack_Options::get_option_and_ensure_autoload( 'carousel_background_color', '' ) ),
258
				'comment'                         => __( 'Comment', 'jetpack' ),
259
				'post_comment'                    => __( 'Post Comment', 'jetpack' ),
260
				'write_comment'                   => __( 'Write a Comment...', 'jetpack' ),
261
				'loading_comments'                => __( 'Loading Comments...', 'jetpack' ),
262
				'download_original'               => sprintf( __( 'View full size <span class="photo-size">%1$s<span class="photo-size-times">&times;</span>%2$s</span>', 'jetpack' ), '{0}', '{1}' ),
263
				'no_comment_text'                 => __( 'Please be sure to submit some text with your comment.', 'jetpack' ),
264
				'no_comment_email'                => __( 'Please provide an email address to comment.', 'jetpack' ),
265
				'no_comment_author'               => __( 'Please provide your name to comment.', 'jetpack' ),
266
				'comment_post_error'              => __( 'Sorry, but there was an error posting your comment. Please try again later.', 'jetpack' ),
267
				'comment_approved'                => __( 'Your comment was approved.', 'jetpack' ),
268
				'comment_unapproved'              => __( 'Your comment is in moderation.', 'jetpack' ),
269
				'camera'                          => __( 'Camera', 'jetpack' ),
270
				'aperture'                        => __( 'Aperture', 'jetpack' ),
271
				'shutter_speed'                   => __( 'Shutter Speed', 'jetpack' ),
272
				'focal_length'                    => __( 'Focal Length', 'jetpack' ),
273
				'copyright'                       => __( 'Copyright', 'jetpack' ),
274
				'comment_registration'            => $comment_registration,
275
				'require_name_email'              => $require_name_email,
276
				/** This action is documented in core/src/wp-includes/link-template.php */
277
				'login_url'                       => wp_login_url( apply_filters( 'the_permalink', get_permalink() ) ),
278
				'blog_id'                         => (int) get_current_blog_id(),
279
				'meta_data'                       => array( 'camera', 'aperture', 'shutter_speed', 'focal_length', 'copyright' ),
280
			);
281
282
			if ( ! isset( $localize_strings['jetpack_comments_iframe_src'] ) || empty( $localize_strings['jetpack_comments_iframe_src'] ) ) {
283
				// We're not using Comments after all, so fallback to standard local comments.
284
285
				if ( $is_logged_in ) {
286
					$localize_strings['local_comments_commenting_as'] = '<p id="jp-carousel-commenting-as">' . sprintf( __( 'Commenting as %s', 'jetpack' ), $current_user->data->display_name ) . '</p>';
287
				} else {
288
					if ( $comment_registration ) {
289
						$localize_strings['local_comments_commenting_as'] = '<p id="jp-carousel-commenting-as">' . __( 'You must be <a href="#" class="jp-carousel-comment-login">logged in</a> to post a comment.', 'jetpack' ) . '</p>';
290
					} else {
291
						$required = ( $require_name_email ) ? __( '%s (Required)', 'jetpack' ) : '%s';
292
						$localize_strings['local_comments_commenting_as'] = ''
293
							. '<fieldset><label for="email">' . sprintf( $required, __( 'Email', 'jetpack' ) ) . '</label> '
294
							. '<input type="text" name="email" class="jp-carousel-comment-form-field jp-carousel-comment-form-text-field" id="jp-carousel-comment-form-email-field" /></fieldset>'
295
							. '<fieldset><label for="author">' . sprintf( $required, __( 'Name', 'jetpack' ) ) . '</label> '
296
							. '<input type="text" name="author" class="jp-carousel-comment-form-field jp-carousel-comment-form-text-field" id="jp-carousel-comment-form-author-field" /></fieldset>'
297
							. '<fieldset><label for="url">' . __( 'Website', 'jetpack' ) . '</label> '
298
							. '<input type="text" name="url" class="jp-carousel-comment-form-field jp-carousel-comment-form-text-field" id="jp-carousel-comment-form-url-field" /></fieldset>';
299
					}
300
				}
301
			}
302
303
			/**
304
			 * Handle WP stats for images in full-screen.
305
			 * Build string with tracking info.
306
			 */
307
308
			/**
309
			 * Filter if Jetpack should enable stats collection on carousel views
310
			 *
311
			 * @module carousel
312
			 *
313
			 * @since 4.3.2
314
			 *
315
			 * @param bool Enable Jetpack Carousel stat collection. Default false.
316
			 */
317
			if ( apply_filters( 'jetpack_enable_carousel_stats', false ) && in_array( 'stats', Jetpack::get_active_modules() ) && ! Jetpack::is_development_mode() ) {
318
				$localize_strings['stats'] = 'blog=' . Jetpack_Options::get_option( 'id' ) . '&host=' . parse_url( get_option( 'home' ), PHP_URL_HOST ) . '&v=ext&j=' . JETPACK__API_VERSION . ':' . JETPACK__VERSION;
319
320
				// Set the stats as empty if user is logged in but logged-in users shouldn't be tracked.
321 View Code Duplication
				if ( is_user_logged_in() && function_exists( 'stats_get_options' ) ) {
322
					$stats_options        = stats_get_options();
323
					$track_loggedin_users = isset( $stats_options['reg_users'] ) ? (bool) $stats_options['reg_users'] : false;
324
325
					if ( ! $track_loggedin_users ) {
326
						$localize_strings['stats'] = '';
327
					}
328
				}
329
			}
330
331
			/**
332
			 * Filter the strings passed to the Carousel's js file.
333
			 *
334
			 * @module carousel
335
			 *
336
			 * @since 1.6.0
337
			 *
338
			 * @param array $localize_strings Array of strings passed to the Jetpack js file.
339
			 */
340
			$localize_strings = apply_filters( 'jp_carousel_localize_strings', $localize_strings );
341
			wp_localize_script( 'jetpack-carousel', 'jetpackCarouselStrings', $localize_strings );
342
			wp_enqueue_style( 'jetpack-carousel', plugins_url( 'jetpack-carousel.css', __FILE__ ), array(), $this->asset_version( '20120629' ) );
343
			wp_style_add_data( 'jetpack-carousel', 'rtl', 'replace' );
344
345
			/**
346
			 * Fires after carousel assets are enqueued for the first time.
347
			 * Allows for adding additional assets to the carousel page.
348
			 *
349
			 * @module carousel
350
			 *
351
			 * @since 1.6.0
352
			 *
353
			 * @param bool $first_run First load if Carousel on the page.
354
			 * @param array $localized_strings Array of strings passed to the Jetpack js file.
355
			 */
356
			do_action( 'jp_carousel_enqueue_assets', $this->first_run, $localize_strings );
357
358
			$this->first_run = false;
359
		}
360
	}
361
362
	function set_in_gallery( $output ) {
363
		if (
364
			class_exists( 'Jetpack_AMP_Support' )
365
			&& Jetpack_AMP_Support::is_amp_request()
366
		) {
367
			return $output;
368
		}
369
		$this->in_gallery = true;
370
		return $output;
371
	}
372
373
	/**
374
	 * Adds data-* attributes required by carousel to img tags in post HTML
375
	 * content. To be used by 'the_content' filter.
376
	 *
377
	 * @see add_data_to_images()
378
	 * @see wp_make_content_images_responsive() in wp-includes/media.php
379
	 *
380
	 * @param string $content HTML content of the post
381
	 * @return string Modified HTML content of the post
382
	 */
383
	function add_data_img_tags_and_enqueue_assets( $content ) {
384
		if (
385
			class_exists( 'Jetpack_AMP_Support' )
386
			&& Jetpack_AMP_Support::is_amp_request()
387
		) {
388
			return $content;
389
		}
390
391
		if ( ! preg_match_all( '/<img [^>]+>/', $content, $matches ) ) {
392
			return $content;
393
		}
394
		$selected_images = array();
395
		foreach ( $matches[0] as $image_html ) {
396
			if ( preg_match( '/(wp-image-|data-id=)\"?([0-9]+)\"?/i', $image_html, $class_id ) &&
397
				! preg_match( '/wp-block-jetpack-slideshow_image/', $image_html ) ) {
398
				$attachment_id = absint( $class_id[2] );
399
				/**
400
				 * If exactly the same image tag is used more than once, overwrite it.
401
				 * All identical tags will be replaced later with 'str_replace()'.
402
				 */
403
				$selected_images[ $attachment_id  ] = $image_html;
404
			}
405
		}
406
407
		$find    = array();
408
		$replace = array();
409
		if ( empty( $selected_images ) ) {
410
			return $content;
411
		}
412
413
		$attachments = get_posts(
414
			array(
415
				'include'          => array_keys( $selected_images ),
416
				'post_type'        => 'any',
417
				'post_status'      => 'any',
418
				'suppress_filters' => false,
419
			)
420
		);
421
422
		foreach ( $attachments as $attachment ) {
423
			$image_html = $selected_images[ $attachment->ID ];
424
425
			$attributes      = $this->add_data_to_images( array(), $attachment );
426
			$attributes_html = '';
427
			foreach ( $attributes as $k => $v ) {
428
				$attributes_html .= esc_attr( $k ) . '="' . esc_attr( $v ) . '" ';
429
			}
430
431
			$find[]    = $image_html;
432
			$replace[] = str_replace( '<img ', "<img $attributes_html", $image_html );
433
		}
434
435
		$content = str_replace( $find, $replace, $content );
436
		$this->enqueue_assets();
437
		return $content;
438
	}
439
440
	function add_data_to_images( $attr, $attachment = null ) {
441
		if (
442
			class_exists( 'Jetpack_AMP_Support' )
443
			&& Jetpack_AMP_Support::is_amp_request()
444
		) {
445
			return $attr;
446
		}
447
448
		$attachment_id = intval( $attachment->ID );
449
		if ( ! wp_attachment_is_image( $attachment_id ) ) {
450
			return $attr;
451
		}
452
453
		$orig_file       = wp_get_attachment_image_src( $attachment_id, 'full' );
454
		$orig_file       = isset( $orig_file[0] ) ? $orig_file[0] : wp_get_attachment_url( $attachment_id );
455
		$meta            = wp_get_attachment_metadata( $attachment_id );
456
		$size            = isset( $meta['width'] ) ? intval( $meta['width'] ) . ',' . intval( $meta['height'] ) : '';
457
		$img_meta        = ( ! empty( $meta['image_meta'] ) ) ? (array) $meta['image_meta'] : array();
458
		$comments_opened = intval( comments_open( $attachment_id ) );
459
460
		/**
461
		 * Note: Cannot generate a filename from the width and height wp_get_attachment_image_src() returns because
462
		 * it takes the $content_width global variable themes can set in consideration, therefore returning sizes
463
		 * which when used to generate a filename will likely result in a 404 on the image.
464
		 * $content_width has no filter we could temporarily de-register, run wp_get_attachment_image_src(), then
465
		 * re-register. So using returned file URL instead, which we can define the sizes from through filename
466
		 * parsing in the JS, as this is a failsafe file reference.
467
		 *
468
		 * EG with Twenty Eleven activated:
469
		 * array(4) { [0]=> string(82) "http://vanillawpinstall.blah/wp-content/uploads/2012/06/IMG_3534-1024x764.jpg" [1]=> int(584) [2]=> int(435) [3]=> bool(true) }
470
		 *
471
		 * EG with Twenty Ten activated:
472
		 * array(4) { [0]=> string(82) "http://vanillawpinstall.blah/wp-content/uploads/2012/06/IMG_3534-1024x764.jpg" [1]=> int(640) [2]=> int(477) [3]=> bool(true) }
473
		 */
474
475
		$medium_file_info = wp_get_attachment_image_src( $attachment_id, 'medium' );
476
		$medium_file      = isset( $medium_file_info[0] ) ? $medium_file_info[0] : '';
477
478
		$large_file_info = wp_get_attachment_image_src( $attachment_id, 'large' );
479
		$large_file      = isset( $large_file_info[0] ) ? $large_file_info[0] : '';
480
481
		$attachment       = get_post( $attachment_id );
482
		$attachment_title = wptexturize( $attachment->post_title );
483
		$attachment_desc  = wpautop( wptexturize( $attachment->post_content ) );
484
		// Not yet providing geo-data, need to "fuzzify" for privacy
485 View Code Duplication
		if ( ! empty( $img_meta ) ) {
486
			foreach ( $img_meta as $k => $v ) {
487
				if ( 'latitude' == $k || 'longitude' == $k ) {
488
					unset( $img_meta[ $k ] );
489
				}
490
			}
491
		}
492
493
		// See https://github.com/Automattic/jetpack/issues/2765
494
		if ( isset( $img_meta['keywords'] ) ) {
495
			unset( $img_meta['keywords'] );
496
		}
497
498
		$img_meta = json_encode( array_map( 'strval', array_filter( $img_meta, 'is_scalar' ) ) );
499
500
		$attr['data-attachment-id']     = $attachment_id;
501
		$attr['data-permalink']         = esc_attr( get_permalink( $attachment->ID ) );
502
		$attr['data-orig-file']         = esc_attr( $orig_file );
503
		$attr['data-orig-size']         = $size;
504
		$attr['data-comments-opened']   = $comments_opened;
505
		$attr['data-image-meta']        = esc_attr( $img_meta );
506
		$attr['data-image-title']       = esc_attr( htmlspecialchars( $attachment_title ) );
507
		$attr['data-image-description'] = esc_attr( htmlspecialchars( $attachment_desc ) );
508
		$attr['data-medium-file']       = esc_attr( $medium_file );
509
		$attr['data-large-file']        = esc_attr( $large_file );
510
511
		return $attr;
512
	}
513
514
	function add_data_to_container( $html ) {
515
		global $post;
516
		if (
517
			class_exists( 'Jetpack_AMP_Support' )
518
			&& Jetpack_AMP_Support::is_amp_request()
519
		) {
520
			return $html;
521
		}
522
523
		if ( isset( $post ) ) {
524
			$blog_id = (int) get_current_blog_id();
525
526
			$extra_data = array(
527
				'data-carousel-extra' => array(
528
					'blog_id'   => $blog_id,
529
					'permalink' => get_permalink( $post->ID ),
530
				),
531
			);
532
533
			/**
534
			 * Filter the data added to the Gallery container.
535
			 *
536
			 * @module carousel
537
			 *
538
			 * @since 1.6.0
539
			 *
540
			 * @param array $extra_data Array of data about the site and the post.
541
			 */
542
			$extra_data = apply_filters( 'jp_carousel_add_data_to_container', $extra_data );
543
544
			foreach ( (array) $extra_data as $data_key => $data_values ) {
545
				// Do not go any further if DOMDocument is disabled on the server.
546
				if ( ! class_exists( 'DOMDocument' ) ) {
547
					return $html;
548
				}
549
550
				// Let's grab all containers from the HTML.
551
				$dom_doc = new DOMDocument();
552
553
				/*
554
				 * The @ is not enough to suppress errors when dealing with libxml,
555
				 * we have to tell it directly how we want to handle errors.
556
				 */
557
				$old_libxml_disable_entity_loader = libxml_disable_entity_loader( true );
558
				$old_libxml_use_internal_errors   = libxml_use_internal_errors( true );
559
				@$dom_doc->loadHTML( $html ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
560
				libxml_use_internal_errors( $old_libxml_use_internal_errors );
561
				libxml_disable_entity_loader( $old_libxml_disable_entity_loader );
562
563
				// Let's look for lists and divs.
564
				$ul_tags  = $dom_doc->getElementsByTagName( 'ul' );
565
				$div_tags = $dom_doc->getElementsByTagName( 'div' );
566
567
				// Loop through each ul, and add the data to it if it is a gallery block.
568
				foreach ( $ul_tags as $ul_tag ) {
569
					if ( false !== strpos( $ul_tag->getAttribute( 'class' ), 'wp-block-gallery' ) ) {
570
						$ul_tag->setAttribute(
571
							$data_key,
572
							wp_json_encode( $data_values )
573
						);
574
					}
575
				}
576
577
				/*
578
				 * Loop through each div and add the data, only when it's a gallery block div.
579
				 * We want to avoid adding data to divs like wp-block-columns.
580
				 * We do however want data on divs like wp-block-jetpack-tiled-gallery.
581
				 */
582
				foreach ( $div_tags as $div_tag ) {
583
					if (
584
						false === strpos( $div_tag->getAttribute( 'class' ), 'wp-block-' )
585
						|| false !== strpos( $div_tag->getAttribute( 'class' ), 'gallery' )
586
					) {
587
						$div_tag->setAttribute(
588
							$data_key,
589
							wp_json_encode( $data_values )
590
						);
591
					}
592
				}
593
594
				// Save our updated HTML.
595
				$html = $dom_doc->saveHTML();
596
			}
597
		}
598
599
		return $html;
600
	}
601
602
	function get_attachment_comments() {
603
		if ( ! headers_sent() ) {
604
			header( 'Content-type: text/javascript' );
605
		}
606
607
		/**
608
		 * Allows for the checking of privileges of the blog user before comments
609
		 * are packaged as JSON and sent back from the get_attachment_comments
610
		 * AJAX endpoint
611
		 *
612
		 * @module carousel
613
		 *
614
		 * @since 1.6.0
615
		 */
616
		do_action( 'jp_carousel_check_blog_user_privileges' );
617
618
		$attachment_id = ( isset( $_REQUEST['id'] ) ) ? (int) $_REQUEST['id'] : 0;
619
		$offset        = ( isset( $_REQUEST['offset'] ) ) ? (int) $_REQUEST['offset'] : 0;
620
621
		if ( ! $attachment_id ) {
622
			echo json_encode( __( 'Missing attachment ID.', 'jetpack' ) );
623
			die();
624
		}
625
626
		if ( $offset < 1 ) {
627
			$offset = 0;
628
		}
629
630
		$comments = get_comments(
631
			array(
632
				'status'  => 'approve',
633
				'order'   => ( 'asc' == get_option( 'comment_order' ) ) ? 'ASC' : 'DESC',
634
				'number'  => 10,
635
				'offset'  => $offset,
636
				'post_id' => $attachment_id,
637
			)
638
		);
639
640
		$out = array();
641
642
		// Can't just send the results, they contain the commenter's email address.
643
		foreach ( $comments as $comment ) {
644
			$avatar = get_avatar( $comment->comment_author_email, 64 );
645
			if ( ! $avatar ) {
646
				$avatar = '';
647
			}
648
			$out[] = array(
649
				'id'              => $comment->comment_ID,
650
				'parent_id'       => $comment->comment_parent,
651
				'author_markup'   => get_comment_author_link( $comment->comment_ID ),
652
				'gravatar_markup' => $avatar,
653
				'date_gmt'        => $comment->comment_date_gmt,
654
				'content'         => wpautop( $comment->comment_content ),
655
			);
656
		}
657
658
		die( json_encode( $out ) );
659
	}
660
661
	function post_attachment_comment() {
662
		if ( ! headers_sent() ) {
663
			header( 'Content-type: text/javascript' );
664
		}
665
666
		if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'carousel_nonce' ) ) {
667
			die( json_encode( array( 'error' => __( 'Nonce verification failed.', 'jetpack' ) ) ) );
668
		}
669
670
		$_blog_id = (int) $_POST['blog_id'];
671
		$_post_id = (int) $_POST['id'];
672
		$comment  = $_POST['comment'];
673
674
		if ( empty( $_blog_id ) ) {
675
			die( json_encode( array( 'error' => __( 'Missing target blog ID.', 'jetpack' ) ) ) );
676
		}
677
678
		if ( empty( $_post_id ) ) {
679
			die( json_encode( array( 'error' => __( 'Missing target post ID.', 'jetpack' ) ) ) );
680
		}
681
682
		if ( empty( $comment ) ) {
683
			die( json_encode( array( 'error' => __( 'No comment text was submitted.', 'jetpack' ) ) ) );
684
		}
685
686
		// Used in context like NewDash
687
		$switched = false;
688
		if ( is_multisite() && $_blog_id != get_current_blog_id() ) {
689
			switch_to_blog( $_blog_id );
690
			$switched = true;
691
		}
692
693
		/** This action is documented in modules/carousel/jetpack-carousel.php */
694
		do_action( 'jp_carousel_check_blog_user_privileges' );
695
696
		if ( ! comments_open( $_post_id ) ) {
697
			die( json_encode( array( 'error' => __( 'Comments on this post are closed.', 'jetpack' ) ) ) );
698
		}
699
700
		if ( is_user_logged_in() ) {
701
			$user         = wp_get_current_user();
702
			$user_id      = $user->ID;
703
			$display_name = $user->display_name;
704
			$email        = $user->user_email;
705
			$url          = $user->user_url;
706
707
			if ( empty( $user_id ) ) {
708
				die( json_encode( array( 'error' => __( 'Sorry, but we could not authenticate your request.', 'jetpack' ) ) ) );
709
			}
710
		} else {
711
			$user_id      = 0;
712
			$display_name = $_POST['author'];
713
			$email        = $_POST['email'];
714
			$url          = $_POST['url'];
715
716
			if ( get_option( 'require_name_email' ) ) {
717
				if ( empty( $display_name ) ) {
718
					die( json_encode( array( 'error' => __( 'Please provide your name.', 'jetpack' ) ) ) );
719
				}
720
721
				if ( empty( $email ) ) {
722
					die( json_encode( array( 'error' => __( 'Please provide an email address.', 'jetpack' ) ) ) );
723
				}
724
725
				if ( ! is_email( $email ) ) {
726
					die( json_encode( array( 'error' => __( 'Please provide a valid email address.', 'jetpack' ) ) ) );
727
				}
728
			}
729
		}
730
731
		$comment_data = array(
732
			'comment_content'      => $comment,
733
			'comment_post_ID'      => $_post_id,
734
			'comment_author'       => $display_name,
735
			'comment_author_email' => $email,
736
			'comment_author_url'   => $url,
737
			'comment_approved'     => 0,
738
			'comment_type'         => '',
739
		);
740
741
		if ( ! empty( $user_id ) ) {
742
			$comment_data['user_id'] = $user_id;
743
		}
744
745
		// Note: wp_new_comment() sanitizes and validates the values (too).
746
		$comment_id = wp_new_comment( $comment_data );
747
748
		/**
749
		 * Fires before adding a new comment to the database via the get_attachment_comments ajax endpoint.
750
		 *
751
		 * @module carousel
752
		 *
753
		 * @since 1.6.0
754
		 */
755
		do_action( 'jp_carousel_post_attachment_comment' );
756
		$comment_status = wp_get_comment_status( $comment_id );
757
758
		if ( true == $switched ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
759
			restore_current_blog();
760
		}
761
762
		die(
763
			json_encode(
764
				array(
765
					'comment_id'     => $comment_id,
766
					'comment_status' => $comment_status,
767
				)
768
			)
769
		);
770
	}
771
772
	function register_settings() {
773
		add_settings_section( 'carousel_section', __( 'Image Gallery Carousel', 'jetpack' ), array( $this, 'carousel_section_callback' ), 'media' );
774
775
		if ( ! $this->in_jetpack ) {
776
			add_settings_field( 'carousel_enable_it', __( 'Enable carousel', 'jetpack' ), array( $this, 'carousel_enable_it_callback' ), 'media', 'carousel_section' );
777
			register_setting( 'media', 'carousel_enable_it', array( $this, 'carousel_enable_it_sanitize' ) );
778
		}
779
780
		add_settings_field( 'carousel_background_color', __( 'Background color', 'jetpack' ), array( $this, 'carousel_background_color_callback' ), 'media', 'carousel_section' );
781
		register_setting( 'media', 'carousel_background_color', array( $this, 'carousel_background_color_sanitize' ) );
782
783
		add_settings_field( 'carousel_display_exif', __( 'Metadata', 'jetpack' ), array( $this, 'carousel_display_exif_callback' ), 'media', 'carousel_section' );
784
		register_setting( 'media', 'carousel_display_exif', array( $this, 'carousel_display_exif_sanitize' ) );
785
786
		// No geo setting yet, need to "fuzzify" data first, for privacy
787
		// add_settings_field('carousel_display_geo', __( 'Geolocation', 'jetpack' ), array( $this, 'carousel_display_geo_callback' ), 'media', 'carousel_section' );
788
		// register_setting( 'media', 'carousel_display_geo', array( $this, 'carousel_display_geo_sanitize' ) );
789
	}
790
791
	// Fulfill the settings section callback requirement by returning nothing
792
	function carousel_section_callback() {
793
		return;
794
	}
795
796
	function test_1or0_option( $value, $default_to_1 = true ) {
797
		if ( true == $default_to_1 ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
798
			// Binary false (===) of $value means it has not yet been set, in which case we do want to default sites to 1
799
			if ( false === $value ) {
800
				$value = 1;
801
			}
802
		}
803
		return ( 1 == $value ) ? 1 : 0;
804
	}
805
806
	function sanitize_1or0_option( $value ) {
807
		return ( 1 == $value ) ? 1 : 0;
808
	}
809
810
	function settings_checkbox( $name, $label_text, $extra_text = '', $default_to_checked = true ) {
811
		if ( empty( $name ) ) {
812
			return;
813
		}
814
		$option = $this->test_1or0_option( get_option( $name ), $default_to_checked );
815
		echo '<fieldset>';
816
		echo '<input type="checkbox" name="' . esc_attr( $name ) . '" id="' . esc_attr( $name ) . '" value="1" ';
817
		checked( '1', $option );
818
		echo '/> <label for="' . esc_attr( $name ) . '">' . $label_text . '</label>';
819
		if ( ! empty( $extra_text ) ) {
820
			echo '<p class="description">' . $extra_text . '</p>';
821
		}
822
		echo '</fieldset>';
823
	}
824
825
	function settings_select( $name, $values, $extra_text = '' ) {
826
		if ( empty( $name ) || ! is_array( $values ) || empty( $values ) ) {
827
			return;
828
		}
829
		$option = get_option( $name );
830
		echo '<fieldset>';
831
		echo '<select name="' . esc_attr( $name ) . '" id="' . esc_attr( $name ) . '">';
832
		foreach ( $values as $key => $value ) {
833
			echo '<option value="' . esc_attr( $key ) . '" ';
834
			selected( $key, $option );
835
			echo '>' . esc_html( $value ) . '</option>';
836
		}
837
		echo '</select>';
838
		if ( ! empty( $extra_text ) ) {
839
			echo '<p class="description">' . $extra_text . '</p>';
840
		}
841
		echo '</fieldset>';
842
	}
843
844
	function carousel_display_exif_callback() {
845
		$this->settings_checkbox( 'carousel_display_exif', __( 'Show photo metadata (<a href="https://en.wikipedia.org/wiki/Exchangeable_image_file_format" rel="noopener noreferrer" target="_blank">Exif</a>) in carousel, when available.', 'jetpack' ) );
846
	}
847
848
	function carousel_display_exif_sanitize( $value ) {
849
		return $this->sanitize_1or0_option( $value );
850
	}
851
852
	function carousel_display_geo_callback() {
853
		$this->settings_checkbox( 'carousel_display_geo', __( 'Show map of photo location in carousel, when available.', 'jetpack' ) );
854
	}
855
856
	function carousel_display_geo_sanitize( $value ) {
857
		return $this->sanitize_1or0_option( $value );
858
	}
859
860
	function carousel_background_color_callback() {
861
		$this->settings_select(
862
			'carousel_background_color', array(
863
				'black' => __( 'Black', 'jetpack' ),
864
				'white' => __( 'White', 'jetpack' ),
865
			)
866
		);
867
	}
868
869
	function carousel_background_color_sanitize( $value ) {
870
		return ( 'white' == $value ) ? 'white' : 'black';
871
	}
872
873
	function carousel_enable_it_callback() {
874
		$this->settings_checkbox( 'carousel_enable_it', __( 'Display images in full-size carousel slideshow.', 'jetpack' ) );
875
	}
876
877
	function carousel_enable_it_sanitize( $value ) {
878
		return $this->sanitize_1or0_option( $value );
879
	}
880
}
881
882
new Jetpack_Carousel;
883