افزونه وبپی پرو
با افزونه WebP Free وبپی پرو، سرعت وبسایت خود را به طور چشمگیری افزایش دهید، سئوی تصاویر را بهبود بخشید و تجربهی کاربری بهتری برای بازدیدکنندگان خود رقم بزنید. مدیریت و بهینهسازی تصاویر همزمان در اختیار شماست❤️🔥
امکانات افزونه وبپی پرو:
-
تبدیل آسان به فرمت WebP:
-
مدیریت هوشمند متن جایگزین (Alt Text):
-
داشبورد آماری و گزارشدهی دقیق:
-
کنترل کامل در دستان شما:
-
سازگاری و بهینهسازی: در بستههای کوچک انجام میشود.
- قیمت نسخه پرو ۳۵۰/۰۰۰ تومان
- ضمانت بازگشت وجه ۳ روزه
- بصورت افزونه و قطعه کد
- نسخه تستی رایگان
ویژگی وبپی پرو Webp Pro
-
تبدیل آسان به فرمت WebP:
کاهش حجم تصاویر بدون افت کیفیت با وبپی پرو. - افزایش چشمگیر سرعت بارگذاری صفحات.
- بهبود رتبه سایت در موتورهای جستجو (SEO-friendly).
- امکان تبدیل انتخابی تصاویر با یک کلیک.
-
مدیریت هوشمند متن جایگزین (Alt Text):
بروزرسانی گروهی: به صورت خودکار برای تصاویری که متن جایگزین ندارند، Alt Text هوشمند تولید کنید. - ویرایش زنده: به راحتی و مستقیماً از داخل لیست تصاویر، متنهای جایگزین را ویرایش نمایید.
- بهبود سئو و دسترسپذیری: به موتورهای جستجو و کاربران کمتوان کمک کنید تا محتوای تصویری شما را بهتر درک کنند.
-
داشبورد آماری و گزارشدهی دقیق:
آمار جامع کتابخانه: گزارشی کامل از تعداد کل تصاویر، فرمتها (JPG, PNG, WebP) و وضعیت متنهای جایگزین دریافت کنید. - گزارش لحظهای عملیات: تمام فعالیتهای افزونه، از تبدیل تصاویر تا بروزرسانیها، به صورت دقیق ثبت و نمایش داده میشود.
- پاکسازی گزارشات: به راحتی و با یک کلیک، گزارشات وباگ ها رابا افزونه وبپی پرو پاک کنید.
-
کنترل کامل در دستان شما:
تنظیمات کیفیت: کیفیت تصاویر WebP خروجی را مطابق با نیاز خود تنظیم کنید (از ۱۰ تا ۱۰۰). - انتخاب فرمتها: مشخص کنید کدام فرمتها (JPG, PNG, JPEG) برای تبدیل در نظر گرفته شوند.
- رابط کاربری ساده و جذاب: تمام امکانات در یک محیط کاربرپسند و تببندی شده در دسترس شماست.
-
سازگاری و بهینهسازی:
پاکسازی خودکار کش: پس از انجام عملیات مهم، کش افزونههای محبوب مانند WP Rocket و LiteSpeed به صورت خودکار پاک میشود تا تغییرات فوراً اعمال شوند. - پردازش دستهای: برای جلوگیری از فشار به سرور، عملیات گروهی در بستههای کوچک انجام میشود.
کدهارا درون افزونه wp code بذارید-یک اسنیپت جدید PHPبسازید-اسم دلخواه و در نهایت کدها را کپی پیست کنید و ذخیره
<?php /** * Plugin Name: WebP Free * Plugin URI: https://mrbarati.com/forms/ * Description: افزونه رایگان برای تبدیل تصاویر به فرمت WebP و مدیریت متن جایگزین با امکانات محدود. * Version: 0.0 * Author: مهندس براتی * Author URI: https://mrbarati.com * License: GPLv3 * Requires at least: 6.0 * Requires PHP: 7.4 */ if (!defined('ABSPATH')) { exit; } final class WebP_Free { private static $instance = null; public static function instance() { if (is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } private function __construct() { $this->setup_hooks(); } private function setup_hooks() { register_activation_hook(__FILE__, [__CLASS__, 'activate']); register_deactivation_hook(__FILE__, [__CLASS__, 'deactivate']); register_uninstall_hook(__FILE__, [__CLASS__, 'uninstall']); add_action('admin_menu', [$this, 'register_admin_menu']); add_filter('plugin_action_links_' . plugin_basename(__FILE__), [$this, 'add_settings_link']); add_action('admin_head-settings_page_webp-free', [$this, 'inject_admin_assets']); add_action('wp_ajax_webpfree_scan_images', [$this, 'ajax_scan_images']); add_action('wp_ajax_webpfree_update_images', [$this, 'ajax_update_images']); add_action('wp_ajax_webpfree_load_images_table', [$this, 'ajax_load_images_table']); add_action('wp_ajax_webpfree_save_alt_texts', [$this, 'ajax_save_alt_texts']); add_action('wp_ajax_webpfree_convert_to_webp', [$this, 'ajax_convert_to_webp']); add_action('wp_ajax_webpfree_save_settings', [$this, 'ajax_save_settings']); add_action('wp_ajax_webpfree_clear_logs', [$this, 'ajax_clear_logs']); add_action('wp_ajax_webpfree_refresh_nonce', [$this, 'ajax_refresh_nonce']); add_action('wp_ajax_webpfree_refresh_logs', [$this, 'ajax_refresh_logs']); add_filter('the_content', [$this, 'replace_images_with_webp_in_content']); } public static function activate() { if (!function_exists('wp_get_image_editor')) { wp_die('خطا: افزونه نیاز به تابع wp_get_image_editor دارد. لطفاً مطمئن شوید که کتابخانه GD یا Imagick روی سرور شما نصب است.'); } add_option('webpfree_formats', ['jpeg', 'png', 'jpg']); add_option('webpfree_quality', 80); add_option('webpfree_logs', []); } public static function deactivate() { delete_option('webpfree_formats'); delete_option('webpfree_quality'); delete_option('webpfree_logs'); } public static function uninstall() { global $wpdb; delete_option('webpfree_formats'); delete_option('webpfree_quality'); delete_option('webpfree_logs'); $wpdb->delete($wpdb->postmeta, ['meta_key' => '_webp_converted_to']); } private function clear_caches() { if (function_exists('rocket_clean_domain')) { rocket_clean_domain(); } if (class_exists('LiteSpeed_Cache')) { do_action('litespeed_purge_all'); } wp_cache_flush(); } private function add_log($message) { $logs = get_option('webpfree_logs', []); $new_log = [ 'message' => sanitize_text_field($message), 'time' => current_time('mysql') ]; array_unshift($logs, $new_log); $logs = array_slice($logs, 0, 40); update_option('webpfree_logs', $logs); } public function add_settings_link($links) { $settings_link = '<a href="' . esc_url(admin_url('options-general.php?page=webp-free')) . '">تنظیمات</a>'; array_unshift($links, $settings_link); return $links; } public function register_admin_menu() { add_options_page( 'تنظیمات WebP Free', 'WebP Free', 'manage_options', 'webp-free', [$this, 'render_settings_page'] ); } public function inject_admin_assets() { $this->embed_css(); $this->embed_js(); } private function embed_css() { ?> <style> .rtl .nav-tab-wrapper { margin-bottom: 20px; background: linear-gradient(135deg, #2271b1 0%, #1a5d93 100%); padding: 5px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .rtl .nav-tab { background: #fff; color: #004aad; border: none; margin: 5px; padding: 10px 20px; border-radius: 5px; transition: all 0.3s ease; font-weight: bold; font-size: 16px; } .rtl .nav-tab:hover, .rtl .nav-tab.nav-tab-active { background: #004aad; color: #fff; } .rtl .tab-content { display: none; padding: 25px; background: #fff; border: 1px solid #c3c4c7; border-top: 0; border-radius: 0 0 8px 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); font-size: 15px; } .rtl .tab-content.active { display: block; } .rtl .tab-content h2 { color: #004aad; font-weight: bold; font-size: 21px; } .rtl .tab-content p, .rtl .tab-content li { font-size: 15px; line-height: 1.8; color: #333; } .rtl .result-container, .rtl .log-box { margin-top: 20px; } .rtl .actions-bar { margin-bottom: 15px; display: flex; gap: 10px; align-items: center; } .rtl #webp-image-list, .rtl .widefat { margin-top: 20px; } .rtl #webp-image-list th.check-column { padding: 8px 0; } .rtl #webp-image-list td, .rtl #webp-image-list th { vertical-align: middle; } .rtl #webp-image-list tbody tr { height: 70px; } .rtl #webp-image-list img { max-height: 60px; width: auto; max-width: 100%; border-radius: 3px; box-shadow: 0 0 5px rgba(0,0,0,0.1); } .rtl td.editable-alt { cursor: pointer; position: relative; padding: 8px; } .rtl .alt-text-input { display: none; width: calc(100% - 110px); padding: 5px; vertical-align: middle; } .rtl .save-single-alt { display: none; background-color: #4CAF50; color: #fff; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer; margin-right: 5px; vertical-align: middle; } .rtl .cancel-single-alt { display: none; background-color: #FF0000; color: #fff; border: none; border-radius: 3px; padding: 5px 10px; cursor: pointer; margin-right: 5px; vertical-align: middle; } .rtl .max-ten-warning { color: #FF0000; } #webpfree-toast-container { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 99999; width: 380px; max-width: 90%; } .ir-toast { font-family: inherit; font-size: 15px; background-color: #fff; color: #1d2327; padding: 20px; margin-bottom: 10px; border-right-width: 5px; border-right-style: solid; border-radius: 4px; box-shadow: 0 5px 25px rgba(0,0,0,0.2); display: flex; align-items: flex-start; gap: 12px; opacity: 0; transform: scale(0.9); transition: all 0.3s ease-out; } .ir-toast.show { opacity: 1; transform: scale(1); } .ir-toast .ir-toast-icon { font-size: 20px; line-height: 1.4; } .ir-toast .ir-toast-message { flex-grow: 1; line-height: 1.6; } .ir-toast .ir-toast-message b { font-weight: 600; } .ir-toast .ir-toast-close { background: none; border: none; font-size: 22px; cursor: pointer; color: #777; padding: 0 5px; } .ir-toast.success { border-right-color: #4CAF50; } .ir-toast.success .ir-toast-icon { color: #4CAF50; } .ir-toast.error { border-right-color: #F44336; } .ir-toast.error .ir-toast-icon { color: #F44336; } #log-container { max-height: 400px; overflow-y: auto; background: #f9f9f9; border: 1px solid #ddd; padding: 10px; } #log-container ul { margin: 0; padding: 0; list-style: none; } #log-container li { background: #fff; border-bottom: 1px solid #eee; padding: 8px 12px; font-size: 15px; display: flex; justify-content: space-between; align-items: center; } #log-container li:last-child { border-bottom: none; } #log-container li::before { content: counters(item, ".") ". "; counter-increment: item; margin-left: 10px; } .log-time { color: #777; font-size: 14px; } #help h3 { margin-top: 25px; border-bottom: 2px solid #004aad; padding-bottom: 5px; color: #004aad; font-size: 20px; } #help p { line-height: 1.7; } #webp-pro-btn { display: inline-block; padding: 15px 30px; font-size: 15px; font-weight: bold; background-color: #2271b1; color: #fff; text-decoration: none; border-radius: 5px; margin-top: 20px; margin-left: 10px; } #webp-pro-btn:hover { background-color: #1a5d93; } .progress-container { margin-top: 15px; display: none; } .progress-bar { width: 100%; background-color: #f3f3f3; border-radius: 5px; overflow: hidden; } .progress-bar-fill { height: 20px; background-color: #4CAF50; width: 0%; transition: width 0.3s ease-in-out; } .progress-text { margin-top: 5px; font-size: 15px; color: #333; } #image-table-container > p { color: #FF0000; font-weight: bold; font-size: 15px; } .rtl td.editable-alt[title] { color: #FF0000; font-weight: bold; } </style> <?php } private function embed_js() { $localized_data = [ 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('webpfree_nonce'), 'i18n' => [ 'loading' => 'در حال بارگذاری...', 'error' => 'یک خطای غیرمنتظره رخ داد!', 'saving' => 'در حال ذخیره...', 'scan' => 'اسکن و نمایش گزارشات', 'scanning' => 'در حال اسکن...', 'startUpdate' => 'شروع بروزرسانی گروهی', 'updating' => 'در حال پردازش...', 'convertingSelected' => 'تبدیل منتخب به WebP', 'converting' => 'در حال تبدیل...', 'saveAltChanges' => 'OK', 'cancelAltChanges' => 'Noki', 'clearing' => 'در حال پاکسازی...', 'progress' => 'پیشرفت: %s%% (پردازش %s از %s تصویر)' ] ]; ?> <script> jQuery(document).ready(function($) { let webpFree = <?php echo wp_json_encode($localized_data); ?>; const i18n = webpFree.i18n; function showToast(message, isSuccess = true, duration = 4000) { const container = $('#webpfree-toast-container'); if (container.length === 0) { $('body').append('<div id="webpfree-toast-container"></div>'); } const icon = isSuccess ? '✔' : '✖'; const toastClass = isSuccess ? 'success' : 'error'; const toast = $(`<div class="ir-toast ${toastClass}"><span class="ir-toast-icon">${icon}</span><div class="ir-toast-message">${message}</div><button class="ir-toast-close" title="بستن">×</button></div>`); $('#webpfree-toast-container').append(toast); setTimeout(() => { toast.addClass('show'); }, 100); const autoClose = setTimeout(() => { toast.removeClass('show').on('transitionend', () => toast.remove()); }, duration); toast.find('.ir-toast-close').on('click', () => { clearTimeout(autoClose); toast.removeClass('show').on('transitionend', () => toast.remove()); }); } async function refreshNonce() { try { const response = await $.post(webpFree.ajaxurl, { action: 'webpfree_refresh_nonce' }); if (response.success && response.data.new_nonce) { webpFree.nonce = response.data.new_nonce; return true; } } catch (e) { console.error('Nonce refresh failed:', e); showToast(i18n.error, false); } return false; } $('.nav-tab').on('click', function(e) { e.preventDefault(); const target = $(this).attr('href'); $('.nav-tab').removeClass('nav-tab-active'); $(this).addClass('nav-tab-active'); $('.tab-content').removeClass('active'); $(target).addClass('active'); if (target === '#webp') { loadImagesTable(); showToast('نسخه رایگان: محدود به نمایش و تبدیل ۱۴ تصویر.', true, 4000); } else if (target === '#update') { showToast('نسخه رایگان: محدود به بروزرسانی ۱۴ تصویر.', true, 4000); } else if (target === '#logs') { $('#log-container').html('<p>برای مشاهده گزارشات، دکمه اسکن را کلیک کنید.</p>'); showToast('نسخه رایگان: گزارشات عملیات اخیر.', true, 4000); } else if (target === '#stats') { scanImages(); showToast('نسخه رایگان: نمایش آمار تصاویر.', true, 4000); } else { showToast('نسخه رایگان: امکانات محدود.', true, 4000); } }); if (window.location.hash && $('.nav-tab[href="' + window.location.hash + '"]').length) { $('.nav-tab[href="' + window.location.hash + '"]').trigger('click'); } else { $('.nav-tab-wrapper a:first').trigger('click'); } function scanImages() { $('#stats-table-container').html('<p>' + i18n.loading + '</p>'); $.post(webpFree.ajaxurl, { action: 'webpfree_scan_images', nonce: webpFree.nonce }) .done(res => { if (res.success) { $('#stats-table-container').html(res.data.table); } else { showToast(res.data.message || i18n.error, false); } }) .fail(() => showToast(i18n.error, false)); } $('#scan-images').on('click', function() { const btn = $(this).prop('disabled', true).text(i18n.scanning); scanImages(); btn.prop('disabled', false).text(i18n.scan); }); function refreshLogs() { $('#log-container').html('<p>' + i18n.loading + '</p>'); $.post(webpFree.ajaxurl, { action: 'webpfree_refresh_logs', nonce: webpFree.nonce }) .done(res => { if (res.success) { $('#log-container').html(res.data.logs); showToast('گزارشات با موفقیت بارگذاری شد.', true); } else { showToast(res.data.message || i18n.error, false); } }) .fail(() => showToast(i18n.error, false)); } $('#scan-logs').on('click', function() { const btn = $(this).prop('disabled', true).text(i18n.scanning); refreshLogs(); btn.prop('disabled', false).text(i18n.scan); }); $('#start-processing').on('click', async function() { if (!await refreshNonce()) return; const btn = $(this).prop('disabled', true).text(i18n.updating); const batchSize = 14; const progressContainer = $('#progress-container'); const progressBar = progressContainer.find('.progress-bar-fill'); const progressText = progressContainer.find('.progress-text'); progressContainer.show(); let totalImages = 0; try { const countResponse = await $.post(webpFree.ajaxurl, { action: 'webpfree_scan_images', nonce: webpFree.nonce, count_only: true }); if (countResponse.success && countResponse.data.total_without_alt) { totalImages = Math.min(countResponse.data.total_without_alt, 14); } else { showToast(i18n.error, false); btn.prop('disabled', false).text(i18n.startUpdate); progressContainer.hide(); return; } } catch (e) { showToast(i18n.error, false); btn.prop('disabled', false).text(i18n.startUpdate); progressContainer.hide(); return; } let processed = 0; let updatedCount = 0; async function processBatch(offset) { try { const response = await $.post(webpFree.ajaxurl, { action: 'webpfree_update_images', nonce: webpFree.nonce, offset: offset, limit: batchSize }); if (response.success) { processed += response.data.processed; updatedCount += response.data.updated; const percentage = Math.min((processed / totalImages) * 100, 100).toFixed(1); progressBar.css('width', percentage + '%'); progressText.text(i18n.progress.replace('%s', percentage).replace('%s', processed).replace('%s', totalImages)); if (processed < totalImages && processed < 14) { await processBatch(offset + batchSize); } else { showToast(`بروزرسانی گروهی: ${updatedCount} متن جایگزین بروزرسانی شد.`, true); btn.prop('disabled', false).text(i18n.startUpdate); progressContainer.hide(); } } else { showToast(response.data.message || i18n.error, false); btn.prop('disabled', false).text(i18n.startUpdate); progressContainer.hide(); } } catch (e) { showToast(i18n.error, false); btn.prop('disabled', false).text(i18n.startUpdate); progressContainer.hide(); } } await processBatch(0); }); function loadImagesTable() { $('#image-table-container').html('<p>' + i18n.loading + '</p>'); $.post(webpFree.ajaxurl, { action: 'webpfree_load_images_table', nonce: webpFree.nonce }) .done(res => { if (res.success) { $('#image-table-container').html(res.data.table); } else { showToast(res.data.message || i18n.error, false); } }) .fail(() => showToast(i18n.error, false)); } $('#image-table-container').on('change', '#select-all-images', function() { $('#image-table-container .image-select').prop('checked', this.checked).trigger('change'); }); $('#image-table-container').on('change', '.image-select', function() { $('#convert-webp').prop('disabled', $('#image-table-container .image-select:checked').length === 0); }); $('#image-table-container').on('dblclick', '.editable-alt', function() { const cell = $(this); cell.find('.alt-text-display').hide(); cell.find('.alt-text-input, .save-single-alt, .cancel-single-alt').show(); cell.find('.alt-text-input').focus().data('original-value', cell.find('.alt-text-input').val()); }); $('#image-table-container').on('click', '.save-single-alt', async function() { if (!await refreshNonce()) return; const btn = $(this); const cell = btn.closest('.editable-alt'); const input = cell.find('.alt-text-input'); const display = cell.find('.alt-text-display'); const imageId = cell.closest('tr').data('id'); const newText = input.val(); btn.text('...').prop('disabled', true); const updateData = { id: imageId, text: newText }; $.post(webpFree.ajaxurl, { action: 'webpfree_save_alt_texts', nonce: webpFree.nonce, updates: [updateData] }) .done(res => { if (res.success) { display.text(newText); showToast(res.data.message, true); } else { showToast(res.data.message || i18n.error, false); } }) .fail(() => showToast(i18n.error, false)) .always(() => { input.hide(); btn.hide().text(i18n.saveAltChanges).prop('disabled', false); cell.find('.cancel-single-alt').hide(); display.show(); }); }); $('#image-table-container').on('click', '.cancel-single-alt', function() { const cell = $(this).closest('.editable-alt'); const input = cell.find('.alt-text-input'); const display = cell.find('.alt-text-display'); input.val(input.data('original-value')).hide(); $(this).hide(); cell.find('.save-single-alt').hide(); display.show(); }); $('#convert-webp').on('click', async function() { if (!await refreshNonce()) return; const btn = $(this); const imageIds = $('#image-table-container .image-select:checked').map((_, el) => $(el).val()).get(); if (imageIds.length > 0) { btn.prop('disabled', true).text(i18n.converting); $.post(webpFree.ajaxurl, { action: 'webpfree_convert_to_webp', nonce: webpFree.nonce, image_ids: imageIds }) .done(res => { if (res.success) { loadImagesTable(); $.post(webpFree.ajaxurl, { action: 'webpfree_clear_caches', nonce: webpFree.nonce }); } showToast(res.data.message, res.success); }) .fail(() => showToast(i18n.error, false)) .always(() => { btn.prop('disabled', false).text(i18n.convertingSelected); if ($('#select-all-images').length) $('#select-all-images').prop('checked', false); $('#convert-webp').prop('disabled', true); }); } }); $('#save-settings').on('click', async function() { if (!await refreshNonce()) return; const btn = $(this).prop('disabled', true).text(i18n.saving); $.post(webpFree.ajaxurl, { action: 'webpfree_save_settings', nonce: webpFree.nonce, settings: $('#bulk-webp-settings').serialize() }) .done(res => { if (res.success) { $.post(webpFree.ajaxurl, { action: 'webpfree_clear_caches', nonce: webpFree.nonce }); } showToast(res.data.message, res.success); }) .fail(() => showToast(i18n.error, false)) .always(() => btn.prop('disabled', false).text('ذخیره تنظیمات')); }); $('#clear-logs-btn').on('click', async function() { if (!await refreshNonce()) return; if (!confirm('آیا از پاک کردن تمام گزارشات مطمئن هستید؟ این عمل غیرقابل بازگشت است.')) return; const btn = $(this).prop('disabled', true).text(i18n.clearing); $.post(webpFree.ajaxurl, { action: 'webpfree_clear_logs', nonce: webpFree.nonce }) .done(res => { if (res.success) $('#log-container').html('<p>گزارشات با موفقیت پاک شدند.</p>'); showToast(res.data.message, res.success); }) .fail(() => showToast(i18n.error, false)) .always(() => btn.prop('disabled', false).text('پاک کردن همه گزارشات')); }); }); </script> <?php } public function render_settings_page() { $formats = get_option('webpfree_formats', ['jpeg', 'png', 'jpg']); $quality = get_option('webpfree_quality', 80); ?> <div class="wrap"> <div id="webpfree-toast-container"></div> <h1>تنظیمات WebP Free</h1> <h2 class="nav-tab-wrapper"> <a href="#stats" class="nav-tab">آمار</a> <a href="#update" class="nav-tab">بروزرسانی گروهی</a> <a href="#webp" class="nav-tab">تبدیل به WebP</a> <a href="#bulk-webp" class="nav-tab">تنظیمات</a> <a href="#logs" class="nav-tab">گزارشات</a> <a href="#help" class="nav-tab">راهنما</a> </h2> <div id="stats" class="tab-content"> <h2>آمار کتابخانه رسانه</h2> <button id="scan-images" class="button button-primary" aria-label="اسکن و نمایش آمار تصاویر">اسکن و نمایش آمار</button> <div id="stats-table-container" class="result-container"></div> </div> <div id="update" class="tab-content"> <h2>بروزرسانی خودکار متن جایگزین</h2> <p>این عملیات تصاویر فاقد متن جایگزین را در بستههای کوچک پردازش میکند و پیشرفت با نوار پیشرفت نمایش داده میشود: نکته: خطای غیرمنتظره یعنی عکسی بدون متن جایگزین ندارید و این به معنای خطای سیستمی نیست!</p> <button id="start-processing" class="button button-primary" aria-label="شروع بروزرسانی گروهی متنهای جایگزین">شروع بروزرسانی گروهی</button> <div id="progress-container" class="progress-container"> <div class="progress-bar"> <div class="progress-bar-fill"></div> </div> <div class="progress-text">پیشرفت: ۰٪ (پردازش ۰ از ۰ تصویر)</div> </div> </div> <div id="webp" class="tab-content"> <h2>تبدیل انتخابی به WebP و ویرایش متن جایگزین</h2> <div class="notice notice-warning inline"><p><strong>هشدار:</strong> برای جلوگیری از فشار به سرور، توصیه میشود هر بار <span class="max-ten-warning">حداکثر ۱۰</span> تصویر را برای تبدیل انتخاب کنید.</p></div> <div class="actions-bar"><button id="convert-webp" class="button button-primary" disabled aria-label="تبدیل تصاویر انتخابشده به فرمت WebP">تبدیل منتخب به WebP</button></div> <div id="image-table-container"></div> </div> <div id="bulk-webp" class="tab-content"> <h2>تنظیمات تبدیل WebP</h2> <form id="bulk-webp-settings"> <table class="form-table"> <tr> <th scope="row"><label for="quality">کیفیت تصویر:</label></th> <td> <input type="number" id="quality" name="quality" value="<?php echo esc_attr($quality); ?>" min="10" max="100" class="small-text" /> <p class="description">این عدد، درصد کیفیت تصویر خروجی را تعیین میکند (بین ۱۰ تا ۱۰۰). عدد کمتر به معنای حجم پایینتر و کیفیت کمتر است. مقدار پیشنهادی برای تعادل بین حجم و کیفیت، ۸۰ است.</p> </td> </tr> <tr> <th scope="row">فرمتهای قابل تبدیل:</th> <td> <fieldset> <label><input type="checkbox" name="formats[]" value="jpeg" <?php checked(in_array('jpeg', $formats)); ?>> JPEG</label> <label><input type="checkbox" name="formats[]" value="jpg" <?php checked(in_array('jpg', $formats)); ?>> JPG</label> <label><input type="checkbox" name="formats[]" value="png" <?php checked(in_array('png', $formats)); ?>> PNG</label> </fieldset> </td> </tr> </table> <p class="submit"><button type="button" id="save-settings" class="button button-secondary" aria-label="ذخیره تنظیمات افزونه">ذخیره تنظیمات</button></p> </form> </div> <div id="logs" class="tab-content"> <h2>گزارش عملیات</h2> <p>برای مشاهده گزارشات، دکمه "اسکن و نمایش گزارشات" را کلیک کنید.</p> <div class="actions-bar"> <button id="scan-logs" class="button button-primary" aria-label="اسکن و نمایش گزارشات عملیات">اسکن و نمایش گزارشات</button> <button id="clear-logs-btn" class="button button-danger" aria-label="پاک کردن تمام گزارشات">پاک کردن همه گزارشات</button> </div> <div id="log-container" class="result-container"></div> </div> <div id="help" class="tab-content"> <h2>راهنمای جامع افزونه</h2> <p>این افزونه ابزاری قدرتمند برای بهینهسازی تصاویر سایت شما از طریق مدیریت متن جایگزین و تبدیل آنها به فرمت مدرن WebP است.</p> <a href="https://mrbarati.com/webp-pro/" id="webp-pro-btn" target="_blank" rel="noopener" aria-label="خرید نسخه Webp Pro">خرید نسخه Webp Pro</a> <h3>تب آمار</h3> <p>این بخش یک نمای کلی از وضعیت تصاویر کتابخانه رسانه شما ارائه میدهد. با کلیک روی دکمه "اسکن"، میتوانید آماری دقیق از تعداد کل تصاویر، تعداد تصاویر JPG و PNG، تعداد تصاویر WebP و وضعیت متنهای جایگزین (alt) مشاهده کنید.</p> <h3>تب بروزرسانی گروهی</h3> <p>این ابزار تصاویر فاقد متن جایگزین را تا سقف ۱۴ تصویر پردازش میکند. متن جایگزین بر اساس اولویت (عنوان نوشته، عنوان تصویر، نام فایل) تولید میشود.</p> <h3>تب تبدیل به WebP</h3> <p>تا ۱۴ تصویر JPG و PNG نمایش داده میشود. میتوانید تصاویر را انتخاب کرده، متن جایگزین را ویرایش کنید و آنها را به WebP تبدیل کنید.</p> <h3>تب تنظیمات</h3> <p>کیفیت تصویر WebP و فرمتهای قابل تبدیل را میتوانید تنظیم کنید.</p> <h3>تب گزارشات</h3> <p>تمام عملیات مهم با تاریخ و زمان در اینجا ثبت میشود.</p> </div> </div> <?php } public function ajax_scan_images() { check_ajax_referer('webpfree_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'دسترسی مجاز نیست.']); } global $wpdb; $total_images = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'attachment' AND post_mime_type LIKE 'image/%'"); $jpeg_images = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'attachment' AND (post_mime_type = 'image/jpeg' OR post_mime_type = 'image/jpg')"); $png_images = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'attachment' AND post_mime_type = 'image/png'"); $webp_images = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->posts} WHERE post_type = 'attachment' AND post_mime_type = 'image/webp'"); $images_with_alt = (int) $wpdb->get_var("SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id WHERE p.post_type = 'attachment' AND p.post_mime_type LIKE 'image/%' AND pm.meta_key = '_wp_attachment_image_alt' AND pm.meta_value != ''"); $images_without_alt = $total_images - $images_with_alt; if (isset($_POST['count_only']) && $_POST['count_only']) { wp_send_json_success(['total_without_alt' => $images_without_alt]); } $percent_with_alt = ($total_images > 0) ? round(($images_with_alt / $total_images) * 100, 1) : 0; $percent_without_alt = ($total_images > 0) ? 100 - $percent_with_alt : 0; ob_start(); ?> <table class="widefat striped"> <thead> <tr> <th>معیار</th> <th>تعداد</th> <th>درصد</th> </tr> </thead> <tbody> <tr><td>کل تصاویر کتابخانه</td><td><?php echo esc_html(number_format_i18n($total_images)); ?></td><td>۱۰۰٪</td></tr> <tr><td>تعداد تصاویر JPG/JPEG</td><td><?php echo esc_html(number_format_i18n($jpeg_images)); ?></td><td>-</td></tr> <tr><td>تعداد تصاویر PNG</td><td><?php echo esc_html(number_format_i18n($png_images)); ?></td><td>-</td></tr> <tr><td>تعداد تصاویر WebP</td><td><?php echo esc_html(number_format_i18n($webp_images)); ?></td><td>-</td></tr> <tr><td>تصاویر دارای متن جایگزین</td><td><?php echo esc_html(number_format_i18n($images_with_alt)); ?></td><td><?php echo esc_html($percent_with_alt); ?>٪</td></tr> <tr><td>تصاویر بدون متن جایگزین</td><td><?php echo esc_html(number_format_i18n($images_without_alt)); ?></td><td><?php echo esc_html($percent_without_alt); ?>٪</td></tr> </tbody> </table> <?php wp_send_json_success(['table' => ob_get_clean()]); } public function ajax_refresh_logs() { check_ajax_referer('webpfree_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'دسترسی مجاز نیست.']); } $logs = get_option('webpfree_logs', []); ob_start(); if (empty($logs)) { echo '<p>هیچ گزارشی یافت نشد.</p>'; } else { ?> <ul style="counter-reset: item;"> <?php foreach ($logs as $log) : ?> <li> <span class="log-message"><?php echo esc_html($log['message']); ?></span> <span class="log-time"><?php echo esc_html(wp_date('Y/m/d H:i:s', strtotime($log['time']))); ?></span> </li> <?php endforeach; ?> </ul> <?php } wp_send_json_success(['logs' => ob_get_clean()]); } public function ajax_update_images() { check_ajax_referer('webpfree_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'دسترسی مجاز نیست.']); } global $wpdb; $offset = isset($_POST['offset']) ? max(0, intval($_POST['offset'])) : 0; $limit = min(14, max(1, isset($_POST['limit']) ? intval($_POST['limit']) : 14)); $images = $wpdb->get_results($wpdb->prepare( "SELECT p.ID, p.post_parent, p.post_title, p.guid FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = '_wp_attachment_image_alt' WHERE p.post_type = 'attachment' AND p.post_mime_type LIKE 'image/%%' AND (pm.meta_value IS NULL OR pm.meta_value = '') LIMIT %d OFFSET %d", $limit, $offset )); if (empty($images)) { wp_send_json_success([ 'message' => 'هیچ تصویری برای بروزرسانی یافت نشد.', 'processed' => 0, 'updated' => 0 ]); return; } $updated_count = 0; foreach ($images as $image) { $alt_text = ''; if ($image->post_parent > 0) { $alt_text = get_the_title($image->post_parent); } if (empty(trim($alt_text))) { $alt_text = $image->post_title; } if (empty(trim($alt_text))) { $alt_text = pathinfo($image->guid, PATHINFO_FILENAME); } $alt_text = ucwords(str_replace(['-', '_'], ' ', $alt_text)); if (update_post_meta($image->ID, '_wp_attachment_image_alt', sanitize_text_field($alt_text))) { $updated_count++; } } if ($updated_count > 0) { $this->add_log(sprintf('بروزرسانی گروهی: %s متن جایگزین بروزرسانی شد.', number_format_i18n($updated_count))); $this->clear_caches(); } wp_send_json_success([ 'message' => sprintf('%s تصویر با موفقیت بروزرسانی شد.', number_format_i18n($updated_count)), 'processed' => count($images), 'updated' => $updated_count ]); } public function ajax_load_images_table() { check_ajax_referer('webpfree_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'دسترسی مجاز نیست.']); } $query_args = [ 'post_type' => 'attachment', 'post_status' => 'inherit', 'posts_per_page' => 14, 'post_mime_type' => ['image/jpeg', 'image/png', 'image/jpg'], 'meta_query' => [ [ 'key' => '_webp_converted_to', 'compare' => 'NOT EXISTS', ], ], 'orderby' => 'date', 'order' => 'DESC', ]; $query = new WP_Query($query_args); ob_start(); if ($query->have_posts()) { ?> <table class="widefat striped" id="webp-image-list"> <thead> <tr> <th class="check-column"><input type="checkbox" id="select-all-images"></th> <th>پیشنمایش</th> <th>نام فایل</th> <th>متن جایگزین (دوبار کلیک کنید برای ویرایش)</th> <th>حجم (KB)</th> </tr> </thead> <tbody> <?php while ($query->have_posts()) : $query->the_post(); $image_id = get_the_ID(); $file_path = get_attached_file($image_id); $size_kb = (file_exists($file_path)) ? round(filesize($file_path) / 1024, 1) : '-'; $alt_text = get_post_meta($image_id, '_wp_attachment_image_alt', true); $filename = wp_basename(get_the_guid()); $display_filename = (mb_strlen($filename) > 22) ? mb_substr($filename, 0, 22) . '...' : $filename; ?> <tr data-id="<?php echo esc_attr($image_id); ?>"> <th class="check-column"><input type="checkbox" class="image-select" value="<?php echo esc_attr($image_id); ?>"></th> <td><?php echo wp_get_attachment_image($image_id, [60, 60], true); ?></td> <td title="<?php echo esc_attr($filename); ?>"><?php echo esc_html($display_filename); ?></td> <td class="editable-alt" title="دوبار کلیک کنید برای ویرایش"> <span class="alt-text-display"><?php echo esc_html($alt_text); ?></span> <input type="text" class="alt-text-input" value="<?php echo esc_attr($alt_text); ?>"> <button class="save-single-alt">OK</button> <button class="cancel-single-alt">Noki</button> </td> <td><?php echo esc_html($size_kb); ?></td> </tr> <?php endwhile; wp_reset_postdata(); ?> </tbody> </table> <?php } else { ?> <div class="notice notice-info inline"> <p>تبریک! تمام تصاویر واجد شرایط به فرمت WebP تبدیل شدهاند.</p> </div> <?php } wp_send_json_success(['table' => ob_get_clean()]); } public function ajax_save_alt_texts() { check_ajax_referer('webpfree_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'دسترسی مجاز نیست.']); } if (!isset($_POST['updates']) || !is_array($_POST['updates'])) { wp_send_json_error(['message' => 'دادههای نامعتبر.']); } $updates = (array) $_POST['updates']; $updated_count = 0; foreach ($updates as $update) { $image_id = isset($update['id']) ? intval($update['id']) : 0; $alt_text = isset($update['text']) ? sanitize_text_field(wp_unslash($update['text'])) : ''; if ($image_id > 0 && current_user_can('edit_post', $image_id)) { if (update_post_meta($image_id, '_wp_attachment_image_alt', $alt_text)) { $updated_count++; $this->add_log(sprintf('متن جایگزین برای تصویر ID %d به "%s" تغییر یافت.', $image_id, $alt_text)); $this->clear_caches(); } } } if ($updated_count > 0) { wp_send_json_success(['message' => sprintf('متن جایگزین برای %s تصویر ذخیره شد.', number_format_i18n($updated_count))]); } else { wp_send_json_error(['message' => 'هیچ تغییری برای ذخیره وجود نداشت.']); } } public function ajax_convert_to_webp() { check_ajax_referer('webpfree_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'دسترسی مجاز نیست.']); } $image_ids = isset($_POST['image_ids']) ? array_map('intval', (array) $_POST['image_ids']) : []; if (empty($image_ids)) { wp_send_json_error(['message' => 'هیچ تصویری برای تبدیل انتخاب نشده است.']); } $success_count = 0; $failed_count = 0; foreach ($image_ids as $image_id) { $result = $this->convert_and_create_webp_attachment($image_id); if ($result) { $success_count++; } else { $failed_count++; } } $message = sprintf( 'عملیات انجام شد: <br><b>موفق:</b> %s تصویر <br><b>ناموفق:</b> %s تصویر', number_format_i18n($success_count), number_format_i18n($failed_count) ); if ($success_count > 0) { $this->add_log(sprintf('تبدیل گروهی: %d تصویر به WebP تبدیل شد.', $success_count)); $this->clear_caches(); } wp_send_json_success(['message' => $message]); } public function ajax_save_settings() { check_ajax_referer('webpfree_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'دسترسی مجاز نیست.']); } $settings = []; if (isset($_POST['settings'])) { parse_str($_POST['settings'], $settings); } $quality = isset($settings['quality']) ? absint($settings['quality']) : 80; $formats = isset($settings['formats']) && is_array($settings['formats']) ? array_map('sanitize_text_field', $settings['formats']) : []; if ($quality < 10 || $quality > 100) { wp_send_json_error(['message' => 'کیفیت تصویر باید بین ۱۰ تا ۱۰۰ باشد.']); return; } if (empty($formats)) { wp_send_json_error(['message' => 'حداقل یک فرمت باید انتخاب شود.']); return; } update_option('webpfree_quality', $quality); update_option('webpfree_formats', $formats); $this->add_log('تنظیمات افزونه ذخیره شد.'); $this->clear_caches(); wp_send_json_success(['message' => 'تنظیمات با موفقیت ذخیره شد.']); } public function ajax_clear_logs() { check_ajax_referer('webpfree_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'دسترسی مجاز نیست.']); } delete_option('webpfree_logs'); wp_send_json_success(['message' => 'تمام گزارشات با موفقیت پاک شدند.']); } public function ajax_clear_caches() { check_ajax_referer('webpfree_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => 'دسترسی مجاز نیست.']); } $this->clear_caches(); wp_send_json_success(['message' => 'کشها با موفقیت پاک شدند.']); } public function ajax_refresh_nonce() { if (!current_user_can('manage_options')) { wp_send_json_error(); } wp_send_json_success(['new_nonce' => wp_create_nonce('webpfree_nonce')]); } private function convert_and_create_webp_attachment($original_attachment_id) { if (!function_exists('wp_get_image_editor') || !function_exists('wp_generate_attachment_metadata') || !function_exists('wp_insert_attachment')) { $this->add_log(sprintf('خطا: توابع مورد نیاز برای تبدیل تصویر ID %d در دسترس نیست.', $original_attachment_id)); return false; } $original_file_path = get_attached_file($original_attachment_id); if (!$original_file_path || !file_exists($original_file_path)) { $this->add_log(sprintf('خطا: فایل تصویر ID %d یافت نشد.', $original_attachment_id)); return false; } $editor = wp_get_image_editor($original_file_path); if (is_wp_error($editor)) { $this->add_log(sprintf('خطا: ویرایشگر تصویر برای ID %d با خطا مواجه شد: %s', $original_attachment_id, $editor->get_error_message())); return false; } $editor->set_quality(get_option('webpfree_quality', 80)); $webp_saved = $editor->save($editor->generate_filename('webp'), 'image/webp'); if (is_wp_error($webp_saved) || !isset($webp_saved['path'])) { $this->add_log(sprintf('خطا: ذخیره تصویر WebP برای ID %d با خطا مواجه شد: %s', $original_attachment_id, is_wp_error($webp_saved) ? $webp_saved->get_error_message() : 'خطای ناشناخته')); return false; } $webp_path = $webp_saved['path']; $original_post = get_post($original_attachment_id); $attachment_data = [ 'post_mime_type' => $webp_saved['mime-type'], 'post_title' => $original_post->post_title . ' (WebP)', 'post_content' => $original_post->post_content, 'post_status' => 'inherit' ]; $webp_attachment_id = wp_insert_attachment($attachment_data, $webp_path, 0, true); if (is_wp_error($webp_attachment_id)) { @unlink($webp_path); $this->add_log(sprintf('خطا: ایجاد پیوست WebP برای تصویر ID %d با خطا مواجه شد: %s', $original_attachment_id, $webp_attachment_id->get_error_message())); return false; } if (file_exists(ABSPATH . 'wp-admin/includes/image.php')) { require_once ABSPATH . 'wp-admin/includes/image.php'; wp_update_attachment_metadata($webp_attachment_id, wp_generate_attachment_metadata($webp_attachment_id, $webp_path)); } $original_alt = get_post_meta($original_attachment_id, '_wp_attachment_image_alt', true); if (!empty($original_alt)) { update_post_meta($webp_attachment_id, '_wp_attachment_image_alt', $original_alt); } update_post_meta($original_attachment_id, '_webp_converted_to', $webp_attachment_id); $this->add_log(sprintf('تصویر "%s" (ID: %d) به WebP (ID: %d) تبدیل شد.', $original_post->post_title, $original_attachment_id, $webp_attachment_id)); return true; } public function replace_images_with_webp_in_content($content) { if (is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) { return $content; } preg_match_all('/<img[^>]+>/i', $content, $matches); if (empty($matches[0])) { return $content; } foreach ($matches[0] as $img_tag) { $original_id = 0; if (preg_match('/wp-image-([0-9]+)/i', $img_tag, $class_id_match)) { $original_id = intval($class_id_match[1]); } else if (preg_match('/src="([^"]+)"/i', $img_tag, $src_match)) { $original_id = attachment_url_to_postid($src_match[1]); } if (!$original_id) { continue; } $webp_id = get_post_meta($original_id, '_webp_converted_to', true); if (!empty($webp_id) && get_post($webp_id)) { preg_match('/class="([^"]+)"/', $img_tag, $class_match); $classes = isset($class_match[1]) ? $class_match[1] : ''; $size = 'full'; if (preg_match('/size-([a-zA-Z0-9_-]+)/', $classes, $size_match)) { $size = $size_match[1]; } $webp_url = wp_get_attachment_image_url($webp_id, $size); $alt_text = get_post_meta($original_id, '_wp_attachment_image_alt', true); $new_tag = sprintf( '<picture><source type="image/webp" srcset="%s"><img src="%s" alt="%s" class="%s"></picture>', esc_url($webp_url), esc_url(wp_get_attachment_image_url($original_id, $size)), esc_attr($alt_text), esc_attr($classes) ); if ($new_tag) { $content = str_replace($img_tag, $new_tag, $content); } } } return $content; } } WebP_Free::instance();
سفارش خرید نسخه پرو⬇️
خدمات پشتیبانی سایت وردپرسی
بعد از تکمیل فرم وبررسی اولیه باشما تماس می گیرم
۴/۵ - (۸ امتیاز)
یک پاسخ
از نمونه خارجی بهتر و سبکتر بود!