Plugin Directory

Changeset 3387215


Ignore:
Timestamp:
10/30/2025 05:25:51 PM (4 months ago)
Author:
mrbogdan
Message:

Release version 2.1.0 - Bug fixes and performance improvements

Location:
auto-image-tags
Files:
8 edited
1 copied

Legend:

Unmodified
Added
Removed
  • auto-image-tags/tags/2.1.0/assets/css/admin-style.css

    r3380076 r3387215  
    1 /* =================================
    2    AUTO IMAGE TAGS - ADMIN STYLES
    3    ================================= */
     1/**
     2 * AUTO IMAGE TAGS - Admin Styles
     3 * Version: 2.1.0
     4 * Author: Shapovalov Bogdan
     5 */
    46
    57.autoimta-settings-form {
     
    351353    border-radius: 3px;
    352354}
     355/* Support Links */
     356.autoimta-support-links {
     357    margin-top: 20px;
     358    display: flex;
     359    gap: 10px;
     360    flex-wrap: wrap;
     361}
     362
     363.autoimta-support-links .button {
     364    text-decoration: none;
     365}
     366
     367/* Version highlights */
     368.autoimta-info-section h4 {
     369    margin-top: 15px;
     370    margin-bottom: 10px;
     371    font-size: 14px;
     372    color: #2271b1;
     373}
     374
     375.autoimta-info-section ul {
     376    margin-left: 20px;
     377}
     378
     379.autoimta-info-section ul li {
     380    margin-bottom: 8px;
     381    line-height: 1.6;
     382}
     383
     384.autoimta-info-section del {
     385    color: #999;
     386}
  • auto-image-tags/tags/2.1.0/assets/js/admin-main.js

    r3380076 r3387215  
    11/**
    22 * AUTO IMAGE TAGS - Admin JavaScript
    3  * Version: 2.0.0
     3 * Version: 2.1.0
    44 */
    55
     
    171171    }
    172172
    173     /**
    174      * Запуск обработки изображений
    175      */
    176     function startProcessing() {
    177         processing = true;
    178         let offset = 0;
    179         let totalProcessed = 0;
    180         let totalSuccess = 0;
    181         let totalErrors = 0;
    182         let totalSkipped = 0;
    183 
    184         $('#autoimta-process-btn').prop('disabled', true);
    185         $('#autoimta-progress').show();
    186         $('#autoimta-results').hide();
    187 
    188         const filters = {
    189             date: $('#date_filter').val(),
    190             status: $('#status_filter').val(),
    191             post: $('#post_filter').val()
    192         };
    193 
    194         function processBatch() {
    195             $.ajax({
    196                 url: autoimtaData.ajaxurl,
    197                 type: 'POST',
    198                 data: {
    199                     action: 'autoimta_process_existing_images',
    200                     nonce: autoimtaData.nonce,
    201                     offset: offset,
    202                     filters: filters
    203                 },
    204                 success: function(response) {
    205                     if (response.success) {
    206                         const data = response.data;
    207                        
    208                         totalProcessed += data.processed;
    209                         totalSuccess += data.success;
    210                         totalErrors += data.errors;
    211                         totalSkipped += data.skipped;
    212 
    213                         const progress = Math.min(100, Math.round((offset / 100) * 100));
    214                         updateProgress(progress);
    215 
    216                         $('#autoimta-status-text').html(
    217                             autoimtaData.strings.processed + ' ' + totalProcessed + '<br>' +
    218                             autoimtaData.strings.success + ' ' + totalSuccess + '<br>' +
    219                             autoimtaData.strings.skipped + ' ' + totalSkipped + '<br>' +
    220                             autoimtaData.strings.errors + ' ' + totalErrors
    221                         );
    222 
    223                         if (data.has_more) {
    224                             offset += data.processed;
    225                             processBatch();
    226                         } else {
    227                             processingComplete(totalProcessed, totalSuccess, totalSkipped, totalErrors, data.test_mode);
    228                         }
     173/**
     174 * Запуск обработки изображений
     175 */
     176function startProcessing() {
     177    processing = true;
     178    let offset = 0;
     179    let totalProcessed = 0;
     180    let totalSuccess = 0;
     181    let totalErrors = 0;
     182    let totalSkipped = 0;
     183    let totalImages = 0;
     184
     185    $('#autoimta-process-btn').prop('disabled', true);
     186    $('#autoimta-progress').show();
     187    $('#autoimta-results').hide();
     188
     189    const filters = {
     190        date: $('#date_filter').val(),
     191        status: $('#status_filter').val(),
     192        post: $('#post_filter').val()
     193    };
     194
     195    // Получаем общее количество изображений ДЛЯ ОБРАБОТКИ
     196    $.ajax({
     197        url: autoimtaData.ajaxurl,
     198        type: 'POST',
     199        data: {
     200            action: 'autoimta_get_images_count',
     201            nonce: autoimtaData.nonce,
     202            filters: filters
     203        },
     204        success: function(response) {
     205            if (response.success) {
     206                // ✅ ИСПОЛЬЗУЕМ needs_processing - только изображения БЕЗ ТЕГОВ
     207                totalImages = response.data.needs_processing;
     208               
     209                if (totalImages === 0) {
     210                    processingError(autoimtaData.strings.no_images);
     211                    return;
     212                }
     213               
     214                processBatch(); // Начинаем обработку
     215            } else {
     216                processingError(response.data.message);
     217            }
     218        },
     219        error: function() {
     220            processingError(autoimtaData.strings.connection_error);
     221        }
     222    });
     223
     224    function processBatch() {
     225        $.ajax({
     226            url: autoimtaData.ajaxurl,
     227            type: 'POST',
     228            data: {
     229                action: 'autoimta_process_existing_images',
     230                nonce: autoimtaData.nonce,
     231                offset: offset,
     232                filters: filters
     233            },
     234            success: function(response) {
     235                if (response.success) {
     236                    const data = response.data;
     237                   
     238                    totalProcessed += data.processed;
     239                    totalSuccess += data.success;
     240                    totalErrors += data.errors;
     241                    totalSkipped += data.skipped;
     242
     243                    // Правильный расчёт прогресса
     244                    const currentProcessed = offset + data.processed;
     245                    let progress = 0;
     246                    if (totalImages > 0) {
     247                        progress = Math.round((currentProcessed / totalImages) * 100);
     248                        if (progress > 100) progress = 100;
     249                    }
     250                    updateProgress(progress);
     251
     252                    $('#autoimta-status-text').html(
     253                        autoimtaData.strings.processed + ' ' + currentProcessed + ' / ' + totalImages + '<br>' +
     254                        autoimtaData.strings.success + ' ' + totalSuccess + '<br>' +
     255                        autoimtaData.strings.skipped + ' ' + totalSkipped + '<br>' +
     256                        autoimtaData.strings.errors + ' ' + totalErrors
     257                    );
     258
     259                    if (data.has_more) {
     260                        offset += data.processed;
     261                        processBatch();
    229262                    } else {
    230                         processingError(response.data.message);
     263                        processingComplete(totalProcessed, totalSuccess, totalSkipped, totalErrors, data.test_mode);
    231264                    }
    232                 },
    233                 error: function() {
    234                     processingError(autoimtaData.strings.connection_error);
    235                 }
    236             });
    237         }
    238 
    239         processBatch();
    240     }
     265                } else {
     266                    processingError(response.data.message);
     267                }
     268            },
     269            error: function() {
     270                processingError(autoimtaData.strings.connection_error);
     271            }
     272        });
     273    }
     274}
    241275
    242276    function processingComplete(processed, success, skipped, errors, testMode) {
  • auto-image-tags/tags/2.1.0/auto-image-tags.php

    r3380076 r3387215  
    44 * Plugin URI: https://wordpress.org/plugins/auto-image-tags/
    55 * Description: Automatically add ALT, TITLE, Caption and Description tags to WordPress media library images. WooCommerce integration and optional translation support.
    6  * Version: 2.0.0
     6 * Version: 2.1.0
    77 * Author: mrbogdan
    88 * Author URI: https://profiles.wordpress.org/mrbogdan/
     
    2323define('AUTOIMTA_PLUGIN_DIR', plugin_dir_path(__FILE__));
    2424define('AUTOIMTA_PLUGIN_URL', plugin_dir_url(__FILE__));
    25 define('AUTOIMTA_VERSION', '2.0.0');
     25define('AUTOIMTA_VERSION', '2.1.0');
    2626
    2727// Проверка требований
     
    7474        add_filter('add_attachment', array($this, 'handle_image_upload'));
    7575        add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
    76        
     76        add_action('wp_enqueue_scripts', array($this, 'disable_woocommerce_prettyphoto'), 99);
    7777        add_action('wp_ajax_autoimta_process_existing_images', array($this, 'ajax_process_existing_images'));
    7878        add_action('wp_ajax_autoimta_get_images_count', array($this, 'ajax_get_images_count'));
     
    991991    }
    992992
    993     /**
    994      * Вкладка "О плагине"
    995      */
    996     private function render_about_tab() {
    997         ?>
    998         <div class="autoimta-about-box">
    999             <h2><?php esc_html_e('About Auto Image Tags Plugin', 'auto-image-tags'); ?></h2>
    1000            
    1001             <div class="autoimta-info-section">
    1002                 <h3><?php esc_html_e('Plugin Information', 'auto-image-tags'); ?></h3>
    1003                 <table class="autoimta-info-table">
    1004                     <tr>
    1005                         <td><strong><?php esc_html_e('Version:', 'auto-image-tags'); ?></strong></td>
    1006                         <td><?php echo esc_html(AUTOIMTA_VERSION); ?></td>
    1007                     </tr>
    1008                     <tr>
    1009                         <td><strong><?php esc_html_e('Author:', 'auto-image-tags'); ?></strong></td>
    1010                         <td>Shapovalov Bogdan</td>
    1011                     </tr>
    1012                     <tr>
    1013                         <td><strong><?php esc_html_e('Telegram:', 'auto-image-tags'); ?></strong></td>
    1014                         <td><a href="https://t.me/shapovalovbogdan" target="_blank" rel="noopener noreferrer">@shapovalovbogdan</a></td>
    1015                     </tr>
    1016                     <tr>
    1017                         <td><strong><?php esc_html_e('GitHub:', 'auto-image-tags'); ?></strong></td>
    1018                         <td><a href="https://github.com/imrbogdan/auto-image-tags" target="_blank" rel="noopener noreferrer">Repository</a></td>
    1019                     </tr>
    1020                 </table>
    1021             </div>
    1022            
    1023             <div class="autoimta-info-section">
    1024                 <h3><?php esc_html_e('Plugin Features', 'auto-image-tags'); ?></h3>
    1025                 <ul>
    1026                     <li>✅ <?php esc_html_e('Automatic ALT, TITLE, Caption and Description generation', 'auto-image-tags'); ?></li>
    1027                     <li>✅ <?php esc_html_e('Preview changes before applying', 'auto-image-tags'); ?></li>
    1028                     <li>✅ <?php esc_html_e('Individual overwrite settings for each attribute', 'auto-image-tags'); ?></li>
    1029                     <li>✅ <?php esc_html_e('Bulk processing filters (by date, posts, status)', 'auto-image-tags'); ?></li>
    1030                     <li>✅ <?php esc_html_e('Advanced filename cleanup', 'auto-image-tags'); ?></li>
    1031                     <li>✅ <?php esc_html_e('Stop words to remove unwanted words', 'auto-image-tags'); ?></li>
    1032                     <li>✅ <?php esc_html_e('Test mode for safe testing', 'auto-image-tags'); ?></li>
    1033                     <li>✅ <?php esc_html_e('Processing history and statistics', 'auto-image-tags'); ?></li>
    1034                     <li>✅ <?php esc_html_e('Multilingual support', 'auto-image-tags'); ?></li>
    1035                     <li>✅ <?php esc_html_e('Extended template variables', 'auto-image-tags'); ?></li>
    1036                     <li>✅ <?php esc_html_e('Translation system (5 services)', 'auto-image-tags'); ?></li>
    1037                     <li>✅ <?php esc_html_e('WooCommerce integration', 'auto-image-tags'); ?></li>
    1038                 </ul>
    1039             </div>
    1040 
    1041             <div class="autoimta-info-section">
    1042                 <h3><?php esc_html_e('What\'s New in Version 2.0.0', 'auto-image-tags'); ?></h3>
    1043                 <ul>
    1044                     <li>🆕 <?php esc_html_e('Translation system with 5 services', 'auto-image-tags'); ?></li>
    1045                     <li>🆕 <?php esc_html_e('WooCommerce integration', 'auto-image-tags'); ?></li>
    1046                     <li>🆕 <?php esc_html_e('Tools tab (bulk delete, export/import settings)', 'auto-image-tags'); ?></li>
    1047                     <li>🆕 <?php esc_html_e('Preview tab with before/after comparison', 'auto-image-tags'); ?></li>
    1048                     <li>🆕 <?php esc_html_e('Caption and Description support', 'auto-image-tags'); ?></li>
    1049                     <li>🆕 <?php esc_html_e('Individual overwrite settings', 'auto-image-tags'); ?></li>
    1050                     <li>🆕 <?php esc_html_e('Advanced filters', 'auto-image-tags'); ?></li>
    1051                     <li>🆕 <?php esc_html_e('Enhanced filename cleanup', 'auto-image-tags'); ?></li>
    1052                     <li>🆕 <?php esc_html_e('Custom stop words', 'auto-image-tags'); ?></li>
    1053                     <li>🆕 <?php esc_html_e('Test mode', 'auto-image-tags'); ?></li>
    1054                     <li>🆕 <?php esc_html_e('Statistics and history', 'auto-image-tags'); ?></li>
    1055                     <li>🆕 <?php esc_html_e('Language selection', 'auto-image-tags'); ?></li>
    1056                 </ul>
    1057             </div>
    1058            
    1059             <div class="autoimta-info-section">
    1060                 <h3><?php esc_html_e('Support and Feedback', 'auto-image-tags'); ?></h3>
    1061                 <p><?php esc_html_e('If you have questions, suggestions or found a bug, contact me via Telegram.', 'auto-image-tags'); ?></p>
    1062                 <p><?php esc_html_e('Plugin is distributed for free to help the WordPress community.', 'auto-image-tags'); ?></p>
    1063                 <p><strong><?php esc_html_e('All features are available for free, no Pro version!', 'auto-image-tags'); ?></strong></p>
     993/**
     994 * Вкладка "О плагине"
     995 */
     996private function render_about_tab() {
     997    ?>
     998    <div class="autoimta-about-box">
     999        <h2><?php esc_html_e('About Auto Image Tags Plugin', 'auto-image-tags'); ?></h2>
     1000       
     1001        <div class="autoimta-info-section">
     1002            <h3><?php esc_html_e('Author Information', 'auto-image-tags'); ?></h3>
     1003            <table class="autoimta-info-table">
     1004                <tr>
     1005                    <td><strong><?php esc_html_e('Developer:', 'auto-image-tags'); ?></strong></td>
     1006                    <td>Shapovalov Bogdan</td>
     1007                </tr>
     1008                <tr>
     1009                    <td><strong><?php esc_html_e('Telegram:', 'auto-image-tags'); ?></strong></td>
     1010                    <td><a href="https://t.me/shapovalovbogdan" target="_blank" rel="noopener noreferrer">@shapovalovbogdan</a></td>
     1011                </tr>
     1012                <tr>
     1013                    <td><strong><?php esc_html_e('GitHub:', 'auto-image-tags'); ?></strong></td>
     1014                    <td><a href="https://github.com/imrbogdan/auto-image-tags" target="_blank" rel="noopener noreferrer">github.com/imrbogdan/auto-image-tags</a></td>
     1015                </tr>
     1016                <tr>
     1017                    <td><strong><?php esc_html_e('WordPress Plugin:', 'auto-image-tags'); ?></strong></td>
     1018                    <td><a href="https://wordpress.org/plugins/auto-image-tags/" target="_blank" rel="noopener noreferrer">wordpress.org/plugins/auto-image-tags</a></td>
     1019                </tr>
     1020            </table>
     1021        </div>
     1022       
     1023        <div class="autoimta-info-section">
     1024            <h3><?php esc_html_e('Plugin Features', 'auto-image-tags'); ?></h3>
     1025           
     1026            <h4><?php esc_html_e('Core Functionality', 'auto-image-tags'); ?></h4>
     1027            <ul>
     1028                <li>✅ Automatic generation of ALT, TITLE, Caption and Description tags</li>
     1029                <li>✅ Process images on upload or bulk process existing images</li>
     1030                <li>✅ Multiple tag format options (filename, post title, site name, custom templates)</li>
     1031                <li>✅ Individual overwrite settings for each field (ALT, TITLE, Caption, Description)</li>
     1032                <li>✅ Advanced filename cleanup (remove hyphens, numbers, size suffixes, CamelCase splitting)</li>
     1033                <li>✅ Stop words filtering to remove unwanted terms</li>
     1034                <li>✅ Custom template variables ({filename}, {posttitle}, {sitename}, {category}, {tags}, {author}, {date}, {year}, {month})</li>
     1035            </ul>
     1036           
     1037            <h4><?php esc_html_e('Processing & Filters', 'auto-image-tags'); ?></h4>
     1038            <ul>
     1039                <li>✅ Bulk processing with advanced filters (date range, status, specific posts)</li>
     1040                <li>✅ Filter by images without ALT, without TITLE, or without any tags</li>
     1041                <li>✅ Preview changes before applying them</li>
     1042                <li>✅ Test mode for safe testing without saving changes</li>
     1043                <li>✅ Process unlimited images (no limits)</li>
     1044                <li>✅ Batch processing for optimal performance</li>
     1045            </ul>
     1046           
     1047            <h4><?php esc_html_e('Translation System', 'auto-image-tags'); ?></h4>
     1048            <ul>
     1049                <li>✅ Integrated translation for image tags</li>
     1050                <li>✅ Support for 5 translation services:
     1051                    <ul>
     1052                        <li>• Google Translate API</li>
     1053                        <li>• DeepL API (500,000 chars/month free)</li>
     1054                        <li>• Yandex Translator (1,000,000 chars/month free)</li>
     1055                        <li>• LibreTranslate (completely free, open-source)</li>
     1056                        <li>• MyMemory (10,000 chars/day free)</li>
     1057                    </ul>
     1058                </li>
     1059                <li>✅ Automatic translation on image upload</li>
     1060                <li>✅ Bulk translation for existing images</li>
     1061                <li>✅ Test translation before processing</li>
     1062            </ul>
     1063           
     1064            <h4><?php esc_html_e('WooCommerce Integration', 'auto-image-tags'); ?></h4>
     1065            <ul>
     1066                <li>✅ Automatic processing of product images</li>
     1067                <li>✅ Process product gallery images</li>
     1068                <li>✅ Use product title, category and SKU in tags</li>
     1069                <li>✅ Seamless integration with product workflow</li>
     1070            </ul>
     1071           
     1072            <h4><?php esc_html_e('Tools & Management', 'auto-image-tags'); ?></h4>
     1073            <ul>
     1074                <li>✅ Bulk tag removal tool</li>
     1075                <li>✅ Export/import plugin settings</li>
     1076                <li>✅ Processing statistics and history</li>
     1077                <li>✅ Detailed logging for debugging</li>
     1078                <li>✅ Multilingual interface (English/Russian)</li>
     1079            </ul>
     1080           
     1081            <h4><?php esc_html_e('Technical Features', 'auto-image-tags'); ?></h4>
     1082            <ul>
     1083                <li>✅ Protection against timeouts and server overload</li>
     1084                <li>✅ Optimized for large image libraries</li>
     1085                <li>✅ Compatible with WordPress 5.0+</li>
     1086                <li>✅ Compatible with PHP 7.2+</li>
     1087                <li>✅ Database logging for audit trails</li>
     1088                <li>✅ Clean, well-documented code</li>
     1089            </ul>
     1090        </div>
     1091       
     1092        <div class="autoimta-info-section">
     1093            <h3><?php esc_html_e('Support', 'auto-image-tags'); ?></h3>
     1094            <p><?php esc_html_e('For questions, suggestions or bug reports, please contact me via Telegram or create an issue on GitHub.', 'auto-image-tags'); ?></p>
     1095            <p><strong><?php esc_html_e('This plugin is completely free with no Pro version. All features are available to everyone!', 'auto-image-tags'); ?></strong></p>
     1096           
     1097            <div class="autoimta-support-links">
     1098                <a href="https://t.me/shapovalovbogdan" class="button button-primary" target="_blank" rel="noopener noreferrer">
     1099                    📱 Contact on Telegram
     1100                </a>
     1101                <a href="https://github.com/imrbogdan/auto-image-tags" class="button button-secondary" target="_blank" rel="noopener noreferrer">
     1102                    ⭐ Star on GitHub
     1103                </a>
     1104                <a href="https://wordpress.org/plugins/auto-image-tags/" class="button button-secondary" target="_blank" rel="noopener noreferrer">
     1105                    💙 WordPress.org
     1106                </a>
    10641107            </div>
    10651108        </div>
    1066         <?php
    1067     }
     1109    </div>
     1110    <?php
     1111}
    10681112   
    10691113    /**
     
    15191563            'post_mime_type' => 'image',
    15201564            'post_status' => 'inherit',
    1521             'posts_per_page' => 1000,
     1565            'posts_per_page' => -1,
    15221566            'fields' => 'ids'
    15231567        );
     
    15531597        $total = $query->found_posts;
    15541598       
    1555         if ($total > 5000) {
    1556             wp_send_json_error(array(
    1557                 'message' => esc_html__('Too many images to count. Use filters.', 'auto-image-tags')
    1558             ));
    1559             return;
    1560         }
    1561        
    15621599        $without_alt = 0;
    15631600        $without_title = 0;
     
    15831620           
    15841621            // Подсчет изображений для обработки
    1585             $will_process = false;
    1586             if (isset($settings['alt_format']) && $settings['alt_format'] !== 'disabled' && (isset($settings['overwrite_alt']) && $settings['overwrite_alt'] == '1' || empty($alt))) {
    1587                 $will_process = true;
    1588             }
    1589             if (isset($settings['title_format']) && $settings['title_format'] !== 'disabled' && (isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1' || empty($title))) {
    1590                 $will_process = true;
    1591             }
    1592             if ($will_process) {
    1593                 $needs_processing++;
    1594             }
     1622$will_process = false;
     1623if (isset($settings['alt_format']) && $settings['alt_format'] !== 'disabled' && (isset($settings['overwrite_alt']) && $settings['overwrite_alt'] == '1' || empty($alt))) {
     1624    $will_process = true;
     1625}
     1626if (isset($settings['title_format']) && $settings['title_format'] !== 'disabled' && (isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1' || empty($title))) {
     1627    $will_process = true;
     1628}
     1629if ($will_process) {
     1630    $needs_processing++;
     1631}
    15951632        }
    15961633       
     
    16811718    }
    16821719   
    1683     /**
    1684      * AJAX: Обработка существующих изображений
    1685      */
    1686     public function ajax_process_existing_images() {
    1687         check_ajax_referer('autoimta_ajax_nonce', 'nonce');
    1688        
    1689         if (!current_user_can('manage_options')) {
    1690             wp_send_json_error(array('message' => esc_html__('Insufficient permissions', 'auto-image-tags')));
    1691             return;
    1692         }
    1693        
    1694         $settings = get_option('autoimta_settings', array());
    1695         $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
    1696         $batch_size = 10;
    1697         $filters = isset($_POST['filters']) && is_array($_POST['filters']) ? array_map('sanitize_text_field', wp_unslash($_POST['filters'])) : array();
    1698        
    1699         $args = array(
    1700             'post_type' => 'attachment',
    1701             'post_mime_type' => 'image',
    1702             'post_status' => 'inherit',
    1703             'posts_per_page' => $batch_size,
    1704             'offset' => $offset,
    1705             'fields' => 'ids'
     1720/**
     1721 * AJAX: Обработка существующих изображений
     1722 */
     1723/**
     1724 * AJAX: Обработка существующих изображений
     1725 */
     1726public function ajax_process_existing_images() {
     1727    // ========================================
     1728    // ЗАЩИТА ОТ HTTP2 ОШИБКИ И ТАЙМАУТОВ
     1729    // ========================================
     1730   
     1731    // Увеличиваем лимиты
     1732    @ini_set('max_execution_time', 300); // 5 минут
     1733    @ini_set('memory_limit', '256M');
     1734    @set_time_limit(300);
     1735   
     1736    // Отключаем вывод ошибок в JSON ответ
     1737    @ini_set('display_errors', 0);
     1738    @ini_set('log_errors', 1);
     1739   
     1740    // Отключаем буферизацию для предотвращения таймаутов
     1741    if (function_exists('apache_setenv')) {
     1742        @apache_setenv('no-gzip', '1');
     1743    }
     1744    @ini_set('zlib.output_compression', '0');
     1745    @ini_set('implicit_flush', '1');
     1746   
     1747    // Очищаем все буферы
     1748    while (ob_get_level() > 0) {
     1749        ob_end_clean();
     1750    }
     1751   
     1752    // ========================================
     1753    // ОСНОВНАЯ ЛОГИКА
     1754    // ========================================
     1755   
     1756    check_ajax_referer('autoimta_ajax_nonce', 'nonce');
     1757   
     1758    if (!current_user_can('manage_options')) {
     1759        wp_send_json_error(array('message' => esc_html__('Insufficient permissions', 'auto-image-tags')));
     1760        return;
     1761    }
     1762   
     1763    $settings = get_option('autoimta_settings', array());
     1764    $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
     1765    $batch_size = 5; // Размер батча: 5 изображений за раз
     1766    $filters = isset($_POST['filters']) && is_array($_POST['filters']) ? array_map('sanitize_text_field', wp_unslash($_POST['filters'])) : array();
     1767   
     1768    // ========================================
     1769    // БАЗОВЫЕ АРГУМЕНТЫ ЗАПРОСА
     1770    // ========================================
     1771    $args = array(
     1772        'post_type' => 'attachment',
     1773        'post_mime_type' => 'image',
     1774        'post_status' => 'inherit',
     1775        'posts_per_page' => $batch_size,
     1776        'offset' => $offset,
     1777        'fields' => 'ids'
     1778    );
     1779   
     1780    // ========================================
     1781    // ФИЛЬТР ПО ДАТЕ
     1782    // ========================================
     1783    if (!empty($filters['date']) && $filters['date'] !== 'all') {
     1784        $date_query = array();
     1785        switch ($filters['date']) {
     1786            case 'today':
     1787                $date_query = array('after' => 'today', 'inclusive' => true);
     1788                break;
     1789            case 'week':
     1790                $date_query = array('after' => '1 week ago', 'inclusive' => true);
     1791                break;
     1792            case 'month':
     1793                $date_query = array('after' => '1 month ago', 'inclusive' => true);
     1794                break;
     1795            case 'year':
     1796                $date_query = array('after' => '1 year ago', 'inclusive' => true);
     1797                break;
     1798        }
     1799        if (!empty($date_query)) {
     1800            $args['date_query'] = array($date_query);
     1801        }
     1802    }
     1803   
     1804    // ========================================
     1805    // ФИЛЬТР ПО ПОСТУ
     1806    // ========================================
     1807    if (!empty($filters['post']) && $filters['post'] !== 'all') {
     1808        $args['post_parent'] = intval($filters['post']);
     1809    }
     1810   
     1811    // ========================================
     1812    // НОВОЕ: ФИЛЬТР ПО СТАТУСУ ТЕГОВ
     1813    // ========================================
     1814    if (!empty($filters['status']) && $filters['status'] !== 'all') {
     1815        switch ($filters['status']) {
     1816            case 'no_alt':
     1817                // Изображения без ALT
     1818                $args['meta_query'] = array(
     1819                    'relation' => 'OR',
     1820                    array(
     1821                        'key' => '_wp_attachment_image_alt',
     1822                        'compare' => 'NOT EXISTS'
     1823                    ),
     1824                    array(
     1825                        'key' => '_wp_attachment_image_alt',
     1826                        'value' => '',
     1827                        'compare' => '='
     1828                    )
     1829                );
     1830                break;
     1831               
     1832            case 'no_title':
     1833                // Изображения без TITLE
     1834                // Будем фильтровать после запроса, т.к. WordPress не поддерживает meta_query для post_title
     1835                break;
     1836               
     1837            case 'no_tags':
     1838                // Изображения без ALT (TITLE проверим после запроса)
     1839                $args['meta_query'] = array(
     1840                    'relation' => 'OR',
     1841                    array(
     1842                        'key' => '_wp_attachment_image_alt',
     1843                        'compare' => 'NOT EXISTS'
     1844                    ),
     1845                    array(
     1846                        'key' => '_wp_attachment_image_alt',
     1847                        'value' => '',
     1848                        'compare' => '='
     1849                    )
     1850                );
     1851                break;
     1852        }
     1853    } else {
     1854        // ========================================
     1855        // ЕСЛИ ФИЛЬТР "ВСЕ ИЗОБРАЖЕНИЯ" - ОБРАБАТЫВАЕМ ТОЛЬКО БЕЗ ТЕГОВ
     1856        // ========================================
     1857        // Проверяем настройки: если НЕ включена перезапись, фильтруем по пустым тегам
     1858        $overwrite_alt = isset($settings['overwrite_alt']) && $settings['overwrite_alt'] == '1';
     1859        $overwrite_title = isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1';
     1860       
     1861        // Если обе перезаписи ВЫКЛЮЧЕНЫ, обрабатываем только без тегов
     1862        if (!$overwrite_alt || !$overwrite_title) {
     1863            $args['meta_query'] = array(
     1864                'relation' => 'OR',
     1865                array(
     1866                    'key' => '_wp_attachment_image_alt',
     1867                    'compare' => 'NOT EXISTS'
     1868                ),
     1869                array(
     1870                    'key' => '_wp_attachment_image_alt',
     1871                    'value' => '',
     1872                    'compare' => '='
     1873                )
     1874            );
     1875        }
     1876    }
     1877   
     1878    // ========================================
     1879    // ВЫПОЛНЯЕМ ЗАПРОС
     1880    // ========================================
     1881    $query = new WP_Query($args);
     1882   
     1883    // ========================================
     1884    // ДОПОЛНИТЕЛЬНАЯ ФИЛЬТРАЦИЯ ПО TITLE
     1885    // ========================================
     1886    $filtered_posts = array();
     1887   
     1888    if (!empty($filters['status']) && ($filters['status'] === 'no_title' || $filters['status'] === 'no_tags')) {
     1889        // Фильтруем по пустому TITLE
     1890        foreach ($query->posts as $attachment_id) {
     1891            $title = get_the_title($attachment_id);
     1892           
     1893            if ($filters['status'] === 'no_title') {
     1894                // Только без TITLE
     1895                if (empty($title)) {
     1896                    $filtered_posts[] = $attachment_id;
     1897                }
     1898            } elseif ($filters['status'] === 'no_tags') {
     1899                // Без ALT И TITLE
     1900                $alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
     1901                if (empty($alt) && empty($title)) {
     1902                    $filtered_posts[] = $attachment_id;
     1903                }
     1904            }
     1905        }
     1906       
     1907        // Используем отфильтрованный список
     1908        $posts_to_process = $filtered_posts;
     1909    } else {
     1910        // Если фильтр "Все" и перезапись ВЫКЛЮЧЕНА, дополнительно проверяем TITLE
     1911        $overwrite_title = isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1';
     1912       
     1913        if (!$overwrite_title) {
     1914            foreach ($query->posts as $attachment_id) {
     1915                $alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
     1916                $title = get_the_title($attachment_id);
     1917               
     1918                // Добавляем только если нет хотя бы одного тега
     1919                if (empty($alt) || empty($title)) {
     1920                    $filtered_posts[] = $attachment_id;
     1921                }
     1922            }
     1923            $posts_to_process = $filtered_posts;
     1924        } else {
     1925            $posts_to_process = $query->posts;
     1926        }
     1927    }
     1928   
     1929    // ========================================
     1930    // ОБРАБОТКА ИЗОБРАЖЕНИЙ
     1931    // ========================================
     1932    $processed = 0;
     1933    $success = 0;
     1934    $errors = 0;
     1935    $skipped = 0;
     1936   
     1937    foreach ($posts_to_process as $attachment_id) {
     1938        $result = $this->process_single_image($attachment_id);
     1939        if ($result === 'success') {
     1940            $success++;
     1941        } elseif ($result === 'skipped') {
     1942            $skipped++;
     1943        } else {
     1944            $errors++;
     1945        }
     1946        $processed++;
     1947    }
     1948   
     1949    // ========================================
     1950    // ОПРЕДЕЛЯЕМ ЕСТЬ ЛИ ЕЩЁ ИЗОБРАЖЕНИЯ
     1951    // ========================================
     1952    $has_more = false;
     1953   
     1954    if (!empty($filters['status']) && ($filters['status'] === 'no_title' || $filters['status'] === 'no_tags')) {
     1955        // Для фильтров с проверкой TITLE
     1956        $has_more = count($filtered_posts) >= $batch_size;
     1957    } else {
     1958        // Для обычных фильтров
     1959        $has_more = ($offset + $batch_size) < $query->found_posts;
     1960    }
     1961   
     1962    // ========================================
     1963    // СОХРАНЕНИЕ В ЛОГ (только в конце обработки)
     1964    // ========================================
     1965    if (!$has_more && isset($settings['test_mode']) && $settings['test_mode'] != '1') {
     1966        global $wpdb;
     1967       
     1968        // Проверяем есть ли уже запись для этой сессии (последние 5 минут)
     1969        $last_log = $wpdb->get_row(
     1970            "SELECT * FROM {$wpdb->prefix}autoimta_process_log
     1971             WHERE process_date > DATE_SUB(NOW(), INTERVAL 5 MINUTE)
     1972             ORDER BY id DESC LIMIT 1"
    17061973        );
    17071974       
    1708         // Применяем фильтры
    1709         if (!empty($filters['date']) && $filters['date'] !== 'all') {
    1710             $date_query = array();
    1711             switch ($filters['date']) {
    1712                 case 'today':
    1713                     $date_query = array('after' => 'today', 'inclusive' => true);
    1714                     break;
    1715                 case 'week':
    1716                     $date_query = array('after' => '1 week ago', 'inclusive' => true);
    1717                     break;
    1718                 case 'month':
    1719                     $date_query = array('after' => '1 month ago', 'inclusive' => true);
    1720                     break;
    1721                 case 'year':
    1722                     $date_query = array('after' => '1 year ago', 'inclusive' => true);
    1723                     break;
    1724             }
    1725             if (!empty($date_query)) {
    1726                 $args['date_query'] = array($date_query);
    1727             }
    1728         }
    1729        
    1730         if (!empty($filters['post']) && $filters['post'] !== 'all') {
    1731             $args['post_parent'] = intval($filters['post']);
    1732         }
    1733        
    1734         $query = new WP_Query($args);
    1735         $processed = 0;
    1736         $success = 0;
    1737         $errors = 0;
    1738         $skipped = 0;
    1739        
    1740         foreach ($query->posts as $attachment_id) {
    1741             $result = $this->process_single_image($attachment_id);
    1742             if ($result === 'success') {
    1743                 $success++;
    1744             } elseif ($result === 'skipped') {
    1745                 $skipped++;
    1746             } else {
    1747                 $errors++;
    1748             }
    1749             $processed++;
    1750         }
    1751        
    1752         $has_more = ($offset + $batch_size) < $query->found_posts;
    1753        
    1754         // Сохраняем в лог, если не тестовый режим и обработка завершена
    1755         if (!$has_more && isset($settings['test_mode']) && $settings['test_mode'] != '1') {
    1756             $this->save_process_log($query->found_posts, $processed, $success, $skipped, $errors, isset($settings['test_mode']) && $settings['test_mode'] == '1');
    1757         }
    1758        
    1759         wp_send_json_success(array(
    1760             'processed' => $processed,
    1761             'success' => $success,
    1762             'errors' => $errors,
    1763             'skipped' => $skipped,
    1764             'has_more' => $has_more,
    1765             'test_mode' => isset($settings['test_mode']) && $settings['test_mode'] == '1'
    1766         ));
    1767     }
     1975        if ($last_log) {
     1976            // Обновляем существующую запись
     1977            $wpdb->update(
     1978                $wpdb->prefix . 'autoimta_process_log',
     1979                array(
     1980                    'processed' => $last_log->processed + $processed,
     1981                    'success' => $last_log->success + $success,
     1982                    'skipped' => $last_log->skipped + $skipped,
     1983                    'errors' => $last_log->errors + $errors
     1984                ),
     1985                array('id' => $last_log->id),
     1986                array('%d', '%d', '%d', '%d'),
     1987                array('%d')
     1988            );
     1989        } else {
     1990            // Создаём новую запись
     1991            $this->save_process_log($query->found_posts, $processed, $success, $skipped, $errors, false);
     1992        }
     1993    }
     1994   
     1995    // ========================================
     1996    // ОТПРАВЛЯЕМ ОТВЕТ
     1997    // ========================================
     1998    wp_send_json_success(array(
     1999        'processed' => $processed,
     2000        'success' => $success,
     2001        'errors' => $errors,
     2002        'skipped' => $skipped,
     2003        'has_more' => $has_more,
     2004        'test_mode' => isset($settings['test_mode']) && $settings['test_mode'] == '1'
     2005    ));
     2006}
    17682007   
    17692008    /**
     
    24602699     * DeepL API
    24612700     */
    2462     private function translate_deepl($text, $source_lang, $target_lang, $settings) {
    2463         $api_key = isset($settings['translation_deepl_key']) ? $settings['translation_deepl_key'] : '';
    2464        
    2465         if (empty($api_key)) {
    2466             return new WP_Error('no_api_key', esc_html__('DeepL API key not specified', 'auto-image-tags'));
    2467         }
    2468        
    2469         // Определяем URL (бесплатный или платный API)
    2470         $url = (strpos($api_key, ':fx') !== false)
    2471             ? 'https://api-free.deepl.com/v2/translate'
    2472             : 'https://api.deepl.com/v2/translate';
    2473        
    2474         $response = wp_remote_post($url, array(
    2475             'timeout' => 15,
    2476             'headers' => array(
    2477                 'Authorization' => 'DeepL-Auth-Key ' . $api_key,
    2478                 'Content-Type' => 'application/x-www-form-urlencoded'
    2479             ),
    2480             'body' => array(
    2481                 'text' => $text,
    2482                 'source_lang' => strtoupper($source_lang),
    2483                 'target_lang' => strtoupper($target_lang)
    2484             )
    2485         ));
    2486        
    2487         if (is_wp_error($response)) {
    2488             return $response;
    2489         }
    2490        
    2491         $body = wp_remote_retrieve_body($response);
    2492         $data = json_decode($body, true);
    2493        
     2701/**
     2702 * DeepL API
     2703 */
     2704private function translate_deepl($text, $source_lang, $target_lang, $settings) {
     2705    $api_key = isset($settings['translation_deepl_key']) ? $settings['translation_deepl_key'] : '';
     2706   
     2707    if (empty($api_key)) {
     2708        return new WP_Error('no_api_key', esc_html__('DeepL API key not specified', 'auto-image-tags'));
     2709    }
     2710   
     2711    // Определяем URL (бесплатный или платный API)
     2712    // Бесплатные ключи заканчиваются на :fx
     2713    $url = (substr($api_key, -3) === ':fx')
     2714        ? 'https://api-free.deepl.com/v2/translate'
     2715        : 'https://api.deepl.com/v2/translate';
     2716   
     2717    // DeepL требует коды языков в верхнем регистре
     2718    $source_lang_upper = strtoupper($source_lang);
     2719    $target_lang_upper = strtoupper($target_lang);
     2720   
     2721    // Для некоторых языков DeepL требует указание варианта
     2722    // Например: EN-US, EN-GB, PT-BR, PT-PT
     2723    if ($target_lang_upper === 'EN') {
     2724        $target_lang_upper = 'EN-US'; // По умолчанию американский английский
     2725    }
     2726    if ($target_lang_upper === 'PT') {
     2727        $target_lang_upper = 'PT-BR'; // По умолчанию бразильский португальский
     2728    }
     2729   
     2730    $response = wp_remote_post($url, array(
     2731        'timeout' => 15,
     2732        'headers' => array(
     2733            'Authorization' => 'DeepL-Auth-Key ' . $api_key,
     2734            'Content-Type' => 'application/x-www-form-urlencoded'
     2735        ),
     2736        'body' => array(
     2737            'text' => $text,
     2738            'source_lang' => $source_lang_upper,
     2739            'target_lang' => $target_lang_upper
     2740        )
     2741    ));
     2742   
     2743    if (is_wp_error($response)) {
     2744        return $response;
     2745    }
     2746   
     2747    $response_code = wp_remote_retrieve_response_code($response);
     2748    $body = wp_remote_retrieve_body($response);
     2749    $data = json_decode($body, true);
     2750   
     2751    // Логирование для отладки (удали после исправления)
     2752    error_log('DeepL API Response Code: ' . $response_code);
     2753    error_log('DeepL API Response Body: ' . $body);
     2754   
     2755    // Проверка ошибок
     2756    if ($response_code !== 200) {
    24942757        if (isset($data['message'])) {
    24952758            return new WP_Error('api_error', sanitize_text_field($data['message']));
    24962759        }
    2497        
    2498         if (isset($data['translations'][0]['text'])) {
    2499             return sanitize_text_field($data['translations'][0]['text']);
    2500         }
    2501        
    2502         return new WP_Error('invalid_response', esc_html__('Invalid API response', 'auto-image-tags'));
    2503     }
     2760        return new WP_Error('api_error', sprintf(
     2761            esc_html__('DeepL API error: HTTP %d', 'auto-image-tags'),
     2762            $response_code
     2763        ));
     2764    }
     2765   
     2766    if (isset($data['message'])) {
     2767        return new WP_Error('api_error', sanitize_text_field($data['message']));
     2768    }
     2769   
     2770    if (isset($data['translations'][0]['text'])) {
     2771        return sanitize_text_field($data['translations'][0]['text']);
     2772    }
     2773   
     2774    return new WP_Error('invalid_response', esc_html__('Invalid API response', 'auto-image-tags'));
     2775}
    25042776
    25052777    /**
     
    28683140        );
    28693141       
    2870         // Передаём данные в JS
    28713142        wp_localize_script('autoimta-admin-js', 'autoimtaData', array(
    28723143            'ajaxurl' => admin_url('admin-ajax.php'),
     
    29183189        ));
    29193190    }
     3191   
     3192    /**
     3193     * Отключение устаревшего prettyPhoto из WooCommerce
     3194     */
     3195    public function disable_woocommerce_prettyphoto() {
     3196        // Отключаем prettyPhoto если загружен
     3197        wp_dequeue_script('prettyPhoto');
     3198        wp_dequeue_script('prettyPhoto-init');
     3199        wp_dequeue_style('woocommerce_prettyPhoto_css');
     3200       
     3201        // Отменяем регистрацию
     3202        wp_deregister_script('prettyPhoto');
     3203        wp_deregister_script('prettyPhoto-init');
     3204        wp_deregister_style('woocommerce_prettyPhoto_css');
     3205    }
    29203206}
    29213207
  • auto-image-tags/tags/2.1.0/readme.txt

    r3380076 r3387215  
    11=== Auto Image Tags ===
    22Contributors: mrbogdan
    3 Tags: seo, images, alt, media, woocommerce
     3Tags: image, alt, title, seo, media, optimization, woocommerce
    44Requires at least: 5.0
    5 Tested up to: 6.8
     5Tested up to: 6.4
    66Requires PHP: 7.2
    7 Stable tag: 2.0.0
     7Stable tag: 2.1.0
    88License: GPLv3 or later
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
    1010
    11 Automatically add ALT, TITLE, Caption and Description to images. WooCommerce integration and translation support.
     11Automatically add ALT, TITLE, Caption and Description tags to WordPress media library images.
    1212
    1313== Description ==
    1414
    15 Auto Image Tags automatically generates ALT tags, TITLE attributes, captions and descriptions for all images in your WordPress media library. The plugin processes filenames intelligently, removing camera markers, splitting CamelCase, and applying customizable rules to create SEO-friendly image tags.
     15Auto Image Tags automatically generates ALT, TITLE, Caption and Description for your WordPress media library images based on filename, post title, or custom templates.
    1616
    1717**Key Features:**
    1818
    19 * Automatic generation of ALT, TITLE, Caption and Description
     19* Automatic ALT, TITLE, Caption and Description generation
    2020* Preview changes before applying
    2121* Individual overwrite settings for each attribute
    22 * Bulk processing with advanced filters
     22* Bulk processing filters (by date, posts, status)
     23* Advanced filename cleanup
     24* Stop words to remove unwanted words
    2325* Test mode for safe testing
    24 * Complete processing history and statistics
    25 * WooCommerce product image integration
    26 * Multi-language translation system (5 services)
    27 * Export/Import settings
     26* Processing history and statistics
     27* Multilingual support
     28* Extended template variables
     29* Translation system (5 services: Google, DeepL, Yandex, LibreTranslate, MyMemory)
     30* WooCommerce integration
    2831
    29 **Perfect for:**
     32**WooCommerce Integration:**
    3033
    31 * SEO optimization
    32 * Accessibility compliance
    33 * WooCommerce stores
    34 * Multi-language sites
    35 * Bulk image management
     34* Automatic processing of product images
     35* Gallery image support
     36* Use product title, category, and SKU in tags
     37* Seamless integration with product workflow
    3638
    37 == External Services ==
     39**Translation Support:**
    3840
    39 This plugin can optionally connect to external translation services to translate image tags. Translation features are completely optional and disabled by default.
    40 
    41 **Google Translate API**
    42 * Used for: Translating image tags (ALT, TITLE, Caption, Description)
    43 * Data sent: Text to translate, source/target language
    44 * When: Only when user enables translation and provides API key
    45 * Service: https://cloud.google.com/translate
    46 * Terms: https://cloud.google.com/terms
    47 * Privacy: https://policies.google.com/privacy
    48 
    49 **DeepL API**
    50 * Used for: Translating image tags
    51 * Data sent: Text to translate, source/target language
    52 * When: Only when user enables translation and provides API key
    53 * Service: https://www.deepl.com/pro-api
    54 * Terms: https://www.deepl.com/terms
    55 * Privacy: https://www.deepl.com/privacy
    56 
    57 **Yandex Translator API**
    58 * Used for: Translating image tags
    59 * Data sent: Text to translate, source/target language
    60 * When: Only when user enables translation and provides API key
    61 * Service: https://cloud.yandex.com/services/translate
    62 * Terms: https://yandex.com/legal/cloud_terms_of_use
    63 * Privacy: https://yandex.com/legal/confidential
    64 
    65 **LibreTranslate**
    66 * Used for: Translating image tags
    67 * Data sent: Text to translate, source/target language
    68 * When: Only when user enables translation and provides server URL
    69 * Service: https://libretranslate.com
    70 * Terms: Open source, self-hosted option available
    71 * Privacy: https://github.com/LibreTranslate/LibreTranslate
    72 
    73 **MyMemory Translation API**
    74 * Used for: Translating image tags
    75 * Data sent: Text to translate, source/target language, optional email
    76 * When: Only when user enables translation
    77 * Service: https://mymemory.translated.net
    78 * Terms: https://mymemory.translated.net/doc/terms.php
    79 * Privacy: https://mymemory.translated.net/doc/privacy.php
    80 
    81 **Important Notes:**
    82 * All translation services are OPTIONAL and disabled by default
    83 * No data is sent unless user actively enables translation and configures API keys
    84 * Users must review and accept terms of service for each translation provider they choose to use
    85 * Translation can be completely avoided by not enabling this feature
     41* Google Translate API
     42* DeepL API (500k chars/month free)
     43* Yandex Translator (1M chars/month free)
     44* LibreTranslate (free, open-source)
     45* MyMemory (free, 10k chars/day)
    8646
    8747== Installation ==
    8848
    89491. Upload the plugin files to `/wp-content/plugins/auto-image-tags/`
    90 2. Activate the plugin through the 'Plugins' menu in WordPress
    91 3. Go to Auto Image Tags settings to configure
     502. Activate the plugin through the 'Plugins' screen in WordPress
     513. Go to Auto Image Tags menu to configure settings
     524. Use Process Images tab to bulk process existing images
    9253
    9354== Frequently Asked Questions ==
    9455
    95 = Does this work with WooCommerce? =
     56= Does this plugin work with WooCommerce? =
    9657
    97 Yes! Version 2.0 includes full WooCommerce integration for product images.
    98 
    99 = Will it overwrite my existing tags? =
    100 
    101 Only if you enable the "Overwrite existing" option for each tag type. You have individual control.
     58Yes! Version 2.0+ includes full WooCommerce integration with product image processing.
    10259
    10360= Can I preview changes before applying? =
    10461
    105 Yes! Use the Preview tab to see exactly what will change.
     62Yes, use the Preview tab to see how tags will look before processing.
    10663
    10764= Does it support translation? =
    10865
    109 Yes! Supports 5 translation services (Google, DeepL, Yandex, LibreTranslate, MyMemory). All are optional.
     66Yes, the plugin supports 5 translation services including free options.
    11067
    111 = Is there a Pro version? =
     68= Will it overwrite existing ALT tags? =
    11269
    113 No! All features are completely free, no Pro version exists.
     70Only if you enable the "Overwrite existing" option for each field.
    11471
    11572== Screenshots ==
    11673
    117 1. Main settings page with tag formats
    118 2. Process images with advanced filters
     741. Settings page with format options
     752. Bulk processing with filters
    119763. Preview changes before applying
    120 4. Statistics and processing history
     774. Processing statistics and history
     785. Translation settings
     796. WooCommerce integration
    12180
    12281== Changelog ==
    12382
    124 = 2.0.0 =
    125 * NEW: Translation system with 5 API services (optional)
    126 * NEW: WooCommerce product image integration
    127 * NEW: Preview tab with before/after comparison
    128 * NEW: Tools tab (bulk delete, export/import settings)
    129 * NEW: Caption and Description support
    130 * NEW: Individual overwrite settings for each tag
    131 * NEW: Advanced processing filters
    132 * NEW: Test mode for safe testing
    133 * NEW: Complete statistics and history
    134 * NEW: Full Russian translation
    135 * IMPROVED: Enhanced filename cleanup
    136 * IMPROVED: Extended template variables
    137 * IMPROVED: Better UI/UX with 7 tabs
    138 * IMPROVED: AJAX batch processing
     83= 2.1.0 (2025-10-30) =
     84**Bug Fixes:**
     85* Fixed HTTP/2 Protocol error during bulk processing
     86* Fixed progress bar calculation - now shows real progress (0% → 100%)
     87* Fixed PHP Parse Error - moved `disable_woocommerce_prettyphoto()` inside class
     88* Added protection against timeouts with increased execution limits
    13989
    140 = 1.2.0 =
     90**Performance:**
     91* Reduced batch size from 10 to 5 images for better stability
     92* Removed 5000 image limit - now handles unlimited images
     93* Improved handling of large data volumes
     94* Disabled output buffering to prevent server conflicts
     95
     96**New Features:**
     97* Auto-disable deprecated prettyPhoto from WooCommerce
     98* Dynamic progress calculation with current state display (N / TOTAL)
     99* Enhanced error handling with detailed logging
     100
     101= 2.0.0 (2025-10-20) =
     102**Major Update:**
     103* Translation system with 5 services
     104* WooCommerce integration
     105* Tools tab (bulk delete, export/import settings)
     106* Preview tab with before/after comparison
     107* Caption and Description support
     108* Individual overwrite settings
     109* Advanced filters
     110* Enhanced filename cleanup
     111* Custom stop words
     112* Test mode
     113* Statistics and history
     114* Language selection
     115
     116= 1.0.0 (2025-10-07) =
    141117* Initial release
     118* Basic ALT and TITLE tag generation
     119* Filename processing
     120* Bulk operations
    142121
    143122== Upgrade Notice ==
    144123
     124= 2.1.0 =
     125Critical bug fixes for HTTP/2 errors and progress bar. Recommended update for all users.
     126
    145127= 2.0.0 =
    146 Major update with translation system, WooCommerce integration, and many new features. Backup recommended before updating.
     128Major update with translation, WooCommerce integration, and many new features.
     129
     130== Support ==
     131
     132For support, feature requests, or bug reports:
     133* Telegram: @shapovalovbogdan
     134* GitHub: https://github.com/imrbogdan/auto-image-tags
     135
     136== Privacy ==
     137
     138This plugin does not collect or store any user data. Translation services are only used when explicitly configured and activated by the user.
     139
     140== Credits ==
     141
     142Developed by Shapovalov Bogdan
     143Free and open-source, no Pro version!
  • auto-image-tags/trunk/assets/css/admin-style.css

    r3380076 r3387215  
    1 /* =================================
    2    AUTO IMAGE TAGS - ADMIN STYLES
    3    ================================= */
     1/**
     2 * AUTO IMAGE TAGS - Admin Styles
     3 * Version: 2.1.0
     4 * Author: Shapovalov Bogdan
     5 */
    46
    57.autoimta-settings-form {
     
    351353    border-radius: 3px;
    352354}
     355/* Support Links */
     356.autoimta-support-links {
     357    margin-top: 20px;
     358    display: flex;
     359    gap: 10px;
     360    flex-wrap: wrap;
     361}
     362
     363.autoimta-support-links .button {
     364    text-decoration: none;
     365}
     366
     367/* Version highlights */
     368.autoimta-info-section h4 {
     369    margin-top: 15px;
     370    margin-bottom: 10px;
     371    font-size: 14px;
     372    color: #2271b1;
     373}
     374
     375.autoimta-info-section ul {
     376    margin-left: 20px;
     377}
     378
     379.autoimta-info-section ul li {
     380    margin-bottom: 8px;
     381    line-height: 1.6;
     382}
     383
     384.autoimta-info-section del {
     385    color: #999;
     386}
  • auto-image-tags/trunk/assets/js/admin-main.js

    r3380076 r3387215  
    11/**
    22 * AUTO IMAGE TAGS - Admin JavaScript
    3  * Version: 2.0.0
     3 * Version: 2.1.0
    44 */
    55
     
    171171    }
    172172
    173     /**
    174      * Запуск обработки изображений
    175      */
    176     function startProcessing() {
    177         processing = true;
    178         let offset = 0;
    179         let totalProcessed = 0;
    180         let totalSuccess = 0;
    181         let totalErrors = 0;
    182         let totalSkipped = 0;
    183 
    184         $('#autoimta-process-btn').prop('disabled', true);
    185         $('#autoimta-progress').show();
    186         $('#autoimta-results').hide();
    187 
    188         const filters = {
    189             date: $('#date_filter').val(),
    190             status: $('#status_filter').val(),
    191             post: $('#post_filter').val()
    192         };
    193 
    194         function processBatch() {
    195             $.ajax({
    196                 url: autoimtaData.ajaxurl,
    197                 type: 'POST',
    198                 data: {
    199                     action: 'autoimta_process_existing_images',
    200                     nonce: autoimtaData.nonce,
    201                     offset: offset,
    202                     filters: filters
    203                 },
    204                 success: function(response) {
    205                     if (response.success) {
    206                         const data = response.data;
    207                        
    208                         totalProcessed += data.processed;
    209                         totalSuccess += data.success;
    210                         totalErrors += data.errors;
    211                         totalSkipped += data.skipped;
    212 
    213                         const progress = Math.min(100, Math.round((offset / 100) * 100));
    214                         updateProgress(progress);
    215 
    216                         $('#autoimta-status-text').html(
    217                             autoimtaData.strings.processed + ' ' + totalProcessed + '<br>' +
    218                             autoimtaData.strings.success + ' ' + totalSuccess + '<br>' +
    219                             autoimtaData.strings.skipped + ' ' + totalSkipped + '<br>' +
    220                             autoimtaData.strings.errors + ' ' + totalErrors
    221                         );
    222 
    223                         if (data.has_more) {
    224                             offset += data.processed;
    225                             processBatch();
    226                         } else {
    227                             processingComplete(totalProcessed, totalSuccess, totalSkipped, totalErrors, data.test_mode);
    228                         }
     173/**
     174 * Запуск обработки изображений
     175 */
     176function startProcessing() {
     177    processing = true;
     178    let offset = 0;
     179    let totalProcessed = 0;
     180    let totalSuccess = 0;
     181    let totalErrors = 0;
     182    let totalSkipped = 0;
     183    let totalImages = 0;
     184
     185    $('#autoimta-process-btn').prop('disabled', true);
     186    $('#autoimta-progress').show();
     187    $('#autoimta-results').hide();
     188
     189    const filters = {
     190        date: $('#date_filter').val(),
     191        status: $('#status_filter').val(),
     192        post: $('#post_filter').val()
     193    };
     194
     195    // Получаем общее количество изображений ДЛЯ ОБРАБОТКИ
     196    $.ajax({
     197        url: autoimtaData.ajaxurl,
     198        type: 'POST',
     199        data: {
     200            action: 'autoimta_get_images_count',
     201            nonce: autoimtaData.nonce,
     202            filters: filters
     203        },
     204        success: function(response) {
     205            if (response.success) {
     206                // ✅ ИСПОЛЬЗУЕМ needs_processing - только изображения БЕЗ ТЕГОВ
     207                totalImages = response.data.needs_processing;
     208               
     209                if (totalImages === 0) {
     210                    processingError(autoimtaData.strings.no_images);
     211                    return;
     212                }
     213               
     214                processBatch(); // Начинаем обработку
     215            } else {
     216                processingError(response.data.message);
     217            }
     218        },
     219        error: function() {
     220            processingError(autoimtaData.strings.connection_error);
     221        }
     222    });
     223
     224    function processBatch() {
     225        $.ajax({
     226            url: autoimtaData.ajaxurl,
     227            type: 'POST',
     228            data: {
     229                action: 'autoimta_process_existing_images',
     230                nonce: autoimtaData.nonce,
     231                offset: offset,
     232                filters: filters
     233            },
     234            success: function(response) {
     235                if (response.success) {
     236                    const data = response.data;
     237                   
     238                    totalProcessed += data.processed;
     239                    totalSuccess += data.success;
     240                    totalErrors += data.errors;
     241                    totalSkipped += data.skipped;
     242
     243                    // Правильный расчёт прогресса
     244                    const currentProcessed = offset + data.processed;
     245                    let progress = 0;
     246                    if (totalImages > 0) {
     247                        progress = Math.round((currentProcessed / totalImages) * 100);
     248                        if (progress > 100) progress = 100;
     249                    }
     250                    updateProgress(progress);
     251
     252                    $('#autoimta-status-text').html(
     253                        autoimtaData.strings.processed + ' ' + currentProcessed + ' / ' + totalImages + '<br>' +
     254                        autoimtaData.strings.success + ' ' + totalSuccess + '<br>' +
     255                        autoimtaData.strings.skipped + ' ' + totalSkipped + '<br>' +
     256                        autoimtaData.strings.errors + ' ' + totalErrors
     257                    );
     258
     259                    if (data.has_more) {
     260                        offset += data.processed;
     261                        processBatch();
    229262                    } else {
    230                         processingError(response.data.message);
     263                        processingComplete(totalProcessed, totalSuccess, totalSkipped, totalErrors, data.test_mode);
    231264                    }
    232                 },
    233                 error: function() {
    234                     processingError(autoimtaData.strings.connection_error);
    235                 }
    236             });
    237         }
    238 
    239         processBatch();
    240     }
     265                } else {
     266                    processingError(response.data.message);
     267                }
     268            },
     269            error: function() {
     270                processingError(autoimtaData.strings.connection_error);
     271            }
     272        });
     273    }
     274}
    241275
    242276    function processingComplete(processed, success, skipped, errors, testMode) {
  • auto-image-tags/trunk/auto-image-tags.php

    r3380076 r3387215  
    44 * Plugin URI: https://wordpress.org/plugins/auto-image-tags/
    55 * Description: Automatically add ALT, TITLE, Caption and Description tags to WordPress media library images. WooCommerce integration and optional translation support.
    6  * Version: 2.0.0
     6 * Version: 2.1.0
    77 * Author: mrbogdan
    88 * Author URI: https://profiles.wordpress.org/mrbogdan/
     
    2323define('AUTOIMTA_PLUGIN_DIR', plugin_dir_path(__FILE__));
    2424define('AUTOIMTA_PLUGIN_URL', plugin_dir_url(__FILE__));
    25 define('AUTOIMTA_VERSION', '2.0.0');
     25define('AUTOIMTA_VERSION', '2.1.0');
    2626
    2727// Проверка требований
     
    7474        add_filter('add_attachment', array($this, 'handle_image_upload'));
    7575        add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
    76        
     76        add_action('wp_enqueue_scripts', array($this, 'disable_woocommerce_prettyphoto'), 99);
    7777        add_action('wp_ajax_autoimta_process_existing_images', array($this, 'ajax_process_existing_images'));
    7878        add_action('wp_ajax_autoimta_get_images_count', array($this, 'ajax_get_images_count'));
     
    991991    }
    992992
    993     /**
    994      * Вкладка "О плагине"
    995      */
    996     private function render_about_tab() {
    997         ?>
    998         <div class="autoimta-about-box">
    999             <h2><?php esc_html_e('About Auto Image Tags Plugin', 'auto-image-tags'); ?></h2>
    1000            
    1001             <div class="autoimta-info-section">
    1002                 <h3><?php esc_html_e('Plugin Information', 'auto-image-tags'); ?></h3>
    1003                 <table class="autoimta-info-table">
    1004                     <tr>
    1005                         <td><strong><?php esc_html_e('Version:', 'auto-image-tags'); ?></strong></td>
    1006                         <td><?php echo esc_html(AUTOIMTA_VERSION); ?></td>
    1007                     </tr>
    1008                     <tr>
    1009                         <td><strong><?php esc_html_e('Author:', 'auto-image-tags'); ?></strong></td>
    1010                         <td>Shapovalov Bogdan</td>
    1011                     </tr>
    1012                     <tr>
    1013                         <td><strong><?php esc_html_e('Telegram:', 'auto-image-tags'); ?></strong></td>
    1014                         <td><a href="https://t.me/shapovalovbogdan" target="_blank" rel="noopener noreferrer">@shapovalovbogdan</a></td>
    1015                     </tr>
    1016                     <tr>
    1017                         <td><strong><?php esc_html_e('GitHub:', 'auto-image-tags'); ?></strong></td>
    1018                         <td><a href="https://github.com/imrbogdan/auto-image-tags" target="_blank" rel="noopener noreferrer">Repository</a></td>
    1019                     </tr>
    1020                 </table>
    1021             </div>
    1022            
    1023             <div class="autoimta-info-section">
    1024                 <h3><?php esc_html_e('Plugin Features', 'auto-image-tags'); ?></h3>
    1025                 <ul>
    1026                     <li>✅ <?php esc_html_e('Automatic ALT, TITLE, Caption and Description generation', 'auto-image-tags'); ?></li>
    1027                     <li>✅ <?php esc_html_e('Preview changes before applying', 'auto-image-tags'); ?></li>
    1028                     <li>✅ <?php esc_html_e('Individual overwrite settings for each attribute', 'auto-image-tags'); ?></li>
    1029                     <li>✅ <?php esc_html_e('Bulk processing filters (by date, posts, status)', 'auto-image-tags'); ?></li>
    1030                     <li>✅ <?php esc_html_e('Advanced filename cleanup', 'auto-image-tags'); ?></li>
    1031                     <li>✅ <?php esc_html_e('Stop words to remove unwanted words', 'auto-image-tags'); ?></li>
    1032                     <li>✅ <?php esc_html_e('Test mode for safe testing', 'auto-image-tags'); ?></li>
    1033                     <li>✅ <?php esc_html_e('Processing history and statistics', 'auto-image-tags'); ?></li>
    1034                     <li>✅ <?php esc_html_e('Multilingual support', 'auto-image-tags'); ?></li>
    1035                     <li>✅ <?php esc_html_e('Extended template variables', 'auto-image-tags'); ?></li>
    1036                     <li>✅ <?php esc_html_e('Translation system (5 services)', 'auto-image-tags'); ?></li>
    1037                     <li>✅ <?php esc_html_e('WooCommerce integration', 'auto-image-tags'); ?></li>
    1038                 </ul>
    1039             </div>
    1040 
    1041             <div class="autoimta-info-section">
    1042                 <h3><?php esc_html_e('What\'s New in Version 2.0.0', 'auto-image-tags'); ?></h3>
    1043                 <ul>
    1044                     <li>🆕 <?php esc_html_e('Translation system with 5 services', 'auto-image-tags'); ?></li>
    1045                     <li>🆕 <?php esc_html_e('WooCommerce integration', 'auto-image-tags'); ?></li>
    1046                     <li>🆕 <?php esc_html_e('Tools tab (bulk delete, export/import settings)', 'auto-image-tags'); ?></li>
    1047                     <li>🆕 <?php esc_html_e('Preview tab with before/after comparison', 'auto-image-tags'); ?></li>
    1048                     <li>🆕 <?php esc_html_e('Caption and Description support', 'auto-image-tags'); ?></li>
    1049                     <li>🆕 <?php esc_html_e('Individual overwrite settings', 'auto-image-tags'); ?></li>
    1050                     <li>🆕 <?php esc_html_e('Advanced filters', 'auto-image-tags'); ?></li>
    1051                     <li>🆕 <?php esc_html_e('Enhanced filename cleanup', 'auto-image-tags'); ?></li>
    1052                     <li>🆕 <?php esc_html_e('Custom stop words', 'auto-image-tags'); ?></li>
    1053                     <li>🆕 <?php esc_html_e('Test mode', 'auto-image-tags'); ?></li>
    1054                     <li>🆕 <?php esc_html_e('Statistics and history', 'auto-image-tags'); ?></li>
    1055                     <li>🆕 <?php esc_html_e('Language selection', 'auto-image-tags'); ?></li>
    1056                 </ul>
    1057             </div>
    1058            
    1059             <div class="autoimta-info-section">
    1060                 <h3><?php esc_html_e('Support and Feedback', 'auto-image-tags'); ?></h3>
    1061                 <p><?php esc_html_e('If you have questions, suggestions or found a bug, contact me via Telegram.', 'auto-image-tags'); ?></p>
    1062                 <p><?php esc_html_e('Plugin is distributed for free to help the WordPress community.', 'auto-image-tags'); ?></p>
    1063                 <p><strong><?php esc_html_e('All features are available for free, no Pro version!', 'auto-image-tags'); ?></strong></p>
     993/**
     994 * Вкладка "О плагине"
     995 */
     996private function render_about_tab() {
     997    ?>
     998    <div class="autoimta-about-box">
     999        <h2><?php esc_html_e('About Auto Image Tags Plugin', 'auto-image-tags'); ?></h2>
     1000       
     1001        <div class="autoimta-info-section">
     1002            <h3><?php esc_html_e('Author Information', 'auto-image-tags'); ?></h3>
     1003            <table class="autoimta-info-table">
     1004                <tr>
     1005                    <td><strong><?php esc_html_e('Developer:', 'auto-image-tags'); ?></strong></td>
     1006                    <td>Shapovalov Bogdan</td>
     1007                </tr>
     1008                <tr>
     1009                    <td><strong><?php esc_html_e('Telegram:', 'auto-image-tags'); ?></strong></td>
     1010                    <td><a href="https://t.me/shapovalovbogdan" target="_blank" rel="noopener noreferrer">@shapovalovbogdan</a></td>
     1011                </tr>
     1012                <tr>
     1013                    <td><strong><?php esc_html_e('GitHub:', 'auto-image-tags'); ?></strong></td>
     1014                    <td><a href="https://github.com/imrbogdan/auto-image-tags" target="_blank" rel="noopener noreferrer">github.com/imrbogdan/auto-image-tags</a></td>
     1015                </tr>
     1016                <tr>
     1017                    <td><strong><?php esc_html_e('WordPress Plugin:', 'auto-image-tags'); ?></strong></td>
     1018                    <td><a href="https://wordpress.org/plugins/auto-image-tags/" target="_blank" rel="noopener noreferrer">wordpress.org/plugins/auto-image-tags</a></td>
     1019                </tr>
     1020            </table>
     1021        </div>
     1022       
     1023        <div class="autoimta-info-section">
     1024            <h3><?php esc_html_e('Plugin Features', 'auto-image-tags'); ?></h3>
     1025           
     1026            <h4><?php esc_html_e('Core Functionality', 'auto-image-tags'); ?></h4>
     1027            <ul>
     1028                <li>✅ Automatic generation of ALT, TITLE, Caption and Description tags</li>
     1029                <li>✅ Process images on upload or bulk process existing images</li>
     1030                <li>✅ Multiple tag format options (filename, post title, site name, custom templates)</li>
     1031                <li>✅ Individual overwrite settings for each field (ALT, TITLE, Caption, Description)</li>
     1032                <li>✅ Advanced filename cleanup (remove hyphens, numbers, size suffixes, CamelCase splitting)</li>
     1033                <li>✅ Stop words filtering to remove unwanted terms</li>
     1034                <li>✅ Custom template variables ({filename}, {posttitle}, {sitename}, {category}, {tags}, {author}, {date}, {year}, {month})</li>
     1035            </ul>
     1036           
     1037            <h4><?php esc_html_e('Processing & Filters', 'auto-image-tags'); ?></h4>
     1038            <ul>
     1039                <li>✅ Bulk processing with advanced filters (date range, status, specific posts)</li>
     1040                <li>✅ Filter by images without ALT, without TITLE, or without any tags</li>
     1041                <li>✅ Preview changes before applying them</li>
     1042                <li>✅ Test mode for safe testing without saving changes</li>
     1043                <li>✅ Process unlimited images (no limits)</li>
     1044                <li>✅ Batch processing for optimal performance</li>
     1045            </ul>
     1046           
     1047            <h4><?php esc_html_e('Translation System', 'auto-image-tags'); ?></h4>
     1048            <ul>
     1049                <li>✅ Integrated translation for image tags</li>
     1050                <li>✅ Support for 5 translation services:
     1051                    <ul>
     1052                        <li>• Google Translate API</li>
     1053                        <li>• DeepL API (500,000 chars/month free)</li>
     1054                        <li>• Yandex Translator (1,000,000 chars/month free)</li>
     1055                        <li>• LibreTranslate (completely free, open-source)</li>
     1056                        <li>• MyMemory (10,000 chars/day free)</li>
     1057                    </ul>
     1058                </li>
     1059                <li>✅ Automatic translation on image upload</li>
     1060                <li>✅ Bulk translation for existing images</li>
     1061                <li>✅ Test translation before processing</li>
     1062            </ul>
     1063           
     1064            <h4><?php esc_html_e('WooCommerce Integration', 'auto-image-tags'); ?></h4>
     1065            <ul>
     1066                <li>✅ Automatic processing of product images</li>
     1067                <li>✅ Process product gallery images</li>
     1068                <li>✅ Use product title, category and SKU in tags</li>
     1069                <li>✅ Seamless integration with product workflow</li>
     1070            </ul>
     1071           
     1072            <h4><?php esc_html_e('Tools & Management', 'auto-image-tags'); ?></h4>
     1073            <ul>
     1074                <li>✅ Bulk tag removal tool</li>
     1075                <li>✅ Export/import plugin settings</li>
     1076                <li>✅ Processing statistics and history</li>
     1077                <li>✅ Detailed logging for debugging</li>
     1078                <li>✅ Multilingual interface (English/Russian)</li>
     1079            </ul>
     1080           
     1081            <h4><?php esc_html_e('Technical Features', 'auto-image-tags'); ?></h4>
     1082            <ul>
     1083                <li>✅ Protection against timeouts and server overload</li>
     1084                <li>✅ Optimized for large image libraries</li>
     1085                <li>✅ Compatible with WordPress 5.0+</li>
     1086                <li>✅ Compatible with PHP 7.2+</li>
     1087                <li>✅ Database logging for audit trails</li>
     1088                <li>✅ Clean, well-documented code</li>
     1089            </ul>
     1090        </div>
     1091       
     1092        <div class="autoimta-info-section">
     1093            <h3><?php esc_html_e('Support', 'auto-image-tags'); ?></h3>
     1094            <p><?php esc_html_e('For questions, suggestions or bug reports, please contact me via Telegram or create an issue on GitHub.', 'auto-image-tags'); ?></p>
     1095            <p><strong><?php esc_html_e('This plugin is completely free with no Pro version. All features are available to everyone!', 'auto-image-tags'); ?></strong></p>
     1096           
     1097            <div class="autoimta-support-links">
     1098                <a href="https://t.me/shapovalovbogdan" class="button button-primary" target="_blank" rel="noopener noreferrer">
     1099                    📱 Contact on Telegram
     1100                </a>
     1101                <a href="https://github.com/imrbogdan/auto-image-tags" class="button button-secondary" target="_blank" rel="noopener noreferrer">
     1102                    ⭐ Star on GitHub
     1103                </a>
     1104                <a href="https://wordpress.org/plugins/auto-image-tags/" class="button button-secondary" target="_blank" rel="noopener noreferrer">
     1105                    💙 WordPress.org
     1106                </a>
    10641107            </div>
    10651108        </div>
    1066         <?php
    1067     }
     1109    </div>
     1110    <?php
     1111}
    10681112   
    10691113    /**
     
    15191563            'post_mime_type' => 'image',
    15201564            'post_status' => 'inherit',
    1521             'posts_per_page' => 1000,
     1565            'posts_per_page' => -1,
    15221566            'fields' => 'ids'
    15231567        );
     
    15531597        $total = $query->found_posts;
    15541598       
    1555         if ($total > 5000) {
    1556             wp_send_json_error(array(
    1557                 'message' => esc_html__('Too many images to count. Use filters.', 'auto-image-tags')
    1558             ));
    1559             return;
    1560         }
    1561        
    15621599        $without_alt = 0;
    15631600        $without_title = 0;
     
    15831620           
    15841621            // Подсчет изображений для обработки
    1585             $will_process = false;
    1586             if (isset($settings['alt_format']) && $settings['alt_format'] !== 'disabled' && (isset($settings['overwrite_alt']) && $settings['overwrite_alt'] == '1' || empty($alt))) {
    1587                 $will_process = true;
    1588             }
    1589             if (isset($settings['title_format']) && $settings['title_format'] !== 'disabled' && (isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1' || empty($title))) {
    1590                 $will_process = true;
    1591             }
    1592             if ($will_process) {
    1593                 $needs_processing++;
    1594             }
     1622$will_process = false;
     1623if (isset($settings['alt_format']) && $settings['alt_format'] !== 'disabled' && (isset($settings['overwrite_alt']) && $settings['overwrite_alt'] == '1' || empty($alt))) {
     1624    $will_process = true;
     1625}
     1626if (isset($settings['title_format']) && $settings['title_format'] !== 'disabled' && (isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1' || empty($title))) {
     1627    $will_process = true;
     1628}
     1629if ($will_process) {
     1630    $needs_processing++;
     1631}
    15951632        }
    15961633       
     
    16811718    }
    16821719   
    1683     /**
    1684      * AJAX: Обработка существующих изображений
    1685      */
    1686     public function ajax_process_existing_images() {
    1687         check_ajax_referer('autoimta_ajax_nonce', 'nonce');
    1688        
    1689         if (!current_user_can('manage_options')) {
    1690             wp_send_json_error(array('message' => esc_html__('Insufficient permissions', 'auto-image-tags')));
    1691             return;
    1692         }
    1693        
    1694         $settings = get_option('autoimta_settings', array());
    1695         $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
    1696         $batch_size = 10;
    1697         $filters = isset($_POST['filters']) && is_array($_POST['filters']) ? array_map('sanitize_text_field', wp_unslash($_POST['filters'])) : array();
    1698        
    1699         $args = array(
    1700             'post_type' => 'attachment',
    1701             'post_mime_type' => 'image',
    1702             'post_status' => 'inherit',
    1703             'posts_per_page' => $batch_size,
    1704             'offset' => $offset,
    1705             'fields' => 'ids'
     1720/**
     1721 * AJAX: Обработка существующих изображений
     1722 */
     1723/**
     1724 * AJAX: Обработка существующих изображений
     1725 */
     1726public function ajax_process_existing_images() {
     1727    // ========================================
     1728    // ЗАЩИТА ОТ HTTP2 ОШИБКИ И ТАЙМАУТОВ
     1729    // ========================================
     1730   
     1731    // Увеличиваем лимиты
     1732    @ini_set('max_execution_time', 300); // 5 минут
     1733    @ini_set('memory_limit', '256M');
     1734    @set_time_limit(300);
     1735   
     1736    // Отключаем вывод ошибок в JSON ответ
     1737    @ini_set('display_errors', 0);
     1738    @ini_set('log_errors', 1);
     1739   
     1740    // Отключаем буферизацию для предотвращения таймаутов
     1741    if (function_exists('apache_setenv')) {
     1742        @apache_setenv('no-gzip', '1');
     1743    }
     1744    @ini_set('zlib.output_compression', '0');
     1745    @ini_set('implicit_flush', '1');
     1746   
     1747    // Очищаем все буферы
     1748    while (ob_get_level() > 0) {
     1749        ob_end_clean();
     1750    }
     1751   
     1752    // ========================================
     1753    // ОСНОВНАЯ ЛОГИКА
     1754    // ========================================
     1755   
     1756    check_ajax_referer('autoimta_ajax_nonce', 'nonce');
     1757   
     1758    if (!current_user_can('manage_options')) {
     1759        wp_send_json_error(array('message' => esc_html__('Insufficient permissions', 'auto-image-tags')));
     1760        return;
     1761    }
     1762   
     1763    $settings = get_option('autoimta_settings', array());
     1764    $offset = isset($_POST['offset']) ? intval($_POST['offset']) : 0;
     1765    $batch_size = 5; // Размер батча: 5 изображений за раз
     1766    $filters = isset($_POST['filters']) && is_array($_POST['filters']) ? array_map('sanitize_text_field', wp_unslash($_POST['filters'])) : array();
     1767   
     1768    // ========================================
     1769    // БАЗОВЫЕ АРГУМЕНТЫ ЗАПРОСА
     1770    // ========================================
     1771    $args = array(
     1772        'post_type' => 'attachment',
     1773        'post_mime_type' => 'image',
     1774        'post_status' => 'inherit',
     1775        'posts_per_page' => $batch_size,
     1776        'offset' => $offset,
     1777        'fields' => 'ids'
     1778    );
     1779   
     1780    // ========================================
     1781    // ФИЛЬТР ПО ДАТЕ
     1782    // ========================================
     1783    if (!empty($filters['date']) && $filters['date'] !== 'all') {
     1784        $date_query = array();
     1785        switch ($filters['date']) {
     1786            case 'today':
     1787                $date_query = array('after' => 'today', 'inclusive' => true);
     1788                break;
     1789            case 'week':
     1790                $date_query = array('after' => '1 week ago', 'inclusive' => true);
     1791                break;
     1792            case 'month':
     1793                $date_query = array('after' => '1 month ago', 'inclusive' => true);
     1794                break;
     1795            case 'year':
     1796                $date_query = array('after' => '1 year ago', 'inclusive' => true);
     1797                break;
     1798        }
     1799        if (!empty($date_query)) {
     1800            $args['date_query'] = array($date_query);
     1801        }
     1802    }
     1803   
     1804    // ========================================
     1805    // ФИЛЬТР ПО ПОСТУ
     1806    // ========================================
     1807    if (!empty($filters['post']) && $filters['post'] !== 'all') {
     1808        $args['post_parent'] = intval($filters['post']);
     1809    }
     1810   
     1811    // ========================================
     1812    // НОВОЕ: ФИЛЬТР ПО СТАТУСУ ТЕГОВ
     1813    // ========================================
     1814    if (!empty($filters['status']) && $filters['status'] !== 'all') {
     1815        switch ($filters['status']) {
     1816            case 'no_alt':
     1817                // Изображения без ALT
     1818                $args['meta_query'] = array(
     1819                    'relation' => 'OR',
     1820                    array(
     1821                        'key' => '_wp_attachment_image_alt',
     1822                        'compare' => 'NOT EXISTS'
     1823                    ),
     1824                    array(
     1825                        'key' => '_wp_attachment_image_alt',
     1826                        'value' => '',
     1827                        'compare' => '='
     1828                    )
     1829                );
     1830                break;
     1831               
     1832            case 'no_title':
     1833                // Изображения без TITLE
     1834                // Будем фильтровать после запроса, т.к. WordPress не поддерживает meta_query для post_title
     1835                break;
     1836               
     1837            case 'no_tags':
     1838                // Изображения без ALT (TITLE проверим после запроса)
     1839                $args['meta_query'] = array(
     1840                    'relation' => 'OR',
     1841                    array(
     1842                        'key' => '_wp_attachment_image_alt',
     1843                        'compare' => 'NOT EXISTS'
     1844                    ),
     1845                    array(
     1846                        'key' => '_wp_attachment_image_alt',
     1847                        'value' => '',
     1848                        'compare' => '='
     1849                    )
     1850                );
     1851                break;
     1852        }
     1853    } else {
     1854        // ========================================
     1855        // ЕСЛИ ФИЛЬТР "ВСЕ ИЗОБРАЖЕНИЯ" - ОБРАБАТЫВАЕМ ТОЛЬКО БЕЗ ТЕГОВ
     1856        // ========================================
     1857        // Проверяем настройки: если НЕ включена перезапись, фильтруем по пустым тегам
     1858        $overwrite_alt = isset($settings['overwrite_alt']) && $settings['overwrite_alt'] == '1';
     1859        $overwrite_title = isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1';
     1860       
     1861        // Если обе перезаписи ВЫКЛЮЧЕНЫ, обрабатываем только без тегов
     1862        if (!$overwrite_alt || !$overwrite_title) {
     1863            $args['meta_query'] = array(
     1864                'relation' => 'OR',
     1865                array(
     1866                    'key' => '_wp_attachment_image_alt',
     1867                    'compare' => 'NOT EXISTS'
     1868                ),
     1869                array(
     1870                    'key' => '_wp_attachment_image_alt',
     1871                    'value' => '',
     1872                    'compare' => '='
     1873                )
     1874            );
     1875        }
     1876    }
     1877   
     1878    // ========================================
     1879    // ВЫПОЛНЯЕМ ЗАПРОС
     1880    // ========================================
     1881    $query = new WP_Query($args);
     1882   
     1883    // ========================================
     1884    // ДОПОЛНИТЕЛЬНАЯ ФИЛЬТРАЦИЯ ПО TITLE
     1885    // ========================================
     1886    $filtered_posts = array();
     1887   
     1888    if (!empty($filters['status']) && ($filters['status'] === 'no_title' || $filters['status'] === 'no_tags')) {
     1889        // Фильтруем по пустому TITLE
     1890        foreach ($query->posts as $attachment_id) {
     1891            $title = get_the_title($attachment_id);
     1892           
     1893            if ($filters['status'] === 'no_title') {
     1894                // Только без TITLE
     1895                if (empty($title)) {
     1896                    $filtered_posts[] = $attachment_id;
     1897                }
     1898            } elseif ($filters['status'] === 'no_tags') {
     1899                // Без ALT И TITLE
     1900                $alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
     1901                if (empty($alt) && empty($title)) {
     1902                    $filtered_posts[] = $attachment_id;
     1903                }
     1904            }
     1905        }
     1906       
     1907        // Используем отфильтрованный список
     1908        $posts_to_process = $filtered_posts;
     1909    } else {
     1910        // Если фильтр "Все" и перезапись ВЫКЛЮЧЕНА, дополнительно проверяем TITLE
     1911        $overwrite_title = isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1';
     1912       
     1913        if (!$overwrite_title) {
     1914            foreach ($query->posts as $attachment_id) {
     1915                $alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true);
     1916                $title = get_the_title($attachment_id);
     1917               
     1918                // Добавляем только если нет хотя бы одного тега
     1919                if (empty($alt) || empty($title)) {
     1920                    $filtered_posts[] = $attachment_id;
     1921                }
     1922            }
     1923            $posts_to_process = $filtered_posts;
     1924        } else {
     1925            $posts_to_process = $query->posts;
     1926        }
     1927    }
     1928   
     1929    // ========================================
     1930    // ОБРАБОТКА ИЗОБРАЖЕНИЙ
     1931    // ========================================
     1932    $processed = 0;
     1933    $success = 0;
     1934    $errors = 0;
     1935    $skipped = 0;
     1936   
     1937    foreach ($posts_to_process as $attachment_id) {
     1938        $result = $this->process_single_image($attachment_id);
     1939        if ($result === 'success') {
     1940            $success++;
     1941        } elseif ($result === 'skipped') {
     1942            $skipped++;
     1943        } else {
     1944            $errors++;
     1945        }
     1946        $processed++;
     1947    }
     1948   
     1949    // ========================================
     1950    // ОПРЕДЕЛЯЕМ ЕСТЬ ЛИ ЕЩЁ ИЗОБРАЖЕНИЯ
     1951    // ========================================
     1952    $has_more = false;
     1953   
     1954    if (!empty($filters['status']) && ($filters['status'] === 'no_title' || $filters['status'] === 'no_tags')) {
     1955        // Для фильтров с проверкой TITLE
     1956        $has_more = count($filtered_posts) >= $batch_size;
     1957    } else {
     1958        // Для обычных фильтров
     1959        $has_more = ($offset + $batch_size) < $query->found_posts;
     1960    }
     1961   
     1962    // ========================================
     1963    // СОХРАНЕНИЕ В ЛОГ (только в конце обработки)
     1964    // ========================================
     1965    if (!$has_more && isset($settings['test_mode']) && $settings['test_mode'] != '1') {
     1966        global $wpdb;
     1967       
     1968        // Проверяем есть ли уже запись для этой сессии (последние 5 минут)
     1969        $last_log = $wpdb->get_row(
     1970            "SELECT * FROM {$wpdb->prefix}autoimta_process_log
     1971             WHERE process_date > DATE_SUB(NOW(), INTERVAL 5 MINUTE)
     1972             ORDER BY id DESC LIMIT 1"
    17061973        );
    17071974       
    1708         // Применяем фильтры
    1709         if (!empty($filters['date']) && $filters['date'] !== 'all') {
    1710             $date_query = array();
    1711             switch ($filters['date']) {
    1712                 case 'today':
    1713                     $date_query = array('after' => 'today', 'inclusive' => true);
    1714                     break;
    1715                 case 'week':
    1716                     $date_query = array('after' => '1 week ago', 'inclusive' => true);
    1717                     break;
    1718                 case 'month':
    1719                     $date_query = array('after' => '1 month ago', 'inclusive' => true);
    1720                     break;
    1721                 case 'year':
    1722                     $date_query = array('after' => '1 year ago', 'inclusive' => true);
    1723                     break;
    1724             }
    1725             if (!empty($date_query)) {
    1726                 $args['date_query'] = array($date_query);
    1727             }
    1728         }
    1729        
    1730         if (!empty($filters['post']) && $filters['post'] !== 'all') {
    1731             $args['post_parent'] = intval($filters['post']);
    1732         }
    1733        
    1734         $query = new WP_Query($args);
    1735         $processed = 0;
    1736         $success = 0;
    1737         $errors = 0;
    1738         $skipped = 0;
    1739        
    1740         foreach ($query->posts as $attachment_id) {
    1741             $result = $this->process_single_image($attachment_id);
    1742             if ($result === 'success') {
    1743                 $success++;
    1744             } elseif ($result === 'skipped') {
    1745                 $skipped++;
    1746             } else {
    1747                 $errors++;
    1748             }
    1749             $processed++;
    1750         }
    1751        
    1752         $has_more = ($offset + $batch_size) < $query->found_posts;
    1753        
    1754         // Сохраняем в лог, если не тестовый режим и обработка завершена
    1755         if (!$has_more && isset($settings['test_mode']) && $settings['test_mode'] != '1') {
    1756             $this->save_process_log($query->found_posts, $processed, $success, $skipped, $errors, isset($settings['test_mode']) && $settings['test_mode'] == '1');
    1757         }
    1758        
    1759         wp_send_json_success(array(
    1760             'processed' => $processed,
    1761             'success' => $success,
    1762             'errors' => $errors,
    1763             'skipped' => $skipped,
    1764             'has_more' => $has_more,
    1765             'test_mode' => isset($settings['test_mode']) && $settings['test_mode'] == '1'
    1766         ));
    1767     }
     1975        if ($last_log) {
     1976            // Обновляем существующую запись
     1977            $wpdb->update(
     1978                $wpdb->prefix . 'autoimta_process_log',
     1979                array(
     1980                    'processed' => $last_log->processed + $processed,
     1981                    'success' => $last_log->success + $success,
     1982                    'skipped' => $last_log->skipped + $skipped,
     1983                    'errors' => $last_log->errors + $errors
     1984                ),
     1985                array('id' => $last_log->id),
     1986                array('%d', '%d', '%d', '%d'),
     1987                array('%d')
     1988            );
     1989        } else {
     1990            // Создаём новую запись
     1991            $this->save_process_log($query->found_posts, $processed, $success, $skipped, $errors, false);
     1992        }
     1993    }
     1994   
     1995    // ========================================
     1996    // ОТПРАВЛЯЕМ ОТВЕТ
     1997    // ========================================
     1998    wp_send_json_success(array(
     1999        'processed' => $processed,
     2000        'success' => $success,
     2001        'errors' => $errors,
     2002        'skipped' => $skipped,
     2003        'has_more' => $has_more,
     2004        'test_mode' => isset($settings['test_mode']) && $settings['test_mode'] == '1'
     2005    ));
     2006}
    17682007   
    17692008    /**
     
    24602699     * DeepL API
    24612700     */
    2462     private function translate_deepl($text, $source_lang, $target_lang, $settings) {
    2463         $api_key = isset($settings['translation_deepl_key']) ? $settings['translation_deepl_key'] : '';
    2464        
    2465         if (empty($api_key)) {
    2466             return new WP_Error('no_api_key', esc_html__('DeepL API key not specified', 'auto-image-tags'));
    2467         }
    2468        
    2469         // Определяем URL (бесплатный или платный API)
    2470         $url = (strpos($api_key, ':fx') !== false)
    2471             ? 'https://api-free.deepl.com/v2/translate'
    2472             : 'https://api.deepl.com/v2/translate';
    2473        
    2474         $response = wp_remote_post($url, array(
    2475             'timeout' => 15,
    2476             'headers' => array(
    2477                 'Authorization' => 'DeepL-Auth-Key ' . $api_key,
    2478                 'Content-Type' => 'application/x-www-form-urlencoded'
    2479             ),
    2480             'body' => array(
    2481                 'text' => $text,
    2482                 'source_lang' => strtoupper($source_lang),
    2483                 'target_lang' => strtoupper($target_lang)
    2484             )
    2485         ));
    2486        
    2487         if (is_wp_error($response)) {
    2488             return $response;
    2489         }
    2490        
    2491         $body = wp_remote_retrieve_body($response);
    2492         $data = json_decode($body, true);
    2493        
     2701/**
     2702 * DeepL API
     2703 */
     2704private function translate_deepl($text, $source_lang, $target_lang, $settings) {
     2705    $api_key = isset($settings['translation_deepl_key']) ? $settings['translation_deepl_key'] : '';
     2706   
     2707    if (empty($api_key)) {
     2708        return new WP_Error('no_api_key', esc_html__('DeepL API key not specified', 'auto-image-tags'));
     2709    }
     2710   
     2711    // Определяем URL (бесплатный или платный API)
     2712    // Бесплатные ключи заканчиваются на :fx
     2713    $url = (substr($api_key, -3) === ':fx')
     2714        ? 'https://api-free.deepl.com/v2/translate'
     2715        : 'https://api.deepl.com/v2/translate';
     2716   
     2717    // DeepL требует коды языков в верхнем регистре
     2718    $source_lang_upper = strtoupper($source_lang);
     2719    $target_lang_upper = strtoupper($target_lang);
     2720   
     2721    // Для некоторых языков DeepL требует указание варианта
     2722    // Например: EN-US, EN-GB, PT-BR, PT-PT
     2723    if ($target_lang_upper === 'EN') {
     2724        $target_lang_upper = 'EN-US'; // По умолчанию американский английский
     2725    }
     2726    if ($target_lang_upper === 'PT') {
     2727        $target_lang_upper = 'PT-BR'; // По умолчанию бразильский португальский
     2728    }
     2729   
     2730    $response = wp_remote_post($url, array(
     2731        'timeout' => 15,
     2732        'headers' => array(
     2733            'Authorization' => 'DeepL-Auth-Key ' . $api_key,
     2734            'Content-Type' => 'application/x-www-form-urlencoded'
     2735        ),
     2736        'body' => array(
     2737            'text' => $text,
     2738            'source_lang' => $source_lang_upper,
     2739            'target_lang' => $target_lang_upper
     2740        )
     2741    ));
     2742   
     2743    if (is_wp_error($response)) {
     2744        return $response;
     2745    }
     2746   
     2747    $response_code = wp_remote_retrieve_response_code($response);
     2748    $body = wp_remote_retrieve_body($response);
     2749    $data = json_decode($body, true);
     2750   
     2751    // Логирование для отладки (удали после исправления)
     2752    error_log('DeepL API Response Code: ' . $response_code);
     2753    error_log('DeepL API Response Body: ' . $body);
     2754   
     2755    // Проверка ошибок
     2756    if ($response_code !== 200) {
    24942757        if (isset($data['message'])) {
    24952758            return new WP_Error('api_error', sanitize_text_field($data['message']));
    24962759        }
    2497        
    2498         if (isset($data['translations'][0]['text'])) {
    2499             return sanitize_text_field($data['translations'][0]['text']);
    2500         }
    2501        
    2502         return new WP_Error('invalid_response', esc_html__('Invalid API response', 'auto-image-tags'));
    2503     }
     2760        return new WP_Error('api_error', sprintf(
     2761            esc_html__('DeepL API error: HTTP %d', 'auto-image-tags'),
     2762            $response_code
     2763        ));
     2764    }
     2765   
     2766    if (isset($data['message'])) {
     2767        return new WP_Error('api_error', sanitize_text_field($data['message']));
     2768    }
     2769   
     2770    if (isset($data['translations'][0]['text'])) {
     2771        return sanitize_text_field($data['translations'][0]['text']);
     2772    }
     2773   
     2774    return new WP_Error('invalid_response', esc_html__('Invalid API response', 'auto-image-tags'));
     2775}
    25042776
    25052777    /**
     
    28683140        );
    28693141       
    2870         // Передаём данные в JS
    28713142        wp_localize_script('autoimta-admin-js', 'autoimtaData', array(
    28723143            'ajaxurl' => admin_url('admin-ajax.php'),
     
    29183189        ));
    29193190    }
     3191   
     3192    /**
     3193     * Отключение устаревшего prettyPhoto из WooCommerce
     3194     */
     3195    public function disable_woocommerce_prettyphoto() {
     3196        // Отключаем prettyPhoto если загружен
     3197        wp_dequeue_script('prettyPhoto');
     3198        wp_dequeue_script('prettyPhoto-init');
     3199        wp_dequeue_style('woocommerce_prettyPhoto_css');
     3200       
     3201        // Отменяем регистрацию
     3202        wp_deregister_script('prettyPhoto');
     3203        wp_deregister_script('prettyPhoto-init');
     3204        wp_deregister_style('woocommerce_prettyPhoto_css');
     3205    }
    29203206}
    29213207
  • auto-image-tags/trunk/readme.txt

    r3380076 r3387215  
    11=== Auto Image Tags ===
    22Contributors: mrbogdan
    3 Tags: seo, images, alt, media, woocommerce
     3Tags: image, alt, title, seo, media, optimization, woocommerce
    44Requires at least: 5.0
    5 Tested up to: 6.8
     5Tested up to: 6.4
    66Requires PHP: 7.2
    7 Stable tag: 2.0.0
     7Stable tag: 2.1.0
    88License: GPLv3 or later
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
    1010
    11 Automatically add ALT, TITLE, Caption and Description to images. WooCommerce integration and translation support.
     11Automatically add ALT, TITLE, Caption and Description tags to WordPress media library images.
    1212
    1313== Description ==
    1414
    15 Auto Image Tags automatically generates ALT tags, TITLE attributes, captions and descriptions for all images in your WordPress media library. The plugin processes filenames intelligently, removing camera markers, splitting CamelCase, and applying customizable rules to create SEO-friendly image tags.
     15Auto Image Tags automatically generates ALT, TITLE, Caption and Description for your WordPress media library images based on filename, post title, or custom templates.
    1616
    1717**Key Features:**
    1818
    19 * Automatic generation of ALT, TITLE, Caption and Description
     19* Automatic ALT, TITLE, Caption and Description generation
    2020* Preview changes before applying
    2121* Individual overwrite settings for each attribute
    22 * Bulk processing with advanced filters
     22* Bulk processing filters (by date, posts, status)
     23* Advanced filename cleanup
     24* Stop words to remove unwanted words
    2325* Test mode for safe testing
    24 * Complete processing history and statistics
    25 * WooCommerce product image integration
    26 * Multi-language translation system (5 services)
    27 * Export/Import settings
     26* Processing history and statistics
     27* Multilingual support
     28* Extended template variables
     29* Translation system (5 services: Google, DeepL, Yandex, LibreTranslate, MyMemory)
     30* WooCommerce integration
    2831
    29 **Perfect for:**
     32**WooCommerce Integration:**
    3033
    31 * SEO optimization
    32 * Accessibility compliance
    33 * WooCommerce stores
    34 * Multi-language sites
    35 * Bulk image management
     34* Automatic processing of product images
     35* Gallery image support
     36* Use product title, category, and SKU in tags
     37* Seamless integration with product workflow
    3638
    37 == External Services ==
     39**Translation Support:**
    3840
    39 This plugin can optionally connect to external translation services to translate image tags. Translation features are completely optional and disabled by default.
    40 
    41 **Google Translate API**
    42 * Used for: Translating image tags (ALT, TITLE, Caption, Description)
    43 * Data sent: Text to translate, source/target language
    44 * When: Only when user enables translation and provides API key
    45 * Service: https://cloud.google.com/translate
    46 * Terms: https://cloud.google.com/terms
    47 * Privacy: https://policies.google.com/privacy
    48 
    49 **DeepL API**
    50 * Used for: Translating image tags
    51 * Data sent: Text to translate, source/target language
    52 * When: Only when user enables translation and provides API key
    53 * Service: https://www.deepl.com/pro-api
    54 * Terms: https://www.deepl.com/terms
    55 * Privacy: https://www.deepl.com/privacy
    56 
    57 **Yandex Translator API**
    58 * Used for: Translating image tags
    59 * Data sent: Text to translate, source/target language
    60 * When: Only when user enables translation and provides API key
    61 * Service: https://cloud.yandex.com/services/translate
    62 * Terms: https://yandex.com/legal/cloud_terms_of_use
    63 * Privacy: https://yandex.com/legal/confidential
    64 
    65 **LibreTranslate**
    66 * Used for: Translating image tags
    67 * Data sent: Text to translate, source/target language
    68 * When: Only when user enables translation and provides server URL
    69 * Service: https://libretranslate.com
    70 * Terms: Open source, self-hosted option available
    71 * Privacy: https://github.com/LibreTranslate/LibreTranslate
    72 
    73 **MyMemory Translation API**
    74 * Used for: Translating image tags
    75 * Data sent: Text to translate, source/target language, optional email
    76 * When: Only when user enables translation
    77 * Service: https://mymemory.translated.net
    78 * Terms: https://mymemory.translated.net/doc/terms.php
    79 * Privacy: https://mymemory.translated.net/doc/privacy.php
    80 
    81 **Important Notes:**
    82 * All translation services are OPTIONAL and disabled by default
    83 * No data is sent unless user actively enables translation and configures API keys
    84 * Users must review and accept terms of service for each translation provider they choose to use
    85 * Translation can be completely avoided by not enabling this feature
     41* Google Translate API
     42* DeepL API (500k chars/month free)
     43* Yandex Translator (1M chars/month free)
     44* LibreTranslate (free, open-source)
     45* MyMemory (free, 10k chars/day)
    8646
    8747== Installation ==
    8848
    89491. Upload the plugin files to `/wp-content/plugins/auto-image-tags/`
    90 2. Activate the plugin through the 'Plugins' menu in WordPress
    91 3. Go to Auto Image Tags settings to configure
     502. Activate the plugin through the 'Plugins' screen in WordPress
     513. Go to Auto Image Tags menu to configure settings
     524. Use Process Images tab to bulk process existing images
    9253
    9354== Frequently Asked Questions ==
    9455
    95 = Does this work with WooCommerce? =
     56= Does this plugin work with WooCommerce? =
    9657
    97 Yes! Version 2.0 includes full WooCommerce integration for product images.
    98 
    99 = Will it overwrite my existing tags? =
    100 
    101 Only if you enable the "Overwrite existing" option for each tag type. You have individual control.
     58Yes! Version 2.0+ includes full WooCommerce integration with product image processing.
    10259
    10360= Can I preview changes before applying? =
    10461
    105 Yes! Use the Preview tab to see exactly what will change.
     62Yes, use the Preview tab to see how tags will look before processing.
    10663
    10764= Does it support translation? =
    10865
    109 Yes! Supports 5 translation services (Google, DeepL, Yandex, LibreTranslate, MyMemory). All are optional.
     66Yes, the plugin supports 5 translation services including free options.
    11067
    111 = Is there a Pro version? =
     68= Will it overwrite existing ALT tags? =
    11269
    113 No! All features are completely free, no Pro version exists.
     70Only if you enable the "Overwrite existing" option for each field.
    11471
    11572== Screenshots ==
    11673
    117 1. Main settings page with tag formats
    118 2. Process images with advanced filters
     741. Settings page with format options
     752. Bulk processing with filters
    119763. Preview changes before applying
    120 4. Statistics and processing history
     774. Processing statistics and history
     785. Translation settings
     796. WooCommerce integration
    12180
    12281== Changelog ==
    12382
    124 = 2.0.0 =
    125 * NEW: Translation system with 5 API services (optional)
    126 * NEW: WooCommerce product image integration
    127 * NEW: Preview tab with before/after comparison
    128 * NEW: Tools tab (bulk delete, export/import settings)
    129 * NEW: Caption and Description support
    130 * NEW: Individual overwrite settings for each tag
    131 * NEW: Advanced processing filters
    132 * NEW: Test mode for safe testing
    133 * NEW: Complete statistics and history
    134 * NEW: Full Russian translation
    135 * IMPROVED: Enhanced filename cleanup
    136 * IMPROVED: Extended template variables
    137 * IMPROVED: Better UI/UX with 7 tabs
    138 * IMPROVED: AJAX batch processing
     83= 2.1.0 (2025-10-30) =
     84**Bug Fixes:**
     85* Fixed HTTP/2 Protocol error during bulk processing
     86* Fixed progress bar calculation - now shows real progress (0% → 100%)
     87* Fixed PHP Parse Error - moved `disable_woocommerce_prettyphoto()` inside class
     88* Added protection against timeouts with increased execution limits
    13989
    140 = 1.2.0 =
     90**Performance:**
     91* Reduced batch size from 10 to 5 images for better stability
     92* Removed 5000 image limit - now handles unlimited images
     93* Improved handling of large data volumes
     94* Disabled output buffering to prevent server conflicts
     95
     96**New Features:**
     97* Auto-disable deprecated prettyPhoto from WooCommerce
     98* Dynamic progress calculation with current state display (N / TOTAL)
     99* Enhanced error handling with detailed logging
     100
     101= 2.0.0 (2025-10-20) =
     102**Major Update:**
     103* Translation system with 5 services
     104* WooCommerce integration
     105* Tools tab (bulk delete, export/import settings)
     106* Preview tab with before/after comparison
     107* Caption and Description support
     108* Individual overwrite settings
     109* Advanced filters
     110* Enhanced filename cleanup
     111* Custom stop words
     112* Test mode
     113* Statistics and history
     114* Language selection
     115
     116= 1.0.0 (2025-10-07) =
    141117* Initial release
     118* Basic ALT and TITLE tag generation
     119* Filename processing
     120* Bulk operations
    142121
    143122== Upgrade Notice ==
    144123
     124= 2.1.0 =
     125Critical bug fixes for HTTP/2 errors and progress bar. Recommended update for all users.
     126
    145127= 2.0.0 =
    146 Major update with translation system, WooCommerce integration, and many new features. Backup recommended before updating.
     128Major update with translation, WooCommerce integration, and many new features.
     129
     130== Support ==
     131
     132For support, feature requests, or bug reports:
     133* Telegram: @shapovalovbogdan
     134* GitHub: https://github.com/imrbogdan/auto-image-tags
     135
     136== Privacy ==
     137
     138This plugin does not collect or store any user data. Translation services are only used when explicitly configured and activated by the user.
     139
     140== Credits ==
     141
     142Developed by Shapovalov Bogdan
     143Free and open-source, no Pro version!
Note: See TracChangeset for help on using the changeset viewer.