更新了可以拖拽排序,带分页输出,添加缩略图和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>';
*/
点我进行打赏