wp-multinetwork-switcher/wp-multinetwork-switcher.php

741 lines
21 KiB
PHP
Raw Normal View History

2025-06-16 01:03:01 +08:00
<?php
/**
* Plugin Name: WP Multi-Network Switcher
2025-06-16 01:03:01 +08:00
* Plugin URI: https://wpmultinetwork.com
* Description: Display current network information in admin bar and provide network switching functionality for WP Multi Network environments.
* Version: 1.0.2
2025-06-16 01:03:01 +08:00
* Author: WPMultiNetwork.com
* Author URI: https://wpmultinetwork.com
* Text Domain: wp-multinetwork-switcher
2025-06-16 01:03:01 +08:00
* Domain Path: /languages
* Requires at least: 5.0
* Tested up to: 6.8
* Requires PHP: 7.4
* Network: true
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
*/
if (!defined("ABSPATH")) {
exit();
2025-06-16 01:03:01 +08:00
}
define("WPMN_ADMINBAR_VERSION", "1.0.2");
define("WPMN_ADMINBAR_PLUGIN_DIR", plugin_dir_path(__FILE__));
define("WPMN_ADMINBAR_PLUGIN_URL", plugin_dir_url(__FILE__));
define("WPMN_ADMINBAR_TEXT_DOMAIN", "wp-multinetwork-switcher");
2025-06-16 01:03:01 +08:00
class WP_MultiNetwork_AdminBar
{
2025-06-16 01:03:01 +08:00
private static $instance = null;
private $networks_cache = [];
private $admin_color_scheme = "fresh";
public static function getInstance()
{
2025-06-16 01:03:01 +08:00
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct()
{
add_action("plugins_loaded", [$this, "init"]);
2025-06-16 01:03:01 +08:00
}
public function init()
{
2025-06-16 01:03:01 +08:00
$this->loadTextDomain();
2025-06-16 01:03:01 +08:00
if (!$this->isMultiNetwork()) {
return;
}
$this->detectAdminColorScheme();
add_action("admin_bar_menu", [$this, "addNetworkInfoToAdminBar"], 100);
add_action("admin_enqueue_scripts", [$this, "enqueueAdminAssets"]);
add_action("wp_enqueue_scripts", [$this, "enqueueAdminAssets"]);
add_action("wp_ajax_switch_network", [$this, "handleNetworkSwitch"]);
add_action("wp_ajax_get_network_info", [$this, "getNetworkInfo"]);
add_action("admin_head", [$this, "addAdminHeadStyles"]);
add_action("wp_head", [$this, "addAdminHeadStyles"]);
2025-06-16 01:03:01 +08:00
}
private function loadTextDomain()
{
2025-06-16 01:03:01 +08:00
load_plugin_textdomain(
WPMN_ADMINBAR_TEXT_DOMAIN,
false,
dirname(plugin_basename(__FILE__)) . "/languages"
2025-06-16 01:03:01 +08:00
);
}
private function isMultiNetwork()
{
return is_multisite() && function_exists("get_networks");
2025-06-16 01:03:01 +08:00
}
private function detectAdminColorScheme()
{
$current_user = wp_get_current_user();
if ($current_user) {
$user_color_scheme = get_user_meta(
$current_user->ID,
"admin_color",
true
);
$this->admin_color_scheme = $user_color_scheme
? $user_color_scheme
: "fresh";
}
}
private function getColorSchemeColors()
{
$schemes = [
"fresh" => [
"primary" => "#0073aa",
"secondary" => "#72aee6",
"hover" => "#005a87",
"background" => "rgba(240, 245, 250, 0.1)",
],
"light" => [
"primary" => "#0073aa",
"secondary" => "#72aee6",
"hover" => "#005a87",
"background" => "rgba(240, 245, 250, 0.1)",
],
"blue" => [
"primary" => "#096484",
"secondary" => "#4796b3",
"hover" => "#07526c",
"background" => "rgba(70, 150, 179, 0.1)",
],
"midnight" => [
"primary" => "#e14d43",
"secondary" => "#77a6b9",
"hover" => "#dd382d",
"background" => "rgba(119, 166, 185, 0.1)",
],
"sunrise" => [
"primary" => "#d1864a",
"secondary" => "#c8b03c",
"hover" => "#b77729",
"background" => "rgba(200, 176, 60, 0.1)",
],
"ectoplasm" => [
"primary" => "#a3b745",
"secondary" => "#c8d035",
"hover" => "#8b9a3e",
"background" => "rgba(200, 208, 53, 0.1)",
],
"ocean" => [
"primary" => "#627c83",
"secondary" => "#9ebaa0",
"hover" => "#576e74",
"background" => "rgba(158, 186, 160, 0.1)",
],
"coffee" => [
"primary" => "#c7a589",
"secondary" => "#9ea476",
"hover" => "#b79570",
"background" => "rgba(158, 164, 118, 0.1)",
],
];
return isset($schemes[$this->admin_color_scheme])
? $schemes[$this->admin_color_scheme]
: $schemes["fresh"];
}
public function addNetworkInfoToAdminBar($wp_admin_bar)
{
if (!current_user_can("manage_network")) {
2025-06-16 01:03:01 +08:00
return;
}
2025-06-16 01:03:01 +08:00
$current_network = $this->getCurrentNetworkInfo();
$all_networks = $this->getAllNetworks();
2025-06-16 01:03:01 +08:00
if (empty($current_network) || count($all_networks) <= 1) {
return;
}
2025-06-16 01:03:01 +08:00
$wp_admin_bar->add_menu([
"id" => "wp-multinetwork-switcher",
"title" => $this->getNetworkDisplayTitle($current_network),
"href" => "#",
"meta" => [
"class" => "wp-multinetwork-switcher",
"title" => sprintf(
__("Current Network: %s", WPMN_ADMINBAR_TEXT_DOMAIN),
$current_network["domain"]
),
],
2025-06-16 01:03:01 +08:00
]);
2025-06-16 01:03:01 +08:00
foreach ($all_networks as $network) {
$is_current = $network->id == $current_network["id"];
2025-06-16 01:03:01 +08:00
$site_count = $this->getNetworkSitesCount($network->id);
2025-06-16 01:03:01 +08:00
$wp_admin_bar->add_menu([
"parent" => "wp-multinetwork-switcher",
"id" => "network-" . $network->id,
"title" => $this->getNetworkMenuTitle(
$network,
$is_current,
$site_count
),
"href" => $is_current
? "#"
: $this->getNetworkSwitchUrl($network),
"meta" => [
"class" => $is_current
? "current-network"
: "switch-network",
"data-network-id" => $network->id,
"data-nonce" => wp_create_nonce("network_switch_nonce"),
"title" => sprintf(
__(
"Network: %s%s (%d sites)",
WPMN_ADMINBAR_TEXT_DOMAIN
),
2025-06-16 01:03:01 +08:00
$network->domain,
$network->path,
$site_count
),
],
2025-06-16 01:03:01 +08:00
]);
}
if (current_user_can("manage_network")) {
2025-06-16 01:03:01 +08:00
$wp_admin_bar->add_menu([
"parent" => "wp-multinetwork-switcher",
"id" => "network-admin-all",
"title" => $this->getNetworkAdminLinkTitle(),
"href" => network_admin_url(),
"meta" => [
"class" => "network-admin-link",
],
2025-06-16 01:03:01 +08:00
]);
}
}
private function getCurrentNetworkInfo()
{
2025-06-16 01:03:01 +08:00
$current_network = get_network();
2025-06-16 01:03:01 +08:00
if (!$current_network) {
return null;
}
2025-06-16 01:03:01 +08:00
return [
"id" => $current_network->id,
"domain" => $current_network->domain,
"path" => $current_network->path,
"site_name" => get_network_option(
$current_network->id,
"site_name",
$current_network->domain
),
2025-06-16 01:03:01 +08:00
];
}
private function getAllNetworks()
{
if (!empty($this->networks_cache)) {
return $this->networks_cache;
}
if (!function_exists("get_networks")) {
2025-06-16 01:03:01 +08:00
return [];
}
$this->networks_cache = get_networks([
"number" => 100,
"orderby" => "domain",
2025-06-16 01:03:01 +08:00
]);
return $this->networks_cache;
2025-06-16 01:03:01 +08:00
}
private function getNetworkSitesCount($network_id)
{
$cache_key = "wpmn_network_sites_count_" . $network_id;
2025-06-16 01:03:01 +08:00
$count = wp_cache_get($cache_key);
2025-06-16 01:03:01 +08:00
if (false === $count) {
$sites = get_sites([
"network_id" => $network_id,
"count" => true,
"number" => 1000,
2025-06-16 01:03:01 +08:00
]);
$count = is_numeric($sites) ? intval($sites) : count($sites);
wp_cache_set($cache_key, $count, "", 300);
2025-06-16 01:03:01 +08:00
}
2025-06-16 01:03:01 +08:00
return $count;
}
private function getNetworkDisplayTitle($network_info)
{
$site_name = !empty($network_info["site_name"])
? $network_info["site_name"]
: $network_info["domain"];
$current_site_count = $this->getNetworkSitesCount($network_info["id"]);
2025-06-16 01:03:01 +08:00
return sprintf(
"%s %s %s",
$this->getDashicon("admin-site-alt3", "network-indicator"),
sprintf(
'<span class="network-name">%s</span>',
esc_html($site_name)
),
sprintf(
'<span class="network-count">(%d)</span>',
$current_site_count
)
2025-06-16 01:03:01 +08:00
);
}
private function getNetworkMenuTitle($network, $is_current, $site_count)
{
$site_name = get_network_option(
$network->id,
"site_name",
$network->domain
);
$indicator = $is_current ? $this->getDashicon("yes") . " " : "";
2025-06-16 01:03:01 +08:00
return sprintf(
'%s<strong>%s</strong> <span class="site-count">(%s)</span>',
2025-06-16 01:03:01 +08:00
$indicator,
esc_html($site_name),
sprintf(
_n(
"%d site",
"%d sites",
$site_count,
WPMN_ADMINBAR_TEXT_DOMAIN
),
$site_count
)
);
}
private function getNetworkAdminLinkTitle()
{
return sprintf(
"%s %s",
$this->getDashicon("admin-tools"),
__("Manage All Networks", WPMN_ADMINBAR_TEXT_DOMAIN)
2025-06-16 01:03:01 +08:00
);
}
private function getDashicon($icon, $class = "")
{
$class_attr = $class ? sprintf(' class="%s"', esc_attr($class)) : "";
return sprintf(
'<span%s data-dashicon="%s"></span>',
$class_attr,
esc_attr($icon)
);
}
private function getNetworkSwitchUrl($network)
{
$protocol = is_ssl() ? "https://" : "http://";
2025-06-16 01:03:01 +08:00
$url = $protocol . $network->domain . $network->path;
2025-06-16 01:03:01 +08:00
if (is_admin()) {
$url .= "wp-admin/";
2025-06-16 01:03:01 +08:00
}
2025-06-16 01:03:01 +08:00
return $url;
}
public function handleNetworkSwitch()
{
if (!check_ajax_referer("network_switch_nonce", "nonce", false)) {
wp_send_json_error(__("Invalid nonce.", WPMN_ADMINBAR_TEXT_DOMAIN));
}
if (!current_user_can("manage_network")) {
wp_send_json_error(
__("Insufficient permissions.", WPMN_ADMINBAR_TEXT_DOMAIN)
);
}
$network_id = intval($_POST["network_id"]);
2025-06-16 01:03:01 +08:00
$network = get_network($network_id);
2025-06-16 01:03:01 +08:00
if (!$network) {
wp_send_json_error(
__("Network does not exist.", WPMN_ADMINBAR_TEXT_DOMAIN)
);
2025-06-16 01:03:01 +08:00
}
if (!$this->userCanAccessNetwork(get_current_user_id(), $network_id)) {
wp_send_json_error(
__("Access denied to this network.", WPMN_ADMINBAR_TEXT_DOMAIN)
);
}
2025-06-16 01:03:01 +08:00
$switch_url = $this->getNetworkSwitchUrl($network);
wp_send_json_success(["redirect_url" => $switch_url]);
}
public function getNetworkInfo()
{
if (!check_ajax_referer("network_switch_nonce", "nonce", false)) {
wp_send_json_error(__("Invalid nonce.", WPMN_ADMINBAR_TEXT_DOMAIN));
}
if (!current_user_can("manage_network")) {
wp_send_json_error(
__("Insufficient permissions.", WPMN_ADMINBAR_TEXT_DOMAIN)
);
}
$network_id = intval($_POST["network_id"]);
$network = get_network($network_id);
if (!$network) {
wp_send_json_error(
__("Network does not exist.", WPMN_ADMINBAR_TEXT_DOMAIN)
);
}
$site_count = $this->getNetworkSitesCount($network_id);
$site_name = get_network_option(
$network_id,
"site_name",
$network->domain
);
wp_send_json_success([
"id" => $network->id,
"domain" => $network->domain,
"path" => $network->path,
"site_name" => $site_name,
"site_count" => $site_count,
]);
2025-06-16 01:03:01 +08:00
}
private function userCanAccessNetwork($user_id, $network_id)
{
if (is_super_admin($user_id)) {
return true;
}
$sites = get_sites([
"network_id" => $network_id,
"number" => 100,
]);
foreach ($sites as $site) {
if (is_user_member_of_blog($user_id, $site->blog_id)) {
return true;
}
}
return false;
}
public function enqueueAdminAssets()
{
if (!is_admin_bar_showing() || !current_user_can("manage_network")) {
2025-06-16 01:03:01 +08:00
return;
}
wp_enqueue_script("jquery");
wp_enqueue_style("dashicons");
wp_add_inline_script("jquery", $this->getInlineJS());
}
public function addAdminHeadStyles()
{
if (!is_admin_bar_showing() || !current_user_can("manage_network")) {
return;
}
echo '<style type="text/css">' . $this->getInlineCSS() . "</style>";
2025-06-16 01:03:01 +08:00
}
private function getInlineCSS()
{
$colors = $this->getColorSchemeColors();
return sprintf(
'
2025-06-16 01:03:01 +08:00
#wpadminbar .wp-multinetwork-switcher .ab-item {
padding: 0 10px !important;
}
#wpadminbar .network-indicator[data-dashicon]:before {
content: "\\f319";
font-family: dashicons;
2025-06-16 01:03:01 +08:00
font-size: 16px;
margin-right: 5px;
vertical-align: middle;
}
#wpadminbar [data-dashicon="yes"]:before {
content: "\\f147";
font-family: dashicons;
color: %s;
margin-right: 5px;
}
#wpadminbar [data-dashicon="admin-tools"]:before {
content: "\\f348";
font-family: dashicons;
margin-right: 5px;
2025-06-16 01:03:01 +08:00
}
2025-06-16 01:03:01 +08:00
#wpadminbar .network-name {
font-weight: 600;
}
2025-06-16 01:03:01 +08:00
#wpadminbar .network-count {
font-size: 12px;
opacity: 0.8;
margin-left: 5px;
}
2025-06-16 01:03:01 +08:00
#wpadminbar .wp-multinetwork-switcher .ab-submenu {
min-width: 300px;
}
2025-06-16 01:03:01 +08:00
#wpadminbar .wp-multinetwork-switcher .ab-submenu .ab-item {
line-height: 1.3;
padding: 8px 12px !important;
white-space: normal;
}
2025-06-16 01:03:01 +08:00
#wpadminbar .current-network .ab-item {
background-color: %s !important;
color: %s !important;
2025-06-16 01:03:01 +08:00
}
2025-06-16 01:03:01 +08:00
#wpadminbar .switch-network:hover .ab-item {
background-color: %s !important;
2025-06-16 01:03:01 +08:00
color: #fff !important;
}
2025-06-16 01:03:01 +08:00
#wpadminbar .site-count {
font-size: 11px;
opacity: 0.8;
font-weight: normal;
}
2025-06-16 01:03:01 +08:00
#wpadminbar .network-admin-link {
border-top: 1px solid rgba(255,255,255,0.2);
margin-top: 5px;
padding-top: 5px;
}
2025-06-16 01:03:01 +08:00
#wpadminbar .network-admin-link .ab-item {
color: %s !important;
2025-06-16 01:03:01 +08:00
font-weight: 600;
}
#wpadminbar .switching-indicator {
font-size: 11px;
opacity: 0.7;
font-style: italic;
}
2025-06-16 01:03:01 +08:00
@media screen and (max-width: 782px) {
#wpadminbar .wp-multinetwork-switcher .ab-submenu {
min-width: 280px;
left: -120px;
}
}
',
$colors["secondary"],
$colors["background"],
$colors["secondary"],
$colors["hover"],
$colors["secondary"]
);
2025-06-16 01:03:01 +08:00
}
private function getInlineJS()
{
$ajax_url = admin_url("admin-ajax.php");
$switching_text = __("Switching...", WPMN_ADMINBAR_TEXT_DOMAIN);
$error_text = __(
"Error switching network. Please try again.",
WPMN_ADMINBAR_TEXT_DOMAIN
);
return sprintf(
"
2025-06-16 01:03:01 +08:00
jQuery(document).ready(function($) {
var switchingInProgress = false;
2025-06-16 01:03:01 +08:00
$('.switch-network a').on('click', function(e) {
e.preventDefault();
if (switchingInProgress) {
return false;
2025-06-16 01:03:01 +08:00
}
var \$link = $(this);
var \$item = \$link.closest('.switch-network');
var networkId = \$item.data('network-id');
var nonce = \$item.data('nonce');
var originalHref = \$link.attr('href');
if (!networkId || !nonce) {
window.location.href = originalHref;
return;
}
switchingInProgress = true;
\$link.append(' <span class=\"switching-indicator\">(%s)</span>');
$.ajax({
url: '%s',
type: 'POST',
data: {
action: 'switch_network',
network_id: networkId,
nonce: nonce
},
success: function(response) {
if (response.success && response.data.redirect_url) {
window.location.href = response.data.redirect_url;
} else {
alert(response.data || '%s');
switchingInProgress = false;
\$link.find('.switching-indicator').remove();
}
},
error: function() {
alert('%s');
switchingInProgress = false;
\$link.find('.switching-indicator').remove();
}
});
2025-06-16 01:03:01 +08:00
});
2025-06-16 01:03:01 +08:00
$(document).keydown(function(e) {
if (e.ctrlKey && e.shiftKey && e.keyCode === 78) {
e.preventDefault();
var \$switcher = $('#wp-multinetwork-switcher');
if (\$switcher.length) {
\$switcher.trigger('click');
}
2025-06-16 01:03:01 +08:00
}
});
2025-06-16 01:03:01 +08:00
$('#wp-multinetwork-switcher').attr({
'role': 'menubar',
'aria-label': '%s'
2025-06-16 01:03:01 +08:00
});
2025-06-16 01:03:01 +08:00
$('#wp-multinetwork-switcher .ab-submenu').attr('role', 'menu');
$('#wp-multinetwork-switcher .ab-submenu li').attr('role', 'menuitem');
});
",
$switching_text,
$ajax_url,
$error_text,
$error_text,
esc_js(__("Network Switcher", WPMN_ADMINBAR_TEXT_DOMAIN))
);
2025-06-16 01:03:01 +08:00
}
}
WP_MultiNetwork_AdminBar::getInstance();
register_activation_hook(__FILE__, function () {
2025-06-16 01:03:01 +08:00
if (!is_multisite()) {
deactivate_plugins(plugin_basename(__FILE__));
wp_die(
__(
"This plugin requires WordPress Multisite to be enabled.",
WPMN_ADMINBAR_TEXT_DOMAIN
),
__("Plugin Activation Error", WPMN_ADMINBAR_TEXT_DOMAIN),
["back_link" => true]
2025-06-16 01:03:01 +08:00
);
}
2025-06-16 01:03:01 +08:00
global $wp_version;
if (version_compare($wp_version, "5.0", "<")) {
2025-06-16 01:03:01 +08:00
deactivate_plugins(plugin_basename(__FILE__));
wp_die(
__(
"This plugin requires WordPress 5.0 or higher.",
WPMN_ADMINBAR_TEXT_DOMAIN
),
__("Plugin Activation Error", WPMN_ADMINBAR_TEXT_DOMAIN),
["back_link" => true]
2025-06-16 01:03:01 +08:00
);
}
});
register_deactivation_hook(__FILE__, function () {
2025-06-16 01:03:01 +08:00
wp_cache_flush();
});
function wpmn_get_network_sites_count($network_id)
{
if (!is_multisite() || !function_exists("get_sites")) {
2025-06-16 01:03:01 +08:00
return 0;
}
2025-06-16 01:03:01 +08:00
$sites = get_sites([
"network_id" => $network_id,
"count" => true,
"number" => 1000,
2025-06-16 01:03:01 +08:00
]);
2025-06-16 01:03:01 +08:00
return is_numeric($sites) ? intval($sites) : count($sites);
}
function wpmn_user_can_access_network($user_id, $network_id)
{
2025-06-16 01:03:01 +08:00
$user = get_userdata($user_id);
if (!$user) {
return false;
}
2025-06-16 01:03:01 +08:00
if (is_super_admin($user_id)) {
return true;
}
2025-06-16 01:03:01 +08:00
$sites = get_sites([
"network_id" => $network_id,
"number" => 100,
2025-06-16 01:03:01 +08:00
]);
2025-06-16 01:03:01 +08:00
foreach ($sites as $site) {
if (is_user_member_of_blog($user_id, $site->blog_id)) {
return true;
}
}
2025-06-16 01:03:01 +08:00
return false;
}
function wpmn_get_network_display_name($network_id)
{
2025-06-16 01:03:01 +08:00
$network = get_network($network_id);
if (!$network) {
return "";
2025-06-16 01:03:01 +08:00
}
$site_name = get_network_option($network_id, "site_name");
2025-06-16 01:03:01 +08:00
return !empty($site_name) ? $site_name : $network->domain;
}