Easy Image Gallery

Register CPT for image galleries with visual selector, flexible layouts, and shortcode display.

Display estimated delivery date
PHP
/**
 * Snippet Name:     Easy Image Gallery
 * Snippet Author:   coding-bunny.com
 * Description:      Register CPT for image galleries with visual selector, flexible layouts, and shortcode display.
 * Version:          1.0.0
 *
 * USAGE EXAMPLES:
 * 
 * Basic usage (show a gallery by its ID):
 * [custom_gallery id="123"]
 * 
 * Grid layout with 4 columns, 1:1 thumbs, large images, with lightbox and overlay:
 * [custom_gallery id="123" layout="grid" columns="4" thumb_ratio="1:1" size="large" lightbox="true" overlay="true"]
 * 
 * Justified layout, row height 200px, medium images, no overlay:
 * [custom_gallery id="123" layout="justified" rowheight="200" size="medium" overlay="false"]
 * 
 * AVAILABLE PARAMETERS:
 * - id:             ID of the gallery post (required)
 * - layout:         Gallery layout: 'grid' or 'justified' (default: 'grid')
 * - columns:        Number of columns (for grid layout, default: 3, min: 1, max: 6)
 * - size:           Image size: 'thumbnail', 'medium', 'large', 'full' (default: 'large')
 * - lightbox:       Enable lightbox popup on click (true/false, default: true)
 * - overlay:        Show hover overlay effect (true/false, default: true)
 * - showtitle:      Show image title on hover (true/false, default: false)
 * - thumb_ratio:    Thumbnail aspect ratio for grid: 'original', '1:1', '2:3', '3:2', '4:3', '3:4' (default: 'original')
 * - rowheight:      Row height for justified layout (default: 220)
 * - margins:        Margin (gap) between images in px (default: 8)
 * - borderradius:   Border radius for images in px (default: 0)
 * 
 */

if ( ! defined( 'ABSPATH' ) ) exit;

add_action('init', function() {
    register_post_type('cbscg_gallery', [
        'labels' => [
            'name' => __('Image Gallery', 'cbscg'),
            'singular_name' => __('Gallery', 'cbscg'),
            'add_new_item' => __('Add New Gallery', 'cbscg'),
            'edit_item' => __('Edit Gallery', 'cbscg'),
            'view_item' => __('View Gallery', 'cbscg'),
            'all_items' => __('All Galleries', 'cbscg'),
        ],
        'public' => true,
        'has_archive' => true,
        'rewrite' => ['slug' => 'galleries'],
        'menu_icon' => 'dashicons-format-gallery',
        'supports' => ['title'],
        'show_in_rest' => true,
        'capability_type' => 'post',
    ]);
});

add_action('add_meta_boxes', function() {
    add_meta_box(
        'cbscg_gallery_images',
        __('Gallery Images & Settings', 'cbscg'),
        function($post) {
            wp_nonce_field('cbscg_save_gallery', 'cbscg_gallery_nonce');
            $image_ids = get_post_meta($post->ID, '_cbscg_image_ids', true);
            $image_ids = is_array($image_ids) ? array_map('absint', $image_ids) : array_filter(array_map('absint', explode(',', (string) $image_ids)));
            $columns    = absint(get_post_meta($post->ID, '_cbscg_columns', true)) ?: 3;
            $layout     = sanitize_key(get_post_meta($post->ID, '_cbscg_layout', true)) ?: 'grid';
            $size       = sanitize_key(get_post_meta($post->ID, '_cbscg_size', true)) ?: 'large';
            $lightbox   = get_post_meta($post->ID, '_cbscg_lightbox', true);
            $lightbox   = ($lightbox === '' || $lightbox) ? 'true' : 'false';
            $overlay    = get_post_meta($post->ID, '_cbscg_overlay', true);
            $overlay    = ($overlay === '' || $overlay) ? 'true' : 'false';
            $showtitle  = get_post_meta($post->ID, '_cbscg_showtitle', true);
            $showtitle  = ($showtitle === '' || $showtitle) ? 'true' : 'false';
            $thumb_ratio = sanitize_text_field(get_post_meta($post->ID, '_cbscg_thumb_ratio', true)) ?: 'original';
            $rowheight  = absint(get_post_meta($post->ID, '_cbscg_rowheight', true)) ?: 220;
            $margins    = absint(get_post_meta($post->ID, '_cbscg_margins', true)) ?: 8;
            $borderradius = absint(get_post_meta($post->ID, '_cbscg_borderradius', true)) ?: 0;
            ?>
            <input type="hidden" id="cbscg-gallery-ids" name="cbscg_gallery_ids" value="<?php echo esc_attr(implode(',', $image_ids)); ?>">
            <button type="button" class="button" id="cbscg-select-images"><?php _e('Select Images', 'cbscg'); ?></button>
            <ul id="cbscg-preview">
                <?php foreach ($image_ids as $id): ?>
                    <li data-id="<?php echo esc_attr($id); ?>">
                        <?php echo wp_kses_post(wp_get_attachment_image($id, 'thumbnail')); ?>
                    </li>
                <?php endforeach; ?>
            </ul>
            <hr>
            <table class="form-table" style="margin-top:1em;">
                <tr>
                    <th><label for="cbscg_layout"><?php _e('Layout', 'cbscg'); ?></label></th>
                    <td>
                        <select name="cbscg_layout" id="cbscg_layout">
                            <option value="grid" <?php selected($layout, 'grid'); ?>>Grid</option>
                            <option value="justified" <?php selected($layout, 'justified'); ?>>Justified</option>
                        </select>
                    </td>
                </tr>
                <tr id="cbscg_columns_row">
                    <th><label for="cbscg_columns"><?php _e('Columns', 'cbscg'); ?></label></th>
                    <td>
                        <select name="cbscg_columns" id="cbscg_columns">
                            <?php for($i=1;$i<=6;$i++) echo "<option value=\"$i\" ".selected($columns,$i,false).">$i</option>"; ?>
                        </select>
                    </td>
                </tr>
                <tr id="cbscg_thumb_ratio_row">
                    <th><label for="cbscg_thumb_ratio"><?php _e('Thumb Ratio', 'cbscg'); ?></label></th>
                    <td>
                        <select name="cbscg_thumb_ratio" id="cbscg_thumb_ratio">
                            <option value="original" <?php selected($thumb_ratio,'original'); ?>>Original</option>
                            <option value="1:1" <?php selected($thumb_ratio,'1:1'); ?>>1:1</option>
                            <option value="2:3" <?php selected($thumb_ratio,'2:3'); ?>>2:3</option>
                            <option value="3:2" <?php selected($thumb_ratio,'3:2'); ?>>3:2</option>
                            <option value="4:3" <?php selected($thumb_ratio,'4:3'); ?>>4:3</option>
                            <option value="3:4" <?php selected($thumb_ratio,'3:4'); ?>>3:4</option>
                        </select>
                    </td>
                </tr>
                <tr id="cbscg_rowheight_row">
                    <th><label for="cbscg_rowheight"><?php _e('Justified Row Height', 'cbscg'); ?></label></th>
                    <td>
                        <input type="number" name="cbscg_rowheight" id="cbscg_rowheight" value="<?php echo esc_attr($rowheight); ?>" style="width:60px;"> px
                    </td>
                </tr>
                <tr id="cbscg_margins_row">
                    <th><label for="cbscg_margins"><?php _e('Gallery Margins', 'cbscg'); ?></label></th>
                    <td>
                        <input type="number" name="cbscg_margins" id="cbscg_margins" value="<?php echo esc_attr($margins); ?>" style="width:60px;"> px
                    </td>
                </tr>
                <tr>
                    <th><label for="cbscg_borderradius"><?php _e('Border Radius', 'cbscg'); ?></label></th>
                    <td>
                        <input type="number" name="cbscg_borderradius" id="cbscg_borderradius" min="0" max="50" value="<?php echo esc_attr($borderradius); ?>" style="width:60px;"> px
                    </td>
                </tr>
                <tr>
                    <th><label for="cbscg_size"><?php _e('Image Size', 'cbscg'); ?></label></th>
                    <td>
                        <select name="cbscg_size" id="cbscg_size">
                            <option value="thumbnail" <?php selected($size,'thumbnail'); ?>>Thumbnail</option>
                            <option value="medium" <?php selected($size,'medium'); ?>>Medium</option>
                            <option value="large" <?php selected($size,'large'); ?>>Large</option>
                            <option value="full" <?php selected($size,'full'); ?>>Full</option>
                        </select>
                    </td>
                </tr>
                <tr>
                    <th><label for="cbscg_lightbox"><?php _e('Lightbox', 'cbscg'); ?></label></th>
                    <td>
                        <input type="checkbox" name="cbscg_lightbox" id="cbscg_lightbox" value="1" <?php checked($lightbox,'true'); ?>>
                    </td>
                </tr>
                <tr>
                    <th><label for="cbscg_overlay"><?php _e('Overlay hover effect', 'cbscg'); ?></label></th>
                    <td>
                        <input type="checkbox" name="cbscg_overlay" id="cbscg_overlay" value="1" <?php checked($overlay,'true'); ?>>
                    </td>
                </tr>
                <tr>
                    <th><label for="cbscg_showtitle"><?php _e('Show Image Title', 'cbscg'); ?></label></th>
                    <td>
                        <input type="checkbox" name="cbscg_showtitle" id="cbscg_showtitle" value="1" <?php checked($showtitle,'true'); ?>>
                    </td>
                </tr>
            </table>
            <hr>
            <label style="font-weight:bold;"><?php _e('Shortcode ready to copy:', 'cbscg'); ?></label>
            <input type="text" id="cbscg-shortcode-display" readonly style="width:99%;font-family:monospace;background:#f7f7f7;border:1px solid #e5e5e5;" onclick="this.select()">
            <script>
                function cbscg_generate_shortcode() {
                    var id = "<?php echo intval($post->ID); ?>";
                    var columns = document.getElementById('cbscg_columns').value;
                    var layout = document.getElementById('cbscg_layout').value;
                    var size = document.getElementById('cbscg_size').value;
                    var lightbox = document.getElementById('cbscg_lightbox').checked ? "true" : "false";
                    var overlay = document.getElementById('cbscg_overlay').checked ? "true" : "false";
                    var showtitle = document.getElementById('cbscg_showtitle').checked ? "true" : "false";
                    var thumb_ratio = document.getElementById('cbscg_thumb_ratio').value;
                    var rowheight = document.getElementById('cbscg_rowheight').value;
                    var margins = document.getElementById('cbscg_margins').value;
                    var borderradius = document.getElementById('cbscg_borderradius').value;

                    var sc = '[custom_gallery id="'+id+'" layout="'+layout+'" size="'+size+'" lightbox="'+lightbox+'" overlay="'+overlay+'" showtitle="'+showtitle+'" borderradius="'+borderradius+'"';
                    if(layout === 'grid') {
                        sc += ' columns="'+columns+'" thumb_ratio="'+thumb_ratio+'" margins="'+margins+'"';
                    }
                    if(layout === 'justified') {
                        sc += ' rowheight="'+rowheight+'" margins="'+margins+'"';
                    }
                    sc += ']';
                    document.getElementById('cbscg-shortcode-display').value = sc;
                }

                function cbscg_toggle_fields() {
                    var layout = document.getElementById('cbscg_layout').value;
                    if(layout === 'grid') {
                        document.getElementById('cbscg_columns_row').style.display = '';
                        document.getElementById('cbscg_thumb_ratio_row').style.display = '';
                        document.getElementById('cbscg_rowheight_row').style.display = 'none';
                        document.getElementById('cbscg_margins_row').style.display = '';
                    } else {
                        document.getElementById('cbscg_columns_row').style.display = 'none';
                        document.getElementById('cbscg_thumb_ratio_row').style.display = 'none';
                        document.getElementById('cbscg_rowheight_row').style.display = '';
                        document.getElementById('cbscg_margins_row').style.display = '';
                    }
                }

                jQuery(function($){
                    cbscg_toggle_fields();
                    cbscg_generate_shortcode();
                    $('#cbscg_columns,#cbscg_layout,#cbscg_size,#cbscg_lightbox,#cbscg_overlay,#cbscg_showtitle,#cbscg_thumb_ratio,#cbscg_rowheight,#cbscg_margins,#cbscg_borderradius').on('change input', function() {
                        cbscg_toggle_fields();
                        cbscg_generate_shortcode();
                    });
                });
            </script>
            <style>
                #cbscg-preview { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; cursor: move; }
                #cbscg-preview li { list-style: none; border: 1px solid #ccc; border-radius: 4px; }
                #cbscg-preview li img { max-width: 100px; max-height: 100px; }
                #cbscg-preview li.selected { outline: 2px solid #007cba; }
            </style>
            <script>
            jQuery(document).ready(function($) {
                let frame;
                $('#cbscg-select-images').on('click', function(e) {
                    e.preventDefault();
                    let selected_ids = $('#cbscg-gallery-ids').val()
                        ? $('#cbscg-gallery-ids').val().split(',').map(id => parseInt(id)).filter(id => id)
                        : [];
                    if (!frame) {
                        frame = wp.media({
                            multiple: true,
                            title: '<?php echo esc_js(__('Select Images', 'cbscg')); ?>',
                            library: { type: 'image' }
                        });
                    }
                    frame.on('open', function() {
                        let selection = frame.state().get('selection');
                        selection.reset();
                        if (selected_ids.length > 0) {
                            selected_ids.forEach(function(id) {
                                let attachment = wp.media.attachment(id);
                                attachment.fetch();
                                selection.add(attachment ? attachment : null);
                            });
                        }
                    });
                    frame.off('select');
                    frame.on('select', function() {
                        const selection = frame.state().get('selection');
                        const ids = selection.map(att => att.id);
                        $('#cbscg-gallery-ids').val(ids.join(','));
                        $('#cbscg-preview').empty();
                        selection.each(att => {
                            const thumb = att.attributes.sizes?.thumbnail?.url || att.attributes.icon;
                            $('#cbscg-preview').append(`<li data-id="${att.id}"><img src="${thumb}" /></li>`);
                        });
                    });
                    frame.open();
                });
                $('#cbscg-preview').on('click', 'li', function(e){
                    if (e.ctrlKey || e.metaKey) {
                        $(this).toggleClass('selected');
                    } else if (e.shiftKey) {
                        var $items = $(this).parent().children();
                        var first = $items.index($(this).parent().find('.selected').first());
                        var last = $items.index(this);
                        $items.removeClass('selected');
                        $items.slice(Math.min(first, last), Math.max(first, last) + 1).addClass('selected');
                    } else {
                        $(this).addClass('selected').siblings().removeClass('selected');
                    }
                });
                $(document).on('keydown', function(e) {
                    if ((e.key === "Delete" || e.key === "Backspace") && $('#cbscg-preview li.selected').length) {
                        $('#cbscg-preview li.selected').remove();
                        updateIds();
                    }
                });
                $('#cbscg-preview').on('dblclick', 'li.selected', function() {
                    $(this).remove();
                    updateIds();
                });
                $('#cbscg-preview').sortable({
                    update: updateIds
                });
                function updateIds() {
                    const newOrder = $('#cbscg-preview').children().map(function() {
                        return $(this).data('id');
                    }).get();
                    $('#cbscg-gallery-ids').val(newOrder.join(','));
                }
            });
            </script>
            <?php
        },
        'cbscg_gallery',
        'normal',
        'high'
    );
});

add_action('save_post_cbscg_gallery', function($post_id) {
    if ( (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || !isset($_POST['cbscg_gallery_nonce']) ) return;
    if ( !wp_verify_nonce($_POST['cbscg_gallery_nonce'], 'cbscg_save_gallery') ) return;
    if ( !current_user_can('edit_post', $post_id) ) return;

    if ( isset($_POST['cbscg_gallery_ids']) ) {
        $ids = array_filter(array_map('absint', explode(',', sanitize_text_field($_POST['cbscg_gallery_ids']))));
        update_post_meta($post_id, '_cbscg_image_ids', $ids);
    }
    update_post_meta($post_id, '_cbscg_columns',     isset($_POST['cbscg_columns']) ? absint($_POST['cbscg_columns']) : 3);
    update_post_meta($post_id, '_cbscg_layout',      isset($_POST['cbscg_layout']) ? sanitize_key($_POST['cbscg_layout']) : 'grid');
    update_post_meta($post_id, '_cbscg_size',        isset($_POST['cbscg_size']) ? sanitize_key($_POST['cbscg_size']) : 'large');
    update_post_meta($post_id, '_cbscg_lightbox',    !empty($_POST['cbscg_lightbox']) ? 1 : 0);
    update_post_meta($post_id, '_cbscg_overlay',     !empty($_POST['cbscg_overlay']) ? 1 : 0);
    update_post_meta($post_id, '_cbscg_showtitle',   !empty($_POST['cbscg_showtitle']) ? 1 : 0);
    update_post_meta($post_id, '_cbscg_thumb_ratio', isset($_POST['cbscg_thumb_ratio']) ? sanitize_text_field($_POST['cbscg_thumb_ratio']) : 'original');
    update_post_meta($post_id, '_cbscg_rowheight',   isset($_POST['cbscg_rowheight']) ? absint($_POST['cbscg_rowheight']) : 220);
    update_post_meta($post_id, '_cbscg_margins',     isset($_POST['cbscg_margins']) ? absint($_POST['cbscg_margins']) : 8);
    update_post_meta($post_id, '_cbscg_borderradius', isset($_POST['cbscg_borderradius']) ? absint($_POST['cbscg_borderradius']) : 0);
});

add_filter('manage_cbscg_gallery_posts_columns', function($columns) {
    $columns['cbscg_shortcode'] = __('Shortcode', 'cbscg');
    return $columns;
});
add_action('manage_cbscg_gallery_posts_custom_column', function($column, $post_id) {
    if ($column === 'cbscg_shortcode') {
        $columns    = absint(get_post_meta($post_id, '_cbscg_columns', true)) ?: 3;
        $layout     = sanitize_key(get_post_meta($post_id, '_cbscg_layout', true)) ?: 'grid';
        $size       = sanitize_key(get_post_meta($post_id, '_cbscg_size', true)) ?: 'large';
        $lightbox   = get_post_meta($post_id, '_cbscg_lightbox', true);
        $lightbox   = ($lightbox === '' || $lightbox) ? 'true' : 'false';
        $overlay    = get_post_meta($post_id, '_cbscg_overlay', true);
        $overlay    = ($overlay === '' || $overlay) ? 'true' : 'false';
        $showtitle  = get_post_meta($post_id, '_cbscg_showtitle', true);
        $showtitle  = ($showtitle === '' || $showtitle) ? 'true' : 'false';
        $thumb_ratio = sanitize_text_field(get_post_meta($post_id, '_cbscg_thumb_ratio', true)) ?: 'original';
        $rowheight  = absint(get_post_meta($post_id, '_cbscg_rowheight', true)) ?: 220;
        $margins    = absint(get_post_meta($post_id, '_cbscg_margins', true)) ?: 8;
        $borderradius = absint(get_post_meta($post_id, '_cbscg_borderradius', true)) ?: 0;
        $shortcode = '[custom_gallery id="' . absint($post_id) . '" layout="' . esc_attr($layout) . '" size="' . esc_attr($size) . '" lightbox="' . esc_attr($lightbox) . '" overlay="' . esc_attr($overlay) . '" showtitle="' . esc_attr($showtitle) . '" borderradius="' . esc_attr($borderradius) . '"';
        if ($layout === 'grid') {
            $shortcode .= ' columns="' . esc_attr($columns) . '" thumb_ratio="' . esc_attr($thumb_ratio) . '" margins="' . esc_attr($margins) . '"';
        }
        if ($layout === 'justified') {
            $shortcode .= ' rowheight="' . esc_attr($rowheight) . '" margins="' . esc_attr($margins) . '"';
        }
        $shortcode .= ']';
        printf(
            '<input type="text" readonly value="%s" style="width:99%%;font-family:monospace;background:#f7f7f7;border:1px solid #e5e5e5;" onclick="this.select()">',
            esc_attr($shortcode)
        );
    }
}, 10, 2);

add_action('wp_footer', function () {
    ?>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/justifiedGallery@3.8.1/dist/css/justifiedGallery.min.css" crossorigin="anonymous" referrerpolicy="no-referrer">
    <style>
        .cbscg-lightbox {
            position: fixed; inset: 0;
            background: rgba(0,0,0,0.85);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: 9999;
        }
        .cbscg-lightbox[aria-hidden="false"] { display: flex; }
        .cbscg-lightbox img {
            max-width: 90vw;
            max-height: 90vh;
            border-radius: 6px;
            box-shadow: 0 0 20px #0008;
        }
        .cbscg-lightbox .cbscg-lightbox-close {
            position: absolute;
            top: 24px; right: 24px;
            background: none; border: none; color: #fff;
            font-size: 2rem; cursor: pointer; z-index: 10001;
        }
    </style>
    <div class="cbscg-lightbox" aria-modal="true" aria-hidden="true" tabindex="-1" role="dialog">
        <button class="cbscg-lightbox-close" aria-label="Close">×</button>
        <img src="" alt="">
    </div>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.8.1/dist/js/jquery.justifiedGallery.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script>
    document.addEventListener('DOMContentLoaded', function () {
        if (window._cbscgLightboxLoaded) return;
        window._cbscgLightboxLoaded = true;
        const overlay = document.querySelector('.cbscg-lightbox');
        const img = overlay.querySelector('img');
        const closeBtn = overlay.querySelector('.cbscg-lightbox-close');
        let lastTrigger = null;
        document.body.addEventListener('click', function (e) {
            const link = e.target.closest('.cbscg-lightbox-link');
            if (link) {
                e.preventDefault();
                img.src = link.getAttribute('href');
                img.alt = link.querySelector('img')?.alt || '';
                overlay.setAttribute('aria-hidden', 'false');
                overlay.focus();
                lastTrigger = link;
            }
        });
        function closeLightbox() {
            overlay.setAttribute('aria-hidden', 'true');
            img.src = '';
            if (lastTrigger) { lastTrigger.focus(); }
        }
        overlay.addEventListener('click', function(e) {
            if (e.target === overlay) closeLightbox();
        });
        closeBtn.addEventListener('click', closeLightbox);
        document.addEventListener('keydown', function(e) {
            if (overlay.getAttribute('aria-hidden') === 'false' && (e.key === 'Escape' || e.key === 'Esc')) {
                closeLightbox();
            }
        });
    });
    </script>
    <?php
}, 5);

add_shortcode('custom_gallery', function($atts) {
    static $count = 0;
    $count++;
    $defaults = [
        'id' => 0,
        'layout' => 'grid',
        'columns' => 3,
        'size' => 'large',
        'lightbox' => 'true',
        'overlay' => 'true',
        'rowheight' => 220,
        'margins' => 8,
        'showtitle' => 'false',
        'thumb_ratio' => 'original',
        'borderradius' => 0,
    ];
    $atts = shortcode_atts($defaults, $atts, 'custom_gallery');
    $gallery_id = absint($atts['id']);
    $layout = in_array($atts['layout'], ['grid', 'justified'], true) ? $atts['layout'] : 'grid';
    $columns = max(1, min(6, absint($atts['columns'])));
    $size = in_array($atts['size'], ['thumbnail','medium','large','full'], true) ? $atts['size'] : 'large';
    $lightbox = filter_var($atts['lightbox'], FILTER_VALIDATE_BOOLEAN);
    $overlay = filter_var($atts['overlay'], FILTER_VALIDATE_BOOLEAN);
    $rowheight = absint($atts['rowheight']);
    $margins = absint($atts['margins']);
    $showtitle = filter_var($atts['showtitle'], FILTER_VALIDATE_BOOLEAN);
    $thumb_ratio = strtolower(trim($atts['thumb_ratio']));
    $unique_id = 'cbscg-gallery-' . $count;
    $borderradius = absint($atts['borderradius']);
    $ratio_map = [
        '1:1'   => [1, 1],
        '2:3'   => [2, 3],
        '3:2'   => [3, 2],
        '4:3'   => [4, 3],
        '3:4'   => [3, 4],
    ];
    $thumb_ratio_clean = str_replace(' ', '', $thumb_ratio);
    $padding_pct = '';
    if ($layout === 'grid' && $thumb_ratio_clean !== 'original' && isset($ratio_map[$thumb_ratio_clean])) {
        list($w, $h) = $ratio_map[$thumb_ratio_clean];
        $padding_pct = (floatval($h) / floatval($w)) * 100 . '%';
    }
    $ids = get_post_meta($gallery_id, '_cbscg_image_ids', true);
    $ids = is_array($ids) ? array_map('absint', $ids) : array_filter(array_map('absint', explode(',', (string) $ids)));
    if (empty($ids)) {
        return '<p>' . esc_html__('No images found in gallery.', 'cbscg') . '</p>';
    }

    if ($layout === 'justified') {
        add_action('wp_footer', function () use ($unique_id, $rowheight, $margins, $overlay, $lightbox, $showtitle, $borderradius) {
            ?>
            <script>
            jQuery(function($){
                $('#<?php echo esc_js($unique_id); ?>').justifiedGallery({
                    rowHeight: <?php echo (int)$rowheight; ?>,
                    margins: <?php echo (int)$margins; ?>,
                    captions: <?php echo $showtitle ? 'true' : 'false'; ?>,
                    lastRow: 'justify'
                });
            });
            </script>
            <style>
                .jg-caption {
                display:none !important;
                }
                #<?php echo esc_attr($unique_id); ?> a img {
                    border-radius: <?php echo (int)$borderradius; ?>px;
                    box-shadow: 0 1px 6px #0001;
                    <?php if ($lightbox): ?>cursor: zoom-in;<?php endif; ?>
                    transition: filter 0.3s;
                    display: block;
                    width: 100%;
                    height: auto;
                }
                <?php if ($overlay): ?>
                #<?php echo esc_attr($unique_id); ?> a:hover img,
                #<?php echo esc_attr($unique_id); ?> a:focus img {
                    filter: brightness(50%);
                }
                <?php endif; ?>
                <?php if ($showtitle): ?>
                #<?php echo esc_attr($unique_id); ?> .caption {
                    background: none !important;
                    color: #fff !important;
                    font-size: 1.2em !important;
                    font-weight: bold !important;
                    text-align: center !important;
                    white-space: pre-line !important;
                    left: 50% !important;
                    top: 50% !important;
                    transform: translate(-50%,-50%) !important;
                    position: absolute !important;
                    width: max-content !important;
                    min-width: 40% !important;
                    max-width: 90% !important;
                    opacity: 0;
                    pointer-events: none;
                    z-index: 2;
                    text-shadow: 0 0 6px #000, 0 0 1px #000;
                    transition: opacity 0.2s;
                }
                #<?php echo esc_attr($unique_id); ?> a:hover .caption,
                #<?php echo esc_attr($unique_id); ?> a:focus .caption {
                    opacity: 1 !important;
                }
                <?php endif; ?>
            </style>
            <?php
        }, 20);
        ob_start();
        echo '<div id="' . esc_attr($unique_id) . '" class="cbscg-justified-gallery">';
        foreach ($ids as $id) {
            $thumb = wp_get_attachment_image_url($id, $size);
            $full  = wp_get_attachment_image_url($id, 'full');
            $title = get_the_title($id);
            if (!$thumb || !$full) continue;
            $title_attr = esc_attr($title);
            $img_tag = '<img src="' . esc_url($thumb) . '" alt="' . $title_attr . '" loading="lazy">';
            echo '<a href="' . esc_url($full) . '" class="cbscg-lightbox-link" title="' . $title_attr . '">';
            echo $img_tag;
            if ($showtitle) {
                echo '<div class="caption">' . esc_html($title) . '</div>';
            }
            echo '</a>';
        }
        echo '</div>';
        return ob_get_clean();
    } else {
        add_action('wp_footer', function () use ($columns, $unique_id, $overlay, $lightbox, $showtitle, $thumb_ratio_clean, $padding_pct, $borderradius, $margins) {
            ?>
            <style>
                #<?php echo esc_attr($unique_id); ?> {
                    display: grid;
                    gap: <?php echo (int)$margins; ?>px;
                    grid-template-columns: repeat(<?php echo esc_attr($columns); ?>, 1fr);
                    width: 100%;
                }
                #<?php echo esc_attr($unique_id); ?> .gallery-item img {
                    width: 100%;
                    height: 100%;
                    object-fit: cover;
                    border-radius: <?php echo (int)$borderradius; ?>px;
                    <?php if ($lightbox): ?>cursor: zoom-in;<?php endif; ?>
                    box-shadow: 0 1px 6px #0001;
                    transition: filter 0.3s;
                    display: block;
                    position: absolute;
                    left: 0; top: 0;
                }
                .cbscg-thumb-wrap {
                    position: relative;
                    width: 100%;
                    <?php if ($thumb_ratio_clean !== 'original') : ?>
                    height: 0;
                    padding-top: <?php echo $padding_pct; ?>;
                    <?php endif; ?>
                    overflow: hidden;
                }
                <?php if ($thumb_ratio_clean === 'original'): ?>
                .cbscg-thumb-wrap img {
                    position: static;
                    height: auto;
                }
                <?php endif; ?>
                .cbscg-thumb-wrap img {
                    transition: filter 0.3s ease;
                }
                <?php if ($overlay): ?>
                .cbscg-thumb-wrap:hover img,
                .cbscg-thumb-wrap:focus img {
                    filter: brightness(50%);
                }
                <?php endif; ?>
                <?php if ($showtitle): ?>
                .cbscg-title-center {
                    position: absolute;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    color: #fff;
                    font-size: 1.2em;
                    font-weight: bold;
                    text-align: center;
                    white-space: pre-line;
                    pointer-events: none;
                    opacity: 0;
                    transition: opacity 0.2s;
                    z-index: 2;
                    text-shadow: 0 0 6px #000, 0 0 1px #000;
                }
                .cbscg-thumb-wrap:hover .cbscg-title-center,
                .cbscg-thumb-wrap:focus .cbscg-title-center {
                    opacity: 1;
                }
                <?php endif; ?>
                @media (max-width: 900px) {
                    #<?php echo esc_attr($unique_id); ?> {
                        grid-template-columns: repeat(2, 1fr);
                    }
                }
                @media (max-width: 500px) {
                    #<?php echo esc_attr($unique_id); ?> {
                        grid-template-columns: 1fr;
                    }
                }
            </style>
            <?php
        }, 20);
        ob_start();
        echo '<div id="' . esc_attr($unique_id) . '" class="cbscg-gallery layout-grid">';
        foreach ($ids as $id) {
            $thumb = wp_get_attachment_image_url($id, $size);
            $full  = wp_get_attachment_image_url($id, 'full');
            $title = get_the_title($id);
            if (!$thumb || !$full) continue;
            $title_attr = esc_attr($title);
            echo '<div class="gallery-item">';
            if ($lightbox) echo '<a href="' . esc_url($full) . '" class="cbscg-lightbox-link" tabindex="0" aria-label="' . $title_attr . '">';
            echo '<div class="cbscg-thumb-wrap">';
            echo '<img src="' . esc_url($thumb) . '" loading="lazy" alt="' . $title_attr . '">';
            if ($showtitle) {
                echo '<span class="cbscg-title-center">' . esc_html($title) . '</span>';
            }
            echo '</div>';
            if ($lightbox) echo '</a>';
            echo '</div>';
        }
        echo '</div>';
        return ob_get_clean();
    }
});

How To Implement This Solution?

Leave a Reply