aiimatg_log('Current admin page hook: ' . $hook);
}
$plugin_version = AIIMATG_PLUGIN_VERSION;
$settings_page = 'aiimatg_toplevel_page-dashboard';
// Verify nonce if this is a form submission
$verified = false;
if (isset($_REQUEST['_wpnonce'])) {
$verified = wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])), 'aiimatg_nonce_action');
}
// Process page parameter with proper unslashing
$page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
// Always enqueue admin styles
wp_enqueue_style(
'aiimatg-admin-css',
AIIMATG_PLUGIN_URL . 'assets/css/admin.css',
array(),
$plugin_version
);
// Settings page specific styles and scripts
if ($page === 'aiimatg-settings') {
wp_enqueue_style(
'aiimatg-settings-css',
AIIMATG_PLUGIN_URL . 'assets/css/settings.css',
array(),
$plugin_version
);
wp_enqueue_script(
'aiimatg-settings-js',
AIIMATG_PLUGIN_URL . 'assets/js/settings.js',
array('jquery'),
$plugin_version,
true
);
}
// Load bulk optimization scripts only on the bulk optimization page
if ($page === 'aiimatg-bulk-optimization') {
// Enqueue bulk optimization script
wp_enqueue_script(
'aiimatg-bulk-optimization-js',
AIIMATG_PLUGIN_URL . 'assets/js/bulk-optimization.js',
array('jquery'),
$plugin_version,
true
);
// Add debugging info
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Enqueued bulk optimization script for page: ' . $page, 'info');
}
// Localize bulk optimization script
$localization_data = array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('aiimatg_nonce'),
'debug' => defined('WP_DEBUG') && WP_DEBUG,
'plugin_url' => AIIMATG_PLUGIN_URL,
'i18n' => array(
'regenerate_alt_text' => __('Regenerate Alt Text', 'ai-image-alt-text-generator'),
'generate_alt_text' => __('Generate Alt Text', 'ai-image-alt-text-generator')
),
'messages' => array(
'processing' => __('Processing images...', 'ai-image-alt-text-generator'),
'success' => __('Successfully processed!', 'ai-image-alt-text-generator'),
'error' => __('Error occurred: ', 'ai-image-alt-text-generator'),
'loading' => __('Loading images...', 'ai-image-alt-text-generator'),
'no_images' => __('No images found in the media library. Try uploading some images first.', 'ai-image-alt-text-generator'),
'no_template' => __('Template loading failed. Please refresh the page.', 'ai-image-alt-text-generator'),
'ajax_error' => __('AJAX request failed. Please check console for details.', 'ai-image-alt-text-generator')
)
);
wp_localize_script('aiimatg-bulk-optimization-js', 'AIIMATG_AJAX', $localization_data);
// Add debugging info
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Localized bulk optimization script with data: ' . json_encode(array(
'ajax_url' => admin_url('admin-ajax.php'),
'debug' => defined('WP_DEBUG') && WP_DEBUG,
'messages_count' => count($localization_data['messages'])
)));
}
// Return early since we don't need to load other scripts
return;
}
// Return early if not on a relevant page
if ($hook !== $settings_page && 'upload.php' !== $hook && 'post.php' !== $hook) {
return;
}
// Enqueue admin script
wp_enqueue_script(
'aiimatg-admin-js',
AIIMATG_PLUGIN_URL . 'assets/js/admin.js',
array('jquery'),
$plugin_version,
true
);
// Localize admin script
wp_localize_script('aiimatg-admin-js', 'AIIMATG_AJAX', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('aiimatg_nonce'),
'debug' => defined('WP_DEBUG') && WP_DEBUG,
'messages' => array(
'processing' => __('Processing images...', 'ai-image-alt-text-generator'),
'success' => __('Successfully processed!', 'ai-image-alt-text-generator'),
'error' => __('Error occurred:', 'ai-image-alt-text-generator')
)
));
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Scripts and styles enqueued successfully');
}
}
/**
* Enqueues custom script in Elementor editor
*
* @return void
*/
function enqueue_custom_script_in_elementor_editor()
{
$this->enqueue_all_scripts();
}
/**
* Enqueues all scripts and styles
*
* @return void
*/
public function enqueue_all_scripts()
{
// Verify nonce for GET parameters
$nonce = isset($_REQUEST['_wpnonce']) ? wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])), 'aiimatg_nonce') : false;
if (!wp_verify_nonce($nonce, 'aiimatg_nonce')) {
throw new \Exception('Security check failed');
}
$current_action = isset($_GET['action']) ? sanitize_text_field(wp_unslash($_GET['action'])) : '';
if (is_admin() || $current_action === 'elementor') {
$plugin_version = AIIMATG_PLUGIN_VERSION; // Matches the version in plugin header
wp_enqueue_style(
'aiimatg-admin-css',
AIIMATG_PLUGIN_URL . 'assets/css/admin.css',
array(),
$plugin_version
);
wp_enqueue_script(
'aiimatg-admin-js',
AIIMATG_PLUGIN_URL . 'assets/js/admin.js',
array('jquery'),
$plugin_version,
true
);
wp_localize_script('aiimatg-admin-js', 'AIIMATG_AJAX', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('aiimatg_nonce'),
'messages' => array(
'processing' => __('Processing images...', 'ai-image-alt-text-generator'),
'success' => __('Successfully processed!', 'ai-image-alt-text-generator'),
)
));
}
}
/**
* Call API for alt text generation
*
* @param array $images Images array to process
* @return array|false Result or false on failure
*/
public function call_api_for_alt_text($images)
{
// Get the API client
$api_client = $this->get_api_client();
if (!$api_client) {
$this->aiimatg_log('Alt Text Generator: API client not available', 'error');
return false;
}
// Start tracking time for performance logging
$start_time = microtime(true);
// Call API to generate alt text - this now returns the raw API response
$raw_result = $api_client->generate_alt_text($images);
// Log the raw API response for debugging
$this->aiimatg_log('Alt Text Generator: Raw API response: ' . json_encode($raw_result), 'debug');
// Process the API result to transform it into our standardized format
$processed_result = $api_client->process_api_result($raw_result);
// End timing and log for performance tracking
$end_time = microtime(true);
$execution_time = $end_time - $start_time;
$this->aiimatg_log('Alt Text Generator: API execution time: ' . $execution_time . ' seconds', 'info');
// Handle error case
if (!$processed_result['success']) {
$this->aiimatg_log('Alt Text Generator: API error: ' . $processed_result['message'], 'error');
// Don't throw fatal exception - return a structured error response instead
return array(
'success' => false,
'message' => $processed_result['message'],
'formatted' => array(),
'updated' => array(),
'raw_response' => isset($raw_result) ? $raw_result : null
);
}
// Return the data on success
return $processed_result;
}
/**
* Generate alt text for an image
* return array
*/
public function generate_single_alt_text()
{
try {
// Verify nonce - check both parameter names for compatibility
if (isset($_POST['_wpnonce'])) {
check_ajax_referer('aiimatg_nonce', '_wpnonce');
} elseif (isset($_POST['nonce'])) {
check_ajax_referer('aiimatg_nonce', 'nonce');
} else {
throw new \Exception('Security check failed');
}
// Log all POST data for debugging
$this->aiimatg_log('generate_single_alt_text called with POST data: ' . json_encode($_POST), 'debug');
// Get and validate image ID
$image_id = isset($_POST['image_id']) ? absint($_POST['image_id']) : 0;
// Accept both parameter names for backward compatibility
$focus_keyword = '';
if (isset($_POST['keyword']) && !empty($_POST['keyword'])) {
$focus_keyword = sanitize_text_field(wp_unslash($_POST['keyword']));
} elseif (isset($_POST['focus_keyword']) && !empty($_POST['focus_keyword'])) {
$focus_keyword = sanitize_text_field(wp_unslash($_POST['focus_keyword']));
}
if (!$image_id) {
throw new \Exception('Invalid image ID');
}
// Verify the image exists
if (!wp_get_attachment_url($image_id)) {
throw new \Exception('Image does not exist or is not accessible');
}
// Check if we have a valid API token
$access_token = get_option('aiimatg_access_token', '');
if (empty($access_token)) {
// Try to get the API client - this will return false if no token is available
$api_client = $this->get_api_client();
if (!$api_client) {
throw new \Exception('API token is not configured. Please set up your API token in the settings.');
}
}
// Note: Credit limitations are now handled server-side through the API
// No client-side credit checking per WordPress.org guidelines
// Get the image URL
$image_url = wp_get_attachment_url($image_id);
if (!$image_url) {
throw new \Exception('Could not get image URL');
}
// Check if the image URL is accessible
$headers = get_headers($image_url, 1);
if (!$headers || strpos($headers[0], '200') === false) {
$this->aiimatg_log('Image URL is not accessible: ' . $image_url, 'error');
throw new \Exception('Image URL is not accessible. Please ensure the image is publicly accessible.');
}
// Get additional context information about the image
$filename = basename($image_url);
$image_title = get_the_title($image_id);
$post_parent = wp_get_post_parent_id($image_id);
if ($post_parent) {
$context = get_post_type($post_parent);
$context_title = get_the_title($post_parent);
} else {
$context = 'none';
$context_title = '';
}
// Prepare image data for API call
$images = array(
array(
'id' => (string)$image_id,
'imageUrl' => $image_url,
'keyword' => $focus_keyword,
'filename' => $filename,
'title' => $image_title,
'context' => $context,
'contextTitle' => $context_title
)
);
$this->aiimatg_log('Alt Text Generator: Calling API for image ' . $image_id, 'info');
$this->aiimatg_log('Image data: ' . json_encode($images), 'debug');
// Make the API call
$api_result = $this->call_api_for_alt_text($images);
$this->aiimatg_log('API result: ' . json_encode($api_result), 'debug');
// Check for raw_response in case of error for more detailed debugging
if (isset($api_result['raw_response'])) {
$this->aiimatg_log('Raw API response: ' . json_encode($api_result['raw_response']), 'debug');
}
// Check for successful response format or the inconsistent format
// where success=false but there's valid data
if (!$api_result) {
throw new \Exception('Failed to generate alt text - No API response');
}
// Get detailed message from the response
$message = 'Failed to generate alt text';
if (isset($api_result['message'])) {
$message = $api_result['message'];
} else if (isset($api_result['data']) && isset($api_result['data']['message'])) {
$message = $api_result['data']['message'];
}
// Handle inconsistent response structure where success might be false
// but contains valid data with a successful message
$has_valid_data = false;
if (isset($api_result['success']) && $api_result['success'] === false) {
// Check for the inconsistent format where data.message indicates success
if (isset($api_result['data']) && is_array($api_result['data']) &&
isset($api_result['data']['message']) &&
strpos($api_result['data']['message'], 'success') !== false) {
$this->aiimatg_log('Detected inconsistent API response - success=false but message indicates success', 'warning');
$has_valid_data = true;
// Continue processing
}
} else if (isset($api_result['success']) && $api_result['success'] === true) {
$has_valid_data = true;
}
if (!$has_valid_data && empty($api_result['updated'])) {
// Special error case: The response indicates that the API was called but failed
if (isset($api_result['error']) && $api_result['error'] === 'limit_exceeded') {
$error_message = __('You may have reached your usage limit for the alt text generation service. ', 'ai-image-alt-text-generator');
} elseif (isset($api_result['data']) && is_array($api_result['data'])) {
// Check server-side error logs and add more specific error info if available
$this->aiimatg_log('API generation failed - possibly image format or URL access issue', 'error');
throw new \Exception('The API service couldn\'t generate alt text. This could be due to image format issues or the image URL not being publicly accessible.');
} else {
throw new \Exception($message);
}
}
// Check if our image was included in the updated list
$image_updated = false;
if (!empty($api_result['updated'])) {
$image_updated = in_array((string)$image_id, $api_result['updated']);
}
// If the image wasn't in the updated list but the response was successful,
// it's possible we need to check the message for success indication
if (!$image_updated && $has_valid_data) {
// We'll process anyway since the API indicated some form of success
$this->aiimatg_log('Image ID not found in updated list but response indicates success - continuing', 'warning');
} else if (!$image_updated) {
throw new \Exception('Image was not updated by the API');
}
// Get the alt text that was generated
$result = $this->process_api_result_for_image($api_result, $image_id);
if (!$result['success']) {
throw new \Exception($result['message']);
}
// Update the alt text metadata
update_post_meta($image_id, '_wp_attachment_image_alt', wp_strip_all_tags($result['alt_text']));
// Track this generation for analytics
$this->track_generation($image_id);
// Note: Credit tracking is now handled entirely server-side
// No client-side credit tracking per WordPress.org guidelines
// Return success response
wp_send_json_success(array(
'id' => (int) $image_id,
'alt_text' => esc_attr(str_replace('\"', '', $result['alt_text'])),
'message' => $result['message']
));
} catch (\Exception $e) {
$this->aiimatg_log('Alt text generation error: ' . $e->getMessage(), 'error');
wp_send_json_error(array(
'message' => $e->getMessage()
));
}
}
/**
* Get total image count for bulk generation
*
* @return void
*/
public function get_image_counts()
{
try {
// Verify nonce - check both parameter names for compatibility
if (isset($_POST['_wpnonce'])) {
check_ajax_referer('aiimatg_nonce', '_wpnonce');
} else if (isset($_POST['nonce'])) {
check_ajax_referer('aiimatg_nonce', 'nonce');
} else {
throw new \Exception('Security check failed');
}
if (!current_user_can('upload_files')) {
throw new \Exception('Permission denied');
}
$bulk_option = isset($_POST['bulk_option']) ? sanitize_text_field(wp_unslash($_POST['bulk_option'])) : 'all';
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Getting image counts for option: ' . $bulk_option);
}
$cache_key = 'aiimatg_image_counts_' . md5($bulk_option);
$cached_results = wp_cache_get($cache_key, 'aiimatg_image_counts');
if (false !== $cached_results) {
$this->aiimatg_log('Using cached image counts data');
wp_send_json_success($cached_results);
return;
}
$total_query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => 1,
'fields' => 'ids',
);
$total_query = new WP_Query($total_query_args);
$total = $total_query->found_posts;
$with_alt_query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => 1,
'fields' => 'ids',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
array(
'key' => '_wp_attachment_image_alt',
'value' => '',
'compare' => '!='
)
)
);
$with_alt_query = new WP_Query($with_alt_query_args);
$with_alt = $with_alt_query->found_posts;
$missing = $total - $with_alt;
$coverage = $total > 0 ? round(($with_alt / $total) * 100) : 0;
$count = 0;
$meta_query_missing = array(
'relation' => 'OR',
array(
'key' => '_wp_attachment_image_alt',
'compare' => 'NOT EXISTS'
),
array(
'key' => '_wp_attachment_image_alt',
'value' => '',
'compare' => '='
)
);
if ($bulk_option === 'all') {
$count = $total;
} else if ($bulk_option === 'missing') {
$count = $missing;
} else if ($bulk_option === 'products' || $bulk_option === 'woo_products') {
if (class_exists('WooCommerce')) {
// Get product IDs
$product_ids = get_posts(array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids'
));
if (!empty($product_ids)) {
$products_query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => 1,
'fields' => 'ids',
'post_parent__in' => $product_ids,
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => $meta_query_missing
);
$products_query = new WP_Query($products_query_args);
$count = $products_query->found_posts;
}
}
} else if ($bulk_option === 'content') {
// Get post/page IDs
$content_ids = get_posts(array(
'post_type' => array('post', 'page'),
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids'
));
if (!empty($content_ids)) {
$content_query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => 1,
'fields' => 'ids',
'post_parent__in' => $content_ids,
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => $meta_query_missing
);
$content_query = new WP_Query($content_query_args);
$count = $content_query->found_posts;
}
}
$product_missing = 0;
$content_missing = 0;
$woo_product_images = 0;
if (class_exists('WooCommerce')) {
// Get product IDs
$product_ids = get_posts(array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids'
));
if (!empty($product_ids)) {
$missing_product_query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => 1,
'fields' => 'ids',
'post_parent__in' => $product_ids,
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => $meta_query_missing
);
$missing_product_query = new WP_Query($missing_product_query_args);
$product_missing = $missing_product_query->found_posts;
$all_product_query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => 1,
'fields' => 'ids',
'post_parent__in' => $product_ids
);
$all_product_query = new WP_Query($all_product_query_args);
$woo_product_images = $all_product_query->found_posts;
}
}
$content_ids = get_posts(array(
'post_type' => array('post', 'page'),
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids'
));
if (!empty($content_ids)) {
$missing_content_query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => 1,
'fields' => 'ids',
'post_parent__in' => $content_ids,
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => $meta_query_missing
);
$missing_content_query = new WP_Query($missing_content_query_args);
$content_missing = $missing_content_query->found_posts;
}
$other_missing = $missing - ($product_missing + $content_missing);
if ($other_missing > 0) {
$content_missing += $other_missing; // Consider uncategorized as content
}
$result_data = array(
'count' => $count,
'stats' => array(
'total' => $total,
'with_alt' => $with_alt,
'missing' => $missing,
'coverage' => $coverage,
'product_missing' => $product_missing,
'content_missing' => $content_missing,
'woo_product_images' => $woo_product_images
),
'message' => sprintf('Found %d images to process', $count)
);
wp_cache_set($cache_key, $result_data, 'aiimatg_image_counts', 300);
wp_send_json_success($result_data);
} catch (\Exception $e) {
$this->aiimatg_log('Get image counts error: ' . $e->getMessage(), 'error');
wp_send_json_error(array(
'message' => $e->getMessage()
));
}
}
/**
* Get product post IDs
*
* @return array
*/
private function get_product_post_ids()
{
if (!class_exists('WooCommerce')) {
return array();
}
$cache_key = 'aiimatg_product_post_ids';
$cached_product_ids = wp_cache_get($cache_key);
if (false !== $cached_product_ids) {
$this->aiimatg_log('Using cached product post IDs');
return $cached_product_ids;
}
// Use WP_Query to get product IDs
$product_query_args = array(
'post_type' => 'product',
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids'
);
$product_ids = get_posts($product_query_args);
wp_cache_set($cache_key, $product_ids, 'aiimatg_post_ids', 300);
return $product_ids;
}
/**
* Get content post IDs
*
* @return array
*/
private function get_content_post_ids()
{
$cache_key = 'aiimatg_content_post_ids';
$cached_content_ids = wp_cache_get($cache_key);
if (false !== $cached_content_ids) {
$this->aiimatg_log('Using cached content post IDs');
return $cached_content_ids;
}
// Use WP_Query to get content IDs (posts and pages)
$content_query_args = array(
'post_type' => array('post', 'page'),
'post_status' => 'publish',
'posts_per_page' => -1,
'fields' => 'ids', // Only get post IDs
'orderby' => 'ID', // Consistent order
'order' => 'ASC',
);
$content_ids = get_posts($content_query_args);
// Cache the results for 5 minutes (300 seconds)
wp_cache_set($cache_key, $content_ids, 'aiimatg_post_ids', 300);
return $content_ids;
}
/**
* Get total images count
*
* @param string $mode
* @return int
*/
private function get_total_images_count($mode)
{
$cache_key = 'aiimatg_total_images_' . sanitize_key($mode);
$count = wp_cache_get($cache_key);
if (false === $count) {
// If not in cache, perform the query based on mode
switch ($mode) {
case 'missing':
$count = $this->count_images_without_alt();
break;
case 'products':
$count = $this->count_product_images_without_alt();
break;
case 'content':
$count = $this->count_content_images_without_alt();
break;
case 'woo_products':
$count = $this->count_woo_product_images();
break;
default:
$count = $this->count_all_images();
}
// Cache the results for 1 hour
wp_cache_set($cache_key, $count, 'aiimatg_plugin', HOUR_IN_SECONDS);
}
return $count;
}
/**
* Get active content image IDs
*
* @return array
*/
private function get_active_content_image_ids()
{
$cache_key = 'aiimatg_active_content_image_ids';
$image_ids = wp_cache_get($cache_key);
if (false === $image_ids) {
$image_ids = $this->fetch_active_content_image_ids();
wp_cache_set($cache_key, $image_ids, 'aiimatg_plugin', HOUR_IN_SECONDS);
}
return $image_ids;
}
/**
* Fetch active content image IDs
*
* @return array
*/
private function fetch_active_content_image_ids()
{
$query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => -1,
'fields' => 'ids',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_wp_attachment_image_alt',
'compare' => 'NOT EXISTS'
),
array(
'key' => '_wp_attachment_image_alt',
'value' => '',
'compare' => '='
)
),
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'post_tag',
'field' => 'name',
'terms' => array('aiimatg_processed'),
'operator' => 'NOT IN'
)
)
);
$image_query = new WP_Query($query_args);
$image_ids = $image_query->posts;
$content_image_ids = array();
foreach ($image_ids as $image_id) {
if ($this->is_image_used_in_content($image_id)) {
$content_image_ids[] = $image_id;
}
}
return $content_image_ids;
}
/**
* Check if image is used in content
*
* @param int $image_id
* @return bool
*/
private function is_image_used_in_content($image_id)
{
// Check featured images
$featured_posts = get_posts(array(
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_key' => '_thumbnail_id',
'meta_value' => $image_id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
'post_status' => 'publish'
));
if (!empty($featured_posts)) {
return true;
}
$content_query = new WP_Query(array(
'post_type' => array('post', 'page'),
'post_status' => 'publish',
's' => (string)$image_id,
'posts_per_page' => 1
));
return $content_query->have_posts();
}
/**
* Add generate button to image edit page
*
* @param array $form_fields
* @param object $post
* @return array
*/
public function add_generate_button($form_fields, $post)
{
if (strpos($post->post_mime_type, 'image') === 0) {
// Add focus keyword field
$form_fields['focus_keyword'] = array(
'label' => 'Keyword',
'input' => 'text',
'value' => get_post_meta($post->ID, '_aiimatg_focus_keyword', true), // Changed from _aaiimatg_focus_keyword
'helps' => 'Enter a keyword to optimize alt text for SEO'
);
$form_fields['generate_alt'] = array(
'label' => '',
'input' => 'html',
'html' => sprintf(
'
',
$post->ID
)
);
}
return $form_fields;
}
/**
* Count product images without alt
*
* @return int
*/
public function count_product_images_without_alt()
{
$cache_key = 'aiimatg_product_images_without_alt_count';
$count = wp_cache_get($cache_key);
if (false === $count) {
if (!class_exists('WooCommerce')) {
return 0;
}
$query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'relation' => 'AND',
array(
'relation' => 'OR',
array(
'key' => '_wp_attachment_image_alt',
'compare' => 'NOT EXISTS'
),
array(
'key' => '_wp_attachment_image_alt',
'value' => '',
'compare' => '='
)
),
array(
'key' => '_wc_attachment_source',
'compare' => 'EXISTS'
)
)
);
$image_query = new WP_Query($query_args);
$count = $image_query->found_posts;
wp_cache_set($cache_key, $count, 'aiimatg_plugin', HOUR_IN_SECONDS);
}
return (int) $count;
}
/**
* Count all images
*
* @return int
*/
public function count_all_images()
{
$cache_key = 'aiimatg_total_image_count';
$count = wp_cache_get($cache_key);
if (false === $count) {
$image_query = new WP_Query(array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => -1,
'fields' => 'ids'
));
$count = $image_query->found_posts;
wp_cache_set($cache_key, $count, 'aiimatg_plugin', HOUR_IN_SECONDS);
}
return (int) $count;
}
/**
* Count images without alt
*
* @return int
*/
public function count_images_without_alt()
{
$cache_key = 'aiimatg_images_without_alt_count';
$count = wp_cache_get($cache_key);
if (false === $count) {
$query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'relation' => 'OR',
array(
'key' => '_wp_attachment_image_alt',
'compare' => 'NOT EXISTS'
),
array(
'key' => '_wp_attachment_image_alt',
'value' => '',
'compare' => '='
)
)
);
$image_query = new WP_Query($query_args);
$count = $image_query->found_posts;
wp_cache_set($cache_key, $count, 'aiimatg_plugin', HOUR_IN_SECONDS);
}
return (int) $count;
}
/**
* Count content images without alt
*
* @return int
*/
public function count_content_images_without_alt()
{
// Query for images without alt text
$query_args = array(
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_status' => 'inherit',
'posts_per_page' => -1,
'fields' => 'ids',
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_wp_attachment_image_alt',
'compare' => 'NOT EXISTS'
),
array(
'key' => '_wp_attachment_image_alt',
'value' => '',
'compare' => '='
)
)
);
$image_query = new WP_Query($query_args);
$images_without_alt = $image_query->posts;
if (empty($images_without_alt)) {
return 0;
}
$count = 0;
foreach ($images_without_alt as $image_id) {
if ($this->is_image_used_in_content($image_id)) {
$count++;
}
}
return $count;
}
/**
* Track generation
*
* @param int $attachment_id
*/
private function track_generation($attachment_id)
{
if (!$attachment_id || !is_numeric($attachment_id)) {
return false;
}
$current_time = current_time('mysql');
$attachment_post = get_post($attachment_id);
if ($attachment_post) {
$attachment_post->post_modified = $current_time;
$attachment_post->post_modified_gmt = get_gmt_from_date($current_time);
wp_update_post($attachment_post);
}
update_post_meta($attachment_id, '_aiimatg_generated_date', $current_time);
$this->increment_generations_count();
$this->log_generation_event($attachment_id);
return true;
}
/**
* Log generation event
*
* @param int $attachment_id
*/
private function log_generation_event($attachment_id)
{
$logging_enabled = get_option('aiimatg_enable_generation_logging', true);
if ($logging_enabled) {
$log_entry = array(
'attachment_id' => (int) $attachment_id,
'timestamp' => current_time('mysql'),
'user_id' => get_current_user_id()
);
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Logging generation event for attachment ID ' . $attachment_id);
}
$existing_logs = get_option('aiimatg_generation_logs', array());
$existing_logs[] = $log_entry;
$existing_logs = array_slice($existing_logs, -100);
update_option('aiimatg_generation_logs', $existing_logs);
}
}
/**
* Increment the total number of alt text generations
*
* @return void
*/
private function increment_generations_count()
{
$total_generations = intval(get_option('aiimatg_total_generations', 0));
$total_generations++;
update_option('aiimatg_total_generations', $total_generations);
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Total generations incremented to ' . $total_generations);
}
}
/**
* Save credentials
* return array
*/
public function save_credentials()
{
// Verify nonce - check both parameter names for compatibility
if (isset($_POST['_wpnonce'])) {
check_ajax_referer('aiimatg_nonce', '_wpnonce');
} elseif (isset($_POST['nonce'])) {
check_ajax_referer('aiimatg_nonce', 'nonce');
} else {
wp_send_json_error(array('message' => 'Security check failed'));
return;
}
if (!current_user_can('upload_files')) {
wp_send_json_error(array('message' => 'Permission denied'));
return;
}
$token = isset($_POST['token']) ? sanitize_text_field(wp_unslash($_POST['token'])) : '';
if (empty($token)) {
wp_send_json_error(array('message' => 'Token is required'));
return;
}
update_option('aiimatg_access_token', $token);
update_option('aiimatg_api_url', AIIMATG_API_URL);
wp_send_json_success(array(
'message' => 'Credentials saved successfully'
));
}
/**
* Schedules background alt text generation for newly uploaded images.
* return void
*/
public function auto_generate_alt_text_for_attachment($attachment_id)
{
if (!get_option('aiimatg_auto_generate_enabled', 1)) {
return;
}
if (!wp_attachment_is_image($attachment_id)) {
return;
}
$existing_alt_text = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
if (!empty($existing_alt_text)) {
return;
}
// Schedule the task if it's not already scheduled
if (!wp_next_scheduled('aiimatg_process_scheduled_attachment', array($attachment_id))) {
wp_schedule_single_event(time() + 10, 'aiimatg_process_scheduled_attachment', array($attachment_id));
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Scheduled background processing for attachment ID ' . $attachment_id);
}
}
}
/**
* Add custom column to media library for alt text
* return array
*/
public function add_media_columns($columns)
{
$columns['alt_text'] = __('Alt Text', 'ai-image-alt-text-generator');
return $columns;
}
/**
* Populate custom alt text column
* return void
*/
public function populate_media_columns($column_name, $post_id)
{
if ($column_name === 'alt_text') {
$alt_text = get_post_meta($post_id, '_wp_attachment_image_alt', true);
$image_url = wp_get_attachment_image_url($post_id, 'thumbnail');
if ($alt_text) {
echo '
' . esc_html($alt_text) . '
';
$button_text = 'Regenerate Alt Text';
} else {
echo '';
$button_text = 'Generate Alt Text';
}
echo ' ';
}
}
/**
* Enqueue scripts for media library
* return void
* @param string $hook Current admin page hook
*/
public function enqueue_media_library_scripts($hook)
{
if ($hook === 'upload.php') {
wp_enqueue_script(
'aiimatg-media-library',
AIIMATG_PLUGIN_URL . 'assets/js/media-library.js',
array('jquery'),
AIIMATG_PLUGIN_VERSION,
true
);
wp_localize_script('aiimatg-media-library', 'AIIMATG_MEDIA', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('aiimatg_nonce')
));
}
}
/**
* AJAX handler for generating alt text for a single image
* return void
* This method is no longer used as it's been replaced by generate_single_alt_text
* which is registered in the constructor.
*/
private function ajax_generate_single_alt_text()
{
// This method is deprecated and should not be used
wp_send_json_error('This method is deprecated. Please use generate_single_alt_text instead.');
}
/**
* Add bulk actions to media library
* return array
* @param array $bulk_actions Existing bulk actions
* @return array Updated bulk actions
*/
public function add_media_bulk_actions($bulk_actions)
{
$bulk_actions['generate_alt_text'] = __('Generate Alt Text', 'ai-image-alt-text-generator');
return $bulk_actions;
}
/**
* Handle bulk alt text generation
* return void
*/
public function handle_media_bulk_action($redirect_to, $doaction, $post_ids)
{
if ($doaction === 'generate_alt_text') {
$processed = 0;
$failed = 0;
$skipped = 0;
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Bulk action: Processing ' . count($post_ids) . ' items for alt text generation');
}
$images_to_process = array();
foreach ($post_ids as $post_id) {
if (wp_attachment_is_image($post_id)) {
$image_url = wp_get_attachment_url($post_id);
if ($image_url) {
$images_to_process[] = array(
'id' => (string)$post_id,
'imageUrl' => $image_url,
'keyword' => '' // Optional keyword
);
}
} else {
$skipped++;
}
}
if (empty($images_to_process)) {
throw new \Exception('No valid images found');
}
$api_result = $this->call_api_for_alt_text($images_to_process);
if (!$api_result) {
throw new \Exception('Failed to generate alt text for images');
}
if ($api_result && !empty($api_result['updated'])) {
$processed = $api_result['updated'];
} else {
$failed = count($images_to_process);
}
$redirect_to = add_query_arg(
array(
'bulk_alt_text_generated' => 1,
'processed' => $processed,
'failed' => $failed,
'skipped' => $skipped
),
$redirect_to
);
add_action('admin_notices', function () use ($processed, $failed, $skipped) {
// translators: %1$d is the number of successfully processed images, %2$d is the number of failed images, %3$d is the number of skipped non-image files.
$message = sprintf(
// translators: %1$d is the number of successfully processed images, %2$d is the number of failed images, %3$d is the number of skipped non-image files.
__('Alt Text Generation Results: %1$d images processed successfully, %2$d failed, %3$d skipped (non-image files).', 'ai-image-alt-text-generator'),
$processed,
$failed,
$skipped
);
echo '
' . esc_html($message) . '
';
});
}
return $redirect_to;
}
/**
* Initialize media library customizations
*
* @return void
*/
private function init_media_library_customizations()
{
// Add custom column
add_filter('manage_media_columns', array($this, 'add_media_columns'));
add_action('manage_media_custom_column', array($this, 'populate_media_columns'), 10, 2);
// Enqueue scripts
add_action('admin_enqueue_scripts', array($this, 'enqueue_media_library_scripts'));
// AJAX actions
add_action('wp_ajax_handle_media_bulk_action', array($this, 'handle_media_bulk_action_ajax'));
// Add bulk actions
add_filter('bulk_actions-upload', array($this, 'add_media_bulk_actions'));
add_filter('handle_bulk_actions-upload', array($this, 'handle_media_bulk_action'), 10, 3);
}
/**
* Initialize Gutenberg integration
*/
private function init_gutenberg_integration()
{
add_action('enqueue_block_editor_assets', array($this, 'enqueue_gutenberg_assets'));
add_action('wp_ajax_gutenberg_generate_alt_text', array($this, 'ajax_gutenberg_generate_alt_text'));
}
/**
* Enqueue Gutenberg assets
*
* @return void
*/
public function enqueue_gutenberg_assets()
{
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Loading Gutenberg assets for Alt Text Generator');
}
$plugin_version = AIIMATG_PLUGIN_VERSION;
wp_enqueue_script(
'aiimatg-gutenberg-controls',
AIIMATG_PLUGIN_URL . 'assets/js/gutenberg.js',
array(
'wp-blocks',
'wp-element',
'wp-components',
'wp-editor',
'wp-compose',
'wp-data',
'wp-plugins',
'wp-edit-post',
'wp-i18n',
'wp-hooks'
),
$plugin_version,
true
);
wp_enqueue_style(
'aiimatg-gutenberg-styles',
AIIMATG_PLUGIN_URL . 'assets/css/gutenberg.css',
array('wp-edit-post'),
$plugin_version
);
wp_localize_script('aiimatg-gutenberg-controls', 'AIIMATG_GUTENBERG', array(
'nonce' => wp_create_nonce('aiimatg_nonce'),
'ajax_url' => admin_url('admin-ajax.php'),
'messages' => array(
'loading' => __('Loading...', 'ai-image-alt-text-generator'),
'success' => __('Alt Text Generated Successfully', 'ai-image-alt-text-generator'),
'error' => __('Failed to Generate Alt Text', 'ai-image-alt-text-generator')
)
));
}
/**
* AJAX handler for Gutenberg alt text generation
*
* @return void
*/
public function ajax_gutenberg_generate_alt_text()
{
// Verify nonce for security
if (!isset($_POST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'aiimatg_nonce')) {
wp_send_json_error(array(
'message' => 'Permission denied: Invalid security token'
));
return;
}
// Verify current user has permission to edit posts
if (!current_user_can('edit_posts')) {
wp_send_json_error(array(
'message' => 'Permission denied: You do not have sufficient permissions to edit posts.'
));
return;
}
// Log the raw POST data for debugging
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Gutenberg AJAX: Raw POST data: ' . json_encode($_POST), 'debug');
}
// Get the image ID from the AJAX request - check both parameter names for compatibility
$image_id = 0;
if (isset($_POST['image_id'])) {
$image_id = intval($_POST['image_id']);
} elseif (isset($_POST['id'])) {
$image_id = intval($_POST['id']);
} else {
$this->aiimatg_log('Gutenberg AJAX: No image_id or id found in POST data', 'error');
}
$focus_keyword = isset($_POST['focus_keyword']) ? sanitize_text_field(wp_unslash($_POST['focus_keyword'])) : '';
if (!$image_id) {
wp_send_json_error(array(
'message' => 'Invalid image ID',
'expected' => 'id or image_id parameter'
));
return;
}
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Gutenberg AJAX: Received params - id: ' . $image_id . ', focus_keyword: ' . $focus_keyword);
}
// Verify this is actually an image attachment
if (!wp_attachment_is_image($image_id)) {
wp_send_json_error(array(
'message' => 'The selected ID is not a valid image attachment',
'id' => $image_id
));
return;
}
$image_url = wp_get_attachment_url($image_id);
if (!$image_url) {
wp_send_json_error(array(
'message' => 'Could not find image URL for the provided ID: ' . $image_id,
'attachment_url' => wp_get_attachment_url($image_id),
'image_exists' => wp_attachment_is_image($image_id)
));
return;
}
$images = array(
array(
'id' => (string)$image_id,
'imageUrl' => $image_url,
'keyword' => $focus_keyword
)
);
// Call the API to generate alt text
$api_result = $this->call_api_for_alt_text($images);
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Gutenberg API Response: ' . json_encode($api_result), 'debug');
}
// Process the result
$result = $this->process_api_result_for_image($api_result, $image_id);
if (!$result['success']) {
wp_send_json_error(array(
'message' => $result['message'],
'id' => $image_id
));
return;
}
// Track this generation for analytics
$this->track_generation($image_id);
// Send success response
wp_send_json_success(array(
'id' => $image_id,
'alt_text' => esc_attr($result['alt_text']),
'message' => $result['message']
));
}
/**
* AJAX handler for getting attachment image
*
* @return void
*/
public function get_attachment_image_ajax()
{
try {
// Verify nonce
$nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
if (!wp_verify_nonce($nonce, 'aiimatg_nonce')) {
throw new \Exception('Security check failed');
}
if (!current_user_can('upload_files')) {
throw new \Exception('Permission denied');
}
$attachment_id = isset($_POST['attachment_id']) ? intval($_POST['attachment_id']) : 0;
if (!$attachment_id) {
throw new \Exception('Invalid attachment ID');
}
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Generating image HTML for attachment ID: ' . $attachment_id);
}
$this->aiimatg_log('Processing image request for ID: ' . $attachment_id, 'info');
if (!wp_attachment_is_image($attachment_id)) {
$this->aiimatg_log('Attachment is not an image or does not exist: ' . $attachment_id, 'warning');
$image_html = '
' . esc_html__('Not an image', 'ai-image-alt-text-generator') . '
' . esc_html__('Unable to retrieve credit information at this time.', 'ai-image-alt-text-generator') . '
';
echo '
' . esc_html__('Please check your API token in the settings.', 'ai-image-alt-text-generator') . '
';
echo '
';
return;
}
// Display the credit information
?>
aiimatg_log('API token validation: Invalid nonce', 'error');
wp_send_json([
'success' => false,
'data' => [
'message' => __('Security check failed', 'ai-image-alt-text-generator'),
'valid' => false
]
]);
return;
}
// Get access token from POST or current option
$access_token = '';
if (isset($_POST['token']) && !empty($_POST['token'])) {
$access_token = sanitize_text_field(wp_unslash($_POST['token']));
$this->aiimatg_log('Using token from POST: ' . substr($access_token, 0, 5) . '...', 'debug');
} else {
$access_token = get_option('aiimatg_access_token', '');
$this->aiimatg_log('Using token from options: ' . substr($access_token, 0, 5) . '...', 'debug');
}
// Update the token if received from POST
if (isset($_POST['token']) && !empty($_POST['token'])) {
update_option('aiimatg_access_token', $access_token);
$this->aiimatg_log('Token saved to options', 'debug');
}
// Create API client with the token
if (empty($access_token)) {
$this->aiimatg_log('API token is empty', 'error');
wp_send_json([
'success' => false,
'data' => [
'message' => __('API token is empty. Please enter a valid API token.', 'ai-image-alt-text-generator'),
'valid' => false
]
]);
return;
}
// Create a new API client instance with the latest token
require_once AIIMATG_PLUGIN_DIR . 'src/Aiimatg_Api_Client.php';
$api_client = new Aiimatg_Api_Client();
// Call the API to validate the token
$result = $api_client->validate_token();
$this->aiimatg_log('API token validation result: ' . json_encode($result), $result['success'] ? 'info' : 'error');
if ($result['success']) {
// Save API response data to options
if (isset($result['data'])) {
$data = $result['data'];
$this->aiimatg_log('Saving API data to options: ' . json_encode($data), 'debug');
// Note: Service limitations are handled server-side
// We don't store or check limits client-side per WordPress.org guidelines
// We may store usage statistics for display purposes only
// But will not use them to limit functionality client-side
// Save API response
update_option('aiimatg_api_response', $result);
}
// Send success response in the expected format
wp_send_json([
'success' => true,
'data' => [
'message' => $result['message'],
'valid' => true,
'api_data' => isset($result['data']) ? $result['data'] : null
]
]);
} else {
// Send error response in the expected format
wp_send_json([
'success' => false,
'data' => [
'message' => !empty($result['message']) ? $result['message'] : __('Please enter a valid API access token', 'ai-image-alt-text-generator'),
'valid' => false
]
]);
}
}
/**
* Get API client instance
*
* @return Aiimatg_Api_Client|false API client or false on failure
*/
private function get_api_client()
{
if (!$this->api_client) {
$access_token = get_option('aiimatg_access_token', '');
if (empty($access_token)) {
return false;
}
require_once AIIMATG_PLUGIN_DIR . 'src/Aiimatg_Api_Client.php';
$this->api_client = new Aiimatg_Api_Client($access_token);
}
return $this->api_client;
}
/**
* Process API result for a specific image
*
* @param array $api_result API result
* @param int $image_id Image ID
* @return array Processed result
*/
private function process_api_result_for_image($api_result, $image_id) {
// Default return value (failure case)
$result = array(
'success' => false,
'message' => 'Failed to generate alt text',
'alt_text' => ''
);
// Log the API result for debugging
$this->aiimatg_log('Processing API result for image ID: ' . $image_id, 'debug');
$this->aiimatg_log('API result: ' . json_encode($api_result), 'debug');
// Handle the response when success is false but we have a "success" message
if (isset($api_result['success']) && $api_result['success'] === false) {
if (isset($api_result['data']) && isset($api_result['data']['message'])) {
// Special case where success is false but message indicates success
if (strpos($api_result['data']['message'], 'success') !== false) {
$this->aiimatg_log('Found success message in error response - attempting to extract alt text', 'warning');
// Look for alt text in common places
$alt_text = '';
// Check common places for alt text data
if (isset($api_result['data']['altText'])) {
$alt_text = $api_result['data']['altText'];
} else if (isset($api_result['data']['alt_text'])) {
$alt_text = $api_result['data']['alt_text'];
} else if (isset($api_result['data']['data']) && is_array($api_result['data']['data'])) {
foreach ($api_result['data']['data'] as $item) {
if (isset($item['id']) && (string)$item['id'] === (string)$image_id) {
if (isset($item['altText'])) {
$alt_text = $item['altText'];
break;
}
}
}
}
// If we found alt text, return success
if (!empty($alt_text)) {
$this->aiimatg_log('Successfully extracted alt text from inconsistent response', 'info');
return array(
'success' => true,
'message' => 'Alt text generated successfully',
'alt_text' => $alt_text
);
}
} else {
// This is a genuine error message
$result['message'] = $api_result['data']['message'];
$this->aiimatg_log('API error: ' . $result['message'], 'error');
return $result;
}
}
}
// If API call failed entirely
if (!$api_result || !is_array($api_result) || empty($api_result['success']) || empty($api_result['formatted'])) {
$message = isset($api_result['message']) ? $api_result['message'] : 'API call failed';
$this->aiimatg_log('API call failed: ' . $message, 'error');
$result['message'] = $message;
return $result;
}
// Convert string ID to integer for comparison
$image_id_int = (int)$image_id;
$image_id_str = (string)$image_id;
// Check if this image was updated
if (!empty($api_result['updated']) && in_array($image_id_str, $api_result['updated'])) {
// Find the formatted data for this image
foreach ($api_result['formatted'] as $item) {
if (isset($item['id']) && ((string)$item['id'] === $image_id_str || (int)$item['id'] === $image_id_int)) {
// Found the image data
$alt_text = isset($item['altText']) ? $item['altText'] : '';
if (empty($alt_text)) {
$result['message'] = 'Generated alt text was empty';
return $result;
}
// Return success with the alt text
return array(
'success' => true,
'message' => 'Alt text generated successfully',
'alt_text' => $alt_text
);
}
}
}
// If we got here, try one more lookup approach for raw API responses
if (isset($api_result['data']) && isset($api_result['data']['data']) && is_array($api_result['data']['data'])) {
foreach ($api_result['data']['data'] as $item) {
if (isset($item['id']) && ((string)$item['id'] === $image_id_str || (int)$item['id'] === $image_id_int)) {
// Found the image data directly in the nested structure
$alt_text = isset($item['altText']) ? $item['altText'] : '';
if (!empty($alt_text)) {
// Return success with the alt text
return array(
'success' => true,
'message' => 'Alt text generated successfully',
'alt_text' => $alt_text
);
}
}
}
}
// If we got here, the image was not found in the results
$result['message'] = 'Image ID not found in API response';
return $result;
}
/**
* Processes an attachment for alt text generation as a scheduled background task.
* @param int $attachment_id The ID of the attachment to process.
* @return void
*/
public function process_scheduled_attachment_alt_text($attachment_id)
{
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing START for attachment ID ' . $attachment_id);
}
// Double-check conditions, as settings might have changed since scheduling
if (!get_option('aiimatg_auto_generate_enabled', 1)) {
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing CANCELLED for attachment ID ' . $attachment_id . ' - auto-generate disabled.');
}
return;
}
if (!wp_attachment_is_image($attachment_id)) {
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing CANCELLED for attachment ID ' . $attachment_id . ' - not an image.');
}
return;
}
$existing_alt_text = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
if (!empty($existing_alt_text)) {
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing CANCELLED for attachment ID ' . $attachment_id . ' - alt text already exists.');
}
return;
}
$image_url = wp_get_attachment_url($attachment_id);
if (!$image_url) {
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing FAILED for attachment ID ' . $attachment_id . ' - could not get image URL.');
}
return;
}
$images = array(array(
'id' => (string)$attachment_id,
'imageUrl' => $image_url,
'keyword' => '' // Optional keyword, can be enhanced later if needed
));
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing - Calling API for attachment ID ' . $attachment_id);
}
$api_result = $this->call_api_for_alt_text($images);
if (!$api_result) {
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing FAILED for attachment ID ' . $attachment_id . ' - API call failed or returned no result.');
}
return;
}
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing - API result for ' . $attachment_id . ': ' . json_encode($api_result));
}
$result = $this->process_api_result_for_image($api_result, $attachment_id);
if (!$result['success']) {
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing FAILED for attachment ID ' . $attachment_id . ' - processing API result failed: ' . $result['message']);
}
return;
}
if (defined('WP_DEBUG') && WP_DEBUG) {
$this->aiimatg_log('Alt Text Generator: Background processing SUCCESS for attachment ID ' . $attachment_id);
}
$this->track_generation($attachment_id);
}
}