alttextlab_attachment = new AltTextLab_Attachment();
$this->wc_integration = new AltTextLab_WC_Integration();
$this->attachment_helper = new AltTextLab_Attachment_Helper();
$this->assets_storage = new AltTextLab_Storage();
}
/**
* Processes cleanup when WordPress posts are removed.
*
*/
public function on_post_deleted($post_id) {
$this->assets_storage->remove_alttextlab_asset($post_id);
}
/**
* Registers ALT text generation option in the bulk actions dropdown menu
*
*/
public function add_bulk_select_action( $actions ) {
$actions[ 'alttextlab_options' ] = esc_html__( '↓ AltTextLab', 'alttextlab' );
$actions[ 'alttextlab_generate_alt' ] = esc_html__( 'Generate alt texts', 'alttextlab' );
$actions[ 'alttextlab_sync_alt' ] = esc_html__( 'Sync alt text', 'alttextlab' );
return $actions;
}
/**
* Executes the selected bulk operation on multiple items
*
*/
public function bulk_select_action_handler( $redirect, $do_action, $items ) {
if ( $do_action === 'alttextlab_sync_alt' ) {
$redirect = remove_query_arg( array( 'alttextlab_sync_alt' ), $redirect );
$batch_id = uniqid();
set_transient( 'alttextlab_sync_alt_selected_posts_' . $batch_id, array_map( 'intval', $items ), 2048 );
set_transient( 'alttextlab_sync_alt_redirect_' . $batch_id, $redirect, 2048 );
return add_query_arg(
array(
'page' => 'alttextlab-sync-alt-in-posts',
'alttextlab_action' => 'sync-selected',
'alttextlab_batch_id' => $batch_id,
),
admin_url( 'admin.php' )
);
}
// Bail early if action is not alttextlab_generate_alt
if ( $do_action !== 'alttextlab_generate_alt' ) {
return $redirect;
}
// remove the query arg from URL because we do not need them anymore
$redirect = remove_query_arg(
array( 'alttextlab_generate_alt' ),
$redirect
);
$batch_id = uniqid();
$all_media_ids = [];
// Extract all media file IDs from the selected posts
foreach ($items as $post_id) {
$media_ids = $this->get_media_ids_from_post($post_id);
$all_media_ids = array_merge($all_media_ids, $media_ids);
}
// Writing to transient storage
set_transient('alttextlab_bulk_select_generate_' . $batch_id, array_unique($all_media_ids), 2048);
set_transient('alttextlab_bulk_select_generate_redirect_' . $batch_id, $redirect, 2048);
return admin_url('admin.php?page=alttextlab-bulk-generate&alttextlab_action=bulk-select-generate&alttextlab_batch_id=' . $batch_id . '&alttextlab_from_attachments=1&alttextlab_mode=all');
return $redirect;
}
/**
* Updates alt text for product images
*
*/
public function update_product_images( $post_id, $overwrite, $keywords, $is_ajax ) {
$metadata = get_post_meta( $post_id );
if ( empty($metadata) ) {
return false;
}
// Create list of image IDs to process (featured image + gallery images):
$image_attachment_ids = [];
if ( array_key_exists('_thumbnail_id', $metadata) && is_array($metadata['_thumbnail_id']) ) {
array_push( $image_attachment_ids, $metadata['_thumbnail_id'][0] );
}
if ( array_key_exists('_product_image_gallery', $metadata) && is_array($metadata['_product_image_gallery']) ) {
$gallery_images = $metadata['_product_image_gallery'][0];
$image_attachment_ids = array_merge( $image_attachment_ids, explode(",", $gallery_images) );
}
// Process each image attachment:
// Check if there are any images
if ( empty($image_attachment_ids) ) {
if ( $is_ajax ) {
// Set a transient to show a success notice after page reload
set_transient( 'alttextlab_enrich_post_content_success', __( '[AltTextLab] No images were found to update.', 'alttextlab' ), 60 );
wp_send_json_success();
}
return true;
}
$total_images_found = count($image_attachment_ids);
$num_alttext_generated = 0;
$no_credits = false;
$api_key_is_invalid = false;
$monthly_limit = false;
foreach ( $image_attachment_ids as $attachment_id ) {
$alt_text = $this->get_attachment_alt_for_post_language( $attachment_id, $post_id );
$should_generate = false;
// If the alt text is empty or need to overwrite, generate it:
if ( $overwrite || empty( $alt_text ) ) {
$should_generate = true;
$product_data = AltTextLab_Utility::has_woocommerce() ? $this->wc_integration->get_product_info($attachment_id, $post_id) : [];
$alt_text = $this->alttextlab_attachment->generate_alt( $attachment_id, null, array( 'keywords' => $keywords, 'explicit_post_id' => $post_id, 'ecommerce' => $product_data ) );
}
// Check if generate_alt returned false or an error
if ( empty($alt_text) || ! is_string( $alt_text ) ) {
continue;
}
elseif ($alt_text === self::ERROR_INVALID_API_KEY) {
$api_key_is_invalid = true;
break;
}
else if ( $alt_text === self::ERROR_NO_CREDITS ) {
$no_credits = true;
break;
}
else if ( $alt_text === self::ERROR_MONTHLY_LIMIT ) {
$monthly_limit = true;
break;
}
else if ( $should_generate ) {
$num_alttext_generated = $num_alttext_generated + 1;
}
}
if ( $is_ajax ) {
$success_msg = $this->return_success_message($api_key_is_invalid, $no_credits, $monthly_limit, $total_images_found, $num_alttext_generated);
set_transient( 'alttextlab_enrich_post_content_success', $success_msg, 60 );
// Return success
wp_send_json_success();
}
return array(
'status' => 'success',
'total_images_found' => $total_images_found,
'num_alttext_generated' => $num_alttext_generated
);
}
/**
* Function get ids of attachment images from different abstract entity
*
*/
private function get_media_ids_from_post($post_id) {
$post = get_post($post_id);
if (!$post) {
return [];
}
//Get images from post content (both embedded and external)
$content = $post->post_content;
$img_src_attr = get_option('alttextlab_refresh_src_attr', 'src');
// Modern WP (6.2+) HTML processor
if (version_compare(get_bloginfo('version'), '6.2') >= 0) {
$media_ids = $this->collect_images_with_tag_processor($content, $img_src_attr, $post_id);
}
else {
$media_ids = $this->collect_images_with_regex($content, $img_src_attr, $post_id);
}
// Get WooCommerce product images (if applicable)
if ($post->post_type === 'product' && class_exists('WooCommerce')) {
if ($thumbnail_id = get_post_thumbnail_id($post_id)) {
$media_ids[] = $thumbnail_id;
}
$gallery_ids = get_post_meta($post_id, '_product_image_gallery', true);
if (!empty($gallery_ids)) {
$media_ids = array_merge($media_ids, explode(',', $gallery_ids));
}
$media_ids = array_filter(array_unique(array_map('intval', $media_ids)));
$media_ids = array_filter($media_ids, function($id) {
return get_post_status($id) !== false;
});
}
// Return unique IDs only
return array_unique(array_filter(array_map('intval', $media_ids)));
}
/**
* Extracts image attachment IDs from post content using WP_HTML_Tag_Processor (WordPress 6.2+)
*
*/
private function collect_images_with_tag_processor($content, $img_src_attr, $post_id)
{
$media_ids = [];
$tags = new WP_HTML_Tag_Processor($content);
while ($tags->next_tag('img')) {
$img_url = $img_url_original = $tags->get_attribute( $img_src_attr );
if (empty($img_url)) continue;
// Normalize URL
if ( substr($img_url, 0, 1) == "/" ) {
$img_url = $img_url_original = home_url() . $img_url;
}
if ( strpos( $img_url, home_url() ) === 0 ) {
$img_url_original = preg_replace( '/-\d+x\d+(?=\.[a-zA-Z]{3,4}$)/', '', $img_url );
}
if ( substr($img_url_original, 0, 2) == "//" ) {
$img_url_original = "https:" . $img_url_original;
}
// Get attachment ID
$attachment_id = $this->attachment_helper->lookup_attachment_id($img_url_original, $post_id);
$attachment_id = $attachment_id ?? $this->attachment_helper->lookup_attachment_id($img_url_original);
if ($attachment_id) {
$media_ids[] = $attachment_id;
}
}
return $media_ids;
}
/**
* Extracts image attachment IDs from post content using regex pattern matching
*
*/
private function collect_images_with_regex($content, $img_src_attr, $post_id)
{
$media_ids = [];
$src_regex = sprintf('/
]*?>/i', $img_src_attr);
preg_match_all($src_regex, $content, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$img_url = $match[1];
if (isset($img_url) && substr($img_url, 0, 1) == "/") {
$img_url = home_url() . $img_url;
}
if (strpos($img_url, home_url()) === 0) {
$img_url = preg_replace('/-\d+x\d+(?=\.[a-zA-Z]{3,4}$)/', '', $img_url);
}
if (substr($img_url, 0, 2) == "//") {
$img_url = "https:" . $img_url;
}
$attachment_id = $this->attachment_helper->lookup_attachment_id($img_url, $post_id)
?: $this->attachment_helper->lookup_attachment_id($img_url);
if ($attachment_id) {
$media_ids[] = $attachment_id;
}
}
return $media_ids;
}
/**
* Register bulk action for post types
*
*/
public function register_bulk_action() {
$post_types = array( 'post', 'page' );
if ( AltTextLab_Utility::has_woocommerce() ) {
array_push($post_types, 'product'); // Add WooCommerce products if available
}
$post_types = apply_filters( 'alttextlab_bulk_action_post_types', $post_types ); // Allow user-defined custom types
foreach ($post_types as $post_type) {
add_filter( "bulk_actions-edit-{$post_type}", [$this, 'add_bulk_select_action'], 100, 1 );
add_filter( "handle_bulk_actions-edit-{$post_type}", [$this, 'bulk_select_action_handler'], 100, 3 );
}
}
/**
* Collect list of the post id.
*
*/
public function collect_list_enrich_post_id( $post_id = null, $overwrite = false, $process_external = false, $keywords = [] ) {
$is_ajax = $this->collect_list_enrich_post_id_is_ajax($post_id, $overwrite, $process_external);
$post_id_array = [];
$post = get_post( $post_id );
// Check if post exists
if ( $post === null ) {
if ($is_ajax){
wp_send_json_error( array(
'message' => __( 'Post not found.', 'alttextlab' ),
'total_count' => 0,
) );
}
return [];
} elseif ( $post->post_type === 'product' && AltTextLab_Utility::has_woocommerce() ) {
$post_id_array['PRODUCT'] = $this->collect_product_images_id( $post_id, $overwrite );
}
$content = $post->post_content;
// Check if content is empty
if ( empty( $content ) ) {
if ( $post_id_array['PRODUCT'] ) {
$empty_content_response = array(
'status' => 'success',
'total_count' => count( $post_id_array['PRODUCT']),
);
set_transient('alttextlab_enric_post_generate_' . $post_id, $post_id_array, 2048);
if($is_ajax){
return wp_send_json_error($empty_content_response);
}else{
return $empty_content_response;
}
} elseif ( $is_ajax ) {
return wp_send_json_error( array(
'message' => __( 'Content is empty.', 'alttextlab' ),
'total_count' => 0,
) );
}
return array(
'total_count' => 0
);
}
$img_src_attr = get_option( 'alttextlab_refresh_src_attr', 'src' );
if ( version_compare( get_bloginfo( 'version' ), '6.2') >= 0 ) {
$tags = new WP_HTML_Tag_Processor( $content );
while ( $tags->next_tag( 'img' ) ) {
$img_url = $img_url_original = $tags->get_attribute( $img_src_attr );
if ( empty($img_url) ) {
continue;
}
// If relative path, convert to full URL:
if ( substr($img_url, 0, 1) == "/" ) {
$img_url = $img_url_original = home_url() . $img_url;
}
// Remove the dimensions from the URL to get the URL of the original image,
// only if the image is hosted on the same site
if ( strpos( $img_url, home_url() ) === 0 ) {
$img_url_original = preg_replace( '/-\d+x\d+(?=\.[a-zA-Z]{3,4}$)/', '', $img_url );
}
// Prepend protocol if missing:
if ( substr($img_url_original, 0, 2) == "//" ) {
$img_url_original = "https:" . $img_url_original;
}
// Get the attachment ID from the image URL
$attachment_id = $this->attachment_helper->lookup_attachment_id($img_url_original, $post_id);
$attachment_id = $attachment_id ?? $this->attachment_helper->lookup_attachment_id($img_url_original);
if ( $attachment_id ) {
$alt_text = $this->get_attachment_alt_for_post_language( $attachment_id, $post_id );
// If the alt text is empty, generate it
if ( $overwrite || empty( $alt_text ) ) {
$post_id_array['POST'][] = ['type'=>'ID', 'value' => $attachment_id];
}
} elseif ( $process_external ) {
// Extract alt text from the image tag
$alt_text = trim( $tags->get_attribute( 'alt' ) ) ?? '';
if ( $overwrite || empty( $alt_text ) ) {
$post_id_array['POST'][] = ['type'=>'URL', 'value' => $img_url_original];
}
}
}
} else {
$src_regex = sprintf('/
]*?>/i', $img_src_attr);
$updated_content = preg_replace_callback(
$src_regex,
function( $matches ) use ( $overwrite, $process_external, $keywords, &$total_images_found, &$num_alttext_generated, &$no_credits, &$post_id_array, $attachment_helper) {
$img_tag = $matches[0];
$img_url = $img_url_original = $matches[2]; // The src URL is captured in the second group.
$total_images_found = $total_images_found + 1;
// If relative path, convert to full URL:
if ( isset($img_url) && substr($img_url, 0, 1) == "/" ) {
$img_url = $img_url_original = home_url() . $img_url;
}
// Remove the dimensions from the URL to get the URL of the original image,
// only if the image is hosted on the same site
if ( strpos( $img_url, home_url() ) === 0 ) {
$img_url_original = preg_replace( '/-\d+x\d+(?=\.[a-zA-Z]{3,4}$)/', '', $img_url );
}
// Prepend protocol if missing:
if ( substr($img_url_original, 0, 2) == "//" ) {
$img_url_original = "https:" . $img_url_original;
}
// Get the attachment ID from the image URL
$attachment_id = $attachment_helper->lookup_attachment_id($img_url_original, $post_id);
$attachment_id = $attachment_id ?? $attachment_helper->lookup_attachment_id($img_url_original);
$alt_text = false;
if ( $attachment_id ) {
$alt_text = $this->get_attachment_alt_for_post_language( $attachment_id, $post_id );
// If the alt text is empty, generate it
if ( $overwrite || empty( $alt_text ) ) {
$post_id_array['POST'][] = ['type'=>'ID', 'value' => $attachment_id];
}
} elseif ( $process_external ) {
// Extract alt text from the image tag
preg_match('/alt="([^"]*)"/i', $img_tag, $matches);
$alt_text = trim( $matches[1] ) ?? '';
if ( $overwrite || empty( $alt_text ) ) {
$post_id_array['POST'][] = ['type'=>'URL', 'value' => $img_url_original];
}
}
// Check if generate_alt returned false or an error
if ( empty( $alt_text ) || ! is_string( $alt_text ) ) {
return $img_tag;
}
if ( false === strpos( $img_tag, ' alt=' ) ) {
// If there's no alt attribute, add one
return str_replace( '
'success',
'total_count' => count($post_id_array['POST'] ? : []) + count($post_id_array['PRODUCT']? : []),
];
if ($is_ajax) {
wp_send_json_success($response);
} else {
return $response;
}
}
/**
* AJAX: return next batch of post IDs that contain images (for Sync Alt in Posts).
*
*/
public function ajax_sync_posts_batch() {
check_ajax_referer( 'alttextlab_sync_posts_batch', 'security' );
$last_post_id = isset( $_REQUEST['last_post_id'] ) ? absint( $_REQUEST['last_post_id'] ) : 0;
$per_page = max( 1, min( 10, (int) ( $_REQUEST['posts_per_page'] ?? 2 ) ) );
$batch_id = isset( $_REQUEST['batch_id'] ) ? sanitize_text_field( $_REQUEST['batch_id'] ) : '';
$sync_selected = ! empty( $_REQUEST['sync_selected'] );
if ( $sync_selected && $batch_id !== '' ) {
$selected_ids = get_transient( 'alttextlab_sync_alt_selected_posts_' . $batch_id );
if ( ! is_array( $selected_ids ) ) {
$selected_ids = array();
}
$selected_ids = array_values( array_filter( $selected_ids, function( $id ) use ( $last_post_id ) {
return $id > $last_post_id;
} ) );
sort( $selected_ids, SORT_NUMERIC );
$chunk = array_slice( $selected_ids, 0, $per_page );
$posts = array_map( function( $id ) { return array( 'id' => (int) $id ); }, $chunk );
$recursive = count( $chunk ) > 0;
$new_last = $chunk ? (int) end( $chunk ) : $last_post_id;
if ( count( $chunk ) === 0 ) {
delete_transient( 'alttextlab_sync_alt_selected_posts_' . $batch_id );
delete_transient( 'alttextlab_sync_alt_redirect_' . $batch_id );
}
wp_send_json( array(
'status' => 'success',
'posts' => $posts,
'recursive' => $recursive,
'last_post_id' => $new_last,
) );
}
global $wpdb;
$post_types = array( 'post', 'page' );
if ( AltTextLab_Utility::has_woocommerce() ) {
$post_types[] = 'product';
}
$post_types = apply_filters( 'alttextlab_sync_alt_post_types', $post_types );
$placeholders = implode( ',', array_fill( 0, count( $post_types ), '%s' ) );
$query = $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts}
WHERE ID > %d
AND post_status IN ( 'publish', 'draft', 'private' )
AND post_type IN ($placeholders)
AND post_content LIKE %s
ORDER BY ID ASC
LIMIT %d",
array_merge( array( $last_post_id ), $post_types, array( '%
get_col( $query );
$posts = array_map( function( $id ) { return array( 'id' => (int) $id ); }, $ids );
$recursive = count( $posts ) > 0;
$new_last = $ids ? (int) end( $ids ) : $last_post_id;
wp_send_json( array(
'status' => 'success',
'posts' => $posts,
'recursive' => $recursive,
'last_post_id' => $new_last,
) );
}
/**
* AJAX: process one post — find img tags, set alt from media library, update post.
*/
public function ajax_sync_posts_process_post() {
check_ajax_referer( 'alttextlab_sync_posts_process', 'security' );
$post_id = isset( $_REQUEST['post_id'] ) ? absint( $_REQUEST['post_id'] ) : 0;
if ( ! $post_id ) {
wp_send_json( array( 'status' => 'error', 'message' => __( 'Invalid post ID', 'alttextlab' ), 'process_count' => 0, 'success_count' => 0, 'images_updated' => 0 ) );
}
$post = get_post( $post_id );
if ( ! $post || empty( $post->post_content ) ) {
wp_send_json( array( 'status' => 'success', 'process_count' => 1, 'success_count' => 0, 'images_updated' => 0 ) );
}
$only_missing = ! empty( $_REQUEST['only_missing'] );
$images_updated = 0;
$updated_content = $this->sync_alt_in_post_content( $post->post_content, $post_id, $only_missing, $images_updated );
$content_changed = ( $updated_content !== $post->post_content );
if ( $content_changed ) {
wp_update_post( array(
'ID' => $post_id,
'post_content' => $updated_content,
) );
}
$success_count = ( $content_changed || $images_updated > 0 ) ? 1 : 0;
wp_send_json( array( 'status' => 'success', 'process_count' => 1, 'success_count' => $success_count, 'images_updated' => $images_updated ) );
}
/**
* In post content, for each img: resolve attachment (by wp-image-ID class or URL), get alt from media library, set/replace alt. No API calls.
*
*/
private function sync_alt_in_post_content( $content, $post_id, $only_missing = false, &$images_updated = 0 ) {
$img_src_attr = get_option( 'alttextlab_refresh_src_attr', 'src' );
if ( version_compare( get_bloginfo( 'version' ), '6.2', '>=' ) && class_exists( 'WP_HTML_Tag_Processor' ) ) {
return $this->sync_alt_in_post_content_tag_processor( $content, $post_id, $img_src_attr, $only_missing, $images_updated );
}
return $this->sync_alt_in_post_content_regex( $content, $post_id, $img_src_attr, $only_missing, $images_updated );
}
private function sync_alt_in_post_content_tag_processor( $content, $post_id, $img_src_attr, $only_missing = false, &$images_updated = 0 ) {
$tags = new WP_HTML_Tag_Processor( $content );
while ( $tags->next_tag( 'img' ) ) {
if ( $only_missing ) {
$current_alt = $tags->get_attribute( 'alt' );
if ( $current_alt !== null && trim( (string) $current_alt ) !== '' ) {
continue;
}
}
$alt_text = $this->get_alt_for_img_in_post( $tags, $post_id, $img_src_attr );
if ( $alt_text !== null && $alt_text !== '' ) {
$tags->set_attribute( 'alt', $alt_text );
++$images_updated;
}
}
return $tags->get_updated_html();
}
/**
* Returns the WPML language code for a post.
*/
private function get_wpml_post_language( $post_id ) {
if ( ! AltTextLab_Utility::is_wpml_active() || ! $post_id ) {
return null;
}
$language_details = apply_filters( 'wpml_post_language_details', null, $post_id );
if ( ! is_array( $language_details ) || empty( $language_details['language_code'] ) ) {
return null;
}
return (string) $language_details['language_code'];
}
/**
* Returns the attachment matching the post language.
*/
private function get_attachment_for_post_language( $attachment_id, $post_id ) {
$attachment_id = (int) $attachment_id;
if ( ! $attachment_id || get_post_type( $attachment_id ) !== 'attachment' ) {
return 0;
}
if ( ! AltTextLab_Utility::is_wpml_active() ) {
return $attachment_id;
}
$post_lang = $this->get_wpml_post_language( $post_id );
if ( ! $post_lang ) {
return $attachment_id;
}
$translated_id = apply_filters( 'wpml_object_id', $attachment_id, 'attachment', false, $post_lang );
if ( $translated_id && (int) $translated_id > 0 && get_post_type( $translated_id ) === 'attachment' && get_post_status( $translated_id ) !== 'trash' ) {
return (int) $translated_id;
}
return $attachment_id;
}
/**
* Returns the attachment alt text for the post language.
*/
private function get_attachment_alt_for_post_language( $attachment_id, $post_id ) {
$localized_attachment_id = $this->get_attachment_for_post_language( $attachment_id, $post_id );
if ( ! $localized_attachment_id ) {
return null;
}
$alt = get_post_meta( $localized_attachment_id, '_wp_attachment_image_alt', true );
if ( is_string( $alt ) && $alt !== '' ) {
return $alt;
}
if ( (int) $localized_attachment_id !== (int) $attachment_id ) {
$fallback_alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
if ( is_string( $fallback_alt ) && $fallback_alt !== '' ) {
return $fallback_alt;
}
}
return null;
}
private function get_alt_for_img_in_post( $tags, $post_id, $img_src_attr ) {
$class = $tags->get_attribute( 'class' );
if ( $class && preg_match( '/\bwp-image-(\d+)\b/', $class, $m ) ) {
$attachment_id = (int) $m[1];
if ( get_post_type( $attachment_id ) === 'attachment' ) {
$alt = $this->get_attachment_alt_for_post_language( $attachment_id, $post_id );
if ( is_string( $alt ) && $alt !== '' ) {
return $alt;
}
}
}
$img_url = $tags->get_attribute( $img_src_attr );
if ( empty( $img_url ) ) {
return null;
}
if ( substr( $img_url, 0, 1 ) === '/' ) {
$img_url = home_url() . $img_url;
}
if ( strpos( $img_url, home_url() ) === 0 ) {
$img_url = preg_replace( '/-\d+x\d+(?=\.[a-zA-Z]{3,4}$)/', '', $img_url );
}
if ( substr( $img_url, 0, 2 ) === '//' ) {
$img_url = 'https:' . $img_url;
}
$attachment_id = $this->attachment_helper->lookup_attachment_id( $img_url, $post_id )
?: $this->attachment_helper->lookup_attachment_id( $img_url );
if ( ! $attachment_id ) {
return null;
}
$alt = $this->get_attachment_alt_for_post_language( $attachment_id, $post_id );
return ( is_string( $alt ) && $alt !== '' ) ? $alt : null;
}
private function sync_alt_in_post_content_regex( $content, $post_id, $img_src_attr, $only_missing = false, &$images_updated = 0 ) {
$attachment_helper = $this->attachment_helper;
$regex = sprintf( '/
]*)(%s="([^"]*)")([^>]*)>/i', preg_quote( $img_src_attr, '/' ) );
return preg_replace_callback( $regex, function ( $matches ) use ( $attachment_helper, $post_id, $img_src_attr, $only_missing, &$images_updated ) {
$before_src = $matches[1];
$img_url = $matches[3];
$after_src = $matches[4];
$full_tag = $matches[0];
if ( $only_missing && preg_match( '/\balt\s*=\s*["\']([^"\']*)["\']/i', $full_tag, $alt_m ) && trim( $alt_m[1] ) !== '' ) {
return $full_tag;
}
if ( substr( $img_url, 0, 1 ) === '/' ) {
$img_url = home_url() . $img_url;
}
if ( strpos( $img_url, home_url() ) === 0 ) {
$img_url = preg_replace( '/-\d+x\d+(?=\.[a-zA-Z]{3,4}$)/', '', $img_url );
}
if ( substr( $img_url, 0, 2 ) === '//' ) {
$img_url = 'https:' . $img_url;
}
$attachment_id = null;
if ( preg_match( '/\bwp-image-(\d+)\b/', $before_src . $after_src, $m ) ) {
$aid = (int) $m[1];
if ( get_post_type( $aid ) === 'attachment' ) {
$attachment_id = $aid;
}
}
if ( ! $attachment_id ) {
$attachment_id = $attachment_helper->lookup_attachment_id( $img_url, $post_id )
?: $attachment_helper->lookup_attachment_id( $img_url );
}
if ( ! $attachment_id ) {
return $full_tag;
}
$alt = $this->get_attachment_alt_for_post_language( $attachment_id, $post_id );
if ( ! is_string( $alt ) || $alt === '' ) {
return $full_tag;
}
++$images_updated;
$esc_alt = esc_attr( $alt );
if ( preg_match( '/\balt\s*=\s*["\'][^"\']*["\']/i', $full_tag ) ) {
return preg_replace( '/\balt\s*=\s*["\'][^"\']*["\']/i', 'alt="' . $esc_alt . '"', $full_tag );
}
return str_replace( '
get_var( $wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->posts}
WHERE post_status IN ( 'publish', 'draft', 'private' )
AND post_type IN ($placeholders)
AND post_content LIKE %s",
array_merge( $post_types, array( '%
get_attachment_alt_for_post_language( $attachment_id, $post_id );
// If the alt text is empty or need to overwrite, generate it:
if ( $overwrite || empty( $alt_text ) ) {
$product_id_array[] = $attachment_id;
}
}
return $product_id_array;
}
/**
* Handles AJAX request for bulk alt text generation
*
*/
public function post_bulk_generate_alt_text_ajax()
{
check_ajax_referer('alttextlab_security_enrich_post_generate', 'security');
$post_id = intval($_REQUEST['post_id'] ?? 0);
$keywords = ( isset( $_REQUEST['keywords'] ) && is_array( $_REQUEST['keywords'] ) ) ? array_map( 'sanitize_text_field', $_REQUEST['keywords'] ) : [];
$recursive = true;
$images_successful_current = 0;
$images_successful_general = intval($_REQUEST['count_successful_images'] ?? 0);
$images_total = intval($_REQUEST['count_total_images'] ?? 0);
$images_to_update_transient = get_transient('alttextlab_enric_post_generate_' . $post_id);
if (empty($images_to_update_transient) || count($images_to_update_transient) == 0) {
$success_msg = sprintf( __('[AltTextLab] No images to process.', 'alttextlab' ) );
set_transient( 'alttextlab_enrich_post_content_success', $success_msg, 60 );
$recursive = false;
wp_send_json(array(
'status' => 'success',
'message' => __('No images to process.', 'alttextlab'),
'success_count' => $images_successful_current,
'recursive' => $recursive,
));
}
if (!empty($images_to_update_transient['PRODUCT'])) {
$attachment_id = $images_to_update_transient['PRODUCT'][0];
$product_data = AltTextLab_Utility::has_woocommerce() ? $this->wc_integration->get_product_info($attachment_id, $post_id) : [];
$response = $this->alttextlab_attachment->generate_alt( $attachment_id, null, array( 'keywords' => $keywords, 'explicit_post_id' => $post_id, 'ecommerce' => $product_data ) );
unset($images_to_update_transient['PRODUCT'][0]);
$images_to_update_transient['PRODUCT'] = array_values($images_to_update_transient['PRODUCT']);
}
elseif (!empty($images_to_update_transient['POST'])) {
$attachment_type = $images_to_update_transient['POST'][0]['type'];
$attachment_value = $images_to_update_transient['POST'][0]['value'];
if ($attachment_type === 'ID'){
$response = $this->alttextlab_attachment->generate_alt($attachment_value, null , array( 'keywords' => $keywords, 'explicit_post_id' => $post_id ) );
}else{
$response = $this->alttextlab_attachment->generate_alt(null, $attachment_value, array( 'keywords' => $keywords ) );
}
unset($images_to_update_transient['POST'][0]);
$images_to_update_transient['POST'] = array_values($images_to_update_transient['POST']);
}
if ($response === self::ERROR_NO_CREDITS) {
$success_msg = sprintf( __(self::MSG_NO_CREDITS, 'alttextlab' ) );
set_transient( 'alttextlab_enrich_post_content_success', $success_msg, 60 );
$recursive = false;
} else if ($response === self::ERROR_INVALID_API_KEY) {
$success_msg = sprintf( __(self::MSG_INVALID_API_KEY, 'alttextlab' ) );
set_transient( 'alttextlab_enrich_post_content_success', $success_msg, 60 );
$recursive = false;
} else if ($response === self::ERROR_MONTHLY_LIMIT) {
$success_msg = sprintf( __( self::MSG_MONTHLY_LIMIT, 'alttextlab' ) );
set_transient( 'alttextlab_enrich_post_content_success', $success_msg, 60 );
$recursive = false;
} else if (!is_array($response) && $response !== false) {
$images_successful_current++;
}
// Check if all items are processed
$all_empty = true;
foreach ($images_to_update_transient as $type => $items) {
if (!empty($items)) {
$all_empty = false;
break;
}
}
if ($all_empty) {
delete_transient('alttextlab_enric_post_generate_' . $post_id);
delete_transient('alttextlab_enric_post_generate__redirect_' . $post_id);
$recursive = false;
$images_successful_general += $images_successful_current;
/* translators: 1: Total images processed count, 2: New alt texts generated count */
$success_msg = sprintf( __(self::MSG_SUCCESS_TEMPLATE, 'alttextlab'), $images_total, $images_successful_general );
set_transient( 'alttextlab_enrich_post_content_success', $success_msg, 60 );
} else {
set_transient('alttextlab_enric_post_generate_' . $post_id, $images_to_update_transient, 2048);
}
wp_send_json(array(
'status' => 'success',
'message' => __('Images successfully updated.', 'alttextlab'),
'success_count' => $images_successful_current,
'recursive' => $recursive
));
}
/**
* Generates a localized success/error message based on image processing results
*
*/
private function return_success_message($api_key_is_invalid, $no_credits, $monthly_limit, $total_images_found, $num_alttext_generated)
{
// Set a transient to show a success notice after page reload
if ( $api_key_is_invalid ){
$success_msg = sprintf( __(self::MSG_INVALID_API_KEY, 'alttextlab' ) );
}
elseif ( $no_credits ) {
$success_msg = sprintf( __(self::MSG_NO_CREDITS, 'alttextlab' ) );
}
elseif ( $monthly_limit ) {
$success_msg = sprintf( __( self::MSG_MONTHLY_LIMIT, 'alttextlab' ) );
}
else {
/* translators: 1: Total number of images processed, 2: Number of new alt texts generated */
$success_msg = sprintf( __(self::MSG_SUCCESS_TEMPLATE, 'alttextlab'), $total_images_found, $num_alttext_generated );
}
return $success_msg;
}
}