Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 150
0.00% covered (danger)
0.00%
0 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Tiled_Gallery
0.00% covered (danger)
0.00%
0 / 143
0.00% covered (danger)
0.00%
0 / 14
2162
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 tiles_enabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 set_atts
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
132
 get_attachments
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
30
 default_scripts_and_styles
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
2
 gallery_shortcode
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
132
 gallery_already_redefined
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 init
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 get_content_width
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 jetpack_gallery_types
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 jetpack_default_gallery_type
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 get_talaveras
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 settings_api_init
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 setting_html
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3use Automattic\Jetpack\Assets;
4use Automattic\Jetpack\Image_CDN\Image_CDN;
5use Automattic\Jetpack\Status;
6
7if ( ! defined( 'ABSPATH' ) ) {
8    exit( 0 );
9}
10
11// Include the class file containing methods for rounding constrained array elements.
12// Here the constrained array element is the dimension of a row, group or an image in the tiled gallery.
13require_once __DIR__ . '/math/class-constrained-array-rounding.php';
14
15// Layouts
16require_once __DIR__ . '/tiled-gallery/tiled-gallery-rectangular.php';
17require_once __DIR__ . '/tiled-gallery/tiled-gallery-square.php';
18require_once __DIR__ . '/tiled-gallery/tiled-gallery-circle.php';
19
20/**
21 * Jetpack tiled gallery class.
22 */
23class Jetpack_Tiled_Gallery {
24    /**
25     * Shortcode attributes.
26     *
27     * @var array
28     */
29    public $atts;
30
31    /**
32     * Text direction (right or left).
33     *
34     * @var string
35     */
36    public $float;
37
38    /**
39     * Supported gallery design types.
40     *
41     * @var array
42     */
43    private static $talaveras = array( 'rectangular', 'square', 'circle', 'rectangle', 'columns' );
44
45    /**
46     * Class constructor.
47     */
48    public function __construct() {
49        add_action( 'admin_init', array( $this, 'settings_api_init' ) );
50        add_filter( 'jetpack_gallery_types', array( $this, 'jetpack_gallery_types' ), 9 );
51        add_filter( 'jetpack_default_gallery_type', array( $this, 'jetpack_default_gallery_type' ) );
52    }
53
54    /**
55     * Check whether tiling is enabled.
56     *
57     * @return bool
58     */
59    public function tiles_enabled() {
60        return '' !== Jetpack_Options::get_option_and_ensure_autoload( 'tiled_galleries', '' );
61    }
62
63    /**
64     * Set attributes.
65     *
66     * @param array $atts - the attributes.
67     */
68    public function set_atts( $atts ) {
69        global $post;
70
71        $this->atts = shortcode_atts(
72            array(
73                'order'     => 'ASC',
74                'orderby'   => 'menu_order ID',
75                'id'        => isset( $post->ID ) ? $post->ID : 0,
76                'include'   => '',
77                'exclude'   => '',
78                'type'      => '',
79                'grayscale' => false,
80                'link'      => '',
81                'columns'   => 3,
82            ),
83            $atts,
84            'gallery'
85        );
86
87        $this->atts['id'] = (int) $this->atts['id'];
88        $this->float      = is_rtl() ? 'right' : 'left';
89
90        // Default to rectangular is tiled galleries are checked
91        if ( $this->tiles_enabled() && ( ! $this->atts['type'] || 'default' === $this->atts['type'] ) ) {
92            /** This filter is already documented in class-jetpack-gallery-settings.php */
93            $this->atts['type'] = apply_filters( 'jetpack_default_gallery_type', 'rectangular' );
94        }
95
96        if ( ! $this->atts['orderby'] ) {
97            $this->atts['orderby'] = sanitize_sql_orderby( $this->atts['orderby'] );
98            if ( ! $this->atts['orderby'] ) {
99                $this->atts['orderby'] = 'menu_order ID';
100            }
101        }
102
103        if ( 'rand' === strtolower( $this->atts['order'] ) ) {
104            $this->atts['orderby'] = 'rand';
105        }
106
107        // We shouldn't have more than 20 columns.
108        if ( ! is_numeric( $this->atts['columns'] ) || 20 < $this->atts['columns'] ) {
109            $this->atts['columns'] = 3;
110        }
111    }
112
113    /**
114     * Get the media attachments.
115     *
116     * @return WP_Post[]
117     */
118    public function get_attachments() {
119        $atts = $this->atts;
120
121        if ( ! empty( $atts['include'] ) ) {
122            $include      = preg_replace( '/[^0-9,]+/', '', $atts['include'] );
123            $_attachments = get_posts(
124                array(
125                    'include'          => $include,
126                    'post_status'      => 'inherit',
127                    'post_type'        => 'attachment',
128                    'post_mime_type'   => 'image',
129                    'order'            => $atts['order'],
130                    'orderby'          => $atts['orderby'],
131                    'suppress_filters' => false,
132                )
133            );
134
135            $attachments = array();
136            foreach ( $_attachments as $key => $val ) {
137                $attachments[ $val->ID ] = $_attachments[ $key ];
138            }
139        } elseif ( 0 === $atts['id'] ) {
140            /*
141             * Should NEVER Happen but infinite_scroll_load_other_plugins_scripts means it does
142             * Querying with post_parent == 0 can generate stupidly memcache sets
143             * on sites with 10000's of unattached attachments as get_children puts every post in the cache.
144             * TODO Fix this properly.
145             */
146            $attachments = array();
147        } elseif ( ! empty( $atts['exclude'] ) ) {
148            $exclude     = preg_replace( '/[^0-9,]+/', '', $atts['exclude'] );
149            $attachments = get_children(
150                array(
151                    'post_parent'      => $atts['id'],
152                    'exclude'          => $exclude,
153                    'post_status'      => 'inherit',
154                    'post_type'        => 'attachment',
155                    'post_mime_type'   => 'image',
156                    'order'            => $atts['order'],
157                    'orderby'          => $atts['orderby'],
158                    'suppress_filters' => false,
159                )
160            );
161        } else {
162            $attachments = get_children(
163                array(
164                    'post_parent'      => $atts['id'],
165                    'post_status'      => 'inherit',
166                    'post_type'        => 'attachment',
167                    'post_mime_type'   => 'image',
168                    'order'            => $atts['order'],
169                    'orderby'          => $atts['orderby'],
170                    'suppress_filters' => false,
171                )
172            );
173        }
174        return $attachments;
175    }
176
177    /**
178     * Enqueue the default scripts and styles.
179     */
180    public static function default_scripts_and_styles() {
181        wp_enqueue_script(
182            'tiled-gallery',
183            Assets::get_file_url_for_environment(
184                '_inc/build/tiled-gallery/tiled-gallery/tiled-gallery.min.js',
185                'modules/tiled-gallery/tiled-gallery/tiled-gallery.js'
186            ),
187            array(),
188            JETPACK__VERSION,
189            array(
190                'in_footer' => true,
191                'strategy'  => 'defer',
192            )
193        );
194        wp_enqueue_style( 'tiled-gallery', plugins_url( 'tiled-gallery/tiled-gallery.css', __FILE__ ), array(), '2023-08-21' );
195        wp_style_add_data( 'tiled-gallery', 'rtl', 'replace' );
196    }
197
198    /**
199     * The gallery shortcode.
200     *
201     * @param mixed $val - the value.
202     * @param array $atts - the attributes.
203     *
204     * @return string
205     */
206    public function gallery_shortcode( $val, $atts ) {
207        if ( ! empty( $val ) ) { // something else is overriding post_gallery, like a custom VIP shortcode
208            return $val;
209        }
210
211        $this->set_atts( $atts );
212
213        $attachments = $this->get_attachments();
214        if ( empty( $attachments ) ) {
215            return '';
216        }
217
218        if ( is_feed() || defined( 'IS_HTML_EMAIL' ) ) {
219            return '';
220        }
221
222        /**
223         * Filters the permissible Tiled Gallery types.
224         *
225         * @module tiled-gallery
226         *
227         * @since 3.7.0
228         *
229         * @param array Array of allowed types. Default: 'rectangular', 'square', 'circle', 'rectangle', 'columns'.
230         */
231        $talaveras = apply_filters( 'jetpack_tiled_gallery_types', self::$talaveras );
232
233        if ( in_array( $this->atts['type'], $talaveras, true ) ) {
234            // Enqueue styles and scripts
235            self::default_scripts_and_styles();
236
237            // Generate gallery HTML
238            $gallery_class = 'Jetpack_Tiled_Gallery_Layout_' . ucfirst( $this->atts['type'] );
239            $gallery       = new $gallery_class( $attachments, $this->atts['link'], $this->atts['grayscale'], (int) $this->atts['columns'] );
240            $gallery_html  = $gallery->HTML();
241
242            if ( $gallery_html && class_exists( 'Jetpack' ) && class_exists( Image_CDN::class ) ) {
243                // Tiled Galleries in Jetpack require that Photon be active.
244                // If it's not active, run it just on the gallery output.
245                if ( ! Image_CDN::is_enabled() && ! ( new Status() )->is_offline_mode() ) {
246                    $gallery_html = Image_CDN::filter_the_content( $gallery_html );
247                }
248            }
249
250            return trim( preg_replace( '/\s+/', ' ', $gallery_html ) ); // remove any new lines from the output so that the reader parses it better
251        }
252
253        return '';
254    }
255
256    /**
257     * See if gallery is already defined.
258     *
259     * @return bool
260     */
261    public static function gallery_already_redefined() {
262        global $shortcode_tags;
263        $redefined = false;
264        if ( ! isset( $shortcode_tags['gallery'] ) || $shortcode_tags['gallery'] !== 'gallery_shortcode' ) {
265            $redefined = true;
266        }
267        /**
268         * Filter the output of the check for another plugin or theme affecting WordPress galleries.
269         *
270         * This will let folks that replace core’s shortcode confirm feature parity with it, so Jetpack's Tiled Galleries can still work.
271         *
272         * @module tiled-gallery
273         *
274         * @since 3.1.0
275         *
276         * @param bool $redefined Does another plugin or theme already redefines the default WordPress gallery?
277         */
278        return apply_filters( 'jetpack_tiled_gallery_shortcode_redefined', $redefined );
279    }
280
281    /**
282     * Initialize the tiled gallery.
283     */
284    public static function init() {
285        if ( self::gallery_already_redefined() ) {
286            return;
287        }
288
289        $gallery = new Jetpack_Tiled_Gallery();
290        add_filter( 'post_gallery', array( $gallery, 'gallery_shortcode' ), 1001, 2 );
291    }
292
293    /**
294     * Get the width of the gallery.
295     *
296     * @return int
297     */
298    public static function get_content_width() {
299        $tiled_gallery_content_width = Jetpack::get_content_width();
300
301        if ( ! $tiled_gallery_content_width ) {
302            $tiled_gallery_content_width = 500;
303        }
304
305        /**
306         * Filter overwriting the default content width.
307         *
308         * @module tiled-gallery
309         *
310         * @since 2.1.0
311         *
312         * @param string $tiled_gallery_content_width Default Tiled Gallery content width.
313         */
314        return apply_filters( 'tiled_gallery_content_width', $tiled_gallery_content_width );
315    }
316
317    /**
318     * Media UI integration
319     *
320     * @param array $types - the type of gallery.
321     *
322     * @return array
323     */
324    public function jetpack_gallery_types( $types ) {
325        if ( get_option( 'tiled_galleries' ) && isset( $types['default'] ) ) {
326            // Tiled is set as the default, meaning that type='default'
327            // will still display the mosaic.
328            $types['thumbnails'] = $types['default'];
329            unset( $types['default'] );
330        }
331
332        $types['rectangular'] = __( 'Tiled Mosaic', 'jetpack' );
333        $types['square']      = __( 'Square Tiles', 'jetpack' );
334        $types['circle']      = __( 'Circles', 'jetpack' );
335        $types['columns']     = __( 'Tiled Columns', 'jetpack' );
336
337        return $types;
338    }
339
340    /**
341     * Get the default gallery type.
342     *
343     * @return string
344     */
345    public function jetpack_default_gallery_type() {
346        return ( get_option( 'tiled_galleries' ) ? 'rectangular' : 'default' );
347    }
348
349    /**
350     * Get the talaveras.
351     *
352     * @return array
353     */
354    public static function get_talaveras() {
355        return self::$talaveras;
356    }
357
358    /**
359     * Add a checkbox field to the Carousel section in Settings > Media
360     * for setting tiled galleries as the default.
361     */
362    public function settings_api_init() {
363        global $wp_settings_sections;
364
365        // Add the setting field [tiled_galleries] and place it in Settings > Media
366        if ( isset( $wp_settings_sections['media']['carousel_section'] ) ) {
367            $section = 'carousel_section';
368        } else {
369            $section = 'default';
370        }
371
372        add_settings_field( 'tiled_galleries', __( 'Tiled Galleries', 'jetpack' ), array( $this, 'setting_html' ), 'media', $section );
373        register_setting( 'media', 'tiled_galleries', 'esc_attr' );
374    }
375
376    /**
377     * Render the settings HTML.
378     */
379    public function setting_html() {
380        echo '<label><input name="tiled_galleries" type="checkbox" value="1" ' .
381            checked( 1, '' !== get_option( 'tiled_galleries' ), false ) . ' /> ' .
382            esc_html__( 'Display all your gallery pictures in a cool mosaic.', 'jetpack' ) . '</br></label>';
383    }
384}
385
386add_action( 'init', array( 'Jetpack_Tiled_Gallery', 'init' ) );