mirror of
https://github.com/WPMultisite/wp-multisite-dashboard.git
synced 2025-08-03 11:11:31 +08:00
1061 lines
37 KiB
PHP
Executable file
1061 lines
37 KiB
PHP
Executable file
<?php
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class WP_MSD_User_Manager {
|
|
|
|
private $wpdb;
|
|
private $cache_group = 'msd_users';
|
|
|
|
public function __construct() {
|
|
global $wpdb;
|
|
$this->wpdb = $wpdb;
|
|
}
|
|
|
|
public function get_recent_users_data($limit = 10) {
|
|
$cache_key = "recent_users_data_{$limit}";
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
$recent_users = $this->get_recent_registered_users($limit);
|
|
$pending_users = $this->get_pending_users($limit);
|
|
$super_admins = get_super_admins();
|
|
|
|
$data = [
|
|
'recent_registrations' => $recent_users,
|
|
'pending_activations' => $pending_users,
|
|
'total_users' => $this->get_total_users_count(),
|
|
'super_admin_count' => count($super_admins),
|
|
'user_actions' => $this->get_available_user_actions(),
|
|
'registration_status' => $this->get_registration_status(),
|
|
'last_updated' => current_time('mysql')
|
|
];
|
|
|
|
wp_cache_set($cache_key, $data, $this->cache_group, 1800);
|
|
return $data;
|
|
}
|
|
|
|
private function get_recent_registered_users($limit = 10) {
|
|
$args = [
|
|
'number' => $limit,
|
|
'orderby' => 'registered',
|
|
'order' => 'DESC',
|
|
'fields' => 'all'
|
|
];
|
|
|
|
$users = get_users($args);
|
|
$user_data = [];
|
|
|
|
foreach ($users as $user) {
|
|
$last_login = get_user_meta($user->ID, 'msd_last_login', true);
|
|
$login_count = get_user_meta($user->ID, 'msd_login_count', true);
|
|
|
|
$user_sites = get_blogs_of_user($user->ID);
|
|
$sites_count = count($user_sites);
|
|
|
|
$user_data[] = [
|
|
'ID' => $user->ID,
|
|
'user_login' => $user->user_login,
|
|
'user_email' => $user->user_email,
|
|
'display_name' => $user->display_name,
|
|
'user_registered' => $user->user_registered,
|
|
'registered_ago' => human_time_diff(strtotime($user->user_registered)) . ' ago',
|
|
'is_super_admin' => is_super_admin($user->ID),
|
|
'sites_count' => $sites_count,
|
|
'last_login' => $last_login,
|
|
'last_login_human' => $last_login ? human_time_diff(strtotime($last_login)) . ' ago' : __('Never', 'wp-multisite-dashboard'),
|
|
'login_count' => intval($login_count),
|
|
'status' => $this->get_user_status_from_activity($last_login),
|
|
'profile_url' => network_admin_url('user-edit.php?user_id=' . $user->ID),
|
|
'sites_url' => network_admin_url('users.php?action=allusers&s=' . urlencode($user->user_login)),
|
|
'avatar_url' => $this->get_user_avatar($user->ID, 32)
|
|
];
|
|
}
|
|
|
|
return $user_data;
|
|
}
|
|
|
|
private function get_user_avatar($user_id, $size = 32) {
|
|
$cache_key = "user_avatar_{$user_id}_{$size}";
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
$avatar_url = get_avatar_url($user_id, [
|
|
'size' => $size,
|
|
'default' => 'mp',
|
|
'force_default' => false
|
|
]);
|
|
|
|
if (empty($avatar_url)) {
|
|
$avatar_url = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgdmlld0JveD0iMCAwIDQwIDQwIj48Y2lyY2xlIGN4PSIyMCIgY3k9IjIwIiByPSIyMCIgZmlsbD0iI2Y2ZjdmNyIgc3Ryb2tlPSIjZGRkIi8+PGNpcmNsZSBjeD0iMjAiIGN5PSIxNSIgcj0iNiIgZmlsbD0iIzk5OSIvPjxlbGxpcHNlIGN4PSIyMCIgY3k9IjMzIiByeD0iMTAiIHJ5PSI3IiBmaWxsPSIjOTk5Ii8+PC9zdmc+';
|
|
}
|
|
|
|
wp_cache_set($cache_key, $avatar_url, $this->cache_group, 3600);
|
|
return $avatar_url;
|
|
}
|
|
|
|
private function get_pending_users($limit = 10) {
|
|
$pending_users = [];
|
|
|
|
$signup_table = $this->wpdb->base_prefix . 'signups';
|
|
$table_exists = $this->wpdb->get_var("SHOW TABLES LIKE '{$signup_table}'") === $signup_table;
|
|
|
|
if ($table_exists) {
|
|
$signups = $this->wpdb->get_results(
|
|
$this->wpdb->prepare(
|
|
"SELECT * FROM {$signup_table}
|
|
WHERE active = 0
|
|
ORDER BY registered DESC
|
|
LIMIT %d",
|
|
$limit
|
|
),
|
|
ARRAY_A
|
|
);
|
|
|
|
foreach ($signups as $signup) {
|
|
$pending_users[] = [
|
|
'ID' => $signup['signup_id'],
|
|
'user_login' => $signup['user_login'],
|
|
'user_email' => $signup['user_email'],
|
|
'domain' => $signup['domain'],
|
|
'path' => $signup['path'],
|
|
'registered' => $signup['registered'],
|
|
'registered_ago' => human_time_diff(strtotime($signup['registered'])) . ' ago',
|
|
'activation_key' => $signup['activation_key'],
|
|
'meta' => maybe_unserialize($signup['meta']),
|
|
'activate_url' => network_admin_url('users.php?action=activate&key=' . $signup['activation_key'])
|
|
];
|
|
}
|
|
}
|
|
|
|
return $pending_users;
|
|
}
|
|
|
|
private function get_user_status_from_activity($last_login) {
|
|
if (empty($last_login)) {
|
|
return 'never_logged_in';
|
|
}
|
|
|
|
$days_since_login = (time() - strtotime($last_login)) / DAY_IN_SECONDS;
|
|
|
|
if ($days_since_login <= 7) {
|
|
return 'active';
|
|
} elseif ($days_since_login <= 30) {
|
|
return 'recent';
|
|
} elseif ($days_since_login <= 90) {
|
|
return 'inactive';
|
|
} else {
|
|
return 'very_inactive';
|
|
}
|
|
}
|
|
|
|
private function get_total_users_count() {
|
|
$user_count = count_users();
|
|
return $user_count['total_users'];
|
|
}
|
|
|
|
private function get_available_user_actions() {
|
|
return [
|
|
'edit_user' => __('Edit User', 'wp-multisite-dashboard'),
|
|
'delete_user' => __('Delete User', 'wp-multisite-dashboard'),
|
|
'send_password_reset' => __('Send Password Reset', 'wp-multisite-dashboard'),
|
|
'promote_super_admin' => __('Make Super Admin', 'wp-multisite-dashboard'),
|
|
'demote_super_admin' => __('Remove Super Admin', 'wp-multisite-dashboard'),
|
|
'activate_user' => __('Activate User', 'wp-multisite-dashboard'),
|
|
'deactivate_user' => __('Deactivate User', 'wp-multisite-dashboard')
|
|
];
|
|
}
|
|
|
|
private function get_registration_status() {
|
|
$registration = get_network_option(null, 'registration');
|
|
|
|
$status_map = [
|
|
'none' => __('Registration disabled', 'wp-multisite-dashboard'),
|
|
'user' => __('User registration enabled', 'wp-multisite-dashboard'),
|
|
'blog' => __('Site registration enabled', 'wp-multisite-dashboard'),
|
|
'all' => __('User and site registration enabled', 'wp-multisite-dashboard')
|
|
];
|
|
|
|
return [
|
|
'setting' => $registration,
|
|
'description' => $status_map[$registration] ?? __('Unknown', 'wp-multisite-dashboard'),
|
|
'settings_url' => network_admin_url('settings.php')
|
|
];
|
|
}
|
|
|
|
public function search_users($search_term = '', $role_filter = '', $activity_filter = '', $limit = 100) {
|
|
$cache_key = "search_users_" . md5($search_term . $role_filter . $activity_filter . $limit);
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
$query_args = [
|
|
'number' => $limit,
|
|
'fields' => 'all',
|
|
'orderby' => 'registered',
|
|
'order' => 'DESC'
|
|
];
|
|
|
|
if (!empty($search_term)) {
|
|
$query_args['search'] = '*' . esc_attr($search_term) . '*';
|
|
$query_args['search_columns'] = ['user_login', 'user_email', 'display_name'];
|
|
}
|
|
|
|
if (!empty($role_filter)) {
|
|
$query_args['role'] = $role_filter;
|
|
}
|
|
|
|
$users = get_users($query_args);
|
|
$user_ids = array_column($users, 'ID');
|
|
|
|
$user_sites_data = $this->get_batch_user_sites($user_ids);
|
|
$user_meta_data = $this->get_batch_user_meta($user_ids);
|
|
$filtered_users = [];
|
|
|
|
foreach ($users as $user) {
|
|
$user_id = $user->ID;
|
|
|
|
$user_data = [
|
|
'ID' => $user_id,
|
|
'user_login' => $user->user_login,
|
|
'user_email' => $user->user_email,
|
|
'display_name' => $user->display_name,
|
|
'user_registered' => $user->user_registered,
|
|
'sites' => $user_sites_data[$user_id] ?? [],
|
|
'roles' => $this->get_user_network_roles($user_id, $user_sites_data[$user_id] ?? []),
|
|
'last_activity' => $user_meta_data[$user_id]['last_activity'] ?? '',
|
|
'login_count' => $user_meta_data[$user_id]['login_count'] ?? 0,
|
|
'status' => $this->get_user_status($user_meta_data[$user_id]['last_activity'] ?? ''),
|
|
'is_super_admin' => is_super_admin($user_id),
|
|
'capabilities' => $this->get_user_capabilities_summary($user_id)
|
|
];
|
|
|
|
$user_data['last_activity_human'] = $this->format_last_activity($user_data['last_activity']);
|
|
|
|
if ($this->user_matches_activity_filter($user_data, $activity_filter)) {
|
|
$filtered_users[] = $user_data;
|
|
}
|
|
}
|
|
|
|
wp_cache_set($cache_key, $filtered_users, $this->cache_group, 1800);
|
|
return $filtered_users;
|
|
}
|
|
|
|
private function get_batch_user_sites($user_ids) {
|
|
if (empty($user_ids)) {
|
|
return [];
|
|
}
|
|
|
|
$user_sites = [];
|
|
$sites = get_sites(['number' => 1000]);
|
|
|
|
foreach ($sites as $site) {
|
|
$blog_id = $site->blog_id;
|
|
$capabilities_key = $this->wpdb->get_blog_prefix($blog_id) . 'capabilities';
|
|
|
|
$user_meta_results = $this->wpdb->get_results(
|
|
$this->wpdb->prepare(
|
|
"SELECT user_id, meta_value FROM {$this->wpdb->usermeta}
|
|
WHERE meta_key = %s AND user_id IN (" . implode(',', array_map('intval', $user_ids)) . ")",
|
|
$capabilities_key
|
|
),
|
|
ARRAY_A
|
|
);
|
|
|
|
$site_name = $this->get_site_name($blog_id);
|
|
|
|
foreach ($user_meta_results as $result) {
|
|
$user_id = $result['user_id'];
|
|
$capabilities = maybe_unserialize($result['meta_value']);
|
|
|
|
if (is_array($capabilities) && !empty($capabilities)) {
|
|
if (!isset($user_sites[$user_id])) {
|
|
$user_sites[$user_id] = [];
|
|
}
|
|
|
|
$user_sites[$user_id][] = [
|
|
'blog_id' => $blog_id,
|
|
'blogname' => $site_name,
|
|
'domain' => $site->domain . $site->path,
|
|
'roles' => array_keys($capabilities)
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $user_sites;
|
|
}
|
|
|
|
private function get_batch_user_meta($user_ids) {
|
|
if (empty($user_ids)) {
|
|
return [];
|
|
}
|
|
|
|
$user_meta = [];
|
|
$user_ids_str = implode(',', array_map('intval', $user_ids));
|
|
|
|
$results = $this->wpdb->get_results(
|
|
"SELECT user_id, meta_key, meta_value
|
|
FROM {$this->wpdb->usermeta}
|
|
WHERE user_id IN ($user_ids_str)
|
|
AND meta_key IN ('msd_last_activity', 'last_activity', 'msd_login_count', 'msd_last_login', 'msd_user_status')",
|
|
ARRAY_A
|
|
);
|
|
|
|
foreach ($user_ids as $user_id) {
|
|
$user_meta[$user_id] = [
|
|
'last_activity' => '',
|
|
'login_count' => 0,
|
|
'last_login' => '',
|
|
'user_status' => 'active'
|
|
];
|
|
}
|
|
|
|
foreach ($results as $row) {
|
|
$user_id = $row['user_id'];
|
|
|
|
switch ($row['meta_key']) {
|
|
case 'msd_last_activity':
|
|
case 'last_activity':
|
|
if (empty($user_meta[$user_id]['last_activity']) ||
|
|
strtotime($row['meta_value']) > strtotime($user_meta[$user_id]['last_activity'])) {
|
|
$user_meta[$user_id]['last_activity'] = $row['meta_value'];
|
|
}
|
|
break;
|
|
case 'msd_login_count':
|
|
$user_meta[$user_id]['login_count'] = intval($row['meta_value']);
|
|
break;
|
|
case 'msd_last_login':
|
|
$user_meta[$user_id]['last_login'] = $row['meta_value'];
|
|
break;
|
|
case 'msd_user_status':
|
|
$user_meta[$user_id]['user_status'] = $row['meta_value'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $user_meta;
|
|
}
|
|
|
|
private function get_site_name($blog_id) {
|
|
$cache_key = "site_name_{$blog_id}";
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
$site_details = get_blog_details($blog_id);
|
|
$name = $site_details ? $site_details->blogname : __('Unknown Site', 'wp-multisite-dashboard');
|
|
|
|
wp_cache_set($cache_key, $name, $this->cache_group, 3600);
|
|
return $name;
|
|
}
|
|
|
|
private function get_user_network_roles($user_id, $sites_data) {
|
|
$roles = [];
|
|
|
|
foreach ($sites_data as $site) {
|
|
if (!empty($site['roles'])) {
|
|
$roles[$site['blog_id']] = $site['roles'];
|
|
}
|
|
}
|
|
|
|
return $roles;
|
|
}
|
|
|
|
private function get_user_capabilities_summary($user_id) {
|
|
$user = get_userdata($user_id);
|
|
if (!$user) {
|
|
return [];
|
|
}
|
|
|
|
$capabilities = [];
|
|
|
|
if (is_super_admin($user_id)) {
|
|
$capabilities[] = 'Super Admin';
|
|
}
|
|
|
|
if ($user->has_cap('manage_network')) {
|
|
$capabilities[] = 'Network Admin';
|
|
}
|
|
|
|
if ($user->has_cap('manage_options')) {
|
|
$capabilities[] = 'Site Admin';
|
|
}
|
|
|
|
if ($user->has_cap('edit_posts')) {
|
|
$capabilities[] = 'Content Editor';
|
|
}
|
|
|
|
return array_unique($capabilities);
|
|
}
|
|
|
|
private function get_user_status($last_activity) {
|
|
if (empty($last_activity)) {
|
|
return 'inactive';
|
|
}
|
|
|
|
$last_active_timestamp = strtotime($last_activity);
|
|
$now = current_time('timestamp');
|
|
$days_inactive = ($now - $last_active_timestamp) / DAY_IN_SECONDS;
|
|
|
|
if ($days_inactive > 180) {
|
|
return 'inactive_180';
|
|
} elseif ($days_inactive > 90) {
|
|
return 'inactive_90';
|
|
} elseif ($days_inactive > 30) {
|
|
return 'inactive_30';
|
|
}
|
|
|
|
return 'active';
|
|
}
|
|
|
|
private function format_last_activity($last_activity) {
|
|
if (empty($last_activity)) {
|
|
return __('Never', 'wp-multisite-dashboard');
|
|
}
|
|
|
|
$timestamp = strtotime($last_activity);
|
|
if ($timestamp === false) {
|
|
return __('Unknown', 'wp-multisite-dashboard');
|
|
}
|
|
|
|
return human_time_diff($timestamp) . ' ago';
|
|
}
|
|
|
|
private function user_matches_activity_filter($user_data, $activity_filter) {
|
|
if (empty($activity_filter)) {
|
|
return true;
|
|
}
|
|
|
|
switch ($activity_filter) {
|
|
case 'active':
|
|
return $user_data['status'] === 'active';
|
|
case 'inactive_30':
|
|
return in_array($user_data['status'], ['inactive_30', 'inactive_90', 'inactive_180', 'inactive']);
|
|
case 'inactive_90':
|
|
return in_array($user_data['status'], ['inactive_90', 'inactive_180', 'inactive']);
|
|
case 'super_admins':
|
|
return $user_data['is_super_admin'];
|
|
case 'no_sites':
|
|
return empty($user_data['sites']);
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public function perform_bulk_action($action, $user_ids, $additional_data = []) {
|
|
if (!current_user_can('manage_network_users')) {
|
|
return ['success' => false, 'message' => __('Insufficient permissions', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
if (empty($user_ids) || !is_array($user_ids)) {
|
|
return ['success' => false, 'message' => __('No users selected', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$user_ids = array_map('intval', $user_ids);
|
|
$user_ids = array_filter($user_ids);
|
|
|
|
if (empty($user_ids)) {
|
|
return ['success' => false, 'message' => __('Invalid user selection', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
if ($action === 'delete_user') {
|
|
return $this->perform_bulk_delete($user_ids);
|
|
}
|
|
|
|
$results = [];
|
|
$success_count = 0;
|
|
$error_count = 0;
|
|
|
|
foreach ($user_ids as $user_id) {
|
|
$result = $this->perform_single_user_action($action, $user_id, $additional_data);
|
|
$results[] = [
|
|
'user_id' => $user_id,
|
|
'success' => $result['success'],
|
|
'message' => $result['message']
|
|
];
|
|
|
|
if ($result['success']) {
|
|
$success_count++;
|
|
} else {
|
|
$error_count++;
|
|
}
|
|
}
|
|
|
|
$this->log_bulk_action($action, $user_ids, $success_count, $error_count);
|
|
$this->clear_user_caches($user_ids);
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => sprintf(
|
|
__('Bulk action completed: %d successful, %d failed', 'wp-multisite-dashboard'),
|
|
$success_count,
|
|
$error_count
|
|
),
|
|
'results' => $results,
|
|
'success_count' => $success_count,
|
|
'error_count' => $error_count
|
|
];
|
|
}
|
|
|
|
private function perform_bulk_delete($user_ids) {
|
|
if (!current_user_can('delete_users')) {
|
|
return ['success' => false, 'message' => __('Insufficient permissions to delete users', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$success_count = 0;
|
|
$error_count = 0;
|
|
$results = [];
|
|
$super_admins = get_super_admins();
|
|
|
|
foreach ($user_ids as $user_id) {
|
|
$user = get_userdata($user_id);
|
|
|
|
if (!$user) {
|
|
$error_count++;
|
|
$results[] = [
|
|
'user_id' => $user_id,
|
|
'success' => false,
|
|
'message' => __('User not found', 'wp-multisite-dashboard')
|
|
];
|
|
continue;
|
|
}
|
|
|
|
if (in_array($user->user_login, $super_admins)) {
|
|
$error_count++;
|
|
$results[] = [
|
|
'user_id' => $user_id,
|
|
'success' => false,
|
|
'message' => __('Cannot delete super admin', 'wp-multisite-dashboard')
|
|
];
|
|
continue;
|
|
}
|
|
|
|
if ($user_id === get_current_user_id()) {
|
|
$error_count++;
|
|
$results[] = [
|
|
'user_id' => $user_id,
|
|
'success' => false,
|
|
'message' => __('Cannot delete your own account', 'wp-multisite-dashboard')
|
|
];
|
|
continue;
|
|
}
|
|
|
|
require_once ABSPATH . 'wp-admin/includes/user.php';
|
|
$result = wpmu_delete_user($user_id);
|
|
|
|
if ($result) {
|
|
$success_count++;
|
|
$results[] = [
|
|
'user_id' => $user_id,
|
|
'success' => true,
|
|
'message' => __('User deleted', 'wp-multisite-dashboard')
|
|
];
|
|
} else {
|
|
$error_count++;
|
|
$results[] = [
|
|
'user_id' => $user_id,
|
|
'success' => false,
|
|
'message' => __('Failed to delete user', 'wp-multisite-dashboard')
|
|
];
|
|
}
|
|
}
|
|
|
|
$this->log_bulk_action('delete_user', $user_ids, $success_count, $error_count);
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => sprintf(
|
|
__('Bulk delete completed: %d successful, %d failed', 'wp-multisite-dashboard'),
|
|
$success_count,
|
|
$error_count
|
|
),
|
|
'results' => $results,
|
|
'success_count' => $success_count,
|
|
'error_count' => $error_count
|
|
];
|
|
}
|
|
|
|
public function perform_single_user_action($action, $user_id, $additional_data = []) {
|
|
try {
|
|
switch ($action) {
|
|
case 'add_to_site':
|
|
return $this->add_user_to_site($user_id, $additional_data);
|
|
|
|
case 'remove_from_site':
|
|
return $this->remove_user_from_site($user_id, $additional_data);
|
|
|
|
case 'change_role':
|
|
return $this->change_user_role($user_id, $additional_data);
|
|
|
|
case 'send_password_reset':
|
|
return $this->send_password_reset($user_id);
|
|
|
|
case 'activate_user':
|
|
return $this->activate_user($user_id);
|
|
|
|
case 'deactivate_user':
|
|
return $this->deactivate_user($user_id);
|
|
|
|
case 'promote_to_super_admin':
|
|
return $this->promote_to_super_admin($user_id);
|
|
|
|
case 'demote_from_super_admin':
|
|
return $this->demote_from_super_admin($user_id);
|
|
|
|
case 'delete_user':
|
|
return $this->delete_single_user($user_id);
|
|
|
|
default:
|
|
return ['success' => false, 'message' => __('Unknown action', 'wp-multisite-dashboard')];
|
|
}
|
|
} catch (Exception $e) {
|
|
return ['success' => false, 'message' => $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
private function delete_single_user($user_id) {
|
|
if (!current_user_can('delete_users')) {
|
|
return ['success' => false, 'message' => __('Insufficient permissions to delete users', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$user = get_userdata($user_id);
|
|
if (!$user) {
|
|
return ['success' => false, 'message' => __('User not found', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$super_admins = get_super_admins();
|
|
if (in_array($user->user_login, $super_admins)) {
|
|
return ['success' => false, 'message' => __('Cannot delete super admin', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
if ($user_id === get_current_user_id()) {
|
|
return ['success' => false, 'message' => __('Cannot delete your own account', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
require_once ABSPATH . 'wp-admin/includes/user.php';
|
|
$result = wpmu_delete_user($user_id);
|
|
|
|
if ($result) {
|
|
return ['success' => true, 'message' => __('User deleted successfully', 'wp-multisite-dashboard')];
|
|
} else {
|
|
return ['success' => false, 'message' => __('Failed to delete user', 'wp-multisite-dashboard')];
|
|
}
|
|
}
|
|
|
|
private function add_user_to_site($user_id, $data) {
|
|
$site_id = intval($data['target_site_id'] ?? 0);
|
|
$role = sanitize_text_field($data['target_role'] ?? 'subscriber');
|
|
|
|
if (!$site_id) {
|
|
return ['success' => false, 'message' => __('Site ID required', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
if (!get_blog_details($site_id)) {
|
|
return ['success' => false, 'message' => __('Site not found', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$result = add_user_to_blog($site_id, $user_id, $role);
|
|
|
|
if (is_wp_error($result)) {
|
|
return ['success' => false, 'message' => $result->get_error_message()];
|
|
}
|
|
|
|
return ['success' => true, 'message' => __('User added to site', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
private function remove_user_from_site($user_id, $data) {
|
|
$site_id = intval($data['target_site_id'] ?? 0);
|
|
|
|
if (!$site_id) {
|
|
return ['success' => false, 'message' => __('Site ID required', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
if ($site_id == 1 && is_super_admin($user_id)) {
|
|
return ['success' => false, 'message' => __('Cannot remove super admin from main site', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$result = remove_user_from_blog($user_id, $site_id);
|
|
|
|
if (is_wp_error($result)) {
|
|
return ['success' => false, 'message' => $result->get_error_message()];
|
|
}
|
|
|
|
return ['success' => true, 'message' => __('User removed from site', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
private function change_user_role($user_id, $data) {
|
|
$site_id = intval($data['target_site_id'] ?? 0);
|
|
$new_role = sanitize_text_field($data['new_role'] ?? $data['target_role'] ?? '');
|
|
|
|
if (!$site_id || !$new_role) {
|
|
return ['success' => false, 'message' => __('Site ID and role required', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
switch_to_blog($site_id);
|
|
$user = get_userdata($user_id);
|
|
|
|
if (!$user) {
|
|
restore_current_blog();
|
|
return ['success' => false, 'message' => __('User not found', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$user->set_role($new_role);
|
|
restore_current_blog();
|
|
|
|
return ['success' => true, 'message' => __('User role changed', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
private function send_password_reset($user_id) {
|
|
$user = get_userdata($user_id);
|
|
|
|
if (!$user) {
|
|
return ['success' => false, 'message' => __('User not found', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$key = get_password_reset_key($user);
|
|
|
|
if (is_wp_error($key)) {
|
|
return ['success' => false, 'message' => $key->get_error_message()];
|
|
}
|
|
|
|
$reset_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user->user_login), 'login');
|
|
|
|
$message = sprintf(
|
|
__('A password reset has been requested for your account on %s.', 'wp-multisite-dashboard'),
|
|
get_network_option(null, 'site_name')
|
|
) . "\r\n\r\n";
|
|
|
|
$message .= sprintf(__('Username: %s'), $user->user_login) . "\r\n\r\n";
|
|
$message .= __('To reset your password, visit the following address:') . "\r\n\r\n";
|
|
$message .= $reset_url . "\r\n\r\n";
|
|
$message .= __('This link will expire in 24 hours.') . "\r\n";
|
|
|
|
$title = sprintf(__('[%s] Password Reset'), get_network_option(null, 'site_name'));
|
|
|
|
$sent = wp_mail($user->user_email, $title, $message);
|
|
|
|
if (!$sent) {
|
|
return ['success' => false, 'message' => __('Failed to send password reset email', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
return ['success' => true, 'message' => __('Password reset email sent', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
private function activate_user($user_id) {
|
|
$user = get_userdata($user_id);
|
|
|
|
if (!$user) {
|
|
return ['success' => false, 'message' => __('User not found', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
delete_user_meta($user_id, 'msd_user_deactivated');
|
|
update_user_meta($user_id, 'msd_user_status', 'active');
|
|
|
|
return ['success' => true, 'message' => __('User activated', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
private function deactivate_user($user_id) {
|
|
if (is_super_admin($user_id)) {
|
|
return ['success' => false, 'message' => __('Cannot deactivate super admin', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
if ($user_id === get_current_user_id()) {
|
|
return ['success' => false, 'message' => __('Cannot deactivate your own account', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
update_user_meta($user_id, 'msd_user_deactivated', current_time('mysql'));
|
|
update_user_meta($user_id, 'msd_user_status', 'deactivated');
|
|
|
|
$sessions = WP_Session_Tokens::get_instance($user_id);
|
|
$sessions->destroy_all();
|
|
|
|
return ['success' => true, 'message' => __('User deactivated', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
private function promote_to_super_admin($user_id) {
|
|
if (!current_user_can('manage_network')) {
|
|
return ['success' => false, 'message' => __('Insufficient permissions', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$user = get_userdata($user_id);
|
|
if (!$user) {
|
|
return ['success' => false, 'message' => __('User not found', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
if (is_super_admin($user_id)) {
|
|
return ['success' => false, 'message' => __('User is already a super admin', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
grant_super_admin($user_id);
|
|
|
|
return ['success' => true, 'message' => __('User promoted to super admin', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
private function demote_from_super_admin($user_id) {
|
|
if (!current_user_can('manage_network')) {
|
|
return ['success' => false, 'message' => __('Insufficient permissions', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
if ($user_id === get_current_user_id()) {
|
|
return ['success' => false, 'message' => __('Cannot demote yourself', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$super_admins = get_super_admins();
|
|
if (count($super_admins) <= 1) {
|
|
return ['success' => false, 'message' => __('Cannot demote the last super admin', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
revoke_super_admin($user_id);
|
|
|
|
return ['success' => true, 'message' => __('User demoted from super admin', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
public function get_inactive_users($days = 90, $limit = 100) {
|
|
$cache_key = "inactive_users_{$days}_{$limit}";
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
$cutoff_date = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
|
|
|
$users = get_users([
|
|
'meta_query' => [
|
|
'relation' => 'OR',
|
|
[
|
|
'key' => 'msd_last_activity',
|
|
'value' => $cutoff_date,
|
|
'compare' => '<',
|
|
'type' => 'DATETIME'
|
|
],
|
|
[
|
|
'key' => 'msd_last_activity',
|
|
'compare' => 'NOT EXISTS'
|
|
]
|
|
],
|
|
'fields' => 'all',
|
|
'number' => $limit,
|
|
'orderby' => 'registered',
|
|
'order' => 'ASC'
|
|
]);
|
|
|
|
$user_ids = array_column($users, 'ID');
|
|
$user_sites_data = $this->get_batch_user_sites($user_ids);
|
|
$user_meta_data = $this->get_batch_user_meta($user_ids);
|
|
|
|
$inactive_users = array_map(function($user) use ($user_sites_data, $user_meta_data) {
|
|
$user_id = $user->ID;
|
|
return [
|
|
'ID' => $user_id,
|
|
'user_login' => $user->user_login,
|
|
'user_email' => $user->user_email,
|
|
'display_name' => $user->display_name,
|
|
'user_registered' => $user->user_registered,
|
|
'last_activity' => $user_meta_data[$user_id]['last_activity'] ?? '',
|
|
'last_activity_human' => $this->format_last_activity($user_meta_data[$user_id]['last_activity'] ?? ''),
|
|
'sites_count' => count($user_sites_data[$user_id] ?? []),
|
|
'is_super_admin' => is_super_admin($user_id)
|
|
];
|
|
}, $users);
|
|
|
|
wp_cache_set($cache_key, $inactive_users, $this->cache_group, 3600);
|
|
return $inactive_users;
|
|
}
|
|
|
|
public function get_user_activity_summary($user_id) {
|
|
$cache_key = "user_activity_{$user_id}";
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
$user = get_userdata($user_id);
|
|
if (!$user) {
|
|
return null;
|
|
}
|
|
|
|
$user_sites = $this->get_batch_user_sites([$user_id])[$user_id] ?? [];
|
|
|
|
$summary = [
|
|
'user_info' => [
|
|
'ID' => $user_id,
|
|
'login' => $user->user_login,
|
|
'email' => $user->user_email,
|
|
'display_name' => $user->display_name,
|
|
'registered' => $user->user_registered
|
|
],
|
|
'network_stats' => [
|
|
'total_sites' => count($user_sites),
|
|
'total_posts' => 0,
|
|
'total_comments' => 0,
|
|
'last_login' => get_user_meta($user_id, 'msd_last_login', true),
|
|
'login_count' => get_user_meta($user_id, 'msd_login_count', true)
|
|
],
|
|
'roles_summary' => [],
|
|
'capabilities' => $this->get_user_capabilities_summary($user_id),
|
|
'security_info' => [
|
|
'is_super_admin' => is_super_admin($user_id),
|
|
'last_ip' => get_user_meta($user_id, 'msd_last_login_ip', true),
|
|
'failed_logins' => $this->get_user_failed_logins($user_id)
|
|
]
|
|
];
|
|
|
|
foreach ($user_sites as $site) {
|
|
$blog_id = $site['blog_id'];
|
|
|
|
switch_to_blog($blog_id);
|
|
|
|
$posts = get_posts([
|
|
'author' => $user_id,
|
|
'post_status' => 'publish',
|
|
'numberposts' => -1,
|
|
'fields' => 'ids'
|
|
]);
|
|
$summary['network_stats']['total_posts'] += count($posts);
|
|
|
|
$comments = get_comments([
|
|
'user_id' => $user_id,
|
|
'status' => 'approve',
|
|
'count' => true
|
|
]);
|
|
$summary['network_stats']['total_comments'] += intval($comments);
|
|
|
|
restore_current_blog();
|
|
|
|
if (!empty($site['roles'])) {
|
|
$summary['roles_summary'][$site['blogname']] = $site['roles'];
|
|
}
|
|
}
|
|
|
|
wp_cache_set($cache_key, $summary, $this->cache_group, 1800);
|
|
return $summary;
|
|
}
|
|
|
|
private function get_user_failed_logins($user_id) {
|
|
$user = get_userdata($user_id);
|
|
if (!$user) {
|
|
return 0;
|
|
}
|
|
|
|
$count = $this->wpdb->get_var(
|
|
$this->wpdb->prepare(
|
|
"SELECT COUNT(*) FROM {$this->wpdb->base_prefix}msd_security_log
|
|
WHERE event_type = 'failed_login'
|
|
AND description LIKE %s
|
|
AND created_at >= %s",
|
|
'%' . $user->user_login . '%',
|
|
date('Y-m-d H:i:s', strtotime('-7 days'))
|
|
)
|
|
);
|
|
|
|
return intval($count);
|
|
}
|
|
|
|
public function get_user_statistics() {
|
|
$cache_key = 'user_statistics';
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
$total_users = count_users();
|
|
$inactive_30 = count($this->get_inactive_users(30, 1000));
|
|
$inactive_90 = count($this->get_inactive_users(90, 1000));
|
|
$super_admins = count(get_super_admins());
|
|
|
|
$statistics = [
|
|
'total_users' => $total_users['total_users'],
|
|
'active_users' => $total_users['total_users'] - $inactive_30,
|
|
'inactive_30_days' => $inactive_30,
|
|
'inactive_90_days' => $inactive_90,
|
|
'super_admins' => $super_admins,
|
|
'users_by_role' => $total_users['avail_roles']
|
|
];
|
|
|
|
wp_cache_set($cache_key, $statistics, $this->cache_group, 3600);
|
|
return $statistics;
|
|
}
|
|
|
|
private function log_bulk_action($action, $user_ids, $success_count, $error_count) {
|
|
if (class_exists('WP_MSD_Network_Data')) {
|
|
$network_data = new WP_MSD_Network_Data();
|
|
$network_data->log_activity(
|
|
0,
|
|
'bulk_user_action',
|
|
sprintf(
|
|
__('Bulk %s performed on %d users: %d successful, %d failed', 'wp-multisite-dashboard'),
|
|
$action,
|
|
count($user_ids),
|
|
$success_count,
|
|
$error_count
|
|
),
|
|
'medium'
|
|
);
|
|
}
|
|
}
|
|
|
|
private function clear_user_caches($user_ids = []) {
|
|
wp_cache_flush_group($this->cache_group);
|
|
|
|
if (!empty($user_ids)) {
|
|
foreach ($user_ids as $user_id) {
|
|
wp_cache_delete("user_activity_{$user_id}", $this->cache_group);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function cleanup_inactive_users($days = 180, $dry_run = true) {
|
|
if (!current_user_can('delete_users')) {
|
|
return ['success' => false, 'message' => __('Insufficient permissions', 'wp-multisite-dashboard')];
|
|
}
|
|
|
|
$inactive_users = $this->get_inactive_users($days, 1000);
|
|
$super_admins = get_super_admins();
|
|
$candidates_for_deletion = [];
|
|
|
|
foreach ($inactive_users as $user) {
|
|
if (!in_array($user['user_login'], $super_admins) && $user['sites_count'] === 0) {
|
|
$candidates_for_deletion[] = $user;
|
|
}
|
|
}
|
|
|
|
if ($dry_run) {
|
|
return [
|
|
'success' => true,
|
|
'message' => sprintf(__('%d users would be deleted', 'wp-multisite-dashboard'), count($candidates_for_deletion)),
|
|
'users' => $candidates_for_deletion
|
|
];
|
|
}
|
|
|
|
$deleted_count = 0;
|
|
foreach ($candidates_for_deletion as $user) {
|
|
if (wpmu_delete_user($user['ID'])) {
|
|
$deleted_count++;
|
|
}
|
|
}
|
|
|
|
$this->clear_user_caches();
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => sprintf(__('%d inactive users deleted', 'wp-multisite-dashboard'), $deleted_count),
|
|
'deleted_count' => $deleted_count
|
|
];
|
|
}
|
|
}
|