diff --git a/wp-multinetwork-switcher.php b/wp-multinetwork-switcher.php index e0a796e..2cd95cd 100644 --- a/wp-multinetwork-switcher.php +++ b/wp-multinetwork-switcher.php @@ -3,7 +3,7 @@ * Plugin Name: WP Multi-Network Switcher * 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 + * Version: 1.0.3 * Author: WPMultiNetwork.com * Author URI: https://wpmultinetwork.com * Text Domain: wp-multinetwork-switcher @@ -20,16 +20,14 @@ if (!defined("ABSPATH")) { exit(); } -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"); +define("WPMN_SWITCHER_VERSION", "1.0.3"); +define("WPMN_SWITCHER_PLUGIN_DIR", plugin_dir_path(__FILE__)); +define("WPMN_SWITCHER_PLUGIN_URL", plugin_dir_url(__FILE__)); -class WP_MultiNetwork_AdminBar +class WP_MultiNetwork_Switcher { private static $instance = null; private $networks_cache = []; - private $admin_color_scheme = "fresh"; public static function getInstance() { @@ -52,8 +50,6 @@ class WP_MultiNetwork_AdminBar 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"]); @@ -61,12 +57,16 @@ class WP_MultiNetwork_AdminBar add_action("wp_ajax_get_network_info", [$this, "getNetworkInfo"]); add_action("admin_head", [$this, "addAdminHeadStyles"]); add_action("wp_head", [$this, "addAdminHeadStyles"]); + add_action("wp_dashboard_setup", [$this, "addDashboardWidget"]); + add_action("wp_network_dashboard_setup", [$this, "addDashboardWidget"]); + add_action("admin_enqueue_scripts", [$this, "enqueueDashboardAssets"]); + add_action("admin_print_styles", [$this, "printDashboardStyles"]); } private function loadTextDomain() { load_plugin_textdomain( - WPMN_ADMINBAR_TEXT_DOMAIN, + "wp-multinetwork-switcher", false, dirname(plugin_basename(__FILE__)) . "/languages" ); @@ -77,79 +77,6 @@ class WP_MultiNetwork_AdminBar return is_multisite() && function_exists("get_networks"); } - 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")) { @@ -170,7 +97,7 @@ class WP_MultiNetwork_AdminBar "meta" => [ "class" => "wp-multinetwork-switcher", "title" => sprintf( - __("Current Network: %s", WPMN_ADMINBAR_TEXT_DOMAIN), + __("Current Network: %s", "wp-multinetwork-switcher"), $current_network["domain"] ), ], @@ -200,7 +127,7 @@ class WP_MultiNetwork_AdminBar "title" => sprintf( __( "Network: %s%s (%d sites)", - WPMN_ADMINBAR_TEXT_DOMAIN + "wp-multinetwork-switcher" ), $network->domain, $network->path, @@ -223,6 +150,428 @@ class WP_MultiNetwork_AdminBar } } + public function addDashboardWidget() + { + if (!current_user_can("manage_network")) { + return; + } + + wp_add_dashboard_widget( + "wpmn_network_dashboard", + __("Multi Network Overview", "wp-multinetwork-switcher"), + [$this, "renderDashboardWidget"] + ); + } + + public function renderDashboardWidget() + { + $all_networks = $this->getAllNetworks(); + $current_network = $this->getCurrentNetworkInfo(); + $total_sites = 0; + + foreach ($all_networks as $network) { + $total_sites += $this->getNetworkSitesCount($network->id); + } + + $super_admin_count = $this->getSuperAdminCount(); + + echo '
"; + } + + public function printDashboardStyles() + { + $screen = get_current_screen(); + if (!$screen || !current_user_can("manage_network")) { + return; + } + + if ( + $screen->id === "dashboard" || + $screen->id === "dashboard-network" + ) { + echo '"; + } + } + + private function getSuperAdminCount() + { + $cache_key = "wpmn_super_admin_count"; + $count = wp_cache_get($cache_key); + + if (false === $count) { + $super_admins = get_super_admins(); + $count = count($super_admins); + wp_cache_set($cache_key, $count, "", 300); + } + + return $count; + } + + private function getNetworkFavicon($network_id) + { + $cache_key = "wpmn_network_favicon_" . $network_id; + $favicon_url = wp_cache_get($cache_key); + + if (false === $favicon_url) { + $main_site_id = get_main_site_id($network_id); + + if ($main_site_id) { + switch_to_blog($main_site_id); + $favicon_url = get_site_icon_url(32); + + if (!$favicon_url) { + $favicon_url = + get_template_directory_uri() . "/favicon.ico"; + if (!@file_get_contents($favicon_url)) { + $favicon_url = admin_url("images/wordpress-logo.svg"); + } + } + + restore_current_blog(); + } else { + $favicon_url = admin_url("images/wordpress-logo.svg"); + } + + wp_cache_set($cache_key, $favicon_url, "", 300); + } + + return $favicon_url; + } + + public function enqueueDashboardAssets() + { + $screen = get_current_screen(); + if (!$screen || !current_user_can("manage_network")) { + return; + } + + if ( + $screen->id === "dashboard" || + $screen->id === "dashboard-network" + ) { + wp_enqueue_script("jquery"); + wp_add_inline_script("jquery", $this->getDashboardJS()); + } + } + + private function getDashboardCSS() + { + return " + .wpmn-dashboard-widget { + padding: 16px; + } + + .wpmn-stats-row { + display: flex; + margin-bottom: 20px; + gap: 15px; + } + + .wpmn-stat-item { + flex: 1; + text-align: center; + padding: 12px; + background: #f8f9fa; + border-radius: 4px; + } + + .wpmn-stat-number { + font-size: 20px; + font-weight: 600; + line-height: 1.2; + } + + .wpmn-stat-label { + font-size: 12px; + color: #666; + margin-top: 5px; + } + + .wpmn-current-network { + background: #f8f9fa; + padding: 15px; + border-radius: 4px; + margin-bottom: 20px; + } + + .wpmn-current-network h4 { + margin: 0 0 10px 0; + color: #0073aa; + } + + .wpmn-current-network-info { + display: flex; + align-items: center; + gap: 12px; + } + + .wpmn-network-favicon { + width: 45px; + height: 45px; + border-radius: 4px; + object-fit: cover; + flex-shrink: 0; + border: 1px solid #c3c4c7; + background: #ffffff; + padding: 2%; + } + + .wpmn-current-network-details p { + margin: 5px 0; + } + + .wpmn-network-list h4 { + margin: 0 0 15px 0; + color: #333; + } + + .wpmn-network-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 10px; + margin-bottom: 20px; + } + + .wpmn-network-item { + border: 1px solid #ddd; + border-radius: 4px; + padding: 12px; + background: #fff; + transition: all 0.2s ease; + } + + .wpmn-network-item.current { + border-color: #0073aa; + background: rgba(240, 245, 250, 0.1); + } + + .wpmn-network-item a { + text-decoration: none; + color: inherit; + display: block; + } + + .wpmn-network-item:not(.current):hover { + border-color: #72aee6; + background: #f8f9fa; + } + + .wpmn-network-name { + font-weight: 400; + margin-bottom: 5px; + color: #333; + } + + .wpmn-network-info { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 12px; + color: #666; + } + + .wpmn-network-domain { + font-family: monospace; + } + + .wpmn-network-count { + padding: 2px 6px; + border-radius: 3px; + font-size: 9px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; + background: #e8f5e8; + color: #2e7d32; + } + + .wpmn-network-more { + display: flex; + align-items: center; + justify-content: center; + border: 1px dashed #ddd; + border-radius: 4px; + padding: 12px; + color: #666; + font-size: 12px; + font-style: italic; + background: #fafafa; + } + + .wpmn-actions { + text-align: center; + padding-top: 15px; + border-top: 1px solid #eee; + } + + @media (max-width: 600px) { + .wpmn-stats-row { + flex-direction: column; + gap: 10px; + } + + .wpmn-network-grid { + grid-template-columns: 1fr; + } + + .wpmn-current-network-info { + flex-direction: column; + align-items: flex-start; + gap: 8px; + } + } + "; + } + + private function getDashboardJS() + { + return " + jQuery(document).ready(function($) { + $('.wpmn-switch-link').on('click', function(e) { + e.preventDefault(); + var link = $(this); + var networkId = link.data('network-id'); + + if (confirm('" . + esc_js(__("Switch to this network?", "wp-multinetwork-switcher")) . + "')) { + window.location.href = link.attr('href'); + } + }); + }); + "; + } + private function getCurrentNetworkInfo() { $current_network = get_network(); @@ -313,7 +662,7 @@ class WP_MultiNetwork_AdminBar $indicator = $is_current ? $this->getDashicon("yes") . " " : ""; return sprintf( - '%s%s (%s)', + '%s %s (%s)', $indicator, esc_html($site_name), sprintf( @@ -321,7 +670,7 @@ class WP_MultiNetwork_AdminBar "%d site", "%d sites", $site_count, - WPMN_ADMINBAR_TEXT_DOMAIN + "wp-multinetwork-switcher" ), $site_count ) @@ -333,7 +682,7 @@ class WP_MultiNetwork_AdminBar return sprintf( "%s %s", $this->getDashicon("admin-tools"), - __("Manage All Networks", WPMN_ADMINBAR_TEXT_DOMAIN) + __("Manage All Networks", "wp-multinetwork-switcher") ); } @@ -362,12 +711,14 @@ class WP_MultiNetwork_AdminBar public function handleNetworkSwitch() { if (!check_ajax_referer("network_switch_nonce", "nonce", false)) { - wp_send_json_error(__("Invalid nonce.", WPMN_ADMINBAR_TEXT_DOMAIN)); + wp_send_json_error( + __("Invalid nonce.", "wp-multinetwork-switcher") + ); } if (!current_user_can("manage_network")) { wp_send_json_error( - __("Insufficient permissions.", WPMN_ADMINBAR_TEXT_DOMAIN) + __("Insufficient permissions.", "wp-multinetwork-switcher") ); } @@ -376,13 +727,13 @@ class WP_MultiNetwork_AdminBar if (!$network) { wp_send_json_error( - __("Network does not exist.", WPMN_ADMINBAR_TEXT_DOMAIN) + __("Network does not exist.", "wp-multinetwork-switcher") ); } if (!$this->userCanAccessNetwork(get_current_user_id(), $network_id)) { wp_send_json_error( - __("Access denied to this network.", WPMN_ADMINBAR_TEXT_DOMAIN) + __("Access denied to this network.", "wp-multinetwork-switcher") ); } @@ -393,12 +744,14 @@ class WP_MultiNetwork_AdminBar public function getNetworkInfo() { if (!check_ajax_referer("network_switch_nonce", "nonce", false)) { - wp_send_json_error(__("Invalid nonce.", WPMN_ADMINBAR_TEXT_DOMAIN)); + wp_send_json_error( + __("Invalid nonce.", "wp-multinetwork-switcher") + ); } if (!current_user_can("manage_network")) { wp_send_json_error( - __("Insufficient permissions.", WPMN_ADMINBAR_TEXT_DOMAIN) + __("Insufficient permissions.", "wp-multinetwork-switcher") ); } @@ -407,7 +760,7 @@ class WP_MultiNetwork_AdminBar if (!$network) { wp_send_json_error( - __("Network does not exist.", WPMN_ADMINBAR_TEXT_DOMAIN) + __("Network does not exist.", "wp-multinetwork-switcher") ); } @@ -470,26 +823,29 @@ class WP_MultiNetwork_AdminBar private function getInlineCSS() { - $colors = $this->getColorSchemeColors(); - - return sprintf( - ' + return ' #wpadminbar .wp-multinetwork-switcher .ab-item { padding: 0 10px !important; } #wpadminbar .network-indicator[data-dashicon]:before { - content: "\\f319"; - font-family: dashicons; - font-size: 16px; - margin-right: 5px; - vertical-align: middle; + content: "\f230"; + position: relative; + float: left; + font: normal 20px / 1 dashicons; + speak: never; + padding: 6px 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-image: none !important; + margin-right: 6px; + color: #9ca2a7; } #wpadminbar [data-dashicon="yes"]:before { content: "\\f147"; font-family: dashicons; - color: %s; + color: #72aee6; margin-right: 5px; } @@ -500,7 +856,7 @@ class WP_MultiNetwork_AdminBar } #wpadminbar .network-name { - font-weight: 600; + font-weight: 400; } #wpadminbar .network-count { @@ -515,17 +871,20 @@ class WP_MultiNetwork_AdminBar #wpadminbar .wp-multinetwork-switcher .ab-submenu .ab-item { line-height: 1.3; - padding: 8px 12px !important; + padding: 5px 12px !important; white-space: normal; + display: flex; + align-items: center; + gap: 12px; } #wpadminbar .current-network .ab-item { - background-color: %s !important; - color: %s !important; + background-color: rgba(240, 245, 250, 0.1) !important; + color: #72aee6 !important; } #wpadminbar .switch-network:hover .ab-item { - background-color: %s !important; + background-color: #1d2327 !important; color: #fff !important; } @@ -542,8 +901,8 @@ class WP_MultiNetwork_AdminBar } #wpadminbar .network-admin-link .ab-item { - color: %s !important; - font-weight: 600; + color: #72aee6 !important; + font-weight: 400; } #wpadminbar .switching-indicator { @@ -558,22 +917,16 @@ class WP_MultiNetwork_AdminBar left: -120px; } } - ', - $colors["secondary"], - $colors["background"], - $colors["secondary"], - $colors["hover"], - $colors["secondary"] - ); + '; } private function getInlineJS() { $ajax_url = admin_url("admin-ajax.php"); - $switching_text = __("Switching...", WPMN_ADMINBAR_TEXT_DOMAIN); + $switching_text = __("Switching...", "wp-multinetwork-switcher"); $error_text = __( "Error switching network. Please try again.", - WPMN_ADMINBAR_TEXT_DOMAIN + "wp-multinetwork-switcher" ); return sprintf( @@ -650,12 +1003,12 @@ class WP_MultiNetwork_AdminBar $ajax_url, $error_text, $error_text, - esc_js(__("Network Switcher", WPMN_ADMINBAR_TEXT_DOMAIN)) + esc_js(__("Network Switcher", "wp-multinetwork-switcher")) ); } } -WP_MultiNetwork_AdminBar::getInstance(); +WP_MultiNetwork_Switcher::getInstance(); register_activation_hook(__FILE__, function () { if (!is_multisite()) { @@ -663,9 +1016,9 @@ register_activation_hook(__FILE__, function () { wp_die( __( "This plugin requires WordPress Multisite to be enabled.", - WPMN_ADMINBAR_TEXT_DOMAIN + "wp-multinetwork-switcher" ), - __("Plugin Activation Error", WPMN_ADMINBAR_TEXT_DOMAIN), + __("Plugin Activation Error", "wp-multinetwork-switcher"), ["back_link" => true] ); } @@ -676,9 +1029,9 @@ register_activation_hook(__FILE__, function () { wp_die( __( "This plugin requires WordPress 5.0 or higher.", - WPMN_ADMINBAR_TEXT_DOMAIN + "wp-multinetwork-switcher" ), - __("Plugin Activation Error", WPMN_ADMINBAR_TEXT_DOMAIN), + __("Plugin Activation Error", "wp-multinetwork-switcher"), ["back_link" => true] ); }