'attachment',
'post_status' => 'inherit',
'post_mime_type' => array( 'image/jpeg', 'image/png', 'image/gif', 'image/webp' ),
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_wp_attachment_image_alt',
'compare' => 'NOT EXISTS',
),
array(
'key' => '_wp_attachment_image_alt',
'value' => '',
'compare' => '=',
),
),
)
);
foreach ( $query->posts as $attachment_id ) {
$thumb = wp_get_attachment_image_src( $attachment_id, 'thumbnail' );
$full = wp_get_attachment_image_src( $attachment_id, 'full' );
$title = get_the_title( $attachment_id );
$parent_id = wp_get_post_parent_id( $attachment_id );
$location = __( 'Media Library', 'fix-experts-ai-image-alt-tag-generator' );
if ( $parent_id && 'product' === get_post_type( $parent_id ) ) {
$location = __( 'WooCommerce Product', 'fix-experts-ai-image-alt-tag-generator' );
}
$items[] = array(
'unique_key' => 'attachment-' . $attachment_id,
'type' => 'attachment',
'id' => $attachment_id,
'post_id' => 0,
'index' => 0,
'src' => $full ? $full[0] : '',
'thumb' => $thumb ? $thumb[0] : ( $full ? $full[0] : '' ),
'title' => $title ? $title : __( '(untitled)', 'fix-experts-ai-image-alt-tag-generator' ),
'location' => $location,
'edit_link' => get_edit_post_link( $parent_id ? $parent_id : $attachment_id, 'raw' ),
'current_alt' => '',
);
}
return $items;
}
/**
* Images embedded directly inside published post/page content that lack
* a non-empty alt attribute (independent of any Media Library alt meta,
* since WordPress "bakes" alt text into content at insertion time).
*/
public static function scan_content_images() {
$items = array();
$limit = (int) AIATG_Settings::get( 'content_scan_limit' );
$query = new WP_Query(
array(
'post_type' => array( 'post', 'page' ),
'post_status' => 'publish',
'posts_per_page' => $limit > 0 ? $limit : 200,
'fields' => 'ids',
's' => '',
)
);
foreach ( $query->posts as $post_id ) {
$content = get_post_field( 'post_content', $post_id );
if ( false === stripos( $content, ' $src ) {
$items[] = array(
'unique_key' => 'content-' . $post_id . '-' . $index,
'type' => 'content',
'id' => 0,
'post_id' => $post_id,
'index' => $index,
'src' => $src,
'thumb' => $src,
'title' => $title ? $title : __( '(untitled)', 'fix-experts-ai-image-alt-tag-generator' ),
'location' => get_post_type( $post_id ) === 'page' ? __( 'Page content', 'fix-experts-ai-image-alt-tag-generator' ) : __( 'Post content', 'fix-experts-ai-image-alt-tag-generator' ),
'edit_link' => get_edit_post_link( $post_id, 'raw' ),
'current_alt' => '',
);
}
}
return $items;
}
/**
* Parses HTML content and returns a 0-indexed map of img-tag-index => src
* for every
tag whose alt attribute is missing or empty.
*
* The index refers to the Nth
tag found in document order and is
* the same numbering used by save_content_alt() to write the value back.
*/
public static function extract_missing_alt_images( $content ) {
$missing = array();
$dom = self::load_dom( $content );
if ( ! $dom ) {
return $missing;
}
$imgs = $dom->getElementsByTagName( 'img' );
foreach ( $imgs as $index => $img ) {
$alt = $img->getAttribute( 'alt' );
$src = $img->getAttribute( 'src' );
if ( '' === trim( $alt ) && ! empty( $src ) ) {
$missing[ $index ] = $src;
}
}
return $missing;
}
/**
* Writes $alt_text into the Nth
tag of $content and returns the
* updated HTML. Returns WP_Error if the index can't be found, or if
* expected_src is given and no longer matches (content changed since
* the scan, so the index could now point at a different image).
*/
public static function set_content_image_alt( $content, $index, $alt_text, $expected_src = '' ) {
$dom = self::load_dom( $content );
if ( ! $dom ) {
return new WP_Error( 'aiatg_dom_error', __( 'Could not parse post content.', 'fix-experts-ai-image-alt-tag-generator' ) );
}
$imgs = $dom->getElementsByTagName( 'img' );
if ( ! isset( $imgs[ $index ] ) ) {
return new WP_Error( 'aiatg_image_not_found', __( 'That image could not be found in the post content anymore (content may have changed).', 'fix-experts-ai-image-alt-tag-generator' ) );
}
if ( '' !== $expected_src && $imgs[ $index ]->getAttribute( 'src' ) !== $expected_src ) {
return new WP_Error( 'aiatg_image_mismatch', __( 'The post content changed since this was scanned, so this image could not be safely updated. Please rescan.', 'fix-experts-ai-image-alt-tag-generator' ) );
}
$imgs[ $index ]->setAttribute( 'alt', $alt_text );
return self::dom_to_html( $dom );
}
/**
* Loads HTML content into a DOMDocument wrapped in a neutral