-
-
+
+
-
-
-
-
+ `;
+
+ if (todos.length === 0) {
+ html += `
+
+
No todos yet. Click "Add Todo" to get started!
+
+ `;
+ } else {
+ html += '
';
+ todos.forEach(todo => {
+ const completedClass = todo.completed ? 'completed' : '';
+ html += `
+
+
+
+
${this.escapeHtml(todo.title)}
+ ${todo.description ? `
${this.escapeHtml(todo.description)}
` : ''}
+
+ ${todo.priority}
+ ${todo.created_at_human}
+
+
+
+
+
+
+
+ `;
+ });
+ html += '
';
+ }
+
+ html += `
+
`;
- $('#msd-quick-links-editor').append(html);
+
+ $container.html(html);
},
- removeQuickLinkRow(e) {
- $(e.currentTarget).closest('.msd-link-item').fadeOut(200, function() {
- $(this).remove();
- });
+ showTodoForm() {
+ $('#msd-todo-form').addClass('active');
+ $('#msd-todo-title').focus();
},
- addNewsSourceRow() {
- const html = `
-
-
-
-
-
-
- `;
- $('#msd-news-sources-editor').append(html);
+ cancelTodoForm() {
+ $('#msd-todo-form').removeClass('active');
+ this.clearTodoForm();
},
- removeNewsSourceRow(e) {
- $(e.currentTarget).closest('.msd-news-source-item').fadeOut(200, function() {
- $(this).remove();
- });
+ clearTodoForm() {
+ $('#msd-todo-title').val('');
+ $('#msd-todo-description').val('');
+ $('#msd-todo-priority').val('medium');
+ $('#msd-todo-form').removeData('edit-id');
},
- saveQuickLinks() {
- const links = [];
- let hasErrors = false;
-
- $('.msd-link-item').each(function() {
- const $item = $(this);
- const title = $item.find('.msd-link-title').val().trim();
- const url = $item.find('.msd-link-url').val().trim();
- const icon = $item.find('.msd-link-icon').val().trim();
- const newTab = $item.find('.msd-link-newtab').is(':checked');
-
- if (title && url) {
- if (!MSD.isValidUrl(url)) {
- $item.find('.msd-link-url').addClass('error');
- hasErrors = true;
- return;
- }
-
- $item.find('.msd-link-url').removeClass('error');
- links.push({ title, url, icon, new_tab: newTab });
- } else if (title || url) {
- $item.addClass('error');
- hasErrors = true;
+ saveTodo() {
+ const title = $('#msd-todo-title').val().trim();
+ if (!title) {
+ if (window.MSD_Core) {
+ window.MSD_Core.showNotice('Title is required', 'error');
}
- });
-
- if (hasErrors) {
- this.showNotice('Please fill in all required fields correctly', 'error');
return;
}
- this.makeAjaxRequest('msd_save_quick_links', { links }, (response) => {
- this.showNotice(response.data.message, 'success');
- this.hideQuickLinksModal();
- setTimeout(() => location.reload(), 1000);
- }, (error) => {
- this.showNotice('Failed to save quick links', 'error');
- });
- },
-
- saveNewsSources() {
- const sources = [];
- let hasErrors = false;
-
- $('.msd-news-source-item').each(function() {
- const $item = $(this);
- const name = $item.find('.msd-news-name').val().trim();
- const url = $item.find('.msd-news-url').val().trim();
- const enabled = $item.find('.msd-news-enabled').is(':checked');
-
- if (name && url) {
- if (!MSD.isValidUrl(url)) {
- $item.find('.msd-news-url').addClass('error');
- hasErrors = true;
- return;
- }
-
- $item.find('.msd-news-url').removeClass('error');
- sources.push({ name, url, enabled });
- } else if (name || url) {
- $item.addClass('error');
- hasErrors = true;
- }
- });
-
- if (hasErrors) {
- this.showNotice('Please fill in all required fields correctly', 'error');
- return;
- }
-
- this.makeAjaxRequest('msd_save_news_sources', { sources }, (response) => {
- this.showNotice(response.data.message, 'success');
- this.hideNewsSourcesModal();
- this.loadWidget('custom_news');
- }, (error) => {
- this.showNotice('Failed to save news sources', 'error');
- });
- },
-
- saveContactInfo() {
- const contactInfo = {
- name: $('#msd-contact-name').val().trim(),
- email: $('#msd-contact-email').val().trim(),
- phone: $('#msd-contact-phone').val().trim(),
- website: $('#msd-contact-website').val().trim(),
- description: $('#msd-contact-description').val().trim(),
- qq: $('#msd-contact-qq').val().trim(),
- wechat: $('#msd-contact-wechat').val().trim(),
- whatsapp: $('#msd-contact-whatsapp').val().trim(),
- telegram: $('#msd-contact-telegram').val().trim(),
- qr_code: $('#msd-contact-qr-code').val().trim()
+ const data = {
+ title: title,
+ description: $('#msd-todo-description').val().trim(),
+ priority: $('#msd-todo-priority').val()
};
- if (!contactInfo.name || !contactInfo.email) {
- this.showNotice('Name and email are required', 'error');
+ const editId = $('#msd-todo-form').data('edit-id');
+ const action = editId ? 'msd_update_todo_item' : 'msd_save_todo_item';
+
+ if (editId) {
+ data.id = editId;
+ }
+
+ if (window.MSD_Core) {
+ window.MSD_Core.makeAjaxRequest(action, data, (response) => {
+ window.MSD_Core.showNotice(response.data.message, 'success');
+ this.cancelTodoForm();
+ this.loadWidget('todo_items');
+ }, (error) => {
+ window.MSD_Core.showNotice(error || 'Failed to save todo', 'error');
+ });
+ }
+ },
+
+ editTodo(e) {
+ const $item = $(e.currentTarget).closest('.msd-todo-item');
+ const id = $item.data('id');
+ const title = $item.find('.msd-todo-title').text();
+ const description = $item.find('.msd-todo-description').text();
+
+ $('#msd-todo-title').val(title);
+ $('#msd-todo-description').val(description);
+ $('#msd-todo-form').data('edit-id', id).addClass('active');
+ $('#msd-todo-title').focus();
+ },
+
+ deleteTodo(e) {
+ if (!confirm(msdAjax.strings.confirm_delete)) {
return;
}
- this.makeAjaxRequest('msd_save_contact_info', contactInfo, (response) => {
- this.showNotice(response.data.message, 'success');
- this.hideContactInfoModal();
- setTimeout(() => location.reload(), 1000);
- }, (error) => {
- this.showNotice('Failed to save contact information', 'error');
- });
- },
+ const $item = $(e.currentTarget).closest('.msd-todo-item');
+ const id = $item.data('id');
- selectQRImage() {
- if (wp && wp.media) {
- const frame = wp.media({
- title: 'Select QR Code Image',
- button: { text: 'Use Image' },
- multiple: false
+ if (window.MSD_Core) {
+ window.MSD_Core.makeAjaxRequest('msd_delete_todo_item', { id }, (response) => {
+ window.MSD_Core.showNotice(response.data.message, 'success');
+ this.loadWidget('todo_items');
+ }, (error) => {
+ window.MSD_Core.showNotice(error || 'Failed to delete todo', 'error');
});
-
- frame.on('select', function() {
- const attachment = frame.state().get('selection').first().toJSON();
- $('#msd-contact-qr-code').val(attachment.url);
- $('#msd-qr-preview img').attr('src', attachment.url);
- $('#msd-qr-preview').show();
- });
-
- frame.open();
- } else {
- const url = prompt('Enter QR code image URL:');
- if (url) {
- $('#msd-contact-qr-code').val(url);
- $('#msd-qr-preview img').attr('src', url);
- $('#msd-qr-preview').show();
- }
}
},
- removeQRCode() {
- $('#msd-contact-qr-code').val('');
- $('#msd-qr-preview').hide();
+ toggleTodoComplete(e) {
+ const $item = $(e.currentTarget).closest('.msd-todo-item');
+ const id = $item.data('id');
+
+ if (window.MSD_Core) {
+ window.MSD_Core.makeAjaxRequest('msd_toggle_todo_complete', { id }, (response) => {
+ this.loadWidget('todo_items');
+ }, (error) => {
+ window.MSD_Core.showNotice(error || 'Failed to update todo', 'error');
+ });
+ }
},
- clearNewsCache() {
- this.makeAjaxRequest('msd_refresh_widget_data', { widget: 'custom_news' }, (response) => {
- this.showNotice('News cache cleared successfully', 'success');
- this.loadWidget('custom_news');
- }, (error) => {
- this.showNotice('Failed to clear news cache', 'error');
- });
+ handleUserAction(e) {
+ e.preventDefault();
+ const $btn = $(e.currentTarget);
+ const action = $btn.data('action');
+ const userId = $btn.data('user-id');
+
+ if (!action || !userId) {
+ if (window.MSD_Core) {
+ window.MSD_Core.showNotice('Invalid action or user ID', 'error');
+ }
+ return;
+ }
+
+ if (!confirm('Are you sure you want to perform this action?')) {
+ return;
+ }
+
+ $btn.prop('disabled', true).text('Processing...');
+
+ if (window.MSD_Core) {
+ window.MSD_Core.makeAjaxRequest('msd_manage_user_action', {
+ user_action: action,
+ user_id: userId
+ }, (response) => {
+ window.MSD_Core.showNotice(response.data.message || 'Action completed successfully', 'success');
+ this.loadWidget('user_management');
+ }, (error) => {
+ window.MSD_Core.showNotice(error || 'Action failed', 'error');
+ }).always(() => {
+ $btn.prop('disabled', false).text($btn.data('original-text') || 'Action');
+ });
+ }
},
handleWidgetError($container, error, widgetType) {
- this.retryCount++;
+ if (window.MSD_Core) {
+ window.MSD_Core.retryCount++;
- if (this.retryCount <= this.maxRetries) {
- setTimeout(() => {
- this.loadWidget(widgetType);
- }, 2000 * this.retryCount);
- return;
+ if (window.MSD_Core.retryCount <= window.MSD_Core.maxRetries) {
+ setTimeout(() => {
+ this.loadWidget(widgetType);
+ }, 2000 * window.MSD_Core.retryCount);
+ return;
+ }
}
const html = `
@@ -1129,114 +735,19 @@ jQuery(document).ready(function($) {
$container.html(html);
},
- makeAjaxRequest(action, data, successCallback, errorCallback) {
- const ajaxData = {
- action: action,
- nonce: msdAjax.nonce,
- ...data
- };
-
- return $.post(msdAjax.ajaxurl, ajaxData)
- .done((response) => {
- if (response.success) {
- successCallback(response);
- } else {
- errorCallback(response.data || 'Unknown error');
- }
- })
- .fail((xhr, status, error) => {
- errorCallback(error);
- });
- },
-
- showNotice(message, type = 'info', duration = 5000) {
- const $notice = $(`
${this.escapeHtml(message)}
`);
-
- const $container = $('.wrap h1').first();
- if ($container.length) {
- $container.after($notice);
- } else {
- $('body').prepend($notice);
- }
-
- if (duration > 0) {
- setTimeout(() => {
- $notice.fadeOut(300, () => $notice.remove());
- }, duration);
- }
-
- $notice.on('click', () => {
- $notice.fadeOut(300, () => $notice.remove());
- });
- },
-
- formatNumber(num) {
- if (num >= 1000000) {
- return (num / 1000000).toFixed(1) + 'M';
- } else if (num >= 1000) {
- return (num / 1000).toFixed(1) + 'K';
- }
- return num.toString();
- },
-
- formatTime(date) {
- return date.toLocaleTimeString([], {
- hour: '2-digit',
- minute: '2-digit'
- });
- },
-
- escapeHtml(text) {
- const div = document.createElement('div');
- div.textContent = text;
- return div.innerHTML;
- },
-
- decodeHtmlEntities(text) {
- const textarea = document.createElement('textarea');
- textarea.innerHTML = text;
- return textarea.value;
- },
-
- truncateText(text, maxLength) {
- if (!text || text.length <= maxLength) {
- return text;
- }
- return text.substring(0, maxLength).trim() + '...';
- },
-
- isValidUrl(string) {
- try {
- const url = new URL(string);
- return url.protocol === 'http:' || url.protocol === 'https:';
- } catch (_) {
- return false;
- }
- },
-
- getStorageStatusClass(status) {
- const statusMap = {
- critical: 'critical',
- warning: 'warning',
- good: '',
- default: ''
- };
- return statusMap[status] || statusMap.default;
- }
+ formatNumber: window.MSD_Core ? window.MSD_Core.formatNumber : function(num) { return num.toString(); },
+ escapeHtml: window.MSD_Core ? window.MSD_Core.escapeHtml : function(text) { return text; },
+ decodeHtmlEntities: window.MSD_Core ? window.MSD_Core.decodeHtmlEntities : function(text) { return text; },
+ truncateText: window.MSD_Core ? window.MSD_Core.truncateText : function(text, maxLength) { return text; },
+ getDefaultFavicon: window.MSD_Core ? window.MSD_Core.getDefaultFavicon : function() { return ''; },
+ getDefaultAvatar: window.MSD_Core ? window.MSD_Core.getDefaultAvatar : function() { return ''; },
+ formatNewsDate: window.MSD_Core ? window.MSD_Core.formatNewsDate : function(date) { return date; },
+ getUserStatusClass: window.MSD_Core ? window.MSD_Core.getUserStatusClass : function(status) { return status; },
+ getUserStatusLabel: window.MSD_Core ? window.MSD_Core.getUserStatusLabel : function(status) { return status; },
+ getRegistrationLabel: window.MSD_Core ? window.MSD_Core.getRegistrationLabel : function(reg) { return reg; },
+ getStorageStatusClass: window.MSD_Core ? window.MSD_Core.getStorageStatusClass : function(status) { return status; }
};
- MSD.init();
-
- $('head').append(`
-
- `);
+ window.MSD_Widgets = MSD_Widgets;
+ MSD_Widgets.init();
});
diff --git a/assets/dashboard.css b/assets/dashboard.css
index 7c02423..1f97a98 100644
--- a/assets/dashboard.css
+++ b/assets/dashboard.css
@@ -1634,8 +1634,8 @@ div#network_dashboard_right_now .inside {
.msd-widget-toggle .description {
margin: 0;
- font-size: 13px;
- color: var(--msd-text-light);
+ font-size: 11px;
+ color: #8c8c8c;
line-height: 1.4;
}
@@ -1869,3 +1869,33 @@ body.modal-open {
display: inline-block;
filter: grayscale(55%);
}
+
+.msd-update-notice {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px;
+ margin-bottom: var(--msd-spacing);
+ background: #fff3cd;
+ border: 1px solid #ffeaa7;
+ border-radius: var(--msd-radius-small);
+ color: #856404;
+ font-size: 13px;
+ font-weight: 500;
+}
+
+.msd-update-notice .dashicons {
+ color: #dba617;
+ font-size: 16px;
+ line-height: 1.2;
+}
+
+.msd-update-link {
+ color: var(--msd-primary);
+ text-decoration: none;
+ font-weight: 500;
+}
+
+.msd-update-link:hover {
+ text-decoration: underline;
+}
diff --git a/includes/class-admin-interface.php b/includes/class-admin-interface.php
new file mode 100644
index 0000000..a4ddcb7
--- /dev/null
+++ b/includes/class-admin-interface.php
@@ -0,0 +1,333 @@
+id === 'dashboard-network') {
+ include_once WP_MSD_PLUGIN_DIR . 'templates/admin-modals.php';
+ }
+ }
+
+ public function add_network_widgets() {
+ $plugin_core = WP_MSD_Plugin_Core::get_instance();
+ $enabled_widgets = $plugin_core->get_enabled_widgets();
+
+ $widgets = [
+ 'msd_network_overview' => ['Network Overview', 'render_network_overview_widget'],
+ 'msd_quick_site_management' => ['Quick Site Management', 'render_quick_site_widget'],
+ 'msd_storage_performance' => ['Storage Usage', 'render_storage_performance_widget'],
+ 'msd_server_info' => ['Server Information', 'render_server_info_widget'],
+ 'msd_quick_links' => ['Quick Links', 'render_quick_links_widget'],
+ 'msd_version_info' => ['Version Information', 'render_version_info_widget'],
+ 'msd_custom_news' => ['Network News', 'render_custom_news_widget'],
+ 'msd_user_management' => ['User Management', 'render_user_management_widget'],
+ 'msd_contact_info' => ['Contact Information', 'render_contact_info_widget'],
+ 'msd_last_edits' => ['Recent Network Activity', 'render_last_edits_widget'],
+ 'msd_todo_widget' => ['Todo List', 'render_todo_widget']
+ ];
+
+ foreach ($widgets as $widget_id => $widget_data) {
+ if (!empty($enabled_widgets[$widget_id])) {
+ wp_add_dashboard_widget(
+ $widget_id,
+ $widget_data[0],
+ [$this, $widget_data[1]]
+ );
+ }
+ }
+ }
+
+ public function render_network_overview_widget() {
+ echo '
';
+ echo '
' . __('Loading...', 'wp-multisite-dashboard') . '
';
+ echo '
';
+ }
+
+ public function render_quick_site_widget() {
+ echo '
';
+ echo '
' . __('Loading...', 'wp-multisite-dashboard') . '
';
+ echo '
';
+ }
+
+ public function render_storage_performance_widget() {
+ echo '
';
+ }
+
+ public function render_server_info_widget() {
+ echo '
';
+ echo '';
+ $this->render_server_info_content();
+ echo '
';
+ }
+
+ private function render_server_info_content() {
+ global $wpdb, $wp_version;
+
+ $data = [
+ 'PHP Version' => phpversion(),
+ 'MySQL Version' => $wpdb->db_version(),
+ 'Server Software' => $_SERVER["SERVER_SOFTWARE"] ?? 'Unknown',
+ 'Server Time' => current_time('Y-m-d H:i:s'),
+ 'Memory Limit' => ini_get('memory_limit'),
+ 'Max Upload Size' => size_format(wp_max_upload_size()),
+ ];
+
+ $icons = [
+ 'PHP Version' => 'dashicons-editor-code',
+ 'MySQL Version' => 'dashicons-database',
+ 'Server Software' => 'dashicons-admin-tools',
+ 'Server Time' => 'dashicons-clock',
+ 'Memory Limit' => 'dashicons-performance',
+ 'Max Upload Size' => 'dashicons-upload',
+ ];
+
+ echo '
';
+ foreach ($data as $label => $value) {
+ $icon = $icons[$label] ?? 'dashicons-info';
+ echo '
';
+ echo '';
+ echo '' . esc_html($label) . '';
+ echo '' . esc_html($value) . '';
+ echo '
';
+ }
+ echo '
';
+ }
+
+ public function render_version_info_widget() {
+ echo '
';
+ echo '';
+ $this->render_version_info_content();
+ echo '
';
+ }
+
+ private function render_version_info_content() {
+ $plugin_data = get_plugin_data(WP_MSD_PLUGIN_DIR . 'wp-multisite-dashboard.php');
+ global $wpdb;
+
+ echo '';
+
+ $plugin_core = WP_MSD_Plugin_Core::get_instance();
+ $update_checker = $plugin_core->get_update_checker();
+ $update_available = false;
+
+ if ($update_checker) {
+ $update = $update_checker->checkForUpdates();
+ if ($update && version_compare($update->version, WP_MSD_VERSION, '>')) {
+ $update_available = [
+ 'version' => $update->version,
+ 'details_url' => $update->details_url ?? '#'
+ ];
+ }
+ }
+
+ if ($update_available) {
+ echo '
';
+ }
+
+ echo '
';
+
+ echo '
';
+ echo '';
+ echo 'Plugin Version';
+ echo '' . esc_html($plugin_data['Version']) . '';
+ echo '
';
+
+ echo '
';
+
+ echo '
';
+ echo '';
+ echo 'Required PHP';
+ echo '' . esc_html($plugin_data['RequiresPHP']) . '';
+ echo '
';
+
+ echo '
';
+ echo '';
+ echo 'Database Tables';
+ $activity_table = $wpdb->base_prefix . 'msd_activity_log';
+ $activity_exists = $wpdb->get_var("SHOW TABLES LIKE '{$activity_table}'") === $activity_table;
+ if ($activity_exists) {
+ echo '✓ Activity table created';
+ } else {
+ echo '⚠ Activity table missing';
+ }
+ echo '
';
+
+ echo '
';
+ }
+
+ public function render_custom_news_widget() {
+ echo '
';
+ echo '
';
+ echo '
' . __('Loading...', 'wp-multisite-dashboard') . '
';
+ echo '
';
+ }
+
+ public function render_user_management_widget() {
+ echo '
';
+ echo '
';
+ echo '
' . __('Loading...', 'wp-multisite-dashboard') . '
';
+ echo '
';
+ }
+
+ public function render_contact_info_widget() {
+ echo '
';
+ echo '';
+ $this->render_contact_info_content();
+ echo '
';
+ }
+
+ private function render_contact_info_content() {
+ $contact_info = get_site_option('msd_contact_info', [
+ 'name' => get_network_option(null, 'site_name'),
+ 'email' => get_network_option(null, 'admin_email'),
+ 'phone' => '',
+ 'website' => network_home_url(),
+ 'description' => 'Network Administrator Contact Information',
+ 'qq' => '',
+ 'wechat' => '',
+ 'whatsapp' => '',
+ 'telegram' => '',
+ 'qr_code' => ''
+ ]);
+
+ echo '
';
+ }
+
+ public function render_last_edits_widget() {
+ echo '
';
+ echo '
';
+ echo '
' . __('Loading...', 'wp-multisite-dashboard') . '
';
+ echo '
';
+ }
+
+ public function render_quick_links_widget() {
+ $quick_links = get_site_option('msd_quick_links', []);
+
+ echo '
';
+
+ if (empty($quick_links)) {
+ echo '
';
+ echo '
' . __('No quick links configured.', 'wp-multisite-dashboard') . '
';
+ echo '
';
+ echo '
';
+ } else {
+ echo '
';
+ echo '';
+ }
+
+ echo '
';
+ }
+
+ public function render_todo_widget() {
+ echo '
';
+ }
+}
diff --git a/includes/class-ajax-handler.php b/includes/class-ajax-handler.php
new file mode 100644
index 0000000..469e626
--- /dev/null
+++ b/includes/class-ajax-handler.php
@@ -0,0 +1,744 @@
+register_ajax_actions();
+ }
+
+ private function register_ajax_actions() {
+ $ajax_actions = [
+ 'msd_get_network_overview',
+ 'msd_get_site_list',
+ 'msd_get_storage_data',
+ 'msd_get_server_info',
+ 'msd_get_version_info',
+ 'msd_get_custom_news',
+ 'msd_get_network_settings',
+ 'msd_get_user_management',
+ 'msd_get_last_edits',
+ 'msd_get_todo_items',
+ 'msd_save_news_sources',
+ 'msd_save_quick_links',
+ 'msd_save_contact_info',
+ 'msd_save_todo_item',
+ 'msd_update_todo_item',
+ 'msd_delete_todo_item',
+ 'msd_toggle_todo_complete',
+ 'msd_reorder_quick_links',
+ 'msd_toggle_widget',
+ 'msd_refresh_widget_data',
+ 'msd_clear_cache',
+ 'msd_manage_user_action',
+ 'msd_check_plugin_update',
+ 'msd_clear_widget_cache'
+ ];
+
+ foreach ($ajax_actions as $action) {
+ add_action("wp_ajax_{$action}", [$this, str_replace('msd_', '', $action)]);
+ }
+ }
+
+ public function get_network_overview() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $network_data = new WP_MSD_Network_Data();
+ $overview = [
+ 'total_posts' => $network_data->get_total_posts(),
+ 'total_pages' => $network_data->get_total_pages(),
+ 'multisite_config' => $network_data->get_multisite_configuration(),
+ 'network_info' => $network_data->get_network_information(),
+ 'critical_alerts' => 0,
+ 'network_status' => $network_data->get_overall_network_status(),
+ 'last_updated' => current_time('mysql')
+ ];
+
+ wp_send_json_success($overview);
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to load network overview', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function get_site_list() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $network_data = new WP_MSD_Network_Data();
+ $sites = $network_data->get_recent_active_sites(10);
+ wp_send_json_success($sites);
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to load sites', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function get_storage_data() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $network_data = new WP_MSD_Network_Data();
+ $storage_data = $network_data->get_storage_usage_data(5);
+ wp_send_json_success($storage_data);
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to load storage data', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function get_server_info() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ global $wpdb, $wp_version;
+
+ $server_info = [
+ 'wordpress_version' => $wp_version,
+ 'php_version' => phpversion(),
+ 'mysql_version' => $wpdb->db_version(),
+ 'server_software' => $_SERVER["SERVER_SOFTWARE"] ?? 'Unknown',
+ 'server_time' => current_time('Y-m-d H:i:s'),
+ 'php_memory_limit' => ini_get('memory_limit'),
+ 'max_upload_size' => size_format(wp_max_upload_size()),
+ 'active_plugins' => count(get_option('active_plugins', [])),
+ 'total_users' => count_users()['total_users'],
+ 'last_updated' => current_time('mysql')
+ ];
+
+ wp_send_json_success($server_info);
+ }
+
+ public function get_version_info() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ $plugin_data = get_plugin_data(WP_MSD_PLUGIN_DIR . 'wp-multisite-dashboard.php');
+ global $wpdb;
+
+ $activity_table = $wpdb->base_prefix . 'msd_activity_log';
+ $activity_exists = $wpdb->get_var("SHOW TABLES LIKE '{$activity_table}'") === $activity_table;
+
+ $plugin_core = WP_MSD_Plugin_Core::get_instance();
+ $update_checker = $plugin_core->get_update_checker();
+ $update_available = false;
+
+ if ($update_checker) {
+ $update = $update_checker->checkForUpdates();
+ if ($update && version_compare($update->version, WP_MSD_VERSION, '>')) {
+ $update_available = [
+ 'version' => $update->version,
+ 'details_url' => $update->details_url ?? '#'
+ ];
+ }
+ }
+
+ $version_info = [
+ 'plugin_name' => $plugin_data['Name'],
+ 'plugin_version' => $plugin_data['Version'],
+ 'plugin_author' => $plugin_data['Author'],
+ 'plugin_uri' => $plugin_data['AuthorURI'],
+ 'text_domain' => $plugin_data['TextDomain'],
+ 'required_php' => $plugin_data['RequiresPHP'],
+ 'description' => strip_tags($plugin_data['Description']),
+ 'database_status' => $activity_exists ? 'active' : 'missing',
+ 'database_message' => $activity_exists ? 'Activity table created' : 'Activity table missing',
+ 'update_available' => $update_available ? true : false,
+ 'update_info' => $update_available ?: null,
+ 'last_updated' => current_time('mysql')
+ ];
+
+ wp_send_json_success($version_info);
+ }
+
+ public function get_custom_news() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $news_sources = get_site_option('msd_news_sources', []);
+ $news_items = [];
+
+ foreach ($news_sources as $source) {
+ if (!$source['enabled']) continue;
+
+ $feed_items = $this->fetch_rss_feed($source['url'], 5);
+ foreach ($feed_items as $item) {
+ $item['source'] = $source['name'];
+ $news_items[] = $item;
+ }
+ }
+
+ usort($news_items, function($a, $b) {
+ return strtotime($b['date']) - strtotime($a['date']);
+ });
+
+ $news_items = array_slice($news_items, 0, 10);
+
+ wp_send_json_success($news_items);
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to load news', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function get_network_settings() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $network_data = new WP_MSD_Network_Data();
+ $settings_data = $network_data->get_network_settings_overview();
+ wp_send_json_success($settings_data);
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to load network settings', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function get_user_management() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $user_manager = new WP_MSD_User_Manager();
+ $user_data = $user_manager->get_recent_users_data();
+ wp_send_json_success($user_data);
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to load user data', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function get_last_edits() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $network_data = new WP_MSD_Network_Data();
+ $last_edits = $network_data->get_recent_network_activity(10);
+ wp_send_json_success($last_edits);
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to load recent activity', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function get_todo_items() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $todos = get_user_meta(get_current_user_id(), 'msd_todos', true);
+ if (!is_array($todos)) {
+ $todos = [];
+ }
+
+ foreach ($todos as &$todo) {
+ if (isset($todo['created_at'])) {
+ $todo['created_at_human'] = human_time_diff(strtotime($todo['created_at'])) . ' ago';
+ }
+ if (isset($todo['updated_at'])) {
+ $todo['updated_at_human'] = human_time_diff(strtotime($todo['updated_at'])) . ' ago';
+ }
+ }
+
+ wp_send_json_success($todos);
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to load todo items', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function save_news_sources() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ $sources = [];
+ if (isset($_POST['sources']) && is_array($_POST['sources'])) {
+ foreach ($_POST['sources'] as $source) {
+ if (!empty($source['name']) && !empty($source['url'])) {
+ $sources[] = [
+ 'name' => sanitize_text_field($source['name']),
+ 'url' => esc_url_raw($source['url']),
+ 'enabled' => !empty($source['enabled'])
+ ];
+ }
+ }
+ }
+
+ update_site_option('msd_news_sources', $sources);
+
+ $cache_keys = [];
+ foreach ($sources as $source) {
+ $cache_keys[] = 'msd_rss_' . md5($source['url']);
+ }
+
+ foreach ($cache_keys as $key) {
+ delete_site_transient($key);
+ }
+
+ wp_send_json_success(['message' => __('News sources saved successfully', 'wp-multisite-dashboard')]);
+ }
+
+ public function save_quick_links() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ $links = [];
+ if (isset($_POST['links']) && is_array($_POST['links'])) {
+ foreach ($_POST['links'] as $link) {
+ if (!empty($link['title']) && !empty($link['url'])) {
+ $links[] = [
+ 'title' => sanitize_text_field($link['title']),
+ 'url' => esc_url_raw($link['url']),
+ 'icon' => sanitize_text_field($link['icon']),
+ 'new_tab' => !empty($link['new_tab'])
+ ];
+ }
+ }
+ }
+
+ update_site_option('msd_quick_links', $links);
+ wp_send_json_success(['message' => __('Quick links saved successfully', 'wp-multisite-dashboard')]);
+ }
+
+ public function save_contact_info() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ $contact_info = [
+ 'name' => sanitize_text_field($_POST['name'] ?? ''),
+ 'email' => sanitize_email($_POST['email'] ?? ''),
+ 'phone' => sanitize_text_field($_POST['phone'] ?? ''),
+ 'website' => esc_url_raw($_POST['website'] ?? ''),
+ 'description' => sanitize_textarea_field($_POST['description'] ?? ''),
+ 'qq' => sanitize_text_field($_POST['qq'] ?? ''),
+ 'wechat' => sanitize_text_field($_POST['wechat'] ?? ''),
+ 'whatsapp' => sanitize_text_field($_POST['whatsapp'] ?? ''),
+ 'telegram' => sanitize_text_field($_POST['telegram'] ?? ''),
+ 'qr_code' => esc_url_raw($_POST['qr_code'] ?? '')
+ ];
+
+ update_site_option('msd_contact_info', $contact_info);
+ wp_send_json_success(['message' => __('Contact information saved successfully', 'wp-multisite-dashboard')]);
+ }
+
+ public function save_todo_item() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $title = sanitize_text_field($_POST['title'] ?? '');
+ $description = sanitize_textarea_field($_POST['description'] ?? '');
+
+ if (empty($title)) {
+ wp_send_json_error(__('Title is required', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $todos = get_user_meta(get_current_user_id(), 'msd_todos', true);
+ if (!is_array($todos)) {
+ $todos = [];
+ }
+
+ $new_todo = [
+ 'id' => uniqid(),
+ 'title' => $title,
+ 'description' => $description,
+ 'completed' => false,
+ 'priority' => sanitize_text_field($_POST['priority'] ?? 'medium'),
+ 'created_at' => current_time('mysql'),
+ 'updated_at' => current_time('mysql')
+ ];
+
+ $todos[] = $new_todo;
+
+ $result = update_user_meta(get_current_user_id(), 'msd_todos', $todos);
+
+ if ($result) {
+ wp_send_json_success(['message' => __('Todo item created', 'wp-multisite-dashboard')]);
+ } else {
+ wp_send_json_error(__('Failed to create todo item', 'wp-multisite-dashboard'));
+ }
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to create todo item', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function update_todo_item() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $id = sanitize_text_field($_POST['id'] ?? '');
+ $title = sanitize_text_field($_POST['title'] ?? '');
+ $description = sanitize_textarea_field($_POST['description'] ?? '');
+
+ if (empty($id) || empty($title)) {
+ wp_send_json_error(__('ID and title are required', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $todos = get_user_meta(get_current_user_id(), 'msd_todos', true);
+ if (!is_array($todos)) {
+ wp_send_json_error(__('No todos found', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $updated = false;
+ foreach ($todos as &$todo) {
+ if ($todo['id'] === $id) {
+ $todo['title'] = $title;
+ $todo['description'] = $description;
+ if (isset($_POST['priority'])) {
+ $todo['priority'] = sanitize_text_field($_POST['priority']);
+ }
+ $todo['updated_at'] = current_time('mysql');
+ $updated = true;
+ break;
+ }
+ }
+
+ if ($updated) {
+ $result = update_user_meta(get_current_user_id(), 'msd_todos', $todos);
+ if ($result) {
+ wp_send_json_success(['message' => __('Todo item updated', 'wp-multisite-dashboard')]);
+ } else {
+ wp_send_json_error(__('Failed to update todo item', 'wp-multisite-dashboard'));
+ }
+ } else {
+ wp_send_json_error(__('Todo item not found', 'wp-multisite-dashboard'));
+ }
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to update todo item', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function delete_todo_item() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $id = sanitize_text_field($_POST['id'] ?? '');
+
+ if (empty($id)) {
+ wp_send_json_error(__('ID is required', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $todos = get_user_meta(get_current_user_id(), 'msd_todos', true);
+ if (!is_array($todos)) {
+ wp_send_json_error(__('No todos found', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $filtered_todos = array_filter($todos, function($todo) use ($id) {
+ return $todo['id'] !== $id;
+ });
+
+ if (count($filtered_todos) < count($todos)) {
+ $result = update_user_meta(get_current_user_id(), 'msd_todos', array_values($filtered_todos));
+ if ($result) {
+ wp_send_json_success(['message' => __('Todo item deleted', 'wp-multisite-dashboard')]);
+ } else {
+ wp_send_json_error(__('Failed to delete todo item', 'wp-multisite-dashboard'));
+ }
+ } else {
+ wp_send_json_error(__('Todo item not found', 'wp-multisite-dashboard'));
+ }
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to delete todo item', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function toggle_todo_complete() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $id = sanitize_text_field($_POST['id'] ?? '');
+
+ if (empty($id)) {
+ wp_send_json_error(__('ID is required', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $todos = get_user_meta(get_current_user_id(), 'msd_todos', true);
+ if (!is_array($todos)) {
+ wp_send_json_error(__('No todos found', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $updated = false;
+ foreach ($todos as &$todo) {
+ if ($todo['id'] === $id) {
+ $todo['completed'] = !$todo['completed'];
+ $todo['updated_at'] = current_time('mysql');
+ $updated = true;
+ break;
+ }
+ }
+
+ if ($updated) {
+ $result = update_user_meta(get_current_user_id(), 'msd_todos', $todos);
+ if ($result) {
+ wp_send_json_success(['message' => __('Todo status updated', 'wp-multisite-dashboard')]);
+ } else {
+ wp_send_json_error(__('Failed to update todo status', 'wp-multisite-dashboard'));
+ }
+ } else {
+ wp_send_json_error(__('Todo item not found', 'wp-multisite-dashboard'));
+ }
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to update todo status', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function reorder_quick_links() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ $order = $_POST['order'] ?? [];
+ if (!is_array($order)) {
+ wp_send_json_error(__('Invalid order data', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $current_links = get_site_option('msd_quick_links', []);
+ $reordered_links = [];
+
+ foreach ($order as $index) {
+ $index = intval($index);
+ if (isset($current_links[$index])) {
+ $reordered_links[] = $current_links[$index];
+ }
+ }
+
+ update_site_option('msd_quick_links', $reordered_links);
+ wp_send_json_success(['message' => __('Links reordered successfully', 'wp-multisite-dashboard')]);
+ }
+
+ public function toggle_widget() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ $widget_id = sanitize_text_field($_POST['widget_id'] ?? '');
+ $enabled = !empty($_POST['enabled']);
+
+ if (empty($widget_id)) {
+ wp_send_json_error(__('Invalid widget ID', 'wp-multisite-dashboard'));
+ }
+
+ $enabled_widgets = get_site_option('msd_enabled_widgets', []);
+ $enabled_widgets[$widget_id] = $enabled ? 1 : 0;
+ update_site_option('msd_enabled_widgets', $enabled_widgets);
+
+ wp_send_json_success(['message' => __('Widget settings updated', 'wp-multisite-dashboard')]);
+ }
+
+ public function refresh_widget_data() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ $widget = sanitize_text_field($_POST['widget'] ?? '');
+
+ $network_data = new WP_MSD_Network_Data();
+ $network_data->clear_widget_cache($widget);
+
+ wp_send_json_success(['message' => __('Cache cleared', 'wp-multisite-dashboard')]);
+ }
+
+ public function clear_cache() {
+ if (!current_user_can('manage_network')) {
+ wp_send_json_error(__('Insufficient permissions', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $nonce = $_POST['nonce'] ?? '';
+ if (!wp_verify_nonce($nonce, 'msd_ajax_nonce') && !wp_verify_nonce($nonce, 'msd_clear_cache')) {
+ wp_send_json_error(__('Invalid nonce', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $cache_type = sanitize_text_field($_POST['cache_type'] ?? 'all');
+
+ try {
+ switch ($cache_type) {
+ case 'network':
+ $network_data = new WP_MSD_Network_Data();
+ $network_data->clear_all_caches();
+ break;
+
+ case 'all':
+ default:
+ $network_data = new WP_MSD_Network_Data();
+ $network_data->clear_all_caches();
+ wp_cache_flush();
+ break;
+ }
+
+ wp_send_json_success(['message' => __('Cache cleared successfully', 'wp-multisite-dashboard')]);
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to clear cache', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function manage_user_action() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ try {
+ $action = sanitize_text_field($_POST['user_action'] ?? '');
+ $user_id = intval($_POST['user_id'] ?? 0);
+ $additional_data = $_POST['additional_data'] ?? [];
+
+ $user_manager = new WP_MSD_User_Manager();
+ $result = $user_manager->perform_single_user_action($action, $user_id, $additional_data);
+
+ if ($result['success']) {
+ wp_send_json_success($result);
+ } else {
+ wp_send_json_error($result['message']);
+ }
+ } catch (Exception $e) {
+ wp_send_json_error(__('Failed to perform user action', 'wp-multisite-dashboard'));
+ }
+ }
+
+ public function check_plugin_update() {
+ if (!$this->verify_ajax_request()) {
+ return;
+ }
+
+ $plugin_core = WP_MSD_Plugin_Core::get_instance();
+ $update_checker = $plugin_core->get_update_checker();
+
+ if (!$update_checker) {
+ wp_send_json_success(['message' => __('No updates available', 'wp-multisite-dashboard')]);
+ return;
+ }
+
+ $update = $update_checker->checkForUpdates();
+
+ if ($update && version_compare($update->version, WP_MSD_VERSION, '>')) {
+ wp_send_json_success([
+ 'version' => $update->version,
+ 'details_url' => $update->details_url ?? '#'
+ ]);
+ } else {
+ wp_send_json_success(['message' => __('No updates available', 'wp-multisite-dashboard')]);
+ }
+ }
+
+ public function clear_widget_cache() {
+ if (!current_user_can('manage_network')) {
+ wp_send_json_error(__('Insufficient permissions', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ $nonce = $_POST['nonce'] ?? '';
+ if (!wp_verify_nonce($nonce, 'msd_ajax_nonce') && !wp_verify_nonce($nonce, 'msd_clear_cache')) {
+ wp_send_json_error(__('Invalid nonce', 'wp-multisite-dashboard'));
+ return;
+ }
+
+ delete_site_transient('msd_detected_widgets');
+
+ wp_send_json_success(['message' => __('Widget cache cleared successfully', 'wp-multisite-dashboard')]);
+ }
+
+ private function verify_ajax_request() {
+ if (!wp_verify_nonce($_POST['nonce'] ?? '', 'msd_ajax_nonce')) {
+ wp_send_json_error(__('Invalid nonce', 'wp-multisite-dashboard'));
+ return false;
+ }
+
+ if (!current_user_can('manage_network')) {
+ wp_send_json_error(__('Insufficient permissions', 'wp-multisite-dashboard'));
+ return false;
+ }
+
+ return true;
+ }
+
+ private function fetch_rss_feed($url, $limit = 5) {
+ $cache_key = 'msd_rss_' . md5($url);
+ $cached = get_site_transient($cache_key);
+
+ if ($cached !== false) {
+ return $cached;
+ }
+
+ $response = wp_remote_get($url, [
+ 'timeout' => 15,
+ 'headers' => [
+ 'User-Agent' => 'WP-Multisite-Dashboard/' . WP_MSD_VERSION
+ ]
+ ]);
+
+ if (is_wp_error($response)) {
+ return [];
+ }
+
+ $body = wp_remote_retrieve_body($response);
+ $feed_items = [];
+
+ try {
+ $xml = simplexml_load_string($body);
+ if ($xml === false) {
+ return [];
+ }
+
+ $items = $xml->channel->item ?? $xml->entry ?? [];
+ $count = 0;
+
+ foreach ($items as $item) {
+ if ($count >= $limit) break;
+
+ $title = (string)($item->title ?? '');
+ $link = (string)($item->link ?? $item->link['href'] ?? '');
+ $description = (string)($item->description ?? $item->summary ?? '');
+ $date = (string)($item->pubDate ?? $item->updated ?? '');
+
+ if (!empty($title) && !empty($link)) {
+ $description = html_entity_decode($description, ENT_QUOTES, 'UTF-8');
+ $description = wp_trim_words(strip_tags($description), 20);
+
+ $feed_items[] = [
+ 'title' => html_entity_decode($title, ENT_QUOTES, 'UTF-8'),
+ 'link' => $link,
+ 'description' => $description,
+ 'date' => $date
+ ];
+ $count++;
+ }
+ }
+ } catch (Exception $e) {
+ return [];
+ }
+
+ set_site_transient($cache_key, $feed_items, 3600);
+ return $feed_items;
+ }
+}
diff --git a/includes/class-helpers.php b/includes/class-helpers.php
new file mode 100644
index 0000000..7110c32
--- /dev/null
+++ b/includes/class-helpers.php
@@ -0,0 +1,331 @@
+";
+ echo "
" . esc_html($message) . "
";
+ echo "
";
+ }
+
+ public static function render_widget_header($title, $widget_id = null, $show_refresh = true) {
+ echo '';
+ }
+
+ public static function render_loading_state($message = null) {
+ $message = $message ?: __('Loading...', 'wp-multisite-dashboard');
+
+ echo '
';
+ echo '';
+ echo esc_html($message);
+ echo '
';
+ }
+
+ public static function render_empty_state($message, $action_text = null, $action_url = null) {
+ echo '
';
+ }
+
+ public static function render_error_state($message, $retry_action = null) {
+ echo '
';
+ echo '
' . esc_html($message) . '
';
+
+ if ($retry_action) {
+ echo '
';
+ }
+
+ echo '
';
+ }
+
+ public static function get_priority_badge($priority) {
+ $badges = [
+ 'low' => ['Low Priority', 'msd-priority-low'],
+ 'medium' => ['Medium Priority', 'msd-priority-medium'],
+ 'high' => ['High Priority', 'msd-priority-high']
+ ];
+
+ if (!isset($badges[$priority])) {
+ $priority = 'medium';
+ }
+
+ return sprintf(
+ '
%s',
+ esc_attr($badges[$priority][1]),
+ esc_html($badges[$priority][0])
+ );
+ }
+
+ public static function get_status_badge($status, $label = null) {
+ $badges = [
+ 'active' => ['Active', 'msd-status-good'],
+ 'inactive' => ['Inactive', 'msd-status-warning'],
+ 'critical' => ['Critical', 'msd-status-critical'],
+ 'warning' => ['Warning', 'msd-status-warning'],
+ 'good' => ['Good', 'msd-status-good'],
+ 'neutral' => ['Neutral', 'msd-status-neutral']
+ ];
+
+ if (!isset($badges[$status])) {
+ $status = 'neutral';
+ }
+
+ $display_label = $label ?: $badges[$status][0];
+
+ return sprintf(
+ '
%s',
+ esc_attr($badges[$status][1]),
+ esc_html($display_label)
+ );
+ }
+
+ public static function format_file_size($bytes) {
+ if ($bytes >= 1073741824) {
+ return number_format($bytes / 1073741824, 2) . ' GB';
+ } elseif ($bytes >= 1048576) {
+ return number_format($bytes / 1048576, 2) . ' MB';
+ } elseif ($bytes >= 1024) {
+ return number_format($bytes / 1024, 2) . ' KB';
+ } else {
+ return $bytes . ' bytes';
+ }
+ }
+
+ public static function format_time_ago($timestamp) {
+ if (empty($timestamp)) {
+ return __('Never', 'wp-multisite-dashboard');
+ }
+
+ if (is_string($timestamp)) {
+ $timestamp = strtotime($timestamp);
+ }
+
+ if (!$timestamp) {
+ return __('Unknown', 'wp-multisite-dashboard');
+ }
+
+ return human_time_diff($timestamp) . ' ago';
+ }
+
+ public static function render_progress_bar($percentage, $label = null, $status = 'good') {
+ $percentage = max(0, min(100, intval($percentage)));
+ $status_class = "msd-progress-{$status}";
+
+ echo '
';
+
+ if ($label) {
+ echo '
' . esc_html($label) . '
';
+ }
+
+ echo '
';
+
+ echo '
' . $percentage . '%
';
+ echo '
';
+ }
+
+ public static function render_data_table($headers, $rows, $empty_message = null) {
+ if (empty($rows)) {
+ if ($empty_message) {
+ self::render_empty_state($empty_message);
+ }
+ return;
+ }
+
+ echo '
';
+ echo '
';
+
+ if (!empty($headers)) {
+ echo '';
+ foreach ($headers as $header) {
+ echo '' . esc_html($header) . ' | ';
+ }
+ echo '
';
+ }
+
+ echo '';
+ foreach ($rows as $row) {
+ echo '';
+ foreach ($row as $cell) {
+ echo '' . $cell . ' | ';
+ }
+ echo '
';
+ }
+ echo '';
+
+ echo '
';
+ echo '
';
+ }
+
+ public static function render_action_buttons($actions) {
+ if (empty($actions)) {
+ return;
+ }
+
+ echo '
';
+ }
+
+ public static function sanitize_widget_data($data) {
+ if (is_array($data)) {
+ return array_map([self::class, 'sanitize_widget_data'], $data);
+ }
+
+ if (is_string($data)) {
+ return sanitize_text_field($data);
+ }
+
+ return $data;
+ }
+
+ public static function validate_nonce($nonce, $action) {
+ return wp_verify_nonce($nonce, $action);
+ }
+
+ public static function can_manage_network() {
+ return current_user_can('manage_network');
+ }
+
+ public static function get_current_screen_id() {
+ $screen = get_current_screen();
+ return $screen ? $screen->id : '';
+ }
+
+ public static function is_network_admin_page($page_slug = null) {
+ if (!is_network_admin()) {
+ return false;
+ }
+
+ if ($page_slug) {
+ return isset($_GET['page']) && $_GET['page'] === $page_slug;
+ }
+
+ return true;
+ }
+
+ public static function format_number($num) {
+ if ($num >= 1000000) {
+ return number_format($num / 1000000, 1) . 'M';
+ } elseif ($num >= 1000) {
+ return number_format($num / 1000, 1) . 'K';
+ }
+ return number_format($num);
+ }
+
+ public static function truncate_text($text, $length = 50, $suffix = '...') {
+ if (strlen($text) <= $length) {
+ return $text;
+ }
+ return substr($text, 0, $length) . $suffix;
+ }
+
+ public static function escape_js($text) {
+ return esc_js($text);
+ }
+
+ public static function get_default_avatar_url() {
+ return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48Y2lyY2xlIGN4PSIyMCIgY3k9IjIwIiByPSIyMCIgZmlsbD0iI2Y2ZjdmNyIgc3Ryb2tlPSIjZGRkIi8+PGNpcmNsZSBjeD0iMjAiIGN5PSIxNSIgcj0iNiIgZmlsbD0iIzk5OSIvPjxlbGxpcHNlIGN4PSIyMCIgY3k9IjMzIiByeD0iMTAiIHJ5PSI3IiBmaWxsPSIjOTk5Ii8+PC9zdmc+';
+ }
+
+ public static function get_default_favicon_url() {
+ return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIj48cmVjdCB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9IiNmMGYwZjAiLz48dGV4dCB4PSI1MCUiIHk9IjUwJSIgZm9udC1mYW1pbHk9IkFyaWFsLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEyIiBmaWxsPSIjOTk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBkeT0iMC4zNWVtIj5TPC90ZXh0Pjwvc3ZnPg==';
+ }
+
+ public static function is_valid_url($url) {
+ return filter_var($url, FILTER_VALIDATE_URL) !== false;
+ }
+
+ public static function is_valid_email($email) {
+ return is_email($email);
+ }
+
+ public static function get_storage_status_class($percentage) {
+ if ($percentage > 90) {
+ return 'critical';
+ } elseif ($percentage > 75) {
+ return 'warning';
+ }
+ return 'good';
+ }
+
+ public static function get_user_status_class($status) {
+ $status_map = [
+ 'active' => 'good',
+ 'recent' => 'good',
+ 'inactive' => 'warning',
+ 'very_inactive' => 'critical',
+ 'never_logged_in' => 'neutral'
+ ];
+ return $status_map[$status] ?? 'neutral';
+ }
+
+ public static function get_user_status_label($status) {
+ $status_labels = [
+ 'active' => __('Active', 'wp-multisite-dashboard'),
+ 'recent' => __('Recent', 'wp-multisite-dashboard'),
+ 'inactive' => __('Inactive', 'wp-multisite-dashboard'),
+ 'very_inactive' => __('Very Inactive', 'wp-multisite-dashboard'),
+ 'never_logged_in' => __('Never Logged In', 'wp-multisite-dashboard')
+ ];
+ return $status_labels[$status] ?? __('Unknown', 'wp-multisite-dashboard');
+ }
+
+ public static function decode_html_entities($text) {
+ return html_entity_decode($text, ENT_QUOTES, 'UTF-8');
+ }
+
+ public static function strip_tags_and_limit($text, $limit = 100) {
+ $text = strip_tags($text);
+ return self::truncate_text($text, $limit);
+ }
+}
diff --git a/includes/class-plugin-core.php b/includes/class-plugin-core.php
new file mode 100644
index 0000000..7047c50
--- /dev/null
+++ b/includes/class-plugin-core.php
@@ -0,0 +1,294 @@
+init_update_checker();
+ $this->init_components();
+ }
+
+ private function init_components() {
+ $this->ajax_handler = new WP_MSD_Ajax_Handler();
+ $this->admin_interface = new WP_MSD_Admin_Interface();
+ $this->settings_manager = new WP_MSD_Settings_Manager();
+ }
+
+ private function init_update_checker() {
+ if (file_exists(plugin_dir_path(__FILE__) . '../lib/plugin-update-checker/plugin-update-checker.php')) {
+ require_once plugin_dir_path(__FILE__) . '../lib/plugin-update-checker/plugin-update-checker.php';
+
+ if (class_exists('YahnisElsts\PluginUpdateChecker\v5p3\PucFactory')) {
+ $this->update_checker = \YahnisElsts\PluginUpdateChecker\v5p3\PucFactory::buildUpdateChecker(
+ 'https://updates.weixiaoduo.com/wp-multisite-dashboard.json',
+ WP_MSD_PLUGIN_DIR . 'wp-multisite-dashboard.php',
+ 'wp-multisite-dashboard'
+ );
+ }
+ }
+ }
+
+ public function init() {
+ load_plugin_textdomain('wp-multisite-dashboard');
+
+ $this->enabled_widgets = get_site_option('msd_enabled_widgets', [
+ 'msd_network_overview' => 1,
+ 'msd_quick_site_management' => 1,
+ 'msd_storage_performance' => 1,
+ 'msd_server_info' => 1,
+ 'msd_quick_links' => 1,
+ 'msd_version_info' => 1,
+ 'msd_custom_news' => 1,
+ 'msd_network_settings' => 1,
+ 'msd_user_management' => 1,
+ 'msd_contact_info' => 1,
+ 'msd_last_edits' => 1,
+ 'msd_todo_widget' => 1
+ ]);
+
+ $this->enhance_network_dashboard();
+ }
+
+ public function admin_init() {
+ if (!current_user_can('manage_network')) {
+ return;
+ }
+
+ add_action('wp_network_dashboard_setup', [$this->admin_interface, 'add_network_widgets']);
+ add_action('wp_network_dashboard_setup', [$this, 'manage_system_widgets'], 20);
+ add_action('wp_network_dashboard_setup', [$this, 'cache_detected_widgets'], 30);
+ add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
+ }
+
+ public function add_admin_menu() {
+ add_submenu_page(
+ 'settings.php',
+ 'Dashboard Settings',
+ 'Dashboard Settings',
+ 'manage_network',
+ 'msd-settings',
+ [$this->settings_manager, 'render_settings_page']
+ );
+ }
+
+ public function enqueue_admin_scripts($hook) {
+ $allowed_hooks = ['index.php', 'dashboard.php', 'settings_page_msd-settings'];
+
+ if (!in_array($hook, $allowed_hooks)) {
+ return;
+ }
+
+ wp_enqueue_script(
+ 'msd-dashboard-core',
+ WP_MSD_PLUGIN_URL . 'assets/dashboard-core.js',
+ ['jquery'],
+ WP_MSD_VERSION,
+ true
+ );
+
+ wp_enqueue_script(
+ 'msd-dashboard-widgets',
+ WP_MSD_PLUGIN_URL . 'assets/dashboard-widgets.js',
+ ['msd-dashboard-core'],
+ WP_MSD_VERSION,
+ true
+ );
+
+ wp_enqueue_script(
+ 'msd-dashboard-modals',
+ WP_MSD_PLUGIN_URL . 'assets/dashboard-modals.js',
+ ['msd-dashboard-core', 'jquery-ui-sortable'],
+ WP_MSD_VERSION,
+ true
+ );
+
+ wp_enqueue_style(
+ 'msd-dashboard',
+ WP_MSD_PLUGIN_URL . 'assets/dashboard.css',
+ [],
+ WP_MSD_VERSION
+ );
+
+ wp_localize_script('msd-dashboard-core', 'msdAjax', [
+ 'ajaxurl' => admin_url('admin-ajax.php'),
+ 'nonce' => wp_create_nonce('msd_ajax_nonce'),
+ 'strings' => [
+ 'confirm_action' => __('Are you sure?', 'wp-multisite-dashboard'),
+ 'loading' => __('Loading...', 'wp-multisite-dashboard'),
+ 'error_occurred' => __('An error occurred', 'wp-multisite-dashboard'),
+ 'refresh_success' => __('Data refreshed successfully', 'wp-multisite-dashboard'),
+ 'confirm_delete' => __('Are you sure you want to delete this item?', 'wp-multisite-dashboard'),
+ 'save_success' => __('Saved successfully', 'wp-multisite-dashboard')
+ ]
+ ]);
+ }
+
+ private function enhance_network_dashboard() {
+ add_filter('dashboard_recent_posts_query_args', [$this, 'enhance_recent_posts']);
+ add_action('wp_network_dashboard_setup', [$this, 'enhance_right_now_widget']);
+ add_action('admin_footer', [$this, 'add_right_now_enhancements']);
+ }
+
+ public function enhance_recent_posts($query_args) {
+ $query_args['post_status'] = 'publish';
+ return $query_args;
+ }
+
+ public function enhance_right_now_widget() {
+ add_action('network_dashboard_right_now_content_table_end', [$this, 'add_right_now_plugin_link']);
+ }
+
+ public function add_right_now_plugin_link() {
+ if (!current_user_can('manage_network_plugins')) {
+ return;
+ }
+
+ echo '
';
+ echo '' . __('Add New Plugin', 'wp-multisite-dashboard') . ' | ';
+ echo '' . __('Manage Plugins', 'wp-multisite-dashboard') . ' | ';
+ echo '
';
+ }
+
+ public function add_right_now_enhancements() {
+ $screen = get_current_screen();
+ if (!$screen || $screen->id !== 'dashboard-network') {
+ return;
+ }
+
+ $sites_count = get_sites(['count' => true]);
+ $users_count = count_users()['total_users'];
+ $themes_count = count(wp_get_themes(['allowed' => 'network']));
+ $plugins_count = count(get_plugins());
+ ?>
+
+
+
+ $priorities) {
+ if (!is_array($priorities)) continue;
+
+ foreach ($priorities as $priority => $widgets) {
+ if (!is_array($widgets)) continue;
+
+ foreach ($widgets as $widget_id => $widget_data) {
+ if (strpos($widget_id, 'msd_') !== 0) {
+ $widget_title = $widget_data['title'] ?? $widget_id;
+ if (is_callable($widget_title)) {
+ $widget_title = $widget_id;
+ }
+
+ $detected_widgets[$widget_id] = [
+ 'id' => $widget_id,
+ 'title' => $widget_title,
+ 'context' => $context,
+ 'priority' => $priority,
+ 'is_custom' => false,
+ 'is_system' => in_array($widget_id, [
+ 'network_dashboard_right_now',
+ 'dashboard_activity',
+ 'dashboard_plugins',
+ 'dashboard_primary',
+ 'dashboard_secondary'
+ ])
+ ];
+ }
+ }
+ }
+ }
+
+ set_site_transient('msd_detected_widgets', $detected_widgets, 12 * HOUR_IN_SECONDS);
+ }
+
+ public function get_enabled_widgets() {
+ return $this->enabled_widgets;
+ }
+
+ public function get_update_checker() {
+ return $this->update_checker;
+ }
+}
diff --git a/includes/class-settings-manager.php b/includes/class-settings-manager.php
new file mode 100644
index 0000000..f6f7b8b
--- /dev/null
+++ b/includes/class-settings-manager.php
@@ -0,0 +1,148 @@
+save_settings();
+ return;
+ }
+
+ $widget_options = [
+ 'msd_network_overview' => 'Network Overview',
+ 'msd_quick_site_management' => 'Quick Site Management',
+ 'msd_storage_performance' => 'Storage Usage',
+ 'msd_server_info' => 'Server Information',
+ 'msd_quick_links' => 'Quick Links',
+ 'msd_version_info' => 'Version Information',
+ 'msd_custom_news' => 'Network News',
+ 'msd_user_management' => 'User Management',
+ 'msd_contact_info' => 'Contact Information',
+ 'msd_last_edits' => 'Recent Network Activity',
+ 'msd_todo_widget' => 'Todo List'
+ ];
+
+ include WP_MSD_PLUGIN_DIR . 'templates/settings-page.php';
+ }
+
+ private function save_settings() {
+ $enabled_widgets = [];
+ $widget_options = [
+ 'msd_network_overview',
+ 'msd_quick_site_management',
+ 'msd_storage_performance',
+ 'msd_server_info',
+ 'msd_quick_links',
+ 'msd_version_info',
+ 'msd_custom_news',
+ 'msd_user_management',
+ 'msd_contact_info',
+ 'msd_last_edits',
+ 'msd_todo_widget'
+ ];
+
+ foreach ($widget_options as $widget_id) {
+ if (isset($_POST['widgets'][$widget_id])) {
+ $enabled_widgets[$widget_id] = 1;
+ }
+ }
+
+ update_site_option('msd_enabled_widgets', $enabled_widgets);
+
+ $disabled_system_widgets = [];
+ if (isset($_POST['system_widgets']) && is_array($_POST['system_widgets'])) {
+ $available_widgets = $this->get_available_system_widgets();
+ foreach ($available_widgets as $widget_id => $widget_data) {
+ if (!$widget_data['is_custom'] && !isset($_POST['system_widgets'][$widget_id])) {
+ $disabled_system_widgets[] = $widget_id;
+ }
+ }
+ }
+
+ update_site_option('msd_disabled_system_widgets', $disabled_system_widgets);
+
+ wp_safe_redirect(add_query_arg('updated', 'true', network_admin_url('settings.php?page=msd-settings')));
+ exit;
+ }
+
+ public function get_available_system_widgets() {
+ $known_system_widgets = [
+ 'network_dashboard_right_now' => [
+ 'id' => 'network_dashboard_right_now',
+ 'title' => 'Right Now',
+ 'context' => 'normal',
+ 'priority' => 'core',
+ 'is_custom' => false,
+ 'is_system' => true
+ ],
+ 'dashboard_activity' => [
+ 'id' => 'dashboard_activity',
+ 'title' => 'Activity',
+ 'context' => 'normal',
+ 'priority' => 'high',
+ 'is_custom' => false,
+ 'is_system' => true
+ ],
+ 'dashboard_plugins' => [
+ 'id' => 'dashboard_plugins',
+ 'title' => 'Plugins',
+ 'context' => 'normal',
+ 'priority' => 'core',
+ 'is_custom' => false,
+ 'is_system' => true
+ ],
+ 'dashboard_primary' => [
+ 'id' => 'dashboard_primary',
+ 'title' => 'WordPress Events and News',
+ 'context' => 'side',
+ 'priority' => 'core',
+ 'is_custom' => false,
+ 'is_system' => true
+ ],
+ 'dashboard_secondary' => [
+ 'id' => 'dashboard_secondary',
+ 'title' => 'Other WordPress News',
+ 'context' => 'side',
+ 'priority' => 'core',
+ 'is_custom' => false,
+ 'is_system' => true
+ ]
+ ];
+
+ $available_widgets = $known_system_widgets;
+
+ $cached_widgets = get_site_transient('msd_detected_widgets');
+ if ($cached_widgets && is_array($cached_widgets)) {
+ foreach ($cached_widgets as $widget_id => $widget_data) {
+ if (!isset($available_widgets[$widget_id])) {
+ $available_widgets[$widget_id] = $widget_data;
+ }
+ }
+ }
+
+ return $available_widgets;
+ }
+
+ public function get_widget_description($widget_id) {
+ $descriptions = [
+ 'msd_network_overview' => __('Network statistics and multisite configuration information', 'wp-multisite-dashboard'),
+ 'msd_quick_site_management' => __('Quick access to recently active sites with favicons', 'wp-multisite-dashboard'),
+ 'msd_storage_performance' => __('Top 5 sites by storage usage and performance insights', 'wp-multisite-dashboard'),
+ 'msd_server_info' => __('Server specifications and WordPress environment details', 'wp-multisite-dashboard'),
+ 'msd_quick_links' => __('Customizable quick access links for common tasks with drag-and-drop reordering', 'wp-multisite-dashboard'),
+ 'msd_version_info' => __('Plugin version and system information with help links', 'wp-multisite-dashboard'),
+ 'msd_custom_news' => __('Custom news sources and updates', 'wp-multisite-dashboard'),
+ 'msd_network_settings' => __('Network configuration and settings overview', 'wp-multisite-dashboard'),
+ 'msd_user_management' => __('Recent user registrations and user management tools', 'wp-multisite-dashboard'),
+ 'msd_contact_info' => __('Network administrator contact information with instant messaging and QR code support', 'wp-multisite-dashboard'),
+ 'msd_last_edits' => __('Recent posts, pages, and content activity across the network', 'wp-multisite-dashboard'),
+ 'msd_todo_widget' => __('Simple todo list for network administrators with priority levels', 'wp-multisite-dashboard')
+ ];
+
+ return $descriptions[$widget_id] ?? '';
+ }
+}
diff --git a/includes/class-todo-manager.php b/includes/class-todo-manager.php
index 4c493b5..3706ef0 100644
--- a/includes/class-todo-manager.php
+++ b/includes/class-todo-manager.php
@@ -62,9 +62,9 @@ class WP_MSD_Todo_Manager {
$where_sql = implode(' AND ', $where_clauses);
- $sql = "SELECT * FROM {$this->table_name}
- WHERE {$where_sql}
- ORDER BY completed ASC, priority DESC, created_at DESC
+ $sql = "SELECT * FROM {$this->table_name}
+ WHERE {$where_sql}
+ ORDER BY completed ASC, priority DESC, created_at DESC
LIMIT %d";
$where_values[] = $limit;
@@ -104,7 +104,7 @@ class WP_MSD_Todo_Manager {
}
$user_id = $user_id ?: get_current_user_id();
-
+
if (!$user_id) {
return false;
}
@@ -125,7 +125,7 @@ class WP_MSD_Todo_Manager {
if ($result !== false) {
$this->clear_cache();
-
+
if (class_exists('WP_MSD_Network_Data')) {
$network_data = new WP_MSD_Network_Data();
$network_data->log_activity(
@@ -143,7 +143,7 @@ class WP_MSD_Todo_Manager {
public function delete_todo_item($item_id, $user_id = null) {
$item_id = intval($item_id);
-
+
if (!$item_id) {
return false;
}
@@ -170,7 +170,7 @@ class WP_MSD_Todo_Manager {
if ($result !== false && $item) {
$this->clear_cache();
-
+
if (class_exists('WP_MSD_Network_Data')) {
$network_data = new WP_MSD_Network_Data();
$network_data->log_activity(
@@ -187,7 +187,7 @@ class WP_MSD_Todo_Manager {
public function toggle_todo_item($item_id, $completed = null, $user_id = null) {
$item_id = intval($item_id);
-
+
if (!$item_id) {
return false;
}
@@ -210,11 +210,11 @@ class WP_MSD_Todo_Manager {
...$where_values
)
);
-
+
if (!$current_item) {
return false;
}
-
+
$completed = !$current_item->completed;
}
@@ -238,7 +238,7 @@ class WP_MSD_Todo_Manager {
public function update_todo_item($item_id, $data, $user_id = null) {
$item_id = intval($item_id);
-
+
if (!$item_id) {
return false;
}
@@ -329,7 +329,7 @@ class WP_MSD_Todo_Manager {
];
$total_result = $this->wpdb->get_row(
- "SELECT
+ "SELECT
COUNT(*) as total,
SUM(completed) as completed,
SUM(CASE WHEN completed = 0 THEN 1 ELSE 0 END) as pending,
@@ -345,9 +345,9 @@ class WP_MSD_Todo_Manager {
}
$priority_results = $this->wpdb->get_results(
- "SELECT priority, COUNT(*) as count
- FROM {$this->table_name}
- WHERE completed = 0
+ "SELECT priority, COUNT(*) as count
+ FROM {$this->table_name}
+ WHERE completed = 0
GROUP BY priority"
);
@@ -358,10 +358,10 @@ class WP_MSD_Todo_Manager {
}
$user_results = $this->wpdb->get_results(
- "SELECT user_id, COUNT(*) as total, SUM(completed) as completed
- FROM {$this->table_name}
- GROUP BY user_id
- ORDER BY total DESC
+ "SELECT user_id, COUNT(*) as total, SUM(completed) as completed
+ FROM {$this->table_name}
+ GROUP BY user_id
+ ORDER BY total DESC
LIMIT 10"
);
@@ -392,10 +392,10 @@ class WP_MSD_Todo_Manager {
return $cached;
}
- $sql = "SELECT * FROM {$this->table_name}
- WHERE due_date < NOW()
- AND completed = 0
- ORDER BY due_date ASC
+ $sql = "SELECT * FROM {$this->table_name}
+ WHERE due_date < NOW()
+ AND completed = 0
+ ORDER BY due_date ASC
LIMIT %d";
$results = $this->wpdb->get_results(
@@ -445,7 +445,7 @@ class WP_MSD_Todo_Manager {
if ($result !== false) {
$this->clear_cache();
-
+
if (class_exists('WP_MSD_Network_Data')) {
$network_data = new WP_MSD_Network_Data();
$network_data->log_activity(
@@ -489,7 +489,7 @@ class WP_MSD_Todo_Manager {
}
$cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
-
+
$result = $this->wpdb->delete(
$this->table_name,
[
@@ -501,7 +501,7 @@ class WP_MSD_Todo_Manager {
if ($result !== false) {
$this->clear_cache();
-
+
if (class_exists('WP_MSD_Network_Data')) {
$network_data = new WP_MSD_Network_Data();
$network_data->log_activity(
@@ -515,4 +515,4 @@ class WP_MSD_Todo_Manager {
return $result !== false;
}
-}
\ No newline at end of file
+}
diff --git a/languages/wp-multisite-dashboard-zh_CN.l10n.php b/languages/wp-multisite-dashboard-zh_CN.l10n.php
new file mode 100644
index 0000000..0769cf6
--- /dev/null
+++ b/languages/wp-multisite-dashboard-zh_CN.l10n.php
@@ -0,0 +1,3 @@
+NULL,'plural-forms'=>'nplurals=2; plural=(n != 1);','language'=>'zh_CN','pot-creation-date'=>'2025-06-24 04:39+0800','po-revision-date'=>'2025-06-24 04:42+0800','translation-revision-date'=>'2025-06-24 04:42+0800','project-id-version'=>'WP Multisite Dashboard','x-generator'=>'Poedit 3.6','messages'=>['Loading...'=>'加载中...','Never'=>'从不','Unknown'=>'未知','Version %s available! '=>'版本:%s 已可用!','View Details'=>'查看详情','Edit Contact Info'=>'编辑联系信息','No quick links configured.'=>'未配置快速链接。','Add Links'=>'添加链接','Edit Links'=>'编辑链接','Failed to load network overview'=>'加载网络概览失败','Failed to load sites'=>'加载站点失败','Failed to load storage data'=>'加载存储数据失败','Failed to load news'=>'加载新闻失败','Failed to load network settings'=>'加载网络设置失败','Failed to load user data'=>'加载用户数据失败','Failed to load recent activity'=>'加载最近活动失败','Failed to load todo items'=>'加载待办事项失败','News sources saved successfully'=>'新闻源保存成功','Quick links saved successfully'=>'快速链接保存成功','Contact information saved successfully'=>'联系信息保存成功','Title is required'=>'标题必填','Todo item created'=>'待办事项已创建','Failed to create todo item'=>'创建待办事项失败','ID and title are required'=>'ID 和标题必填','No todos found'=>'未找到待办事项','Todo item updated'=>'待办事项已更新','Failed to update todo item'=>'更新待办事项失败','Todo item not found'=>'待办事项未找到','ID is required'=>'ID 必填','Todo item deleted'=>'待办事项已删除','Failed to delete todo item'=>'删除待办事项失败','Todo status updated'=>'待办事项状态已更新','Failed to update todo status'=>'更新待办事项状态失败','Invalid order data'=>'无效的排序数据','Links reordered successfully'=>'链接重新排序成功','Invalid widget ID'=>'无效的小工具 ID','Widget settings updated'=>'小工具设置已更新','Cache cleared'=>'缓存已清除','Insufficient permissions'=>'权限不足','Invalid nonce'=>'无效的随机数','Cache cleared successfully'=>'缓存清除成功','Failed to clear cache'=>'清除缓存失败','Failed to perform user action'=>'执行用户操作失败','Active'=>'启用','Subdomain Installation'=>'子域名安装','Subdirectory Installation'=>'子目录安装','Registration disabled'=>'注册已禁用','User registration only'=>'仅用户注册','Site registration only'=>'仅站点注册','User and site registration'=>'用户和站点注册','Post'=>'文章','Page'=>'页面','All systems operating normally'=>'所有系统运行正常','No sites found'=>'未找到站点','No recent activity'=>'无最近活动','Unknown Site'=>'未知站点','Are you sure?'=>'您确定吗?','An error occurred'=>'发生错误','Data refreshed successfully'=>'数据刷新成功','Are you sure you want to delete this item?'=>'您确定要删除此项目吗?','Saved successfully'=>'保存成功','Add New Plugin'=>'添加新插件','Manage Plugins'=>'管理插件','Network statistics and multisite configuration information'=>'网络统计和多站点配置信息','Quick access to recently active sites with favicons'=>'带网站图标的最近活跃站点快速访问','Top 5 sites by storage usage and performance insights'=>'按存储使用情况排名前 5 的站点和性能洞察','Server specifications and WordPress environment details'=>'服务器规格和 WordPress 环境详情','Customizable quick access links for common tasks with drag-and-drop reordering'=>'可自定义的常用任务快速访问链接,支持拖放重新排序','Plugin version and system information with help links'=>'插件版本和系统信息及帮助链接','Custom news sources and updates'=>'自定义新闻源和更新','Network configuration and settings overview'=>'网络配置和设置概览','Recent user registrations and user management tools'=>'最近用户注册和用户管理工具','Network administrator contact information with instant messaging and QR code support'=>'网络管理员联系信息,支持即时消息和二维码','Recent posts, pages, and content activity across the network'=>'整个网络的最近文章、页面和内容活动','Simple todo list for network administrators with priority levels'=>'网络管理员的简单待办事项列表,带优先级','Low'=>'低','Medium'=>'中','High'=>'高','Edit User'=>'编辑用户','Delete User'=>'删除用户','Send Password Reset'=>'发送密码重置','Make Super Admin'=>'设为超级管理员','Remove Super Admin'=>'移除超级管理员','Activate User'=>'激活用户','Deactivate User'=>'停用用户','User registration enabled'=>'用户注册已启用','Site registration enabled'=>'站点注册已启用','User and site registration enabled'=>'用户和站点注册已启用','No users selected'=>'未选择用户','Invalid user selection'=>'用户选择无效','Bulk action completed: %d successful, %d failed'=>'批量操作完成:%d 成功,%d 失败','Insufficient permissions to delete users'=>'删除用户权限不足','User not found'=>'用户未找到','Cannot delete super admin'=>'无法删除超级管理员','Cannot delete your own account'=>'无法删除您自己的账户','User deleted'=>'用户已删除','Failed to delete user'=>'删除用户失败','Bulk delete completed: %d successful, %d failed'=>'批量删除完成:%d 成功,%d 失败','Unknown action'=>'未知操作','User deleted successfully'=>'用户删除成功','Site ID required'=>'需要站点 ID','Site not found'=>'站点未找到','User added to site'=>'用户已添加到站点','Cannot remove super admin from main site'=>'无法从主站点移除超级管理员','User removed from site'=>'用户已从站点移除','Site ID and role required'=>'需要站点 ID 和角色','User role changed'=>'用户角色已更改','A password reset has been requested for your account on %s.'=>'您在 %s 上的账户已请求密码重置。','Username: %s'=>'用户名:%s','To reset your password, visit the following address:'=>'要重置您的密码,请访问以下地址:','This link will expire in 24 hours.'=>'此链接将在 24 小时后过期。','[%s] Password Reset'=>'[%s] 密码重置','Failed to send password reset email'=>'发送密码重置邮件失败','Password reset email sent'=>'密码重置邮件已发送','User activated'=>'用户已激活','Cannot deactivate super admin'=>'无法停用超级管理员','Cannot deactivate your own account'=>'无法停用您自己的账户','User deactivated'=>'用户已停用','User is already a super admin'=>'用户已经是超级管理员','User promoted to super admin'=>'用户已提升为超级管理员','Cannot demote yourself'=>'无法降级您自己','Cannot demote the last super admin'=>'无法降级最后一个超级管理员','User demoted from super admin'=>'用户已从超级管理员降级','Bulk %s performed on %d users: %d successful, %d failed'=>'批量 %s 已对 %d 个用户执行:%d 成功,%d 失败','%d users would be deleted'=>'将删除 %d 个用户','%d inactive users deleted'=>'已删除 %d 个非活跃用户','Edit Contact Information'=>'编辑联系信息','Organization Name:'=>'组织名称:','Network Administrator'=>'网络管理员','Email:'=>'邮箱:','Phone:'=>'电话:','Website:'=>'网站:','Description:'=>'描述:','Brief description or role'=>'简要描述或角色','QQ:'=>'QQ:','WeChat:'=>'微信:','WhatsApp:'=>'WhatsApp:','Telegram:'=>'Telegram:','QR Code Image URL:'=>'二维码图片链接:','Select Image'=>'选择图片','Upload or provide URL for a QR code image (WeChat, contact info, etc.)'=>'上传或提供二维码图片链接(微信、联系信息等)','Save Contact Info'=>'保存联系信息','Cancel'=>'取消','Configure News Sources'=>'配置新闻源','Source Name'=>'源名称','RSS Feed URL'=>'RSS 订阅链接','Enabled'=>'已启用','Remove'=>'移除','Add News Source'=>'添加新闻源','Popular RSS Feeds'=>'热门 RSS 源','Save News Sources'=>'保存新闻源','Configure Quick Links'=>'配置快速链接','Link Title'=>'链接标题','dashicons-admin-home or 🏠'=>'dashicons-admin-home 或 🏠','Icon (Dashicon class or emoji)'=>'图标(Dashicon 类或表情符号)','Open in new tab'=>'在新标签页打开','Add Link'=>'添加链接','Icon Options'=>'图标选项','WordPress Dashicons'=>'WordPress Dashicons','Home'=>'首页','Analytics'=>'分析','Emojis'=>'表情符号','Settings'=>'设置','Dashicons:'=>'Dashicons:','Built into WordPress, always available. Use format:'=>'内置于 WordPress,始终可用。使用格式:','Emojis:'=>'表情符号:','Copy and paste emoji directly. Works on all devices.'=>'直接复制粘贴表情符号。适用于所有设备。','Find more Dashicons at %s'=>'在 %s 查找更多 Dashicons','Save Links'=>'保存链接','Settings saved successfully!'=>'设置保存成功!','Version: %s'=>'版本:%s','Documentation'=>'文档','Support'=>'支持','Save Widget Settings'=>'保存小工具设置','Cache Management'=>'缓存管理','Clear cached data to refresh dashboard widgets.'=>'清除缓存数据以刷新仪表板小工具。','Clear All Caches'=>'清除所有缓存','Clear Network Data'=>'清除网络数据','WP Multisite Dashboard requires WordPress Multisite to be enabled.'=>'WP 多站点仪表板需要启用 WordPress 多站点。','This plugin requires WordPress Multisite to be enabled.'=>'此插件需要启用 WordPress 多站点。','WP Multisite Dashboard has been activated successfully!'=>'WP 多站点仪表板已成功激活!','WP Multisite Dashboard'=>'WP 多站点仪表板','https://wpmultisite.com/plugins/wp-multisite-dashboard'=>'https://wpmultisite.com/plugins/wp-multisite-dashboard','Essential dashboard widgets for WordPress multisite administrators'=>'WordPress 多站点管理员的必备仪表板小工具','WPMultisite.com'=>'文派多站点','https://WPMultisite.com'=>'https://WPMultisite.com']];
diff --git a/languages/wp-multisite-dashboard-zh_CN.mo b/languages/wp-multisite-dashboard-zh_CN.mo
new file mode 100755
index 0000000..7abdae1
Binary files /dev/null and b/languages/wp-multisite-dashboard-zh_CN.mo differ
diff --git a/languages/wp-multisite-dashboard-zh_CN.po b/languages/wp-multisite-dashboard-zh_CN.po
new file mode 100755
index 0000000..df17450
--- /dev/null
+++ b/languages/wp-multisite-dashboard-zh_CN.po
@@ -0,0 +1,1005 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: WP Multisite Dashboard\n"
+"POT-Creation-Date: 2025-06-24 04:39+0800\n"
+"PO-Revision-Date: 2025-06-24 04:42+0800\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.6\n"
+"X-Poedit-Basepath: ..\n"
+"X-Poedit-Flags-xgettext: --add-comments=translators:\n"
+"X-Poedit-WPHeader: wp-multisite-dashboard.php\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-KeywordsList: "
+"__;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
+"X-Poedit-SearchPath-0: .\n"
+"X-Poedit-SearchPathExcluded-0: *.min.js\n"
+
+#: includes/admin-template.php:38 includes/class-admin-interface.php:51
+#: includes/class-admin-interface.php:57 includes/class-admin-interface.php:63
+#: includes/class-admin-interface.php:193
+#: includes/class-admin-interface.php:200
+#: includes/class-admin-interface.php:287
+#: includes/class-admin-interface.php:330 includes/class-helpers.php:38
+#: includes/class-plugin-core.php:138
+msgid "Loading..."
+msgstr "加载中..."
+
+#: includes/admin-template.php:123 includes/class-helpers.php:123
+#: includes/class-user-manager.php:71 includes/class-user-manager.php:426
+msgid "Never"
+msgstr "从不"
+
+#: includes/admin-template.php:131 includes/class-helpers.php:131
+#: includes/class-helpers.php:320 includes/class-network-data.php:171
+#: includes/class-user-manager.php:189 includes/class-user-manager.php:431
+msgid "Unknown"
+msgstr "未知"
+
+#: includes/class-admin-interface.php:150
+#, php-format
+msgid "Version %s available! "
+msgstr "版本:%s 已可用!"
+
+#: includes/class-admin-interface.php:151
+msgid "View Details"
+msgstr "查看详情"
+
+#: includes/class-admin-interface.php:278
+msgid "Edit Contact Info"
+msgstr "编辑联系信息"
+
+#: includes/class-admin-interface.php:298
+msgid "No quick links configured."
+msgstr "未配置快速链接。"
+
+#: includes/class-admin-interface.php:299
+msgid "Add Links"
+msgstr "添加链接"
+
+#: includes/class-admin-interface.php:320
+msgid "Edit Links"
+msgstr "编辑链接"
+
+#: includes/class-ajax-handler.php:65
+msgid "Failed to load network overview"
+msgstr "加载网络概览失败"
+
+#: includes/class-ajax-handler.php:79
+msgid "Failed to load sites"
+msgstr "加载站点失败"
+
+#: includes/class-ajax-handler.php:93
+msgid "Failed to load storage data"
+msgstr "加载存储数据失败"
+
+#: includes/class-ajax-handler.php:190
+msgid "Failed to load news"
+msgstr "加载新闻失败"
+
+#: includes/class-ajax-handler.php:204
+msgid "Failed to load network settings"
+msgstr "加载网络设置失败"
+
+#: includes/class-ajax-handler.php:218
+msgid "Failed to load user data"
+msgstr "加载用户数据失败"
+
+#: includes/class-ajax-handler.php:232
+msgid "Failed to load recent activity"
+msgstr "加载最近活动失败"
+
+#: includes/class-ajax-handler.php:258
+msgid "Failed to load todo items"
+msgstr "加载待办事项失败"
+
+#: includes/class-ajax-handler.php:291
+msgid "News sources saved successfully"
+msgstr "新闻源保存成功"
+
+#: includes/class-ajax-handler.php:314
+msgid "Quick links saved successfully"
+msgstr "快速链接保存成功"
+
+#: includes/class-ajax-handler.php:336
+msgid "Contact information saved successfully"
+msgstr "联系信息保存成功"
+
+#: includes/class-ajax-handler.php:349
+msgid "Title is required"
+msgstr "标题必填"
+
+#: includes/class-ajax-handler.php:373
+msgid "Todo item created"
+msgstr "待办事项已创建"
+
+#: includes/class-ajax-handler.php:375 includes/class-ajax-handler.php:378
+msgid "Failed to create todo item"
+msgstr "创建待办事项失败"
+
+#: includes/class-ajax-handler.php:393
+msgid "ID and title are required"
+msgstr "ID 和标题必填"
+
+#: includes/class-ajax-handler.php:399 includes/class-ajax-handler.php:447
+#: includes/class-ajax-handler.php:485
+msgid "No todos found"
+msgstr "未找到待办事项"
+
+#: includes/class-ajax-handler.php:420
+msgid "Todo item updated"
+msgstr "待办事项已更新"
+
+#: includes/class-ajax-handler.php:422 includes/class-ajax-handler.php:428
+msgid "Failed to update todo item"
+msgstr "更新待办事项失败"
+
+#: includes/class-ajax-handler.php:425 includes/class-ajax-handler.php:463
+#: includes/class-ajax-handler.php:507
+msgid "Todo item not found"
+msgstr "待办事项未找到"
+
+#: includes/class-ajax-handler.php:441 includes/class-ajax-handler.php:479
+msgid "ID is required"
+msgstr "ID 必填"
+
+#: includes/class-ajax-handler.php:458
+msgid "Todo item deleted"
+msgstr "待办事项已删除"
+
+#: includes/class-ajax-handler.php:460 includes/class-ajax-handler.php:466
+msgid "Failed to delete todo item"
+msgstr "删除待办事项失败"
+
+#: includes/class-ajax-handler.php:502
+msgid "Todo status updated"
+msgstr "待办事项状态已更新"
+
+#: includes/class-ajax-handler.php:504 includes/class-ajax-handler.php:510
+msgid "Failed to update todo status"
+msgstr "更新待办事项状态失败"
+
+#: includes/class-ajax-handler.php:521
+msgid "Invalid order data"
+msgstr "无效的排序数据"
+
+#: includes/class-ajax-handler.php:536
+msgid "Links reordered successfully"
+msgstr "链接重新排序成功"
+
+#: includes/class-ajax-handler.php:548
+msgid "Invalid widget ID"
+msgstr "无效的小工具 ID"
+
+#: includes/class-ajax-handler.php:555
+msgid "Widget settings updated"
+msgstr "小工具设置已更新"
+
+#: includes/class-ajax-handler.php:568
+msgid "Cache cleared"
+msgstr "缓存已清除"
+
+#: includes/class-ajax-handler.php:573 includes/class-ajax-handler.php:656
+#: includes/class-ajax-handler.php:678 includes/class-user-manager.php:460
+#: includes/class-user-manager.php:790 includes/class-user-manager.php:809
+#: includes/class-user-manager.php:1025
+msgid "Insufficient permissions"
+msgstr "权限不足"
+
+#: includes/class-ajax-handler.php:579 includes/class-ajax-handler.php:662
+#: includes/class-ajax-handler.php:673
+msgid "Invalid nonce"
+msgstr "无效的随机数"
+
+#: includes/class-ajax-handler.php:600
+msgid "Cache cleared successfully"
+msgstr "缓存清除成功"
+
+#: includes/class-ajax-handler.php:602
+msgid "Failed to clear cache"
+msgstr "清除缓存失败"
+
+#: includes/class-ajax-handler.php:625
+msgid "Failed to perform user action"
+msgstr "执行用户操作失败"
+
+#: includes/class-ajax-handler.php:638 includes/class-ajax-handler.php:650
+msgid "No updates available"
+msgstr ""
+
+#: includes/class-ajax-handler.php:668
+#, fuzzy
+#| msgid "News cache cleared successfully!"
+msgid "Widget cache cleared successfully"
+msgstr "新闻缓存清除成功!"
+
+#: includes/class-helpers.php:314
+msgid "Active"
+msgstr "启用"
+
+#: includes/class-helpers.php:315
+msgid "Recent"
+msgstr ""
+
+#: includes/class-helpers.php:316
+msgid "Inactive"
+msgstr ""
+
+#: includes/class-helpers.php:317
+#| msgid "User activated"
+msgid "Very Inactive"
+msgstr ""
+
+#: includes/class-helpers.php:318
+msgid "Never Logged In"
+msgstr ""
+
+#: includes/class-network-data.php:111
+msgid "Subdomain Installation"
+msgstr "子域名安装"
+
+#: includes/class-network-data.php:112
+msgid "Subdirectory Installation"
+msgstr "子目录安装"
+
+#: includes/class-network-data.php:166 includes/class-user-manager.php:181
+msgid "Registration disabled"
+msgstr "注册已禁用"
+
+#: includes/class-network-data.php:167
+msgid "User registration only"
+msgstr "仅用户注册"
+
+#: includes/class-network-data.php:168
+msgid "Site registration only"
+msgstr "仅站点注册"
+
+#: includes/class-network-data.php:169
+msgid "User and site registration"
+msgstr "用户和站点注册"
+
+#: includes/class-network-data.php:211
+msgid "Post"
+msgstr "文章"
+
+#: includes/class-network-data.php:229
+msgid "Page"
+msgstr "页面"
+
+#: includes/class-network-data.php:363
+msgid "All systems operating normally"
+msgstr "所有系统运行正常"
+
+#: includes/class-network-data.php:369
+msgid "No sites found"
+msgstr "未找到站点"
+
+#: includes/class-network-data.php:405
+msgid "No recent activity"
+msgstr "无最近活动"
+
+#: includes/class-network-data.php:623 includes/class-user-manager.php:359
+msgid "Unknown Site"
+msgstr "未知站点"
+
+#: includes/class-plugin-core.php:137
+msgid "Are you sure?"
+msgstr "您确定吗?"
+
+#: includes/class-plugin-core.php:139
+msgid "An error occurred"
+msgstr "发生错误"
+
+#: includes/class-plugin-core.php:140
+msgid "Data refreshed successfully"
+msgstr "数据刷新成功"
+
+#: includes/class-plugin-core.php:141
+msgid "Are you sure you want to delete this item?"
+msgstr "您确定要删除此项目吗?"
+
+#: includes/class-plugin-core.php:142
+msgid "Saved successfully"
+msgstr "保存成功"
+
+#: includes/class-plugin-core.php:168
+msgid "Add New Plugin"
+msgstr "添加新插件"
+
+#: includes/class-plugin-core.php:169
+msgid "Manage Plugins"
+msgstr "管理插件"
+
+#: includes/class-settings-manager.php:132
+msgid "Network statistics and multisite configuration information"
+msgstr "网络统计和多站点配置信息"
+
+#: includes/class-settings-manager.php:133
+msgid "Quick access to recently active sites with favicons"
+msgstr "带网站图标的最近活跃站点快速访问"
+
+#: includes/class-settings-manager.php:134
+msgid "Top 5 sites by storage usage and performance insights"
+msgstr "按存储使用情况排名前 5 的站点和性能洞察"
+
+#: includes/class-settings-manager.php:135
+msgid "Server specifications and WordPress environment details"
+msgstr "服务器规格和 WordPress 环境详情"
+
+#: includes/class-settings-manager.php:136
+msgid ""
+"Customizable quick access links for common tasks with drag-and-drop "
+"reordering"
+msgstr "可自定义的常用任务快速访问链接,支持拖放重新排序"
+
+#: includes/class-settings-manager.php:137
+msgid "Plugin version and system information with help links"
+msgstr "插件版本和系统信息及帮助链接"
+
+#: includes/class-settings-manager.php:138
+msgid "Custom news sources and updates"
+msgstr "自定义新闻源和更新"
+
+#: includes/class-settings-manager.php:139
+msgid "Network configuration and settings overview"
+msgstr "网络配置和设置概览"
+
+#: includes/class-settings-manager.php:140
+msgid "Recent user registrations and user management tools"
+msgstr "最近用户注册和用户管理工具"
+
+#: includes/class-settings-manager.php:141
+msgid ""
+"Network administrator contact information with instant messaging and QR code "
+"support"
+msgstr "网络管理员联系信息,支持即时消息和二维码"
+
+#: includes/class-settings-manager.php:142
+msgid "Recent posts, pages, and content activity across the network"
+msgstr "整个网络的最近文章、页面和内容活动"
+
+#: includes/class-settings-manager.php:143
+msgid "Simple todo list for network administrators with priority levels"
+msgstr "网络管理员的简单待办事项列表,带优先级"
+
+#: includes/class-todo-manager.php:465
+msgid "Low"
+msgstr "低"
+
+#: includes/class-todo-manager.php:466
+msgid "Medium"
+msgstr "中"
+
+#: includes/class-todo-manager.php:467
+msgid "High"
+msgstr "高"
+
+#: includes/class-user-manager.php:167
+msgid "Edit User"
+msgstr "编辑用户"
+
+#: includes/class-user-manager.php:168
+msgid "Delete User"
+msgstr "删除用户"
+
+#: includes/class-user-manager.php:169
+msgid "Send Password Reset"
+msgstr "发送密码重置"
+
+#: includes/class-user-manager.php:170
+msgid "Make Super Admin"
+msgstr "设为超级管理员"
+
+#: includes/class-user-manager.php:171
+msgid "Remove Super Admin"
+msgstr "移除超级管理员"
+
+#: includes/class-user-manager.php:172
+msgid "Activate User"
+msgstr "激活用户"
+
+#: includes/class-user-manager.php:173
+msgid "Deactivate User"
+msgstr "停用用户"
+
+#: includes/class-user-manager.php:182
+msgid "User registration enabled"
+msgstr "用户注册已启用"
+
+#: includes/class-user-manager.php:183
+msgid "Site registration enabled"
+msgstr "站点注册已启用"
+
+#: includes/class-user-manager.php:184
+msgid "User and site registration enabled"
+msgstr "用户和站点注册已启用"
+
+#: includes/class-user-manager.php:464
+msgid "No users selected"
+msgstr "未选择用户"
+
+#: includes/class-user-manager.php:471
+msgid "Invalid user selection"
+msgstr "用户选择无效"
+
+#: includes/class-user-manager.php:503
+#, php-format
+msgid "Bulk action completed: %d successful, %d failed"
+msgstr "批量操作完成:%d 成功,%d 失败"
+
+#: includes/class-user-manager.php:515 includes/class-user-manager.php:631
+msgid "Insufficient permissions to delete users"
+msgstr "删除用户权限不足"
+
+#: includes/class-user-manager.php:531 includes/class-user-manager.php:636
+#: includes/class-user-manager.php:712 includes/class-user-manager.php:725
+#: includes/class-user-manager.php:761 includes/class-user-manager.php:795
+msgid "User not found"
+msgstr "用户未找到"
+
+#: includes/class-user-manager.php:541 includes/class-user-manager.php:641
+msgid "Cannot delete super admin"
+msgstr "无法删除超级管理员"
+
+#: includes/class-user-manager.php:551 includes/class-user-manager.php:645
+msgid "Cannot delete your own account"
+msgstr "无法删除您自己的账户"
+
+#: includes/class-user-manager.php:564
+msgid "User deleted"
+msgstr "用户已删除"
+
+#: includes/class-user-manager.php:571 includes/class-user-manager.php:654
+msgid "Failed to delete user"
+msgstr "删除用户失败"
+
+#: includes/class-user-manager.php:581
+#, php-format
+msgid "Bulk delete completed: %d successful, %d failed"
+msgstr "批量删除完成:%d 成功,%d 失败"
+
+#: includes/class-user-manager.php:622
+msgid "Unknown action"
+msgstr "未知操作"
+
+#: includes/class-user-manager.php:652
+msgid "User deleted successfully"
+msgstr "用户删除成功"
+
+#: includes/class-user-manager.php:663 includes/class-user-manager.php:683
+msgid "Site ID required"
+msgstr "需要站点 ID"
+
+#: includes/class-user-manager.php:667
+msgid "Site not found"
+msgstr "站点未找到"
+
+#: includes/class-user-manager.php:676
+msgid "User added to site"
+msgstr "用户已添加到站点"
+
+#: includes/class-user-manager.php:687
+msgid "Cannot remove super admin from main site"
+msgstr "无法从主站点移除超级管理员"
+
+#: includes/class-user-manager.php:696
+msgid "User removed from site"
+msgstr "用户已从站点移除"
+
+#: includes/class-user-manager.php:704
+msgid "Site ID and role required"
+msgstr "需要站点 ID 和角色"
+
+#: includes/class-user-manager.php:718
+msgid "User role changed"
+msgstr "用户角色已更改"
+
+#: includes/class-user-manager.php:737
+#, php-format
+msgid "A password reset has been requested for your account on %s."
+msgstr "您在 %s 上的账户已请求密码重置。"
+
+#: includes/class-user-manager.php:741
+#, php-format
+msgid "Username: %s"
+msgstr "用户名:%s"
+
+#: includes/class-user-manager.php:742
+msgid "To reset your password, visit the following address:"
+msgstr "要重置您的密码,请访问以下地址:"
+
+#: includes/class-user-manager.php:744
+msgid "This link will expire in 24 hours."
+msgstr "此链接将在 24 小时后过期。"
+
+#: includes/class-user-manager.php:746
+#, php-format
+msgid "[%s] Password Reset"
+msgstr "[%s] 密码重置"
+
+#: includes/class-user-manager.php:751
+msgid "Failed to send password reset email"
+msgstr "发送密码重置邮件失败"
+
+#: includes/class-user-manager.php:754
+msgid "Password reset email sent"
+msgstr "密码重置邮件已发送"
+
+#: includes/class-user-manager.php:767
+msgid "User activated"
+msgstr "用户已激活"
+
+#: includes/class-user-manager.php:772
+msgid "Cannot deactivate super admin"
+msgstr "无法停用超级管理员"
+
+#: includes/class-user-manager.php:776
+msgid "Cannot deactivate your own account"
+msgstr "无法停用您自己的账户"
+
+#: includes/class-user-manager.php:785
+msgid "User deactivated"
+msgstr "用户已停用"
+
+#: includes/class-user-manager.php:799
+msgid "User is already a super admin"
+msgstr "用户已经是超级管理员"
+
+#: includes/class-user-manager.php:804
+msgid "User promoted to super admin"
+msgstr "用户已提升为超级管理员"
+
+#: includes/class-user-manager.php:813
+msgid "Cannot demote yourself"
+msgstr "无法降级您自己"
+
+#: includes/class-user-manager.php:818
+msgid "Cannot demote the last super admin"
+msgstr "无法降级最后一个超级管理员"
+
+#: includes/class-user-manager.php:823
+msgid "User demoted from super admin"
+msgstr "用户已从超级管理员降级"
+
+#: includes/class-user-manager.php:1002
+#, php-format
+msgid "Bulk %s performed on %d users: %d successful, %d failed"
+msgstr "批量 %s 已对 %d 个用户执行:%d 成功,%d 失败"
+
+#: includes/class-user-manager.php:1041
+#, php-format
+msgid "%d users would be deleted"
+msgstr "将删除 %d 个用户"
+
+#: includes/class-user-manager.php:1057
+#, php-format
+msgid "%d inactive users deleted"
+msgstr "已删除 %d 个非活跃用户"
+
+#: templates/admin-modals.php:33 templates/contact-info-modal.php:23
+msgid "Edit Contact Information"
+msgstr "编辑联系信息"
+
+#: templates/admin-modals.php:41 templates/contact-info-modal.php:31
+msgid "Organization Name:"
+msgstr "组织名称:"
+
+#: templates/admin-modals.php:42 templates/contact-info-modal.php:32
+msgid "Network Administrator"
+msgstr "网络管理员"
+
+#: templates/admin-modals.php:46 templates/contact-info-modal.php:36
+msgid "Email:"
+msgstr "邮箱:"
+
+#: templates/admin-modals.php:51 templates/contact-info-modal.php:41
+msgid "Phone:"
+msgstr "电话:"
+
+#: templates/admin-modals.php:56 templates/contact-info-modal.php:46
+msgid "Website:"
+msgstr "网站:"
+
+#: templates/admin-modals.php:61 templates/contact-info-modal.php:51
+msgid "Description:"
+msgstr "描述:"
+
+#: templates/admin-modals.php:62 templates/contact-info-modal.php:52
+msgid "Brief description or role"
+msgstr "简要描述或角色"
+
+#: templates/admin-modals.php:71 templates/contact-info-modal.php:61
+msgid "QQ:"
+msgstr "QQ:"
+
+#: templates/admin-modals.php:79 templates/contact-info-modal.php:69
+msgid "WeChat:"
+msgstr "微信:"
+
+#: templates/admin-modals.php:89 templates/contact-info-modal.php:79
+msgid "WhatsApp:"
+msgstr "WhatsApp:"
+
+#: templates/admin-modals.php:97 templates/contact-info-modal.php:87
+msgid "Telegram:"
+msgstr "Telegram:"
+
+#: templates/admin-modals.php:106 templates/contact-info-modal.php:96
+msgid "QR Code Image URL:"
+msgstr "二维码图片链接:"
+
+#: templates/admin-modals.php:109 templates/contact-info-modal.php:99
+msgid "Select Image"
+msgstr "选择图片"
+
+#: templates/admin-modals.php:111 templates/contact-info-modal.php:101
+msgid "Upload or provide URL for a QR code image (WeChat, contact info, etc.)"
+msgstr "上传或提供二维码图片链接(微信、联系信息等)"
+
+#: templates/admin-modals.php:124 templates/contact-info-modal.php:114
+msgid "Save Contact Info"
+msgstr "保存联系信息"
+
+#: templates/admin-modals.php:127 templates/admin-modals.php:204
+#: templates/admin-modals.php:318 templates/contact-info-modal.php:117
+#: templates/news-sources-modal.php:70 templates/quick-links-modal.php:117
+msgid "Cancel"
+msgstr "取消"
+
+#: templates/admin-modals.php:136 templates/news-sources-modal.php:18
+msgid "Configure News Sources"
+msgstr "配置新闻源"
+
+#: templates/admin-modals.php:147 templates/news-sources-modal.php:29
+msgid "Source Name"
+msgstr "源名称"
+
+#: templates/admin-modals.php:152 templates/news-sources-modal.php:34
+msgid "RSS Feed URL"
+msgstr "RSS 订阅链接"
+
+#: templates/admin-modals.php:163 templates/news-sources-modal.php:45
+msgid "Enabled"
+msgstr "已启用"
+
+#: templates/admin-modals.php:167 templates/admin-modals.php:250
+#: templates/news-sources-modal.php:49 templates/quick-links-modal.php:49
+msgid "Remove"
+msgstr "移除"
+
+#: templates/admin-modals.php:178 templates/news-sources-modal.php:60
+msgid "Add News Source"
+msgstr "添加新闻源"
+
+#: templates/admin-modals.php:183
+msgid "Popular RSS Feeds"
+msgstr "热门 RSS 源"
+
+#: templates/admin-modals.php:201 templates/news-sources-modal.php:67
+msgid "Save News Sources"
+msgstr "保存新闻源"
+
+#: templates/admin-modals.php:213 templates/quick-links-modal.php:12
+msgid "Configure Quick Links"
+msgstr "配置快速链接"
+
+#: templates/admin-modals.php:224 templates/quick-links-modal.php:23
+msgid "Link Title"
+msgstr "链接标题"
+
+#: templates/admin-modals.php:237 templates/quick-links-modal.php:36
+msgid "dashicons-admin-home or 🏠"
+msgstr "dashicons-admin-home 或 🏠"
+
+#: templates/admin-modals.php:240 templates/quick-links-modal.php:39
+msgid "Icon (Dashicon class or emoji)"
+msgstr "图标(Dashicon 类或表情符号)"
+
+#: templates/admin-modals.php:246 templates/quick-links-modal.php:45
+msgid "Open in new tab"
+msgstr "在新标签页打开"
+
+#: templates/admin-modals.php:261 templates/quick-links-modal.php:60
+msgid "Add Link"
+msgstr "添加链接"
+
+#: templates/admin-modals.php:266 templates/quick-links-modal.php:65
+msgid "Icon Options"
+msgstr "图标选项"
+
+#: templates/admin-modals.php:270 templates/quick-links-modal.php:69
+msgid "WordPress Dashicons"
+msgstr "WordPress Dashicons"
+
+#: templates/admin-modals.php:275 templates/admin-modals.php:291
+#: templates/quick-links-modal.php:74 templates/quick-links-modal.php:90
+msgid "Home"
+msgstr "首页"
+
+#: templates/admin-modals.php:280 templates/quick-links-modal.php:79
+msgid "Analytics"
+msgstr "分析"
+
+#: templates/admin-modals.php:286 templates/quick-links-modal.php:85
+msgid "Emojis"
+msgstr "表情符号"
+
+#: templates/admin-modals.php:296 templates/quick-links-modal.php:95
+msgid "Settings"
+msgstr "设置"
+
+#: templates/admin-modals.php:303 templates/quick-links-modal.php:102
+msgid "Dashicons:"
+msgstr "Dashicons:"
+
+#: templates/admin-modals.php:303 templates/quick-links-modal.php:102
+msgid "Built into WordPress, always available. Use format:"
+msgstr "内置于 WordPress,始终可用。使用格式:"
+
+#: templates/admin-modals.php:304 templates/quick-links-modal.php:103
+msgid "Emojis:"
+msgstr "表情符号:"
+
+#: templates/admin-modals.php:304 templates/quick-links-modal.php:103
+msgid "Copy and paste emoji directly. Works on all devices."
+msgstr "直接复制粘贴表情符号。适用于所有设备。"
+
+#: templates/admin-modals.php:308 templates/quick-links-modal.php:107
+#, php-format
+msgid "Find more Dashicons at %s"
+msgstr "在 %s 查找更多 Dashicons"
+
+#: templates/admin-modals.php:315 templates/quick-links-modal.php:114
+msgid "Save Links"
+msgstr "保存链接"
+
+#: templates/settings-page.php:7
+msgid "Settings saved successfully!"
+msgstr "设置保存成功!"
+
+#: templates/settings-page.php:18
+#, php-format
+msgid "Version: %s"
+msgstr "版本:%s"
+
+#: templates/settings-page.php:21
+msgid "Documentation"
+msgstr "文档"
+
+#: templates/settings-page.php:24
+msgid "Support"
+msgstr "支持"
+
+#: templates/settings-page.php:29
+#, fuzzy
+#| msgid "Widget Configuration"
+msgid "Plugin Widget Configuration"
+msgstr "小工具配置"
+
+#: templates/settings-page.php:30
+#, fuzzy
+#| msgid "Enable or disable dashboard widgets according to your needs."
+msgid "Enable or disable custom dashboard widgets provided by this plugin."
+msgstr "根据您的需要启用或禁用仪表板小工具。"
+
+#: templates/settings-page.php:54
+msgid "System & Third-Party Widgets"
+msgstr ""
+
+#: templates/settings-page.php:55
+msgid ""
+"Control the display of WordPress system widgets and widgets from other "
+"plugins."
+msgstr ""
+
+#: templates/settings-page.php:76
+#, fuzzy
+#| msgid "WordPress Dashicons"
+msgid "WordPress System Widgets"
+msgstr "WordPress Dashicons"
+
+#: templates/settings-page.php:96 templates/settings-page.php:114
+msgid "Third-Party Plugin Widgets"
+msgstr ""
+
+#: templates/settings-page.php:116
+msgid "No third-party widgets detected yet."
+msgstr ""
+
+#: templates/settings-page.php:117
+msgid ""
+"Third-party widgets are automatically detected when you visit the network "
+"dashboard. If you have plugins that add dashboard widgets, visit the "
+"dashboard first, then return here to see them."
+msgstr ""
+
+#: templates/settings-page.php:119
+msgid "Visit Network Dashboard"
+msgstr ""
+
+#: templates/settings-page.php:127
+#, fuzzy
+#| msgid "No sites found"
+msgid "No system widgets found."
+msgstr "未找到站点"
+
+#: templates/settings-page.php:132
+msgid "Save Widget Settings"
+msgstr "保存小工具设置"
+
+#: templates/settings-page.php:138
+msgid "Cache Management"
+msgstr "缓存管理"
+
+#: templates/settings-page.php:139
+msgid "Clear cached data to refresh dashboard widgets."
+msgstr "清除缓存数据以刷新仪表板小工具。"
+
+#: templates/settings-page.php:144
+msgid "Clear All Caches"
+msgstr "清除所有缓存"
+
+#: templates/settings-page.php:148
+msgid "Clear Network Data"
+msgstr "清除网络数据"
+
+#: templates/settings-page.php:152
+#, fuzzy
+#| msgid "Clear News Cache"
+msgid "Clear Widget Cache"
+msgstr "清除新闻缓存"
+
+#: templates/settings-page.php:157
+#, fuzzy
+#| msgid ""
+#| "Clearing caches will force the dashboard widgets to reload fresh data on "
+#| "the next page visit."
+msgid ""
+"Clearing caches will force the dashboard widgets to reload fresh data on the "
+"next page visit. Widget cache contains the list of detected third-party "
+"widgets."
+msgstr "清除缓存将强制仪表板小工具在下次访问页面时重新加载新数据。"
+
+#: templates/settings-page.php:162
+#, fuzzy
+#| msgid "Edit Contact Information"
+msgid "Plugin Information"
+msgstr "编辑联系信息"
+
+#: templates/settings-page.php:163
+msgid "Current plugin status and update information."
+msgstr ""
+
+#: templates/settings-page.php:167
+#, fuzzy
+#| msgid "Current News Sources"
+msgid "Current Version:"
+msgstr "当前新闻源"
+
+#: templates/settings-page.php:172
+#, fuzzy
+#| msgid "Status"
+msgid "Update Status:"
+msgstr "状态"
+
+#: templates/settings-page.php:175
+msgid "Check for Updates"
+msgstr ""
+
+#: wp-multisite-dashboard.php:47
+msgid "WP Multisite Dashboard requires WordPress Multisite to be enabled."
+msgstr "WP 多站点仪表板需要启用 WordPress 多站点。"
+
+#: wp-multisite-dashboard.php:55
+msgid "This plugin requires WordPress Multisite to be enabled."
+msgstr "此插件需要启用 WordPress 多站点。"
+
+#: wp-multisite-dashboard.php:68
+msgid "WP Multisite Dashboard has been activated successfully!"
+msgstr "WP 多站点仪表板已成功激活!"
+
+#. Plugin Name of the plugin/theme
+msgid "WP Multisite Dashboard"
+msgstr "WP 多站点仪表板"
+
+#. Plugin URI of the plugin/theme
+msgid "https://wpmultisite.com/plugins/wp-multisite-dashboard"
+msgstr "https://wpmultisite.com/plugins/wp-multisite-dashboard"
+
+#. Description of the plugin/theme
+msgid "Essential dashboard widgets for WordPress multisite administrators"
+msgstr "WordPress 多站点管理员的必备仪表板小工具"
+
+#. Author of the plugin/theme
+msgid "WPMultisite.com"
+msgstr "文派多站点"
+
+#. Author URI of the plugin/theme
+msgid "https://WPMultisite.com"
+msgstr "https://WPMultisite.com"
+
+#~ msgid ""
+#~ "Add custom links to frequently used pages or external tools. These will "
+#~ "appear as clickable tiles in your Quick Links widget. You can use "
+#~ "WordPress Dashicons or emojis for icons. Links can be reordered by "
+#~ "dragging and dropping."
+#~ msgstr ""
+#~ "添加常用页面或外部工具的自定义链接。这些将作为可点击的磁贴出现在您的快速链"
+#~ "接小部件中。您可以使用 WordPress Dashicons 图标或表情符号作为图标。链接可"
+#~ "以通过拖放重新排序。"
+
+#~ msgid "Home/Dashboard"
+#~ msgstr "首页/仪表板"
+
+#~ msgid "Users"
+#~ msgstr "用户"
+
+#~ msgid "Email"
+#~ msgstr "邮件"
+
+#~ msgid "External Link"
+#~ msgstr "外部链接"
+
+#~ msgid "Link"
+#~ msgstr "链接"
+
+#~ msgid "Drag & Drop Reordering"
+#~ msgstr "拖放重新排序"
+
+#~ msgid ""
+#~ "After saving your links, you can reorder them by dragging and dropping "
+#~ "the tiles in the Quick Links widget."
+#~ msgstr "保存链接后,您可以通过在快速链接小部件中拖放磁贴来重新排序。"
+
+#~ msgid "News Sources Configuration"
+#~ msgstr "新闻源配置"
+
+#~ msgid "Configure custom RSS news sources for the Network News widget."
+#~ msgstr "为网络新闻小部件配置自定义 RSS 新闻源。"
+
+#~ msgid "RSS URL"
+#~ msgstr "RSS 链接"
+
+#~ msgid "Disabled"
+#~ msgstr "已禁用"
+
+#~ msgid "No news sources configured."
+#~ msgstr "未配置新闻源。"
+
+#~ msgid "Manage News Sources"
+#~ msgstr "管理新闻源"
+
+#~ msgid ""
+#~ "You can add any valid RSS or Atom feed URL. The news widget will fetch "
+#~ "and display the latest articles from your configured sources."
+#~ msgstr ""
+#~ "您可以添加任何有效的 RSS 或 Atom 订阅链接。新闻小部件将从您配置的源获取并"
+#~ "显示最新文章。"
+
+#~ msgid "Are you sure you want to clear the cache?"
+#~ msgstr "您确定要清除缓存吗?"
+
+#~ msgid "Cache cleared successfully!"
+#~ msgstr "缓存清除成功!"
+
+#~ msgid "Failed to clear cache."
+#~ msgstr "清除缓存失败。"
+
+#~ msgid "Are you sure you want to clear the news cache?"
+#~ msgstr "您确定要清除新闻缓存吗?"
+
+#~ msgid "Failed to clear news cache."
+#~ msgstr "清除新闻缓存失败。"
+
+#~ msgid "Please go to the dashboard to configure news sources."
+#~ msgstr "请转到仪表板配置新闻源。"
+
+#~ msgid "News sources saved successfully!"
+#~ msgstr "新闻源保存成功!"
+
+#~ msgid "Failed to save news sources."
+#~ msgstr "保存新闻源失败。"
diff --git a/templates/admin-modals.php b/templates/admin-modals.php
new file mode 100644
index 0000000..13382ed
--- /dev/null
+++ b/templates/admin-modals.php
@@ -0,0 +1,838 @@
+ get_network_option(null, 'site_name'),
+ 'email' => get_network_option(null, 'admin_email'),
+ 'phone' => '',
+ 'website' => network_home_url(),
+ 'description' => 'Network Administrator Contact Information',
+ 'qq' => '',
+ 'wechat' => '',
+ 'whatsapp' => '',
+ 'telegram' => '',
+ 'qr_code' => ''
+]);
+
+$news_sources = get_site_option('msd_news_sources', [
+ [
+ 'name' => 'WordPress News',
+ 'url' => 'https://wordpress.org/news/feed/',
+ 'enabled' => true
+ ]
+]);
+
+$quick_links = get_site_option('msd_quick_links', []);
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ dashicons-admin-home
+
+
+
+
+ dashicons-chart-bar
+
+
+
+
+
+
+
+
+
+ 🏠
+ 🏠
+
+
+
+ ⚙️
+ ⚙️
+
+
+
+
+
+
+
+ dashicons-icon-name
+
+
+
+
+ developer.wordpress.org/resource/dashicons/'); ?>
+
+
+
+
+
+
+
+
+
diff --git a/templates/quick-links-modal.php b/templates/quick-links-modal.php
index c486833..c4b3e87 100644
--- a/templates/quick-links-modal.php
+++ b/templates/quick-links-modal.php
@@ -14,10 +14,6 @@ $quick_links = get_site_option('msd_quick_links', []);
@@ -118,35 +94,10 @@ $quick_links = get_site_option('msd_quick_links', []);