Changeset 3387215
- Timestamp:
- 10/30/2025 05:25:51 PM (4 months ago)
- Location:
- auto-image-tags
- Files:
-
- 8 edited
- 1 copied
-
tags/2.1.0 (copied) (copied from auto-image-tags/trunk)
-
tags/2.1.0/assets/css/admin-style.css (modified) (2 diffs)
-
tags/2.1.0/assets/js/admin-main.js (modified) (2 diffs)
-
tags/2.1.0/auto-image-tags.php (modified) (11 diffs)
-
tags/2.1.0/readme.txt (modified) (1 diff)
-
trunk/assets/css/admin-style.css (modified) (2 diffs)
-
trunk/assets/js/admin-main.js (modified) (2 diffs)
-
trunk/auto-image-tags.php (modified) (11 diffs)
-
trunk/readme.txt (modified) (1 diff)
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 */ 4 6 5 7 .autoimta-settings-form { … … 351 353 border-radius: 3px; 352 354 } 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 1 1 /** 2 2 * AUTO IMAGE TAGS - Admin JavaScript 3 * Version: 2. 0.03 * Version: 2.1.0 4 4 */ 5 5 … … 171 171 } 172 172 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 */ 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 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(); 229 262 } else { 230 processing Error(response.data.message);263 processingComplete(totalProcessed, totalSuccess, totalSkipped, totalErrors, data.test_mode); 231 264 } 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 } 241 275 242 276 function processingComplete(processed, success, skipped, errors, testMode) { -
auto-image-tags/tags/2.1.0/auto-image-tags.php
r3380076 r3387215 4 4 * Plugin URI: https://wordpress.org/plugins/auto-image-tags/ 5 5 * Description: Automatically add ALT, TITLE, Caption and Description tags to WordPress media library images. WooCommerce integration and optional translation support. 6 * Version: 2. 0.06 * Version: 2.1.0 7 7 * Author: mrbogdan 8 8 * Author URI: https://profiles.wordpress.org/mrbogdan/ … … 23 23 define('AUTOIMTA_PLUGIN_DIR', plugin_dir_path(__FILE__)); 24 24 define('AUTOIMTA_PLUGIN_URL', plugin_dir_url(__FILE__)); 25 define('AUTOIMTA_VERSION', '2. 0.0');25 define('AUTOIMTA_VERSION', '2.1.0'); 26 26 27 27 // Проверка требований … … 74 74 add_filter('add_attachment', array($this, 'handle_image_upload')); 75 75 add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); 76 76 add_action('wp_enqueue_scripts', array($this, 'disable_woocommerce_prettyphoto'), 99); 77 77 add_action('wp_ajax_autoimta_process_existing_images', array($this, 'ajax_process_existing_images')); 78 78 add_action('wp_ajax_autoimta_get_images_count', array($this, 'ajax_get_images_count')); … … 991 991 } 992 992 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 */ 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('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> 1064 1107 </div> 1065 1108 </div> 1066 <?php 1067 } 1109 </div> 1110 <?php 1111 } 1068 1112 1069 1113 /** … … 1519 1563 'post_mime_type' => 'image', 1520 1564 'post_status' => 'inherit', 1521 'posts_per_page' => 1000,1565 'posts_per_page' => -1, 1522 1566 'fields' => 'ids' 1523 1567 ); … … 1553 1597 $total = $query->found_posts; 1554 1598 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 1562 1599 $without_alt = 0; 1563 1600 $without_title = 0; … … 1583 1620 1584 1621 // Подсчет изображений для обработки 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; 1623 if (isset($settings['alt_format']) && $settings['alt_format'] !== 'disabled' && (isset($settings['overwrite_alt']) && $settings['overwrite_alt'] == '1' || empty($alt))) { 1624 $will_process = true; 1625 } 1626 if (isset($settings['title_format']) && $settings['title_format'] !== 'disabled' && (isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1' || empty($title))) { 1627 $will_process = true; 1628 } 1629 if ($will_process) { 1630 $needs_processing++; 1631 } 1595 1632 } 1596 1633 … … 1681 1718 } 1682 1719 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 */ 1726 public 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" 1706 1973 ); 1707 1974 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 } 1768 2007 1769 2008 /** … … 2460 2699 * DeepL API 2461 2700 */ 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 */ 2704 private 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) { 2494 2757 if (isset($data['message'])) { 2495 2758 return new WP_Error('api_error', sanitize_text_field($data['message'])); 2496 2759 } 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 } 2504 2776 2505 2777 /** … … 2868 3140 ); 2869 3141 2870 // Передаём данные в JS2871 3142 wp_localize_script('autoimta-admin-js', 'autoimtaData', array( 2872 3143 'ajaxurl' => admin_url('admin-ajax.php'), … … 2918 3189 )); 2919 3190 } 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 } 2920 3206 } 2921 3207 -
auto-image-tags/tags/2.1.0/readme.txt
r3380076 r3387215 1 1 === Auto Image Tags === 2 2 Contributors: mrbogdan 3 Tags: seo, images, alt, media, woocommerce3 Tags: image, alt, title, seo, media, optimization, woocommerce 4 4 Requires at least: 5.0 5 Tested up to: 6. 85 Tested up to: 6.4 6 6 Requires PHP: 7.2 7 Stable tag: 2. 0.07 Stable tag: 2.1.0 8 8 License: GPLv3 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html 10 10 11 Automatically add ALT, TITLE, Caption and Description t o images. WooCommerce integration and translation support.11 Automatically add ALT, TITLE, Caption and Description tags to WordPress media library images. 12 12 13 13 == Description == 14 14 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.15 Auto Image Tags automatically generates ALT, TITLE, Caption and Description for your WordPress media library images based on filename, post title, or custom templates. 16 16 17 17 **Key Features:** 18 18 19 * Automatic generation of ALT, TITLE, Caption and Description19 * Automatic ALT, TITLE, Caption and Description generation 20 20 * Preview changes before applying 21 21 * 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 23 25 * 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 28 31 29 ** Perfect for:**32 **WooCommerce Integration:** 30 33 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 36 38 37 == External Services == 39 **Translation Support:** 38 40 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) 86 46 87 47 == Installation == 88 48 89 49 1. 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 50 2. Activate the plugin through the 'Plugins' screen in WordPress 51 3. Go to Auto Image Tags menu to configure settings 52 4. Use Process Images tab to bulk process existing images 92 53 93 54 == Frequently Asked Questions == 94 55 95 = Does this work with WooCommerce? =56 = Does this plugin work with WooCommerce? = 96 57 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. 58 Yes! Version 2.0+ includes full WooCommerce integration with product image processing. 102 59 103 60 = Can I preview changes before applying? = 104 61 105 Yes ! Use the Preview tab to see exactly what will change.62 Yes, use the Preview tab to see how tags will look before processing. 106 63 107 64 = Does it support translation? = 108 65 109 Yes ! Supports 5 translation services (Google, DeepL, Yandex, LibreTranslate, MyMemory). All are optional.66 Yes, the plugin supports 5 translation services including free options. 110 67 111 = Is there a Pro version? =68 = Will it overwrite existing ALT tags? = 112 69 113 No! All features are completely free, no Pro version exists.70 Only if you enable the "Overwrite existing" option for each field. 114 71 115 72 == Screenshots == 116 73 117 1. Main settings page with tag formats118 2. Process images with advancedfilters74 1. Settings page with format options 75 2. Bulk processing with filters 119 76 3. Preview changes before applying 120 4. Statistics and processing history 77 4. Processing statistics and history 78 5. Translation settings 79 6. WooCommerce integration 121 80 122 81 == Changelog == 123 82 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 139 89 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) = 141 117 * Initial release 118 * Basic ALT and TITLE tag generation 119 * Filename processing 120 * Bulk operations 142 121 143 122 == Upgrade Notice == 144 123 124 = 2.1.0 = 125 Critical bug fixes for HTTP/2 errors and progress bar. Recommended update for all users. 126 145 127 = 2.0.0 = 146 Major update with translation system, WooCommerce integration, and many new features. Backup recommended before updating. 128 Major update with translation, WooCommerce integration, and many new features. 129 130 == Support == 131 132 For support, feature requests, or bug reports: 133 * Telegram: @shapovalovbogdan 134 * GitHub: https://github.com/imrbogdan/auto-image-tags 135 136 == Privacy == 137 138 This 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 142 Developed by Shapovalov Bogdan 143 Free 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 */ 4 6 5 7 .autoimta-settings-form { … … 351 353 border-radius: 3px; 352 354 } 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 1 1 /** 2 2 * AUTO IMAGE TAGS - Admin JavaScript 3 * Version: 2. 0.03 * Version: 2.1.0 4 4 */ 5 5 … … 171 171 } 172 172 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 */ 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 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(); 229 262 } else { 230 processing Error(response.data.message);263 processingComplete(totalProcessed, totalSuccess, totalSkipped, totalErrors, data.test_mode); 231 264 } 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 } 241 275 242 276 function processingComplete(processed, success, skipped, errors, testMode) { -
auto-image-tags/trunk/auto-image-tags.php
r3380076 r3387215 4 4 * Plugin URI: https://wordpress.org/plugins/auto-image-tags/ 5 5 * Description: Automatically add ALT, TITLE, Caption and Description tags to WordPress media library images. WooCommerce integration and optional translation support. 6 * Version: 2. 0.06 * Version: 2.1.0 7 7 * Author: mrbogdan 8 8 * Author URI: https://profiles.wordpress.org/mrbogdan/ … … 23 23 define('AUTOIMTA_PLUGIN_DIR', plugin_dir_path(__FILE__)); 24 24 define('AUTOIMTA_PLUGIN_URL', plugin_dir_url(__FILE__)); 25 define('AUTOIMTA_VERSION', '2. 0.0');25 define('AUTOIMTA_VERSION', '2.1.0'); 26 26 27 27 // Проверка требований … … 74 74 add_filter('add_attachment', array($this, 'handle_image_upload')); 75 75 add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); 76 76 add_action('wp_enqueue_scripts', array($this, 'disable_woocommerce_prettyphoto'), 99); 77 77 add_action('wp_ajax_autoimta_process_existing_images', array($this, 'ajax_process_existing_images')); 78 78 add_action('wp_ajax_autoimta_get_images_count', array($this, 'ajax_get_images_count')); … … 991 991 } 992 992 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 */ 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('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> 1064 1107 </div> 1065 1108 </div> 1066 <?php 1067 } 1109 </div> 1110 <?php 1111 } 1068 1112 1069 1113 /** … … 1519 1563 'post_mime_type' => 'image', 1520 1564 'post_status' => 'inherit', 1521 'posts_per_page' => 1000,1565 'posts_per_page' => -1, 1522 1566 'fields' => 'ids' 1523 1567 ); … … 1553 1597 $total = $query->found_posts; 1554 1598 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 1562 1599 $without_alt = 0; 1563 1600 $without_title = 0; … … 1583 1620 1584 1621 // Подсчет изображений для обработки 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; 1623 if (isset($settings['alt_format']) && $settings['alt_format'] !== 'disabled' && (isset($settings['overwrite_alt']) && $settings['overwrite_alt'] == '1' || empty($alt))) { 1624 $will_process = true; 1625 } 1626 if (isset($settings['title_format']) && $settings['title_format'] !== 'disabled' && (isset($settings['overwrite_title']) && $settings['overwrite_title'] == '1' || empty($title))) { 1627 $will_process = true; 1628 } 1629 if ($will_process) { 1630 $needs_processing++; 1631 } 1595 1632 } 1596 1633 … … 1681 1718 } 1682 1719 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 */ 1726 public 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" 1706 1973 ); 1707 1974 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 } 1768 2007 1769 2008 /** … … 2460 2699 * DeepL API 2461 2700 */ 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 */ 2704 private 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) { 2494 2757 if (isset($data['message'])) { 2495 2758 return new WP_Error('api_error', sanitize_text_field($data['message'])); 2496 2759 } 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 } 2504 2776 2505 2777 /** … … 2868 3140 ); 2869 3141 2870 // Передаём данные в JS2871 3142 wp_localize_script('autoimta-admin-js', 'autoimtaData', array( 2872 3143 'ajaxurl' => admin_url('admin-ajax.php'), … … 2918 3189 )); 2919 3190 } 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 } 2920 3206 } 2921 3207 -
auto-image-tags/trunk/readme.txt
r3380076 r3387215 1 1 === Auto Image Tags === 2 2 Contributors: mrbogdan 3 Tags: seo, images, alt, media, woocommerce3 Tags: image, alt, title, seo, media, optimization, woocommerce 4 4 Requires at least: 5.0 5 Tested up to: 6. 85 Tested up to: 6.4 6 6 Requires PHP: 7.2 7 Stable tag: 2. 0.07 Stable tag: 2.1.0 8 8 License: GPLv3 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html 10 10 11 Automatically add ALT, TITLE, Caption and Description t o images. WooCommerce integration and translation support.11 Automatically add ALT, TITLE, Caption and Description tags to WordPress media library images. 12 12 13 13 == Description == 14 14 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.15 Auto Image Tags automatically generates ALT, TITLE, Caption and Description for your WordPress media library images based on filename, post title, or custom templates. 16 16 17 17 **Key Features:** 18 18 19 * Automatic generation of ALT, TITLE, Caption and Description19 * Automatic ALT, TITLE, Caption and Description generation 20 20 * Preview changes before applying 21 21 * 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 23 25 * 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 28 31 29 ** Perfect for:**32 **WooCommerce Integration:** 30 33 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 36 38 37 == External Services == 39 **Translation Support:** 38 40 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) 86 46 87 47 == Installation == 88 48 89 49 1. 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 50 2. Activate the plugin through the 'Plugins' screen in WordPress 51 3. Go to Auto Image Tags menu to configure settings 52 4. Use Process Images tab to bulk process existing images 92 53 93 54 == Frequently Asked Questions == 94 55 95 = Does this work with WooCommerce? =56 = Does this plugin work with WooCommerce? = 96 57 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. 58 Yes! Version 2.0+ includes full WooCommerce integration with product image processing. 102 59 103 60 = Can I preview changes before applying? = 104 61 105 Yes ! Use the Preview tab to see exactly what will change.62 Yes, use the Preview tab to see how tags will look before processing. 106 63 107 64 = Does it support translation? = 108 65 109 Yes ! Supports 5 translation services (Google, DeepL, Yandex, LibreTranslate, MyMemory). All are optional.66 Yes, the plugin supports 5 translation services including free options. 110 67 111 = Is there a Pro version? =68 = Will it overwrite existing ALT tags? = 112 69 113 No! All features are completely free, no Pro version exists.70 Only if you enable the "Overwrite existing" option for each field. 114 71 115 72 == Screenshots == 116 73 117 1. Main settings page with tag formats118 2. Process images with advancedfilters74 1. Settings page with format options 75 2. Bulk processing with filters 119 76 3. Preview changes before applying 120 4. Statistics and processing history 77 4. Processing statistics and history 78 5. Translation settings 79 6. WooCommerce integration 121 80 122 81 == Changelog == 123 82 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 139 89 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) = 141 117 * Initial release 118 * Basic ALT and TITLE tag generation 119 * Filename processing 120 * Bulk operations 142 121 143 122 == Upgrade Notice == 144 123 124 = 2.1.0 = 125 Critical bug fixes for HTTP/2 errors and progress bar. Recommended update for all users. 126 145 127 = 2.0.0 = 146 Major update with translation system, WooCommerce integration, and many new features. Backup recommended before updating. 128 Major update with translation, WooCommerce integration, and many new features. 129 130 == Support == 131 132 For support, feature requests, or bug reports: 133 * Telegram: @shapovalovbogdan 134 * GitHub: https://github.com/imrbogdan/auto-image-tags 135 136 == Privacy == 137 138 This 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 142 Developed by Shapovalov Bogdan 143 Free and open-source, no Pro version!
Note: See TracChangeset
for help on using the changeset viewer.