diff --git a/assets/js/archiver.js b/assets/js/archiver.js
index deaa2ef..e6a21bc 100644
--- a/assets/js/archiver.js
+++ b/assets/js/archiver.js
@@ -1,5 +1,6 @@
/**
- * WP Archiver 现代化前端脚本
+ * WP Archiver Modern Frontend Script
+ * Complete version with all functionality
*/
(function($) {
@@ -8,15 +9,18 @@
const Archiver = {
config: {
- checkInterval: 5000,
- maxChecks: 12,
+ checkInterval: 3000,
+ maxChecks: 20,
fadeInDuration: 300,
cachePrefix: 'archiver_cache_',
- debounceDelay: 300
+ debounceDelay: 300,
+ retryDelay: 1000,
+ maxRetries: 3
},
currentService: null,
serviceCheckTimers: {},
+ loadingCache: {},
init: function() {
$(document).ready(() => {
@@ -25,11 +29,12 @@
this.initAdminPage();
this.initLazyLoading();
this.initServiceChecks();
+ this.addNotificationStyles();
});
},
/**
- * 初始化元框
+ * Initialize metabox
*/
initMetabox: function() {
const $container = $('#archiver-snapshots');
@@ -42,7 +47,7 @@
this.currentService = service;
- // 服务标签切换
+ // Service tab switching
$('.archiver-service-tab').on('click', (e) => {
e.preventDefault();
const $tab = $(e.currentTarget);
@@ -55,10 +60,10 @@
this.loadSnapshots(url, $container, selectedService);
});
- // 延迟加载快照
+ // Load snapshots with retry mechanism
this.loadSnapshots(url, $container, service);
- // 立即存档按钮
+ // Immediate archive button
$('#archiver-immediate-snapshot').on('click', (e) => {
e.preventDefault();
this.triggerSnapshot(url, this.currentService);
@@ -66,19 +71,21 @@
},
/**
- * 初始化管理栏
+ * Initialize admin bar
*/
initAdminBar: function() {
const $trigger = $('#wp-admin-bar-archiver-trigger a');
if (!$trigger.length) return;
- // 延迟加载快照计数
+ // Load snapshot count with delay
const $countItem = $('#wp-admin-bar-archiver-snapshots');
if ($countItem.length && archiver.url) {
- this.updateAdminBarCount(archiver.url);
+ setTimeout(() => {
+ this.updateAdminBarCount(archiver.url);
+ }, 500);
}
- // 触发快照
+ // Trigger snapshot
$trigger.on('click', (e) => {
e.preventDefault();
this.triggerAdminBarSnapshot();
@@ -86,41 +93,34 @@
},
/**
- * 初始化管理页面
+ * Initialize admin page
*/
initAdminPage: function() {
- // 标签切换
- $('.archiver-tab').on('click', function(e) {
+ // Tab switching
+ $('.settings-tab').on('click', function(e) {
e.preventDefault();
const $tab = $(this);
const tabId = $tab.data('tab');
- $('.archiver-tab').removeClass('active');
+ $('.settings-tab').removeClass('active');
$tab.addClass('active');
- $('.archiver-tab-content').removeClass('active');
- $('#' + tabId).addClass('active');
+ $('.settings-section').hide();
+ $('.settings-section[data-section="' + tabId + '"]').show();
- // 保存用户偏好
+ // Save user preference
if (typeof(Storage) !== "undefined") {
localStorage.setItem('archiver_active_tab', tabId);
}
});
- // 恢复上次的标签
+ // Restore last tab
const savedTab = localStorage.getItem('archiver_active_tab');
- if (savedTab && $(`.archiver-tab[data-tab="${savedTab}"]`).length) {
- $(`.archiver-tab[data-tab="${savedTab}"]`).trigger('click');
+ if (savedTab && $(`.settings-tab[data-tab="${savedTab}"]`).length) {
+ $(`.settings-tab[data-tab="${savedTab}"]`).trigger('click');
}
- // 标签链接
- $('.archiver-tab-link').on('click', function(e) {
- e.preventDefault();
- const tab = $(this).attr('href').replace('#', '');
- $(`.archiver-tab[data-tab="${tab}"]`).trigger('click');
- });
-
- // 测试服务连接
+ // Test service connection
$('.test-service').on('click', (e) => {
e.preventDefault();
const $button = $(e.currentTarget);
@@ -130,48 +130,50 @@
},
/**
- * 初始化服务检查
+ * Initialize service checks
*/
initServiceChecks: function() {
- if ($('.archiver-service-card').length === 0) return;
+ if ($('.service-status').length === 0) return;
- // 检查所有服务状态
- $('.archiver-service-card').each((index, element) => {
- const $card = $(element);
- const service = $card.find('.test-service').data('service');
- if (service) {
- this.checkServiceStatus(service);
+ // Check all service statuses
+ $('.service-status').each((index, element) => {
+ const $status = $(element);
+ const serviceId = $status.attr('id').replace('status-', '');
+ if (serviceId) {
+ setTimeout(() => {
+ this.checkServiceStatus(serviceId);
+ }, index * 200); // Staggered loading
}
});
},
/**
- * 检查服务状态
+ * Check service status
*/
checkServiceStatus: function(service) {
- const $status = $(`#service-status-${service}`);
+ const $status = $(`#status-${service}`);
if (!$status.length) return;
- // 清除现有定时器
+ // Clear existing timer
if (this.serviceCheckTimers[service]) {
clearTimeout(this.serviceCheckTimers[service]);
}
$status.removeClass('online offline').find('.status-text').text(archiver.i18n.checking);
- // 模拟检查延迟
+ // Simulate check with realistic delay
this.serviceCheckTimers[service] = setTimeout(() => {
- // 根据服务配置判断状态
- const isEnabled = archiver.enabled_services[service];
+ // Check if service is enabled
+ const isEnabled = archiver.enabled_services && archiver.enabled_services[service];
const statusClass = isEnabled ? 'online' : 'offline';
const statusText = isEnabled ? archiver.i18n.online : archiver.i18n.offline;
$status.addClass(statusClass).find('.status-text').text(statusText);
- }, 1000);
+ }, 800 + Math.random() * 400);
},
/**
- * 测试服务连接
+ * Test service connection
*/
testServiceConnection: function(service, $button) {
const originalText = $button.text();
@@ -185,6 +187,7 @@
service: service,
nonce: archiver.admin_nonce
},
+ timeout: 10000,
success: (response) => {
if (response.success) {
this.showNotification(response.data.message, 'success');
@@ -193,8 +196,12 @@
this.showNotification(response.data.message || archiver.i18n.test_failed, 'error');
}
},
- error: () => {
- this.showNotification(archiver.i18n.test_failed, 'error');
+ error: (xhr, status, error) => {
+ let message = archiver.i18n.test_failed;
+ if (status === 'timeout') {
+ message = 'Connection timeout - service may be slow';
+ }
+ this.showNotification(message, 'error');
},
complete: () => {
$button.prop('disabled', false).text(originalText);
@@ -203,7 +210,7 @@
},
/**
- * 延迟加载
+ * Initialize lazy loading
*/
initLazyLoading: function() {
if ('IntersectionObserver' in window) {
@@ -228,23 +235,34 @@
},
/**
- * 加载快照
+ * Load snapshots with enhanced error handling
*/
- loadSnapshots: function(url, $container, service = null) {
- // 检查本地缓存
- const cacheKey = this.getCacheKey(url, service);
- const cached = this.getCache(cacheKey);
- if (cached) {
- this.displaySnapshots(cached, $container);
- // 后台检查更新
- this.checkForUpdates(url, $container, service);
- return;
+ loadSnapshots: function(url, $container, service = null, retryCount = 0) {
+ if (retryCount === 0) {
+ // Check local cache first
+ const cacheKey = this.getCacheKey(url, service);
+ const cached = this.getCache(cacheKey);
+ if (cached && cached.html) {
+ this.displaySnapshots(cached, $container);
+ // Background check for updates
+ setTimeout(() => {
+ this.checkForUpdates(url, $container, service);
+ }, 2000);
+ return;
+ }
}
- // 显示加载状态
+ // Prevent multiple simultaneous requests
+ const requestKey = `${url}_${service || 'default'}`;
+ if (this.loadingCache[requestKey]) {
+ return;
+ }
+ this.loadingCache[requestKey] = true;
+
+ // Show loading state
this.showLoading($container);
- // 从服务器获取
+ // Fetch from server
$.ajax({
url: archiver.ajax_url,
type: 'POST',
@@ -254,40 +272,72 @@
url: url,
service: service
},
+ timeout: 15000,
success: (response) => {
+ delete this.loadingCache[requestKey];
+
if (response.success) {
+ const cacheKey = this.getCacheKey(url, service);
this.setCache(cacheKey, response.data);
this.displaySnapshots(response.data, $container);
} else {
- this.showError($container, response.data.message);
+ this.showError($container, response.data?.message || 'Failed to load snapshots');
}
},
- error: () => {
- this.showError($container, archiver.i18n.error);
+ error: (xhr, status, error) => {
+ delete this.loadingCache[requestKey];
+
+ // Retry mechanism
+ if (retryCount < this.config.maxRetries) {
+ setTimeout(() => {
+ this.loadSnapshots(url, $container, service, retryCount + 1);
+ }, this.config.retryDelay * (retryCount + 1));
+
+ $container.html(`
+
+
+ ${archiver.i18n.loading} (Retry ${retryCount + 1}/${this.config.maxRetries})
+
+ `);
+ } else {
+ let errorMsg = 'Failed to load archive data';
+ if (status === 'timeout') {
+ errorMsg = 'Request timeout - archive service may be slow';
+ } else if (xhr.status === 0) {
+ errorMsg = 'Network error - please check your connection';
+ }
+ this.showError($container, errorMsg);
+ }
}
});
},
/**
- * 显示快照
+ * Display snapshots with enhanced UI
*/
displaySnapshots: function(data, $container) {
if (!data.html || data.html.includes('archiver-no-snapshots')) {
- $container.html(data.html || '' + archiver.i18n.no_snapshots + '
');
+ const noSnapshotsHtml = `
+
+
${archiver.i18n.no_snapshots}
+
Snapshots will appear here after archiving is complete.
+
+ `;
+ $container.html(data.html || noSnapshotsHtml);
return;
}
$container.hide().html(data.html).fadeIn(this.config.fadeInDuration);
- // 添加淡入动画
- $container.find('li').each(function(index) {
+ // Enhanced animations
+ $container.find('.archiver-snapshot-list li').each(function(index) {
$(this).css({
opacity: 0,
transform: 'translateY(10px)'
- }).delay(index * 50).animate({
+ }).delay(index * 100).animate({
opacity: 1
}, {
- duration: 300,
+ duration: 400,
step: function(now, fx) {
if (fx.prop === 'opacity' && now > 0.5) {
$(this).css('transform', 'translateY(0)');
@@ -295,10 +345,18 @@
}
});
});
+
+ // Add timestamp to links if missing
+ $container.find('a').each(function() {
+ const $link = $(this);
+ if (!$link.attr('title')) {
+ $link.attr('title', 'Archived snapshot - opens in new tab');
+ }
+ });
},
/**
- * 触发快照
+ * Trigger snapshot with enhanced feedback
*/
triggerSnapshot: function(url, service = null) {
const $button = $('#archiver-immediate-snapshot');
@@ -317,39 +375,45 @@
url: url,
service: service
},
+ timeout: 30000,
success: (response) => {
if (response.success) {
$status.removeClass('error').addClass('success')
.html(' ' + response.data.message);
- // 清除缓存并开始轮询
+ // Clear cache and start monitoring
this.clearCache(this.getCacheKey(url, service));
if (response.data.refresh) {
this.startPolling(url, service);
}
- // 触发自定义事件
+ // Trigger custom event
$(document).trigger('archiver:snapshot:success', [url, service]);
} else {
$status.removeClass('success').addClass('error')
- .html(' ' + response.data.message);
+ .html(' ' +
+ (response.data?.message || 'Archive request failed'));
}
},
- error: () => {
+ error: (xhr, status, error) => {
+ let errorMsg = archiver.i18n.error;
+ if (status === 'timeout') {
+ errorMsg = 'Archive request timeout - please try again';
+ }
$status.removeClass('success').addClass('error')
- .html(' ' + archiver.i18n.error);
+ .html(' ' + errorMsg);
},
complete: () => {
$button.prop('disabled', false).removeClass('updating-message');
setTimeout(() => {
$status.fadeOut();
- }, 5000);
+ }, 8000);
}
});
},
/**
- * 管理栏触发快照
+ * Admin bar snapshot trigger
*/
triggerAdminBarSnapshot: function() {
const $item = $('#wp-admin-bar-archiver');
@@ -365,6 +429,7 @@
url: archiver.url,
service: archiver.primary_service
},
+ timeout: 20000,
success: (response) => {
$item.removeClass('archiver-active');
if (response.success) {
@@ -377,20 +442,21 @@
this.showNotification(response.message || archiver.i18n.error, 'error');
}
},
- error: () => {
+ error: (xhr, status) => {
$item.removeClass('archiver-active').addClass('archiver-failure');
- this.showNotification(archiver.i18n.error, 'error');
+ const errorMsg = status === 'timeout' ? 'Archive timeout' : archiver.i18n.error;
+ this.showNotification(errorMsg, 'error');
},
complete: () => {
setTimeout(() => {
$item.removeClass('archiver-success archiver-failure');
- }, 2000);
+ }, 3000);
}
});
},
/**
- * 更新管理栏计数
+ * Update admin bar count
*/
updateAdminBarCount: function(url) {
const $countItem = $('#wp-admin-bar-archiver-snapshots .ab-label');
@@ -405,17 +471,18 @@
url: url,
service: archiver.primary_service
},
+ timeout: 10000,
success: (response) => {
if (response.success && response.data.count !== undefined) {
const count = response.data.count >= 10 ? '10+' : response.data.count;
- $countItem.text(archiver.i18n.view_all + ' (' + count + ')');
+ $countItem.text(`${archiver.i18n.view_all} (${count})`);
}
}
});
},
/**
- * 检查更新
+ * Check for updates
*/
checkForUpdates: function(url, $container, service = null) {
$.ajax({
@@ -427,6 +494,7 @@
url: url,
service: service
},
+ timeout: 8000,
success: (response) => {
if (response.success && response.data.has_update) {
const cacheKey = this.getCacheKey(url, service);
@@ -438,7 +506,7 @@
},
/**
- * 开始轮询
+ * Start polling for updates
*/
startPolling: function(url, service = null) {
let checkCount = 0;
@@ -454,10 +522,20 @@
this.checkForUpdates(url, $container, service);
}, this.config.checkInterval);
+
+ // Visual indicator for polling
+ if ($container.length) {
+ const $indicator = $('Checking for new archives...
');
+ $container.prepend($indicator);
+
+ setTimeout(() => {
+ $indicator.fadeOut(() => $indicator.remove());
+ }, this.config.checkInterval * 3);
+ }
},
/**
- * 显示加载状态
+ * Show loading state
*/
showLoading: function($container) {
const loadingHtml = `
@@ -470,30 +548,61 @@
},
/**
- * 显示错误
+ * Show error state
*/
showError: function($container, message) {
- $container.html('' + message + '
');
+ const errorHtml = `
+
+
${message}
+
+
+ `;
+ $container.html(errorHtml);
+
+ // Retry functionality
+ $container.find('.archiver-retry').on('click', () => {
+ const url = $container.data('url');
+ const service = $container.data('service');
+ this.loadSnapshots(url, $container, service);
+ });
},
/**
- * 显示通知
+ * Show notification
*/
showNotification: function(message, type = 'info') {
- // 如果在管理页面,使用 WordPress 通知
+ // If in admin page, use WordPress notifications
if ($('body').hasClass('wp-admin')) {
- const $notice = $('');
+ const $notice = $(``);
$('.wrap > h1').after($notice);
- // 自动关闭
+ // Auto dismiss
setTimeout(() => {
$notice.fadeOut(() => $notice.remove());
}, 5000);
+ } else {
+ // Frontend notification
+ const $notification = $(`
+
+ ${message}
+
+ `);
+
+ $('body').append($notification);
+
+ setTimeout(() => {
+ $notification.addClass('show');
+ }, 100);
+
+ setTimeout(() => {
+ $notification.removeClass('show');
+ setTimeout(() => $notification.remove(), 300);
+ }, 4000);
}
},
/**
- * 缓存管理
+ * Cache management
*/
getCacheKey: function(url, service = null) {
const serviceKey = service || 'default';
@@ -508,13 +617,22 @@
if (cached) {
const data = JSON.parse(cached);
- // 5分钟缓存
+ // 5 minute cache
if (Date.now() - data.timestamp < 300000) {
return data.content;
+ } else {
+ // Clean expired cache
+ sessionStorage.removeItem(key);
}
}
} catch (e) {
- console.warn('Cache read error:', e);
+ console.warn('Archiver cache read error:', e);
+ // Clean corrupted cache
+ try {
+ sessionStorage.removeItem(key);
+ } catch (cleanError) {
+ console.warn('Failed to clean corrupted cache:', cleanError);
+ }
}
return null;
@@ -530,7 +648,9 @@
};
sessionStorage.setItem(key, JSON.stringify(cacheData));
} catch (e) {
- console.warn('Cache write error:', e);
+ console.warn('Archiver cache write error:', e);
+ // Try to clear some space
+ this.cleanOldCache();
}
},
@@ -541,23 +661,157 @@
if (key) {
sessionStorage.removeItem(key);
} else {
- // 清除所有archiver缓存
- const keys = [];
- for (let i = 0; i < sessionStorage.length; i++) {
- const k = sessionStorage.key(i);
- if (k && k.startsWith(this.config.cachePrefix)) {
- keys.push(k);
- }
- }
- keys.forEach(k => sessionStorage.removeItem(k));
+ // Clear all archiver cache
+ this.cleanOldCache(true);
}
} catch (e) {
- console.warn('Cache clear error:', e);
+ console.warn('Archiver cache clear error:', e);
+ }
+ },
+
+ cleanOldCache: function(clearAll = false) {
+ if (!this.isStorageAvailable()) return;
+
+ try {
+ const keys = [];
+ const now = Date.now();
+
+ for (let i = 0; i < sessionStorage.length; i++) {
+ const key = sessionStorage.key(i);
+ if (key && key.startsWith(this.config.cachePrefix)) {
+ if (clearAll) {
+ keys.push(key);
+ } else {
+ try {
+ const cached = sessionStorage.getItem(key);
+ if (cached) {
+ const data = JSON.parse(cached);
+ // Remove if older than 10 minutes
+ if (now - data.timestamp > 600000) {
+ keys.push(key);
+ }
+ }
+ } catch (e) {
+ // Remove corrupted entries
+ keys.push(key);
+ }
+ }
+ }
+ }
+
+ keys.forEach(key => {
+ try {
+ sessionStorage.removeItem(key);
+ } catch (e) {
+ console.warn('Failed to remove cache key:', key, e);
+ }
+ });
+
+ if (keys.length > 0) {
+ console.log(`Archiver: Cleaned ${keys.length} cache entries`);
+ }
+ } catch (e) {
+ console.warn('Cache cleanup error:', e);
}
},
/**
- * 工具函数
+ * Add notification styles
+ */
+ addNotificationStyles: function() {
+ if ($('#archiver-notification-styles').length) return;
+
+ $('head').append(`
+
+ `);
+ },
+
+ /**
+ * Utility functions
*/
isStorageAvailable: function() {
try {
@@ -581,7 +835,7 @@
},
/**
- * 防抖函数
+ * Debounce function
*/
debounce: function(func, wait) {
let timeout;
@@ -593,13 +847,44 @@
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
+ },
+
+ /**
+ * Global error handler
+ */
+ handleGlobalError: function(error, context = 'Unknown') {
+ console.error(`Archiver Error [${context}]:`, error);
+
+ if (typeof error === 'object' && error.message) {
+ this.showNotification(`Error in ${context}: ${error.message}`, 'error');
+ }
}
};
- // 初始化
+ // Global error handling
+ $(document).ajaxError(function(event, xhr, settings, error) {
+ if (settings.url && settings.url.includes('archiver')) {
+ console.error('Archiver AJAX Error:', {
+ url: settings.url,
+ status: xhr.status,
+ error: error,
+ response: xhr.responseText
+ });
+ }
+ });
+
+ // Initialize when DOM is ready
Archiver.init();
- // 暴露到全局
+ // Expose to global scope
window.WPArchiver = Archiver;
+ // Cleanup on page unload
+ $(window).on('beforeunload', function() {
+ // Clean old cache entries before leaving
+ if (Archiver.isStorageAvailable()) {
+ Archiver.cleanOldCache();
+ }
+ });
+
})(jQuery);
diff --git a/css/archiver.css b/css/archiver.css
deleted file mode 100644
index ca99949..0000000
--- a/css/archiver.css
+++ /dev/null
@@ -1,453 +0,0 @@
-/**
- * WP Archiver 样式表
- */
-
-/* 管理栏样式 */
-#wp-admin-bar-archiver .ab-item {
- transition: all .3s ease;
- color: #72aee6;
-}
-
-#wp-admin-bar-archiver.archiver-success .ab-item {
- color: #46b450 !important;
-}
-
-#wp-admin-bar-archiver.archiver-failure .ab-item {
- color: #dc3232 !important;
-}
-
-#wp-admin-bar-archiver .ab-icon.dashicons.dashicons-backup {
- top: 2px;
-}
-
-#wp-admin-bar-archiver-trigger .ab-icon {
- display: inline-block;
- float: right !important;
- margin: 0 0 0 6px;
- opacity: 0;
- transition: opacity .3s ease;
- vertical-align: middle;
-}
-
-#wp-admin-bar-archiver-trigger .ab-icon::before {
- content: "\f463";
- font-family: dashicons;
- font-size: 18px;
- speak: none;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-#wp-admin-bar-archiver-trigger.archiver-active .ab-icon,
-#wp-admin-bar-archiver.archiver-active .ab-icon {
- opacity: 1;
- animation: archiver-spin 1s infinite linear;
-}
-
-@keyframes archiver-spin {
- 0% { transform: rotate(0); }
- 100% { transform: rotate(360deg); }
-}
-
-/* 管理页面样式 */
-.archiver-admin-wrap {
- max-width: 1200px;
-}
-
-.archiver-version {
- font-size: 13px;
- padding-left: 10px;
- color: #666;
-}
-
-.nav-tab-wrapper {
- margin-bottom: 20px;
-}
-
-.nav-tab {
- border: none;
- background: #fff;
- border-bottom: 2px solid transparent;
- font-weight: 400;
-}
-
-.nav-tab:hover {
- background: #f0f0f1;
- border-bottom-color: #dcdcde;
-}
-
-.nav-tab.nav-tab-active {
- border-bottom-color: #007cba;
- font-weight: 600;
- background: #f0f0f1;
-}
-
-.archiver-tab-content {
- background: #fff;
- border: 1px solid #ccd0d4;
- border-radius: 4px;
- padding: 20px;
- margin-bottom: 20px;
-}
-
-/* 统计卡片 */
-.archiver-stats-container {
- display: flex;
- gap: 20px;
- margin: 20px 0;
- flex-wrap: wrap;
-}
-
-.archiver-stat-card {
- flex: 1;
- min-width: 200px;
- background: #fff;
- border: 1px solid #ccd0d4;
- border-radius: 4px;
- padding: 20px;
- text-align: center;
- box-shadow: 0 1px 1px rgba(0,0,0,.04);
- transition: transform 0.2s, box-shadow 0.2s;
-}
-
-.archiver-stat-card:hover {
- transform: translateY(-2px);
- box-shadow: 0 3px 5px rgba(0,0,0,.1);
-}
-
-.archiver-stat-card h3 {
- margin: 0 0 10px;
- font-size: 14px;
- font-weight: 500;
- color: #646970;
-}
-
-.archiver-stat-card .stat-value {
- font-size: 24px;
- font-weight: 600;
- color: #1d2327;
- line-height: 1;
-}
-
-.stat-subtitle {
- font-size: 12px;
- color: #666;
- margin-top: 8px;
-}
-
-/* 缓存分布 */
-.cache-distribution {
- margin-top: 10px;
-}
-
-.cache-bar {
- display: flex;
- height: 20px;
- background: #f0f0f0;
- border-radius: 10px;
- overflow: hidden;
- box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
-}
-
-.cache-segment {
- height: 100%;
- transition: width 0.3s ease;
- position: relative;
-}
-
-.cache-segment.hot { background: #e74c3c; }
-.cache-segment.warm { background: #f39c12; }
-.cache-segment.cold { background: #3498db; }
-.cache-segment.frozen { background: #95a5a6; }
-
-.cache-legend {
- display: flex;
- justify-content: center;
- gap: 20px;
- margin-top: 10px;
- font-size: 12px;
-}
-
-.legend-item {
- display: flex;
- align-items: center;
-}
-
-.legend-color {
- width: 12px;
- height: 12px;
- border-radius: 2px;
- margin-right: 5px;
-}
-
-.legend-color.hot { background: #e74c3c; }
-.legend-color.warm { background: #f39c12; }
-.legend-color.cold { background: #3498db; }
-.legend-color.frozen { background: #95a5a6; }
-
-/* 待处理列表 */
-.archiver-pending-list {
- max-height: 300px;
- overflow-y: auto;
- border: 1px solid #ddd;
- border-radius: 4px;
- padding: 15px;
- background: #f9f9f9;
- margin: 15px 0;
-}
-
-.archiver-pending-list ul {
- margin: 0;
- padding: 0;
- list-style: none;
-}
-
-.archiver-pending-list li {
- padding: 8px 0;
- border-bottom: 1px solid #eee;
- word-break: break-all;
- font-family: monospace;
- font-size: 13px;
- color: #555;
-}
-
-.archiver-pending-list li:last-child {
- border-bottom: none;
-}
-
-/* 元框样式 */
-#archiver_post,
-#archiver_terms {
- background: #fff;
-}
-
-#archiver-snapshots {
- min-height: 60px;
- position: relative;
-}
-
-#archiver-snapshots ul {
- margin: 0;
- padding: 0;
- list-style: none;
-}
-
-#archiver-snapshots li {
- padding: 8px 0;
- border-bottom: 1px solid #eee;
- transition: all 0.3s ease;
-}
-
-#archiver-snapshots li:last-child {
- border-bottom: none;
-}
-
-#archiver-snapshots li:hover {
- background: #f5f5f5;
- padding-left: 5px;
-}
-
-#archiver-snapshots a {
- text-decoration: none;
- color: #2271b1;
-}
-
-#archiver-snapshots a:hover {
- text-decoration: underline;
- color: #135e96;
-}
-
-/* 加载状态 */
-.archiver-loading {
- text-align: center;
- padding: 20px;
- color: #666;
-}
-
-.archiver-loading .spinner {
- float: none;
- margin: 0 5px 0 0;
-}
-
-/* 状态消息 */
-#archiver-status {
- display: inline-block;
- vertical-align: middle;
- color: #646970;
-}
-
-#archiver-status.success {
- color: #46b450;
-}
-
-#archiver-status.error {
- color: #dc3232;
-}
-
-#archiver-status .dashicons {
- vertical-align: middle;
- margin-right: 3px;
-}
-
-/* 按钮样式 */
-#archiver-immediate-snapshot {
- margin-top: 10px;
-}
-
-#archiver-immediate-snapshot.updating-message:before {
- content: "\f463";
- display: inline-block;
- font-family: dashicons;
- animation: archiver-spin 1s infinite linear;
- margin-right: 5px;
-}
-
-.archiver-view-all {
- font-size: 13px;
-}
-
-/* 工具区域 */
-.archiver-tools-section {
- margin-bottom: 30px;
- padding-bottom: 30px;
- border-bottom: 1px solid #ddd;
-}
-
-.archiver-tools-section:last-child {
- border-bottom: none;
-}
-
-.archiver-tools-section h3 {
- margin-top: 0;
-}
-
-.archiver-tool-form {
- margin-top: 15px;
-}
-
-/* 缓存操作 */
-.archiver-cache-actions {
- margin-top: 20px;
- padding-top: 20px;
- border-top: 1px solid #ddd;
-}
-
-.archiver-cache-actions .button {
- margin-right: 5px;
-}
-
-/* 关于页面 */
-.archiver-about-content {
- max-width: 800px;
-}
-
-.archiver-about-content ul {
- list-style: disc;
- padding-left: 20px;
-}
-
-.archiver-about-content li {
- margin-bottom: 8px;
-}
-
-/* 响应式设计 */
-@media (max-width: 782px) {
- .archiver-stats-container {
- flex-direction: column;
- }
-
- .archiver-stat-card {
- width: 100%;
- min-width: unset;
- }
-
- .cache-legend {
- flex-wrap: wrap;
- }
-
- .nav-tab-wrapper {
- display: flex;
- overflow-x: auto;
- -webkit-overflow-scrolling: touch;
- }
-
- .nav-tab {
- white-space: nowrap;
- flex-shrink: 0;
- }
-}
-
-/* 暗色模式支持 */
-@media (prefers-color-scheme: dark) {
- .wp-admin.admin-color-modern .archiver-stat-card,
- .wp-admin.admin-color-modern .archiver-tab-content {
- background: #1e1e1e;
- border-color: #3c434a;
- color: #ccc;
- }
-
- .wp-admin.admin-color-modern .archiver-stat-card h3 {
- color: #aaa;
- }
-
- .wp-admin.admin-color-modern .stat-value {
- color: #fff;
- }
-
- .wp-admin.admin-color-modern .archiver-pending-list {
- background: #2c3338;
- border-color: #3c434a;
- }
-}
-
-/* 动画效果 */
-@keyframes fadeInUp {
- from {
- opacity: 0;
- transform: translateY(10px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-.archiver-snapshot-list li {
- animation: fadeInUp 0.3s ease forwards;
-}
-
-/* 工具提示 */
-[title] {
- position: relative;
-}
-
-.cache-segment:hover::after {
- content: attr(title);
- position: absolute;
- bottom: 100%;
- left: 50%;
- transform: translateX(-50%);
- background: #333;
- color: #fff;
- padding: 5px 10px;
- border-radius: 3px;
- font-size: 12px;
- white-space: nowrap;
- z-index: 1000;
- pointer-events: none;
-}
-
-/* 缓存统计区域 */
-.archiver-cache-stats {
- background: #f8f9fa;
- border: 1px solid #e1e4e8;
- border-radius: 6px;
- padding: 20px;
- margin: 20px 0;
-}
-
-.archiver-cache-stats h2 {
- margin-top: 0;
- font-size: 18px;
- color: #1d2327;
-}
diff --git a/css/archiver.min.css b/css/archiver.min.css
deleted file mode 100644
index c283d9a..0000000
--- a/css/archiver.min.css
+++ /dev/null
@@ -1 +0,0 @@
-#wp-admin-bar-archiver .ab-item{transition:all .3s ease;color:#72aee6}#wp-admin-bar-archiver.archiver-success .ab-item{color:#46b450!important}#wp-admin-bar-archiver.archiver-failure .ab-item{color:#dc3232!important}#wp-admin-bar-archiver .ab-icon.dashicons.dashicons-backup{top:2px}#wp-admin-bar-archiver-trigger .ab-icon{display:inline-block;float:right!important;margin:0 0 0 6px;opacity:0;transition:opacity .3s ease;vertical-align:middle}#wp-admin-bar-archiver-trigger .ab-icon::before{content:"\f463";font-family:dashicons;font-size:18px;speak:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#wp-admin-bar-archiver-trigger.archiver-active .ab-icon{opacity:1;animation:archiver-spin 1s infinite linear}@keyframes archiver-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.nav-tab{border:0 solid #c3c4c7;background:#fff}.nav-tab.nav-tab-active{border-bottom:2px solid #007cba;font-weight:600;background:#f0f0f1}.nav-tab:hover:not(.active){background:#f0f0f1;border-bottom-color:#dcdcde}.card{background:#fff;border:1px solid #ccd0d4;border-radius:4px;max-width:unset;margin-top:20px;padding:20px}.postbox{border:0 solid #c3c4c7;background:#ffffff00}.archiver-stats-container{display:flex;gap:20px;margin:0 0 20px}.archiver-stat-card{flex:1;background:#fff;border:1px solid #ccd0d4;border-radius:4px;padding:15px;text-align:center;box-shadow:0 1px 1px rgba(0,0,0,.04)}.archiver-stat-card h3{margin:0 0 10px;font-size:14px;font-weight:500;color:#646970}.archiver-stat-card .stat-value{font-size:18px;font-weight:600;color:#1d2327}.archiver-pending-list{max-height:300px;overflow-y:auto;border:1px solid #ddd;padding:10px;background:#f9f9f9;margin:0 0 20px}.archiver-pending-list ul{margin:0;padding:0;list-style:none}.archiver-pending-list li{padding:5px 0;border-bottom:1px solid #eee;word-break:break-all}.archiver-pending-list li:last-child{border-bottom:none}@media (max-width:782px){.archiver-stats-container{flex-direction:column}.archiver-stat-card{width:100%}}#archiver_post,#archiver_terms{background:#fff;border-radius:4px}#archiver-snapshots ul{margin:0;padding:0;list-style:none}#archiver-snapshots li{padding:5px 0;border-bottom:1px solid #eee}#archiver-snapshots li:last-child{border-bottom:none}#archiver-snapshots a{text-decoration:none;color:#2271b1}#archiver-snapshots a:hover{text-decoration:underline}#archiver-status{vertical-align:middle;color:#646970}#archiver-status .dashicons{vertical-align:middle;margin-right:3px}#archiver-immediate-snapshot{margin-top:10px}
\ No newline at end of file
diff --git a/includes/class-archiver-admin.php b/includes/class-archiver-admin.php
index 5a51346..5a83d67 100644
--- a/includes/class-archiver-admin.php
+++ b/includes/class-archiver-admin.php
@@ -1,6 +1,6 @@
archiver = $archiver;
- // 延迟初始化缓存对象
- add_action('init', array($this, 'init_cache'));
-
+ // Initialize hooks
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'handle_settings_actions'));
- add_action('admin_notices', array($this, 'admin_notices'));
+ add_action('admin_notices', array($this, 'check_system_requirements'));
// AJAX handlers
add_action('wp_ajax_archiver_get_service_stats', array($this, 'ajax_get_service_stats'));
add_action('wp_ajax_archiver_test_service', array($this, 'ajax_test_service'));
+
+ // Initialize cache after WordPress is fully loaded
+ add_action('wp_loaded', array($this, 'init_cache'));
}
public function init_cache() {
@@ -43,11 +44,40 @@ class Archiver_Admin {
);
}
+ public function check_system_requirements() {
+ if (!isset($_GET['page']) || $_GET['page'] !== 'archiver-settings') {
+ return;
+ }
+
+ // Check if cache is enabled
+ $cache_enabled = get_option('archiver_cache_enabled', true);
+ if (!$cache_enabled) {
+ echo '';
+ echo '' . __('Performance Tip:', 'archiver') . ' ';
+ echo __('Enable caching to improve performance and reduce API calls.', 'archiver');
+ echo '
';
+ }
+
+ // Check cache table
+ global $wpdb;
+ $table_name = $wpdb->prefix . 'archiver_cache';
+ $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") === $table_name;
+
+ if (!$table_exists) {
+ echo '';
+ echo __('Cache table is missing. Please deactivate and reactivate the plugin to fix this issue.', 'archiver');
+ echo '
';
+ }
+ }
+
public function render_admin_page() {
if (!current_user_can('manage_options')) {
wp_die(__('You do not have sufficient permissions to access this page.', 'archiver'));
}
+ // Get services - handle case where constant might not be defined
+ $archiver_services = $this->get_available_services();
+
$post_types = get_post_types(array('public' => true), 'objects');
$selected_post_types = get_option('archiver_post_types', array('post', 'page'));
$cache_enabled = get_option('archiver_cache_enabled', true);
@@ -55,14 +85,19 @@ class Archiver_Admin {
$primary_service = get_option('archiver_primary_service', 'wenpai');
$auto_archive = get_option('archiver_auto_archive', true);
$archive_on_publish = get_option('archiver_archive_on_publish', true);
+ $max_queue_size = get_option('archiver_max_queue_size', 500);
+ $batch_size = get_option('archiver_batch_size', 10);
+ $update_frequency = get_option('archiver_update_frequency', 'hourly');
?>
-
+
+
+
@@ -70,7 +105,7 @@ class Archiver_Admin {