最新版image_collection模块带用法

更新了可以拖拽排序,带分页输出,添加缩略图和ID和图片数量到管理列

<?php

/**
 * Image Collection Custom Post Type with Sortable Gallery Meta Box
 * Paste this code into your theme's functions.php file
 */

// Register Custom Post Type
function register_image_collection_post_type()
{
    $labels = array(
        'name' => __('Image Collections', 'bearing'),
        'singular_name' => __('Image Collection', 'bearing'),
        'menu_name' => __('Image Collections', 'bearing'),
        'add_new' => __('Add New', 'bearing'),
        'add_new_item' => __('Add New Image Collection', 'bearing'),
        'edit_item' => __('Edit Image Collection', 'bearing'),
        'new_item' => __('New Image Collection', 'bearing'),
        'view_item' => __('View Image Collection', 'bearing'),
        'search_items' => __('Search Image Collections', 'bearing'),
        'not_found' => __('No image collections found', 'bearing'),
        'not_found_in_trash' => __('No image collections found in Trash', 'bearing'),
    );

    $args = array(
        'labels' => $labels,
        'public' => true,
        'has_archive' => true,
        'publicly_queryable' => false,
        'show_ui' => true,
        'show_in_menu' => true,
        'query_var' => true,
        'rewrite' => array('slug' => 'image-collection'),
        'capability_type' => 'post',
        'has_archive' => false,
        'hierarchical' => false,
        'menu_position' => 5,
        'menu_icon' => 'dashicons-format-gallery',
        'supports' => array('title', 'editor', 'thumbnail'),
    );

    register_post_type('image_collection', $args);
}
add_action('init', 'register_image_collection_post_type');

// Add Gallery Meta Box
function add_gallery_meta_box()
{
    add_meta_box(
        'image_collection_gallery',
        __('Gallery Images', 'bearing'),
        'render_gallery_meta_box',
        'image_collection',
        'normal',
        'high'
    );
}
add_action('add_meta_boxes', 'add_gallery_meta_box');

// Render Gallery Meta Box
function render_gallery_meta_box($post)
{
    wp_nonce_field('save_gallery_meta', 'gallery_meta_nonce');

    $gallery_images = get_post_meta($post->ID, '_gallery_images', true);
    $gallery_images = !empty($gallery_images) ? $gallery_images : array();

    // Pagination settings
    $per_page = 12;
    $current_page = isset($_GET['gallery_page']) ? max(1, intval($_GET['gallery_page'])) : 1;
    $total_images = count($gallery_images);
    $total_pages = ceil($total_images / $per_page);
    $offset = ($current_page - 1) * $per_page;
    $paginated_images = array_slice($gallery_images, $offset, $per_page, true);
    ?>

    <div class="gallery-meta-box">
        <style>
            .gallery-meta-box {
                padding: 10px 0;
            }

            .gallery-images-container {
                display: grid;
                grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
                gap: 15px;
                margin: 20px 0;
                min-height: 100px;
            }

            .gallery-image-item {
                position: relative;
                border: 1px solid #ddd;
                border-radius: 4px;
                overflow: hidden;
                background: #f9f9f9;
                cursor: move;
                transition: all 0.2s ease;
            }

            .gallery-image-item:hover {
                border-color: #0073aa;
                box-shadow: 0 2px 8px rgba(0, 115, 170, 0.2);
            }

            .gallery-image-item.ui-sortable-helper {
                opacity: 0.8;
                transform: rotate(2deg);
                box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
                z-index: 1000;
            }

            .gallery-image-item.ui-sortable-placeholder {
                border: 2px dashed #0073aa;
                background: #e5f5fa;
                visibility: visible !important;
            }

            .gallery-image-item img {
                width: 100%;
                height: 150px;
                object-fit: cover;
                display: block;
                pointer-events: none;
            }

            .gallery-image-actions {
                position: absolute;
                top: 5px;
                right: 5px;
                display: flex;
                gap: 5px;
                opacity: 0;
                transition: opacity 0.2s ease;
            }

            .gallery-image-item:hover .gallery-image-actions {
                opacity: 1;
            }

            .gallery-image-actions button {
                background: rgba(0, 0, 0, 0.7);
                color: white;
                border: none;
                border-radius: 3px;
                padding: 5px 8px;
                cursor: pointer;
                font-size: 12px;
            }

            .gallery-image-actions button:hover {
                background: rgba(0, 0, 0, 0.9);
            }

            .drag-handle {
                position: absolute;
                top: 5px;
                left: 5px;
                background: rgba(0, 115, 170, 0.9);
                color: white;
                padding: 5px 8px;
                border-radius: 3px;
                font-size: 12px;
                cursor: move;
                opacity: 0;
                transition: opacity 0.2s ease;
            }

            .gallery-image-item:hover .drag-handle {
                opacity: 1;
            }

            .gallery-add-button {
                display: inline-block;
                margin-bottom: 15px;
            }

            .gallery-pagination {
                margin-top: 20px;
                padding-top: 15px;
                border-top: 1px solid #ddd;
                display: flex;
                align-items: center;
                gap: 10px;
            }

            .gallery-pagination button {
                padding: 5px 10px;
            }

            .gallery-pagination span {
                color: #666;
            }

            .no-images-message {
                padding: 40px;
                text-align: center;
                color: #666;
                background: #f9f9f9;
                border: 2px dashed #ddd;
                border-radius: 4px;
            }

            .gallery-controls {
                display: flex;
                gap: 10px;
                margin-bottom: 15px;
                align-items: center;
            }

            .sort-notice {
                display: none;
                background: #d4edda;
                border: 1px solid #c3e6cb;
                color: #155724;
                padding: 8px 12px;
                border-radius: 4px;
                font-size: 13px;
            }

            .sort-notice.active {
                display: inline-block;
            }
        </style>

        <div class="gallery-controls">
            <button type="button" class="button button-primary gallery-add-button" id="add-gallery-images">
                <?php esc_html_e('Add Images', 'bearing'); ?>
            </button>
            <span class="sort-notice" id="sort-notice">
                ✓ <?php esc_html_e('Order changed - Save post to keep changes', 'bearing'); ?>
            </span>
        </div>

        <input type="hidden" id="gallery-images-data" name="gallery_images"
            value="<?php echo esc_attr(json_encode($gallery_images)); ?>">

        <div class="gallery-images-container" id="gallery-images-container">
            <?php if (empty($paginated_images)): ?>
                <div class="no-images-message">
                    <?php esc_html_e('No images added yet. Click "Add Images" to get started.', 'bearing'); ?>
                </div>
            <?php else: ?>
                <?php foreach ($paginated_images as $index => $image_id):
                    $image_url = wp_get_attachment_image_url($image_id, 'medium');
                    if (!$image_url)
                        continue;
                    ?>
                    <div class="gallery-image-item" data-image-id="<?php echo esc_attr($image_id); ?>"
                        data-index="<?php echo esc_attr($index); ?>">
                        <span class="drag-handle">⋮⋮</span>
                        <img src="<?php echo esc_url($image_url); ?>" alt="">
                        <div class="gallery-image-actions">
                            <button type="button" class="edit-gallery-image"
                                data-image-id="<?php echo esc_attr($image_id); ?>"><?php esc_html_e('Edit', 'bearing'); ?></button>
                            <button type="button" class="delete-gallery-image"
                                data-image-id="<?php echo esc_attr($image_id); ?>"><?php esc_html_e('Delete', 'bearing'); ?></button>
                        </div>
                    </div>
                <?php endforeach; ?>
            <?php endif; ?>
        </div>

        <?php if ($total_pages > 1): ?>
            <div class="gallery-pagination">
                <button type="button" class="button gallery-prev-page" <?php echo $current_page <= 1 ? 'disabled' : ''; ?>><?php esc_html_e('Previous', 'bearing'); ?></button>
                <span><?php echo sprintf(esc_html__('Page %d of %d (%d images total)', 'bearing'), $current_page, $total_pages, $total_images); ?></span>
                <button type="button" class="button gallery-next-page" <?php echo $current_page >= $total_pages ? 'disabled' : ''; ?>><?php esc_html_e('Next', 'bearing'); ?></button>
                <input type="hidden" id="gallery-current-page" value="<?php echo $current_page; ?>">
                <input type="hidden" id="gallery-total-pages" value="<?php echo $total_pages; ?>">
            </div>
        <?php endif; ?>

        <script type="text/javascript">
            jQuery(document).ready(function ($) {
                var galleryData = [];
                var currentPage = parseInt($('#gallery-current-page').val()) || 1;
                var perPage = 12;

                // Initialize gallery data
                try {
                    galleryData = JSON.parse($('#gallery-images-data').val()) || [];
                } catch (e) {
                    galleryData = [];
                }

                // Initialize sortable
                function initSortable() {
                    $('#gallery-images-container').sortable({
                        items: '.gallery-image-item',
                        cursor: 'move',
                        opacity: 0.8,
                        placeholder: 'ui-sortable-placeholder',
                        tolerance: 'pointer',
                        update: function (event, ui) {
                            // Get new order from current page
                            var newPageOrder = [];
                            $('#gallery-images-container .gallery-image-item').each(function () {
                                newPageOrder.push(parseInt($(this).data('image-id')));
                            });

                            // Calculate the offset for current page
                            var offset = (currentPage - 1) * perPage;

                            // Create new complete array with reordered page
                            var newGalleryData = [];

                            // Add items before current page
                            for (var i = 0; i < offset; i++) {
                                if (galleryData[i]) {
                                    newGalleryData.push(galleryData[i]);
                                }
                            }

                            // Add reordered items from current page
                            newPageOrder.forEach(function (id) {
                                newGalleryData.push(id);
                            });

                            // Add items after current page
                            for (var i = offset + perPage; i < galleryData.length; i++) {
                                newGalleryData.push(galleryData[i]);
                            }

                            galleryData = newGalleryData;
                            updateGalleryData();

                            // Show save notice
                            $('#sort-notice').addClass('active');
                        }
                    });
                }

                // Initialize on load if there are images
                if (galleryData.length > 0) {
                    initSortable();
                }

                // Add images
                $('#add-gallery-images').on('click', function (e) {
                    e.preventDefault();

                    var frame = wp.media({
                        title: '<?php echo esc_js(__('Select or Upload Images', 'bearing')); ?>',
                        button: {
                            text: '<?php echo esc_js(__('Add to Gallery', 'bearing')); ?>'
                        },
                        multiple: true
                    });

                    frame.on('select', function () {
                        var attachments = frame.state().get('selection').toJSON();

                        attachments.forEach(function (attachment) {
                            if (!galleryData.includes(attachment.id)) {
                                galleryData.push(attachment.id);
                            }
                        });

                        updateGalleryData();
                        renderGallery();
                    });

                    frame.open();
                });

                // Edit image
                $(document).on('click', '.edit-gallery-image', function (e) {
                    e.preventDefault();
                    var imageId = $(this).data('image-id');

                    var frame = wp.media({
                        title: '<?php echo esc_js(__('Edit Image', 'bearing')); ?>',
                        button: {
                            text: '<?php echo esc_js(__('Update Image', 'bearing')); ?>'
                        },
                        multiple: false
                    });

                    frame.on('open', function () {
                        var selection = frame.state().get('selection');
                        var attachment = wp.media.attachment(imageId);
                        attachment.fetch();
                        selection.add(attachment);
                    });

                    frame.on('select', function () {
                        var attachment = frame.state().get('selection').first().toJSON();
                        var index = galleryData.indexOf(imageId);

                        if (index !== -1) {
                            galleryData[index] = attachment.id;
                            updateGalleryData();
                            renderGallery();
                        }
                    });

                    frame.open();
                });

                // Delete image
                $(document).on('click', '.delete-gallery-image', function (e) {
                    e.preventDefault();

                    if (!confirm('<?php echo esc_js(__('Are you sure you want to delete this image from the gallery?', 'bearing')); ?>')) {
                        return;
                    }

                    var imageId = $(this).data('image-id');
                    var index = galleryData.indexOf(imageId);

                    if (index !== -1) {
                        galleryData.splice(index, 1);
                        updateGalleryData();
                        renderGallery();
                    }
                });

                // Pagination
                $('.gallery-prev-page').on('click', function () {
                    if (currentPage > 1) {
                        currentPage--;
                        renderGallery();
                    }
                });

                $('.gallery-next-page').on('click', function () {
                    var totalPages = Math.ceil(galleryData.length / perPage);
                    if (currentPage < totalPages) {
                        currentPage++;
                        renderGallery();
                    }
                });

                function updateGalleryData() {
                    $('#gallery-images-data').val(JSON.stringify(galleryData));
                }

                function renderGallery() {
                    var totalPages = Math.ceil(galleryData.length / perPage);
                    var offset = (currentPage - 1) * perPage;
                    var paginatedData = galleryData.slice(offset, offset + perPage);

                    if (currentPage > totalPages && totalPages > 0) {
                        currentPage = totalPages;
                        paginatedData = galleryData.slice((currentPage - 1) * perPage, currentPage * perPage);
                    }

                    var container = $('#gallery-images-container');

                    // Destroy sortable before clearing
                    if (container.hasClass('ui-sortable')) {
                        container.sortable('destroy');
                    }

                    container.empty();

                    if (galleryData.length === 0) {
                        container.html('<div class="no-images-message"><?php echo esc_html(__('No images added yet. Click "Add Images" to get started.', 'bearing')); ?></div>');
                        $('.gallery-pagination').hide();
                        return;
                    }

                    var imagesLoaded = 0;
                    paginatedData.forEach(function (imageId, idx) {
                        wp.media.attachment(imageId).fetch().then(function (data) {
                            var imageUrl = data.sizes && data.sizes.medium ? data.sizes.medium.url : data.url;
                            var imageHtml = '<div class="gallery-image-item" data-image-id="' + imageId + '">' +
                                '<span class="drag-handle">⋮⋮</span>' +
                                '<img src="' + imageUrl + '" alt="">' +
                                '<div class="gallery-image-actions">' +
                                '<button type="button" class="edit-gallery-image" data-image-id="' + imageId + '"><?php echo esc_html(__('Edit', 'bearing')); ?></button>' +
                                '<button type="button" class="delete-gallery-image" data-image-id="' + imageId + '"><?php echo esc_html(__('Delete', 'bearing')); ?></button>' +
                                '</div></div>';
                            container.append(imageHtml);

                            imagesLoaded++;
                            // Reinitialize sortable after all images are loaded
                            if (imagesLoaded === paginatedData.length) {
                                initSortable();
                            }
                        });
                    });

                    // Update pagination
                    if (totalPages > 1) {
                        $('.gallery-pagination').show();
                        $('.gallery-pagination span').html('<?php echo esc_html(__('Page', 'bearing')); ?> ' + currentPage + ' <?php echo esc_html(__('of', 'bearing')); ?> ' + totalPages + ' (' + galleryData.length + ' <?php echo esc_html(__('images total', 'bearing')); ?>');
                        $('.gallery-prev-page').prop('disabled', currentPage <= 1);
                        $('.gallery-next-page').prop('disabled', currentPage >= totalPages);
                    } else {
                        $('.gallery-pagination').hide();
                    }
                }
            });
        </script>
    </div>
    <?php
}

// Save Gallery Meta Box Data
function save_gallery_meta($post_id)
{
    // Security checks
    if (!isset($_POST['gallery_meta_nonce']) || !wp_verify_nonce($_POST['gallery_meta_nonce'], 'save_gallery_meta')) {
        return;
    }

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if (!current_user_can('edit_post', $post_id)) {
        return;
    }

    // Save gallery images
    if (isset($_POST['gallery_images'])) {
        $gallery_images = json_decode(stripslashes($_POST['gallery_images']), true);

        if (is_array($gallery_images)) {
            // Validate that all items are integers (attachment IDs)
            $gallery_images = array_filter($gallery_images, 'is_numeric');
            $gallery_images = array_map('intval', $gallery_images);
            update_post_meta($post_id, '_gallery_images', $gallery_images);
        } else {
            delete_post_meta($post_id, '_gallery_images');
        }
    } else {
        delete_post_meta($post_id, '_gallery_images');
    }
}
add_action('save_post_image_collection', 'save_gallery_meta');

// Enqueue WordPress Media Scripts
function enqueue_gallery_scripts($hook)
{
    global $post;

    if ($hook == 'post-new.php' || $hook == 'post.php') {
        if ('image_collection' === $post->post_type) {
            wp_enqueue_media();
            wp_enqueue_script('jquery-ui-sortable');
        }
    }
}
add_action('admin_enqueue_scripts', 'enqueue_gallery_scripts');

// Optional: Display gallery on frontend with pagination
function display_image_collection_gallery($post_id = null, $per_page = 12)
{
    if (!$post_id) {
        $post_id = get_the_ID();
    }

    $gallery_images = get_post_meta($post_id, '_gallery_images', true);

    if (empty($gallery_images) || !is_array($gallery_images)) {
        return '<p>' . esc_html__('No images in this collection.', 'bearing') . '</p>';
    }

    // Get current page from URL parameter
    $current_page = isset($_GET['gallery_page']) ? max(1, intval($_GET['gallery_page'])) : 1;

    // Calculate pagination
    $total_images = count($gallery_images);
    $total_pages = ceil($total_images / $per_page);
    $current_page = min($current_page, $total_pages); // Don't exceed max pages
    $offset = ($current_page - 1) * $per_page;

    // Get images for current page
    $paginated_images = array_slice($gallery_images, $offset, $per_page);

    // Start output with styles
    $output = '<style>
        .image-collection-gallery {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
            gap: 20px;
            margin: 30px 0;
        }
        .gallery-item {
            position: relative;
            overflow: hidden;
            border-radius: 8px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
            transition: transform 0.3s ease;
        }
        .gallery-item:hover {
            transform: translateY(-5px);
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
        }
        .gallery-item img {
            width: 100%;
            height: 300px;
            object-fit: cover;
            display: block;
        }
        .gallery-pagination-wrapper {
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 15px;
            margin: 30px 0;
            flex-wrap: wrap;
        }
        .gallery-pagination-wrapper a,
        .gallery-pagination-wrapper span {
            padding: 8px 15px;
            text-decoration: none;
            border: 1px solid #ddd;
            border-radius: 4px;
            background: #fff;
            color: #333;
            transition: all 0.3s ease;
        }
        .gallery-pagination-wrapper a:hover {
            background: #0073aa;
            color: #fff;
            border-color: #0073aa;
        }
        .gallery-pagination-wrapper .current-page {
            background: #0073aa;
            color: #fff;
            border-color: #0073aa;
            font-weight: bold;
        }
        .gallery-pagination-wrapper .disabled {
            opacity: 0.5;
            pointer-events: none;
            cursor: not-allowed;
        }
        .gallery-page-numbers {
            display: flex;
            gap: 5px;
        }
        @media (max-width: 768px) {
            .image-collection-gallery {
                grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
                gap: 10px;
            }
            .gallery-item img {
                height: 200px;
            }
        }
    </style>';

    $output .= '<div class="image-collection-gallery">';

    foreach ($paginated_images as $image_id) {
        $image_url = wp_get_attachment_image_url($image_id, 'large');
        $image_alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
        $image_title = get_the_title($image_id);

        if ($image_url) {
            $output .= '<div class="gallery-item">';
            $output .= '<a href="' . esc_url(wp_get_attachment_url($image_id)) . '" target="_blank">';
            $output .= '<img src="' . esc_url($image_url) . '" alt="' . esc_attr($image_alt) . '" title="' . esc_attr($image_title) . '">';
            $output .= '</a>';
            $output .= '</div>';
        }
    }

    $output .= '</div>';

    // Add pagination if there's more than one page
    if ($total_pages > 1) {
        $output .= '<div class="gallery-pagination-wrapper">';

        // Get current URL without gallery_page parameter
        $current_url = remove_query_arg('gallery_page');

        // Previous button
        if ($current_page > 1) {
            $prev_url = add_query_arg('gallery_page', $current_page - 1, $current_url);
            $output .= '<a href="' . esc_url($prev_url) . '" class="prev-page">← ' . esc_html__('Previous', 'bearing') . '</a>';
        } else {
            $output .= '<span class="prev-page disabled">← ' . esc_html__('Previous', 'bearing') . '</span>';
        }

        // Page numbers
        $output .= '<div class="gallery-page-numbers">';

        // Show first page
        if ($current_page > 3) {
            $page_url = add_query_arg('gallery_page', 1, $current_url);
            $output .= '<a href="' . esc_url($page_url) . '">1</a>';
            if ($current_page > 4) {
                $output .= '<span>...</span>';
            }
        }

        // Show pages around current page
        for ($i = max(1, $current_page - 2); $i <= min($total_pages, $current_page + 2); $i++) {
            if ($i == $current_page) {
                $output .= '<span class="current-page">' . $i . '</span>';
            } else {
                $page_url = add_query_arg('gallery_page', $i, $current_url);
                $output .= '<a href="' . esc_url($page_url) . '">' . $i . '</a>';
            }
        }

        // Show last page
        if ($current_page < $total_pages - 2) {
            if ($current_page < $total_pages - 3) {
                $output .= '<span>...</span>';
            }
            $page_url = add_query_arg('gallery_page', $total_pages, $current_url);
            $output .= '<a href="' . esc_url($page_url) . '">' . $total_pages . '</a>';
        }

        $output .= '</div>';

        // Next button
        if ($current_page < $total_pages) {
            $next_url = add_query_arg('gallery_page', $current_page + 1, $current_url);
            $output .= '<a href="' . esc_url($next_url) . '" class="next-page">' . esc_html__('Next', 'bearing') . ' →</a>';
        } else {
            $output .= '<span class="next-page disabled">' . esc_html__('Next', 'bearing') . ' →</span>';
        }

        $output .= '</div>';

        // Page info
        $output .= '<div style="text-align: center; color: #666; margin-top: 10px;">';
        $output .= sprintf(esc_html__('Showing %d-%d of %d images', 'bearing'), (($offset + 1)), min(($offset + $per_page), $total_images), $total_images);
        $output .= '</div>';
    }

    return $output;
}

// Shortcode to display gallery: [image_collection_gallery] or [image_collection_gallery id="123" per_page="20"]
function image_collection_gallery_shortcode($atts)
{
    $atts = shortcode_atts(array(
        'id' => get_the_ID(),
        'per_page' => 12,
    ), $atts);

    return display_image_collection_gallery($atts['id'], $atts['per_page']);
}
add_shortcode('image_collection_gallery', 'image_collection_gallery_shortcode');


// Add custom admin columns for Image Collection
function add_image_collection_admin_columns($columns)
{
    // Create new columns array with custom order
    $new_columns = array();

    // Add checkbox column first
    $new_columns['cb'] = $columns['cb'];

    // Add featured image column
    $new_columns['featured_image'] = __('Featured Image', 'bearing');

    // Add post ID column
    $new_columns['post_id'] = __('ID', 'bearing');

    // Add title and other default columns
    $new_columns['title'] = $columns['title'];

    // Add image count column
    $new_columns['image_count'] = __('Images', 'bearing');

    // Add remaining default columns (author, date, etc.)
    $new_columns['author'] = $columns['author'];
    $new_columns['date'] = $columns['date'];

    return $new_columns;
}
add_filter('manage_image_collection_posts_columns', 'add_image_collection_admin_columns');

// Populate custom admin columns
function populate_image_collection_admin_columns($column, $post_id)
{
    switch ($column) {
        case 'featured_image':
            $thumbnail = get_the_post_thumbnail($post_id, array(60, 60));
            if ($thumbnail) {
                echo $thumbnail;
            } else {
                echo '<span style="color: #999;">—</span>';
            }
            break;

        case 'post_id':
            echo '<strong>' . $post_id . '</strong>';
            break;

        case 'image_count':
            $gallery_images = get_post_meta($post_id, '_gallery_images', true);
            $count = is_array($gallery_images) ? count($gallery_images) : 0;

            if ($count > 0) {
                echo '<span class="image-count-badge">' . $count . '</span>';
            } else {
                echo '<span style="color: #999;">0</span>';
            }
            break;
    }
}
add_action('manage_image_collection_posts_custom_column', 'populate_image_collection_admin_columns', 10, 2);

// Make custom columns sortable
function make_image_collection_columns_sortable($columns)
{
    $columns['post_id'] = 'ID';
    $columns['image_count'] = 'image_count';
    return $columns;
}
add_filter('manage_edit-image_collection_sortable_columns', 'make_image_collection_columns_sortable');

// Handle sorting for image count column
function image_collection_custom_orderby($query)
{
    if (!is_admin() || !$query->is_main_query()) {
        return;
    }

    if ('image_count' === $query->get('orderby')) {
        $query->set('meta_key', '_gallery_images');
        $query->set('orderby', 'meta_value_num');
    }
}
add_action('pre_get_posts', 'image_collection_custom_orderby');

// Add custom CSS for admin columns
function image_collection_admin_css()
{
    global $post_type;

    if ('image_collection' === $post_type) {
        ?>
        <style>
            .column-featured_image {
                width: 80px;
            }

            .column-featured_image img {
                border-radius: 4px;
                border: 1px solid #ddd;
                display: block;
            }

            .column-post_id {
                width: 60px;
                text-align: center;
            }

            .column-image_count {
                width: 80px;
                text-align: center;
            }

            .image-count-badge {
                display: inline-block;
                background: #2271b1;
                color: white;
                padding: 4px 10px;
                border-radius: 12px;
                font-weight: 600;
                font-size: 12px;
            }
        </style>
        <?php
    }
}
add_action('admin_head', 'image_collection_admin_css');

用法

// In your template file:
<?php echo display_image_collection_gallery(get_the_ID(), 12); ?>

// Or use shortcode in post content:
[image_collection_gallery]

// With custom settings:
[image_collection_gallery id="123" per_page="20"]

// In a custom template:
<?php 
$post_id = 456;
echo display_image_collection_gallery($post_id, 16); // 16 images per page
?>

第一版

<?php
/**
 * Image Collection Custom Post Type with Gallery Meta Box
 * Paste this code into your theme's functions.php file
 */

// Register Custom Post Type
function register_image_collection_post_type() {
    $labels = array(
        'name'               => 'Image Collections',
        'singular_name'      => 'Image Collection',
        'menu_name'          => 'Image Collections',
        'add_new'            => 'Add New',
        'add_new_item'       => 'Add New Image Collection',
        'edit_item'          => 'Edit Image Collection',
        'new_item'           => 'New Image Collection',
        'view_item'          => 'View Image Collection',
        'search_items'       => 'Search Image Collections',
        'not_found'          => 'No image collections found',
        'not_found_in_trash' => 'No image collections found in Trash',
    );

    $args = array(
        'labels'              => $labels,
        'public'              => true,
        'has_archive'         => true,
        'publicly_queryable'  => true,
        'show_ui'             => true,
        'show_in_menu'        => true,
        'query_var'           => true,
        'rewrite'             => array('slug' => 'image-collection'),
        'capability_type'     => 'post',
        'has_archive'         => true,
        'hierarchical'        => false,
        'menu_position'       => 5,
        'menu_icon'           => 'dashicons-format-gallery',
        'supports'            => array('title', 'editor', 'thumbnail'),
    );

    register_post_type('image_collection', $args);
}
add_action('init', 'register_image_collection_post_type');

// Add Gallery Meta Box
function add_gallery_meta_box() {
    add_meta_box(
        'image_collection_gallery',
        'Gallery Images',
        'render_gallery_meta_box',
        'image_collection',
        'normal',
        'high'
    );
}
add_action('add_meta_boxes', 'add_gallery_meta_box');

// Render Gallery Meta Box
function render_gallery_meta_box($post) {
    wp_nonce_field('save_gallery_meta', 'gallery_meta_nonce');
    
    $gallery_images = get_post_meta($post->ID, '_gallery_images', true);
    $gallery_images = !empty($gallery_images) ? $gallery_images : array();
    
    // Pagination settings
    $per_page = 12;
    $current_page = isset($_GET['gallery_page']) ? max(1, intval($_GET['gallery_page'])) : 1;
    $total_images = count($gallery_images);
    $total_pages = ceil($total_images / $per_page);
    $offset = ($current_page - 1) * $per_page;
    $paginated_images = array_slice($gallery_images, $offset, $per_page, true);
    ?>
    
    <div class="gallery-meta-box">
        <style>
            .gallery-meta-box {
                padding: 10px 0;
            }
            .gallery-images-container {
                display: grid;
                grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
                gap: 15px;
                margin: 20px 0;
                min-height: 100px;
            }
            .gallery-image-item {
                position: relative;
                border: 1px solid #ddd;
                border-radius: 4px;
                overflow: hidden;
                background: #f9f9f9;
            }
            .gallery-image-item img {
                width: 100%;
                height: 150px;
                object-fit: cover;
                display: block;
            }
            .gallery-image-actions {
                position: absolute;
                top: 5px;
                right: 5px;
                display: flex;
                gap: 5px;
            }
            .gallery-image-actions button {
                background: rgba(0,0,0,0.7);
                color: white;
                border: none;
                border-radius: 3px;
                padding: 5px 8px;
                cursor: pointer;
                font-size: 12px;
            }
            .gallery-image-actions button:hover {
                background: rgba(0,0,0,0.9);
            }
            .gallery-add-button {
                display: inline-block;
                margin-bottom: 15px;
            }
            .gallery-pagination {
                margin-top: 20px;
                padding-top: 15px;
                border-top: 1px solid #ddd;
                display: flex;
                align-items: center;
                gap: 10px;
            }
            .gallery-pagination button {
                padding: 5px 10px;
            }
            .gallery-pagination span {
                color: #666;
            }
            .no-images-message {
                padding: 40px;
                text-align: center;
                color: #666;
                background: #f9f9f9;
                border: 2px dashed #ddd;
                border-radius: 4px;
            }
        </style>

        <button type="button" class="button button-primary gallery-add-button" id="add-gallery-images">
            Add Images
        </button>
        
        <input type="hidden" id="gallery-images-data" name="gallery_images" value="<?php echo esc_attr(json_encode($gallery_images)); ?>">
        
        <div class="gallery-images-container" id="gallery-images-container">
            <?php if (empty($paginated_images)): ?>
                <div class="no-images-message">No images added yet. Click "Add Images" to get started.</div>
            <?php else: ?>
                <?php foreach ($paginated_images as $index => $image_id): 
                    $image_url = wp_get_attachment_image_url($image_id, 'medium');
                    if (!$image_url) continue;
                ?>
                    <div class="gallery-image-item" data-image-id="<?php echo esc_attr($image_id); ?>" data-index="<?php echo esc_attr($index); ?>">
                        <img src="<?php echo esc_url($image_url); ?>" alt="">
                        <div class="gallery-image-actions">
                            <button type="button" class="edit-gallery-image" data-image-id="<?php echo esc_attr($image_id); ?>">Edit</button>
                            <button type="button" class="delete-gallery-image" data-image-id="<?php echo esc_attr($image_id); ?>">Delete</button>
                        </div>
                    </div>
                <?php endforeach; ?>
            <?php endif; ?>
        </div>
        
        <?php if ($total_pages > 1): ?>
            <div class="gallery-pagination">
                <button type="button" class="button gallery-prev-page" <?php echo $current_page <= 1 ? 'disabled' : ''; ?>>Previous</button>
                <span>Page <?php echo $current_page; ?> of <?php echo $total_pages; ?></span>
                <button type="button" class="button gallery-next-page" <?php echo $current_page >= $total_pages ? 'disabled' : ''; ?>>Next</button>
                <input type="hidden" id="gallery-current-page" value="<?php echo $current_page; ?>">
                <input type="hidden" id="gallery-total-pages" value="<?php echo $total_pages; ?>">
            </div>
        <?php endif; ?>

        <script type="text/javascript">
        jQuery(document).ready(function($) {
            var galleryData = [];
            var currentPage = parseInt($('#gallery-current-page').val()) || 1;
            var perPage = 12;
            
            // Initialize gallery data
            try {
                galleryData = JSON.parse($('#gallery-images-data').val()) || [];
            } catch(e) {
                galleryData = [];
            }

            // Add images
            $('#add-gallery-images').on('click', function(e) {
                e.preventDefault();
                
                var frame = wp.media({
                    title: 'Select or Upload Images',
                    button: { text: 'Add to Gallery' },
                    multiple: true
                });

                frame.on('select', function() {
                    var attachments = frame.state().get('selection').toJSON();
                    
                    attachments.forEach(function(attachment) {
                        if (!galleryData.includes(attachment.id)) {
                            galleryData.push(attachment.id);
                        }
                    });
                    
                    updateGalleryData();
                    renderGallery();
                });

                frame.open();
            });

            // Edit image
            $(document).on('click', '.edit-gallery-image', function(e) {
                e.preventDefault();
                var imageId = $(this).data('image-id');
                
                var frame = wp.media({
                    title: 'Edit Image',
                    button: { text: 'Update Image' },
                    multiple: false
                });

                frame.on('open', function() {
                    var selection = frame.state().get('selection');
                    var attachment = wp.media.attachment(imageId);
                    attachment.fetch();
                    selection.add(attachment);
                });

                frame.on('select', function() {
                    var attachment = frame.state().get('selection').first().toJSON();
                    var index = galleryData.indexOf(imageId);
                    
                    if (index !== -1) {
                        galleryData[index] = attachment.id;
                        updateGalleryData();
                        renderGallery();
                    }
                });

                frame.open();
            });

            // Delete image
            $(document).on('click', '.delete-gallery-image', function(e) {
                e.preventDefault();
                
                if (!confirm('Are you sure you want to delete this image from the gallery?')) {
                    return;
                }
                
                var imageId = $(this).data('image-id');
                var index = galleryData.indexOf(imageId);
                
                if (index !== -1) {
                    galleryData.splice(index, 1);
                    updateGalleryData();
                    renderGallery();
                }
            });

            // Pagination
            $('.gallery-prev-page').on('click', function() {
                if (currentPage > 1) {
                    currentPage--;
                    renderGallery();
                }
            });

            $('.gallery-next-page').on('click', function() {
                var totalPages = Math.ceil(galleryData.length / perPage);
                if (currentPage < totalPages) {
                    currentPage++;
                    renderGallery();
                }
            });

            function updateGalleryData() {
                $('#gallery-images-data').val(JSON.stringify(galleryData));
            }

            function renderGallery() {
                var totalPages = Math.ceil(galleryData.length / perPage);
                var offset = (currentPage - 1) * perPage;
                var paginatedData = galleryData.slice(offset, offset + perPage);
                
                if (currentPage > totalPages && totalPages > 0) {
                    currentPage = totalPages;
                    paginatedData = galleryData.slice((currentPage - 1) * perPage, currentPage * perPage);
                }

                var container = $('#gallery-images-container');
                container.empty();

                if (galleryData.length === 0) {
                    container.html('<div class="no-images-message">No images added yet. Click "Add Images" to get started.</div>');
                    $('.gallery-pagination').hide();
                    return;
                }

                paginatedData.forEach(function(imageId, idx) {
                    wp.media.attachment(imageId).fetch().then(function(data) {
                        var imageUrl = data.sizes && data.sizes.medium ? data.sizes.medium.url : data.url;
                        var imageHtml = '<div class="gallery-image-item" data-image-id="' + imageId + '">' +
                            '<img src="' + imageUrl + '" alt="">' +
                            '<div class="gallery-image-actions">' +
                            '<button type="button" class="edit-gallery-image" data-image-id="' + imageId + '">Edit</button>' +
                            '<button type="button" class="delete-gallery-image" data-image-id="' + imageId + '">Delete</button>' +
                            '</div></div>';
                        container.append(imageHtml);
                    });
                });

                // Update pagination
                if (totalPages > 1) {
                    $('.gallery-pagination').show();
                    $('.gallery-pagination span').text('Page ' + currentPage + ' of ' + totalPages);
                    $('.gallery-prev-page').prop('disabled', currentPage <= 1);
                    $('.gallery-next-page').prop('disabled', currentPage >= totalPages);
                } else {
                    $('.gallery-pagination').hide();
                }
            }
        });
        </script>
    </div>
    <?php
}

// Save Gallery Meta Box Data
function save_gallery_meta($post_id) {
    // Security checks
    if (!isset($_POST['gallery_meta_nonce']) || !wp_verify_nonce($_POST['gallery_meta_nonce'], 'save_gallery_meta')) {
        return;
    }

    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return;
    }

    if (!current_user_can('edit_post', $post_id)) {
        return;
    }

    // Save gallery images
    if (isset($_POST['gallery_images'])) {
        $gallery_images = json_decode(stripslashes($_POST['gallery_images']), true);
        
        if (is_array($gallery_images)) {
            // Validate that all items are integers (attachment IDs)
            $gallery_images = array_filter($gallery_images, 'is_numeric');
            $gallery_images = array_map('intval', $gallery_images);
            update_post_meta($post_id, '_gallery_images', $gallery_images);
        } else {
            delete_post_meta($post_id, '_gallery_images');
        }
    } else {
        delete_post_meta($post_id, '_gallery_images');
    }
}
add_action('save_post_image_collection', 'save_gallery_meta');

// Enqueue WordPress Media Scripts
function enqueue_gallery_scripts($hook) {
    global $post;

    if ($hook == 'post-new.php' || $hook == 'post.php') {
        if ('image_collection' === $post->post_type) {
            wp_enqueue_media();
        }
    }
}
add_action('admin_enqueue_scripts', 'enqueue_gallery_scripts');

// Optional: Display gallery on frontend
function display_image_collection_gallery($post_id = null) {
    if (!$post_id) {
        $post_id = get_the_ID();
    }

    $gallery_images = get_post_meta($post_id, '_gallery_images', true);
    
    if (empty($gallery_images) || !is_array($gallery_images)) {
        return '<p>No images in this collection.</p>';
    }

    $output = '<div class="image-collection-gallery">';
    
    foreach ($gallery_images as $image_id) {
        $image_url = wp_get_attachment_image_url($image_id, 'large');
        $image_alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
        
        if ($image_url) {
            $output .= '<div class="gallery-item">';
            $output .= '<img src="' . esc_url($image_url) . '" alt="' . esc_attr($image_alt) . '">';
            $output .= '</div>';
        }
    }
    
    $output .= '</div>';
    
    return $output;
}

// Shortcode to display gallery: [image_collection_gallery]
function image_collection_gallery_shortcode($atts) {
    $atts = shortcode_atts(array(
        'id' => get_the_ID(),
    ), $atts);

    return display_image_collection_gallery($atts['id']);
}
add_shortcode('image_collection_gallery', 'image_collection_gallery_shortcode');
?>

获取图片函数

<?php

/**
 * Get all gallery images with complete metadata
 * 
 * @param int $post_id The post ID (defaults to current post)
 * @param string $image_size The image size to retrieve (thumbnail, medium, large, full, or custom size)
 * @return array Array of image objects with all metadata
 */
function get_image_collection_gallery_data($post_id = null, $image_size = 'full')
{
    if (!$post_id) {
        $post_id = get_the_ID();
    }

    $gallery_images = get_post_meta($post_id, '_gallery_images', true);

    if (empty($gallery_images) || !is_array($gallery_images)) {
        return array();
    }

    $images_data = array();

    foreach ($gallery_images as $index => $image_id) {
        // Skip if attachment doesn't exist
        if (!wp_attachment_is_image($image_id)) {
            continue;
        }

        $attachment = get_post($image_id);
        
        if (!$attachment) {
            continue;
        }

        // Get image metadata
        $metadata = wp_get_attachment_metadata($image_id);
        
        // Get all image sizes
        $image_sizes = array();
        foreach (get_intermediate_image_sizes() as $size) {
            $image_data = wp_get_attachment_image_src($image_id, $size);
            if ($image_data) {
                $image_sizes[$size] = array(
                    'url' => $image_data[0],
                    'width' => $image_data[1],
                    'height' => $image_data[2]
                );
            }
        }
        
        // Add full size
        $full_image = wp_get_attachment_image_src($image_id, 'full');
        $image_sizes['full'] = array(
            'url' => $full_image[0],
            'width' => $full_image[1],
            'height' => $full_image[2]
        );

        // Build comprehensive image object
        $image_object = array(
            'attachment_id' => $image_id,
            'position' => $index + 1,
            
            // Primary image URL (requested size)
            'url' => wp_get_attachment_image_url($image_id, $image_size),
            'width' => $metadata['width'] ?? null,
            'height' => $metadata['height'] ?? null,
            
            // All available image sizes
            'sizes' => $image_sizes,
            
            // Attachment post data
            'title' => get_the_title($image_id),
            'alt' => get_post_meta($image_id, '_wp_attachment_image_alt', true),
            'caption' => $attachment->post_excerpt,
            'description' => $attachment->post_content,
            
            // File information
            'filename' => basename(get_attached_file($image_id)),
            'filesize' => filesize(get_attached_file($image_id)),
            'filesize_human' => size_format(filesize(get_attached_file($image_id))),
            'mime_type' => get_post_mime_type($image_id),
            
            // Dates
            'uploaded_date' => $attachment->post_date,
            'modified_date' => $attachment->post_modified,
            
            // Author
            'author_id' => $attachment->post_author,
            'author_name' => get_the_author_meta('display_name', $attachment->post_author),
            
            // Additional metadata
            'metadata' => $metadata,
            
            // Image HTML
            'img_tag' => wp_get_attachment_image($image_id, $image_size, false, array('alt' => get_post_meta($image_id, '_wp_attachment_image_alt', true))),
        );

        $images_data[] = $image_object;
    }

    return $images_data;
}

/**
 * Get a single image from the gallery by position or attachment ID
 * 
 * @param int $post_id The post ID
 * @param mixed $identifier Position number (1-based) or attachment ID
 * @param string $by Search by 'position' or 'attachment_id'
 * @param string $image_size Image size to retrieve
 * @return array|null Image data or null if not found
 */
function get_image_collection_single_image($post_id, $identifier, $by = 'position', $image_size = 'full')
{
    $all_images = get_image_collection_gallery_data($post_id, $image_size);

    if (empty($all_images)) {
        return null;
    }

    foreach ($all_images as $image) {
        if ($by === 'position' && $image['position'] === $identifier) {
            return $image;
        } elseif ($by === 'attachment_id' && $image['attachment_id'] === $identifier) {
            return $image;
        }
    }

    return null;
}

/**
 * Get gallery images count
 * 
 * @param int $post_id The post ID
 * @return int Number of images in the gallery
 */
function get_image_collection_count($post_id = null)
{
    if (!$post_id) {
        $post_id = get_the_ID();
    }

    $gallery_images = get_post_meta($post_id, '_gallery_images', true);

    return is_array($gallery_images) ? count($gallery_images) : 0;
}

/**
 * Get first image from gallery
 * 
 * @param int $post_id The post ID
 * @param string $image_size Image size to retrieve
 * @return array|null First image data or null
 */
function get_image_collection_first_image($post_id = null, $image_size = 'full')
{
    if (!$post_id) {
        $post_id = get_the_ID();
    }

    $images = get_image_collection_gallery_data($post_id, $image_size);

    return !empty($images) ? $images[0] : null;
}

/**
 * Get gallery images as JSON (useful for JavaScript)
 * 
 * @param int $post_id The post ID
 * @param string $image_size Image size to retrieve
 * @return string JSON string of gallery data
 */
function get_image_collection_json($post_id = null, $image_size = 'full')
{
    return wp_json_encode(get_image_collection_gallery_data($post_id, $image_size));
}

/**
 * Check if post has gallery images
 * 
 * @param int $post_id The post ID
 * @return bool True if has images, false otherwise
 */
function has_image_collection_gallery($post_id = null)
{
    return get_image_collection_count($post_id) > 0;
}

/**
 * EXAMPLE USAGE:
 * 
 * // Get all images with full metadata
 * $images = get_image_collection_gallery_data(get_the_ID(), 'large');
 * 
 * foreach ($images as $image) {
 *     echo '<div class="gallery-item">';
 *     echo '<img src="' . esc_url($image['url']) . '" alt="' . esc_attr($image['alt']) . '">';
 *     echo '<h3>' . esc_html($image['title']) . '</h3>';
 *     echo '<p>' . esc_html($image['caption']) . '</p>';
 *     echo '<span>Position: ' . $image['position'] . '</span>';
 *     echo '<span>Size: ' . $image['filesize_human'] . '</span>';
 *     echo '</div>';
 * }
 * 
 * // Get single image by position
 * $first_image = get_image_collection_single_image(get_the_ID(), 1, 'position');
 * 
 * // Get single image by attachment ID
 * $specific_image = get_image_collection_single_image(get_the_ID(), 123, 'attachment_id');
 * 
 * // Check if post has gallery
 * if (has_image_collection_gallery()) {
 *     echo 'This post has ' . get_image_collection_count() . ' images';
 * }
 * 
 * // Get as JSON for JavaScript
 * $json_data = get_image_collection_json(get_the_ID(), 'medium');
 * echo '<script>var galleryData = ' . $json_data . ';</script>';
 */
×
最新版image_collection模块带用法

最新版image_collection模块带用法

更新了可以拖拽排序,带分页输出,添加缩略...