reference plugin and image gen analysis
This commit is contained in:
3039
igny8-ai-seo-wp-plugin/assets/css/core-backup.css
Normal file
3039
igny8-ai-seo-wp-plugin/assets/css/core-backup.css
Normal file
File diff suppressed because it is too large
Load Diff
3039
igny8-ai-seo-wp-plugin/assets/css/core.css
Normal file
3039
igny8-ai-seo-wp-plugin/assets/css/core.css
Normal file
File diff suppressed because it is too large
Load Diff
51
igny8-ai-seo-wp-plugin/assets/css/image-injection.css
Normal file
51
igny8-ai-seo-wp-plugin/assets/css/image-injection.css
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Igny8 Image Injection CSS
|
||||
*
|
||||
* Responsive image display styles for marker-based image injection
|
||||
*
|
||||
* @package Igny8
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
/* Desktop and larger screens (769px+) */
|
||||
@media (min-width: 769px) {
|
||||
.igny8-article-image-desktop {
|
||||
display: block !important;
|
||||
}
|
||||
.igny8-article-image-mobile {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile and smaller screens (768px and below) */
|
||||
@media (max-width: 768px) {
|
||||
.igny8-article-image-desktop {
|
||||
display: none !important;
|
||||
}
|
||||
.igny8-article-image-mobile {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Image wrapper styling */
|
||||
.igny8-image-wrapper {
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.igny8-image-wrapper img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Loading state */
|
||||
.igny8-image-wrapper img[loading="lazy"] {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.igny8-image-wrapper img[loading="lazy"].loaded {
|
||||
opacity: 1;
|
||||
}
|
||||
4961
igny8-ai-seo-wp-plugin/assets/js/core.js
Normal file
4961
igny8-ai-seo-wp-plugin/assets/js/core.js
Normal file
File diff suppressed because it is too large
Load Diff
436
igny8-ai-seo-wp-plugin/assets/js/image-queue-processor.js
Normal file
436
igny8-ai-seo-wp-plugin/assets/js/image-queue-processor.js
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
* Igny8 Image Queue Processor
|
||||
* Sequential image generation with individual progress tracking
|
||||
*/
|
||||
|
||||
// Process AI Image Generation for Drafts (Sequential Image Processing with Queue Modal)
|
||||
function processAIImageGenerationDrafts(postIds) {
|
||||
console.log('Igny8: processAIImageGenerationDrafts called with postIds:', postIds);
|
||||
|
||||
// Event 1: Generate Images button clicked
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('INFO', 'Generate Images button clicked', {
|
||||
postIds: postIds,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
// Get image generation settings from saved options (passed via wp_localize_script)
|
||||
const desktopEnabled = window.IGNY8_PAGE?.imageSettings?.desktop_enabled || false;
|
||||
const mobileEnabled = window.IGNY8_PAGE?.imageSettings?.mobile_enabled || false;
|
||||
const maxInArticleImages = window.IGNY8_PAGE?.imageSettings?.max_in_article_images || 1;
|
||||
|
||||
// Event 2: Settings retrieved
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('SUCCESS', 'Settings retrieved', {
|
||||
desktop: desktopEnabled,
|
||||
mobile: mobileEnabled,
|
||||
maxImages: maxInArticleImages
|
||||
});
|
||||
}
|
||||
|
||||
// Build image queue based on settings
|
||||
const imageQueue = [];
|
||||
|
||||
postIds.forEach((postId, postIndex) => {
|
||||
// Featured image (always)
|
||||
imageQueue.push({
|
||||
post_id: postId,
|
||||
post_number: postIndex + 1,
|
||||
type: 'featured',
|
||||
device: '',
|
||||
label: 'Featured Image',
|
||||
post_title: `Post ${postIndex + 1}`
|
||||
});
|
||||
|
||||
// Desktop in-article images
|
||||
if (desktopEnabled) {
|
||||
for (let i = 1; i <= maxInArticleImages; i++) {
|
||||
imageQueue.push({
|
||||
post_id: postId,
|
||||
post_number: postIndex + 1,
|
||||
type: 'article',
|
||||
device: 'desktop',
|
||||
index: i,
|
||||
label: `desktop-${i}`,
|
||||
section: i,
|
||||
post_title: `Post ${postIndex + 1}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile in-article images
|
||||
if (mobileEnabled) {
|
||||
for (let i = 1; i <= maxInArticleImages; i++) {
|
||||
imageQueue.push({
|
||||
post_id: postId,
|
||||
post_number: postIndex + 1,
|
||||
type: 'article',
|
||||
device: 'mobile',
|
||||
index: i,
|
||||
label: `mobile-${i}`,
|
||||
section: i,
|
||||
post_title: `Post ${postIndex + 1}`
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Igny8: Image queue built:', imageQueue);
|
||||
|
||||
// Show queue modal
|
||||
showImageQueueModal(imageQueue, imageQueue.length);
|
||||
|
||||
// Start processing queue
|
||||
processImageQueue(imageQueue, 0);
|
||||
}
|
||||
|
||||
// Show modal with image queue and individual progress bars
|
||||
function showImageQueueModal(queue, totalImages) {
|
||||
if (window.currentProgressModal) {
|
||||
window.currentProgressModal.remove();
|
||||
}
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.id = 'igny8-image-queue-modal';
|
||||
modal.className = 'igny8-modal';
|
||||
|
||||
let queueHTML = '';
|
||||
queue.forEach((item, index) => {
|
||||
const itemId = `queue-item-${index}`;
|
||||
queueHTML += `
|
||||
<div class="queue-item" id="${itemId}" data-status="pending">
|
||||
<div style="display: flex; gap: 10px; align-items: center;">
|
||||
<div style="flex: 1;">
|
||||
<div class="queue-item-header">
|
||||
<span class="queue-number">${index + 1}</span>
|
||||
<span class="queue-label">${item.label}</span>
|
||||
<span class="queue-post-title">${item.post_title}</span>
|
||||
<span class="queue-status">⏳ Pending</span>
|
||||
</div>
|
||||
<div class="queue-progress-bar">
|
||||
<div class="queue-progress-fill" style="width: 0%"></div>
|
||||
<div class="queue-progress-text">0%</div>
|
||||
</div>
|
||||
<div class="queue-error" style="display: none;"></div>
|
||||
</div>
|
||||
<div class="queue-thumbnail" style="width: 75px; height: 75px; background: #f0f0f0; border-radius: 4px; display: flex; align-items: center; justify-content: center; overflow: hidden; flex-shrink: 0;">
|
||||
<span style="color: #999; font-size: 11px;">No image</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
modal.innerHTML = `
|
||||
<div class="igny8-modal-content" style="max-width: 950px; max-height: 80vh; overflow-y: auto;">
|
||||
<div class="igny8-modal-header">
|
||||
<h3>🎨 Generating Images</h3>
|
||||
<p style="margin: 5px 0; color: var(--text-light);">Total: ${totalImages} images in queue</p>
|
||||
</div>
|
||||
<div class="igny8-modal-body">
|
||||
<div class="image-queue-container">
|
||||
${queueHTML}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
.queue-item {
|
||||
margin-bottom: 12px;
|
||||
padding: 12px;
|
||||
background: var(--panel-2);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.queue-item[data-status="processing"] {
|
||||
background: rgba(59, 130, 246, 0.1);
|
||||
border-color: rgb(59, 130, 246);
|
||||
}
|
||||
.queue-item[data-status="completed"] {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border-color: rgb(16, 185, 129);
|
||||
}
|
||||
.queue-item[data-status="failed"] {
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border-color: rgb(239, 68, 68);
|
||||
}
|
||||
.queue-item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.queue-number {
|
||||
background: rgb(59, 130, 246);
|
||||
color: white;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.queue-label {
|
||||
font-weight: 600;
|
||||
}
|
||||
.queue-post-title {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.queue-status {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.queue-progress-bar {
|
||||
height: 20px;
|
||||
background: rgba(0,0,0,0.1);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.queue-progress-fill {
|
||||
height: 100%;
|
||||
background: rgb(59, 130, 246);
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.queue-progress-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-shadow: 0 0 3px rgba(255,255,255,0.8);
|
||||
}
|
||||
.queue-item[data-status="completed"] .queue-progress-fill {
|
||||
background: rgb(16, 185, 129);
|
||||
}
|
||||
.queue-item[data-status="failed"] .queue-progress-fill {
|
||||
background: rgb(239, 68, 68);
|
||||
}
|
||||
.queue-error {
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border-left: 3px solid rgb(239, 68, 68);
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
modal.classList.add('open');
|
||||
window.currentProgressModal = modal;
|
||||
}
|
||||
|
||||
// Process image queue sequentially with progressive loading
|
||||
function processImageQueue(queue, currentIndex) {
|
||||
if (currentIndex >= queue.length) {
|
||||
// All done
|
||||
console.log('Igny8: All images processed');
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('SUCCESS', 'All images processed', {
|
||||
total: queue.length,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (window.currentProgressModal) {
|
||||
window.currentProgressModal.remove();
|
||||
window.currentProgressModal = null;
|
||||
}
|
||||
showNotification('Image generation complete!', 'success');
|
||||
|
||||
// Reload table
|
||||
if (window.loadTableData && window.IGNY8_PAGE?.tableId) {
|
||||
window.loadTableData(window.IGNY8_PAGE.tableId);
|
||||
}
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
const item = queue[currentIndex];
|
||||
const itemElement = document.getElementById(`queue-item-${currentIndex}`);
|
||||
|
||||
if (!itemElement) {
|
||||
console.error('Queue item element not found:', currentIndex);
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('ERROR', 'Queue item element not found', {
|
||||
index: currentIndex,
|
||||
itemId: `queue-item-${currentIndex}`
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(() => processImageQueue(queue, currentIndex + 1), 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update UI to processing
|
||||
itemElement.setAttribute('data-status', 'processing');
|
||||
itemElement.querySelector('.queue-status').textContent = '⏳ Generating...';
|
||||
|
||||
const progressFill = itemElement.querySelector('.queue-progress-fill');
|
||||
const progressText = itemElement.querySelector('.queue-progress-text');
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('INFO', `Processing ${item.label}`, {
|
||||
postId: item.post_id,
|
||||
type: item.type,
|
||||
device: item.device || 'N/A',
|
||||
index: item.index || 1,
|
||||
queuePosition: `${currentIndex + 1}/${queue.length}`
|
||||
});
|
||||
}
|
||||
|
||||
// Progressive loading: 50% in 7s, 75% in next 5s, then 5% every second until 95%
|
||||
let currentProgress = 0;
|
||||
let phase = 1;
|
||||
let phaseStartTime = Date.now();
|
||||
|
||||
const progressInterval = setInterval(() => {
|
||||
const elapsed = Date.now() - phaseStartTime;
|
||||
|
||||
if (phase === 1 && currentProgress < 50) {
|
||||
// Phase 1: 0% to 50% in 7 seconds (7.14% per second)
|
||||
currentProgress += 0.714;
|
||||
if (currentProgress >= 50 || elapsed >= 7000) {
|
||||
currentProgress = 50;
|
||||
phase = 2;
|
||||
phaseStartTime = Date.now();
|
||||
}
|
||||
} else if (phase === 2 && currentProgress < 75) {
|
||||
// Phase 2: 50% to 75% in 5 seconds (5% per second)
|
||||
currentProgress += 0.5;
|
||||
if (currentProgress >= 75 || elapsed >= 5000) {
|
||||
currentProgress = 75;
|
||||
phase = 3;
|
||||
phaseStartTime = Date.now();
|
||||
}
|
||||
} else if (phase === 3 && currentProgress < 95) {
|
||||
// Phase 3: 75% to 95% - 5% every second
|
||||
if (elapsed >= 1000) {
|
||||
currentProgress = Math.min(95, currentProgress + 5);
|
||||
phaseStartTime = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
progressFill.style.width = currentProgress + '%';
|
||||
progressText.textContent = Math.round(currentProgress) + '%';
|
||||
}, 100);
|
||||
|
||||
// Generate single image
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'igny8_ai_generate_single_image');
|
||||
formData.append('nonce', window.IGNY8_PAGE.nonce);
|
||||
formData.append('post_id', item.post_id);
|
||||
formData.append('type', item.type);
|
||||
formData.append('device', item.device || '');
|
||||
formData.append('index', item.index || 1);
|
||||
// Add meta box integration fields
|
||||
formData.append('image_label', item.label || '');
|
||||
formData.append('section', item.section || '');
|
||||
|
||||
fetch(window.IGNY8_PAGE.ajaxUrl, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// Stop progressive loading
|
||||
clearInterval(progressInterval);
|
||||
|
||||
if (data.success) {
|
||||
// Success - complete to 100%
|
||||
progressFill.style.width = '100%';
|
||||
progressText.textContent = '100%';
|
||||
itemElement.setAttribute('data-status', 'completed');
|
||||
itemElement.querySelector('.queue-status').textContent = '✅ Complete';
|
||||
|
||||
// Display thumbnail if image URL is available
|
||||
if (data.data?.image_url) {
|
||||
const thumbnailDiv = itemElement.querySelector('.queue-thumbnail');
|
||||
if (thumbnailDiv) {
|
||||
thumbnailDiv.innerHTML = `<img src="${data.data.image_url}" style="width: 100%; height: 100%; object-fit: cover; border-radius: 4px;" alt="Generated image">`;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✓ Image ${currentIndex + 1} generated successfully`);
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('SUCCESS', `${item.label} generated successfully`, {
|
||||
postId: item.post_id,
|
||||
attachmentId: data.data?.attachment_id,
|
||||
provider: data.data?.provider,
|
||||
queuePosition: `${currentIndex + 1}/${queue.length}`
|
||||
});
|
||||
}
|
||||
|
||||
// Process next image after short delay
|
||||
setTimeout(() => processImageQueue(queue, currentIndex + 1), 500);
|
||||
} else {
|
||||
// Error - show at 90%
|
||||
progressFill.style.width = '90%';
|
||||
progressText.textContent = 'Failed';
|
||||
itemElement.setAttribute('data-status', 'failed');
|
||||
itemElement.querySelector('.queue-status').textContent = '❌ Failed';
|
||||
|
||||
const errorDiv = itemElement.querySelector('.queue-error');
|
||||
errorDiv.textContent = data.data?.message || 'Unknown error';
|
||||
errorDiv.style.display = 'block';
|
||||
|
||||
console.error(`✗ Image ${currentIndex + 1} failed:`, data.data?.message);
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('ERROR', `${item.label} generation failed`, {
|
||||
postId: item.post_id,
|
||||
error: data.data?.message || 'Unknown error',
|
||||
queuePosition: `${currentIndex + 1}/${queue.length}`
|
||||
});
|
||||
}
|
||||
|
||||
// Continue to next image despite error
|
||||
setTimeout(() => processImageQueue(queue, currentIndex + 1), 1000);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Exception - stop progressive loading
|
||||
clearInterval(progressInterval);
|
||||
|
||||
progressFill.style.width = '90%';
|
||||
progressText.textContent = 'Error';
|
||||
itemElement.setAttribute('data-status', 'failed');
|
||||
itemElement.querySelector('.queue-status').textContent = '❌ Error';
|
||||
|
||||
const errorDiv = itemElement.querySelector('.queue-error');
|
||||
errorDiv.textContent = 'Exception: ' + error.message;
|
||||
errorDiv.style.display = 'block';
|
||||
|
||||
console.error(`✗ Image ${currentIndex + 1} exception:`, error);
|
||||
|
||||
// Log to Image Generation Debug
|
||||
if (window.addImageGenDebugLog) {
|
||||
window.addImageGenDebugLog('ERROR', `${item.label} request exception`, {
|
||||
postId: item.post_id,
|
||||
error: error.message,
|
||||
queuePosition: `${currentIndex + 1}/${queue.length}`
|
||||
});
|
||||
}
|
||||
|
||||
// Continue to next image despite error
|
||||
setTimeout(() => processImageQueue(queue, currentIndex + 1), 1000);
|
||||
});
|
||||
}
|
||||
14
igny8-ai-seo-wp-plugin/assets/shortcodes/_README.php
Normal file
14
igny8-ai-seo-wp-plugin/assets/shortcodes/_README.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
/**
|
||||
* ==============================
|
||||
* 📁 Folder Scope Declaration
|
||||
* ==============================
|
||||
* Folder: /shortcodes/
|
||||
* Purpose: All shortcode handler files (split by module)
|
||||
* Rules:
|
||||
* - Must be organized by module
|
||||
* - Each shortcode must be self-contained
|
||||
* - No cross-module dependencies
|
||||
* - Must use components for rendering
|
||||
* - Frontend-only functionality
|
||||
*/
|
||||
278
igny8-ai-seo-wp-plugin/assets/shortcodes/image-gallery.php
Normal file
278
igny8-ai-seo-wp-plugin/assets/shortcodes/image-gallery.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
/**
|
||||
* ==========================
|
||||
* 🔐 IGNY8 FILE RULE HEADER
|
||||
* ==========================
|
||||
* @file : image-gallery.php
|
||||
* @location : /assets/shortcodes/image-gallery.php
|
||||
* @type : Shortcode
|
||||
* @scope : Global
|
||||
* @allowed : Shortcode registration, frontend rendering, image display
|
||||
* @reusability : Globally Reusable
|
||||
* @notes : Image gallery shortcodes for frontend display
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display specific in-article image by ID
|
||||
*
|
||||
* Usage: [igny8-image id="desktop-1"]
|
||||
*
|
||||
* @param array $atts Shortcode attributes
|
||||
* @return string HTML output
|
||||
*/
|
||||
add_shortcode('igny8-image', function($atts) {
|
||||
$atts = shortcode_atts(['id' => ''], $atts);
|
||||
$post_id = get_the_ID();
|
||||
|
||||
if (empty($post_id)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$images = get_post_meta($post_id, '_igny8_inarticle_images', true);
|
||||
if (!is_array($images)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Display specific image by ID
|
||||
if (!empty($atts['id']) && isset($images[$atts['id']])) {
|
||||
$image_data = $images[$atts['id']];
|
||||
|
||||
// Device detection - only show if device matches
|
||||
$is_mobile = wp_is_mobile();
|
||||
$is_desktop = !$is_mobile;
|
||||
|
||||
// Check if image should be displayed based on device
|
||||
$should_display = false;
|
||||
if (strpos($atts['id'], 'desktop-') === 0 && $is_desktop) {
|
||||
$should_display = true;
|
||||
} elseif (strpos($atts['id'], 'mobile-') === 0 && $is_mobile) {
|
||||
$should_display = true;
|
||||
}
|
||||
|
||||
if (!$should_display) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$attachment_id = intval($image_data['attachment_id']);
|
||||
|
||||
if ($attachment_id > 0) {
|
||||
return wp_get_attachment_image($attachment_id, 'large', false, [
|
||||
'class' => 'igny8-inarticle-image',
|
||||
'data-image-id' => esc_attr($atts['id']),
|
||||
'data-device' => esc_attr($image_data['device']),
|
||||
'alt' => esc_attr($image_data['label'])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
/**
|
||||
* Display all in-article images
|
||||
*
|
||||
* Usage: [igny8-images]
|
||||
*
|
||||
* @param array $atts Shortcode attributes
|
||||
* @return string HTML output
|
||||
*/
|
||||
add_shortcode('igny8-images', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'device' => '', // Filter by device type (desktop/mobile)
|
||||
'size' => 'large', // Image size
|
||||
'class' => 'igny8-image-gallery' // CSS class
|
||||
], $atts);
|
||||
|
||||
$post_id = get_the_ID();
|
||||
|
||||
if (empty($post_id)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$images = get_post_meta($post_id, '_igny8_inarticle_images', true);
|
||||
if (!is_array($images) || empty($images)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$output = '<div class="' . esc_attr($atts['class']) . '">';
|
||||
$output .= '<p style="background: #f0f0f0; padding: 10px; border-left: 4px solid #0073aa; margin: 10px 0; font-weight: bold;">This is coming from shortcode</p>';
|
||||
|
||||
foreach ($images as $label => $image_data) {
|
||||
// Filter by device if specified
|
||||
if (!empty($atts['device']) && $image_data['device'] !== $atts['device']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attachment_id = intval($image_data['attachment_id']);
|
||||
|
||||
if ($attachment_id > 0) {
|
||||
$output .= wp_get_attachment_image($attachment_id, $atts['size'], false, [
|
||||
'class' => 'igny8-inarticle-image',
|
||||
'data-image-id' => esc_attr($label),
|
||||
'data-device' => esc_attr($image_data['device']),
|
||||
'alt' => esc_attr($image_data['label'])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
});
|
||||
|
||||
/**
|
||||
* Display desktop images only
|
||||
*
|
||||
* Usage: [igny8-desktop-images]
|
||||
*
|
||||
* @param array $atts Shortcode attributes
|
||||
* @return string HTML output
|
||||
*/
|
||||
add_shortcode('igny8-desktop-images', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'size' => 'large',
|
||||
'class' => 'igny8-desktop-gallery'
|
||||
], $atts);
|
||||
|
||||
return do_shortcode('[igny8-images device="desktop" size="' . $atts['size'] . '" class="' . $atts['class'] . '"]');
|
||||
});
|
||||
|
||||
/**
|
||||
* Display mobile images only
|
||||
*
|
||||
* Usage: [igny8-mobile-images]
|
||||
*
|
||||
* @param array $atts Shortcode attributes
|
||||
* @return string HTML output
|
||||
*/
|
||||
add_shortcode('igny8-mobile-images', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'size' => 'large',
|
||||
'class' => 'igny8-mobile-gallery'
|
||||
], $atts);
|
||||
|
||||
return do_shortcode('[igny8-images device="mobile" size="' . $atts['size'] . '" class="' . $atts['class'] . '"]');
|
||||
});
|
||||
|
||||
/**
|
||||
* Display image count
|
||||
*
|
||||
* Usage: [igny8-image-count]
|
||||
*
|
||||
* @param array $atts Shortcode attributes
|
||||
* @return string HTML output
|
||||
*/
|
||||
add_shortcode('igny8-image-count', function($atts) {
|
||||
$atts = shortcode_atts(['device' => ''], $atts);
|
||||
|
||||
$post_id = get_the_ID();
|
||||
|
||||
if (empty($post_id)) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
$images = get_post_meta($post_id, '_igny8_inarticle_images', true);
|
||||
if (!is_array($images)) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if (!empty($atts['device'])) {
|
||||
$count = 0;
|
||||
foreach ($images as $image_data) {
|
||||
if ($image_data['device'] === $atts['device']) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return (string) $count;
|
||||
}
|
||||
|
||||
return (string) count($images);
|
||||
});
|
||||
|
||||
/**
|
||||
* Display image gallery with responsive design
|
||||
*
|
||||
* Usage: [igny8-responsive-gallery]
|
||||
*
|
||||
* @param array $atts Shortcode attributes
|
||||
* @return string HTML output
|
||||
*/
|
||||
add_shortcode('igny8-responsive-gallery', function($atts) {
|
||||
$atts = shortcode_atts([
|
||||
'desktop_size' => 'large',
|
||||
'mobile_size' => 'medium',
|
||||
'class' => 'igny8-responsive-gallery'
|
||||
], $atts);
|
||||
|
||||
$post_id = get_the_ID();
|
||||
|
||||
if (empty($post_id)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$images = get_post_meta($post_id, '_igny8_inarticle_images', true);
|
||||
if (!is_array($images) || empty($images)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$output = '<div class="' . esc_attr($atts['class']) . '">';
|
||||
|
||||
// Desktop images
|
||||
$desktop_images = array_filter($images, function($img) {
|
||||
return $img['device'] === 'desktop';
|
||||
});
|
||||
|
||||
if (!empty($desktop_images)) {
|
||||
$output .= '<div class="igny8-desktop-images" style="display: block;">';
|
||||
foreach ($desktop_images as $label => $image_data) {
|
||||
$attachment_id = intval($image_data['attachment_id']);
|
||||
if ($attachment_id > 0) {
|
||||
$output .= wp_get_attachment_image($attachment_id, $atts['desktop_size'], false, [
|
||||
'class' => 'igny8-desktop-image',
|
||||
'data-image-id' => esc_attr($label)
|
||||
]);
|
||||
}
|
||||
}
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
// Mobile images
|
||||
$mobile_images = array_filter($images, function($img) {
|
||||
return $img['device'] === 'mobile';
|
||||
});
|
||||
|
||||
if (!empty($mobile_images)) {
|
||||
$output .= '<div class="igny8-mobile-images" style="display: none;">';
|
||||
foreach ($mobile_images as $label => $image_data) {
|
||||
$attachment_id = intval($image_data['attachment_id']);
|
||||
if ($attachment_id > 0) {
|
||||
$output .= wp_get_attachment_image($attachment_id, $atts['mobile_size'], false, [
|
||||
'class' => 'igny8-mobile-image',
|
||||
'data-image-id' => esc_attr($label)
|
||||
]);
|
||||
}
|
||||
}
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
// Add responsive CSS
|
||||
$output .= '<style>
|
||||
@media (max-width: 768px) {
|
||||
.igny8-desktop-images { display: none !important; }
|
||||
.igny8-mobile-images { display: block !important; }
|
||||
}
|
||||
@media (min-width: 769px) {
|
||||
.igny8-desktop-images { display: block !important; }
|
||||
.igny8-mobile-images { display: none !important; }
|
||||
}
|
||||
</style>';
|
||||
|
||||
return $output;
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
cluster_name,sector_id,status,keyword_count,total_volume,avg_difficulty,mapped_pages_count
|
||||
"Car Interior Accessories",1,"active",25,45000,42,0
|
||||
"Car Storage Solutions",1,"active",18,32000,38,0
|
||||
"Car Beverage Holders",1,"active",12,18000,35,0
|
||||
|
@@ -0,0 +1,4 @@
|
||||
idea_title,idea_description,content_structure,content_type,keyword_cluster_id,target_keywords,status,estimated_word_count
|
||||
"Top 10 Car Interior Accessories for 2024","A comprehensive list of the best car interior accessories available this year, including reviews and recommendations.","review","post",1,"car accessories, car storage solutions, car interior accessories","new",1200
|
||||
"How to Organize Your Car Interior Like a Pro","Step-by-step guide to organizing your car interior for maximum efficiency and comfort.","guide_tutorial","post",2,"car organization, car storage tips, car interior organization","new",1500
|
||||
"DIY Car Storage Solutions That Actually Work","Creative and practical DIY storage solutions you can make at home for your car.","guide_tutorial","post",2,"DIY car storage, car storage solutions, car organization tips","new",800
|
||||
|
@@ -0,0 +1,4 @@
|
||||
keyword,search_volume,difficulty,cpc,intent,status,sector_id,cluster_id
|
||||
"car accessories",12000,45,2.50,"commercial","unmapped",1,0
|
||||
"car storage solutions",8500,38,1.80,"informational","unmapped",1,0
|
||||
"car interior accessories",15000,52,3.20,"commercial","unmapped",1,0
|
||||
|
Reference in New Issue
Block a user