v2.0 稳定版发布

This commit is contained in:
文派备案 2025-05-28 14:44:57 +08:00
parent b2bd73cb85
commit c177f340dc
14 changed files with 2979 additions and 1835 deletions

View file

@ -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(`
<div class="archiver-loading">
<span class="spinner is-active"></span>
${archiver.i18n.loading} (Retry ${retryCount + 1}/${this.config.maxRetries})
</div>
`);
} 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 || '<p class="archiver-no-snapshots">' + archiver.i18n.no_snapshots + '</p>');
const noSnapshotsHtml = `
<div class="archiver-no-snapshots">
<p><em>${archiver.i18n.no_snapshots}</em></p>
<small>Snapshots will appear here after archiving is complete.</small>
</div>
`;
$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('<span class="dashicons dashicons-yes"></span> ' + 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('<span class="dashicons dashicons-warning"></span> ' + response.data.message);
.html('<span class="dashicons dashicons-warning"></span> ' +
(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('<span class="dashicons dashicons-warning"></span> ' + archiver.i18n.error);
.html('<span class="dashicons dashicons-warning"></span> ' + 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 = $('<div class="archiver-polling-indicator">Checking for new archives...</div>');
$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('<p class="archiver-error">' + message + '</p>');
const errorHtml = `
<div class="archiver-error">
<p><span class="dashicons dashicons-warning"></span> ${message}</p>
<button class="button button-small archiver-retry">Try Again</button>
</div>
`;
$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 = $('<div class="notice notice-' + type + ' is-dismissible"><p>' + message + '</p></div>');
const $notice = $(`<div class="notice notice-${type} is-dismissible"><p>${message}</p></div>`);
$('.wrap > h1').after($notice);
// 自动关闭
// Auto dismiss
setTimeout(() => {
$notice.fadeOut(() => $notice.remove());
}, 5000);
} else {
// Frontend notification
const $notification = $(`
<div class="archiver-notification archiver-notification-${type}">
${message}
</div>
`);
$('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(`
<style id="archiver-notification-styles">
.archiver-notification {
position: fixed;
top: 32px;
right: 20px;
background: #fff;
border-left: 4px solid #00a0d2;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
padding: 12px 16px;
border-radius: 3px;
z-index: 999999;
transform: translateX(100%);
transition: transform 0.3s ease;
max-width: 350px;
word-wrap: break-word;
font-size: 13px;
}
.archiver-notification.show {
transform: translateX(0);
}
.archiver-notification-success {
border-left-color: #46b450;
}
.archiver-notification-error {
border-left-color: #dc3232;
}
.archiver-notification-info {
border-left-color: #00a0d2;
}
.archiver-error {
text-align: center;
padding: 20px;
color: #dc3232;
}
.archiver-error .dashicons {
margin-right: 5px;
}
.archiver-retry {
margin-top: 10px;
}
.archiver-polling-indicator {
background: #e7f3ff;
border: 1px solid #b8dcf2;
color: #0073aa;
padding: 8px 12px;
border-radius: 3px;
margin-bottom: 10px;
font-size: 12px;
text-align: center;
}
.archiver-no-snapshots {
text-align: center;
padding: 20px;
color: #646970;
}
.archiver-no-snapshots small {
display: block;
margin-top: 8px;
font-size: 11px;
}
@media (max-width: 782px) {
.archiver-notification {
top: 46px;
right: 10px;
left: 10px;
max-width: none;
transform: translateY(-100%);
}
.archiver-notification.show {
transform: translateY(0);
}
}
</style>
`);
},
/**
* 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);

View file

@ -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;
}

View file

@ -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}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
<?php
/**
* WP Archiver 缓存管理系统
* WP Archiver Cache Management System - Fixed Version
*/
if (!defined('ABSPATH')) {
@ -17,35 +17,69 @@ class Archiver_Cache {
'frozen' => 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();
}
}
});

View file

@ -0,0 +1,508 @@
<?php
/**
* WP Archiver Dashboard Widget
*/
if (!defined('ABSPATH')) {
exit;
}
class Archiver_Dashboard {
protected $archiver;
public function __construct($archiver) {
$this->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');
}
?>
<div class="archiver-dashboard-widget">
<div class="archiver-dashboard-stats">
<div class="archiver-stat-item">
<span class="stat-number"><?php echo number_format($stats['total_archived']); ?></span>
<span class="stat-label"><?php _e('Total Archived', 'archiver'); ?></span>
</div>
<div class="archiver-stat-item">
<span class="stat-number <?php echo $pending_count > 0 ? 'has-pending' : ''; ?>"><?php echo number_format($pending_count); ?></span>
<span class="stat-label"><?php _e('Pending', 'archiver'); ?></span>
</div>
<div class="archiver-stat-item">
<span class="stat-number"><?php echo number_format($stats['failed_snapshots']); ?></span>
<span class="stat-label"><?php _e('Failed', 'archiver'); ?></span>
</div>
</div>
<div class="archiver-dashboard-info">
<p>
<strong><?php _e('Last Run:', 'archiver'); ?></strong>
<?php echo esc_html($last_run_formatted); ?>
</p>
<p>
<strong><?php _e('Next Run:', 'archiver'); ?></strong>
<?php echo esc_html($next_run_formatted); ?>
</p>
</div>
<div class="archiver-dashboard-actions">
<div class="action-group">
<input type="url" id="archiver-dashboard-url" placeholder="<?php esc_attr_e('Enter URL to archive...', 'archiver'); ?>" />
<button type="button" class="button button-primary" id="archiver-dashboard-submit">
<?php _e('Archive Now', 'archiver'); ?>
</button>
</div>
<div class="action-group">
<button type="button" class="button" id="archiver-process-queue">
<?php _e('Process Queue', 'archiver'); ?>
</button>
<button type="button" class="button" id="archiver-refresh-stats">
<?php _e('Refresh Stats', 'archiver'); ?>
</button>
</div>
</div>
<div id="archiver-dashboard-status" class="archiver-status" style="display: none;"></div>
<?php if ($pending_count > 0): ?>
<div class="archiver-queue-preview">
<h4><?php _e('Recent Queue Items', 'archiver'); ?></h4>
<div class="queue-items">
<?php
$queue_items = array_merge(
array_slice(get_option('archiver_urls_to_update', array()), 0, 3),
array_slice(get_option('archiver_background_queue', array()), 0, 3)
);
foreach (array_slice($queue_items, 0, 3) as $item):
$url = is_array($item) ? $item['url'] : $item;
?>
<div class="queue-item"><?php echo esc_html($url); ?></div>
<?php endforeach; ?>
<?php if ($pending_count > 3): ?>
<div class="queue-more">
<?php printf(__('... and %d more', 'archiver'), $pending_count - 3); ?>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
<style>
.archiver-dashboard-widget {
font-size: 13px;
}
.archiver-dashboard-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-bottom: 15px;
}
.archiver-stat-item {
text-align: center;
padding: 10px;
background: #f8f9fa;
border-radius: 4px;
border: 1px solid #e1e4e8;
}
.stat-number {
font-size: 18px;
font-weight: 600;
color: #2271b1;
line-height: 1;
}
.stat-number.has-pending {
color: #d63638;
}
.stat-label {
display: block;
font-size: 11px;
color: #646970;
margin-top: 5px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.archiver-dashboard-info {
background: #fff;
border: 1px solid #ccd0d4;
border-radius: 4px;
padding: 10px;
margin-bottom: 15px;
}
.archiver-dashboard-info p {
margin: 5px 0;
}
.archiver-dashboard-actions {
margin-bottom: 15px;
}
.action-group {
display: flex;
gap: 5px;
margin-bottom: 8px;
}
.action-group:last-child {
margin-bottom: 0;
}
#archiver-dashboard-url {
flex: 1;
font-size: 12px;
height: 28px;
}
.archiver-dashboard-widget .button {
font-size: 11px;
height: 28px;
line-height: 26px;
padding: 0 10px;
}
.archiver-status {
padding: 8px 10px;
border-radius: 3px;
margin-top: 10px;
font-size: 12px;
}
.archiver-status.success {
background: #d7eddb;
border-left: 4px solid #46b450;
color: #155724;
}
.archiver-status.error {
background: #f8d7da;
border-left: 4px solid #dc3545;
color: #721c24;
}
.archiver-status.info {
background: #cce7ff;
border-left: 4px solid #007cba;
color: #004085;
}
.archiver-queue-preview {
border-top: 1px solid #ddd;
padding-top: 15px;
margin-top: 15px;
}
.archiver-queue-preview h4 {
margin: 0 0 10px;
font-size: 12px;
color: #23282d;
}
.queue-item {
background: #f6f7f7;
padding: 6px 8px;
margin-bottom: 3px;
border-radius: 3px;
font-size: 11px;
word-break: break-all;
color: #50575e;
font-family: Consolas, Monaco, monospace;
}
.queue-more {
text-align: center;
font-style: italic;
color: #646970;
font-size: 11px;
margin-top: 5px;
}
.archiver-dashboard-widget .spinner {
float: none;
margin: 0 5px 0 0;
}
</style>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Archive URL
$('#archiver-dashboard-submit').on('click', function() {
const $button = $(this);
const $status = $('#archiver-dashboard-status');
const url = $('#archiver-dashboard-url').val().trim();
if (!url) {
showStatus('<?php echo esc_js(__('Please enter a valid URL', 'archiver')); ?>', 'error');
return;
}
$button.prop('disabled', true).text('<?php echo esc_js(__('Processing...', 'archiver')); ?>');
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'archiver_dashboard_trigger',
url: url,
nonce: '<?php echo wp_create_nonce('archiver_dashboard_nonce'); ?>'
},
success: function(response) {
if (response.success) {
showStatus(response.data.message, 'success');
$('#archiver-dashboard-url').val('');
refreshStats();
} else {
showStatus(response.data.message || '<?php echo esc_js(__('Archive failed', 'archiver')); ?>', 'error');
}
},
error: function() {
showStatus('<?php echo esc_js(__('Request failed', 'archiver')); ?>', 'error');
},
complete: function() {
$button.prop('disabled', false).text('<?php echo esc_js(__('Archive Now', 'archiver')); ?>');
}
});
});
// Process queue
$('#archiver-process-queue').on('click', function() {
const $button = $(this);
$button.prop('disabled', true).text('<?php echo esc_js(__('Processing...', 'archiver')); ?>');
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'archiver_dashboard_trigger',
process_queue: true,
nonce: '<?php echo wp_create_nonce('archiver_dashboard_nonce'); ?>'
},
success: function(response) {
if (response.success) {
showStatus(response.data.message, 'success');
refreshStats();
} else {
showStatus(response.data.message || '<?php echo esc_js(__('Process failed', 'archiver')); ?>', 'error');
}
},
error: function() {
showStatus('<?php echo esc_js(__('Request failed', 'archiver')); ?>', 'error');
},
complete: function() {
$button.prop('disabled', false).text('<?php echo esc_js(__('Process Queue', 'archiver')); ?>');
}
});
});
// Refresh stats
$('#archiver-refresh-stats').on('click', function() {
refreshStats();
});
// Enter key support
$('#archiver-dashboard-url').on('keypress', function(e) {
if (e.which === 13) {
$('#archiver-dashboard-submit').trigger('click');
}
});
function showStatus(message, type) {
const $status = $('#archiver-dashboard-status');
$status.removeClass('success error info')
.addClass(type)
.html(message)
.show();
setTimeout(function() {
$status.fadeOut();
}, 5000);
}
function refreshStats() {
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'archiver_dashboard_stats',
nonce: '<?php echo wp_create_nonce('archiver_dashboard_nonce'); ?>'
},
success: function(response) {
if (response.success) {
location.reload(); // Simple refresh for now
}
}
});
}
});
</script>
<?php
}
/**
* Add new method to handle timestamp timezone issues
*/
private function format_timestamp_with_timezone($timestamp) {
if (!$timestamp) {
return __('Not available', '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);
$datetime->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);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* 主要的 Archiver
* Main Archiver Class
*/
if (!defined('ABSPATH')) {
@ -26,16 +26,49 @@ class Archiver {
public function __construct($args = array()) {
$this->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 {
<div class="archiver-service-tabs">
<?php foreach ($enabled_services as $service_id => $enabled) :
if (!$enabled) continue;
$service = $this->services[$service_id];
$service = isset($this->services[$service_id]) ? $this->services[$service_id] : array('name' => ucfirst($service_id));
?>
<button class="archiver-service-tab <?php echo $service_id === $primary_service ? 'active' : ''; ?>"
data-service="<?php echo esc_attr($service_id); ?>">
@ -187,7 +298,6 @@ class Archiver {
<div class="archiver-actions">
<button id="archiver-immediate-snapshot" class="button button-secondary">
<span class="dashicons dashicons-backup"></span>
<?php _e('Archive Now', 'archiver'); ?>
</button>
<span id="archiver-status" style="margin-left: 10px; display: none;"></span>
@ -195,7 +305,7 @@ class Archiver {
<?php
}
// AJAX 处理方法
// AJAX handler methods
public function ajax_get_snapshots() {
check_ajax_referer('archiver_immediate_snapshot', '_ajax_nonce');
@ -206,7 +316,15 @@ class Archiver {
wp_send_json_error(['message' => __('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 = '<ul class="archiver-snapshot-list">';
$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(
'<li><a href="%s" target="_blank" rel="noopener noreferrer">%s</a></li>',
@ -316,7 +455,7 @@ class Archiver {
$html .= sprintf(
'<a href="%s" target="_blank" rel="noopener noreferrer">%s <span class="dashicons dashicons-external"></span></a>',
esc_url($view_all_url),
__('See all snapshots', 'archiver')
__('View all snapshots', 'archiver')
);
$html .= '</p>';
@ -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);
}
}

View file

@ -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('<p>' + archiver.i18n.no_snapshots + '</p>');
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('<span class="spinner is-active"></span> ' + 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('<span class="dashicons dashicons-yes"></span> ' + response.data.message);
// 清除缓存并开始轮询
this.clearCache(url);
if (response.data.refresh) {
this.startPolling(url);
}
} else {
$status.removeClass('success').addClass('error')
.html('<span class="dashicons dashicons-warning"></span> ' + response.data.message);
}
},
error: () => {
$status.removeClass('success').addClass('error')
.html('<span class="dashicons dashicons-warning"></span> ' + 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 = `
<div class="archiver-loading">
<span class="spinner is-active"></span>
${archiver.i18n.loading}
</div>
`;
$container.html(loadingHtml);
},
/**
* 显示错误
*/
showError: function($container, message) {
$container.html('<p class="archiver-error">' + message + '</p>');
},
/**
* 缓存管理
*/
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);

1
js/archiver.min.js vendored
View file

@ -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(`<style id="archiver-spin-animation">@keyframes archiver-spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}</style>`)}})(jQuery);

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -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 "您没有足够的权限访问此页面。"

View file

@ -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 <EMAIL@ADDRESS>\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 ""

View file

@ -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 = '<a href="' . admin_url('tools.php?page=archiver-settings') . '">' . __('Settings', 'archiver') . '</a>';
@ -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;