From c177f340dc2c6bebc1960f6e640e9213ad371f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E6=B4=BE=E5=A4=87=E6=A1=88?= <130886204+modiqi@users.noreply.github.com> Date: Wed, 28 May 2025 14:44:57 +0800 Subject: [PATCH] =?UTF-8?q?v2.0=20=E7=A8=B3=E5=AE=9A=E7=89=88=E5=8F=91?= =?UTF-8?q?=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/js/archiver.js | 505 +++++++++++---- css/archiver.css | 453 -------------- css/archiver.min.css | 1 - includes/class-archiver-admin.php | 848 +++++++++++++++++--------- includes/class-archiver-cache.php | 308 +++++++--- includes/class-archiver-dashboard.php | 508 +++++++++++++++ includes/class-archiver.php | 586 +++++++++++++++--- js/archiver.js | 430 ------------- js/archiver.min.js | 1 - languages/archiver-zh_CN.l10n.php | 2 +- languages/archiver-zh_CN.mo | Bin 9671 -> 12858 bytes languages/archiver-zh_CN.po | 498 ++++++++++----- languages/archiver.pot | 597 ++++++++++++------ wp-archiver.php | 77 +-- 14 files changed, 2979 insertions(+), 1835 deletions(-) delete mode 100644 css/archiver.css delete mode 100644 css/archiver.min.css create mode 100644 includes/class-archiver-dashboard.php delete mode 100644 js/archiver.js delete mode 100644 js/archiver.min.js diff --git a/assets/js/archiver.js b/assets/js/archiver.js index deaa2ef..e6a21bc 100644 --- a/assets/js/archiver.js +++ b/assets/js/archiver.js @@ -1,5 +1,6 @@ /** - * WP Archiver 现代化前端脚本 + * WP Archiver Modern Frontend Script + * Complete version with all functionality */ (function($) { @@ -8,15 +9,18 @@ const Archiver = { config: { - checkInterval: 5000, - maxChecks: 12, + checkInterval: 3000, + maxChecks: 20, fadeInDuration: 300, cachePrefix: 'archiver_cache_', - debounceDelay: 300 + debounceDelay: 300, + retryDelay: 1000, + maxRetries: 3 }, currentService: null, serviceCheckTimers: {}, + loadingCache: {}, init: function() { $(document).ready(() => { @@ -25,11 +29,12 @@ this.initAdminPage(); this.initLazyLoading(); this.initServiceChecks(); + this.addNotificationStyles(); }); }, /** - * 初始化元框 + * Initialize metabox */ initMetabox: function() { const $container = $('#archiver-snapshots'); @@ -42,7 +47,7 @@ this.currentService = service; - // 服务标签切换 + // Service tab switching $('.archiver-service-tab').on('click', (e) => { e.preventDefault(); const $tab = $(e.currentTarget); @@ -55,10 +60,10 @@ this.loadSnapshots(url, $container, selectedService); }); - // 延迟加载快照 + // Load snapshots with retry mechanism this.loadSnapshots(url, $container, service); - // 立即存档按钮 + // Immediate archive button $('#archiver-immediate-snapshot').on('click', (e) => { e.preventDefault(); this.triggerSnapshot(url, this.currentService); @@ -66,19 +71,21 @@ }, /** - * 初始化管理栏 + * Initialize admin bar */ initAdminBar: function() { const $trigger = $('#wp-admin-bar-archiver-trigger a'); if (!$trigger.length) return; - // 延迟加载快照计数 + // Load snapshot count with delay const $countItem = $('#wp-admin-bar-archiver-snapshots'); if ($countItem.length && archiver.url) { - this.updateAdminBarCount(archiver.url); + setTimeout(() => { + this.updateAdminBarCount(archiver.url); + }, 500); } - // 触发快照 + // Trigger snapshot $trigger.on('click', (e) => { e.preventDefault(); this.triggerAdminBarSnapshot(); @@ -86,41 +93,34 @@ }, /** - * 初始化管理页面 + * Initialize admin page */ initAdminPage: function() { - // 标签切换 - $('.archiver-tab').on('click', function(e) { + // Tab switching + $('.settings-tab').on('click', function(e) { e.preventDefault(); const $tab = $(this); const tabId = $tab.data('tab'); - $('.archiver-tab').removeClass('active'); + $('.settings-tab').removeClass('active'); $tab.addClass('active'); - $('.archiver-tab-content').removeClass('active'); - $('#' + tabId).addClass('active'); + $('.settings-section').hide(); + $('.settings-section[data-section="' + tabId + '"]').show(); - // 保存用户偏好 + // Save user preference if (typeof(Storage) !== "undefined") { localStorage.setItem('archiver_active_tab', tabId); } }); - // 恢复上次的标签 + // Restore last tab const savedTab = localStorage.getItem('archiver_active_tab'); - if (savedTab && $(`.archiver-tab[data-tab="${savedTab}"]`).length) { - $(`.archiver-tab[data-tab="${savedTab}"]`).trigger('click'); + if (savedTab && $(`.settings-tab[data-tab="${savedTab}"]`).length) { + $(`.settings-tab[data-tab="${savedTab}"]`).trigger('click'); } - // 标签链接 - $('.archiver-tab-link').on('click', function(e) { - e.preventDefault(); - const tab = $(this).attr('href').replace('#', ''); - $(`.archiver-tab[data-tab="${tab}"]`).trigger('click'); - }); - - // 测试服务连接 + // Test service connection $('.test-service').on('click', (e) => { e.preventDefault(); const $button = $(e.currentTarget); @@ -130,48 +130,50 @@ }, /** - * 初始化服务检查 + * Initialize service checks */ initServiceChecks: function() { - if ($('.archiver-service-card').length === 0) return; + if ($('.service-status').length === 0) return; - // 检查所有服务状态 - $('.archiver-service-card').each((index, element) => { - const $card = $(element); - const service = $card.find('.test-service').data('service'); - if (service) { - this.checkServiceStatus(service); + // Check all service statuses + $('.service-status').each((index, element) => { + const $status = $(element); + const serviceId = $status.attr('id').replace('status-', ''); + if (serviceId) { + setTimeout(() => { + this.checkServiceStatus(serviceId); + }, index * 200); // Staggered loading } }); }, /** - * 检查服务状态 + * Check service status */ checkServiceStatus: function(service) { - const $status = $(`#service-status-${service}`); + const $status = $(`#status-${service}`); if (!$status.length) return; - // 清除现有定时器 + // Clear existing timer if (this.serviceCheckTimers[service]) { clearTimeout(this.serviceCheckTimers[service]); } $status.removeClass('online offline').find('.status-text').text(archiver.i18n.checking); - // 模拟检查延迟 + // Simulate check with realistic delay this.serviceCheckTimers[service] = setTimeout(() => { - // 根据服务配置判断状态 - const isEnabled = archiver.enabled_services[service]; + // Check if service is enabled + const isEnabled = archiver.enabled_services && archiver.enabled_services[service]; const statusClass = isEnabled ? 'online' : 'offline'; const statusText = isEnabled ? archiver.i18n.online : archiver.i18n.offline; $status.addClass(statusClass).find('.status-text').text(statusText); - }, 1000); + }, 800 + Math.random() * 400); }, /** - * 测试服务连接 + * Test service connection */ testServiceConnection: function(service, $button) { const originalText = $button.text(); @@ -185,6 +187,7 @@ service: service, nonce: archiver.admin_nonce }, + timeout: 10000, success: (response) => { if (response.success) { this.showNotification(response.data.message, 'success'); @@ -193,8 +196,12 @@ this.showNotification(response.data.message || archiver.i18n.test_failed, 'error'); } }, - error: () => { - this.showNotification(archiver.i18n.test_failed, 'error'); + error: (xhr, status, error) => { + let message = archiver.i18n.test_failed; + if (status === 'timeout') { + message = 'Connection timeout - service may be slow'; + } + this.showNotification(message, 'error'); }, complete: () => { $button.prop('disabled', false).text(originalText); @@ -203,7 +210,7 @@ }, /** - * 延迟加载 + * Initialize lazy loading */ initLazyLoading: function() { if ('IntersectionObserver' in window) { @@ -228,23 +235,34 @@ }, /** - * 加载快照 + * Load snapshots with enhanced error handling */ - loadSnapshots: function(url, $container, service = null) { - // 检查本地缓存 - const cacheKey = this.getCacheKey(url, service); - const cached = this.getCache(cacheKey); - if (cached) { - this.displaySnapshots(cached, $container); - // 后台检查更新 - this.checkForUpdates(url, $container, service); - return; + loadSnapshots: function(url, $container, service = null, retryCount = 0) { + if (retryCount === 0) { + // Check local cache first + const cacheKey = this.getCacheKey(url, service); + const cached = this.getCache(cacheKey); + if (cached && cached.html) { + this.displaySnapshots(cached, $container); + // Background check for updates + setTimeout(() => { + this.checkForUpdates(url, $container, service); + }, 2000); + return; + } } - // 显示加载状态 + // Prevent multiple simultaneous requests + const requestKey = `${url}_${service || 'default'}`; + if (this.loadingCache[requestKey]) { + return; + } + this.loadingCache[requestKey] = true; + + // Show loading state this.showLoading($container); - // 从服务器获取 + // Fetch from server $.ajax({ url: archiver.ajax_url, type: 'POST', @@ -254,40 +272,72 @@ url: url, service: service }, + timeout: 15000, success: (response) => { + delete this.loadingCache[requestKey]; + if (response.success) { + const cacheKey = this.getCacheKey(url, service); this.setCache(cacheKey, response.data); this.displaySnapshots(response.data, $container); } else { - this.showError($container, response.data.message); + this.showError($container, response.data?.message || 'Failed to load snapshots'); } }, - error: () => { - this.showError($container, archiver.i18n.error); + error: (xhr, status, error) => { + delete this.loadingCache[requestKey]; + + // Retry mechanism + if (retryCount < this.config.maxRetries) { + setTimeout(() => { + this.loadSnapshots(url, $container, service, retryCount + 1); + }, this.config.retryDelay * (retryCount + 1)); + + $container.html(` +
+ + ${archiver.i18n.loading} (Retry ${retryCount + 1}/${this.config.maxRetries}) +
+ `); + } else { + let errorMsg = 'Failed to load archive data'; + if (status === 'timeout') { + errorMsg = 'Request timeout - archive service may be slow'; + } else if (xhr.status === 0) { + errorMsg = 'Network error - please check your connection'; + } + this.showError($container, errorMsg); + } } }); }, /** - * 显示快照 + * Display snapshots with enhanced UI */ displaySnapshots: function(data, $container) { if (!data.html || data.html.includes('archiver-no-snapshots')) { - $container.html(data.html || '

' + archiver.i18n.no_snapshots + '

'); + const noSnapshotsHtml = ` +
+

${archiver.i18n.no_snapshots}

+ Snapshots will appear here after archiving is complete. +
+ `; + $container.html(data.html || noSnapshotsHtml); return; } $container.hide().html(data.html).fadeIn(this.config.fadeInDuration); - // 添加淡入动画 - $container.find('li').each(function(index) { + // Enhanced animations + $container.find('.archiver-snapshot-list li').each(function(index) { $(this).css({ opacity: 0, transform: 'translateY(10px)' - }).delay(index * 50).animate({ + }).delay(index * 100).animate({ opacity: 1 }, { - duration: 300, + duration: 400, step: function(now, fx) { if (fx.prop === 'opacity' && now > 0.5) { $(this).css('transform', 'translateY(0)'); @@ -295,10 +345,18 @@ } }); }); + + // Add timestamp to links if missing + $container.find('a').each(function() { + const $link = $(this); + if (!$link.attr('title')) { + $link.attr('title', 'Archived snapshot - opens in new tab'); + } + }); }, /** - * 触发快照 + * Trigger snapshot with enhanced feedback */ triggerSnapshot: function(url, service = null) { const $button = $('#archiver-immediate-snapshot'); @@ -317,39 +375,45 @@ url: url, service: service }, + timeout: 30000, success: (response) => { if (response.success) { $status.removeClass('error').addClass('success') .html(' ' + response.data.message); - // 清除缓存并开始轮询 + // Clear cache and start monitoring this.clearCache(this.getCacheKey(url, service)); if (response.data.refresh) { this.startPolling(url, service); } - // 触发自定义事件 + // Trigger custom event $(document).trigger('archiver:snapshot:success', [url, service]); } else { $status.removeClass('success').addClass('error') - .html(' ' + response.data.message); + .html(' ' + + (response.data?.message || 'Archive request failed')); } }, - error: () => { + error: (xhr, status, error) => { + let errorMsg = archiver.i18n.error; + if (status === 'timeout') { + errorMsg = 'Archive request timeout - please try again'; + } $status.removeClass('success').addClass('error') - .html(' ' + archiver.i18n.error); + .html(' ' + errorMsg); }, complete: () => { $button.prop('disabled', false).removeClass('updating-message'); setTimeout(() => { $status.fadeOut(); - }, 5000); + }, 8000); } }); }, /** - * 管理栏触发快照 + * Admin bar snapshot trigger */ triggerAdminBarSnapshot: function() { const $item = $('#wp-admin-bar-archiver'); @@ -365,6 +429,7 @@ url: archiver.url, service: archiver.primary_service }, + timeout: 20000, success: (response) => { $item.removeClass('archiver-active'); if (response.success) { @@ -377,20 +442,21 @@ this.showNotification(response.message || archiver.i18n.error, 'error'); } }, - error: () => { + error: (xhr, status) => { $item.removeClass('archiver-active').addClass('archiver-failure'); - this.showNotification(archiver.i18n.error, 'error'); + const errorMsg = status === 'timeout' ? 'Archive timeout' : archiver.i18n.error; + this.showNotification(errorMsg, 'error'); }, complete: () => { setTimeout(() => { $item.removeClass('archiver-success archiver-failure'); - }, 2000); + }, 3000); } }); }, /** - * 更新管理栏计数 + * Update admin bar count */ updateAdminBarCount: function(url) { const $countItem = $('#wp-admin-bar-archiver-snapshots .ab-label'); @@ -405,17 +471,18 @@ url: url, service: archiver.primary_service }, + timeout: 10000, success: (response) => { if (response.success && response.data.count !== undefined) { const count = response.data.count >= 10 ? '10+' : response.data.count; - $countItem.text(archiver.i18n.view_all + ' (' + count + ')'); + $countItem.text(`${archiver.i18n.view_all} (${count})`); } } }); }, /** - * 检查更新 + * Check for updates */ checkForUpdates: function(url, $container, service = null) { $.ajax({ @@ -427,6 +494,7 @@ url: url, service: service }, + timeout: 8000, success: (response) => { if (response.success && response.data.has_update) { const cacheKey = this.getCacheKey(url, service); @@ -438,7 +506,7 @@ }, /** - * 开始轮询 + * Start polling for updates */ startPolling: function(url, service = null) { let checkCount = 0; @@ -454,10 +522,20 @@ this.checkForUpdates(url, $container, service); }, this.config.checkInterval); + + // Visual indicator for polling + if ($container.length) { + const $indicator = $('
Checking for new archives...
'); + $container.prepend($indicator); + + setTimeout(() => { + $indicator.fadeOut(() => $indicator.remove()); + }, this.config.checkInterval * 3); + } }, /** - * 显示加载状态 + * Show loading state */ showLoading: function($container) { const loadingHtml = ` @@ -470,30 +548,61 @@ }, /** - * 显示错误 + * Show error state */ showError: function($container, message) { - $container.html('

' + message + '

'); + const errorHtml = ` +
+

${message}

+ +
+ `; + $container.html(errorHtml); + + // Retry functionality + $container.find('.archiver-retry').on('click', () => { + const url = $container.data('url'); + const service = $container.data('service'); + this.loadSnapshots(url, $container, service); + }); }, /** - * 显示通知 + * Show notification */ showNotification: function(message, type = 'info') { - // 如果在管理页面,使用 WordPress 通知 + // If in admin page, use WordPress notifications if ($('body').hasClass('wp-admin')) { - const $notice = $('

' + message + '

'); + const $notice = $(`

${message}

`); $('.wrap > h1').after($notice); - // 自动关闭 + // Auto dismiss setTimeout(() => { $notice.fadeOut(() => $notice.remove()); }, 5000); + } else { + // Frontend notification + const $notification = $(` +
+ ${message} +
+ `); + + $('body').append($notification); + + setTimeout(() => { + $notification.addClass('show'); + }, 100); + + setTimeout(() => { + $notification.removeClass('show'); + setTimeout(() => $notification.remove(), 300); + }, 4000); } }, /** - * 缓存管理 + * Cache management */ getCacheKey: function(url, service = null) { const serviceKey = service || 'default'; @@ -508,13 +617,22 @@ if (cached) { const data = JSON.parse(cached); - // 5分钟缓存 + // 5 minute cache if (Date.now() - data.timestamp < 300000) { return data.content; + } else { + // Clean expired cache + sessionStorage.removeItem(key); } } } catch (e) { - console.warn('Cache read error:', e); + console.warn('Archiver cache read error:', e); + // Clean corrupted cache + try { + sessionStorage.removeItem(key); + } catch (cleanError) { + console.warn('Failed to clean corrupted cache:', cleanError); + } } return null; @@ -530,7 +648,9 @@ }; sessionStorage.setItem(key, JSON.stringify(cacheData)); } catch (e) { - console.warn('Cache write error:', e); + console.warn('Archiver cache write error:', e); + // Try to clear some space + this.cleanOldCache(); } }, @@ -541,23 +661,157 @@ if (key) { sessionStorage.removeItem(key); } else { - // 清除所有archiver缓存 - const keys = []; - for (let i = 0; i < sessionStorage.length; i++) { - const k = sessionStorage.key(i); - if (k && k.startsWith(this.config.cachePrefix)) { - keys.push(k); - } - } - keys.forEach(k => sessionStorage.removeItem(k)); + // Clear all archiver cache + this.cleanOldCache(true); } } catch (e) { - console.warn('Cache clear error:', e); + console.warn('Archiver cache clear error:', e); + } + }, + + cleanOldCache: function(clearAll = false) { + if (!this.isStorageAvailable()) return; + + try { + const keys = []; + const now = Date.now(); + + for (let i = 0; i < sessionStorage.length; i++) { + const key = sessionStorage.key(i); + if (key && key.startsWith(this.config.cachePrefix)) { + if (clearAll) { + keys.push(key); + } else { + try { + const cached = sessionStorage.getItem(key); + if (cached) { + const data = JSON.parse(cached); + // Remove if older than 10 minutes + if (now - data.timestamp > 600000) { + keys.push(key); + } + } + } catch (e) { + // Remove corrupted entries + keys.push(key); + } + } + } + } + + keys.forEach(key => { + try { + sessionStorage.removeItem(key); + } catch (e) { + console.warn('Failed to remove cache key:', key, e); + } + }); + + if (keys.length > 0) { + console.log(`Archiver: Cleaned ${keys.length} cache entries`); + } + } catch (e) { + console.warn('Cache cleanup error:', e); } }, /** - * 工具函数 + * Add notification styles + */ + addNotificationStyles: function() { + if ($('#archiver-notification-styles').length) return; + + $('head').append(` + + `); + }, + + /** + * Utility functions */ isStorageAvailable: function() { try { @@ -581,7 +835,7 @@ }, /** - * 防抖函数 + * Debounce function */ debounce: function(func, wait) { let timeout; @@ -593,13 +847,44 @@ clearTimeout(timeout); timeout = setTimeout(later, wait); }; + }, + + /** + * Global error handler + */ + handleGlobalError: function(error, context = 'Unknown') { + console.error(`Archiver Error [${context}]:`, error); + + if (typeof error === 'object' && error.message) { + this.showNotification(`Error in ${context}: ${error.message}`, 'error'); + } } }; - // 初始化 + // Global error handling + $(document).ajaxError(function(event, xhr, settings, error) { + if (settings.url && settings.url.includes('archiver')) { + console.error('Archiver AJAX Error:', { + url: settings.url, + status: xhr.status, + error: error, + response: xhr.responseText + }); + } + }); + + // Initialize when DOM is ready Archiver.init(); - // 暴露到全局 + // Expose to global scope window.WPArchiver = Archiver; + // Cleanup on page unload + $(window).on('beforeunload', function() { + // Clean old cache entries before leaving + if (Archiver.isStorageAvailable()) { + Archiver.cleanOldCache(); + } + }); + })(jQuery); diff --git a/css/archiver.css b/css/archiver.css deleted file mode 100644 index ca99949..0000000 --- a/css/archiver.css +++ /dev/null @@ -1,453 +0,0 @@ -/** - * WP Archiver 样式表 - */ - -/* 管理栏样式 */ -#wp-admin-bar-archiver .ab-item { - transition: all .3s ease; - color: #72aee6; -} - -#wp-admin-bar-archiver.archiver-success .ab-item { - color: #46b450 !important; -} - -#wp-admin-bar-archiver.archiver-failure .ab-item { - color: #dc3232 !important; -} - -#wp-admin-bar-archiver .ab-icon.dashicons.dashicons-backup { - top: 2px; -} - -#wp-admin-bar-archiver-trigger .ab-icon { - display: inline-block; - float: right !important; - margin: 0 0 0 6px; - opacity: 0; - transition: opacity .3s ease; - vertical-align: middle; -} - -#wp-admin-bar-archiver-trigger .ab-icon::before { - content: "\f463"; - font-family: dashicons; - font-size: 18px; - speak: none; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -#wp-admin-bar-archiver-trigger.archiver-active .ab-icon, -#wp-admin-bar-archiver.archiver-active .ab-icon { - opacity: 1; - animation: archiver-spin 1s infinite linear; -} - -@keyframes archiver-spin { - 0% { transform: rotate(0); } - 100% { transform: rotate(360deg); } -} - -/* 管理页面样式 */ -.archiver-admin-wrap { - max-width: 1200px; -} - -.archiver-version { - font-size: 13px; - padding-left: 10px; - color: #666; -} - -.nav-tab-wrapper { - margin-bottom: 20px; -} - -.nav-tab { - border: none; - background: #fff; - border-bottom: 2px solid transparent; - font-weight: 400; -} - -.nav-tab:hover { - background: #f0f0f1; - border-bottom-color: #dcdcde; -} - -.nav-tab.nav-tab-active { - border-bottom-color: #007cba; - font-weight: 600; - background: #f0f0f1; -} - -.archiver-tab-content { - background: #fff; - border: 1px solid #ccd0d4; - border-radius: 4px; - padding: 20px; - margin-bottom: 20px; -} - -/* 统计卡片 */ -.archiver-stats-container { - display: flex; - gap: 20px; - margin: 20px 0; - flex-wrap: wrap; -} - -.archiver-stat-card { - flex: 1; - min-width: 200px; - background: #fff; - border: 1px solid #ccd0d4; - border-radius: 4px; - padding: 20px; - text-align: center; - box-shadow: 0 1px 1px rgba(0,0,0,.04); - transition: transform 0.2s, box-shadow 0.2s; -} - -.archiver-stat-card:hover { - transform: translateY(-2px); - box-shadow: 0 3px 5px rgba(0,0,0,.1); -} - -.archiver-stat-card h3 { - margin: 0 0 10px; - font-size: 14px; - font-weight: 500; - color: #646970; -} - -.archiver-stat-card .stat-value { - font-size: 24px; - font-weight: 600; - color: #1d2327; - line-height: 1; -} - -.stat-subtitle { - font-size: 12px; - color: #666; - margin-top: 8px; -} - -/* 缓存分布 */ -.cache-distribution { - margin-top: 10px; -} - -.cache-bar { - display: flex; - height: 20px; - background: #f0f0f0; - border-radius: 10px; - overflow: hidden; - box-shadow: inset 0 1px 2px rgba(0,0,0,.1); -} - -.cache-segment { - height: 100%; - transition: width 0.3s ease; - position: relative; -} - -.cache-segment.hot { background: #e74c3c; } -.cache-segment.warm { background: #f39c12; } -.cache-segment.cold { background: #3498db; } -.cache-segment.frozen { background: #95a5a6; } - -.cache-legend { - display: flex; - justify-content: center; - gap: 20px; - margin-top: 10px; - font-size: 12px; -} - -.legend-item { - display: flex; - align-items: center; -} - -.legend-color { - width: 12px; - height: 12px; - border-radius: 2px; - margin-right: 5px; -} - -.legend-color.hot { background: #e74c3c; } -.legend-color.warm { background: #f39c12; } -.legend-color.cold { background: #3498db; } -.legend-color.frozen { background: #95a5a6; } - -/* 待处理列表 */ -.archiver-pending-list { - max-height: 300px; - overflow-y: auto; - border: 1px solid #ddd; - border-radius: 4px; - padding: 15px; - background: #f9f9f9; - margin: 15px 0; -} - -.archiver-pending-list ul { - margin: 0; - padding: 0; - list-style: none; -} - -.archiver-pending-list li { - padding: 8px 0; - border-bottom: 1px solid #eee; - word-break: break-all; - font-family: monospace; - font-size: 13px; - color: #555; -} - -.archiver-pending-list li:last-child { - border-bottom: none; -} - -/* 元框样式 */ -#archiver_post, -#archiver_terms { - background: #fff; -} - -#archiver-snapshots { - min-height: 60px; - position: relative; -} - -#archiver-snapshots ul { - margin: 0; - padding: 0; - list-style: none; -} - -#archiver-snapshots li { - padding: 8px 0; - border-bottom: 1px solid #eee; - transition: all 0.3s ease; -} - -#archiver-snapshots li:last-child { - border-bottom: none; -} - -#archiver-snapshots li:hover { - background: #f5f5f5; - padding-left: 5px; -} - -#archiver-snapshots a { - text-decoration: none; - color: #2271b1; -} - -#archiver-snapshots a:hover { - text-decoration: underline; - color: #135e96; -} - -/* 加载状态 */ -.archiver-loading { - text-align: center; - padding: 20px; - color: #666; -} - -.archiver-loading .spinner { - float: none; - margin: 0 5px 0 0; -} - -/* 状态消息 */ -#archiver-status { - display: inline-block; - vertical-align: middle; - color: #646970; -} - -#archiver-status.success { - color: #46b450; -} - -#archiver-status.error { - color: #dc3232; -} - -#archiver-status .dashicons { - vertical-align: middle; - margin-right: 3px; -} - -/* 按钮样式 */ -#archiver-immediate-snapshot { - margin-top: 10px; -} - -#archiver-immediate-snapshot.updating-message:before { - content: "\f463"; - display: inline-block; - font-family: dashicons; - animation: archiver-spin 1s infinite linear; - margin-right: 5px; -} - -.archiver-view-all { - font-size: 13px; -} - -/* 工具区域 */ -.archiver-tools-section { - margin-bottom: 30px; - padding-bottom: 30px; - border-bottom: 1px solid #ddd; -} - -.archiver-tools-section:last-child { - border-bottom: none; -} - -.archiver-tools-section h3 { - margin-top: 0; -} - -.archiver-tool-form { - margin-top: 15px; -} - -/* 缓存操作 */ -.archiver-cache-actions { - margin-top: 20px; - padding-top: 20px; - border-top: 1px solid #ddd; -} - -.archiver-cache-actions .button { - margin-right: 5px; -} - -/* 关于页面 */ -.archiver-about-content { - max-width: 800px; -} - -.archiver-about-content ul { - list-style: disc; - padding-left: 20px; -} - -.archiver-about-content li { - margin-bottom: 8px; -} - -/* 响应式设计 */ -@media (max-width: 782px) { - .archiver-stats-container { - flex-direction: column; - } - - .archiver-stat-card { - width: 100%; - min-width: unset; - } - - .cache-legend { - flex-wrap: wrap; - } - - .nav-tab-wrapper { - display: flex; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - - .nav-tab { - white-space: nowrap; - flex-shrink: 0; - } -} - -/* 暗色模式支持 */ -@media (prefers-color-scheme: dark) { - .wp-admin.admin-color-modern .archiver-stat-card, - .wp-admin.admin-color-modern .archiver-tab-content { - background: #1e1e1e; - border-color: #3c434a; - color: #ccc; - } - - .wp-admin.admin-color-modern .archiver-stat-card h3 { - color: #aaa; - } - - .wp-admin.admin-color-modern .stat-value { - color: #fff; - } - - .wp-admin.admin-color-modern .archiver-pending-list { - background: #2c3338; - border-color: #3c434a; - } -} - -/* 动画效果 */ -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.archiver-snapshot-list li { - animation: fadeInUp 0.3s ease forwards; -} - -/* 工具提示 */ -[title] { - position: relative; -} - -.cache-segment:hover::after { - content: attr(title); - position: absolute; - bottom: 100%; - left: 50%; - transform: translateX(-50%); - background: #333; - color: #fff; - padding: 5px 10px; - border-radius: 3px; - font-size: 12px; - white-space: nowrap; - z-index: 1000; - pointer-events: none; -} - -/* 缓存统计区域 */ -.archiver-cache-stats { - background: #f8f9fa; - border: 1px solid #e1e4e8; - border-radius: 6px; - padding: 20px; - margin: 20px 0; -} - -.archiver-cache-stats h2 { - margin-top: 0; - font-size: 18px; - color: #1d2327; -} diff --git a/css/archiver.min.css b/css/archiver.min.css deleted file mode 100644 index c283d9a..0000000 --- a/css/archiver.min.css +++ /dev/null @@ -1 +0,0 @@ -#wp-admin-bar-archiver .ab-item{transition:all .3s ease;color:#72aee6}#wp-admin-bar-archiver.archiver-success .ab-item{color:#46b450!important}#wp-admin-bar-archiver.archiver-failure .ab-item{color:#dc3232!important}#wp-admin-bar-archiver .ab-icon.dashicons.dashicons-backup{top:2px}#wp-admin-bar-archiver-trigger .ab-icon{display:inline-block;float:right!important;margin:0 0 0 6px;opacity:0;transition:opacity .3s ease;vertical-align:middle}#wp-admin-bar-archiver-trigger .ab-icon::before{content:"\f463";font-family:dashicons;font-size:18px;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wp-admin-bar-archiver-trigger.archiver-active .ab-icon{opacity:1;animation:archiver-spin 1s infinite linear}@keyframes archiver-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.nav-tab{border:0 solid #c3c4c7;background:#fff}.nav-tab.nav-tab-active{border-bottom:2px solid #007cba;font-weight:600;background:#f0f0f1}.nav-tab:hover:not(.active){background:#f0f0f1;border-bottom-color:#dcdcde}.card{background:#fff;border:1px solid #ccd0d4;border-radius:4px;max-width:unset;margin-top:20px;padding:20px}.postbox{border:0 solid #c3c4c7;background:#ffffff00}.archiver-stats-container{display:flex;gap:20px;margin:0 0 20px}.archiver-stat-card{flex:1;background:#fff;border:1px solid #ccd0d4;border-radius:4px;padding:15px;text-align:center;box-shadow:0 1px 1px rgba(0,0,0,.04)}.archiver-stat-card h3{margin:0 0 10px;font-size:14px;font-weight:500;color:#646970}.archiver-stat-card .stat-value{font-size:18px;font-weight:600;color:#1d2327}.archiver-pending-list{max-height:300px;overflow-y:auto;border:1px solid #ddd;padding:10px;background:#f9f9f9;margin:0 0 20px}.archiver-pending-list ul{margin:0;padding:0;list-style:none}.archiver-pending-list li{padding:5px 0;border-bottom:1px solid #eee;word-break:break-all}.archiver-pending-list li:last-child{border-bottom:none}@media (max-width:782px){.archiver-stats-container{flex-direction:column}.archiver-stat-card{width:100%}}#archiver_post,#archiver_terms{background:#fff;border-radius:4px}#archiver-snapshots ul{margin:0;padding:0;list-style:none}#archiver-snapshots li{padding:5px 0;border-bottom:1px solid #eee}#archiver-snapshots li:last-child{border-bottom:none}#archiver-snapshots a{text-decoration:none;color:#2271b1}#archiver-snapshots a:hover{text-decoration:underline}#archiver-status{vertical-align:middle;color:#646970}#archiver-status .dashicons{vertical-align:middle;margin-right:3px}#archiver-immediate-snapshot{margin-top:10px} \ No newline at end of file diff --git a/includes/class-archiver-admin.php b/includes/class-archiver-admin.php index 5a51346..5a83d67 100644 --- a/includes/class-archiver-admin.php +++ b/includes/class-archiver-admin.php @@ -1,6 +1,6 @@ archiver = $archiver; - // 延迟初始化缓存对象 - add_action('init', array($this, 'init_cache')); - + // Initialize hooks add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_init', array($this, 'handle_settings_actions')); - add_action('admin_notices', array($this, 'admin_notices')); + add_action('admin_notices', array($this, 'check_system_requirements')); // AJAX handlers add_action('wp_ajax_archiver_get_service_stats', array($this, 'ajax_get_service_stats')); add_action('wp_ajax_archiver_test_service', array($this, 'ajax_test_service')); + + // Initialize cache after WordPress is fully loaded + add_action('wp_loaded', array($this, 'init_cache')); } public function init_cache() { @@ -43,11 +44,40 @@ class Archiver_Admin { ); } + public function check_system_requirements() { + if (!isset($_GET['page']) || $_GET['page'] !== 'archiver-settings') { + return; + } + + // Check if cache is enabled + $cache_enabled = get_option('archiver_cache_enabled', true); + if (!$cache_enabled) { + echo '

'; + echo '' . __('Performance Tip:', 'archiver') . ' '; + echo __('Enable caching to improve performance and reduce API calls.', 'archiver'); + echo '

'; + } + + // Check cache table + global $wpdb; + $table_name = $wpdb->prefix . 'archiver_cache'; + $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") === $table_name; + + if (!$table_exists) { + echo '

'; + echo __('Cache table is missing. Please deactivate and reactivate the plugin to fix this issue.', 'archiver'); + echo '

'; + } + } + public function render_admin_page() { if (!current_user_can('manage_options')) { wp_die(__('You do not have sufficient permissions to access this page.', 'archiver')); } + // Get services - handle case where constant might not be defined + $archiver_services = $this->get_available_services(); + $post_types = get_post_types(array('public' => true), 'objects'); $selected_post_types = get_option('archiver_post_types', array('post', 'page')); $cache_enabled = get_option('archiver_cache_enabled', true); @@ -55,14 +85,19 @@ class Archiver_Admin { $primary_service = get_option('archiver_primary_service', 'wenpai'); $auto_archive = get_option('archiver_auto_archive', true); $archive_on_publish = get_option('archiver_archive_on_publish', true); + $max_queue_size = get_option('archiver_max_queue_size', 500); + $batch_size = get_option('archiver_batch_size', 10); + $update_frequency = get_option('archiver_update_frequency', 'hourly'); ?>

- +

+ +

@@ -70,7 +105,7 @@ class Archiver_Admin {

- +
@@ -84,7 +119,7 @@ class Archiver_Admin { - $service) : ?> + $service) : ?> @@ -125,7 +160,7 @@ class Archiver_Admin {

-

@@ -138,200 +173,291 @@ class Archiver_Admin {

- - +
+ + + + + +
-
- - - - -
+ +
+

+

+ display_queue_management(); ?> -
+ +

+ +

- -
-

-

- display_queue_management(); ?> - - -

- -

- -
-
-

- - - - - -
- -
-

-
- - - - -
-
+
+
+

+
+ + + +
- - +
-
- -
@@ -115,7 +150,7 @@ class Archiver_Admin {
+ + + + + + + + + + + + +
+ - - - + + + +
+ + + + +
+ +

+ +

+
- - - - - - - - - - - - - - + +
- - - - -
- - - - -
- -

- + +

+ +
- - - - - - - - - - - -
- - - - -
- - -

- -

-
-
+ + +
+ + + + + + + + + + +
+ + + + +
+ + +

+ +

+
-

- -

-
+

+ +

+ +
@@ -355,6 +481,12 @@ class Archiver_Admin { padding: 20px; } + .archiver-version { + font-size: 13px; + padding-left: 10px; + color: #666; + } + /* Tabs Style */ .archiver-settings-tabs { display: flex; @@ -610,13 +742,69 @@ class Archiver_Admin { array( + 'name' => 'WenPai Archive', + 'save_url' => 'https://web.wenpai.net/save/', + 'fetch_url' => 'https://web.wenpai.net/cdx/', + 'view_url' => 'https://web.wenpai.net/web/', + 'enabled' => true + ), + 'wayback' => array( + 'name' => 'Internet Archive', + 'save_url' => 'https://web.archive.org/save/', + 'fetch_url' => 'https://web.archive.org/cdx/search/cdx', + 'view_url' => 'https://web.archive.org/web/', + 'enabled' => true + ), + 'archive_today' => array( + 'name' => 'Archive.today', + 'save_url' => 'https://archive.today/?run=1&url=', + 'fetch_url' => 'https://archive.today/', + 'view_url' => 'https://archive.today/', + 'enabled' => false + ) + ); + } + private function render_statistics_table() { $pending_count = count(get_option('archiver_urls_to_update', array())); $queue_count = count(get_option('archiver_background_queue', array())); $total_archived = get_option('archiver_total_archived', 0); $failed_snapshots = get_option('archiver_failed_snapshots', 0); - $last_run = get_option('archiver_last_run', __('Never', 'archiver')); - $next_run = wp_next_scheduled('archiver_process_urls'); + + // Get pre-calculated display times + $last_run_display = get_option('archiver_last_run', ''); + $next_run_display = get_option('archiver_next_run_display', ''); + + // Use pre-calculated display times if available + if (empty($last_run_display)) { + $last_run_timestamp = get_option('archiver_last_run_timestamp', 0); + if ($last_run_timestamp && $this->archiver && method_exists($this->archiver, 'get_formatted_schedule_time')) { + $last_run_display = $this->archiver->get_formatted_schedule_time($last_run_timestamp); + } else { + $last_run_display = __('Never', 'archiver'); + } + } + + if (empty($next_run_display)) { + $next_run = wp_next_scheduled('archiver_process_urls'); + if ($next_run && $this->archiver && method_exists($this->archiver, 'get_formatted_schedule_time')) { + $next_run_display = $this->archiver->get_formatted_schedule_time($next_run); + } else { + $next_run_display = __('Not scheduled', 'archiver'); + } + } + ?> @@ -640,27 +828,58 @@ class Archiver_Admin { - + - +
setTimezone($timezone); + return $datetime->format(get_option('date_format') . ' ' . get_option('time_format')); + } else if ($gmt_offset) { + // Use GMT offset + $timestamp += $gmt_offset * HOUR_IN_SECONDS; + return date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $timestamp); + } + } catch (Exception $e) { + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Date formatting error: ' . $e->getMessage()); + } + } + + // Default fallback to WordPress localized date function + return date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $timestamp); + } + private function render_cache_statistics_inline() { - // 确保缓存对象已初始化 + // Initialize cache if not already done if (!$this->cache) { $this->init_cache(); } - if (!$this->cache) { + if (!$this->cache || !method_exists($this->cache, 'get_cache_stats')) { return; } @@ -669,10 +888,10 @@ class Archiver_Admin { return; } - // 安全地获取统计数据 + // Safely get statistics data $total_entries = isset($stats->total_entries) ? intval($stats->total_entries) : 0; $total_api_saves = isset($stats->total_api_saves) ? intval($stats->total_api_saves) : 0; - $cache_size = $this->cache->get_cache_size(); + $cache_size = method_exists($this->cache, 'get_cache_size') ? $this->cache->get_cache_size() : 0; ?>

@@ -714,16 +933,16 @@ class Archiver_Admin {

- -
- + - @@ -732,8 +951,10 @@ class Archiver_Admin {
    - -
  • + +
@@ -742,8 +963,10 @@ class Archiver_Admin {
    - -
  • + +
  • 50) : ?>
  • ...
  • @@ -756,32 +979,56 @@ class Archiver_Admin { save_settings(); + $this->save_content_types(); + } + + // Handle general settings - automation + if (isset($_POST['archiver_automation_submit']) && $_POST['archiver_automation_submit'] == '1') { + if (!check_admin_referer('archiver_settings_action', 'archiver_settings_nonce')) { + add_settings_error('archiver_messages', 'archiver_nonce_error', __('Security check failed.', 'archiver'), 'error'); + return; + } + $this->save_automation_settings(); + } + + // Handle general settings - performance + if (isset($_POST['archiver_performance_submit']) && $_POST['archiver_performance_submit'] == '1') { + if (!check_admin_referer('archiver_settings_action', 'archiver_settings_nonce')) { + add_settings_error('archiver_messages', 'archiver_nonce_error', __('Security check failed.', 'archiver'), 'error'); + return; + } + $this->save_performance_settings(); + } + + // Handle general settings - cache + if (isset($_POST['archiver_cache_submit']) && $_POST['archiver_cache_submit'] == '1') { + if (!check_admin_referer('archiver_settings_action', 'archiver_settings_nonce')) { + add_settings_error('archiver_messages', 'archiver_nonce_error', __('Security check failed.', 'archiver'), 'error'); + return; + } + $this->save_cache_settings(); } // Handle services settings - if (isset($_POST['archiver_services_submit'])) { - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'archiver_services_nonce')) { + if (isset($_POST['archiver_services_submit']) && $_POST['archiver_services_submit'] == '1') { + if (!check_admin_referer('archiver_services_action', 'archiver_services_nonce')) { add_settings_error('archiver_messages', 'archiver_nonce_error', __('Security check failed.', 'archiver'), 'error'); return; } @@ -789,8 +1036,8 @@ class Archiver_Admin { } // Handle manual update - if (isset($_POST['archiver_trigger_update'])) { - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'archiver_manual_update_nonce')) { + if (isset($_POST['archiver_trigger_update']) && $_POST['archiver_trigger_update'] == '1') { + if (!check_admin_referer('archiver_manual_update_action', 'archiver_manual_update_nonce')) { add_settings_error('archiver_messages', 'archiver_nonce_error', __('Security check failed.', 'archiver'), 'error'); return; } @@ -798,8 +1045,8 @@ class Archiver_Admin { } // Handle bulk actions - if (isset($_POST['archiver_submit_bulk_action'])) { - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'archiver_bulk_actions_nonce')) { + if (isset($_POST['archiver_submit_bulk_action']) && $_POST['archiver_submit_bulk_action'] == '1') { + if (!check_admin_referer('archiver_bulk_actions_action', 'archiver_bulk_actions_nonce')) { add_settings_error('archiver_messages', 'archiver_nonce_error', __('Security check failed.', 'archiver'), 'error'); return; } @@ -807,8 +1054,9 @@ class Archiver_Admin { } // Handle cache actions - if (isset($_POST['archiver_preheat_cache']) || isset($_POST['archiver_clear_cache'])) { - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'archiver_settings_nonce')) { + if ((isset($_POST['archiver_preheat_cache']) && $_POST['archiver_preheat_cache'] == '1') || + (isset($_POST['archiver_clear_cache']) && $_POST['archiver_clear_cache'] == '1')) { + if (!check_admin_referer('archiver_settings_action', 'archiver_settings_nonce')) { add_settings_error('archiver_messages', 'archiver_nonce_error', __('Security check failed.', 'archiver'), 'error'); return; } @@ -816,8 +1064,8 @@ class Archiver_Admin { } // Handle manual archive - if (isset($_POST['archiver_manual_archive_submit'])) { - if (!isset($_POST['_wpnonce']) || !wp_verify_nonce($_POST['_wpnonce'], 'archiver_manual_archive_nonce')) { + if (isset($_POST['archiver_manual_archive_submit']) && $_POST['archiver_manual_archive_submit'] == '1') { + if (!check_admin_referer('archiver_manual_archive_action', 'archiver_manual_archive_nonce')) { add_settings_error('archiver_messages', 'archiver_nonce_error', __('Security check failed.', 'archiver'), 'error'); return; } @@ -825,8 +1073,9 @@ class Archiver_Admin { } } - private function save_settings() { + private function save_content_types() { if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); return; } @@ -838,33 +1087,81 @@ class Archiver_Admin { } update_option('archiver_post_types', $post_types); + add_settings_error('archiver_messages', 'archiver_message', __('Content types saved successfully.', 'archiver'), 'success'); + } + + private function save_automation_settings() { + if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); + return; + } + // Save archive options update_option('archiver_auto_archive', isset($_POST['archiver_auto_archive']) ? 1 : 0); update_option('archiver_archive_on_publish', isset($_POST['archiver_archive_on_publish']) ? 1 : 0); - // Save cache settings - update_option('archiver_cache_enabled', isset($_POST['archiver_cache_enabled']) ? 1 : 0); - // Save update frequency - $frequency = isset($_POST['archiver_update_frequency']) ? sanitize_text_field($_POST['archiver_update_frequency']) : 'daily'; - $valid_frequencies = ['hourly', 'twicedaily', 'daily', 'weekly']; + $frequency = isset($_POST['archiver_update_frequency']) ? sanitize_text_field($_POST['archiver_update_frequency']) : 'hourly'; + $valid_frequencies = ['minutes_15', 'minutes_30', 'hourly', 'twicedaily', 'daily', 'weekly']; if (!in_array($frequency, $valid_frequencies)) { - $frequency = 'daily'; + $frequency = 'hourly'; } update_option('archiver_update_frequency', $frequency); // Reschedule cron task - if (method_exists($this->archiver, 'reschedule_cron_task')) { + if ($this->archiver && method_exists($this->archiver, 'reschedule_cron_task')) { $this->archiver->reschedule_cron_task($frequency); } + add_settings_error('archiver_messages', 'archiver_message', __('Automation settings saved successfully.', 'archiver'), 'success'); + } + + private function save_performance_settings() { + if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); + return; + } + + // Save performance settings + $max_queue_size = isset($_POST['archiver_max_queue_size']) ? intval($_POST['archiver_max_queue_size']) : 500; + $batch_size = isset($_POST['archiver_batch_size']) ? intval($_POST['archiver_batch_size']) : 10; + + $max_queue_size = max(50, min(2000, $max_queue_size)); // Limit between 50-2000 + $batch_size = max(1, min(50, $batch_size)); // Limit between 1-50 + + update_option('archiver_max_queue_size', $max_queue_size); + update_option('archiver_batch_size', $batch_size); + + add_settings_error('archiver_messages', 'archiver_message', __('Performance settings saved successfully.', 'archiver'), 'success'); + } + + private function save_cache_settings() { + if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); + return; + } + + // Save cache settings + update_option('archiver_cache_enabled', isset($_POST['archiver_cache_enabled']) ? 1 : 0); + + add_settings_error('archiver_messages', 'archiver_message', __('Cache settings saved successfully.', 'archiver'), 'success'); + } + + private function save_settings() { + if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); + return; + } + + // This method is kept for compatibility, but actual settings are handled in separate methods add_settings_error('archiver_messages', 'archiver_message', __('Settings saved successfully.', 'archiver'), 'success'); } private function save_services_settings() { if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); return; } @@ -891,10 +1188,11 @@ class Archiver_Admin { private function trigger_manual_update() { if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); return; } - if (method_exists($this->archiver, 'process_urls_for_update')) { + if ($this->archiver && method_exists($this->archiver, 'process_urls_for_update')) { $processed = $this->archiver->process_urls_for_update(); add_settings_error( 'archiver_messages', @@ -902,11 +1200,14 @@ class Archiver_Admin { sprintf(__('Processing started for %d URLs.', 'archiver'), $processed), 'success' ); + } else { + add_settings_error('archiver_messages', 'archiver_message', __('Manual update function not available.', 'archiver'), 'error'); } } private function handle_bulk_actions() { if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); return; } @@ -958,7 +1259,7 @@ class Archiver_Admin { $urls = array_unique(array_merge($existing, $urls)); // Apply queue limit - $max_queue = apply_filters('archiver_queue_limit', 100); + $max_queue = get_option('archiver_max_queue_size', 500); $urls = array_slice($urls, 0, $max_queue); update_option('archiver_urls_to_update', $urls); @@ -996,7 +1297,7 @@ class Archiver_Admin { $urls = array_unique(array_merge($existing, $urls)); // Apply queue limit - $max_queue = apply_filters('archiver_queue_limit', 100); + $max_queue = get_option('archiver_max_queue_size', 500); $urls = array_slice($urls, 0, $max_queue); update_option('archiver_urls_to_update', $urls); @@ -1044,14 +1345,16 @@ class Archiver_Admin { private function handle_cache_actions() { if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); return; } + // Initialize cache if needed if (!$this->cache) { $this->init_cache(); } - if (isset($_POST['archiver_preheat_cache']) && $this->cache) { + if (isset($_POST['archiver_preheat_cache']) && $_POST['archiver_preheat_cache'] == '1' && $this->cache) { $count = $this->cache->preheat_cache(); add_settings_error( 'archiver_messages', @@ -1061,13 +1364,14 @@ class Archiver_Admin { ); } - if (isset($_POST['archiver_clear_cache'])) { + if (isset($_POST['archiver_clear_cache']) && $_POST['archiver_clear_cache'] == '1') { $this->clear_all_cache(); } } private function handle_manual_archive() { if (!current_user_can('manage_options')) { + add_settings_error('archiver_messages', 'archiver_message', __('Permission denied.', 'archiver'), 'error'); return; } @@ -1079,8 +1383,13 @@ class Archiver_Admin { } // Add to queue with priority - if ($this->cache) { + if ($this->cache && method_exists($this->cache, 'queue_for_update')) { $this->cache->queue_for_update($url, null, true); + } else { + // Fallback: add to main queue + $existing = get_option('archiver_urls_to_update', array()); + $existing[] = $url; + update_option('archiver_urls_to_update', array_unique($existing)); } add_settings_error( @@ -1094,45 +1403,21 @@ class Archiver_Admin { private function clear_all_cache() { global $wpdb; - // Clear cache table - $table_name = $wpdb->prefix . 'archiver_cache'; - $wpdb->query("TRUNCATE TABLE IF EXISTS {$table_name}"); + try { + // Clear cache table + $table_name = $wpdb->prefix . 'archiver_cache'; + $wpdb->query("TRUNCATE TABLE IF EXISTS {$table_name}"); - // Clear object cache - wp_cache_flush(); + // Clear object cache + wp_cache_flush(); - // Clear transients - $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_archiver_%'"); - $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_archiver_%'"); + // Clear transients + $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_archiver_%'"); + $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_archiver_%'"); - add_settings_error('archiver_messages', 'archiver_message', __('All cache cleared successfully.', 'archiver'), 'success'); - } - - public function admin_notices() { - if (!isset($_GET['page']) || $_GET['page'] !== 'archiver-settings') { - return; - } - - settings_errors('archiver_messages'); - - // Check if cache is enabled - $cache_enabled = get_option('archiver_cache_enabled', true); - if (!$cache_enabled) { - echo '

    '; - echo '' . __('Performance Tip:', 'archiver') . ' '; - echo __('Enable caching to improve performance and reduce API calls.', 'archiver'); - echo '

    '; - } - - // Check cache table - global $wpdb; - $table_name = $wpdb->prefix . 'archiver_cache'; - $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") === $table_name; - - if (!$table_exists) { - echo '

    '; - echo __('Cache table is missing. Please deactivate and reactivate the plugin to fix this issue.', 'archiver'); - echo '

    '; + add_settings_error('archiver_messages', 'archiver_message', __('All cache cleared successfully.', 'archiver'), 'success'); + } catch (Exception $e) { + add_settings_error('archiver_messages', 'archiver_message', __('Error clearing cache: ', 'archiver') . $e->getMessage(), 'error'); } } @@ -1140,7 +1425,7 @@ class Archiver_Admin { public function ajax_get_service_stats() { check_ajax_referer('archiver_admin_nonce', 'nonce'); - $services = ARCHIVER_SERVICES; + $services = $this->get_available_services(); $stats = array(); foreach ($services as $service_id => $service) { @@ -1158,14 +1443,15 @@ class Archiver_Admin { check_ajax_referer('archiver_admin_nonce', 'nonce'); $service_id = isset($_POST['service']) ? sanitize_text_field($_POST['service']) : ''; + $services = $this->get_available_services(); - if (!isset(ARCHIVER_SERVICES[$service_id])) { + if (!isset($services[$service_id])) { wp_send_json_error(__('Invalid service', 'archiver')); } // Test with a sample URL $test_url = home_url(); - $service = ARCHIVER_SERVICES[$service_id]; + $service = $services[$service_id]; // Simulate test $success = true; @@ -1176,4 +1462,4 @@ class Archiver_Admin { 'message' => $message )); } -} +} \ No newline at end of file diff --git a/includes/class-archiver-cache.php b/includes/class-archiver-cache.php index 3286ce3..3ff058d 100644 --- a/includes/class-archiver-cache.php +++ b/includes/class-archiver-cache.php @@ -1,6 +1,6 @@ MONTH_IN_SECONDS ]; - // 添加服务特定的缓存前缀 + // Service-specific cache prefix private $cache_prefix = 'archiver_'; - // 批处理优化 - private $batch_size = 3; + // Batch processing optimization + private $batch_size = 10; private $max_retries = 3; public function __construct() { global $wpdb; $this->table_name = $wpdb->prefix . 'archiver_cache'; - $this->batch_size = apply_filters('archiver_batch_size', 3); + $this->batch_size = get_option('archiver_batch_size', 10); } /** - * 获取快照数据(支持多服务) + * Get available services with fallback + */ + private function get_available_services() { + if (defined('ARCHIVER_SERVICES') && is_array(ARCHIVER_SERVICES)) { + return ARCHIVER_SERVICES; + } + + // Fallback services if constant is not defined + return array( + 'wenpai' => array( + 'name' => 'WenPai Archive', + 'save_url' => 'https://web.wenpai.net/save/', + 'fetch_url' => 'https://web.wenpai.net/cdx/', + 'view_url' => 'https://web.wenpai.net/web/', + 'enabled' => true + ), + 'wayback' => array( + 'name' => 'Internet Archive', + 'save_url' => 'https://web.archive.org/save/', + 'fetch_url' => 'https://web.archive.org/cdx/search/cdx', + 'view_url' => 'https://web.archive.org/web/', + 'enabled' => true + ), + 'archive_today' => array( + 'name' => 'Archive.today', + 'save_url' => 'https://archive.today/?run=1&url=', + 'fetch_url' => 'https://archive.today/', + 'view_url' => 'https://archive.today/', + 'enabled' => false + ) + ); + } + + /** + * Get snapshot data (supports multiple services) */ public function get_snapshots($url, $service = null, $force_refresh = false) { if (!get_option('archiver_cache_enabled', true)) { return $this->fetch_from_archive_service($url, $service); } - // 如果没有指定服务,使用主服务 + // If no service specified, use primary service if (!$service) { $service = get_option('archiver_primary_service', 'wenpai'); } $url_hash = md5($url . '_' . $service); - // 1. 检查内存缓存 + // 1. Check memory cache if (!$force_refresh) { $memory_key = $this->cache_prefix . 'snap_' . $url_hash; $cached_data = wp_cache_get($memory_key, 'archiver'); @@ -56,24 +90,25 @@ class Archiver_Cache { } } - // 2. 检查数据库缓存 + // 2. Check database cache if (!$force_refresh) { $db_cache = $this->get_from_database($url_hash, $service); if ($db_cache !== false) { + $memory_key = $this->cache_prefix . 'snap_' . $url_hash; wp_cache_set($memory_key, $db_cache, 'archiver', HOUR_IN_SECONDS); return $db_cache; } } - // 3. 添加到后台队列(带服务信息) + // 3. Add to background queue (with service info) $this->queue_for_update($url, $service, false); - // 4. 返回过期数据或空数组 + // 4. Return stale data or empty array return $this->get_stale_data($url_hash, $service) ?: []; } /** - * 从数据库获取缓存 + * Get cache from database */ private function get_from_database($url_hash, $service = 'wenpai') { global $wpdb; @@ -91,7 +126,7 @@ class Archiver_Cache { $this->update_access_stats($url_hash, $service); $data = maybe_unserialize($row->snapshot_data); - // 根据访问频率提升缓存级别 + // Promote cache level based on access frequency if ($row->api_calls_saved > 10 && $row->cache_type !== 'hot') { $this->promote_cache($url_hash, $service, 'hot'); } @@ -100,7 +135,7 @@ class Archiver_Cache { } /** - * 获取过期数据 + * Get stale data */ private function get_stale_data($url_hash, $service = 'wenpai') { global $wpdb; @@ -117,7 +152,7 @@ class Archiver_Cache { } /** - * 保存快照数据 + * Save snapshot data */ public function save_snapshots($url, $snapshots, $service = 'wenpai') { global $wpdb; @@ -140,23 +175,25 @@ class Archiver_Cache { 'status' => 'active' ]; - $wpdb->replace($this->table_name, $data); + $result = $wpdb->replace($this->table_name, $data); - // 更新内存缓存 - $memory_key = $this->cache_prefix . 'snap_' . $url_hash; - wp_cache_set($memory_key, $snapshots, 'archiver', HOUR_IN_SECONDS); + if ($result !== false) { + // Update memory cache + $memory_key = $this->cache_prefix . 'snap_' . $url_hash; + wp_cache_set($memory_key, $snapshots, 'archiver', HOUR_IN_SECONDS); - // 更新统计 - $this->increment_archived_count(); + // Update statistics + $this->increment_archived_count(); + } - return true; + return $result !== false; } /** - * 确定缓存类型 + * Determine cache type */ private function determine_cache_type($url) { - // 首页:热数据 + // Homepage: hot data if ($url === home_url() || $url === home_url('/')) { return 'hot'; } @@ -177,12 +214,12 @@ class Archiver_Cache { } /** - * 添加到更新队列(支持多服务) + * Add to update queue (supports multiple services) */ public function queue_for_update($url, $service = null, $priority = false) { $queue = get_option('archiver_background_queue', []); - // 如果没有指定服务,使用所有启用的服务 + // If no service specified, use all enabled services if (!$service) { $enabled_services = get_option('archiver_services', array('wenpai' => true)); foreach ($enabled_services as $service_id => $enabled) { @@ -194,25 +231,25 @@ class Archiver_Cache { $this->add_to_queue($queue, $url, $service, $priority); } - // 限制队列大小 - $max_queue_size = apply_filters('archiver_queue_limit', 100); + // Limit queue size + $max_queue_size = get_option('archiver_max_queue_size', 500); $queue = array_slice($queue, 0, $max_queue_size); update_option('archiver_background_queue', $queue); - // 触发后台处理 + // Trigger background processing if (!wp_next_scheduled('archiver_process_background_queue')) { wp_schedule_single_event(time() + 10, 'archiver_process_background_queue'); } } /** - * 添加单个项目到队列 + * Add single item to queue */ private function add_to_queue(&$queue, $url, $service, $priority) { $item = array('url' => $url, 'service' => $service, 'retries' => 0); - // 检查是否已存在 + // Check if already exists foreach ($queue as $existing) { if (is_array($existing) && $existing['url'] === $url && $existing['service'] === $service) { return; @@ -227,7 +264,7 @@ class Archiver_Cache { } /** - * 处理后台队列 + * Process background queue */ public function process_background_queue() { $queue = get_option('archiver_background_queue', []); @@ -236,13 +273,13 @@ class Archiver_Cache { return; } - // 每次处理批量URL + // Process batch URLs each time $batch = array_splice($queue, 0, $this->batch_size); $failed_items = array(); foreach ($batch as $item) { if (!is_array($item)) { - // 兼容旧格式 + // Compatible with old format $item = array('url' => $item, 'service' => 'wenpai', 'retries' => 0); } @@ -258,20 +295,20 @@ class Archiver_Cache { } } - // 将失败的项目重新加入队列末尾 + // Re-add failed items to end of queue $queue = array_merge($queue, $failed_items); - // 更新队列 + // Update queue update_option('archiver_background_queue', $queue); - // 如果还有待处理项,继续安排 + // Continue scheduling if more items pending if (!empty($queue)) { wp_schedule_single_event(time() + 30, 'archiver_process_background_queue'); } } /** - * 从存档服务获取并缓存数据 + * Fetch and cache snapshots from archive service */ public function fetch_and_cache_snapshots($url, $service = 'wenpai') { $snapshots = $this->fetch_from_archive_service($url, $service); @@ -285,14 +322,14 @@ class Archiver_Cache { } /** - * 从存档服务获取数据 + * Fetch data from archive service */ private function fetch_from_archive_service($url, $service = null) { if (!$service) { $service = get_option('archiver_primary_service', 'wenpai'); } - $services = ARCHIVER_SERVICES; + $services = $this->get_available_services(); if (!isset($services[$service])) { return false; } @@ -313,114 +350,187 @@ class Archiver_Cache { } /** - * 从 Wayback Machine 获取数据 + * Fetch data from Wayback Machine */ private function fetch_from_wayback($url) { + $services = $this->get_available_services(); + $api_url = add_query_arg([ 'url' => $url, 'output' => 'json', - 'limit' => 10 - ], ARCHIVER_SERVICES['wayback']['fetch_url']); + 'limit' => 20, + 'fl' => 'timestamp,original,statuscode,mimetype,length' + ], $services['wayback']['fetch_url']); $response = wp_remote_get($api_url, [ 'timeout' => 30, 'sslverify' => true, 'headers' => [ - 'User-Agent' => 'WP-Archiver/' . ARCHIVER_VERSION + 'User-Agent' => 'WP-Archiver/' . ARCHIVER_VERSION . ' (WordPress/' . get_bloginfo('version') . ')' ] ]); if (is_wp_error($response)) { + if (function_exists('archiver_handle_error')) { + archiver_handle_error('Wayback API error: ' . $response->get_error_message()); + } return false; } - if (wp_remote_retrieve_response_code($response) !== 200) { + $response_code = wp_remote_retrieve_response_code($response); + if ($response_code !== 200) { + if (function_exists('archiver_handle_error')) { + archiver_handle_error('Wayback API returned status: ' . $response_code); + } return false; } $body = wp_remote_retrieve_body($response); + if (empty($body)) { + return false; + } + $data = json_decode($body, true); + if (json_last_error() !== JSON_ERROR_NONE) { + if (function_exists('archiver_handle_error')) { + archiver_handle_error('Wayback API JSON decode error: ' . json_last_error_msg()); + } + return false; + } if (empty($data) || !is_array($data)) { - return false; + return array(); // Return empty array instead of false } return $this->process_wayback_response($data); } /** - * 从 WenPai Archive 获取数据 + * Fetch data from WenPai Archive */ private function fetch_from_wenpai($url) { + $services = $this->get_available_services(); + $api_url = add_query_arg([ 'url' => $url, 'output' => 'json', - ], ARCHIVER_SERVICES['wenpai']['fetch_url']); + 'limit' => 20, + 'fl' => 'timestamp,original,statuscode' + ], $services['wenpai']['fetch_url']); $response = wp_remote_get($api_url, [ 'timeout' => 30, - 'sslverify' => false, // WenPai 可能使用自签名证书 + 'sslverify' => false, // WenPai might use self-signed certificate 'headers' => [ - 'User-Agent' => 'WP-Archiver/' . ARCHIVER_VERSION + 'User-Agent' => 'WP-Archiver/' . ARCHIVER_VERSION . ' (WordPress/' . get_bloginfo('version') . ')' ] ]); if (is_wp_error($response)) { + if (function_exists('archiver_handle_error')) { + archiver_handle_error('WenPai API error: ' . $response->get_error_message()); + } return false; } - if (wp_remote_retrieve_response_code($response) !== 200) { + $response_code = wp_remote_retrieve_response_code($response); + if ($response_code !== 200) { + if (function_exists('archiver_handle_error')) { + archiver_handle_error('WenPai API returned status: ' . $response_code); + } return false; } $body = wp_remote_retrieve_body($response); - $data = json_decode($body, true); - - if (empty($data) || !is_array($data)) { - return false; + if (empty($body)) { + return array(); // Return empty array instead of false } - return $this->process_wayback_response($data); // 使用相同的处理方法 + $data = json_decode($body, true); + if (json_last_error() !== JSON_ERROR_NONE) { + // Try to parse as plain text (CDX format) + return $this->parse_cdx_response($body); + } + + if (empty($data) || !is_array($data)) { + return array(); + } + + return $this->process_wayback_response($data); // Use same processing method } /** - * 处理 Wayback Machine 响应 + * Parse CDX format response + */ + private function parse_cdx_response($body) { + $lines = explode("\n", trim($body)); + if (empty($lines)) { + return array(); + } + + $snapshots = array(); + foreach ($lines as $line) { + $line = trim($line); + if (empty($line)) continue; + + $parts = preg_split('/\s+/', $line); + if (count($parts) >= 3) { + $snapshots[] = array( + 'timestamp' => $parts[1], + 'original' => $parts[2], + 'statuscode' => isset($parts[4]) ? $parts[4] : '200' + ); + } + } + + return array_reverse(array_slice($snapshots, -20)); // Return latest 20 + } + + /** + * Process Wayback Machine response */ private function process_wayback_response($data) { if (count($data) < 2) { - return []; + return array(); } $headers = array_shift($data); - $snapshots = []; + $snapshots = array(); - // 只取最新的10条 - $data = array_slice($data, -10); + // Only take latest 20 entries + $data = array_slice($data, -20); foreach ($data as $row) { - $snapshot = []; + $snapshot = array(); foreach ($row as $i => $value) { if (isset($headers[$i])) { $snapshot[$headers[$i]] = $value; } } - $snapshots[] = $snapshot; + + // Filter out failed snapshots + if (isset($snapshot['statuscode']) && $snapshot['statuscode'] === '200') { + $snapshots[] = $snapshot; + } elseif (!isset($snapshot['statuscode'])) { + // Include if no status code (assume success) + $snapshots[] = $snapshot; + } } return array_reverse($snapshots); } /** - * 从 Archive.today 获取数据 + * Fetch data from Archive.today */ private function fetch_from_archive_today($url) { - // Archive.today 没有官方 API,返回空数组 - // 但仍可以触发保存 + // Archive.today doesn't have official API, return empty array + // But can still trigger save return array(); } /** - * 更新访问统计 + * Update access statistics */ private function update_access_stats($url_hash, $service = 'wenpai') { global $wpdb; @@ -435,7 +545,7 @@ class Archiver_Cache { } /** - * 提升缓存级别 + * Promote cache level */ private function promote_cache($url_hash, $service, $new_type) { global $wpdb; @@ -454,12 +564,12 @@ class Archiver_Cache { } /** - * 清理过期缓存 + * Clean expired cache */ public function cleanup_expired_cache() { global $wpdb; - // 保留最近30天的数据,即使已过期 + // Keep data from last 30 days, even if expired $cutoff_date = date('Y-m-d H:i:s', time() - (30 * DAY_IN_SECONDS)); $deleted = $wpdb->query($wpdb->prepare( @@ -468,14 +578,14 @@ class Archiver_Cache { current_time('mysql'), $cutoff_date )); - // 清理孤立的内存缓存 + // Clean orphaned memory cache wp_cache_flush_group('archiver'); return $deleted; } /** - * 获取缓存统计 + * Get cache statistics */ public function get_cache_stats() { global $wpdb; @@ -483,7 +593,7 @@ class Archiver_Cache { $stats = $wpdb->get_row( "SELECT COUNT(*) as total_entries, - SUM(api_calls_saved) as total_api_saves, + SUM(CASE WHEN api_calls_saved IS NOT NULL THEN api_calls_saved ELSE 0 END) as total_api_saves, SUM(CASE WHEN cache_type = 'hot' THEN 1 ELSE 0 END) as hot_entries, SUM(CASE WHEN cache_type = 'warm' THEN 1 ELSE 0 END) as warm_entries, SUM(CASE WHEN cache_type = 'cold' THEN 1 ELSE 0 END) as cold_entries, @@ -493,10 +603,10 @@ class Archiver_Cache { WHERE status = 'active'" ); - // 添加服务级别的统计 + // Add service-level statistics if ($stats) { $stats->service_stats = $wpdb->get_results( - "SELECT service, COUNT(*) as count, SUM(api_calls_saved) as saves + "SELECT service, COUNT(*) as count, SUM(CASE WHEN api_calls_saved IS NOT NULL THEN api_calls_saved ELSE 0 END) as saves FROM {$this->table_name} WHERE status = 'active' GROUP BY service" @@ -507,7 +617,7 @@ class Archiver_Cache { } /** - * 预热缓存 + * Preheat cache */ public function preheat_cache($post_types = null) { if (!$post_types) { @@ -531,10 +641,12 @@ class Archiver_Cache { foreach ($posts as $post_id) { $url = get_permalink($post_id); - foreach ($enabled_services as $service_id => $enabled) { - if ($enabled) { - $this->queue_for_update($url, $service_id); - $count++; + if ($url) { + foreach ($enabled_services as $service_id => $enabled) { + if ($enabled) { + $this->queue_for_update($url, $service_id); + $count++; + } } } } @@ -543,7 +655,7 @@ class Archiver_Cache { } /** - * 获取缓存大小 + * Get cache size */ public function get_cache_size() { global $wpdb; @@ -559,20 +671,20 @@ class Archiver_Cache { } /** - * 优化缓存表 + * Optimize cache table */ public function optimize_cache_table() { global $wpdb; - // 优化表 + // Optimize table $wpdb->query("OPTIMIZE TABLE {$this->table_name}"); - // 更新统计信息 + // Update statistics $wpdb->query("ANALYZE TABLE {$this->table_name}"); } /** - * 增加存档计数 + * Increment archived count */ private function increment_archived_count() { $count = get_option('archiver_total_archived', 0); @@ -580,7 +692,7 @@ class Archiver_Cache { } /** - * 增加失败计数 + * Increment failed count */ private function increment_failed_count() { $count = get_option('archiver_failed_snapshots', 0); @@ -588,7 +700,7 @@ class Archiver_Cache { } /** - * 获取服务健康状态 + * Get service health status */ public function get_service_health($service = null) { global $wpdb; @@ -599,7 +711,7 @@ class Archiver_Cache { "SELECT COUNT(*) as total_attempts, SUM(CASE WHEN snapshot_count > 0 THEN 1 ELSE 0 END) as successful, - AVG(api_calls_saved) as avg_saves + AVG(CASE WHEN api_calls_saved IS NOT NULL THEN api_calls_saved ELSE 0 END) as avg_saves FROM {$this->table_name} WHERE created_at > DATE_SUB(NOW(), INTERVAL 7 DAY)" . $where ); @@ -612,17 +724,19 @@ class Archiver_Cache { } } -// 注册清理任务 +// Register cleanup task add_action('archiver_cleanup_cache', function() { - $cache = new Archiver_Cache(); - $cleaned = $cache->cleanup_expired_cache(); + if (class_exists('Archiver_Cache')) { + $cache = new Archiver_Cache(); + $cleaned = $cache->cleanup_expired_cache(); - if ($cleaned > 0) { - error_log('[WP Archiver] Cleaned ' . $cleaned . ' expired cache entries'); - } + if ($cleaned > 0 && defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Cleaned ' . $cleaned . ' expired cache entries'); + } - // 每周优化一次表 - if (date('w') == 0) { // 周日 - $cache->optimize_cache_table(); + // Optimize table once a week + if (date('w') == 0) { // Sunday + $cache->optimize_cache_table(); + } } }); diff --git a/includes/class-archiver-dashboard.php b/includes/class-archiver-dashboard.php new file mode 100644 index 0000000..b2337af --- /dev/null +++ b/includes/class-archiver-dashboard.php @@ -0,0 +1,508 @@ +archiver = $archiver; + + add_action('wp_dashboard_setup', array($this, 'add_dashboard_widget')); + add_action('wp_ajax_archiver_dashboard_trigger', array($this, 'ajax_dashboard_trigger')); + add_action('wp_ajax_archiver_dashboard_stats', array($this, 'ajax_dashboard_stats')); + } + + /** + * Add dashboard widget + */ + public function add_dashboard_widget() { + if (!current_user_can('manage_options')) { + return; + } + + // Remove the second callback function to cancel configuration options + wp_add_dashboard_widget( + 'archiver_dashboard_widget', + __('Archive Status', 'archiver'), + array($this, 'render_dashboard_widget') + // Remove configuration callback + // array($this, 'render_dashboard_widget_config') + ); + } + + /** + * Render dashboard widget + */ +public function render_dashboard_widget() { + $stats = $this->get_dashboard_stats(); + $pending_count = count(get_option('archiver_urls_to_update', array())) + + count(get_option('archiver_background_queue', array())); + + // Get pre-calculated display times or timestamps + $last_run_display = get_option('archiver_last_run', ''); + $last_run_timestamp = get_option('archiver_last_run_timestamp', 0); + $next_run = wp_next_scheduled('archiver_process_urls'); + + // Format last run time + if (!empty($last_run_display)) { + $last_run_formatted = $last_run_display; + } elseif ($last_run_timestamp > 0) { + $last_run_formatted = $this->format_timestamp_with_timezone($last_run_timestamp); + } else { + // If we have a string that might be a date + $last_run = get_option('archiver_last_run'); + if (!empty($last_run) && ($timestamp = strtotime($last_run)) !== false) { + $last_run_formatted = $this->format_timestamp_with_timezone($timestamp); + } else { + $last_run_formatted = __('Never', 'archiver'); + } + } + + // Format next run time + $next_run_display = get_option('archiver_next_run_display', ''); + if (!empty($next_run_display)) { + $next_run_formatted = $next_run_display; + } elseif ($next_run) { + $next_run_formatted = $this->format_timestamp_with_timezone($next_run); + } else { + $next_run_formatted = __('Not scheduled', 'archiver'); + } + + ?> +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    + +
    +

    + + +

    +

    + + +

    +
    + +
    +
    + + +
    +
    + + +
    +
    + + + + 0): ?> +
    +

    +
    + +
    + + + 3): ?> +
    + +
    + +
    +
    + +
    + + + + + setTimezone($timezone); + return $datetime->format(get_option('date_format') . ' ' . get_option('time_format')); + } else if ($gmt_offset) { + // Use GMT offset + $timestamp += $gmt_offset * HOUR_IN_SECONDS; + return date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $timestamp); + } + } catch (Exception $e) { + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Date formatting error: ' . $e->getMessage()); + } + } + + // Default fallback to WordPress localized date function + return date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $timestamp); +} + + /** + * Get dashboard statistics + */ + private function get_dashboard_stats() { + return array( + 'total_archived' => get_option('archiver_total_archived', 0), + 'failed_snapshots' => get_option('archiver_failed_snapshots', 0), + 'pending_urls' => count(get_option('archiver_urls_to_update', array())), + 'queue_count' => count(get_option('archiver_background_queue', array())) + ); + } + + /** + * AJAX handler for dashboard trigger + */ + public function ajax_dashboard_trigger() { + check_ajax_referer('archiver_dashboard_nonce', 'nonce'); + + if (!current_user_can('manage_options')) { + wp_send_json_error(array('message' => __('Permission denied', 'archiver'))); + } + + // Process queue + if (isset($_POST['process_queue'])) { + if ($this->archiver && method_exists($this->archiver, 'process_urls_for_update')) { + $processed = $this->archiver->process_urls_for_update(); + wp_send_json_success(array( + 'message' => sprintf(__('Started processing %d URLs from queue', 'archiver'), $processed) + )); + } else { + wp_send_json_error(array('message' => __('Queue processing not available', 'archiver'))); + } + return; + } + + // Archive single URL + $url = isset($_POST['url']) ? esc_url_raw($_POST['url']) : ''; + + if (empty($url) || !filter_var($url, FILTER_VALIDATE_URL)) { + wp_send_json_error(array('message' => __('Please enter a valid URL', 'archiver'))); + } + + // Add to queue with high priority + if ($this->archiver && method_exists($this->archiver, 'trigger_url_snapshot')) { + $this->archiver->trigger_url_snapshot($url); + wp_send_json_success(array( + 'message' => sprintf(__('URL added to archive queue: %s', 'archiver'), $url) + )); + } else { + wp_send_json_error(array('message' => __('Archive functionality not available', 'archiver'))); + } + } + + /** + * AJAX handler for dashboard stats + */ + public function ajax_dashboard_stats() { + check_ajax_referer('archiver_dashboard_nonce', 'nonce'); + + if (!current_user_can('manage_options')) { + wp_send_json_error(array('message' => __('Permission denied', 'archiver'))); + } + + $stats = $this->get_dashboard_stats(); + wp_send_json_success($stats); + } +} \ No newline at end of file diff --git a/includes/class-archiver.php b/includes/class-archiver.php index d0e0979..1acf776 100644 --- a/includes/class-archiver.php +++ b/includes/class-archiver.php @@ -1,6 +1,6 @@ name = __('Archiver', 'archiver'); $this->snapshot_max_count = apply_filters('archiver_snapshot_max_count', 10); - $this->services = ARCHIVER_SERVICES; - // 初始化缓存系统 - $this->cache = new Archiver_Cache(); + // Initialize services with fallback + $this->services = $this->get_available_services(); - // 注册钩子 + // Register hooks add_action('init', array($this, 'setup_cron')); add_action('rest_api_init', array($this, 'register_rest_routes')); } + /** + * Get available services with fallback + */ + private function get_available_services() { + if (defined('ARCHIVER_SERVICES') && is_array(ARCHIVER_SERVICES)) { + return ARCHIVER_SERVICES; + } + + // Fallback services if constant is not defined + return array( + 'wenpai' => array( + 'name' => 'WenPai Archive', + 'save_url' => 'https://web.wenpai.net/save/', + 'fetch_url' => 'https://web.wenpai.net/cdx/', + 'view_url' => 'https://web.wenpai.net/web/', + 'enabled' => true + ), + 'wayback' => array( + 'name' => 'Internet Archive', + 'save_url' => 'https://web.archive.org/save/', + 'fetch_url' => 'https://web.archive.org/cdx/search/cdx', + 'view_url' => 'https://web.archive.org/web/', + 'enabled' => true + ), + 'archive_today' => array( + 'name' => 'Archive.today', + 'save_url' => 'https://archive.today/?run=1&url=', + 'fetch_url' => 'https://archive.today/', + 'view_url' => 'https://archive.today/', + 'enabled' => false + ) + ); + } + public function get_slug() { return $this->slug; } @@ -48,19 +81,24 @@ class Archiver { public function init() { $this->set_locale(); - // 注册脚本和样式 + // Initialize cache system after WordPress is fully loaded + if (class_exists('Archiver_Cache')) { + $this->cache = new Archiver_Cache(); + } + + // Register scripts and styles add_action('wp_enqueue_scripts', array($this, 'register_scripts_and_styles'), 5); add_action('admin_enqueue_scripts', array($this, 'register_scripts_and_styles'), 5); add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts')); add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts')); - // AJAX 处理 + // AJAX handlers add_action('wp_ajax_archiver_immediate_snapshot', array($this, 'ajax_immediate_snapshot')); add_action('wp_ajax_archiver_check_cache_update', array($this, 'ajax_check_cache_update')); add_action('wp_ajax_archiver_get_snapshots', array($this, 'ajax_get_snapshots')); if ($this->can_run()) { - // 内容钩子 + // Content hooks if (get_option('archiver_auto_archive', true)) { add_action('save_post', array($this, 'trigger_post_snapshot')); add_action('created_term', array($this, 'trigger_term_snapshot'), 10, 3); @@ -70,9 +108,15 @@ class Archiver { add_action('admin_bar_menu', array($this, 'add_admin_bar_links'), 999); } - // 后台处理钩子 - add_action('archiver_process_background_queue', array($this->cache, 'process_background_queue')); + // Background processing hooks + if ($this->cache) { + add_action('archiver_process_background_queue', array($this->cache, 'process_background_queue')); + } add_action('archiver_fetch_single_url', array($this, 'fetch_single_url_background')); + + // Register new hook for priority queue + add_action('archiver_process_priority_queue', array($this, 'process_priority_queue')); + add_action('archiver_trigger_single_service', array($this, 'trigger_single_service_archive'), 10, 2); } public function admin_init() { @@ -90,18 +134,77 @@ class Archiver { } public function setup_cron() { - $frequency = get_option('archiver_update_frequency', 'daily'); - if (!wp_next_scheduled('archiver_process_urls')) { - wp_schedule_event(time(), $frequency, 'archiver_process_urls'); + // Add custom Cron intervals - ensure "hourly" works correctly + add_filter('cron_schedules', array($this, 'add_custom_cron_intervals')); + + // Get current frequency setting + $frequency = get_option('archiver_update_frequency', 'hourly'); + + // Check existing scheduled tasks + $timestamp = wp_next_scheduled('archiver_process_urls'); + + // If no schedule exists or the last schedule time is more than 24 hours ago, reschedule task + $last_schedule_time = get_option('archiver_last_schedule_time', 0); + $schedule_age = time() - $last_schedule_time; + + if (!$timestamp || $schedule_age > DAY_IN_SECONDS) { + // Cancel any existing scheduled tasks + if ($timestamp) { + wp_unschedule_event($timestamp, 'archiver_process_urls'); + } + + // Ensure no duplicate tasks + $this->clear_duplicate_cron_tasks('archiver_process_urls'); + + // Create new scheduled task, add 1-60 seconds random delay + $offset = mt_rand(1, 60); + wp_schedule_event(time() + $offset, $frequency, 'archiver_process_urls'); + + // Record scheduling time + update_option('archiver_last_schedule_time', time()); + + // Record next run time for immediate display + $this->update_next_run_display_time(); + + // Debug log + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Rescheduled process_urls task with frequency: ' . $frequency); + } } + add_action('archiver_process_urls', array($this, 'process_urls_for_update')); } + /** + * Add custom Cron intervals - ensure "hourly" and other intervals work as expected + */ + public function add_custom_cron_intervals($schedules) { + // Add 15-minute interval for high-frequency scenarios + $schedules['minutes_15'] = array( + 'interval' => 15 * MINUTE_IN_SECONDS, + 'display' => __('Every 15 Minutes', 'archiver') + ); + + // Add 30-minute interval for medium-frequency scenarios + $schedules['minutes_30'] = array( + 'interval' => 30 * MINUTE_IN_SECONDS, + 'display' => __('Every 30 Minutes', 'archiver') + ); + + // Ensure hourly interval exists and is clearly defined + $schedules['exact_hourly'] = array( + 'interval' => HOUR_IN_SECONDS, + 'display' => __('Exactly Every Hour', 'archiver') + ); + + return $schedules; + } + public function can_run() { return apply_filters('archiver_can_run', __return_true()); } - // Meta Box 相关方法 + // Meta Box related methods public function add_post_meta_box() { $post_types = get_option('archiver_post_types', array('post', 'page')); add_meta_box( @@ -122,7 +225,15 @@ class Archiver { } public function output_term_meta_box() { + if (!isset($_GET['tag_ID'])) { + return; + } + $term = get_term($_GET['tag_ID']); + if (is_wp_error($term)) { + return; + } + $url = get_term_link($term); if (!is_wp_error($url)) { $this->current_permalink = $url; @@ -169,7 +280,7 @@ class Archiver {
    $enabled) : if (!$enabled) continue; - $service = $this->services[$service_id]; + $service = isset($this->services[$service_id]) ? $this->services[$service_id] : array('name' => ucfirst($service_id)); ?> @@ -195,7 +305,7 @@ class Archiver { __('Invalid URL', 'archiver')]); } - $snapshots = $this->cache->get_snapshots($url, $service); + // Debug logging + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Getting snapshots for URL: ' . $url . ' Service: ' . $service); + } + + $snapshots = array(); + if ($this->cache && method_exists($this->cache, 'get_snapshots')) { + $snapshots = $this->cache->get_snapshots($url, $service); + } if (!empty($snapshots)) { $html = $this->format_snapshots_html($snapshots, $service); @@ -237,29 +355,35 @@ class Archiver { wp_send_json_error(['message' => __('Invalid URL.', 'archiver')], 400); } - // 触发快照到所有启用的服务 - $enabled_services = get_option('archiver_services', array('wayback' => true)); - $triggered = 0; + // Debug logging + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Triggering snapshot for URL: ' . $url . ' Service: ' . $service); + } - foreach ($enabled_services as $service_id => $enabled) { - if ($enabled && (!$service || $service === $service_id)) { - $result = $this->trigger_archive_service($url, $service_id); - if ($result) { - $triggered++; + // Add URL to high-priority queue + if ($this->cache && method_exists($this->cache, 'queue_for_update')) { + if ($service) { + $this->cache->queue_for_update($url, $service, true); + } else { + // Add to queue for all enabled services + $enabled_services = get_option('archiver_services', array('wenpai' => true)); + foreach ($enabled_services as $service_id => $enabled) { + if ($enabled) { + $this->cache->queue_for_update($url, $service_id, true); + } } - // 强制刷新缓存 - $this->cache->queue_for_update($url, $service_id, true); } } - if ($triggered > 0) { - wp_send_json_success([ - 'message' => sprintf(__('Snapshot request submitted to %d service(s).', 'archiver'), $triggered), - 'refresh' => true - ]); - } else { - wp_send_json_error(['message' => __('Failed to trigger snapshot.', 'archiver')]); + // Schedule an immediate event to process high-priority queue + if (!wp_next_scheduled('archiver_process_priority_queue')) { + wp_schedule_single_event(time() + 5, 'archiver_process_priority_queue'); } + + wp_send_json_success([ + 'message' => __('Snapshot request submitted and will be processed shortly.', 'archiver'), + 'refresh' => true + ]); } public function ajax_check_cache_update() { @@ -272,7 +396,11 @@ class Archiver { wp_send_json_error(); } - $snapshots = $this->cache->get_snapshots($url, $service); + $snapshots = array(); + if ($this->cache && method_exists($this->cache, 'get_snapshots')) { + $snapshots = $this->cache->get_snapshots($url, $service, true); // Force refresh + } + wp_send_json_success([ 'has_update' => !empty($snapshots), 'count' => count($snapshots), @@ -280,7 +408,7 @@ class Archiver { ]); } - private function format_snapshots_html($snapshots, $service = 'wayback') { + private function format_snapshots_html($snapshots, $service = 'wenpai') { if (empty($snapshots)) { return ''; } @@ -288,17 +416,28 @@ class Archiver { $html = '
      '; $date_format = get_option('date_format'); $time_format = get_option('time_format'); - $service_config = $this->services[$service] ?? $this->services['wenpai']; + $service_config = isset($this->services[$service]) ? $this->services[$service] : $this->services['wenpai']; foreach (array_slice($snapshots, 0, $this->snapshot_max_count) as $snapshot) { $timestamp = isset($snapshot['timestamp']) ? $snapshot['timestamp'] : ''; $original = isset($snapshot['original']) ? $snapshot['original'] : ''; if ($timestamp && $original) { - $date_time = date('Y.m.d H:i', strtotime($timestamp)); - $adjusted_date = get_date_from_gmt($date_time); + // Handle different timestamp formats + if (strlen($timestamp) === 14) { + // Format: YYYYMMDDHHMMSS + $date_time = substr($timestamp, 0, 4) . '-' . + substr($timestamp, 4, 2) . '-' . + substr($timestamp, 6, 2) . ' ' . + substr($timestamp, 8, 2) . ':' . + substr($timestamp, 10, 2) . ':' . + substr($timestamp, 12, 2); + } else { + $date_time = $timestamp; + } + $snapshot_url = $service_config['view_url'] . $timestamp . '/' . $original; - $formatted_date = date_i18n($date_format . ' @ ' . $time_format, strtotime($adjusted_date)); + $formatted_date = date_i18n($date_format . ' @ ' . $time_format, strtotime($date_time)); $html .= sprintf( '
    • %s
    • ', @@ -316,7 +455,7 @@ class Archiver { $html .= sprintf( '%s ', esc_url($view_all_url), - __('See all snapshots', 'archiver') + __('View all snapshots', 'archiver') ); $html .= '

      '; @@ -359,7 +498,7 @@ class Archiver { ]); // Add service submenu - $enabled_services = get_option('archiver_services', array('wayback' => true)); + $enabled_services = get_option('archiver_services', array('wenpai' => true)); foreach ($enabled_services as $service_id => $enabled) { if ($enabled && isset($this->services[$service_id])) { $service = $this->services[$service_id]; @@ -374,7 +513,7 @@ class Archiver { } } - // 获取当前页面 URL + // Get current page URL public function get_current_permalink() { if (empty($this->current_permalink)) { if (is_admin()) { @@ -433,7 +572,7 @@ class Archiver { return $permalink; } - // 脚本和样式 + // Scripts and styles public function register_scripts_and_styles() { wp_register_script( 'archiver', @@ -450,7 +589,7 @@ class Archiver { ARCHIVER_VERSION ); - // 添加管理员 nonce + // Add admin nonce $admin_nonce = is_admin() ? wp_create_nonce('archiver_admin_nonce') : ''; wp_localize_script('archiver', 'archiver', array( @@ -469,7 +608,7 @@ class Archiver { 'success' => __('Snapshot triggered successfully!', 'archiver'), 'error' => __('Failed to trigger snapshot.', 'archiver'), 'no_snapshots' => __('No archives yet.', 'archiver'), - 'view_all' => __('See all snapshots', 'archiver'), + 'view_all' => __('View all snapshots', 'archiver'), 'checking' => __('Checking...', 'archiver'), 'online' => __('Online', 'archiver'), 'offline' => __('Offline', 'archiver'), @@ -493,7 +632,8 @@ class Archiver { 'term.php', 'profile.php', 'user-edit.php', - 'tools_page_archiver-settings' + 'tools_page_archiver-settings', + 'index.php' // Dashboard ); if (in_array($hook, $allowed_hooks)) { @@ -545,18 +685,25 @@ class Archiver { $url = $request->get_param('url'); $service = $request->get_param('service'); - if ($service) { - $this->cache->queue_for_update($url, $service, true); - } else { - // Queue for all enabled services - $enabled_services = get_option('archiver_services', array('wayback' => true)); - foreach ($enabled_services as $service_id => $enabled) { - if ($enabled) { - $this->cache->queue_for_update($url, $service_id, true); + if ($this->cache && method_exists($this->cache, 'queue_for_update')) { + if ($service) { + $this->cache->queue_for_update($url, $service, true); + } else { + // Queue for all enabled services + $enabled_services = get_option('archiver_services', array('wenpai' => true)); + foreach ($enabled_services as $service_id => $enabled) { + if ($enabled) { + $this->cache->queue_for_update($url, $service_id, true); + } } } } + // Schedule processing of high-priority queue + if (!wp_next_scheduled('archiver_process_priority_queue')) { + wp_schedule_single_event(time() + 5, 'archiver_process_priority_queue'); + } + return new WP_REST_Response(array( 'success' => true, 'message' => __('Snapshot request recorded.', 'archiver') @@ -567,7 +714,10 @@ class Archiver { $url = $request->get_param('url'); $service = $request->get_param('service'); - $snapshots = $this->cache->get_snapshots($url, $service); + $snapshots = array(); + if ($this->cache && method_exists($this->cache, 'get_snapshots')) { + $snapshots = $this->cache->get_snapshots($url, $service); + } return new WP_REST_Response(array( 'success' => true, @@ -576,7 +726,7 @@ class Archiver { ), 200); } - // 触发快照 + // Trigger snapshots - modified to async mode public function trigger_post_snapshot($post_id) { if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) { return; @@ -608,27 +758,131 @@ class Archiver { $this->trigger_url_snapshot($url); } + /** + * Optimized URL snapshot trigger method - changed to use async queue + */ public function trigger_url_snapshot($url) { if (empty($url)) { return; } - // 如果启用了立即存档,直接触发 - if (get_option('archiver_archive_on_publish', true)) { + // Create a high-priority queue item + $priority = get_option('archiver_archive_on_publish', true) ? 'high' : 'normal'; + + // Add to queue regardless of immediate archiving setting + if ($this->cache && method_exists($this->cache, 'queue_for_update')) { + // Add to cache queue $enabled_services = get_option('archiver_services', array('wenpai' => true)); foreach ($enabled_services as $service_id => $enabled) { if ($enabled) { - $this->trigger_archive_service($url, $service_id); + $this->cache->queue_for_update($url, $service_id, ($priority === 'high')); + } + } + } else { + // Fall back to simple queue if cache class unavailable + $existing = get_option('archiver_urls_to_update', array()); + if (!is_array($existing)) { + $existing = array(); + } + + $existing[] = array( + 'url' => $url, + 'priority' => $priority, + 'added' => time() + ); + update_option('archiver_urls_to_update', array_unique($existing)); + } + + // If immediate archiving is enabled, schedule a near-term single event to process the high-priority queue + if ($priority === 'high' && !wp_next_scheduled('archiver_process_priority_queue')) { + wp_schedule_single_event(time() + 5, 'archiver_process_priority_queue'); + } + } + + /** + * Process high-priority queue + */ + public function process_priority_queue() { + // First get the background queue + $background_queue = get_option('archiver_background_queue', array()); + if (!is_array($background_queue)) { + $background_queue = array(); + } + + // Find high-priority items added within the last 10 minutes + $priority_items = array(); + $regular_items = array(); + $current_time = time(); + $cutoff_time = $current_time - (10 * MINUTE_IN_SECONDS); + + foreach ($background_queue as $item) { + // Check if it's an array and has an 'added' field + if (is_array($item) && isset($item['added']) && $item['added'] > $cutoff_time) { + $priority_items[] = $item; + } else { + $regular_items[] = $item; + } + } + + // Process only up to 5 high-priority items + $batch_size = min(5, count($priority_items)); + if ($batch_size === 0) { + return; // No high-priority items to process + } + + // Remove items to be processed from the queue + $batch = array_slice($priority_items, 0, $batch_size); + $remaining_priority_items = array_slice($priority_items, $batch_size); + + // Update queue + update_option('archiver_background_queue', array_merge($regular_items, $remaining_priority_items)); + + // Process high-priority items + foreach ($batch as $item) { + if (is_array($item)) { + $url = isset($item['url']) ? $item['url'] : (is_string($item) ? $item : ''); + $service = isset($item['service']) ? $item['service'] : null; + + if (!empty($url)) { + // Process archive request asynchronously + $this->schedule_nonblocking_archive($url, $service); + } + } else if (is_string($item)) { + // Handle old format queue items + $this->schedule_nonblocking_archive($item); + } + } + + // If more high-priority items remain, schedule another processing run + if (count($remaining_priority_items) > 0) { + wp_schedule_single_event(time() + 30, 'archiver_process_priority_queue'); + } + } + + /** + * Schedule non-blocking archive request + */ + private function schedule_nonblocking_archive($url, $service = null) { + $enabled_services = get_option('archiver_services', array('wenpai' => true)); + + foreach ($enabled_services as $service_id => $enabled) { + if ($enabled && (!$service || $service === $service_id)) { + // Register separate events for each service to avoid processing multiple services in the same request + if (!wp_next_scheduled('archiver_trigger_single_service', array($url, $service_id))) { + wp_schedule_single_event(time() + rand(5, 15), 'archiver_trigger_single_service', array($url, $service_id)); } } } - - // 添加到更新队列 - $this->cache->queue_for_update($url); } - private function trigger_archive_service($url, $service = 'wenpai') { + /** + * Trigger single service archive - executed in a separate WP-Cron request + */ + public function trigger_single_service_archive($url, $service) { if (!isset($this->services[$service])) { + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Service not found: ' . $service); + } return false; } @@ -636,57 +890,227 @@ class Archiver { $save_url = $service_config['save_url'] . urlencode($url); $response = wp_remote_get($save_url, array( - 'timeout' => 10, - 'sslverify' => true, + 'timeout' => 15, + 'sslverify' => ($service !== 'wenpai'), // WenPai might use self-signed certificates 'headers' => array( - 'User-Agent' => 'WP-Archiver/' . ARCHIVER_VERSION + 'User-Agent' => 'WP-Archiver/' . ARCHIVER_VERSION . ' (WordPress/' . get_bloginfo('version') . ')' ) )); - return !is_wp_error($response) && in_array(wp_remote_retrieve_response_code($response), array(200, 201, 202)); + $success = !is_wp_error($response) && in_array(wp_remote_retrieve_response_code($response), array(200, 201, 202)); + + // Debug logging + if (defined('WP_DEBUG') && WP_DEBUG) { + if ($success) { + error_log('[WP Archiver] Successfully triggered ' . $service . ' for: ' . $url); + } else { + $error_msg = is_wp_error($response) ? $response->get_error_message() : 'HTTP ' . wp_remote_retrieve_response_code($response); + error_log('[WP Archiver] Failed to trigger ' . $service . ' for: ' . $url . ' - ' . $error_msg); + } + } + + // Update cache + if ($success && $this->cache && method_exists($this->cache, 'fetch_and_cache_snapshots')) { + // Wait a moment before refreshing the cache to give the archive service time to process + wp_schedule_single_event(time() + 60, 'archiver_fetch_single_url', array($url)); + } + + return $success; } - // 处理URL更新 + /** + * Old synchronous archive call method - kept for compatibility but no longer used directly + */ + private function trigger_archive_service($url, $service = 'wenpai') { + if (!isset($this->services[$service])) { + return false; + } + + // Convert this operation to a non-blocking call + if (!wp_next_scheduled('archiver_trigger_single_service', array($url, $service))) { + wp_schedule_single_event(time() + 1, 'archiver_trigger_single_service', array($url, $service)); + } + + return true; + } + + // Process URL updates public function process_urls_for_update() { $urls = get_option('archiver_urls_to_update', array()); if (empty($urls)) { + $this->update_run_times(); return 0; } - $batch_size = apply_filters('archiver_batch_size', 5); + $batch_size = get_option('archiver_batch_size', 10); $batch = array_splice($urls, 0, $batch_size); foreach ($batch as $url) { + if (is_array($url) && isset($url['url'])) { + $url = $url['url']; + } wp_schedule_single_event(time() + rand(5, 30), 'archiver_fetch_single_url', array($url)); } update_option('archiver_urls_to_update', $urls); - update_option('archiver_last_run', current_time('mysql')); - + + // Update run times + $this->update_run_times(); + return count($batch); } + /** + * Update run time records - use UTC timestamps for internal storage + */ + private function update_run_times() { + // Record last run time using UTC time + $now_utc = time(); + update_option('archiver_last_run_timestamp', $now_utc); + + // Also store a formatted version using site's timezone for direct display + update_option('archiver_last_run', $this->get_formatted_schedule_time($now_utc)); + + // Calculate and record next run time + $next_run = wp_next_scheduled('archiver_process_urls'); + if ($next_run) { + update_option('archiver_next_run_timestamp', $next_run); + update_option('archiver_next_run_display', $this->get_formatted_schedule_time($next_run)); + } + + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Run times updated. Last run: ' . date('Y-m-d H:i:s', $now_utc) . ', Next run timestamp: ' . ($next_run ?: 'none')); + } + } + public function fetch_single_url_background($url) { $enabled_services = get_option('archiver_services', array('wenpai' => true)); foreach ($enabled_services as $service_id => $enabled) { if ($enabled) { // Fetch and cache snapshots - $this->cache->fetch_and_cache_snapshots($url, $service_id); - - // Also trigger new snapshot - $this->trigger_archive_service($url, $service_id); + if ($this->cache && method_exists($this->cache, 'fetch_and_cache_snapshots')) { + $this->cache->fetch_and_cache_snapshots($url, $service_id); + } } } } - // 重新安排 Cron 任务 + /** + * Fixed Cron task rescheduling method + */ public function reschedule_cron_task($frequency) { + // Convert to exact interval if hourly is specified + if ($frequency === 'hourly') { + $frequency = 'exact_hourly'; + } + + // Ensure frequency is valid + $valid_frequencies = array('minutes_15', 'minutes_30', 'exact_hourly', 'hourly', 'twicedaily', 'daily', 'weekly'); + if (!in_array($frequency, $valid_frequencies)) { + $frequency = 'hourly'; // Default to hourly + } + + // Cancel all existing tasks $timestamp = wp_next_scheduled('archiver_process_urls'); if ($timestamp) { wp_unschedule_event($timestamp, 'archiver_process_urls'); } - - wp_schedule_event(time(), $frequency, 'archiver_process_urls'); + + // Ensure any duplicate tasks are cleaned up + $this->clear_duplicate_cron_tasks('archiver_process_urls'); + + // Create new task, add 1-60 seconds random delay + $offset = mt_rand(1, 60); + wp_schedule_event(time() + $offset, $frequency, 'archiver_process_urls'); + + // Record scheduling time + update_option('archiver_last_schedule_time', time()); + + // Update displayed next run time to immediately update UI + $this->update_next_run_display_time(); + + // Debug log + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Cron task rescheduled with frequency: ' . $frequency); + } } -} + + /** + * Update next run display time + */ + private function update_next_run_display_time() { + $next_run = wp_next_scheduled('archiver_process_urls'); + if ($next_run) { + // Save original timestamp and formatted display time + update_option('archiver_next_run_timestamp', $next_run); + update_option('archiver_next_run_display', $this->get_formatted_schedule_time($next_run)); + } else { + delete_option('archiver_next_run_timestamp'); + delete_option('archiver_next_run_display'); + } + } + + /** + * Clear duplicate Cron tasks + */ + private function clear_duplicate_cron_tasks($hook) { + $crons = _get_cron_array(); + $found = false; + + if (empty($crons)) { + return false; + } + + foreach ($crons as $timestamp => $cron) { + if (isset($cron[$hook])) { + foreach ($cron[$hook] as $key => $event) { + if ($found) { + // Already found one event, delete other duplicates + wp_unschedule_event($timestamp, $hook, $event['args']); + } else { + // First time finding an event + $found = true; + } + } + } + } + + return $found; + } + + /** + * Get formatted schedule time (fix timezone issues) + */ + public function get_formatted_schedule_time($timestamp) { + if (!$timestamp) { + return __('Not scheduled', 'archiver'); + } + + // Get WordPress timezone settings + $timezone_string = get_option('timezone_string'); + $gmt_offset = get_option('gmt_offset'); + + try { + if ($timezone_string) { + // Use timezone string + $timezone = new DateTimeZone($timezone_string); + $datetime = new DateTime('@' . $timestamp); // Create GMT time using @ symbol + $datetime->setTimezone($timezone); // Convert to local timezone + return $datetime->format(get_option('date_format') . ' ' . get_option('time_format')); + } else if (!empty($gmt_offset)) { + // Use GMT offset + $local_timestamp = $timestamp + ($gmt_offset * HOUR_IN_SECONDS); + return date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $local_timestamp); + } + } catch (Exception $e) { + // Log debug info on error + if (defined('WP_DEBUG') && WP_DEBUG) { + error_log('[WP Archiver] Time formatting error: ' . $e->getMessage()); + } + } + + // Default fallback + return date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $timestamp); + } +} \ No newline at end of file diff --git a/js/archiver.js b/js/archiver.js deleted file mode 100644 index 223b6bf..0000000 --- a/js/archiver.js +++ /dev/null @@ -1,430 +0,0 @@ -/** - * WP Archiver 前端脚本 - */ - -(function($) { - 'use strict'; - - const Archiver = { - - config: { - checkInterval: 5000, - maxChecks: 12, - fadeInDuration: 300, - cachePrefix: 'archiver_cache_' - }, - - init: function() { - $(document).ready(() => { - this.initMetabox(); - this.initAdminBar(); - this.initAdminPage(); - this.initLazyLoading(); - }); - }, - - /** - * 初始化元框 - */ - initMetabox: function() { - const $container = $('#archiver-snapshots'); - if (!$container.length) return; - - const url = $container.data('url') || $('#archiver-url').val(); - if (!url) return; - - // 延迟加载快照 - this.loadSnapshots(url, $container); - - // 立即存档按钮 - $('#archiver-immediate-snapshot').on('click', (e) => { - e.preventDefault(); - this.triggerSnapshot(url); - }); - }, - - /** - * 初始化管理栏 - */ - initAdminBar: function() { - const $trigger = $('#wp-admin-bar-archiver-trigger a'); - if (!$trigger.length) return; - - // 延迟加载快照计数 - const $countItem = $('#wp-admin-bar-archiver-snapshots'); - if ($countItem.length && archiver.url) { - this.updateAdminBarCount(archiver.url); - } - - // 触发快照 - $trigger.on('click', (e) => { - e.preventDefault(); - this.triggerAdminBarSnapshot(); - }); - }, - - /** - * 初始化管理页面 - */ - initAdminPage: function() { - // 标签切换 - $('.nav-tab').on('click', function(e) { - e.preventDefault(); - const $tab = $(this); - const tabId = $tab.data('tab'); - - $('.nav-tab').removeClass('nav-tab-active'); - $tab.addClass('nav-tab-active'); - - $('.archiver-tab-content').hide(); - $('#' + tabId).fadeIn(300); - - // 保存用户偏好 - if (typeof(Storage) !== "undefined") { - localStorage.setItem('archiver_active_tab', tabId); - } - }); - - // 恢复上次的标签 - const savedTab = localStorage.getItem('archiver_active_tab'); - if (savedTab) { - $(`.nav-tab[data-tab="${savedTab}"]`).trigger('click'); - } - }, - - /** - * 延迟加载 - */ - initLazyLoading: function() { - if ('IntersectionObserver' in window) { - const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - const $element = $(entry.target); - const url = $element.data('url'); - if (url && !$element.data('loaded')) { - this.loadSnapshots(url, $element); - $element.data('loaded', true); - } - } - }); - }); - - $('.archiver-lazy').each(function() { - observer.observe(this); - }); - } - }, - - /** - * 加载快照 - */ - loadSnapshots: function(url, $container) { - // 检查本地缓存 - const cached = this.getCache(url); - if (cached) { - this.displaySnapshots(cached, $container); - // 后台检查更新 - this.checkForUpdates(url, $container); - return; - } - - // 显示加载状态 - this.showLoading($container); - - // 从服务器获取 - $.ajax({ - url: archiver.ajax_url, - type: 'POST', - data: { - action: 'archiver_get_snapshots', - _ajax_nonce: archiver.ajax_nonce, - url: url - }, - success: (response) => { - if (response.success) { - this.setCache(url, response.data); - this.displaySnapshots(response.data, $container); - } else { - this.showError($container, response.data.message); - } - }, - error: () => { - this.showError($container, archiver.i18n.error); - } - }); - }, - - /** - * 显示快照 - */ - displaySnapshots: function(data, $container) { - if (!data.html) { - $container.html('

      ' + archiver.i18n.no_snapshots + '

      '); - return; - } - - $container.hide().html(data.html).fadeIn(this.config.fadeInDuration); - - // 添加动画 - $container.find('li').each(function(index) { - $(this).css({ - opacity: 0, - transform: 'translateY(10px)' - }).delay(index * 50).animate({ - opacity: 1, - transform: 'translateY(0)' - }, 300); - }); - }, - - /** - * 触发快照 - */ - triggerSnapshot: function(url) { - const $button = $('#archiver-immediate-snapshot'); - const $status = $('#archiver-status'); - - $button.prop('disabled', true).addClass('updating-message'); - $status.show().removeClass('error success') - .html(' ' + archiver.i18n.triggering); - - $.ajax({ - url: archiver.ajax_url, - type: 'POST', - data: { - action: 'archiver_immediate_snapshot', - _ajax_nonce: archiver.ajax_nonce, - url: url - }, - success: (response) => { - if (response.success) { - $status.removeClass('error').addClass('success') - .html(' ' + response.data.message); - - // 清除缓存并开始轮询 - this.clearCache(url); - if (response.data.refresh) { - this.startPolling(url); - } - } else { - $status.removeClass('success').addClass('error') - .html(' ' + response.data.message); - } - }, - error: () => { - $status.removeClass('success').addClass('error') - .html(' ' + archiver.i18n.error); - }, - complete: () => { - $button.prop('disabled', false).removeClass('updating-message'); - setTimeout(() => { - $status.fadeOut(); - }, 5000); - } - }); - }, - - /** - * 管理栏触发快照 - */ - triggerAdminBarSnapshot: function() { - const $item = $('#wp-admin-bar-archiver'); - $item.addClass('archiver-active'); - - $.ajax({ - url: archiver.rest_url + 'trigger-snapshot', - method: 'POST', - beforeSend: (xhr) => { - xhr.setRequestHeader('X-WP-Nonce', archiver.nonce); - }, - data: { - url: archiver.url - }, - success: (response) => { - $item.removeClass('archiver-active'); - if (response.success) { - $item.addClass('archiver-success'); - this.clearCache(archiver.url); - this.updateAdminBarCount(archiver.url); - } else { - $item.addClass('archiver-failure'); - } - }, - error: () => { - $item.removeClass('archiver-active').addClass('archiver-failure'); - }, - complete: () => { - setTimeout(() => { - $item.removeClass('archiver-success archiver-failure'); - }, 2000); - } - }); - }, - - /** - * 更新管理栏计数 - */ - updateAdminBarCount: function(url) { - const $countItem = $('#wp-admin-bar-archiver-snapshots .ab-label'); - if (!$countItem.length) return; - - $.ajax({ - url: archiver.ajax_url, - type: 'POST', - data: { - action: 'archiver_get_snapshots', - _ajax_nonce: archiver.ajax_nonce, - url: url - }, - success: (response) => { - if (response.success && response.data.count !== undefined) { - const count = response.data.count >= 10 ? '10+' : response.data.count; - $countItem.text(archiver.i18n.view_all + ' (' + count + ')'); - } - } - }); - }, - - /** - * 检查更新 - */ - checkForUpdates: function(url, $container) { - $.ajax({ - url: archiver.ajax_url, - type: 'POST', - data: { - action: 'archiver_check_cache_update', - _ajax_nonce: archiver.ajax_nonce, - url: url - }, - success: (response) => { - if (response.success && response.data.has_update) { - this.clearCache(url); - this.displaySnapshots(response.data, $container); - } - } - }); - }, - - /** - * 开始轮询 - */ - startPolling: function(url) { - let checkCount = 0; - const $container = $('#archiver-snapshots'); - - const interval = setInterval(() => { - checkCount++; - - if (checkCount > this.config.maxChecks) { - clearInterval(interval); - return; - } - - this.checkForUpdates(url, $container); - }, this.config.checkInterval); - }, - - /** - * 显示加载状态 - */ - showLoading: function($container) { - const loadingHtml = ` -
      - - ${archiver.i18n.loading} -
      - `; - $container.html(loadingHtml); - }, - - /** - * 显示错误 - */ - showError: function($container, message) { - $container.html('

      ' + message + '

      '); - }, - - /** - * 缓存管理 - */ - getCache: function(url) { - if (!this.isStorageAvailable()) return null; - - try { - const key = this.config.cachePrefix + this.hashCode(url); - const cached = sessionStorage.getItem(key); - - if (cached) { - const data = JSON.parse(cached); - // 5分钟缓存 - if (Date.now() - data.timestamp < 300000) { - return data.content; - } - } - } catch (e) { - console.warn('Cache read error:', e); - } - - return null; - }, - - setCache: function(url, data) { - if (!this.isStorageAvailable()) return; - - try { - const key = this.config.cachePrefix + this.hashCode(url); - const cacheData = { - content: data, - timestamp: Date.now() - }; - sessionStorage.setItem(key, JSON.stringify(cacheData)); - } catch (e) { - console.warn('Cache write error:', e); - } - }, - - clearCache: function(url) { - if (!this.isStorageAvailable()) return; - - try { - const key = this.config.cachePrefix + this.hashCode(url); - sessionStorage.removeItem(key); - } catch (e) { - console.warn('Cache clear error:', e); - } - }, - - /** - * 工具函数 - */ - isStorageAvailable: function() { - try { - const test = '__archiver_test__'; - sessionStorage.setItem(test, test); - sessionStorage.removeItem(test); - return true; - } catch (e) { - return false; - } - }, - - hashCode: function(str) { - let hash = 0; - for (let i = 0; i < str.length; i++) { - const char = str.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; - } - return Math.abs(hash).toString(); - } - }; - - // 初始化 - Archiver.init(); - - // 暴露到全局 - window.WPArchiver = Archiver; - -})(jQuery); diff --git a/js/archiver.min.js b/js/archiver.min.js deleted file mode 100644 index 3d29948..0000000 --- a/js/archiver.min.js +++ /dev/null @@ -1 +0,0 @@ -(($)=>{'use strict';$(document).ready(()=>{initAdminBarTrigger();initMetaboxButton();initTabSwitching()});const initAdminBarTrigger=()=>{const $triggerButton=$('#wp-admin-bar-archiver-trigger a');if(!$triggerButton.length)return;$triggerButton.on('click',async(e)=>{e.preventDefault();const $menuItem=$(e.target).closest('li');$menuItem.addClass('archiver-active');try{const response=await wp.apiRequest({url:archiver.rest_url,method:'POST',data:{url:archiver.url},beforeSend:(xhr)=>{xhr.setRequestHeader('X-WP-Nonce',archiver.nonce)}});$menuItem.removeClass('archiver-active');if(response.success){$menuItem.addClass('archiver-success');console.log('Archiver:',response.message)}else{throw new Error(response.message||wp.i18n.__('Unknown error occurred','archiver'));}}catch(error){console.error('Archiver Error:',error);$menuItem.addClass('archiver-failure')}setTimeout(()=>{$menuItem.removeClass('archiver-success archiver-failure')},2000)})};const initMetaboxButton=()=>{const $immediateButton=$('#archiver-immediate-snapshot');$immediateButton.on('click',async(e)=>{e.preventDefault();const $statusElement=$('#archiver-status');const url=$('#archiver-url').val();const nonce=$('#archiver_nonce').val();$immediateButton.prop('disabled',true);$statusElement.show().text(archiver.i18n.triggering).removeClass('error success').addClass('processing');try{const response=await $.ajax({url:archiver.ajax_url,type:'POST',dataType:'json',data:{action:'archiver_immediate_snapshot',_ajax_nonce:nonce,url:url}});if(response.success){$statusElement.text(response.data.message).removeClass('processing').addClass('success');if(response.data.snapshots){$('#archiver-snapshots ul').html(response.data.snapshots.join(''))}}else{throw new Error(response.data.message||'Unknown error');}}catch(error){console.error('Archiver Error:',error);$statusElement.text(`${archiver.i18n.error}:${error.message||'Request failed'}`).removeClass('processing success').addClass('error')}finally{setTimeout(()=>{$immediateButton.prop('disabled',false);$statusElement.fadeOut(500,()=>{$statusElement.removeClass('processing error success').empty()})},3000)}})};const initTabSwitching=()=>{$('.nav-tab').on('click',function(e){e.preventDefault();$('.nav-tab').removeClass('nav-tab-active');$(this).addClass('nav-tab-active');$('.archiver-tab-content').hide();const tabId=$(this).data('tab');$('#'+tabId).show()})};if(!$('#archiver-spin-animation').length){$('head').append(``)}})(jQuery); \ No newline at end of file diff --git a/languages/archiver-zh_CN.l10n.php b/languages/archiver-zh_CN.l10n.php index 6758680..733852e 100644 --- a/languages/archiver-zh_CN.l10n.php +++ b/languages/archiver-zh_CN.l10n.php @@ -1,3 +1,3 @@ NULL,'plural-forms'=>'nplurals=1; plural=0;','language'=>'zh_CN','pot-creation-date'=>'2025-04-06 09:41+0800','po-revision-date'=>'2025-05-27 00:18+0800','translation-revision-date'=>'2025-05-27 00:18+0800','project-id-version'=>'WP Archiver','x-generator'=>'Poedit 3.6','messages'=>['%d posts added to archive queue.'=>'%d 个文章已添加到存档队列。','%d recent posts added to archive queue.'=>'%d 个近期文章已添加到存档队列。','%d recently updated posts added to archive queue.'=>'%d 个最近更新的文章已添加到存档队列。','%s is working correctly'=>'%s 工作正常','Actions'=>'操作','All cache cleared successfully.'=>'所有缓存已成功清除。','and %d more'=>'以及 %d 个','API Calls Saved'=>'API 调用已保存','Archive'=>'存档','Archive all published posts'=>'存档所有已发布文章','Archive immediately when content is published'=>'内容发布后立即存档','Archive Now'=>'立即存档','Archive on Publish'=>'发布时存档','Archive posts from last 30 days'=>'存档过去 30 天的文章','Archive recently updated posts'=>'存档最近更新的文章','Archive Services'=>'存档服务','Archive services saved successfully.'=>'存档服务保存成功。','Archive Snapshots'=>'存档快照','Archive Statistics'=>'存档统计','Archive Tools'=>'存档工具','Archive your content using multiple archive services with advanced caching.'=>'使用具有高级缓存功能的多种存档服务来存档您的内容。','Archiver'=>'时光机','Archiver Settings'=>'时光机设置','Archives'=>'存档','Are you sure you want to clear all cache?'=>'您确定要清除所有缓存吗?','Are you sure?'=>'您确定吗?','Automatic Archiving'=>'自动存档','Automation'=>'自动化','Automation Settings'=>'自动化设置','Bulk Archive'=>'批量存档','Bulk archive operations and manual archiving.'=>'批量存档操作和手动存档。','Cache Actions'=>'缓存操作','Cache Performance'=>'缓存性能','Cache preheating started for %d posts.'=>'已开始为 %d 个文章进行缓存预热。','Cache Settings'=>'缓存设置','Cache Size'=>'缓存大小','Cache table is missing. Please deactivate and reactivate the plugin to fix this issue.'=>'缓存表缺失。请停用并重新激活该插件以修复此问题。','Cached URLs'=>'缓存的 URL','Caching reduces API calls and improves page load times'=>'缓存减少 API 调用并缩短页面加载时间','Checking...'=>'检查...','Clear Cache'=>'清除缓存','Clear Queue'=>'清除队列','Configure automatic archiving and content types.'=>'配置自动存档和内容类型。','Configure caching to improve performance.'=>'配置缓存以提高性能。','Configure which archive services to use for your content.'=>'配置用于您的内容的存档服务。','Connection failed'=>'连接失败','Connection successful'=>'连接成功','Content Types'=>'内容类型','Daily'=>'每天','Document'=>'文档','Enable automatic archiving of content'=>'启用内容自动存档','Enable Cache'=>'启用缓存','Enable caching to improve performance and reduce API calls.'=>'启用缓存以提高性能并减少 API 调用。','Enabled'=>'已启用','Enter URL to archive...'=>'输入要存档的 URL...','Execute'=>'执行','Failed Snapshots'=>'失败快照','Failed to trigger snapshot.'=>'无法触发快照。','General Settings'=>'常规设置','Hourly'=>'每小时','How often should the plugin process pending archives?'=>'插件应该多久处理一次待处理的档案?','http://wenpai.org/plugins/wp-archiver'=>'http://wenpai.org/plugins/wp-archiver','https://wenpai.org/'=>'https://wenpai.org/','Invalid service'=>'无效服务','Invalid URL'=>'无效的网址','Invalid URL.'=>'无效的 URL。','Last Run'=>'上次运行','Loading snapshots...'=>'正在加载快照...','Loading...'=>'加载中...','Manual Archive'=>'手册存档','Metric'=>'指标','Never'=>'从未','Next Run'=>'下次运行','No archives yet.'=>'尚无档案。','No archives yet. A snapshot request has been scheduled.'=>'尚无存档。已安排快照请求。','No pending URLs in the queue.'=>'队列中没有待处理的 URL。','Not scheduled'=>'无计划任务','Offline'=>'离线','Online'=>'在线','Overview of your archive activity and performance.'=>'您的存档活动和性能的概述。','Pending URLs'=>'待定 URL','Performance Tip:'=>'性能提示:','Permission denied.'=>'没有权限。','Please enable at least one archive service.'=>'请至少启用一项存档服务。','Please enter a valid URL.'=>'请输入有效的 URL。','Please select a valid action.'=>'请选择有效的批量操作。','Please select at least one post type.'=>'请选择至少一种文章类型。','Post Types to Archive'=>'要存档的内容类型','Preheat Cache'=>'预热缓存','Preheat cache for recent posts or clear all cached data.'=>'预热最近文章的缓存或清除所有缓存数据。','Primary'=>'主要','Primary service must be enabled.'=>'必须启用主要服务。','Process Queue Now'=>'立即处理队列','Processing started for %d URLs.'=>'已开始处理 %d 个 URL。','Queue cleared successfully.'=>'队列清除成功。','Queue Management'=>'队列管理','Save Services'=>'保存服务','Save Settings'=>'保存设置','Security check failed.'=>'安全检查失败。','See all snapshots'=>'查看所有快照 ↗','Select action...'=>'选择批量操作...','Select which content types should be automatically archived.'=>'选择应自动存档的内容类型。','Service'=>'服务','Service is operational'=>'服务已运行','Settings'=>'设置','Settings saved successfully.'=>'设置保存成功。','Show queue details'=>'显示队列详细信息','Snapshot request recorded.'=>'快照请求已记录。','Snapshot request submitted to %d service(s).'=>'快照请求已提交给 %d 个服务。','Snapshot triggered successfully!'=>'快照触发成功!','Status'=>'状态','Support'=>'支持','Test Connect'=>'测试连接','Test failed'=>'测试失败','Testing...'=>'正在测试...','There are no archives of this URL.'=>'此 URL 没有存档。','Total Archived'=>'总存档','Total Pending:'=>'待处理总数:','Trigger Snapshot'=>'触发快照','Triggering snapshot...'=>'正在触发快照...','Twice Daily'=>'每天两次','Update Frequency'=>'更新频率','URL added to archive queue: %s'=>'已将 URL 添加到存档队列:%s','URLs'=>'网址','Value'=>'数值','Version: %s'=>'版本:%s','View and manage the archive queue.'=>'查看和管理存档队列。','View on %s'=>'查看 %s','Weekly'=>'每周','WenPai.org'=>'文派开源','WP Archiver'=>'文派时光机','WP Archiver requires PHP 5.6 or higher.'=>'WP Archiver 需要 PHP 5.6 或更高版本。','WP Archiver requires WordPress 4.9 or higher.'=>'WP Archiver 需要 WordPress 4.9 或更高版本。','You do not have sufficient permissions to access this page.'=>'您没有足够的权限访问此页面。']]; +return ['domain'=>NULL,'plural-forms'=>'nplurals=1; plural=0;','language'=>'zh_CN','pot-creation-date'=>'2025-04-06 09:41+0800','po-revision-date'=>'2025-05-28 14:29+0800','translation-revision-date'=>'2025-05-28 14:29+0800','project-id-version'=>'WP Archiver','x-generator'=>'Poedit 3.6','messages'=>['%d in main queue, %d in background queue'=>'%d 在主队列中,%d 在后台队列中','%d posts added to archive queue.'=>'%d 个文章已添加到存档队列。','%d recent posts added to archive queue.'=>'%d 个近期文章已添加到存档队列。','%d recently updated posts added to archive queue.'=>'%d 个最近更新的文章已添加到存档队列。','%s is working correctly'=>'%s 工作正常','... and %d more'=>'... 以及 %d 条','Actions'=>'操作','All cache cleared successfully.'=>'所有缓存已成功清除。','and %d more'=>'以及 %d 个','API Calls Saved'=>'API 调用已保存','Archive'=>'存档','Archive all published posts'=>'存档所有已发布文章','Archive failed'=>'存档失败','Archive functionality not available'=>'存档功能不可用','Archive immediately when content is published'=>'内容发布后立即存档','Archive Now'=>'立即存档','Archive on Publish'=>'发布时存档','Archive posts from last 30 days'=>'存档过去 30 天的文章','Archive recently updated posts'=>'存档最近更新的文章','Archive Services'=>'存档服务','Archive services saved successfully.'=>'存档服务保存成功。','Archive Snapshots'=>'存档快照','Archive Statistics'=>'存档统计','Archive Status'=>'存档状态','Archive Tools'=>'存档工具','Archive your content using multiple archive services with advanced caching.'=>'使用具有高级缓存功能的多种存档服务来存档您的内容。','Archiver'=>'时光机','Archiver Settings'=>'时光机设置','Archives'=>'存档','Are you sure you want to clear all cache?'=>'您确定要清除所有缓存吗?','Are you sure?'=>'您确定吗?','Automatic Archiving'=>'自动存档','Automation'=>'自动化','Automation Settings'=>'自动化设置','Automation settings saved successfully.'=>'自动化设置已成功保存。','Batch Processing Size'=>'批量处理大小','Bulk Archive'=>'批量存档','Bulk archive operations and manual archiving.'=>'批量存档操作和手动存档。','Cache Actions'=>'缓存操作','Cache Performance'=>'缓存性能','Cache preheating started for %d posts.'=>'已开始为 %d 个文章进行缓存预热。','Cache Settings'=>'缓存设置','Cache settings saved successfully.'=>'缓存设置已成功保存。','Cache Size'=>'缓存大小','Cache table is missing. Please deactivate and reactivate the plugin to fix this issue.'=>'缓存表缺失。请停用并重新激活该插件以修复此问题。','Cached URLs'=>'缓存的 URL','Caching reduces API calls and improves page load times'=>'缓存减少 API 调用并缩短页面加载时间','Checking...'=>'检查...','Clear Cache'=>'清除缓存','Clear Queue'=>'清除队列','Configure automatic archiving and content types.'=>'配置自动存档和内容类型。','Configure caching to improve performance.'=>'配置缓存以提高性能。','Configure queue size and batch processing settings.'=>'配置队列大小和批处理设置。','Configure which archive services to use for your content.'=>'配置用于您的内容的存档服务。','Connection failed'=>'连接失败','Connection successful'=>'连接成功','Content Types'=>'内容类型','Content types saved successfully.'=>'内容类型保存成功。','Current Queue Status'=>'当前队列状态','Daily'=>'每天','Document'=>'文档','Enable automatic archiving of content'=>'启用内容自动存档','Enable Cache'=>'启用缓存','Enable caching to improve performance and reduce API calls.'=>'启用缓存以提高性能并减少 API 调用。','Enabled'=>'已启用','Enter URL to archive...'=>'输入要存档的 URL...','Error clearing cache: '=>'清除缓存时出错:','Every 15 Minutes'=>'每15分钟','Every 30 Minutes'=>'每30分钟','Exactly Every Hour'=>'每小时','Execute'=>'执行','Failed'=>'失败','Failed Snapshots'=>'失败快照','Failed to trigger snapshot.'=>'无法触发快照。','General Settings'=>'常规设置','Hourly'=>'每小时','How often should the plugin process pending archives?'=>'插件应该多久处理一次待处理的档案?','http://wenpai.org/plugins/wp-archiver'=>'http://wenpai.org/plugins/wp-archiver','https://wenpai.org/'=>'https://wenpai.org/','Invalid service'=>'无效服务','Invalid URL'=>'无效的网址','Invalid URL.'=>'无效的 URL。','Last Run'=>'上次运行','Last Run:'=>'上次运行:','Loading snapshots...'=>'正在加载快照...','Loading...'=>'加载中...','Manual Archive'=>'手册存档','Manual update function not available.'=>'手动更新功能不可用。','Maximum number of URLs that can be queued for archiving. Higher values use more memory.'=>'可排队等待归档的 URL 数量上限。值越高,占用的内存越多。','Maximum Queue Size'=>'最大队列大小','Metric'=>'指标','Never'=>'从未','Next Run'=>'下次运行','Next Run:'=>'下次运行:','No archives yet.'=>'尚无档案。','No archives yet. A snapshot request has been scheduled.'=>'尚无存档。已安排快照请求。','No pending URLs in the queue.'=>'队列中没有待处理的 URL。','Not available'=>'无法使用','Not scheduled'=>'无计划任务','Number of URLs to process at once during scheduled tasks. Higher values process faster but use more resources.'=>'计划任务期间一次处理的 URL 数量。值越高,处理速度越快,但消耗的资源也越多。','Offline'=>'离线','Online'=>'在线','Overview of your archive activity and performance.'=>'您的存档活动和性能的概述。','Pending'=>'待处理','Pending URLs'=>'待定 URL','Performance'=>'性能调整','Performance Settings'=>'性能设置','Performance settings saved successfully.'=>'性能设置保存成功。','Performance Tip:'=>'性能提示:','Permission denied'=>'无权限','Permission denied.'=>'没有权限。','Please enable at least one archive service.'=>'请至少启用一项存档服务。','Please enter a valid URL'=>'请输入有效的 URL','Please enter a valid URL.'=>'请输入有效的 URL。','Please select a valid action.'=>'请选择有效的批量操作。','Please select at least one post type.'=>'请选择至少一种文章类型。','Post Types to Archive'=>'要存档的内容类型','Preheat Cache'=>'预热缓存','Preheat cache for recent posts or clear all cached data.'=>'预热最近文章的缓存或清除所有缓存数据。','Primary'=>'主要','Primary service must be enabled.'=>'必须启用主要服务。','Process failed'=>'进程失败','Process Queue'=>'进程队列','Process Queue Now'=>'立即处理队列','Processing started for %d URLs.'=>'已开始处理 %d 个 URL。','Processing...'=>'队列处理中...','Queue cleared successfully.'=>'队列清除成功。','Queue is nearly full. Consider increasing the maximum queue size or processing more URLs per batch.'=>'队列几乎已满。请考虑增加最大队列大小或每批次处理更多 URL。','Queue Management'=>'队列管理','Queue processing not available'=>'队列处理不可用','Recent Queue Items'=>'最近的队列项目','Refresh Stats'=>'刷新统计数据','Request failed'=>'请求失败','Save Automation Settings'=>'保存自动化设置','Save Cache Settings'=>'保存缓存设置','Save Content Types'=>'保存内容类型','Save Performance Settings'=>'保存性能设置','Save Services'=>'保存服务','Security check failed.'=>'安全检查失败。','Select action...'=>'选择批量操作...','Select which content types should be automatically archived.'=>'选择应自动存档的内容类型。','Service'=>'服务','Service is operational'=>'服务已运行','Settings'=>'设置','Settings saved successfully.'=>'设置保存成功。','Show queue details'=>'显示队列详细信息','Snapshot request recorded.'=>'快照请求已记录。','Snapshot request submitted and will be processed shortly.'=>'快照请求已提交,将很快处理。','Snapshot triggered successfully!'=>'快照触发成功!','Started processing %d URLs from queue'=>'已开始处理队列中的 %d 个 URL','Status'=>'状态','Support'=>'支持','Test Connection'=>'测试连接','Test failed'=>'测试失败','Testing...'=>'正在测试...','There are no archives of this URL.'=>'此 URL 没有存档。','Total Archived'=>'总存档','Total Pending:'=>'待处理总数:','Trigger Snapshot'=>'触发快照','Triggering snapshot...'=>'正在触发快照...','Twice Daily'=>'每天两次','Update Frequency'=>'更新频率','URL added to archive queue: %s'=>'已将 URL 添加到存档队列:%s','URLs'=>'网址','URLs pending'=>'网址待定','Value'=>'数值','Version: %s'=>'版本:%s','View all snapshots'=>'查看所有快照 ↗','View and manage the archive queue.'=>'查看和管理存档队列。','View on %s'=>'查看 %s','Warning:'=>'警告:','Weekly'=>'每周','WenPai.org'=>'文派开源','WP Archiver'=>'文派时光机','WP Archiver requires PHP 5.6 or higher.'=>'WP Archiver 需要 PHP 5.6 或更高版本。','WP Archiver requires WordPress 4.9 or higher.'=>'WP Archiver 需要 WordPress 4.9 或更高版本。','You do not have sufficient permissions to access this page.'=>'您没有足够的权限访问此页面。']]; diff --git a/languages/archiver-zh_CN.mo b/languages/archiver-zh_CN.mo index 520a51e03c6d169c0a3d818d24f195ef0a92d591..02d6c97e2897d99e0af993a64c66dd7a3c240600 100644 GIT binary patch literal 12858 zcma)=4Rl<^b$}<$A2L(;S~@Dc)nz^-K*J4S{QV{GhTgJKz*geLB4_eom3+Ew=L z$`&b!{44oi#x^##j4dPoV_9Hh%aUcCBqwRhNe`#zByDNaBu(^oS8ZByD4aBHN&DTI zd2d&;nzBb%-Gi#pfmDkB}GMtJLf8o)7Sk&wpI2)NSxL(1D-6 zPN~nrIdBSG0e>GJfc&YKEx!lfPx(jiCisuG{5F)K-}@n@_){O|--qF+;YZ+Ikg3#z z@EW)j-T)J}z75_?c{_~2vrzi|6O?)0gg3(L8T2!78hi*o2CsrWkUw?WmWSc(l>Y+C zd~d*yLK$=txE@ORCMfHk20sQLhaZGrhoZ+fq4a+NGK3m{vW_1>8RsA1t*`(;4XJ_ltT)8H-e3vd>!hvL7(Q2cumejI)WVq*20zf?uD9=xLYe0r6gzz%%KrF~eg77e^}ieEh<~omJazCM=t0r956b&~D1Lbr z@~8fee=^=%5Ea!`7~yVsGj!lrq1fY_Q2fvXWxP{R;^r(A`+pZooV*HUzx_QF{ol6y zAd3{eZh)f4olyGCf&8gQ`1c-|hT>NbO8Yh_@w5X<`#n(feICj@=b_B|nteY4KTG*n zFake;6J)*fpy>Yyl=e&EEEt0_?g1$4JORbdufo#rVR!gB%0FN-8TZ#v#=VK8I0G(% zVuy85{J#rI|D#am8-S>yzHQ4tgR;J&<(ro8B6&*vwU#$n-Ue~Gnr_Qqf)bZsv-LSB z^LIk==OJ4^WceC=i2Apn?8DhCQrbTRMZd=(uT-6_Z-=7C9w_a5Y<(}3ymlFi{eK3f z-M>TWFUeNQw^&ZKyw7qWjPm}=Q0&nG=fh)A;`di@D*P>!d8c4(8E=;5d?@og3Z?&( zkU#Y-|HMDvhSL6*mcOxlA4x#$_faT%PloS<_ggN2(ys=}KKm*ZJzAjXaRSOdJ8R$n z6_j`>SiS+J-EX0cd)4iR*FhQg<52u{vn}5ZuckZ?%6U=)b^Jhy_gzr>9kG1Lz8{7% z&a1ZGw|oPNJ^lyE`fkJ+HE;?Peb!iRgfea?l=%)=o`#r84MEvYK9qJ>-C^2&45lgH z0%g2imiwXfI}EReufR$0hfwXHlyfu50vreKzY9$iak=c{4Gc-Qd?~ONn1Yz#jdZwN8!(G zc?Q8M^$VelQwL?-ysckj>-Soovb+qX-CslL{}anMq1bEEWTWp?DC@u5mKQ*2Ujt>G z^_Jg)xJ2!>@4pLgrTiL{b;~71_6{caF%^;grslPhsb?nqR$*En-I}U zJ{<=7o^2mhj)xwEV?cja@c&NxoPme!v*0Bpj)jwy1gmsYW&+^3gE{ z9i$DB&sUK@(@*B?lE;;{d@r1CpHmRC>w5{|pg#@#UxYl1$a(Nh@i@-!m*XfyI& zWIM7G*@7HI63BZH`5Z!?LQW&QkR-Af`3CX~BA?yJ&B!Jx@cA0jiaa0%J~akvF+7Gm zhe&*X9cf0cK|DnEjC>wP?m(6y68F7`&JXbY$U0;#@XG@#YUDmd zKGz~o=qHoQpO@!b5>7JhG{yM$^}L&R?{rK}T`az$A)Cpk6Q*7oHfOw?=fn~TH{s+m zPAnU5Ot!e@eN-A`-ME|1z0-E7Rwv(_h~*gV{~6hHlAg0NlUoUdnr#*m(0MqsGJLLevn@&5mMhY+X0I`mtEkDva!D_jjE}9#my1g>nN+!ihE8+7 zE|v5egQbU~*T<46(cGH0KA+ZWh^3ObRwtdwIk6U6#OhM4*0xDDHMxl-X2hN=8{ISp zO6SB4*eskTY@JCvHD>Ox${4>sn`v@VF)!y#t8x;t*0R3FtK+>DcK57>98Zisv1y&w zOgloK7jJZG zvKfguS$J*oNmt#UPpz~}88jFgi=lsIcj>ZL`U8a9~c zIte#M+_n&udJWlfF~=@!PURcOH+ZE!xys2kG64PauAMpIJi2(Hr?n6r*{^v_=g97k z>)kDTKH1cq&9qS69BXi$R3=7BN;a`i=QO%;$rB_8HAl9)qjj~ZB=48_K$p#6L zSm<|Sewk2)5B5;5wb}I;DC`h7(J0DW6&(B(*uJu*PN$BC-SvodIt)-c2!p`Bm3>w= zCW)0G62c9^WO=MAUaH(kx@VePNU%6IbD(KZQ%-WCzfK8DWI16;FK)t^os%cWvT?Mz zoLUkb+3W!_Ppf($6VEsCPR&hg?;Gpd!bIlG)CblKy8Lb%+K3USmwg_!eG@R}W+g%@ zjv(>%+-w%}=xr`elsVjLN6l?CU2LI-g_lrh*(7R;(gc$!_Mwd=izKyULBz zSj{up*ubd`Grz6CExBy6p#gn7`!1>;bkpqLR5?$`h}dm@W~Gy<#|sXpM?OWkmUn`U z3L+_;&=F`-fp?!;kZvLO>m5<~91ROLM$|&d+>7(+@S$2QWK+m!fo`6d$yUnaB9mE6 z!gGV7Ig*{=b#u`xDxBHj+Q}m9 zM@-Zh^H4~%H?X{|JawfMc2y6LTXMz;T*=xqBx5I$&uSk8 zvpKn#x5690_MlfiUc@4G`CQo?S=S?i$Qn`gNPT@OnReA9X#)(?l5{1mOkM~Qhfcn7 zV~|ufCOy@d2ve3O(^eiH70=}muB=>=Y_6tznDT!2;YUM#HjtnaZaT@;=8BrAvWJFi z5(<`+LqzO%#oeHyt=K6hv14~fILNpcVHeL$kt)MJVxvs@ino=M%W>#PlBAV9>g*|L zC)hGI<|MTzOin@*-D3C4FfP-rKXl7IplMJ?JIO!{K-@>A>WAk^%>+PVlB;;w!Ux$HATEge7^(O*-mu z%y`KJj!CBDoca2;BRi?dCJnu#%#nySY|x8(df)54*DT>LM`fT;MXRy*-^J$3hS6j} z&TaD4Vz-{%(x?wCDKQ@wwAPUC1Ws_zBOb#Gl769&vGH|dPEFmcGVsR^P#b(vsO9X) z%I0Is$5+_dqpH@1p9wqcZ7a-?AGcH4BiWn~xIR2kx%jZ1&AC7%*x3Tg9W1;z#!^(< zi*fKU;VEC+$eCn(l5lgJb)E{&xG-N5!kH{z_?Xfjap5a3U)Pk($@Njv%*rHpYc#h0 zqLy{uHp;WrxX_qeGRSfHMdb65Bh6~)=^r?HKaD*+|0mgiTJjB-7#hB zYk6nGdI?92CEmEwM*@u>PPzZ>!+g$Dd19 z@;%Wpmn4S$KfWhdJGXdBR;K?2C%$@Ae-U=(>0>G#Z&gd>P*h9ZtVfp8ot8?bmoG5k zd9F+q#&6QQR-anI8K7+zHBE?iW?fbjtT9{pto@l}-#9h%Yn!UHH+f}q#I8N7q?I?e znWW1nH~=CG5|PlX6(*Uq*p;w~EbTB^^`lO&RV`o5T_=1NUy5CD}JZEbG`WTST2{PL-^>HA8iMD)tGBa z*;e||w9=2^u*=h#OtYCYy~-4(nR%8kPg`q*ZCKgDOgvL|?$qeaXw@u-aGk-E_@S{m zGBY|YS`|sSEft2~WQ{l0VD(59cAx0Q+*R>Zp5!bIJ{N6DrlU`IljMguzw7u&|KRA} zgMP;zBJTA|TTG?jxy|3+7uHMDk^YkeWbxD?|H4a!3xocqLw-k}e|m4B`+3vu7j0{7 zyVCHELf64}+PcuyCLQ*lD?HOz+_&x>bn_hl!jX}ouEOc({r-Oa^WMnd5r6w8+5LqB z-TDWGJF6vRbl5=x~Rg&wnEp&;-#I`$x1pp{Y?i8{p&}c?U4nT1*zhx z&HmQ2wqQ``+UjrWwhjE@Q^obiY@s-Ku+$s8DQ-GnXj>gLyKux`f58^a%*^R;-{JSK zHR}u-_Pkg+cO)omI#^meG}6D--+qQAD1XCxzxSf)g=5C*X2#lcevGcA;dX!9p!`7R z_nZiA8t5^Ry~B61NMkW$8ZpYq(6E?t{RJ#HdTMWR;Fz_aR+RDmo_)n*TPy8UIB>)k z*BqAydI>UC0ae#;EOZT&pGv)#i$lFu1680ycdy^OuXJ>^@yi&Ob?$ln(!p{g-;_)3 zC;d&wtqp8p%QGr`vQvyJhyFJU*6MA%INHA5?^##u+~D^d^ZT|FF}g}e`j~==DF5j# zg^ioT*<}!89UE}A&~^-;+Ba4kYeX}P#KomH|Jde{{s9T&aJ!X;_m{f2n6{&b*A>^E zE>B?wnqV2Gd8zw&@zQ|b^CCS;XD;}wyTpwb&yTimWv>;6+Y09fOJ|N0wm&^Gc%B_J zGTiI;Y%QGb8Qs%6dU$Vn9z5u97qRO57^rWD{>{a1vWw@7mrfK9o*wOfdGx?xQbTEI z2uF|ZIj0KGw-pW^k?#%0nMN~PFo{EruHQ-kRQeF3nvf~JIOspOSu`eKUdgx1oO$(GzGIK?2a_L zjVX-ZIsVX2f8$o8mCX!=GlbU(#r7w2D!)^)F>Y?NjJDWxC(7Vf1OX+L`Uss>;vo2%54FjuDm0 z8ppsU#loILg|oX$$Bs(I(s@$r)8Ah@w$4Vr*uajXW6vMhMI4evMlP;54jbujE4KZNy$>5GrzbVcXMG6;E%JI?*x!0c0>gyo>Alor9zDmF_QIidH8QxZ&~=gpZLVBh zxge|a`}T<`wBe+yDVwFpbc9InMzk|4CqoooTqEtw0^|nzQa35T(hHR)KD*l-3}SB% zK)-`D!o8qdRTj7ikOr(fby^*}c+sPB*}BPS+d+TesH}PT6t)-{+E6&(QEJ;m*V4=D z3Im-Z7Y~*#Qan0X92izOQeZc=*~{n9=eQ&SohRsCIJ%}Z+^3BZ#Fmov_3qON&a@+| z6?T)lbf+L>b%Cr?>>1FJQ}J^At{0;pSc^}UF`y~Pq}aIBewOTM_9kW-?Y?+L-l37+ zv)JAQy4FqlURiBN9c|lK*nA@FV9qRa0*z^5W@lc`YjXoBXV}UVQJH;}Id*K4G6j2S z(-%U_YTgw(p1JbGCdLa}dqoLunoObm;q{~4gMk60sxsy7FCO1);xD*_nTXI=RI;$V z53){hWSW#=Pt71ggIy>mx!ua9s@U62l?`&Muf7F}j<#3(p^=N*rdBN9wzl-_ z4*&3T9kEuZ0T!|2`R~oCgLP> zSKPf=Oyz{q+YW7jNo^*!N*Y+bLN65dUS^vavy{#pEe>uN8SXBuK11xtz1!M@v3mRb zq1`gexEJixk%1mbqkS9v%N@KmZmDRh&nI)9zkZ3R92-n#ZsLX&RRop_4)I-Q3R_mI z!poaWXLg&bfh|}s$ybxEg=Sg(>7?2mN3f}+6M8vf_ti+g3FHZN8pOoabbMA8a?qBhHiZjahk)x}_AC+V; z?LQ}9GxYp2&IGN;{dM9K^{cN0)ks dx%?*6VrY^eA_9s$BqAUn7;32%Yq_gJYKmsjx*=SZjkw6YAfGmThIxsCf{#%8wM-Q3mPOt>^nxvjgivrWzH`*ZK%pFQ(@pVv9( zp68tNoaa2xJzIORJajE7_MjoP5#xx;SYzJ7-9tH%o)~V7ht)UnE%4t0m4Scd7Ci7GF5u5vbDG4;=&CUy$%VSIC$j0Q?zeMVyzY63HG2+l>_VL57| zl{gWraTd0rmihwfk^J73zd#Q26(_Ci_pY4EV7F1u!_YJ`#bh+&)u;iQkU5zUYT&(? zjE7Mxa0a<)^De5Ri>QfSLbd-CHSkrOf!|;T=J3FEV-KPxyd;VBR|k(#F%_#(6Zi$H zgJZ~H-r_VFKf*Np7S-W!9;j9#54CcOPy_hzZVaFXI*lH_jm*VdM(wHaDAr#C#c_LD zqEu7|rOr8+LU}Rj!ZoP&PvdOdg&N=@s@=z^73f2~O*c_D5YOz=F%t`LCVIFgL`EyH z1J%)9)CwFzJ(A<7C4U3(brUvqwqn$Y*C3EaU+*JY#LmSW_MHj7a0Dp8NhhiVsk zl8icDj~b}M{m_Y%D8Gq?_!(-VaqKX)8-dzXRq59d%?W>>D zI32Gbdn{x|vhKPd3pKzr)XZnQausStRyqC7jn19U7m&mJ%6T5OiT{Fn6koaep%loP zY%JINUq)sa6}wRvynvd>Yp4&+8CU-Y)I_eM+TV2b|8@1l*pX_Vf@(L*InTMwxeAN; z-H-G1{=Y&-n=Xt~@!zNca#;p-G}U=OYJdvlFzYzw<33co9_MeImr*xz-T9@f{|Ba0 z{{x0JU<&Vo2Aq$2y_TXbdQkEnGl_E2C7_$>aY|wu?5Z=%%!~1{oaXc-{brcHKE@+|A<<#FOWCU z+(JFN;Vg%4Bs7|gF33d*r~xmb-uG*$ zJrYl6>Zc4f-cOLt8#0w-wEIKIVfJv+_n;eN@DZYh7)aB|JVEIDu17ME9woDZc$z37 zYKf^tG4UX=o>)RCwGnF~Ii7!zOe3L|Kr|C82&IP! zy{79__`jqN(KE!)2z_p~DV0VL+K5jPv)%Y+8--G0AXSjjE`5@?-Bk{9K8}-#R$>mZ zj8Lj10>pG;C9#OmE0;$ojU=`c&#Ex7)@$?!f&rVK&~86U*kxBH=6Clbw#L|XNx$r# zJ}M)|wk5B&*`tr%*6OQi4*Kl4l-KR$lqt!+U{hUp zzS`z`U#;y-U10x{I&z3tS=Ttz)}^hmSJFO<^%nZq+fUQ8?BKD5=}W6yeBM%DQ%hZq zFJN3L7_4hpW9N?>o8a@R>+8KhLv>?dt)E#|kGq-}^n1)Y5WOZhkY}qha#DlMT;1H* z=x+*|N?#!8J>YL>@YMwEhZ%3#&Es?Jx$*6Gab|Da46nF&X35>%vpqY=es^bweQm-9 zTa=yM-JCr?#@?EE#CGL;YcEc!uyb;E57~L5|JZIjI`5?YIIqNR$xpXu@>j<9cOC29 zao|QzPw%c{)=r+4($(QDTT)^AHl66-@?PKGec|J0Z=UP0MFkahf5D}s@TvCRJ>9*> zw}$s^|MKwB-W`W*d*MWTp)e!u#`%|f&m8VMvWKorw9w!6e)zpEd$njxO5f4W@R9bu zotyfO?Cd>%D!lE4cYXUId#mVdVz}o;EyIm>Pew1c=cl9}DE^)OQ*n+>E;(qsOWOVi D{4l)K diff --git a/languages/archiver-zh_CN.po b/languages/archiver-zh_CN.po index de388ba..8613b23 100644 --- a/languages/archiver-zh_CN.po +++ b/languages/archiver-zh_CN.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: WP Archiver\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-06 09:41+0800\n" -"PO-Revision-Date: 2025-05-27 00:18+0800\n" +"PO-Revision-Date: 2025-05-28 14:29+0800\n" "Last-Translator: \n" "Language-Team: 简体中文\n" "Language: zh_CN\n" @@ -22,83 +22,105 @@ msgstr "" "X-Poedit-SearchPath-0: .\n" "X-Poedit-SearchPathExcluded-0: *.min.js\n" -#: includes/class-archiver-admin.php:969 +#: includes/class-archiver-admin.php:393 +msgid "%d in main queue, %d in background queue" +msgstr "%d 在主队列中,%d 在后台队列中" + +#: includes/class-archiver-admin.php:1270 msgid "%d posts added to archive queue." msgstr "%d 个文章已添加到存档队列。" -#: includes/class-archiver-admin.php:1007 +#: includes/class-archiver-admin.php:1308 msgid "%d recent posts added to archive queue." msgstr "%d 个近期文章已添加到存档队列。" -#: includes/class-archiver-admin.php:1040 +#: includes/class-archiver-admin.php:1341 msgid "%d recently updated posts added to archive queue." msgstr "%d 个最近更新的文章已添加到存档队列。" -#: includes/class-archiver-admin.php:1172 +#: includes/class-archiver-admin.php:1458 +#, php-format msgid "%s is working correctly" msgstr "%s 工作正常" -#: includes/class-archiver-admin.php:83 +#: includes/class-archiver-dashboard.php:142 +msgid "... and %d more" +msgstr "... 以及 %d 条" + +#: includes/class-archiver-admin.php:118 msgid "Actions" msgstr "操作" -#: includes/class-archiver-admin.php:1108 +#: includes/class-archiver-admin.php:1418 msgid "All cache cleared successfully." msgstr "所有缓存已成功清除。" -#: includes/class-archiver-admin.php:749 +#: includes/class-archiver-admin.php:972 msgid "and %d more" msgstr "以及 %d 个" -#: includes/class-archiver-admin.php:686 +#: includes/class-archiver-admin.php:905 msgid "API Calls Saved" msgstr "API 调用已保存" -#: includes/class-archiver-admin.php:199 +#: includes/class-archiver-admin.php:232 msgid "Archive" msgstr "存档" -#: includes/class-archiver-admin.php:179 +#: includes/class-archiver-admin.php:212 msgid "Archive all published posts" msgstr "存档所有已发布文章" -#: includes/class-archiver-admin.php:258 +#: includes/class-archiver-dashboard.php:320 +msgid "Archive failed" +msgstr "存档失败" + +#: includes/class-archiver-dashboard.php:491 +msgid "Archive functionality not available" +msgstr "存档功能不可用" + +#: includes/class-archiver-admin.php:302 msgid "Archive immediately when content is published" msgstr "内容发布后立即存档" -#: includes/class-archiver.php:191 +#: includes/class-archiver-dashboard.php:110 +#: includes/class-archiver-dashboard.php:327 includes/class-archiver.php:301 msgid "Archive Now" msgstr "立即存档" -#: includes/class-archiver-admin.php:248 +#: includes/class-archiver-admin.php:292 msgid "Archive on Publish" msgstr "发布时存档" -#: includes/class-archiver-admin.php:180 +#: includes/class-archiver-admin.php:213 msgid "Archive posts from last 30 days" msgstr "存档过去 30 天的文章" -#: includes/class-archiver-admin.php:181 +#: includes/class-archiver-admin.php:214 msgid "Archive recently updated posts" msgstr "存档最近更新的文章" -#: includes/class-archiver-admin.php:68 +#: includes/class-archiver-admin.php:103 msgid "Archive Services" msgstr "存档服务" -#: includes/class-archiver-admin.php:889 +#: includes/class-archiver-admin.php:1186 msgid "Archive services saved successfully." msgstr "存档服务保存成功。" -#: includes/class-archiver.php:149 +#: includes/class-archiver.php:260 msgid "Archive Snapshots" msgstr "存档快照" -#: includes/class-archiver-admin.php:339 +#: includes/class-archiver-admin.php:465 msgid "Archive Statistics" msgstr "存档统计" -#: includes/class-archiver-admin.php:168 +#: includes/class-archiver-dashboard.php:33 +msgid "Archive Status" +msgstr "存档状态" + +#: includes/class-archiver-admin.php:201 msgid "Archive Tools" msgstr "存档工具" @@ -107,167 +129,212 @@ msgid "" "Archive your content using multiple archive services with advanced caching." msgstr "使用具有高级缓存功能的多种存档服务来存档您的内容。" -#: includes/class-archiver-admin.php:39 includes/class-archiver.php:27 -#: includes/class-archiver.php:340 +#: includes/class-archiver-admin.php:40 includes/class-archiver.php:27 +#: includes/class-archiver.php:479 msgid "Archiver" msgstr "时光机" -#: includes/class-archiver-admin.php:38 +#: includes/class-archiver-admin.php:39 msgid "Archiver Settings" msgstr "时光机设置" -#: includes/class-archiver.php:109 includes/class-archiver.php:130 -#: includes/class-archiver.php:146 +#: includes/class-archiver.php:212 includes/class-archiver.php:241 +#: includes/class-archiver.php:257 msgid "Archives" msgstr "存档" -#: includes/class-archiver-admin.php:316 +#: includes/class-archiver-admin.php:444 msgid "Are you sure you want to clear all cache?" msgstr "您确定要清除所有缓存吗?" -#: includes/class-archiver-admin.php:727 +#: includes/class-archiver-admin.php:946 msgid "Are you sure?" msgstr "您确定吗?" -#: includes/class-archiver-admin.php:233 +#: includes/class-archiver-admin.php:277 msgid "Automatic Archiving" msgstr "自动存档" -#: includes/class-archiver-admin.php:152 +#: includes/class-archiver-admin.php:184 msgid "Automation" msgstr "自动化" -#: includes/class-archiver-admin.php:229 +#: includes/class-archiver-admin.php:271 msgid "Automation Settings" msgstr "自动化设置" -#: includes/class-archiver-admin.php:174 +#: includes/class-archiver-admin.php:1118 +msgid "Automation settings saved successfully." +msgstr "自动化设置已成功保存。" + +#: includes/class-archiver-admin.php:368 +msgid "Batch Processing Size" +msgstr "批量处理大小" + +#: includes/class-archiver-admin.php:207 msgid "Bulk Archive" msgstr "批量存档" -#: includes/class-archiver-admin.php:170 +#: includes/class-archiver-admin.php:203 msgid "Bulk archive operations and manual archiving." msgstr "批量存档操作和手动存档。" -#: includes/class-archiver-admin.php:310 +#: includes/class-archiver-admin.php:438 msgid "Cache Actions" msgstr "缓存操作" -#: includes/class-archiver-admin.php:678 +#: includes/class-archiver-admin.php:897 msgid "Cache Performance" msgstr "缓存性能" -#: includes/class-archiver-admin.php:1059 +#: includes/class-archiver-admin.php:1362 msgid "Cache preheating started for %d posts." msgstr "已开始为 %d 个文章进行缓存预热。" -#: includes/class-archiver-admin.php:155 includes/class-archiver-admin.php:289 +#: includes/class-archiver-admin.php:187 includes/class-archiver-admin.php:415 msgid "Cache Settings" msgstr "缓存设置" -#: includes/class-archiver-admin.php:690 +#: includes/class-archiver-admin.php:1149 +msgid "Cache settings saved successfully." +msgstr "缓存设置已成功保存。" + +#: includes/class-archiver-admin.php:909 msgid "Cache Size" msgstr "缓存大小" -#: includes/class-archiver-admin.php:1134 +#: includes/class-archiver-admin.php:68 msgid "" "Cache table is missing. Please deactivate and reactivate the plugin to fix " "this issue." msgstr "缓存表缺失。请停用并重新激活该插件以修复此问题。" -#: includes/class-archiver-admin.php:682 +#: includes/class-archiver-admin.php:901 msgid "Cached URLs" msgstr "缓存的 URL" -#: includes/class-archiver-admin.php:305 +#: includes/class-archiver-admin.php:433 msgid "Caching reduces API calls and improves page load times" msgstr "缓存减少 API 调用并缩短页面加载时间" -#: includes/class-archiver-admin.php:95 includes/class-archiver.php:473 +#: includes/class-archiver-admin.php:130 includes/class-archiver.php:612 msgid "Checking..." msgstr "检查..." -#: includes/class-archiver-admin.php:317 +#: includes/class-archiver-admin.php:445 msgid "Clear Cache" msgstr "清除缓存" -#: includes/class-archiver-admin.php:728 +#: includes/class-archiver-admin.php:947 msgid "Clear Queue" msgstr "清除队列" -#: includes/class-archiver-admin.php:139 +#: includes/class-archiver-admin.php:174 msgid "Configure automatic archiving and content types." msgstr "配置自动存档和内容类型。" -#: includes/class-archiver-admin.php:291 +#: includes/class-archiver-admin.php:417 msgid "Configure caching to improve performance." msgstr "配置缓存以提高性能。" -#: includes/class-archiver-admin.php:70 +#: includes/class-archiver-admin.php:347 +msgid "Configure queue size and batch processing settings." +msgstr "配置队列大小和批处理设置。" + +#: includes/class-archiver-admin.php:105 msgid "Configure which archive services to use for your content." msgstr "配置用于您的内容的存档服务。" -#: includes/class-archiver.php:477 +#: includes/class-archiver.php:616 msgid "Connection failed" msgstr "连接失败" -#: includes/class-archiver.php:476 +#: includes/class-archiver.php:615 msgid "Connection successful" msgstr "连接成功" -#: includes/class-archiver-admin.php:149 +#: includes/class-archiver-admin.php:181 msgid "Content Types" msgstr "内容类型" -#: includes/class-archiver-admin.php:273 +#: includes/class-archiver-admin.php:1090 +msgid "Content types saved successfully." +msgstr "内容类型保存成功。" + +#: includes/class-archiver-admin.php:383 +msgid "Current Queue Status" +msgstr "当前队列状态" + +#: includes/class-archiver-admin.php:323 msgid "Daily" msgstr "每天" -#: includes/class-archiver-admin.php:62 +#: includes/class-archiver-admin.php:95 msgid "Document" msgstr "文档" -#: includes/class-archiver-admin.php:243 +#: includes/class-archiver-admin.php:287 msgid "Enable automatic archiving of content" msgstr "启用内容自动存档" -#: includes/class-archiver-admin.php:295 +#: includes/class-archiver-admin.php:423 msgid "Enable Cache" msgstr "启用缓存" -#: includes/class-archiver-admin.php:1123 +#: includes/class-archiver-admin.php:57 msgid "Enable caching to improve performance and reduce API calls." msgstr "启用缓存以提高性能并减少 API 调用。" -#: includes/class-archiver-admin.php:81 +#: includes/class-archiver-admin.php:116 msgid "Enabled" msgstr "已启用" -#: includes/class-archiver-admin.php:196 +#: includes/class-archiver-admin.php:229 +#: includes/class-archiver-dashboard.php:108 msgid "Enter URL to archive..." msgstr "输入要存档的 URL..." -#: includes/class-archiver-admin.php:184 +#: includes/class-archiver-admin.php:1420 +msgid "Error clearing cache: " +msgstr "清除缓存时出错:" + +#: includes/class-archiver-admin.php:311 includes/class-archiver.php:185 +msgid "Every 15 Minutes" +msgstr "每15分钟" + +#: includes/class-archiver-admin.php:314 includes/class-archiver.php:191 +msgid "Every 30 Minutes" +msgstr "每30分钟" + +#: includes/class-archiver.php:197 +msgid "Exactly Every Hour" +msgstr "每小时" + +#: includes/class-archiver-admin.php:217 msgid "Execute" msgstr "执行" -#: includes/class-archiver-admin.php:638 +#: includes/class-archiver-dashboard.php:91 +msgid "Failed" +msgstr "失败" + +#: includes/class-archiver-admin.php:826 msgid "Failed Snapshots" msgstr "失败快照" -#: includes/class-archiver.php:261 includes/class-archiver.php:470 +#: includes/class-archiver.php:609 msgid "Failed to trigger snapshot." msgstr "无法触发快照。" -#: includes/class-archiver-admin.php:137 +#: includes/class-archiver-admin.php:172 msgid "General Settings" msgstr "常规设置" -#: includes/class-archiver-admin.php:267 +#: includes/class-archiver-admin.php:317 msgid "Hourly" msgstr "每小时" -#: includes/class-archiver-admin.php:280 +#: includes/class-archiver-admin.php:330 msgid "How often should the plugin process pending archives?" msgstr "插件应该多久处理一次待处理的档案?" @@ -279,275 +346,416 @@ msgstr "http://wenpai.org/plugins/wp-archiver" msgid "https://wenpai.org/" msgstr "https://wenpai.org/" -#: includes/class-archiver-admin.php:1163 +#: includes/class-archiver-admin.php:1449 msgid "Invalid service" msgstr "无效服务" -#: includes/class-archiver.php:206 +#: includes/class-archiver.php:316 msgid "Invalid URL" msgstr "无效的网址" -#: includes/class-archiver.php:237 +#: includes/class-archiver.php:355 msgid "Invalid URL." msgstr "无效的 URL。" -#: includes/class-archiver-admin.php:642 +#: includes/class-archiver-admin.php:830 msgid "Last Run" msgstr "上次运行" -#: includes/class-archiver.php:184 includes/class-archiver.php:467 +#: includes/class-archiver-dashboard.php:97 +msgid "Last Run:" +msgstr "上次运行:" + +#: includes/class-archiver.php:295 includes/class-archiver.php:606 msgid "Loading snapshots..." msgstr "正在加载快照..." -#: includes/class-archiver.php:348 +#: includes/class-archiver.php:487 msgid "Loading..." msgstr "加载中..." -#: includes/class-archiver-admin.php:190 +#: includes/class-archiver-admin.php:223 msgid "Manual Archive" msgstr "手册存档" -#: includes/class-archiver-admin.php:624 +#: includes/class-archiver-admin.php:1204 +msgid "Manual update function not available." +msgstr "手动更新功能不可用。" + +#: includes/class-archiver-admin.php:363 +msgid "" +"Maximum number of URLs that can be queued for archiving. Higher values use " +"more memory." +msgstr "可排队等待归档的 URL 数量上限。值越高,占用的内存越多。" + +#: includes/class-archiver-admin.php:353 +msgid "Maximum Queue Size" +msgstr "最大队列大小" + +#: includes/class-archiver-admin.php:812 msgid "Metric" msgstr "指标" -#: includes/class-archiver-admin.php:618 +#: includes/class-archiver-admin.php:795 +#: includes/class-archiver-dashboard.php:64 msgid "Never" msgstr "从未" -#: includes/class-archiver-admin.php:646 +#: includes/class-archiver-admin.php:834 msgid "Next Run" msgstr "下次运行" -#: includes/class-archiver.php:471 +#: includes/class-archiver-dashboard.php:101 +msgid "Next Run:" +msgstr "下次运行:" + +#: includes/class-archiver.php:610 msgid "No archives yet." msgstr "尚无档案。" -#: includes/class-archiver.php:217 +#: includes/class-archiver.php:335 msgid "No archives yet. A snapshot request has been scheduled." msgstr "尚无存档。已安排快照请求。" -#: includes/class-archiver-admin.php:705 +#: includes/class-archiver-admin.php:924 msgid "No pending URLs in the queue." msgstr "队列中没有待处理的 URL。" -#: includes/class-archiver-admin.php:649 +#: includes/class-archiver-admin.php:847 +#: includes/class-archiver-dashboard.php:413 +msgid "Not available" +msgstr "无法使用" + +#: includes/class-archiver-admin.php:804 +#: includes/class-archiver-dashboard.php:75 includes/class-archiver.php:1087 msgid "Not scheduled" msgstr "无计划任务" -#: includes/class-archiver.php:475 +#: includes/class-archiver-admin.php:378 +msgid "" +"Number of URLs to process at once during scheduled tasks. Higher values " +"process faster but use more resources." +msgstr "" +"计划任务期间一次处理的 URL 数量。值越高,处理速度越快,但消耗的资源也越多。" + +#: includes/class-archiver.php:614 msgid "Offline" msgstr "离线" -#: includes/class-archiver-admin.php:563 includes/class-archiver.php:474 +#: includes/class-archiver-admin.php:695 includes/class-archiver.php:613 msgid "Online" msgstr "在线" -#: includes/class-archiver-admin.php:340 +#: includes/class-archiver-admin.php:466 msgid "Overview of your archive activity and performance." msgstr "您的存档活动和性能的概述。" -#: includes/class-archiver-admin.php:634 +#: includes/class-archiver-dashboard.php:87 +msgid "Pending" +msgstr "待处理" + +#: includes/class-archiver-admin.php:822 msgid "Pending URLs" msgstr "待定 URL" -#: includes/class-archiver-admin.php:1122 +#: includes/class-archiver-admin.php:190 +msgid "Performance" +msgstr "性能调整" + +#: includes/class-archiver-admin.php:346 +msgid "Performance Settings" +msgstr "性能设置" + +#: includes/class-archiver-admin.php:1137 +msgid "Performance settings saved successfully." +msgstr "性能设置保存成功。" + +#: includes/class-archiver-admin.php:56 msgid "Performance Tip:" msgstr "性能提示:" -#: includes/class-archiver.php:230 +#: includes/class-archiver-dashboard.php:461 +#: includes/class-archiver-dashboard.php:502 +msgid "Permission denied" +msgstr "无权限" + +#: includes/class-archiver-admin.php:1078 +#: includes/class-archiver-admin.php:1095 +#: includes/class-archiver-admin.php:1123 +#: includes/class-archiver-admin.php:1142 +#: includes/class-archiver-admin.php:1154 +#: includes/class-archiver-admin.php:1164 +#: includes/class-archiver-admin.php:1191 +#: includes/class-archiver-admin.php:1210 +#: includes/class-archiver-admin.php:1348 +#: includes/class-archiver-admin.php:1374 includes/class-archiver.php:348 msgid "Permission denied." msgstr "没有权限。" -#: includes/class-archiver-admin.php:876 +#: includes/class-archiver-admin.php:1173 msgid "Please enable at least one archive service." msgstr "请至少启用一项存档服务。" -#: includes/class-archiver-admin.php:1077 +#: includes/class-archiver-dashboard.php:300 +#: includes/class-archiver-dashboard.php:481 +msgid "Please enter a valid URL" +msgstr "请输入有效的 URL" + +#: includes/class-archiver-admin.php:1381 msgid "Please enter a valid URL." msgstr "请输入有效的 URL。" -#: includes/class-archiver-admin.php:935 +#: includes/class-archiver-admin.php:1236 #| msgid "Please select a valid bulk action." msgid "Please select a valid action." msgstr "请选择有效的批量操作。" -#: includes/class-archiver-admin.php:836 +#: includes/class-archiver-admin.php:1085 msgid "Please select at least one post type." msgstr "请选择至少一种文章类型。" -#: includes/class-archiver-admin.php:208 +#: includes/class-archiver-admin.php:241 #| msgid "Content Types to Archive" msgid "Post Types to Archive" msgstr "要存档的内容类型" -#: includes/class-archiver-admin.php:313 +#: includes/class-archiver-admin.php:441 msgid "Preheat Cache" msgstr "预热缓存" -#: includes/class-archiver-admin.php:320 +#: includes/class-archiver-admin.php:448 msgid "Preheat cache for recent posts or clear all cached data." msgstr "预热最近文章的缓存或清除所有缓存数据。" -#: includes/class-archiver-admin.php:82 +#: includes/class-archiver-admin.php:117 msgid "Primary" msgstr "主要" -#: includes/class-archiver-admin.php:882 +#: includes/class-archiver-admin.php:1179 msgid "Primary service must be enabled." msgstr "必须启用主要服务。" -#: includes/class-archiver-admin.php:719 +#: includes/class-archiver-dashboard.php:351 +msgid "Process failed" +msgstr "进程失败" + +#: includes/class-archiver-dashboard.php:115 +#: includes/class-archiver-dashboard.php:358 +msgid "Process Queue" +msgstr "进程队列" + +#: includes/class-archiver-admin.php:938 msgid "Process Queue Now" msgstr "立即处理队列" -#: includes/class-archiver-admin.php:902 +#: includes/class-archiver-admin.php:1200 msgid "Processing started for %d URLs." msgstr "已开始处理 %d 个 URL。" -#: includes/class-archiver-admin.php:931 +#: includes/class-archiver-dashboard.php:304 +#: includes/class-archiver-dashboard.php:336 +msgid "Processing..." +msgstr "队列处理中..." + +#: includes/class-archiver-admin.php:1232 msgid "Queue cleared successfully." msgstr "队列清除成功。" -#: includes/class-archiver-admin.php:146 includes/class-archiver-admin.php:163 +#: includes/class-archiver-admin.php:398 +msgid "" +"Queue is nearly full. Consider increasing the maximum queue size or " +"processing more URLs per batch." +msgstr "队列几乎已满。请考虑增加最大队列大小或每批次处理更多 URL。" + +#: includes/class-archiver-admin.php:178 includes/class-archiver-admin.php:196 msgid "Queue Management" msgstr "队列管理" -#: includes/class-archiver-admin.php:129 +#: includes/class-archiver-dashboard.php:472 +msgid "Queue processing not available" +msgstr "队列处理不可用" + +#: includes/class-archiver-dashboard.php:127 +msgid "Recent Queue Items" +msgstr "最近的队列项目" + +#: includes/class-archiver-dashboard.php:118 +msgid "Refresh Stats" +msgstr "刷新统计数据" + +#: includes/class-archiver-dashboard.php:324 +#: includes/class-archiver-dashboard.php:355 +msgid "Request failed" +msgstr "请求失败" + +#: includes/class-archiver-admin.php:338 +msgid "Save Automation Settings" +msgstr "保存自动化设置" + +#: includes/class-archiver-admin.php:456 +msgid "Save Cache Settings" +msgstr "保存缓存设置" + +#: includes/class-archiver-admin.php:263 +msgid "Save Content Types" +msgstr "保存内容类型" + +#: includes/class-archiver-admin.php:407 +msgid "Save Performance Settings" +msgstr "保存性能设置" + +#: includes/class-archiver-admin.php:164 msgid "Save Services" msgstr "保存服务" -#: includes/class-archiver-admin.php:331 -msgid "Save Settings" -msgstr "保存设置" - -#: includes/class-archiver-admin.php:776 includes/class-archiver-admin.php:785 -#: includes/class-archiver-admin.php:794 includes/class-archiver-admin.php:803 -#: includes/class-archiver-admin.php:812 includes/class-archiver-admin.php:821 -#: includes/class-archiver.php:226 +#: includes/class-archiver-admin.php:996 includes/class-archiver-admin.php:1005 +#: includes/class-archiver-admin.php:1014 +#: includes/class-archiver-admin.php:1023 +#: includes/class-archiver-admin.php:1032 +#: includes/class-archiver-admin.php:1041 +#: includes/class-archiver-admin.php:1050 +#: includes/class-archiver-admin.php:1060 +#: includes/class-archiver-admin.php:1069 includes/class-archiver.php:344 msgid "Security check failed." msgstr "安全检查失败。" -#: includes/class-archiver.php:319 includes/class-archiver.php:472 -#| msgid "See all snapshots ↗" -msgid "See all snapshots" -msgstr "查看所有快照 ↗" - -#: includes/class-archiver-admin.php:178 +#: includes/class-archiver-admin.php:211 #| msgid "Select bulk action..." msgid "Select action..." msgstr "选择批量操作..." -#: includes/class-archiver-admin.php:209 +#: includes/class-archiver-admin.php:242 msgid "Select which content types should be automatically archived." msgstr "选择应自动存档的内容类型。" -#: includes/class-archiver-admin.php:79 +#: includes/class-archiver-admin.php:114 msgid "Service" msgstr "服务" -#: includes/class-archiver-admin.php:1150 +#: includes/class-archiver-admin.php:1435 msgid "Service is operational" msgstr "服务已运行" -#: wp-archiver.php:243 +#: wp-archiver.php:245 msgid "Settings" msgstr "设置" -#: includes/class-archiver-admin.php:863 +#: includes/class-archiver-admin.php:1159 msgid "Settings saved successfully." msgstr "设置保存成功。" -#: includes/class-archiver-admin.php:742 +#: includes/class-archiver-admin.php:963 msgid "Show queue details" msgstr "显示队列详细信息" -#: includes/class-archiver.php:562 +#: includes/class-archiver.php:709 msgid "Snapshot request recorded." msgstr "快照请求已记录。" -#: includes/class-archiver.php:257 -msgid "Snapshot request submitted to %d service(s)." -msgstr "快照请求已提交给 %d 个服务。" +#: includes/class-archiver.php:384 +msgid "Snapshot request submitted and will be processed shortly." +msgstr "快照请求已提交,将很快处理。" -#: includes/class-archiver.php:469 +#: includes/class-archiver.php:608 msgid "Snapshot triggered successfully!" msgstr "快照触发成功!" -#: includes/class-archiver-admin.php:80 +#: includes/class-archiver-dashboard.php:469 +msgid "Started processing %d URLs from queue" +msgstr "已开始处理队列中的 %d 个 URL" + +#: includes/class-archiver-admin.php:115 msgid "Status" msgstr "状态" -#: includes/class-archiver-admin.php:63 +#: includes/class-archiver-admin.php:96 msgid "Support" msgstr "支持" -#: includes/class-archiver-admin.php:118 -msgid "Test Connect" +#: includes/class-archiver-admin.php:153 +#| msgid "Test Connect" +msgid "Test Connection" msgstr "测试连接" -#: includes/class-archiver-admin.php:599 +#: includes/class-archiver-admin.php:731 msgid "Test failed" msgstr "测试失败" -#: includes/class-archiver-admin.php:576 +#: includes/class-archiver-admin.php:708 msgid "Testing..." msgstr "正在测试..." -#: includes/class-archiver.php:218 +#: includes/class-archiver.php:336 msgid "There are no archives of this URL." msgstr "此 URL 没有存档。" -#: includes/class-archiver-admin.php:630 +#: includes/class-archiver-admin.php:818 +#: includes/class-archiver-dashboard.php:83 msgid "Total Archived" msgstr "总存档" -#: includes/class-archiver-admin.php:712 +#: includes/class-archiver-admin.php:931 msgid "Total Pending:" msgstr "待处理总数:" -#: includes/class-archiver.php:356 +#: includes/class-archiver.php:495 msgid "Trigger Snapshot" msgstr "触发快照" -#: includes/class-archiver.php:468 +#: includes/class-archiver.php:607 msgid "Triggering snapshot..." msgstr "正在触发快照..." -#: includes/class-archiver-admin.php:270 +#: includes/class-archiver-admin.php:320 msgid "Twice Daily" msgstr "每天两次" -#: includes/class-archiver-admin.php:263 +#: includes/class-archiver-admin.php:307 msgid "Update Frequency" msgstr "更新频率" -#: includes/class-archiver-admin.php:1089 +#: includes/class-archiver-admin.php:1398 +#: includes/class-archiver-dashboard.php:488 +#, php-format msgid "URL added to archive queue: %s" msgstr "已将 URL 添加到存档队列:%s" -#: includes/class-archiver-admin.php:713 +#: includes/class-archiver-admin.php:932 msgid "URLs" msgstr "网址" -#: includes/class-archiver-admin.php:625 +#: includes/class-archiver-admin.php:390 +msgid "URLs pending" +msgstr "网址待定" + +#: includes/class-archiver-admin.php:813 msgid "Value" msgstr "数值" -#: includes/class-archiver-admin.php:61 +#: includes/class-archiver-admin.php:94 #, php-format msgid "Version: %s" msgstr "版本:%s" -#: includes/class-archiver-admin.php:164 +#: includes/class-archiver.php:458 includes/class-archiver.php:611 +#| msgid "See all snapshots" +msgid "View all snapshots" +msgstr "查看所有快照 ↗" + +#: includes/class-archiver-admin.php:197 msgid "View and manage the archive queue." msgstr "查看和管理存档队列。" -#: includes/class-archiver.php:369 +#: includes/class-archiver.php:508 +#, php-format msgid "View on %s" msgstr "查看 %s" -#: includes/class-archiver-admin.php:276 +#: includes/class-archiver-admin.php:397 +msgid "Warning:" +msgstr "警告:" + +#: includes/class-archiver-admin.php:326 msgid "Weekly" msgstr "每周" @@ -559,14 +767,14 @@ msgstr "文派开源" msgid "WP Archiver" msgstr "文派时光机" -#: wp-archiver.php:254 +#: wp-archiver.php:256 msgid "WP Archiver requires PHP 5.6 or higher." msgstr "WP Archiver 需要 PHP 5.6 或更高版本。" -#: wp-archiver.php:262 +#: wp-archiver.php:264 msgid "WP Archiver requires WordPress 4.9 or higher." msgstr "WP Archiver 需要 WordPress 4.9 或更高版本。" -#: includes/class-archiver-admin.php:48 +#: includes/class-archiver-admin.php:75 msgid "You do not have sufficient permissions to access this page." msgstr "您没有足够的权限访问此页面。" diff --git a/languages/archiver.pot b/languages/archiver.pot index af1dddc..e574a50 100644 --- a/languages/archiver.pot +++ b/languages/archiver.pot @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: WP Archiver\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-05-26 16:07+0000\n" +"POT-Creation-Date: 2025-05-28 06:23+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: \n" @@ -16,84 +16,105 @@ msgstr "" "X-Loco-Version: 2.6.11; wp-6.7.2\n" "X-Domain: archiver" -#: includes/class-archiver-admin.php:969 +#: includes/class-archiver-admin.php:393 +msgid "%d in main queue, %d in background queue" +msgstr "" + +#: includes/class-archiver-admin.php:1270 msgid "%d posts added to archive queue." msgstr "" -#: includes/class-archiver-admin.php:1007 +#: includes/class-archiver-admin.php:1308 msgid "%d recent posts added to archive queue." msgstr "" -#: includes/class-archiver-admin.php:1040 +#: includes/class-archiver-admin.php:1341 msgid "%d recently updated posts added to archive queue." msgstr "" -#: includes/class-archiver-admin.php:1172 +#: includes/class-archiver-admin.php:1458 #, php-format msgid "%s is working correctly" msgstr "" -#: includes/class-archiver-admin.php:83 +#: includes/class-archiver-dashboard.php:142 +msgid "... and %d more" +msgstr "" + +#: includes/class-archiver-admin.php:118 msgid "Actions" msgstr "" -#: includes/class-archiver-admin.php:1108 +#: includes/class-archiver-admin.php:1418 msgid "All cache cleared successfully." msgstr "" -#: includes/class-archiver-admin.php:749 +#: includes/class-archiver-admin.php:972 msgid "and %d more" msgstr "" -#: includes/class-archiver-admin.php:686 +#: includes/class-archiver-admin.php:905 msgid "API Calls Saved" msgstr "" -#: includes/class-archiver-admin.php:199 +#: includes/class-archiver-admin.php:232 msgid "Archive" msgstr "" -#: includes/class-archiver-admin.php:179 +#: includes/class-archiver-admin.php:212 msgid "Archive all published posts" msgstr "" -#: includes/class-archiver-admin.php:258 +#: includes/class-archiver-dashboard.php:320 +msgid "Archive failed" +msgstr "" + +#: includes/class-archiver-dashboard.php:491 +msgid "Archive functionality not available" +msgstr "" + +#: includes/class-archiver-admin.php:302 msgid "Archive immediately when content is published" msgstr "" -#: includes/class-archiver.php:191 +#: includes/class-archiver-dashboard.php:110 +#: includes/class-archiver-dashboard.php:327 includes/class-archiver.php:301 msgid "Archive Now" msgstr "" -#: includes/class-archiver-admin.php:248 +#: includes/class-archiver-admin.php:292 msgid "Archive on Publish" msgstr "" -#: includes/class-archiver-admin.php:180 +#: includes/class-archiver-admin.php:213 msgid "Archive posts from last 30 days" msgstr "" -#: includes/class-archiver-admin.php:181 +#: includes/class-archiver-admin.php:214 msgid "Archive recently updated posts" msgstr "" -#: includes/class-archiver-admin.php:68 +#: includes/class-archiver-admin.php:103 msgid "Archive Services" msgstr "" -#: includes/class-archiver-admin.php:889 +#: includes/class-archiver-admin.php:1186 msgid "Archive services saved successfully." msgstr "" -#: includes/class-archiver.php:149 +#: includes/class-archiver.php:260 msgid "Archive Snapshots" msgstr "" -#: includes/class-archiver-admin.php:339 +#: includes/class-archiver-admin.php:465 msgid "Archive Statistics" msgstr "" -#: includes/class-archiver-admin.php:168 +#: includes/class-archiver-dashboard.php:33 +msgid "Archive Status" +msgstr "" + +#: includes/class-archiver-admin.php:201 msgid "Archive Tools" msgstr "" @@ -102,167 +123,212 @@ msgid "" "Archive your content using multiple archive services with advanced caching." msgstr "" -#: includes/class-archiver-admin.php:39 includes/class-archiver.php:27 -#: includes/class-archiver.php:340 +#: includes/class-archiver-admin.php:40 includes/class-archiver.php:27 +#: includes/class-archiver.php:479 msgid "Archiver" msgstr "" -#: includes/class-archiver-admin.php:38 +#: includes/class-archiver-admin.php:39 msgid "Archiver Settings" msgstr "" -#: includes/class-archiver.php:109 includes/class-archiver.php:130 -#: includes/class-archiver.php:146 +#: includes/class-archiver.php:212 includes/class-archiver.php:241 +#: includes/class-archiver.php:257 msgid "Archives" msgstr "" -#: includes/class-archiver-admin.php:316 +#: includes/class-archiver-admin.php:444 msgid "Are you sure you want to clear all cache?" msgstr "" -#: includes/class-archiver-admin.php:727 +#: includes/class-archiver-admin.php:946 msgid "Are you sure?" msgstr "" -#: includes/class-archiver-admin.php:233 +#: includes/class-archiver-admin.php:277 msgid "Automatic Archiving" msgstr "" -#: includes/class-archiver-admin.php:152 +#: includes/class-archiver-admin.php:184 msgid "Automation" msgstr "" -#: includes/class-archiver-admin.php:229 +#: includes/class-archiver-admin.php:271 msgid "Automation Settings" msgstr "" -#: includes/class-archiver-admin.php:174 +#: includes/class-archiver-admin.php:1118 +msgid "Automation settings saved successfully." +msgstr "" + +#: includes/class-archiver-admin.php:368 +msgid "Batch Processing Size" +msgstr "" + +#: includes/class-archiver-admin.php:207 msgid "Bulk Archive" msgstr "" -#: includes/class-archiver-admin.php:170 +#: includes/class-archiver-admin.php:203 msgid "Bulk archive operations and manual archiving." msgstr "" -#: includes/class-archiver-admin.php:310 +#: includes/class-archiver-admin.php:438 msgid "Cache Actions" msgstr "" -#: includes/class-archiver-admin.php:678 +#: includes/class-archiver-admin.php:897 msgid "Cache Performance" msgstr "" -#: includes/class-archiver-admin.php:1059 +#: includes/class-archiver-admin.php:1362 msgid "Cache preheating started for %d posts." msgstr "" -#: includes/class-archiver-admin.php:155 includes/class-archiver-admin.php:289 +#: includes/class-archiver-admin.php:187 includes/class-archiver-admin.php:415 msgid "Cache Settings" msgstr "" -#: includes/class-archiver-admin.php:690 +#: includes/class-archiver-admin.php:1149 +msgid "Cache settings saved successfully." +msgstr "" + +#: includes/class-archiver-admin.php:909 msgid "Cache Size" msgstr "" -#: includes/class-archiver-admin.php:1134 +#: includes/class-archiver-admin.php:68 msgid "" "Cache table is missing. Please deactivate and reactivate the plugin to fix " "this issue." msgstr "" -#: includes/class-archiver-admin.php:682 +#: includes/class-archiver-admin.php:901 msgid "Cached URLs" msgstr "" -#: includes/class-archiver-admin.php:305 +#: includes/class-archiver-admin.php:433 msgid "Caching reduces API calls and improves page load times" msgstr "" -#: includes/class-archiver-admin.php:95 includes/class-archiver.php:473 +#: includes/class-archiver-admin.php:130 includes/class-archiver.php:612 msgid "Checking..." msgstr "" -#: includes/class-archiver-admin.php:317 +#: includes/class-archiver-admin.php:445 msgid "Clear Cache" msgstr "" -#: includes/class-archiver-admin.php:728 +#: includes/class-archiver-admin.php:947 msgid "Clear Queue" msgstr "" -#: includes/class-archiver-admin.php:139 +#: includes/class-archiver-admin.php:174 msgid "Configure automatic archiving and content types." msgstr "" -#: includes/class-archiver-admin.php:291 +#: includes/class-archiver-admin.php:417 msgid "Configure caching to improve performance." msgstr "" -#: includes/class-archiver-admin.php:70 +#: includes/class-archiver-admin.php:347 +msgid "Configure queue size and batch processing settings." +msgstr "" + +#: includes/class-archiver-admin.php:105 msgid "Configure which archive services to use for your content." msgstr "" -#: includes/class-archiver.php:477 +#: includes/class-archiver.php:616 msgid "Connection failed" msgstr "" -#: includes/class-archiver.php:476 +#: includes/class-archiver.php:615 msgid "Connection successful" msgstr "" -#: includes/class-archiver-admin.php:149 +#: includes/class-archiver-admin.php:181 msgid "Content Types" msgstr "" -#: includes/class-archiver-admin.php:273 +#: includes/class-archiver-admin.php:1090 +msgid "Content types saved successfully." +msgstr "" + +#: includes/class-archiver-admin.php:383 +msgid "Current Queue Status" +msgstr "" + +#: includes/class-archiver-admin.php:323 msgid "Daily" msgstr "" -#: includes/class-archiver-admin.php:62 +#: includes/class-archiver-admin.php:95 msgid "Document" msgstr "" -#: includes/class-archiver-admin.php:243 +#: includes/class-archiver-admin.php:287 msgid "Enable automatic archiving of content" msgstr "" -#: includes/class-archiver-admin.php:295 +#: includes/class-archiver-admin.php:423 msgid "Enable Cache" msgstr "" -#: includes/class-archiver-admin.php:1123 +#: includes/class-archiver-admin.php:57 msgid "Enable caching to improve performance and reduce API calls." msgstr "" -#: includes/class-archiver-admin.php:81 +#: includes/class-archiver-admin.php:116 msgid "Enabled" msgstr "" -#: includes/class-archiver-admin.php:196 +#: includes/class-archiver-admin.php:229 +#: includes/class-archiver-dashboard.php:108 msgid "Enter URL to archive..." msgstr "" -#: includes/class-archiver-admin.php:184 +#: includes/class-archiver-admin.php:1420 +msgid "Error clearing cache: " +msgstr "" + +#: includes/class-archiver-admin.php:311 includes/class-archiver.php:185 +msgid "Every 15 Minutes" +msgstr "" + +#: includes/class-archiver-admin.php:314 includes/class-archiver.php:191 +msgid "Every 30 Minutes" +msgstr "" + +#: includes/class-archiver.php:197 +msgid "Exactly Every Hour" +msgstr "" + +#: includes/class-archiver-admin.php:217 msgid "Execute" msgstr "" -#: includes/class-archiver-admin.php:638 +#: includes/class-archiver-dashboard.php:91 +msgid "Failed" +msgstr "" + +#: includes/class-archiver-admin.php:826 msgid "Failed Snapshots" msgstr "" -#: includes/class-archiver.php:261 includes/class-archiver.php:470 +#: includes/class-archiver.php:609 msgid "Failed to trigger snapshot." msgstr "" -#: includes/class-archiver-admin.php:137 +#: includes/class-archiver-admin.php:172 msgid "General Settings" msgstr "" -#: includes/class-archiver-admin.php:267 +#: includes/class-archiver-admin.php:317 msgid "Hourly" msgstr "" -#: includes/class-archiver-admin.php:280 +#: includes/class-archiver-admin.php:330 msgid "How often should the plugin process pending archives?" msgstr "" @@ -274,273 +340,410 @@ msgstr "" msgid "https://wenpai.org/" msgstr "" -#: includes/class-archiver-admin.php:1163 +#: includes/class-archiver-admin.php:1449 msgid "Invalid service" msgstr "" -#: includes/class-archiver.php:206 +#: includes/class-archiver.php:316 msgid "Invalid URL" msgstr "" -#: includes/class-archiver.php:237 +#: includes/class-archiver.php:355 msgid "Invalid URL." msgstr "" -#: includes/class-archiver-admin.php:642 +#: includes/class-archiver-admin.php:830 msgid "Last Run" msgstr "" -#: includes/class-archiver.php:184 includes/class-archiver.php:467 +#: includes/class-archiver-dashboard.php:97 +msgid "Last Run:" +msgstr "" + +#: includes/class-archiver.php:295 includes/class-archiver.php:606 msgid "Loading snapshots..." msgstr "" -#: includes/class-archiver.php:348 +#: includes/class-archiver.php:487 msgid "Loading..." msgstr "" -#: includes/class-archiver-admin.php:190 +#: includes/class-archiver-admin.php:223 msgid "Manual Archive" msgstr "" -#: includes/class-archiver-admin.php:624 +#: includes/class-archiver-admin.php:1204 +msgid "Manual update function not available." +msgstr "" + +#: includes/class-archiver-admin.php:363 +msgid "" +"Maximum number of URLs that can be queued for archiving. Higher values use " +"more memory." +msgstr "" + +#: includes/class-archiver-admin.php:353 +msgid "Maximum Queue Size" +msgstr "" + +#: includes/class-archiver-admin.php:812 msgid "Metric" msgstr "" -#: includes/class-archiver-admin.php:618 +#: includes/class-archiver-admin.php:795 +#: includes/class-archiver-dashboard.php:64 msgid "Never" msgstr "" -#: includes/class-archiver-admin.php:646 +#: includes/class-archiver-admin.php:834 msgid "Next Run" msgstr "" -#: includes/class-archiver.php:471 +#: includes/class-archiver-dashboard.php:101 +msgid "Next Run:" +msgstr "" + +#: includes/class-archiver.php:610 msgid "No archives yet." msgstr "" -#: includes/class-archiver.php:217 +#: includes/class-archiver.php:335 msgid "No archives yet. A snapshot request has been scheduled." msgstr "" -#: includes/class-archiver-admin.php:705 +#: includes/class-archiver-admin.php:924 msgid "No pending URLs in the queue." msgstr "" -#: includes/class-archiver-admin.php:649 +#: includes/class-archiver-admin.php:847 +#: includes/class-archiver-dashboard.php:413 +msgid "Not available" +msgstr "" + +#: includes/class-archiver-admin.php:804 +#: includes/class-archiver-dashboard.php:75 includes/class-archiver.php:1087 msgid "Not scheduled" msgstr "" -#: includes/class-archiver.php:475 +#: includes/class-archiver-admin.php:378 +msgid "" +"Number of URLs to process at once during scheduled tasks. Higher values " +"process faster but use more resources." +msgstr "" + +#: includes/class-archiver.php:614 msgid "Offline" msgstr "" -#: includes/class-archiver-admin.php:563 includes/class-archiver.php:474 +#: includes/class-archiver-admin.php:695 includes/class-archiver.php:613 msgid "Online" msgstr "" -#: includes/class-archiver-admin.php:340 +#: includes/class-archiver-admin.php:466 msgid "Overview of your archive activity and performance." msgstr "" -#: includes/class-archiver-admin.php:634 +#: includes/class-archiver-dashboard.php:87 +msgid "Pending" +msgstr "" + +#: includes/class-archiver-admin.php:822 msgid "Pending URLs" msgstr "" -#: includes/class-archiver-admin.php:1122 +#: includes/class-archiver-admin.php:190 +msgid "Performance" +msgstr "" + +#: includes/class-archiver-admin.php:346 +msgid "Performance Settings" +msgstr "" + +#: includes/class-archiver-admin.php:1137 +msgid "Performance settings saved successfully." +msgstr "" + +#: includes/class-archiver-admin.php:56 msgid "Performance Tip:" msgstr "" -#: includes/class-archiver.php:230 +#: includes/class-archiver-dashboard.php:461 +#: includes/class-archiver-dashboard.php:502 +msgid "Permission denied" +msgstr "" + +#: includes/class-archiver-admin.php:1078 +#: includes/class-archiver-admin.php:1095 +#: includes/class-archiver-admin.php:1123 +#: includes/class-archiver-admin.php:1142 +#: includes/class-archiver-admin.php:1154 +#: includes/class-archiver-admin.php:1164 +#: includes/class-archiver-admin.php:1191 +#: includes/class-archiver-admin.php:1210 +#: includes/class-archiver-admin.php:1348 +#: includes/class-archiver-admin.php:1374 includes/class-archiver.php:348 msgid "Permission denied." msgstr "" -#: includes/class-archiver-admin.php:876 +#: includes/class-archiver-admin.php:1173 msgid "Please enable at least one archive service." msgstr "" -#: includes/class-archiver-admin.php:1077 +#: includes/class-archiver-dashboard.php:300 +#: includes/class-archiver-dashboard.php:481 +msgid "Please enter a valid URL" +msgstr "" + +#: includes/class-archiver-admin.php:1381 msgid "Please enter a valid URL." msgstr "" -#: includes/class-archiver-admin.php:935 +#: includes/class-archiver-admin.php:1236 msgid "Please select a valid action." msgstr "" -#: includes/class-archiver-admin.php:836 +#: includes/class-archiver-admin.php:1085 msgid "Please select at least one post type." msgstr "" -#: includes/class-archiver-admin.php:208 +#: includes/class-archiver-admin.php:241 msgid "Post Types to Archive" msgstr "" -#: includes/class-archiver-admin.php:313 +#: includes/class-archiver-admin.php:441 msgid "Preheat Cache" msgstr "" -#: includes/class-archiver-admin.php:320 +#: includes/class-archiver-admin.php:448 msgid "Preheat cache for recent posts or clear all cached data." msgstr "" -#: includes/class-archiver-admin.php:82 +#: includes/class-archiver-admin.php:117 msgid "Primary" msgstr "" -#: includes/class-archiver-admin.php:882 +#: includes/class-archiver-admin.php:1179 msgid "Primary service must be enabled." msgstr "" -#: includes/class-archiver-admin.php:719 +#: includes/class-archiver-dashboard.php:351 +msgid "Process failed" +msgstr "" + +#: includes/class-archiver-dashboard.php:115 +#: includes/class-archiver-dashboard.php:358 +msgid "Process Queue" +msgstr "" + +#: includes/class-archiver-admin.php:938 msgid "Process Queue Now" msgstr "" -#: includes/class-archiver-admin.php:902 +#: includes/class-archiver-admin.php:1200 msgid "Processing started for %d URLs." msgstr "" -#: includes/class-archiver-admin.php:931 +#: includes/class-archiver-dashboard.php:304 +#: includes/class-archiver-dashboard.php:336 +msgid "Processing..." +msgstr "" + +#: includes/class-archiver-admin.php:1232 msgid "Queue cleared successfully." msgstr "" -#: includes/class-archiver-admin.php:146 includes/class-archiver-admin.php:163 +#: includes/class-archiver-admin.php:398 +msgid "" +"Queue is nearly full. Consider increasing the maximum queue size or " +"processing more URLs per batch." +msgstr "" + +#: includes/class-archiver-admin.php:178 includes/class-archiver-admin.php:196 msgid "Queue Management" msgstr "" -#: includes/class-archiver-admin.php:129 -msgid "Save Services" +#: includes/class-archiver-dashboard.php:472 +msgid "Queue processing not available" msgstr "" -#: includes/class-archiver-admin.php:331 -msgid "Save Settings" +#: includes/class-archiver-dashboard.php:127 +msgid "Recent Queue Items" msgstr "" -#: includes/class-archiver-admin.php:776 includes/class-archiver-admin.php:785 -#: includes/class-archiver-admin.php:794 includes/class-archiver-admin.php:803 -#: includes/class-archiver-admin.php:812 includes/class-archiver-admin.php:821 -#: includes/class-archiver.php:226 -msgid "Security check failed." +#: includes/class-archiver-dashboard.php:118 +msgid "Refresh Stats" msgstr "" -#: includes/class-archiver.php:319 includes/class-archiver.php:472 -msgid "See all snapshots" +#: includes/class-archiver-dashboard.php:324 +#: includes/class-archiver-dashboard.php:355 +msgid "Request failed" msgstr "" -#: includes/class-archiver-admin.php:178 -msgid "Select action..." +#: includes/class-archiver-admin.php:338 +msgid "Save Automation Settings" msgstr "" -#: includes/class-archiver-admin.php:209 -msgid "Select which content types should be automatically archived." -msgstr "" - -#: includes/class-archiver-admin.php:79 -msgid "Service" -msgstr "" - -#: includes/class-archiver-admin.php:1150 -msgid "Service is operational" -msgstr "" - -#: wp-archiver.php:243 -msgid "Settings" -msgstr "" - -#: includes/class-archiver-admin.php:863 -msgid "Settings saved successfully." -msgstr "" - -#: includes/class-archiver-admin.php:742 -msgid "Show queue details" -msgstr "" - -#: includes/class-archiver.php:562 -msgid "Snapshot request recorded." -msgstr "" - -#: includes/class-archiver.php:257 -msgid "Snapshot request submitted to %d service(s)." -msgstr "" - -#: includes/class-archiver.php:469 -msgid "Snapshot triggered successfully!" -msgstr "" - -#: includes/class-archiver-admin.php:80 -msgid "Status" -msgstr "" - -#: includes/class-archiver-admin.php:63 -msgid "Support" -msgstr "" - -#: includes/class-archiver-admin.php:118 -msgid "Test Connect" -msgstr "" - -#: includes/class-archiver-admin.php:599 -msgid "Test failed" -msgstr "" - -#: includes/class-archiver-admin.php:576 -msgid "Testing..." -msgstr "" - -#: includes/class-archiver.php:218 -msgid "There are no archives of this URL." -msgstr "" - -#: includes/class-archiver-admin.php:630 -msgid "Total Archived" -msgstr "" - -#: includes/class-archiver-admin.php:712 -msgid "Total Pending:" -msgstr "" - -#: includes/class-archiver.php:356 -msgid "Trigger Snapshot" -msgstr "" - -#: includes/class-archiver.php:468 -msgid "Triggering snapshot..." -msgstr "" - -#: includes/class-archiver-admin.php:270 -msgid "Twice Daily" +#: includes/class-archiver-admin.php:456 +msgid "Save Cache Settings" msgstr "" #: includes/class-archiver-admin.php:263 +msgid "Save Content Types" +msgstr "" + +#: includes/class-archiver-admin.php:407 +msgid "Save Performance Settings" +msgstr "" + +#: includes/class-archiver-admin.php:164 +msgid "Save Services" +msgstr "" + +#: includes/class-archiver-admin.php:996 includes/class-archiver-admin.php:1005 +#: includes/class-archiver-admin.php:1014 +#: includes/class-archiver-admin.php:1023 +#: includes/class-archiver-admin.php:1032 +#: includes/class-archiver-admin.php:1041 +#: includes/class-archiver-admin.php:1050 +#: includes/class-archiver-admin.php:1060 +#: includes/class-archiver-admin.php:1069 includes/class-archiver.php:344 +msgid "Security check failed." +msgstr "" + +#: includes/class-archiver-admin.php:211 +msgid "Select action..." +msgstr "" + +#: includes/class-archiver-admin.php:242 +msgid "Select which content types should be automatically archived." +msgstr "" + +#: includes/class-archiver-admin.php:114 +msgid "Service" +msgstr "" + +#: includes/class-archiver-admin.php:1435 +msgid "Service is operational" +msgstr "" + +#: wp-archiver.php:245 +msgid "Settings" +msgstr "" + +#: includes/class-archiver-admin.php:1159 +msgid "Settings saved successfully." +msgstr "" + +#: includes/class-archiver-admin.php:963 +msgid "Show queue details" +msgstr "" + +#: includes/class-archiver.php:709 +msgid "Snapshot request recorded." +msgstr "" + +#: includes/class-archiver.php:384 +msgid "Snapshot request submitted and will be processed shortly." +msgstr "" + +#: includes/class-archiver.php:608 +msgid "Snapshot triggered successfully!" +msgstr "" + +#: includes/class-archiver-dashboard.php:469 +msgid "Started processing %d URLs from queue" +msgstr "" + +#: includes/class-archiver-admin.php:115 +msgid "Status" +msgstr "" + +#: includes/class-archiver-admin.php:96 +msgid "Support" +msgstr "" + +#: includes/class-archiver-admin.php:153 +msgid "Test Connection" +msgstr "" + +#: includes/class-archiver-admin.php:731 +msgid "Test failed" +msgstr "" + +#: includes/class-archiver-admin.php:708 +msgid "Testing..." +msgstr "" + +#: includes/class-archiver.php:336 +msgid "There are no archives of this URL." +msgstr "" + +#: includes/class-archiver-admin.php:818 +#: includes/class-archiver-dashboard.php:83 +msgid "Total Archived" +msgstr "" + +#: includes/class-archiver-admin.php:931 +msgid "Total Pending:" +msgstr "" + +#: includes/class-archiver.php:495 +msgid "Trigger Snapshot" +msgstr "" + +#: includes/class-archiver.php:607 +msgid "Triggering snapshot..." +msgstr "" + +#: includes/class-archiver-admin.php:320 +msgid "Twice Daily" +msgstr "" + +#: includes/class-archiver-admin.php:307 msgid "Update Frequency" msgstr "" -#: includes/class-archiver-admin.php:1089 +#: includes/class-archiver-admin.php:1398 +#: includes/class-archiver-dashboard.php:488 #, php-format msgid "URL added to archive queue: %s" msgstr "" -#: includes/class-archiver-admin.php:713 +#: includes/class-archiver-admin.php:932 msgid "URLs" msgstr "" -#: includes/class-archiver-admin.php:625 +#: includes/class-archiver-admin.php:390 +msgid "URLs pending" +msgstr "" + +#: includes/class-archiver-admin.php:813 msgid "Value" msgstr "" -#: includes/class-archiver-admin.php:61 +#: includes/class-archiver-admin.php:94 #, php-format msgid "Version: %s" msgstr "" -#: includes/class-archiver-admin.php:164 +#: includes/class-archiver.php:458 includes/class-archiver.php:611 +msgid "View all snapshots" +msgstr "" + +#: includes/class-archiver-admin.php:197 msgid "View and manage the archive queue." msgstr "" -#: includes/class-archiver.php:369 +#: includes/class-archiver.php:508 #, php-format msgid "View on %s" msgstr "" -#: includes/class-archiver-admin.php:276 +#: includes/class-archiver-admin.php:397 +msgid "Warning:" +msgstr "" + +#: includes/class-archiver-admin.php:326 msgid "Weekly" msgstr "" @@ -552,14 +755,14 @@ msgstr "" msgid "WP Archiver" msgstr "" -#: wp-archiver.php:254 +#: wp-archiver.php:256 msgid "WP Archiver requires PHP 5.6 or higher." msgstr "" -#: wp-archiver.php:262 +#: wp-archiver.php:264 msgid "WP Archiver requires WordPress 4.9 or higher." msgstr "" -#: includes/class-archiver-admin.php:48 +#: includes/class-archiver-admin.php:75 msgid "You do not have sufficient permissions to access this page." msgstr "" diff --git a/wp-archiver.php b/wp-archiver.php index 3fda0e4..44ba838 100644 --- a/wp-archiver.php +++ b/wp-archiver.php @@ -16,13 +16,13 @@ if (!defined('ABSPATH')) { exit; } -// 定义常量 +// Define constants define('ARCHIVER_VERSION', '2.0.0'); define('ARCHIVER_PLUGIN_DIR_URL', plugin_dir_url(__FILE__)); define('ARCHIVER_PLUGIN_DIR_PATH', plugin_dir_path(__FILE__)); define('ARCHIVER_PLUGIN_BASENAME', plugin_basename(__FILE__)); -// 定义存档服务 - 修复原始的 wenpai.net 服务 +// Define archive services define('ARCHIVER_SERVICES', array( 'wayback' => array( 'name' => 'Internet Archive', @@ -47,10 +47,10 @@ define('ARCHIVER_SERVICES', array( ) )); -// 插件激活 +// Plugin activation register_activation_hook(__FILE__, 'archiver_activate'); function archiver_activate() { - // 设置默认选项 + // Set default options add_option('archiver_post_types', array('post', 'page')); add_option('archiver_update_frequency', 'daily'); add_option('archiver_urls_to_update', array()); @@ -62,13 +62,13 @@ function archiver_activate() { add_option('archiver_primary_service', 'wenpai'); add_option('archiver_auto_archive', true); add_option('archiver_archive_on_publish', true); - add_option('archiver_max_queue_size', 100); - add_option('archiver_batch_size', 3); + add_option('archiver_max_queue_size', 500); // Increased default + add_option('archiver_batch_size', 10); // Increased default - // 创建缓存表 + // Create cache table archiver_create_cache_table(); - // 安排定时任务 + // Schedule cron tasks if (!wp_next_scheduled('archiver_process_urls')) { wp_schedule_event(time(), 'daily', 'archiver_process_urls'); } @@ -77,11 +77,11 @@ function archiver_activate() { wp_schedule_event(time(), 'daily', 'archiver_cleanup_cache'); } - // 清理缓存 + // Clear cache wp_cache_flush(); } -// 创建缓存表 +// Create cache table function archiver_create_cache_table() { global $wpdb; @@ -111,14 +111,14 @@ function archiver_create_cache_table() { require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); - // 添加索引以提高查询性能 + // Add indexes for better query performance $wpdb->query("CREATE INDEX IF NOT EXISTS idx_status_expires ON {$table_name} (status, expires_at)"); } -// 插件停用 +// Plugin deactivation register_deactivation_hook(__FILE__, 'archiver_deactivate'); function archiver_deactivate() { - // 删除定时任务 + // Remove scheduled tasks $timestamp = wp_next_scheduled('archiver_process_urls'); if ($timestamp) { wp_unschedule_event($timestamp, 'archiver_process_urls'); @@ -129,14 +129,14 @@ function archiver_deactivate() { wp_unschedule_event($timestamp, 'archiver_cleanup_cache'); } - // 清理缓存 + // Clear cache wp_cache_flush(); } -// 插件卸载 +// Plugin uninstall register_uninstall_hook(__FILE__, 'archiver_uninstall'); function archiver_uninstall() { - // 删除选项 + // Delete options $options = array( 'archiver_post_types', 'archiver_update_frequency', @@ -158,45 +158,46 @@ function archiver_uninstall() { delete_option($option); } - // 删除缓存表 + // Delete cache table global $wpdb; $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}archiver_cache"); - // 清理所有transients + // Clean all transients $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_archiver_%'"); $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_timeout_archiver_%'"); } -// 加载文本域 +// Load text domain add_action('init', 'archiver_load_textdomain'); function archiver_load_textdomain() { load_plugin_textdomain('archiver', false, dirname(plugin_basename(__FILE__)) . '/languages/'); } -// 延长 nonce 有效期(仅针对本插件) +// Extend nonce life (for this plugin only) add_filter('nonce_life', function($lifespan) { if (isset($_GET['page']) && $_GET['page'] === 'archiver-settings') { - return DAY_IN_SECONDS; // 24小时 + return DAY_IN_SECONDS; // 24 hours } return $lifespan; }); -// 错误处理 +// Error handling function archiver_handle_error($message, $type = 'error') { if (defined('WP_DEBUG') && WP_DEBUG) { error_log('[WP Archiver] ' . $type . ': ' . $message); } } -// 加载类文件 - 注意顺序很重要! +// Load class files - order is important! function archiver_load_dependencies() { $includes_dir = ARCHIVER_PLUGIN_DIR_PATH . 'includes/'; - // 按正确顺序加载 + // Load in correct order $files = array( - 'class-archiver-cache.php', // 缓存类必须先加载 - 'class-archiver.php', // 主类 - 'class-archiver-admin.php' // 管理类最后加载 + 'class-archiver-cache.php', // Cache class must be loaded first + 'class-archiver.php', // Main class + 'class-archiver-admin.php', // Admin class loaded last + 'class-archiver-dashboard.php' // Dashboard widget ); foreach ($files as $file) { @@ -209,23 +210,24 @@ function archiver_load_dependencies() { } } -// 初始化插件 +// Initialize plugin add_action('plugins_loaded', 'archiver_init', 5); function archiver_init() { - // 加载依赖 + // Load dependencies archiver_load_dependencies(); - // 运行插件 + // Run plugin archiver_run(); } -// 运行插件主逻辑 +// Run main plugin logic function archiver_run() { try { $archiver = Archiver::get_instance(); if (is_admin()) { new Archiver_Admin($archiver); + new Archiver_Dashboard($archiver); } $archiver->run(); @@ -237,7 +239,7 @@ function archiver_run() { } } -// 添加设置链接到插件列表 +// Add settings link to plugin list add_filter('plugin_action_links_' . ARCHIVER_PLUGIN_BASENAME, 'archiver_add_action_links'); function archiver_add_action_links($links) { $settings_link = '' . __('Settings', 'archiver') . ''; @@ -245,7 +247,7 @@ function archiver_add_action_links($links) { return $links; } -// 检查系统要求 +// Check system requirements add_action('admin_init', 'archiver_check_requirements'); function archiver_check_requirements() { if (version_compare(PHP_VERSION, '5.6', '<')) { @@ -265,17 +267,16 @@ function archiver_check_requirements() { } } -// 性能优化:限制队列大小 +// Performance optimization: limit queue size add_filter('archiver_queue_limit', function($limit) { - return get_option('archiver_max_queue_size', 100); + return get_option('archiver_max_queue_size', 500); }); -// 性能优化:批处理大小 +// Performance optimization: batch processing size add_filter('archiver_batch_size', function($size) { - return get_option('archiver_batch_size', 3); + return get_option('archiver_batch_size', 10); }); - // Integrate UpdatePulse Server for updates using PUC v5.3 require_once plugin_dir_path(__FILE__) . 'lib/plugin-update-checker/plugin-update-checker.php'; use YahnisElsts\PluginUpdateChecker\v5p3\PucFactory;