Files
igny8/igny8-wp-plugin-for-reference-olny/core/admin/meta-boxes.php
2025-11-09 10:27:02 +00:00

388 lines
14 KiB
PHP

<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : meta-boxes.php
* @location : /core/admin/meta-boxes.php
* @type : Function Library
* @scope : Global
* @allowed : Meta box registration, SEO field management
* @reusability : Globally Reusable
* @notes : SEO meta boxes for post editor
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// === SEO Meta Box ===
add_action('add_meta_boxes', function() {
// SEO fields
add_meta_box('igny8_seo_meta', 'Igny8 SEO Fields', function($post) {
$meta_title = get_post_meta($post->ID, '_igny8_meta_title', true);
$meta_desc = get_post_meta($post->ID, '_igny8_meta_description', true);
$primary_kw = get_post_meta($post->ID, '_igny8_primary_keywords', true);
$secondary_kw = get_post_meta($post->ID, '_igny8_secondary_keywords', true);
?>
<div style="padding:8px 4px;">
<label><strong>Meta Title:</strong></label><br>
<input type="text" name="_igny8_meta_title" value="<?php echo esc_attr($meta_title); ?>" style="width:100%;"><br><br>
<label><strong>Meta Description:</strong></label><br>
<textarea name="_igny8_meta_description" rows="3" style="width:100%;"><?php echo esc_textarea($meta_desc); ?></textarea><br><br>
<label><strong>Primary Keyword:</strong></label><br>
<input type="text" name="_igny8_primary_keywords" value="<?php echo esc_attr($primary_kw); ?>" style="width:100%;"><br><br>
<label><strong>Secondary Keywords (comma-separated):</strong></label><br>
<input type="text" name="_igny8_secondary_keywords" value="<?php echo esc_attr($secondary_kw); ?>" style="width:100%;">
</div>
<?php
}, ['post','page','product'], 'normal', 'high');
});
// === Save Meta Box Data ===
add_action('save_post', function($post_id) {
// Security checks
if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
return;
}
// Save SEO fields
$fields = [
'_igny8_meta_title',
'_igny8_meta_description',
'_igny8_primary_keywords',
'_igny8_secondary_keywords',
];
foreach ($fields as $field) {
if (isset($_POST[$field])) {
update_post_meta($post_id, $field, sanitize_text_field($_POST[$field]));
}
}
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log("Igny8 Metabox: SEO fields saved for post $post_id");
}
});
// === In-Article Image Gallery Meta Box ===
add_action('add_meta_boxes', function() {
$enabled_post_types = get_option('igny8_enable_image_metabox', []);
foreach ((array) $enabled_post_types as $pt) {
add_meta_box(
'igny8_image_gallery',
'Igny8 In-Article Images',
'igny8_render_image_gallery_metabox',
$pt,
'side',
'high'
);
}
});
function igny8_render_image_gallery_metabox($post) {
wp_nonce_field('igny8_save_image_gallery', 'igny8_image_gallery_nonce');
$images = get_post_meta($post->ID, '_igny8_inarticle_images', true);
if (!is_array($images)) $images = [];
// Add CSS for grid layout and remove button
?>
<style>
.igny8-image-list {
display: flex;
flex-wrap: wrap;
list-style: none;
margin: 0;
padding: 0;
gap: 5px;
}
.igny8-image-list li {
width: 200px;
margin: 0 5px 5px 0;
box-sizing: border-box;
text-align: center;
position: relative;
border: 1px solid #eee;
padding: 5px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 180px;
}
.igny8-image-list li img {
display: block;
margin: 0 auto 5px auto;
}
.igny8-image-actions {
position: absolute;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 5px;
opacity: 0;
transition: opacity 0.2s ease;
z-index: 10;
}
.igny8-image-list li:hover .igny8-image-actions {
opacity: 1;
}
.igny8-remove-btn, .igny8-replace-btn {
background: #f0f0f0;
border: 1px solid #ccc;
color: #333;
font-size: 10px;
padding: 2px 6px;
cursor: pointer;
border-radius: 2px;
text-decoration: none;
white-space: nowrap;
}
.igny8-remove-btn:hover {
background: #dc3232;
color: #fff;
border-color: #dc3232;
}
.igny8-replace-btn:hover {
background: #0073aa;
color: #fff;
border-color: #0073aa;
}
.igny8-image-list li strong {
font-size: 0.8em;
word-break: break-all;
}
</style>
<?php
echo '<div id="igny8-image-gallery">';
echo '<p><label><input type="radio" name="igny8_image_type" value="desktop" checked> Desktop</label>
<label><input type="radio" name="igny8_image_type" value="mobile"> Mobile</label></p>';
echo '<button type="button" class="button button-primary" id="igny8-add-image">Add Image</button>';
echo '<ul class="igny8-image-list">';
// Sort images by device type and ID
$sorted_images = [];
foreach ($images as $label => $data) {
$sorted_images[$label] = $data;
}
// Custom sort function to order by device type (desktop first) then by ID
uksort($sorted_images, function($a, $b) {
$a_parts = explode('-', $a);
$b_parts = explode('-', $b);
$a_device = $a_parts[0];
$b_device = $b_parts[0];
// Desktop comes before mobile
if ($a_device === 'desktop' && $b_device === 'mobile') return -1;
if ($a_device === 'mobile' && $b_device === 'desktop') return 1;
// If same device, sort by ID number
if ($a_device === $b_device) {
$a_id = intval($a_parts[1]);
$b_id = intval($b_parts[1]);
return $a_id - $b_id;
}
return 0;
});
foreach ($sorted_images as $label => $data) {
echo '<li data-label="' . esc_attr($label) . '">';
echo '<strong>' . esc_html($label) . '</strong><br>';
echo wp_get_attachment_image($data['attachment_id'], 'thumbnail', false, ['style' => 'width: 150px; height: 150px; object-fit: cover;']);
echo '<div class="igny8-image-actions">';
echo '<button type="button" class="igny8-replace-btn">Replace</button>';
echo '<button type="button" class="igny8-remove-btn">Remove</button>';
echo '</div>';
echo '<input type="hidden" name="igny8_image_data[' . esc_attr($label) . '][attachment_id]" value="' . esc_attr($data['attachment_id']) . '">';
echo '<input type="hidden" name="igny8_image_data[' . esc_attr($label) . '][device]" value="' . esc_attr($data['device']) . '">';
echo '</li>';
}
echo '</ul>';
// Inline JS
?>
<script>
jQuery(document).ready(function($) {
// Function to get first available ID for a device type
function getFirstAvailableId(deviceType) {
let existingIds = [];
$('.igny8-image-list li').each(function() {
let label = $(this).data('label');
if (label && label.startsWith(deviceType + '-')) {
let id = parseInt(label.split('-')[1]);
if (!isNaN(id)) {
existingIds.push(id);
}
}
});
// Sort existing IDs and find first gap
existingIds.sort((a, b) => a - b);
for (let i = 1; i <= existingIds.length + 1; i++) {
if (!existingIds.includes(i)) {
return i;
}
}
return 1; // Fallback
}
$('#igny8-add-image').on('click', function(e) {
e.preventDefault();
let type = $('input[name="igny8_image_type"]:checked').val() || 'desktop';
let availableId = getFirstAvailableId(type);
let label = type + '-' + availableId;
const frame = wp.media({
title: 'Select Image',
button: { text: 'Use this image' },
multiple: false
});
frame.on('select', function() {
let attachment = frame.state().get('selection').first().toJSON();
let html = '<li data-label="' + label + '">' +
'<strong>' + label + '</strong><br>' +
'<img src="' + attachment.sizes.thumbnail.url + '" style="width: 150px; height: 150px; object-fit: cover;" /><br>' +
'<div class="igny8-image-actions">' +
'<button type="button" class="igny8-replace-btn">Replace</button>' +
'<button type="button" class="igny8-remove-btn">Remove</button>' +
'</div>' +
'<input type="hidden" name="igny8_image_data[' + label + '][attachment_id]" value="' + attachment.id + '">' +
'<input type="hidden" name="igny8_image_data[' + label + '][device]" value="' + type + '">' +
'</li>';
$('.igny8-image-list').append(html);
});
frame.open();
});
// Handle image removal (event delegation for dynamically added elements)
$(document).on('click', '.igny8-remove-btn', function() {
$(this).closest('li').remove();
});
// Handle image replacement (event delegation for dynamically added elements)
$(document).on('click', '.igny8-replace-btn', function() {
let $li = $(this).closest('li');
let label = $li.data('label');
let type = label.split('-')[0];
const frame = wp.media({
title: 'Replace Image',
button: { text: 'Replace this image' },
multiple: false
});
frame.on('select', function() {
let attachment = frame.state().get('selection').first().toJSON();
// Replace the entire img element to force reload
let $img = $li.find('img');
let newImg = $('<img>').attr({
'src': attachment.sizes.thumbnail.url,
'style': 'width: 150px; height: 150px; object-fit: cover;'
});
$img.replaceWith(newImg);
// Update the hidden input
$li.find('input[name*="[attachment_id]"]').val(attachment.id);
});
frame.open();
});
// Handle Convert Content to Blocks
$('#igny8-convert-to-blocks').on('click', function(e) {
e.preventDefault();
if (!confirm('This will convert the post content from HTML to WordPress blocks. Continue?')) {
return;
}
let $button = $(this);
let originalText = $button.text();
$button.prop('disabled', true).text('Converting...');
// Get post ID from the current post
let postId = $('#post_ID').val();
// Make AJAX request to convert content to blocks
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'igny8_convert_content_to_blocks',
post_id: postId,
nonce: '<?php echo wp_create_nonce('igny8_convert_to_blocks'); ?>'
},
success: function(response) {
console.log('Convert to Blocks Response:', response);
if (response.success) {
console.log('Success data:', response.data);
alert('Content converted successfully! ' + response.data.message);
location.reload();
} else {
console.error('Error data:', response.data);
alert('Error: ' + (response.data || 'Unknown error'));
}
},
error: function(xhr, status, error) {
console.error('Convert to Blocks Error:', {status, error, responseText: xhr.responseText});
alert('Error converting content. Check console for details.');
},
complete: function() {
$button.prop('disabled', false).text(originalText);
}
});
});
});
</script>
<?php
}
// SAVE HANDLER for Image Gallery
add_action('save_post', function($post_id) {
if (!isset($_POST['igny8_image_gallery_nonce']) || !wp_verify_nonce($_POST['igny8_image_gallery_nonce'], 'igny8_save_image_gallery')) return;
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (!current_user_can('edit_post', $post_id)) return;
$images = $_POST['igny8_image_data'] ?? [];
$filtered = [];
foreach ($images as $label => $data) {
if (!empty($data['attachment_id'])) {
$filtered[$label] = [
'label' => sanitize_text_field($label),
'attachment_id' => intval($data['attachment_id']),
'url' => wp_get_attachment_url(intval($data['attachment_id'])),
'device' => sanitize_text_field($data['device'])
];
}
}
update_post_meta($post_id, '_igny8_inarticle_images', $filtered);
if (WP_DEBUG === true) {
error_log("[IGNY8 DEBUG] Saving In-Article Images for Post ID: $post_id");
error_log(print_r($filtered, true));
}
});