| @@ -1,0 +1,333 @@ | ||
| 1 | +<?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName | |
| 2 | +/** | |
| 3 | + * Slideshow shortcode. | |
| 4 | + * Adds a new "slideshow" gallery type when adding a gallery using the classic editor. | |
| 5 | + * | |
| 6 | + * @package automattic/jetpack | |
| 7 | + */ | |
| 8 | + | |
| 9 | +use Automattic\Jetpack\Assets; | |
| 10 | +use Automattic\Jetpack\Extensions\Slideshow; | |
| 11 | + | |
| 12 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 13 | + exit( 0 ); | |
| 14 | +} | |
| 15 | + | |
| 16 | +/** | |
| 17 | + * Slideshow shortcode usage: [gallery type="slideshow"] or the older [slideshow] | |
| 18 | + * | |
| 19 | + * @phan-constructor-used-for-side-effects | |
| 20 | + */ | |
| 21 | +class Jetpack_Slideshow_Shortcode { | |
| 22 | + /** | |
| 23 | + * Number of slideshows on a page. | |
| 24 | + * | |
| 25 | + * @var int | |
| 26 | + */ | |
| 27 | + public $instance_count = 0; | |
| 28 | + | |
| 29 | + /** | |
| 30 | + * Constructor | |
| 31 | + */ | |
| 32 | + public function __construct() { | |
| 33 | + global $shortcode_tags; | |
| 34 | + | |
| 35 | + // Only if the slideshow shortcode has not already been defined. | |
| 36 | + if ( ! array_key_exists( 'slideshow', $shortcode_tags ) ) { | |
| 37 | + add_shortcode( 'slideshow', array( $this, 'shortcode_callback' ) ); | |
| 38 | + } | |
| 39 | + | |
| 40 | + // Only if the gallery shortcode has not been redefined. | |
| 41 | + if ( isset( $shortcode_tags['gallery'] ) && 'gallery_shortcode' === $shortcode_tags['gallery'] ) { | |
| 42 | + add_filter( 'post_gallery', array( $this, 'post_gallery' ), 1002, 2 ); | |
| 43 | + add_filter( 'jetpack_gallery_types', array( $this, 'add_gallery_type' ), 10 ); | |
| 44 | + } | |
| 45 | + } | |
| 46 | + | |
| 47 | + /** | |
| 48 | + * Responds to the [gallery] shortcode, but not an actual shortcode callback. | |
| 49 | + * | |
| 50 | + * @param string $value An empty string if nothing has modified the gallery output, the output html otherwise. | |
| 51 | + * @param array $attr The shortcode attributes array. | |
| 52 | + * | |
| 53 | + * @return string The (un)modified $value | |
| 54 | + */ | |
| 55 | + public function post_gallery( $value, $attr ) { | |
| 56 | + // Bail if somebody else has done something. | |
| 57 | + if ( ! empty( $value ) ) { | |
| 58 | + return $value; | |
| 59 | + } | |
| 60 | + | |
| 61 | + // If [gallery type="slideshow"] have it behave just like [slideshow]. | |
| 62 | + if ( ! empty( $attr['type'] ) && 'slideshow' === $attr['type'] ) { | |
| 63 | + return $this->shortcode_callback( $attr ); | |
| 64 | + } | |
| 65 | + | |
| 66 | + return $value; | |
| 67 | + } | |
| 68 | + | |
| 69 | + /** | |
| 70 | + * Add the Slideshow type to gallery settings | |
| 71 | + * | |
| 72 | + * @see Jetpack_Tiled_Gallery::media_ui_print_templates | |
| 73 | + * | |
| 74 | + * @param array $types An array of types where the key is the value, and the value is the caption. | |
| 75 | + * | |
| 76 | + * @return array | |
| 77 | + */ | |
| 78 | + public function add_gallery_type( $types = array() ) { | |
| 79 | + $types['slideshow'] = esc_html__( 'Slideshow', 'jetpack' ); | |
| 80 | + | |
| 81 | + return $types; | |
| 82 | + } | |
| 83 | + | |
| 84 | + /** | |
| 85 | + * Display shortcode. | |
| 86 | + * | |
| 87 | + * @param array $attr Shortcode attributes. | |
| 88 | + */ | |
| 89 | + public function shortcode_callback( $attr ) { | |
| 90 | + $post_id = get_the_ID(); | |
| 91 | + | |
| 92 | + $attr = shortcode_atts( | |
| 93 | + array( | |
| 94 | + 'trans' => 'fade', | |
| 95 | + 'order' => 'ASC', | |
| 96 | + 'orderby' => 'menu_order ID', | |
| 97 | + 'id' => $post_id, | |
| 98 | + 'include' => '', | |
| 99 | + 'exclude' => '', | |
| 100 | + 'autostart' => true, | |
| 101 | + 'size' => '', | |
| 102 | + ), | |
| 103 | + $attr, | |
| 104 | + 'slideshow' | |
| 105 | + ); | |
| 106 | + | |
| 107 | + if ( 'rand' === strtolower( $attr['order'] ) ) { | |
| 108 | + $attr['orderby'] = 'none'; | |
| 109 | + } | |
| 110 | + | |
| 111 | + $attr['orderby'] = sanitize_sql_orderby( $attr['orderby'] ); | |
| 112 | + if ( ! $attr['orderby'] ) { | |
| 113 | + $attr['orderby'] = 'menu_order ID'; | |
| 114 | + } | |
| 115 | + | |
| 116 | + if ( ! $attr['size'] ) { | |
| 117 | + $attr['size'] = 'full'; | |
| 118 | + } | |
| 119 | + | |
| 120 | + // Don't restrict to the current post if include. | |
| 121 | + $post_parent = ( empty( $attr['include'] ) ) ? (int) $attr['id'] : null; | |
| 122 | + | |
| 123 | + $attachments = get_posts( | |
| 124 | + array( | |
| 125 | + 'post_status' => 'inherit', | |
| 126 | + 'post_type' => 'attachment', | |
| 127 | + 'post_mime_type' => 'image', | |
| 128 | + 'posts_per_page' => - 1, | |
| 129 | + 'post_parent' => $post_parent, | |
| 130 | + 'order' => $attr['order'], | |
| 131 | + 'orderby' => $attr['orderby'], | |
| 132 | + 'include' => $attr['include'], | |
| 133 | + 'exclude' => $attr['exclude'], | |
| 134 | + 'suppress_filters' => false, | |
| 135 | + ) | |
| 136 | + ); | |
| 137 | + | |
| 138 | + if ( ! is_countable( $attachments ) || count( $attachments ) < 1 ) { | |
| 139 | + return false; | |
| 140 | + } | |
| 141 | + | |
| 142 | + $gallery_instance = sprintf( 'gallery-%d-%d', $attr['id'], ++$this->instance_count ); | |
| 143 | + | |
| 144 | + $gallery = array(); | |
| 145 | + foreach ( $attachments as $attachment ) { | |
| 146 | + $attachment_image_src = wp_get_attachment_image_src( $attachment->ID, $attr['size'] ); | |
| 147 | + $attachment_image_src = false !== $attachment_image_src ? $attachment_image_src[0] : ''; // [url, width, height]. | |
| 148 | + $attachment_image_title = get_the_title( $attachment->ID ); | |
| 149 | + $attachment_image_alt = get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true ); | |
| 150 | + /** | |
| 151 | + * Filters the Slideshow slide caption. | |
| 152 | + * | |
| 153 | + * @module shortcodes | |
| 154 | + * | |
| 155 | + * @since 2.3.0 | |
| 156 | + * @since $$next-version$$ Captions render as plain text, so markup returned here is discarded. | |
| 157 | + * | |
| 158 | + * @param string wptexturize( wp_strip_all_tags( $attachment->post_excerpt ) ) Post excerpt. | |
| 159 | + * @param string $attachment ->ID Attachment ID. | |
| 160 | + */ | |
| 161 | + $caption = apply_filters( 'jetpack_slideshow_slide_caption', wptexturize( wp_strip_all_tags( $attachment->post_excerpt ) ), $attachment->ID ); | |
| 162 | + | |
| 163 | + $gallery[] = (object) array( | |
| 164 | + 'src' => (string) esc_url_raw( $attachment_image_src ), | |
| 165 | + 'id' => (string) $attachment->ID, | |
| 166 | + 'title' => (string) esc_attr( $attachment_image_title ), | |
| 167 | + 'alt' => (string) esc_attr( $attachment_image_alt ), | |
| 168 | + 'caption' => (string) $caption, | |
| 169 | + 'itemprop' => 'image', | |
| 170 | + ); | |
| 171 | + } | |
| 172 | + | |
| 173 | + $color = Jetpack_Options::get_option( 'slideshow_background_color', 'black' ); | |
| 174 | + $autostart = $attr['autostart'] ? $attr['autostart'] : 'true'; | |
| 175 | + $js_attr = array( | |
| 176 | + 'gallery' => $gallery, | |
| 177 | + 'selector' => $gallery_instance, | |
| 178 | + 'trans' => $attr['trans'] ? $attr['trans'] : 'fade', | |
| 179 | + 'autostart' => $autostart, | |
| 180 | + 'color' => $color, | |
| 181 | + ); | |
| 182 | + | |
| 183 | + // Show a link to the gallery in feeds. | |
| 184 | + if ( is_feed() ) { | |
| 185 | + return sprintf( | |
| 186 | + '<a href="%s">%s</a>', | |
| 187 | + esc_url( get_permalink( $post_id ) . '#' . $gallery_instance . '-slideshow' ), | |
| 188 | + esc_html__( 'Click to view slideshow.', 'jetpack' ) | |
| 189 | + ); | |
| 190 | + } | |
| 191 | + | |
| 192 | + if ( | |
| 193 | + class_exists( 'Jetpack_AMP_Support' ) | |
| 194 | + && Jetpack_AMP_Support::is_amp_request() | |
| 195 | + ) { | |
| 196 | + // Load the styles and use the rendering method from the Slideshow block. | |
| 197 | + Jetpack_Gutenberg::load_styles_as_required( 'slideshow' ); | |
| 198 | + | |
| 199 | + $amp_args = array( | |
| 200 | + 'ids' => wp_list_pluck( $gallery, 'id' ), | |
| 201 | + ); | |
| 202 | + | |
| 203 | + if ( 'true' == $autostart ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- attribute can be stored as boolean or string. | |
| 204 | + $amp_args['autoplay'] = true; | |
| 205 | + } | |
| 206 | + | |
| 207 | + /* | |
| 208 | + * Blocks can be disabled in Jetpack Settings. | |
| 209 | + * If that's the case, we need to include the slideshow block manually. | |
| 210 | + * render_amp() lives in the block's render.php, which is loaded lazily, | |
| 211 | + * so make sure it is available before calling it. | |
| 212 | + */ | |
| 213 | + if ( ! function_exists( 'Automattic\Jetpack\Extensions\Slideshow\render_amp' ) ) { | |
| 214 | + require_once JETPACK__PLUGIN_DIR . 'extensions/blocks/slideshow/render.php'; | |
| 215 | + } | |
| 216 | + | |
| 217 | + return Slideshow\render_amp( $amp_args ); | |
| 218 | + } | |
| 219 | + | |
| 220 | + return $this->slideshow_js( $js_attr ); | |
| 221 | + } | |
| 222 | + | |
| 223 | + /** | |
| 224 | + * Render the slideshow js | |
| 225 | + * | |
| 226 | + * Returns the necessary markup and js to fire a slideshow. | |
| 227 | + * | |
| 228 | + * @param array $attr Attributes for the slideshow. | |
| 229 | + * | |
| 230 | + * @uses $this->enqueue_scripts() | |
| 231 | + * | |
| 232 | + * @return string HTML output. | |
| 233 | + */ | |
| 234 | + public function slideshow_js( $attr ) { | |
| 235 | + // Enqueue scripts. | |
| 236 | + $this->enqueue_scripts(); | |
| 237 | + | |
| 238 | + $output = '<p class="jetpack-slideshow-noscript robots-nocontent">' . esc_html__( 'This slideshow requires JavaScript.', 'jetpack' ) . '</p>'; | |
| 239 | + | |
| 240 | + /* | |
| 241 | + * Checking for JSON_HEX_AMP and friends here allows us to get rid of | |
| 242 | + * '"', that can sometimes be included in the JSON input in some languages like French. | |
| 243 | + */ | |
| 244 | + $gallery_attributes = _wp_specialchars( | |
| 245 | + wp_check_invalid_utf8( | |
| 246 | + wp_json_encode( $attr['gallery'], JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) | |
| 247 | + ), | |
| 248 | + ENT_QUOTES, | |
| 249 | + false, | |
| 250 | + true | |
| 251 | + ); | |
| 252 | + | |
| 253 | + $output .= sprintf( | |
| 254 | + '<div id="%s" class="jetpack-slideshow-window jetpack-slideshow jetpack-slideshow-%s" data-trans="%s" data-autostart="%s" data-gallery="%s" itemscope itemtype="https://schema.org/ImageGallery"></div>', | |
| 255 | + esc_attr( $attr['selector'] . '-slideshow' ), | |
| 256 | + esc_attr( $attr['color'] ), | |
| 257 | + esc_attr( $attr['trans'] ), | |
| 258 | + esc_attr( $attr['autostart'] ), | |
| 259 | + $gallery_attributes | |
| 260 | + ); | |
| 261 | + | |
| 262 | + return $output; | |
| 263 | + } | |
| 264 | + | |
| 265 | + /** | |
| 266 | + * Actually enqueues the scripts and styles. | |
| 267 | + */ | |
| 268 | + public function enqueue_scripts() { | |
| 269 | + | |
| 270 | + wp_register_script( | |
| 271 | + 'jetpack-shortcode-deps', | |
| 272 | + plugins_url( '_inc/build/shortcodes/js/dependencies.min.js', JETPACK__PLUGIN_FILE ), | |
| 273 | + array( 'jquery' ), | |
| 274 | + '20250905', | |
| 275 | + true | |
| 276 | + ); | |
| 277 | + | |
| 278 | + wp_enqueue_script( | |
| 279 | + 'jetpack-slideshow', | |
| 280 | + Assets::get_file_url_for_environment( '_inc/build/shortcodes/js/slideshow-shortcode.min.js', 'modules/shortcodes/js/slideshow-shortcode.js' ), | |
| 281 | + array( 'jquery', 'jetpack-shortcode-deps' ), | |
| 282 | + '20260915', | |
| 283 | + true | |
| 284 | + ); | |
| 285 | + wp_enqueue_style( | |
| 286 | + 'jetpack-slideshow', | |
| 287 | + plugins_url( '/css/slideshow-shortcode.css', __FILE__ ), | |
| 288 | + array(), | |
| 289 | + JETPACK__VERSION | |
| 290 | + ); | |
| 291 | + wp_style_add_data( 'jetpack-slideshow', 'rtl', 'replace' ); | |
| 292 | + | |
| 293 | + require_once JETPACK__PLUGIN_DIR . '_inc/lib/class-jetpack-spinner.php'; | |
| 294 | + wp_localize_script( | |
| 295 | + 'jetpack-slideshow', | |
| 296 | + 'jetpackSlideshowSettings', | |
| 297 | + /** | |
| 298 | + * Filters the slideshow JavaScript spinner. | |
| 299 | + * | |
| 300 | + * @module shortcodes | |
| 301 | + * | |
| 302 | + * @since 2.1.0 | |
| 303 | + * @since 4.7.0 Added the `speed` option to the array of options. | |
| 304 | + * | |
| 305 | + * @param array $args | |
| 306 | + * - string - spinner - URL of the spinner image. | |
| 307 | + * - string - speed - Speed of the slideshow. Defaults to 4000. | |
| 308 | + * - string - label_prev - Aria label for slideshow's previous button | |
| 309 | + * - string - label_stop - Aria label for slideshow's pause button | |
| 310 | + * - string - label_next - Aria label for slideshow's next button | |
| 311 | + */ | |
| 312 | + apply_filters( | |
| 313 | + 'jetpack_js_slideshow_settings', | |
| 314 | + array( | |
| 315 | + 'spinner' => 'data:image/svg+xml,' . rawurlencode( Jetpack_Spinner::render( 24 ) ), | |
| 316 | + 'speed' => '4000', | |
| 317 | + 'label_prev' => __( 'Previous Slide', 'jetpack' ), | |
| 318 | + 'label_stop' => __( 'Pause Slideshow', 'jetpack' ), | |
| 319 | + 'label_next' => __( 'Next Slide', 'jetpack' ), | |
| 320 | + ) | |
| 321 | + ) | |
| 322 | + ); | |
| 323 | + } | |
| 324 | + | |
| 325 | + /** | |
| 326 | + * Instantiate shortcode. | |
| 327 | + */ | |
| 328 | + public static function init() { | |
| 329 | + new Jetpack_Slideshow_Shortcode(); | |
| 330 | + } | |
| 331 | +} | |
| 332 | + | |
| 333 | +Jetpack_Slideshow_Shortcode::init(); | |