reference plugin and image gen analysis
This commit is contained in:
387
igny8-ai-seo-wp-plugin/core/admin/meta-boxes.php
Normal file
387
igny8-ai-seo-wp-plugin/core/admin/meta-boxes.php
Normal file
@@ -0,0 +1,387 @@
|
||||
<?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));
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user