v1.2.2 稳定版

首发正式版本
This commit is contained in:
feibisi 2025-06-24 23:05:58 +08:00
parent 836b293850
commit a8c3a0b96d
20 changed files with 5001 additions and 4432 deletions

View file

@ -1,255 +0,0 @@
<?php
if (!defined('ABSPATH')) {
exit;
}
class WP_MSD_Admin_Template {
public static function render_dashboard_notice($message, $type = 'info', $dismissible = true) {
$classes = ['notice', "notice-{$type}"];
if ($dismissible) {
$classes[] = 'is-dismissible';
}
$class_string = implode(' ', $classes);
echo "<div class=\"{$class_string}\">";
echo "<p>" . esc_html($message) . "</p>";
echo "</div>";
}
public static function render_widget_header($title, $widget_id = null, $show_refresh = true) {
echo '<div class="msd-widget-header">';
if ($show_refresh && $widget_id) {
echo '<button class="msd-refresh-btn" title="Refresh" data-widget="' . esc_attr($widget_id) . '">↻</button>';
}
if ($title) {
echo '<h3 class="msd-widget-title">' . esc_html($title) . '</h3>';
}
echo '</div>';
}
public static function render_loading_state($message = null) {
$message = $message ?: __('Loading...', 'wp-multisite-dashboard');
echo '<div class="msd-loading">';
echo '<span class="msd-spinner"></span>';
echo esc_html($message);
echo '</div>';
}
public static function render_empty_state($message, $action_text = null, $action_url = null) {
echo '<div class="msd-empty-state">';
echo '<p>' . esc_html($message) . '</p>';
if ($action_text && $action_url) {
echo '<a href="' . esc_url($action_url) . '" class="button button-primary">' . esc_html($action_text) . '</a>';
}
echo '</div>';
}
public static function render_error_state($message, $retry_action = null) {
echo '<div class="msd-error-state">';
echo '<p>' . esc_html($message) . '</p>';
if ($retry_action) {
echo '<button class="button msd-retry-btn" onclick="' . esc_attr($retry_action) . '">Try Again</button>';
}
echo '</div>';
}
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(
'<span class="msd-priority-badge %s">%s</span>',
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(
'<span class="msd-status-badge %s">%s</span>',
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 '<div class="msd-progress-container">';
if ($label) {
echo '<div class="msd-progress-label">' . esc_html($label) . '</div>';
}
echo '<div class="msd-progress-bar">';
echo '<div class="msd-progress-fill ' . esc_attr($status_class) . '" style="width: ' . $percentage . '%"></div>';
echo '</div>';
echo '<div class="msd-progress-text">' . $percentage . '%</div>';
echo '</div>';
}
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 '<div class="msd-data-table-wrapper">';
echo '<table class="msd-data-table">';
if (!empty($headers)) {
echo '<thead><tr>';
foreach ($headers as $header) {
echo '<th>' . esc_html($header) . '</th>';
}
echo '</tr></thead>';
}
echo '<tbody>';
foreach ($rows as $row) {
echo '<tr>';
foreach ($row as $cell) {
echo '<td>' . $cell . '</td>';
}
echo '</tr>';
}
echo '</tbody>';
echo '</table>';
echo '</div>';
}
public static function render_action_buttons($actions) {
if (empty($actions)) {
return;
}
echo '<div class="msd-action-buttons">';
foreach ($actions as $action) {
$class = 'button ' . ($action['primary'] ?? false ? 'button-primary' : 'button-secondary');
$attributes = '';
if (!empty($action['attributes'])) {
foreach ($action['attributes'] as $attr => $value) {
$attributes .= ' ' . esc_attr($attr) . '="' . esc_attr($value) . '"';
}
}
if (!empty($action['url'])) {
echo '<a href="' . esc_url($action['url']) . '" class="' . esc_attr($class) . '"' . $attributes . '>';
echo esc_html($action['text']);
echo '</a>';
} else {
echo '<button type="button" class="' . esc_attr($class) . '"' . $attributes . '>';
echo esc_html($action['text']);
echo '</button>';
}
}
echo '</div>';
}
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;
}
}

View file

@ -1,333 +1,405 @@
<?php
if (!defined('ABSPATH')) {
exit;
if (!defined("ABSPATH")) {
exit();
}
class WP_MSD_Admin_Interface {
public function __construct() {
add_action('admin_footer', [$this, 'render_modals']);
class WP_MSD_Admin_Interface
{
public function __construct()
{
add_action("admin_footer", [$this, "render_modals"]);
}
public function render_modals() {
public function render_modals()
{
$screen = get_current_screen();
if ($screen && $screen->id === 'dashboard-network') {
include_once WP_MSD_PLUGIN_DIR . 'templates/admin-modals.php';
if ($screen && $screen->id === "dashboard-network") {
include_once WP_MSD_PLUGIN_DIR . "templates/admin-modals.php";
}
}
public function add_network_widgets() {
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']
"msd_network_overview" => [
__("Network Overview", "wp-multisite-dashboard"),
"render_network_overview_widget",
],
"msd_quick_site_management" => [
__("Quick Site Management", "wp-multisite-dashboard"),
"render_quick_site_widget",
],
"msd_storage_performance" => [
__("Storage Usage", "wp-multisite-dashboard"),
"render_storage_performance_widget",
],
"msd_server_info" => [
__("Server Information", "wp-multisite-dashboard"),
"render_server_info_widget",
],
"msd_quick_links" => [
__("Quick Links", "wp-multisite-dashboard"),
"render_quick_links_widget",
],
"msd_version_info" => [
__("Version Information", "wp-multisite-dashboard"),
"render_version_info_widget",
],
"msd_custom_news" => [
__("Network News", "wp-multisite-dashboard"),
"render_custom_news_widget",
],
"msd_user_management" => [
__("User Management", "wp-multisite-dashboard"),
"render_user_management_widget",
],
"msd_contact_info" => [
__("Contact Information", "wp-multisite-dashboard"),
"render_contact_info_widget",
],
"msd_last_edits" => [
__("Recent Network Activity", "wp-multisite-dashboard"),
"render_last_edits_widget",
],
"msd_todo_widget" => [
__("Todo List", "wp-multisite-dashboard"),
"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]]
);
wp_add_dashboard_widget($widget_id, $widget_data[0], [
$this,
$widget_data[1],
]);
}
}
}
public function render_network_overview_widget() {
public function render_network_overview_widget()
{
echo '<div id="msd-network-overview" class="msd-widget-content" data-widget="network_overview">';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' . __('Loading...', 'wp-multisite-dashboard') . '</div>';
echo '</div>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' .
__("Loading...", "wp-multisite-dashboard") .
"</div>";
echo "</div>";
}
public function render_quick_site_widget() {
public function render_quick_site_widget()
{
echo '<div id="msd-quick-sites" class="msd-widget-content" data-widget="site_list">';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' . __('Loading...', 'wp-multisite-dashboard') . '</div>';
echo '</div>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' .
__("Loading...", "wp-multisite-dashboard") .
"</div>";
echo "</div>";
}
public function render_storage_performance_widget() {
public function render_storage_performance_widget()
{
echo '<div id="msd-storage-performance" class="msd-widget-content" data-widget="storage_data">';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' . __('Loading...', 'wp-multisite-dashboard') . '</div>';
echo '</div>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' .
__("Loading...", "wp-multisite-dashboard") .
"</div>";
echo "</div>";
}
public function render_server_info_widget() {
public function render_server_info_widget()
{
echo '<div id="msd-server-info" class="msd-widget-content" data-widget="server_info">';
echo '<button class="msd-refresh-btn" title="Refresh" data-widget="server_info">↻</button>';
echo '<button class="msd-refresh-btn" title="' .
esc_attr__("Refresh", "wp-multisite-dashboard") .
'" data-widget="server_info">↻</button>';
$this->render_server_info_content();
echo '</div>';
echo "</div>";
}
private function render_server_info_content() {
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()),
__("PHP Version", "wp-multisite-dashboard") => phpversion(),
__(
"MySQL Version",
"wp-multisite-dashboard"
) => $wpdb->db_version(),
__("Server Software", "wp-multisite-dashboard") =>
$_SERVER["SERVER_SOFTWARE"] ??
__("Unknown", "wp-multisite-dashboard"),
__("Server Time", "wp-multisite-dashboard") => current_time(
"Y-m-d H:i:s"
),
__("Memory Limit", "wp-multisite-dashboard") => ini_get(
"memory_limit"
),
__("Max Upload Size", "wp-multisite-dashboard") => 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',
__(
"PHP Version",
"wp-multisite-dashboard"
) => "dashicons-editor-code",
__(
"MySQL Version",
"wp-multisite-dashboard"
) => "dashicons-database",
__(
"Server Software",
"wp-multisite-dashboard"
) => "dashicons-admin-tools",
__("Server Time", "wp-multisite-dashboard") => "dashicons-clock",
__(
"Memory Limit",
"wp-multisite-dashboard"
) => "dashicons-performance",
__(
"Max Upload Size",
"wp-multisite-dashboard"
) => "dashicons-upload",
];
echo '<div class="msd-server-specs">';
foreach ($data as $label => $value) {
$icon = $icons[$label] ?? 'dashicons-info';
$icon = $icons[$label] ?? "dashicons-info";
echo '<div class="msd-spec-item">';
echo '<span class="msd-spec-icon dashicons ' . esc_attr($icon) . '"></span>';
echo '<span class="msd-spec-label">' . esc_html($label) . '</span>';
echo '<span class="msd-spec-value">' . esc_html($value) . '</span>';
echo '</div>';
echo '<span class="msd-spec-icon dashicons ' .
esc_attr($icon) .
'"></span>';
echo '<span class="msd-spec-label">' . esc_html($label) . "</span>";
echo '<span class="msd-spec-value">' . esc_html($value) . "</span>";
echo "</div>";
}
echo '</div>';
echo "</div>";
}
public function render_version_info_widget() {
public function render_version_info_widget()
{
echo '<div id="msd-version-info" class="msd-widget-content" data-widget="version_info">';
echo '<button class="msd-refresh-btn" title="Refresh" data-widget="version_info">↻</button>';
$this->render_version_info_content();
echo '</div>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' .
__("Loading...", "wp-multisite-dashboard") .
"</div>";
echo "</div>";
}
private function render_version_info_content() {
$plugin_data = get_plugin_data(WP_MSD_PLUGIN_DIR . 'wp-multisite-dashboard.php');
global $wpdb;
echo '<div class="msd-version-header">';
echo '<h3><span class="dashicons dashicons-admin-multisite"></span> ' . esc_html($plugin_data['Name']) . '</h3>';
echo '<div class="msd-version-actions">';
echo '<a href="https://wpmultisite.com/document/wp-multisite-dashboard" target="_blank" class="msd-help-btn msd-help-docs" title="Documentation">';
echo '<span class="dashicons dashicons-book"></span>';
echo '</a>';
echo '<a href="https://wpmultisite.com/support/" target="_blank" class="msd-help-btn msd-help-support" title="Support">';
echo '<span class="dashicons dashicons-admin-comments"></span>';
echo '</a>';
echo '<a href="https://github.com/wpmultisite/wp-multisite-dashboard" target="_blank" class="msd-help-btn msd-help-github" title="GitHub">';
echo '<span class="dashicons dashicons-admin-links"></span>';
echo '</a>';
echo '</div>';
echo '</div>';
$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 '<div class="msd-update-notice">';
echo '<span class="dashicons dashicons-update"></span>';
echo '<span>' . sprintf(__('Version %s available! ', 'wp-multisite-dashboard'), esc_html($update_available['version'])) . '</span>';
echo '<a href="' . esc_url($update_available['details_url']) . '" target="_blank" class="msd-update-link">' . __('View Details', 'wp-multisite-dashboard') . '</a>';
echo '</div>';
}
echo '<div class="msd-version-specs">';
echo '<div class="msd-version-item">';
echo '<span class="msd-version-icon dashicons dashicons-tag"></span>';
echo '<span class="msd-version-label">Plugin Version</span>';
echo '<span class="msd-version-value">' . esc_html($plugin_data['Version']) . '</span>';
echo '</div>';
echo '<div class="msd-version-item">';
echo '<span class="msd-version-icon dashicons dashicons-admin-links"></span>';
echo '<span class="msd-version-label">Author URI</span>';
echo '<span class="msd-version-value"><a href="' . esc_url($plugin_data['AuthorURI']) . '" target="_blank">' . esc_html($plugin_data['AuthorURI']) . '</a></span>';
echo '</div>';
echo '<div class="msd-version-item">';
echo '<span class="msd-version-icon dashicons dashicons-editor-code"></span>';
echo '<span class="msd-version-label">Required PHP</span>';
echo '<span class="msd-version-value msd-status-good">' . esc_html($plugin_data['RequiresPHP']) . '</span>';
echo '</div>';
echo '<div class="msd-version-item">';
echo '<span class="msd-version-icon dashicons dashicons-database"></span>';
echo '<span class="msd-version-label">Database Tables</span>';
$activity_table = $wpdb->base_prefix . 'msd_activity_log';
$activity_exists = $wpdb->get_var("SHOW TABLES LIKE '{$activity_table}'") === $activity_table;
if ($activity_exists) {
echo '<span class="msd-version-value msd-db-status-good">✓ Activity table created</span>';
} else {
echo '<span class="msd-version-value msd-db-status-warning">⚠ Activity table missing</span>';
}
echo '</div>';
echo '</div>';
}
public function render_custom_news_widget() {
public function render_custom_news_widget()
{
echo '<div id="msd-custom-news" class="msd-widget-content" data-widget="custom_news">';
echo '<button class="msd-refresh-btn" title="Refresh" data-widget="custom_news">↻</button>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' . __('Loading...', 'wp-multisite-dashboard') . '</div>';
echo '</div>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' .
__("Loading...", "wp-multisite-dashboard") .
"</div>";
echo "</div>";
}
public function render_user_management_widget() {
public function render_user_management_widget()
{
echo '<div id="msd-user-management" class="msd-widget-content" data-widget="user_management">';
echo '<button class="msd-refresh-btn" title="Refresh" data-widget="user_management">↻</button>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' . __('Loading...', 'wp-multisite-dashboard') . '</div>';
echo '</div>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' .
__("Loading...", "wp-multisite-dashboard") .
"</div>";
echo "</div>";
}
public function render_contact_info_widget() {
public function render_contact_info_widget()
{
echo '<div id="msd-contact-info" class="msd-widget-content" data-widget="contact_info">';
echo '<button class="msd-refresh-btn" title="Refresh" data-widget="contact_info">↻</button>';
echo '<button class="msd-refresh-btn" title="' .
esc_attr__("Refresh", "wp-multisite-dashboard") .
'" data-widget="contact_info">↻</button>';
$this->render_contact_info_content();
echo '</div>';
echo "</div>";
}
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' => ''
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",
"wp-multisite-dashboard"
),
"qq" => "",
"wechat" => "",
"whatsapp" => "",
"telegram" => "",
"qr_code" => "",
]);
echo '<div class="msd-contact-card">';
echo '<div class="msd-contact-header">';
echo '<h3><span class="dashicons dashicons-coffee"></span> ' . esc_html($contact_info['name']) . '</h3>';
echo '</div>';
echo '<h3><span class="dashicons dashicons-coffee"></span> ' .
esc_html($contact_info["name"]) .
"</h3>";
echo "</div>";
echo '<div class="msd-contact-details">';
if (!empty($contact_info['description'])) {
echo '<p class="msd-contact-description">' . esc_html($contact_info['description']) . '</p>';
if (!empty($contact_info["description"])) {
echo '<p class="msd-contact-description">' .
esc_html($contact_info["description"]) .
"</p>";
}
echo '<div class="msd-contact-item">';
echo '<span class="dashicons dashicons-email"></span>';
echo '<a href="mailto:' . esc_attr($contact_info['email']) . '">' . esc_html($contact_info['email']) . '</a>';
echo '</div>';
echo '<a href="mailto:' .
esc_attr($contact_info["email"]) .
'">' .
esc_html($contact_info["email"]) .
"</a>";
echo "</div>";
if (!empty($contact_info['phone'])) {
if (!empty($contact_info["phone"])) {
echo '<div class="msd-contact-item">';
echo '<span class="dashicons dashicons-phone"></span>';
echo '<a href="tel:' . esc_attr($contact_info['phone']) . '">' . esc_html($contact_info['phone']) . '</a>';
echo '</div>';
echo '<a href="tel:' .
esc_attr($contact_info["phone"]) .
'">' .
esc_html($contact_info["phone"]) .
"</a>";
echo "</div>";
}
echo '<div class="msd-contact-item">';
echo '<span class="dashicons dashicons-admin-links"></span>';
echo '<a href="' . esc_url($contact_info['website']) . '" target="_blank">' . esc_html($contact_info['website']) . '</a>';
echo '</div>';
echo '<a href="' .
esc_url($contact_info["website"]) .
'" target="_blank">' .
esc_html($contact_info["website"]) .
"</a>";
echo "</div>";
$im_fields = [
'qq' => ['QQ', 'dashicons-admin-users'],
'wechat' => ['WeChat', 'dashicons-format-chat'],
'whatsapp' => ['WhatsApp', 'dashicons-smartphone'],
'telegram' => ['Telegram', 'dashicons-email-alt']
"qq" => ["QQ", "dashicons-admin-users"],
"wechat" => [
__("WeChat", "wp-multisite-dashboard"),
"dashicons-format-chat",
],
"whatsapp" => [
__("WhatsApp", "wp-multisite-dashboard"),
"dashicons-smartphone",
],
"telegram" => [
__("Telegram", "wp-multisite-dashboard"),
"dashicons-email-alt",
],
];
foreach ($im_fields as $field => $data) {
if (!empty($contact_info[$field])) {
echo '<div class="msd-contact-item">';
echo '<span class="dashicons ' . $data[1] . '"></span>';
echo '<span>' . $data[0] . ': ' . esc_html($contact_info[$field]) . '</span>';
echo '</div>';
echo "<span>" .
esc_html($data[0]) .
": " .
esc_html($contact_info[$field]) .
"</span>";
echo "</div>";
}
}
if (!empty($contact_info['qr_code'])) {
if (!empty($contact_info["qr_code"])) {
echo '<div class="msd-contact-qr">';
echo '<img src="' . esc_url($contact_info['qr_code']) . '" alt="QR Code" class="msd-qr-image">';
echo '</div>';
echo '<img src="' .
esc_url($contact_info["qr_code"]) .
'" alt="' .
esc_attr__("QR Code", "wp-multisite-dashboard") .
'" class="msd-qr-image">';
echo "</div>";
}
echo '</div>';
echo "</div>";
echo '<div class="msd-contact-actions">';
echo '<button class="button button-small" onclick="MSD.showContactInfoModal()">' . __('Edit Contact Info', 'wp-multisite-dashboard') . '</button>';
echo '</div>';
echo '<button class="button button-small" onclick="MSD.showContactInfoModal()">' .
__("Edit Contact Info", "wp-multisite-dashboard") .
"</button>";
echo "</div>";
echo '</div>';
echo "</div>";
}
public function render_last_edits_widget() {
public function render_last_edits_widget()
{
echo '<div id="msd-last-edits" class="msd-widget-content" data-widget="last_edits">';
echo '<button class="msd-refresh-btn" title="Refresh" data-widget="last_edits">↻</button>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' . __('Loading...', 'wp-multisite-dashboard') . '</div>';
echo '</div>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' .
__("Loading...", "wp-multisite-dashboard") .
"</div>";
echo "</div>";
}
public function render_quick_links_widget() {
$quick_links = get_site_option('msd_quick_links', []);
public function render_quick_links_widget()
{
$quick_links = get_site_option("msd_quick_links", []);
echo '<div id="msd-quick-links" class="msd-widget-content">';
if (empty($quick_links)) {
echo '<div class="msd-empty-state">';
echo '<p>' . __('No quick links configured.', 'wp-multisite-dashboard') . '</p>';
echo '<button class="button button-primary button-small" onclick="MSD.showQuickLinksModal()">' . __('Add Links', 'wp-multisite-dashboard') . '</button>';
echo '</div>';
echo "<p>" .
__("No quick links configured.", "wp-multisite-dashboard") .
"</p>";
echo '<button class="button button-primary button-small" onclick="MSD.showQuickLinksModal()">' .
__("Add Links", "wp-multisite-dashboard") .
"</button>";
echo "</div>";
} else {
echo '<div class="msd-quick-links-grid" id="msd-sortable-links">';
foreach ($quick_links as $index => $link) {
$target = !empty($link['new_tab']) ? '_blank' : '_self';
echo '<a href="' . esc_url($link['url']) . '" target="' . $target . '" class="msd-quick-link-item" data-index="' . $index . '">';
$target = !empty($link["new_tab"]) ? "_blank" : "_self";
echo '<a href="' .
esc_url($link["url"]) .
'" target="' .
$target .
'" class="msd-quick-link-item" data-index="' .
$index .
'">';
if (!empty($link['icon'])) {
if (strpos($link['icon'], 'dashicons-') === 0) {
echo '<span class="dashicons ' . esc_attr($link['icon']) . '"></span>';
} elseif (mb_strlen($link['icon']) <= 4 && preg_match('/[\x{1F000}-\x{1F9FF}]/u', $link['icon'])) {
echo '<span class="msd-emoji-icon">' . esc_html($link['icon']) . '</span>';
if (!empty($link["icon"])) {
if (strpos($link["icon"], "dashicons-") === 0) {
echo '<span class="dashicons ' .
esc_attr($link["icon"]) .
'"></span>';
} elseif (
mb_strlen($link["icon"]) <= 4 &&
preg_match("/[\x{1F000}-\x{1F9FF}]/u", $link["icon"])
) {
echo '<span class="msd-emoji-icon">' .
esc_html($link["icon"]) .
"</span>";
}
}
echo '<span>' . esc_html($link['title']) . '</span>';
echo '</a>';
echo "<span>" . esc_html($link["title"]) . "</span>";
echo "</a>";
}
echo '</div>';
echo "</div>";
echo '<div class="msd-widget-footer">';
echo '<button class="button button-secondary button-small" onclick="MSD.showQuickLinksModal()">' . __('Edit Links', 'wp-multisite-dashboard') . '</button>';
echo '</div>';
echo '<button class="button button-secondary button-small" onclick="MSD.showQuickLinksModal()">' .
__("Edit Links", "wp-multisite-dashboard") .
"</button>";
echo "</div>";
}
echo '</div>';
echo "</div>";
}
public function render_todo_widget() {
public function render_todo_widget()
{
echo '<div id="msd-todo-widget" class="msd-widget-content" data-widget="todo_items">';
echo '<button class="msd-refresh-btn" title="Refresh" data-widget="todo_items">↻</button>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' . __('Loading...', 'wp-multisite-dashboard') . '</div>';
echo '</div>';
echo '<div class="msd-loading"><span class="msd-spinner"></span>' .
__("Loading...", "wp-multisite-dashboard") .
"</div>";
echo "</div>";
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,148 +1,278 @@
<?php
if (!defined('ABSPATH')) {
exit;
if (!defined("ABSPATH")) {
exit();
}
class WP_MSD_Settings_Manager {
public function render_settings_page() {
if (isset($_POST['submit']) && wp_verify_nonce($_POST['msd_settings_nonce'], 'msd_settings')) {
class WP_MSD_Settings_Manager
{
public function render_settings_page()
{
if (
isset($_POST["submit"]) &&
wp_verify_nonce($_POST["msd_settings_nonce"], "msd_settings")
) {
$this->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'
"msd_network_overview" => __(
"Network Overview",
"wp-multisite-dashboard"
),
"msd_quick_site_management" => __(
"Quick Site Management",
"wp-multisite-dashboard"
),
"msd_storage_performance" => __(
"Storage Usage",
"wp-multisite-dashboard"
),
"msd_server_info" => __(
"Server Information",
"wp-multisite-dashboard"
),
"msd_quick_links" => __("Quick Links", "wp-multisite-dashboard"),
"msd_version_info" => __(
"Version Information",
"wp-multisite-dashboard"
),
"msd_custom_news" => __("Network News", "wp-multisite-dashboard"),
"msd_user_management" => __(
"User Management",
"wp-multisite-dashboard"
),
"msd_contact_info" => __(
"Contact Information",
"wp-multisite-dashboard"
),
"msd_last_edits" => __(
"Recent Network Activity",
"wp-multisite-dashboard"
),
"msd_todo_widget" => __("Todo List", "wp-multisite-dashboard"),
];
include WP_MSD_PLUGIN_DIR . 'templates/settings-page.php';
include WP_MSD_PLUGIN_DIR . "templates/settings-page.php";
}
private function save_settings() {
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'
"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])) {
if (isset($_POST["widgets"][$widget_id])) {
$enabled_widgets[$widget_id] = 1;
}
}
update_site_option('msd_enabled_widgets', $enabled_widgets);
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])) {
$all_available_widgets = $this->get_all_possible_widgets();
if (
isset($_POST["system_widgets"]) &&
is_array($_POST["system_widgets"])
) {
foreach ($all_available_widgets as $widget_id => $widget_data) {
if (
!$widget_data["is_custom"] &&
!isset($_POST["system_widgets"][$widget_id])
) {
$disabled_system_widgets[] = $widget_id;
}
}
} else {
foreach ($all_available_widgets as $widget_id => $widget_data) {
if (!$widget_data["is_custom"]) {
$disabled_system_widgets[] = $widget_id;
}
}
}
update_site_option('msd_disabled_system_widgets', $disabled_system_widgets);
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;
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
public function get_available_system_widgets()
{
$cached_widgets = get_site_transient("msd_detected_widgets");
$known_system_widgets = $this->get_known_system_widgets();
if ($cached_widgets && is_array($cached_widgets)) {
return array_merge($known_system_widgets, $cached_widgets);
}
return $known_system_widgets;
}
private function get_known_system_widgets()
{
return [
"network_dashboard_right_now" => [
"id" => "network_dashboard_right_now",
"title" => __("Right Now", "wp-multisite-dashboard"),
"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_activity" => [
"id" => "dashboard_activity",
"title" => __("Activity", "wp-multisite-dashboard"),
"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_plugins" => [
"id" => "dashboard_plugins",
"title" => __("Plugins", "wp-multisite-dashboard"),
"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_primary" => [
"id" => "dashboard_primary",
"title" => __(
"WordPress Events and News",
"wp-multisite-dashboard"
),
"context" => "side",
"priority" => "core",
"is_custom" => false,
"is_system" => true,
],
"dashboard_secondary" => [
"id" => "dashboard_secondary",
"title" => __("Other WordPress News", "wp-multisite-dashboard"),
"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;
private function get_all_possible_widgets()
{
$known_widgets = $this->get_known_system_widgets();
$cached_widgets = get_site_transient("msd_detected_widgets");
$stored_disabled = get_site_option("msd_disabled_system_widgets", []);
$all_widgets = $known_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;
if (!isset($all_widgets[$widget_id])) {
$all_widgets[$widget_id] = $widget_data;
}
}
}
return $available_widgets;
foreach ($stored_disabled as $widget_id) {
if (!isset($all_widgets[$widget_id])) {
$all_widgets[$widget_id] = [
"id" => $widget_id,
"title" => $this->generate_widget_title_from_id($widget_id),
"context" => "unknown",
"priority" => "default",
"is_custom" => false,
"is_system" => false,
];
}
}
return $all_widgets;
}
public function get_widget_description($widget_id) {
private function generate_widget_title_from_id($widget_id)
{
$title = str_replace(["_", "-"], " ", $widget_id);
$title = ucwords($title);
return $title;
}
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')
"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] ?? '';
return $descriptions[$widget_id] ?? "";
}
}

View file

@ -1,518 +0,0 @@
<?php
if (!defined('ABSPATH')) {
exit;
}
class WP_MSD_Todo_Manager {
private $wpdb;
private $table_name;
private $cache_group = 'msd_todos';
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
$this->table_name = $wpdb->base_prefix . 'msd_todo_list';
}
public function create_todo_table() {
$charset_collate = $this->wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
item varchar(500) NOT NULL,
completed tinyint(1) DEFAULT 0,
priority enum('low','medium','high') DEFAULT 'medium',
user_id bigint(20) unsigned NOT NULL,
created_at datetime NOT NULL,
updated_at datetime DEFAULT NULL,
due_date datetime DEFAULT NULL,
PRIMARY KEY (id),
KEY user_id (user_id),
KEY completed (completed),
KEY priority (priority),
KEY created_at (created_at)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
public function get_todo_items($limit = 50, $user_id = null, $completed = null) {
$cache_key = "todo_items_{$limit}_" . ($user_id ?? 'all') . "_" . ($completed ?? 'all');
$cached = wp_cache_get($cache_key, $this->cache_group);
if ($cached !== false) {
return $cached;
}
$where_clauses = ['1=1'];
$where_values = [];
if ($user_id !== null) {
$where_clauses[] = 'user_id = %d';
$where_values[] = $user_id;
}
if ($completed !== null) {
$where_clauses[] = 'completed = %d';
$where_values[] = $completed ? 1 : 0;
}
$where_sql = implode(' AND ', $where_clauses);
$sql = "SELECT * FROM {$this->table_name}
WHERE {$where_sql}
ORDER BY completed ASC, priority DESC, created_at DESC
LIMIT %d";
$where_values[] = $limit;
$prepared_sql = $this->wpdb->prepare($sql, ...$where_values);
$results = $this->wpdb->get_results($prepared_sql, ARRAY_A);
$todos = [];
foreach ($results as $row) {
$user_data = get_userdata($row['user_id']);
$todos[] = [
'id' => intval($row['id']),
'item' => $row['item'],
'completed' => (bool)$row['completed'],
'priority' => $row['priority'],
'user_id' => intval($row['user_id']),
'user_name' => $user_data ? $user_data->display_name : 'Unknown User',
'user_avatar' => $user_data ? get_avatar_url($user_data->ID, 32) : '',
'created_at' => $row['created_at'],
'updated_at' => $row['updated_at'],
'due_date' => $row['due_date'],
'created_human' => human_time_diff(strtotime($row['created_at'])) . ' ago',
'due_human' => $row['due_date'] ? human_time_diff(strtotime($row['due_date'])) : null,
'is_overdue' => $row['due_date'] && strtotime($row['due_date']) < current_time('timestamp') && !$row['completed'],
'priority_label' => $this->get_priority_label($row['priority']),
'priority_class' => $this->get_priority_class($row['priority'])
];
}
wp_cache_set($cache_key, $todos, $this->cache_group, 1800);
return $todos;
}
public function add_todo_item($item, $priority = 'medium', $user_id = null, $due_date = null) {
if (empty($item)) {
return false;
}
$user_id = $user_id ?: get_current_user_id();
if (!$user_id) {
return false;
}
$data = [
'item' => sanitize_text_field($item),
'priority' => in_array($priority, ['low', 'medium', 'high']) ? $priority : 'medium',
'user_id' => $user_id,
'created_at' => current_time('mysql'),
'due_date' => $due_date ? date('Y-m-d H:i:s', strtotime($due_date)) : null
];
$result = $this->wpdb->insert(
$this->table_name,
$data,
['%s', '%s', '%d', '%s', '%s']
);
if ($result !== false) {
$this->clear_cache();
if (class_exists('WP_MSD_Network_Data')) {
$network_data = new WP_MSD_Network_Data();
$network_data->log_activity(
0,
'todo_added',
sprintf('Todo item added: %s', substr($item, 0, 50)),
'low',
$user_id
);
}
}
return $result !== false;
}
public function delete_todo_item($item_id, $user_id = null) {
$item_id = intval($item_id);
if (!$item_id) {
return false;
}
$where = ['id' => $item_id];
$where_format = ['%d'];
if ($user_id) {
$where['user_id'] = $user_id;
$where_format[] = '%d';
} elseif (!current_user_can('manage_network')) {
$where['user_id'] = get_current_user_id();
$where_format[] = '%d';
}
$item = $this->wpdb->get_row(
$this->wpdb->prepare(
"SELECT item FROM {$this->table_name} WHERE id = %d",
$item_id
)
);
$result = $this->wpdb->delete($this->table_name, $where, $where_format);
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(
0,
'todo_deleted',
sprintf('Todo item deleted: %s', substr($item->item, 0, 50)),
'low'
);
}
}
return $result !== false;
}
public function toggle_todo_item($item_id, $completed = null, $user_id = null) {
$item_id = intval($item_id);
if (!$item_id) {
return false;
}
$where_clause = 'id = %d';
$where_values = [$item_id];
if ($user_id) {
$where_clause .= ' AND user_id = %d';
$where_values[] = $user_id;
} elseif (!current_user_can('manage_network')) {
$where_clause .= ' AND user_id = %d';
$where_values[] = get_current_user_id();
}
if ($completed === null) {
$current_item = $this->wpdb->get_row(
$this->wpdb->prepare(
"SELECT completed FROM {$this->table_name} WHERE {$where_clause}",
...$where_values
)
);
if (!$current_item) {
return false;
}
$completed = !$current_item->completed;
}
$result = $this->wpdb->update(
$this->table_name,
[
'completed' => $completed ? 1 : 0,
'updated_at' => current_time('mysql')
],
['id' => $item_id],
['%d', '%s'],
['%d']
);
if ($result !== false) {
$this->clear_cache();
}
return $result !== false;
}
public function update_todo_item($item_id, $data, $user_id = null) {
$item_id = intval($item_id);
if (!$item_id) {
return false;
}
$allowed_fields = ['item', 'priority', 'due_date', 'completed'];
$update_data = [];
$update_format = [];
foreach ($data as $field => $value) {
if (!in_array($field, $allowed_fields)) {
continue;
}
switch ($field) {
case 'item':
$update_data['item'] = sanitize_text_field($value);
$update_format[] = '%s';
break;
case 'priority':
if (in_array($value, ['low', 'medium', 'high'])) {
$update_data['priority'] = $value;
$update_format[] = '%s';
}
break;
case 'due_date':
$update_data['due_date'] = $value ? date('Y-m-d H:i:s', strtotime($value)) : null;
$update_format[] = '%s';
break;
case 'completed':
$update_data['completed'] = $value ? 1 : 0;
$update_format[] = '%d';
break;
}
}
if (empty($update_data)) {
return false;
}
$update_data['updated_at'] = current_time('mysql');
$update_format[] = '%s';
$where = ['id' => $item_id];
$where_format = ['%d'];
if ($user_id) {
$where['user_id'] = $user_id;
$where_format[] = '%d';
} elseif (!current_user_can('manage_network')) {
$where['user_id'] = get_current_user_id();
$where_format[] = '%d';
}
$result = $this->wpdb->update(
$this->table_name,
$update_data,
$where,
$update_format,
$where_format
);
if ($result !== false) {
$this->clear_cache();
}
return $result !== false;
}
public function get_todo_statistics() {
$cache_key = 'todo_statistics';
$cached = wp_cache_get($cache_key, $this->cache_group);
if ($cached !== false) {
return $cached;
}
$stats = [
'total' => 0,
'completed' => 0,
'pending' => 0,
'overdue' => 0,
'by_priority' => [
'high' => 0,
'medium' => 0,
'low' => 0
],
'by_user' => []
];
$total_result = $this->wpdb->get_row(
"SELECT
COUNT(*) as total,
SUM(completed) as completed,
SUM(CASE WHEN completed = 0 THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN due_date < NOW() AND completed = 0 THEN 1 ELSE 0 END) as overdue
FROM {$this->table_name}"
);
if ($total_result) {
$stats['total'] = intval($total_result->total);
$stats['completed'] = intval($total_result->completed);
$stats['pending'] = intval($total_result->pending);
$stats['overdue'] = intval($total_result->overdue);
}
$priority_results = $this->wpdb->get_results(
"SELECT priority, COUNT(*) as count
FROM {$this->table_name}
WHERE completed = 0
GROUP BY priority"
);
foreach ($priority_results as $priority_result) {
if (isset($stats['by_priority'][$priority_result->priority])) {
$stats['by_priority'][$priority_result->priority] = intval($priority_result->count);
}
}
$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
LIMIT 10"
);
foreach ($user_results as $user_result) {
$user_data = get_userdata($user_result->user_id);
$stats['by_user'][] = [
'user_id' => intval($user_result->user_id),
'user_name' => $user_data ? $user_data->display_name : 'Unknown User',
'total' => intval($user_result->total),
'completed' => intval($user_result->completed),
'pending' => intval($user_result->total) - intval($user_result->completed)
];
}
wp_cache_set($cache_key, $stats, $this->cache_group, 3600);
return $stats;
}
public function get_user_todos($user_id, $limit = 20) {
return $this->get_todo_items($limit, $user_id);
}
public function get_overdue_todos($limit = 20) {
$cache_key = "overdue_todos_{$limit}";
$cached = wp_cache_get($cache_key, $this->cache_group);
if ($cached !== false) {
return $cached;
}
$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(
$this->wpdb->prepare($sql, $limit),
ARRAY_A
);
$todos = [];
foreach ($results as $row) {
$user_data = get_userdata($row['user_id']);
$todos[] = [
'id' => intval($row['id']),
'item' => $row['item'],
'priority' => $row['priority'],
'user_id' => intval($row['user_id']),
'user_name' => $user_data ? $user_data->display_name : 'Unknown User',
'due_date' => $row['due_date'],
'due_human' => human_time_diff(strtotime($row['due_date'])) . ' overdue',
'created_at' => $row['created_at'],
'priority_label' => $this->get_priority_label($row['priority']),
'priority_class' => $this->get_priority_class($row['priority'])
];
}
wp_cache_set($cache_key, $todos, $this->cache_group, 1800);
return $todos;
}
public function bulk_delete_completed($user_id = null) {
$where_clause = 'completed = 1';
$where_values = [];
if ($user_id) {
$where_clause .= ' AND user_id = %d';
$where_values[] = $user_id;
} elseif (!current_user_can('manage_network')) {
$where_clause .= ' AND user_id = %d';
$where_values[] = get_current_user_id();
}
if (!empty($where_values)) {
$sql = "DELETE FROM {$this->table_name} WHERE {$where_clause}";
$result = $this->wpdb->query($this->wpdb->prepare($sql, ...$where_values));
} else {
$result = $this->wpdb->delete($this->table_name, ['completed' => 1], ['%d']);
}
if ($result !== false) {
$this->clear_cache();
if (class_exists('WP_MSD_Network_Data')) {
$network_data = new WP_MSD_Network_Data();
$network_data->log_activity(
0,
'todo_bulk_delete',
sprintf('Bulk deleted %d completed todo items', $result),
'medium'
);
}
}
return $result !== false;
}
private function get_priority_label($priority) {
$labels = [
'low' => __('Low', 'wp-multisite-dashboard'),
'medium' => __('Medium', 'wp-multisite-dashboard'),
'high' => __('High', 'wp-multisite-dashboard')
];
return $labels[$priority] ?? $labels['medium'];
}
private function get_priority_class($priority) {
$classes = [
'low' => 'msd-priority-low',
'medium' => 'msd-priority-medium',
'high' => 'msd-priority-high'
];
return $classes[$priority] ?? $classes['medium'];
}
public function clear_cache() {
wp_cache_flush_group($this->cache_group);
return true;
}
public function cleanup_old_todos($days = 90) {
if (!current_user_can('manage_network')) {
return false;
}
$cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
$result = $this->wpdb->delete(
$this->table_name,
[
'completed' => 1,
'updated_at' => $cutoff_date
],
['%d', '%s']
);
if ($result !== false) {
$this->clear_cache();
if (class_exists('WP_MSD_Network_Data')) {
$network_data = new WP_MSD_Network_Data();
$network_data->log_activity(
0,
'todo_cleanup',
sprintf('Cleaned up %d old completed todo items', $result),
'medium'
);
}
}
return $result !== false;
}
}