suggestion_generator = $suggestion_generator; } public function get_results() { $results = (array) get_option( Adbear_Alt_Text_Generator_Helpers::get_scan_option_key(), array() ); return $results; } public function scan_batch( $offset = 0, $limit = 50, $reset = false ) { $offset = max( 0, absint( $offset ) ); $limit = max( 1, min( 200, absint( $limit ) ) ); if ( $reset ) { update_option( Adbear_Alt_Text_Generator_Helpers::get_scan_option_key(), array(), false ); update_option( Adbear_Alt_Text_Generator_Helpers::get_scan_report_option_key(), array(), false ); update_option( Adbear_Alt_Text_Generator_Helpers::get_scan_meta_option_key(), array(), false ); $offset = 0; } $total = $this->count_images(); $ids = $this->get_image_ids( $offset, $limit ); $missing_results = (array) get_option( Adbear_Alt_Text_Generator_Helpers::get_scan_option_key(), array() ); $report_results = (array) get_option( Adbear_Alt_Text_Generator_Helpers::get_scan_report_option_key(), array() ); $meta = (array) get_option( Adbear_Alt_Text_Generator_Helpers::get_scan_meta_option_key(), array() ); $meta = wp_parse_args( $meta, array( 'total_images_scanned' => 0, 'missing_alt' => 0, 'missing_titles' => 0, 'elementor_matches' => 0, 'acf_matches' => 0, 'woocommerce_images' => 0, 'last_scan' => 0, ) ); foreach ( $ids as $attachment_id ) { $row = $this->build_row( $attachment_id ); if ( empty( $row ) ) { continue; } $report_results[ $attachment_id ] = $row; if ( ! empty( $row['missing_alt'] ) || ! empty( $row['missing_title'] ) ) { $missing_results[ $attachment_id ] = $row; } elseif ( isset( $missing_results[ $attachment_id ] ) ) { unset( $missing_results[ $attachment_id ] ); } } $processed = min( $total, $offset + count( $ids ) ); $meta = $this->calculate_meta( $report_results, $processed ); update_option( Adbear_Alt_Text_Generator_Helpers::get_scan_option_key(), $missing_results, false ); update_option( Adbear_Alt_Text_Generator_Helpers::get_scan_report_option_key(), $report_results, false ); update_option( Adbear_Alt_Text_Generator_Helpers::get_scan_meta_option_key(), $meta, false ); return array( 'total' => $total, 'processed' => $processed, 'is_complete' => $processed >= $total, ); } protected function count_images() { $query = new WP_Query( array( 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_mime_type' => 'image', 'posts_per_page' => 1, 'fields' => 'ids', 'no_found_rows' => false, ) ); return (int) $query->found_posts; } protected function get_image_ids( $offset, $limit ) { $query = new WP_Query( array( 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_mime_type' => 'image', 'posts_per_page' => $limit, 'offset' => $offset, 'fields' => 'ids', 'orderby' => 'ID', 'order' => 'ASC', 'no_found_rows' => true, ) ); return array_map( 'absint', (array) $query->posts ); } protected function build_row( $attachment_id ) { $attachment_id = absint( $attachment_id ); $post = get_post( $attachment_id ); if ( ! $post || 'attachment' !== $post->post_type ) { return array(); } $current_alt = (string) get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ); $current_title = (string) get_the_title( $attachment_id ); $suggestion = $this->suggestion_generator->generate( $attachment_id ); $file = (string) get_attached_file( $attachment_id ); $url = (string) wp_get_attachment_url( $attachment_id ); $used_in = $this->detect_usage( $attachment_id, $url ); return array( 'attachment_id' => $attachment_id, 'filename' => wp_basename( $file ? $file : $url ), 'url' => $url, 'thumb' => wp_get_attachment_image_url( $attachment_id, 'thumbnail' ), 'uploaded_date' => get_the_date( 'Y-m-d H:i:s', $attachment_id ), 'current_alt' => $current_alt, 'current_title' => $current_title, 'missing_alt' => '' === trim( $current_alt ) ? 1 : 0, 'missing_title' => '' === trim( $current_title ) ? 1 : 0, 'suggested_alt' => isset( $suggestion['alt'] ) ? sanitize_text_field( $suggestion['alt'] ) : '', 'suggested_title' => isset( $suggestion['title'] ) ? sanitize_text_field( $suggestion['title'] ) : '', 'used_in' => $used_in, 'used_in_display' => $this->format_usage_labels( $used_in ), 'file_size_human' => $file && file_exists( $file ) ? size_format( filesize( $file ) ) : '', 'is_elementor' => in_array( 'elementor', $used_in, true ) ? 1 : 0, 'is_acf' => in_array( 'acf', $used_in, true ) ? 1 : 0, 'is_woocommerce' => in_array( 'woocommerce', $used_in, true ) ? 1 : 0, ); } protected function format_usage_labels( $usage_keys ) { $labels = array(); foreach ( (array) $usage_keys as $key ) { switch ( $key ) { case 'featured_image': $labels[] = __( 'Featured Image', 'adbear-alt-text-generator' ); break; case 'woocommerce': $labels[] = __( 'WooCommerce', 'adbear-alt-text-generator' ); break; case 'elementor': $labels[] = __( 'Elementor', 'adbear-alt-text-generator' ); break; case 'acf': $labels[] = __( 'ACF', 'adbear-alt-text-generator' ); break; case 'content': $labels[] = __( 'Content', 'adbear-alt-text-generator' ); break; } } return implode( ', ', $labels ); } protected function detect_usage( $attachment_id, $url ) { $usage = array(); if ( (int) get_post_meta( $attachment_id, '_thumbnail_id', true ) > 0 ) { $usage[] = 'featured_image'; } if ( class_exists( 'WooCommerce' ) && $this->is_used_in_woocommerce( $attachment_id ) ) { $usage[] = 'woocommerce'; } if ( $this->is_used_in_elementor( $attachment_id ) ) { $usage[] = 'elementor'; } if ( $this->is_used_in_acf( $attachment_id ) ) { $usage[] = 'acf'; } if ( $url && $this->is_used_in_content( $url ) ) { $usage[] = 'content'; } return array_values( array_unique( $usage ) ); } protected function is_used_in_content( $url ) { global $wpdb; $like = '%' . $wpdb->esc_like( $url ) . '%'; $count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_content LIKE %s", $like ) ); return $count > 0; } protected function is_used_in_elementor( $attachment_id ) { global $wpdb; $like = '%' . $wpdb->esc_like( '"id":' . absint( $attachment_id ) ) . '%'; $count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(post_id) FROM {$wpdb->postmeta} WHERE meta_key = '_elementor_data' AND meta_value LIKE %s", $like ) ); return $count > 0; } protected function is_used_in_acf( $attachment_id ) { global $wpdb; $count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(meta_id) FROM {$wpdb->postmeta} WHERE meta_value = %s OR meta_value LIKE %s", (string) absint( $attachment_id ), '%' . $wpdb->esc_like( '"' . absint( $attachment_id ) . '"' ) . '%' ) ); return $count > 0; } protected function is_used_in_woocommerce( $attachment_id ) { global $wpdb; $id = absint( $attachment_id ); $count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(meta_id) FROM {$wpdb->postmeta} WHERE (meta_key = '_thumbnail_id' AND meta_value = %s) OR (meta_key = '_product_image_gallery' AND FIND_IN_SET(%d, meta_value))", (string) $id, $id ) ); return $count > 0; } protected function calculate_meta( $rows, $processed ) { $missing_alt = 0; $missing_titles = 0; $elementor_matches = 0; $acf_matches = 0; $woocommerce_images = 0; foreach ( (array) $rows as $row ) { $missing_alt += ! empty( $row['missing_alt'] ) ? 1 : 0; $missing_titles += ! empty( $row['missing_title'] ) ? 1 : 0; $elementor_matches += ! empty( $row['is_elementor'] ) ? 1 : 0; $acf_matches += ! empty( $row['is_acf'] ) ? 1 : 0; $woocommerce_images += ! empty( $row['is_woocommerce'] ) ? 1 : 0; } return array( 'total_images_scanned' => absint( $processed ), 'missing_alt' => absint( $missing_alt ), 'missing_titles' => absint( $missing_titles ), 'elementor_matches' => absint( $elementor_matches ), 'acf_matches' => absint( $acf_matches ), 'woocommerce_images' => absint( $woocommerce_images ), 'last_scan' => time(), ); } }