Changes to LCP Gallery Meta

This commit is contained in:
Jeremy Rangel
2025-01-09 17:50:32 -08:00
parent ceb37fc5af
commit ae31fec647
14 changed files with 651 additions and 35 deletions

View File

@ -692,3 +692,476 @@ function lcp_register_custom_image_sizes() {
// Hook into WordPress to register custom sizes
add_action('after_setup_theme', 'lcp_register_custom_image_sizes');
/* CUSTOM POST TYPES */
// Register the post meta field for the gallery
function to_register_meta_fields() {
register_post_meta('post', 'lcp_post_gallery', [
'type' => 'array',
'description' => 'Custom post gallery',
'single' => true,
'show_in_rest' => true,
]);
}
add_action('init', 'to_register_meta_fields');
// Add meta box for the gallery
function add_lcp_gallery_meta_box() {
add_meta_box(
'lcp_gallery_meta_box',
__('Post Gallery', 'textdomain'),
'render_lcp_gallery_meta_box',
'post',
'normal',
'high'
);
}
add_action('add_meta_boxes', 'add_lcp_gallery_meta_box');
// Render the meta box
function render_lcp_gallery_meta_box($post) {
// Add nonce for security
wp_nonce_field('lcp_gallery_meta_box', 'lcp_gallery_meta_box_nonce');
// Get existing gallery items
$gallery_items = get_post_meta($post->ID, 'lcp_gallery', true);
if (!is_array($gallery_items)) {
$gallery_items = array();
}
?>
<style>
#lcp-gallery-list li {
cursor: move;
padding: 10px;
margin-bottom: 10px;
background: #fff;
border: 1px solid #ddd;
position: relative;
}
#lcp-gallery-list li.dragging {
opacity: 0.5;
border: 2px dashed #999;
}
.drag-handle {
cursor: move;
padding: 5px;
margin-right: 10px;
color: #999;
}
#lcp-media-source-selector {
margin-bottom: 15px;
}
.gallery-item-fields {
margin-top: 10px;
}
.gallery-item-fields input[type="text"],
.gallery-item-fields textarea {
width: 100%;
margin-bottom: 5px;
}
.gallery-item-fields label {
display: block;
margin-top: 5px;
font-weight: bold;
}
.color-picker-wrapper {
display: flex;
align-items: center;
gap: 10px;
margin-top: 5px;
}
.color-picker-wrapper input[type="color"] {
padding: 0;
width: 50px;
height: 30px;
}
</style>
<div id="lcp-gallery-container">
<div id="lcp-media-source-selector">
<select id="lcp-media-source">
<option value="media_library">Media Library</option>
<option value="youtube">YouTube</option>
<option value="vimeo">Vimeo</option>
</select>
<button type="button" class="button" id="add-to-gallery">Add Media</button>
</div>
<ul id="lcp-gallery-list">
<?php
foreach ($gallery_items as $item) {
$media_source = isset($item['media_source']) ? $item['media_source'] : '';
$media_id = isset($item['media_id']) ? $item['media_id'] : '';
$url = isset($item['url']) ? $item['url'] : '';
$title = isset($item['title']) ? $item['title'] : '';
$caption = isset($item['caption']) ? $item['caption'] : '';
$background_color = isset($item['background_color']) ? $item['background_color'] : '';
echo '<li draggable="true" style="background-color: ' . esc_attr($background_color) . ';">';
echo '<span class="drag-handle dashicons dashicons-menu"></span>';
echo '<input type="hidden" name="lcp_gallery[]" value="' . esc_attr(json_encode($item)) . '">';
if ($media_source === 'media_library' && $media_id) {
$image = wp_get_attachment_image_src($media_id, 'thumbnail');
if ($image) {
echo '<img src="' . esc_url($image[0]) . '" alt="" style="max-width: 150px; height: auto;">';
}
} elseif ($media_source === 'youtube' && $url) {
echo '<div class="youtube-thumbnail" style="width: 150px; height: 150px; background: #f0f0f0; display: flex; align-items: center; justify-content: center;">';
echo '<span class="dashicons dashicons-youtube"></span>';
echo '</div>';
}
// Add fields for title, caption, and background color
echo '<div class="gallery-item-fields">';
echo '<label>Title</label>';
echo '<input type="text" class="gallery-item-title" value="' . esc_attr($title) . '" placeholder="Enter title">';
echo '<label>Caption</label>';
echo '<textarea class="gallery-item-caption" rows="3" placeholder="Enter caption">' . esc_textarea($caption) . '</textarea>';
echo '<label>Background Color</label>';
echo '<div class="color-picker-wrapper">';
echo '<input type="color" class="gallery-item-bgcolor" value="' . esc_attr($background_color) . '">';
echo '<button type="button" class="button update-bgcolor">Update Background</button>';
echo '</div>';
echo '</div>';
echo '<button type="button" class="button remove-gallery-item">Remove</button>';
echo '</li>';
}
?>
</ul>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize gallery items array from PHP
let galleryItems = <?php echo json_encode($gallery_items); ?>;
console.log('Initial gallery items:', galleryItems);
// Function to update hidden input values and maintain state
function updateHiddenInputs() {
const list = document.querySelector('#lcp-gallery-list');
list.innerHTML = ''; // Clear the list
console.log('Updating gallery items:', galleryItems);
// Rebuild the list with current items
galleryItems.forEach((item, index) => {
const li = document.createElement('li');
li.draggable = true;
if (item.background_color) {
li.style.backgroundColor = item.background_color;
}
// Add drag handle
const dragHandle = document.createElement('span');
dragHandle.className = 'drag-handle dashicons dashicons-menu';
li.appendChild(dragHandle);
// Add hidden input with item data
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'lcp_gallery[]';
input.value = JSON.stringify(item);
li.appendChild(input);
// Add thumbnail
if (item.media_source === 'media_library' && item.media_id) {
wp.media.attachment(item.media_id).fetch().then(function() {
const img = document.createElement('img');
img.src = wp.media.attachment(item.media_id).get('url');
img.style = 'max-width: 150px; height: auto;';
li.appendChild(img);
});
} else if (item.media_source === 'youtube' && item.url) {
const div = document.createElement('div');
div.className = 'youtube-thumbnail';
div.style = 'width: 150px; height: 150px; background: #f0f0f0; display: flex; align-items: center; justify-content: center;';
div.innerHTML = '<span class="dashicons dashicons-youtube"></span>';
li.appendChild(div);
}
// Add fields container
const fields = document.createElement('div');
fields.className = 'gallery-item-fields';
// Title field
const titleLabel = document.createElement('label');
titleLabel.textContent = 'Title';
fields.appendChild(titleLabel);
const titleInput = document.createElement('input');
titleInput.type = 'text';
titleInput.className = 'gallery-item-title';
titleInput.value = item.title || '';
titleInput.placeholder = 'Enter title';
titleInput.addEventListener('input', () => {
item.title = titleInput.value;
updateItemData(index, item);
});
fields.appendChild(titleInput);
// Caption field
const captionLabel = document.createElement('label');
captionLabel.textContent = 'Caption';
fields.appendChild(captionLabel);
const captionInput = document.createElement('textarea');
captionInput.className = 'gallery-item-caption';
captionInput.rows = 3;
captionInput.value = item.caption || '';
captionInput.placeholder = 'Enter caption';
captionInput.addEventListener('input', () => {
item.caption = captionInput.value;
updateItemData(index, item);
});
fields.appendChild(captionInput);
// Background color field
const colorLabel = document.createElement('label');
colorLabel.textContent = 'Background Color';
fields.appendChild(colorLabel);
const colorWrapper = document.createElement('div');
colorWrapper.className = 'color-picker-wrapper';
const colorInput = document.createElement('input');
colorInput.type = 'color';
colorInput.className = 'gallery-item-bgcolor';
colorInput.value = item.background_color || '#ffffff';
const updateColorBtn = document.createElement('button');
updateColorBtn.type = 'button';
updateColorBtn.className = 'button update-bgcolor';
updateColorBtn.textContent = 'Update Background';
updateColorBtn.addEventListener('click', () => {
const newColor = colorInput.value;
item.background_color = newColor;
li.style.backgroundColor = newColor;
updateItemData(index, item);
console.log('Color updated for item', index, ':', item);
});
colorInput.addEventListener('input', () => {
// Preview the color as user picks
li.style.backgroundColor = colorInput.value;
});
colorWrapper.appendChild(colorInput);
colorWrapper.appendChild(updateColorBtn);
fields.appendChild(colorWrapper);
li.appendChild(fields);
// Add remove button
const removeBtn = document.createElement('button');
removeBtn.type = 'button';
removeBtn.className = 'button remove-gallery-item';
removeBtn.textContent = 'Remove';
removeBtn.onclick = function() {
galleryItems.splice(index, 1);
console.log('Removed item at index', index, '. New gallery items:', galleryItems);
updateHiddenInputs();
};
li.appendChild(removeBtn);
// Add drag and drop event listeners
li.addEventListener('dragstart', handleDragStart);
li.addEventListener('dragend', handleDragEnd);
li.addEventListener('dragover', handleDragOver);
li.addEventListener('drop', handleDrop);
list.appendChild(li);
});
}
// Function to update a specific item's data
function updateItemData(index, item) {
galleryItems[index] = item;
const input = document.querySelector(`#lcp-gallery-list li:nth-child(${index + 1}) input[name^="lcp_gallery"]`);
if (input) {
input.value = JSON.stringify(item);
console.log('Updated item at index', index, ':', item);
console.log('Current gallery items:', galleryItems);
}
}
// Initialize the list with existing items
if (galleryItems.length > 0) {
console.log('Initializing gallery with existing items:', galleryItems);
updateHiddenInputs();
}
// Handle drag and drop
let draggedItem = null;
let draggedIndex = null;
function handleDragStart(e) {
draggedItem = this;
draggedIndex = Array.from(draggedItem.parentNode.children).indexOf(draggedItem);
this.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
console.log('Started dragging item at index:', draggedIndex);
}
function handleDragEnd(e) {
this.classList.remove('dragging');
draggedItem = null;
draggedIndex = null;
}
function handleDragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
}
function handleDrop(e) {
e.preventDefault();
if (draggedItem === this) return;
const droppedIndex = Array.from(this.parentNode.children).indexOf(this);
console.log('Dropping item from index', draggedIndex, 'to index', droppedIndex);
// Reorder galleryItems array
const [movedItem] = galleryItems.splice(draggedIndex, 1);
galleryItems.splice(droppedIndex, 0, movedItem);
console.log('Reordered gallery items:', galleryItems);
updateHiddenInputs();
}
// Initialize the media frame
let mediaFrame;
document.getElementById('add-to-gallery').addEventListener('click', function(e) {
e.preventDefault();
const source = document.getElementById('lcp-media-source').value;
if (source === 'media_library') {
if (mediaFrame) {
mediaFrame.open();
return;
}
mediaFrame = wp.media({
title: 'Select Media',
button: {
text: 'Add to Gallery'
},
multiple: false
});
mediaFrame.on('select', function() {
const attachment = mediaFrame.state().get('selection').first().toJSON();
const item = {
media_source: 'media_library',
media_id: attachment.id,
title: '',
caption: '',
background_color: ''
};
galleryItems.push(item);
console.log('Added new media library item:', item);
console.log('Current gallery items:', galleryItems);
updateHiddenInputs();
});
mediaFrame.open();
} else {
const url = prompt('Enter ' + source + ' URL:');
if (url) {
const item = {
media_source: source,
url: url,
title: '',
caption: '',
background_color: ''
};
galleryItems.push(item);
console.log('Added new URL item:', item);
console.log('Current gallery items:', galleryItems);
updateHiddenInputs();
}
}
});
});
</script>
<?php
}
// Save the gallery meta data
function save_lcp_gallery_meta_box($post_id) {
// Check if our nonce is set
if (!isset($_POST['lcp_gallery_meta_box_nonce'])) {
return;
}
// Verify that the nonce is valid
if (!wp_verify_nonce($_POST['lcp_gallery_meta_box_nonce'], 'lcp_gallery_meta_box')) {
return;
}
// If this is an autosave, our form has not been submitted, so we don't want to do anything
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// Check the user's permissions
if (isset($_POST['post_type']) && 'page' == $_POST['post_type']) {
if (!current_user_can('edit_page', $post_id)) {
return;
}
} else {
if (!current_user_can('edit_post', $post_id)) {
return;
}
}
// Save the gallery data
if (isset($_POST['lcp_gallery'])) {
$gallery_items = array_map(function($item) {
return is_string($item) ? json_decode(wp_unslash($item), true) : $item;
}, $_POST['lcp_gallery']);
update_post_meta($post_id, 'lcp_gallery', $gallery_items);
} else {
delete_post_meta($post_id, 'lcp_gallery');
}
}
add_action('save_post', 'save_lcp_gallery_meta_box');
// Enqueue color picker
function lcp_gallery_admin_enqueue($hook) {
if ('post.php' != $hook && 'post-new.php' != $hook) {
return;
}
wp_enqueue_style('wp-color-picker');
wp_enqueue_script('wp-color-picker');
}
add_action('admin_enqueue_scripts', 'lcp_gallery_admin_enqueue');
// Add AJAX handler for gallery updates
add_action('wp_ajax_update_gallery_meta', 'handle_gallery_meta_update');
add_action('wp_ajax_nopriv_update_gallery_meta', 'handle_gallery_meta_update');
function handle_gallery_meta_update() {
// Verify nonce for security
if (!current_user_can('edit_posts')) {
wp_send_json_error('Permission denied');
return;
}
$post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
$gallery_items = isset($_POST['gallery_items']) ? json_decode(stripslashes($_POST['gallery_items']), true) : array();
// Update the post meta
$updated = update_post_meta($post_id, 'lcp_gallery', $gallery_items);
if ($updated) {
wp_send_json_success(array('message' => 'Gallery updated successfully'));
} else {
wp_send_json_error(array('message' => 'Failed to update gallery'));
}
}