افزونه وبپی پرو
با افزونه 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();
سفارش خرید نسخه پرو⬇️
خدمات پشتیبانی سایت وردپرسی
بعد از تکمیل فرم وبررسی اولیه باشما تماس می گیرم
۴/۵ - (۱۸ امتیاز)
یک پاسخ
از نمونه خارجی بهتر و سبکتر بود!