File: /www/wwwroot/www.cytocare.cn/wp-content/plugins/wp-safe-mode/wp-safe-mode-loader.php
<?php
/*
 * This class is the main loader class for WP Safe Mode, and handles all the enabling/disabling of safe mode.
 * It can be used automatically with the counterpart WP Safe Mode plugin, or independently for inaccessible sites.
 * To use it independently, move it to the mu-plugins folder (preferably an empty mu-plugins folder) or refer to our installation instructions.
 */
class WP_Safe_Mode {
	
	// START Editable Options: The following options can be edited here if you're having to recover your site by uploading this loader directly.
	
	// These two properties are the 'master switch' and can help you access an inaccessible site in safe mode.
	
	/**
	 * If turned on, safe mode will be enabled for all visitors.
	 * @var boolean
	 */
	public static $safe_mode_on = false;
	
	/**
	 * If set to true, settings from the database (via the WP Safe Mode Plugin admin area) will be totally ignored.
	 * Useful if you want to load Safe Mode independently from admin but also ignored previously stored settings that may be breaking the site.
	 * @var array
	 */
	public static $hardcoded_override = false;
	
	// The following properties are for enabling specific safe mode types.
	
	/**
	 * If turned on, safe mode will be enabled for the whole site.
	 * This setting is equivalent to WP_Safe_Mode::$safe_mode_on and is mainly useful in MultiSite environments whilst loding via init()
	 * @var boolean
	 */
	public static $site_safe_mode_on = false;
	
	/**
	 * If turned on, safe mode will be enabled for the whole network when in MultiSite.
	 * This setting is equivalent to WP_Safe_Mode::$safe_mode_on and is mainly useful in MultiSite environments whilst loding via init()
	 * @var boolean
	 */
	public static $network_safe_mode_on = false;
	
	/**
	 * Array of IPs where the plugins will be temporarily disabled. Change the value or add your own to the array below to activate safe mode when you visit the site.
	 * This is useful if you're completely blocked out of your site and you upload this file directly to mu-plugins.
	 * Only applicable if WP_Safe_Mode::$site_safe_mode_on is set to true.
	 * @var array
	 */
	public static $site_ip_array = array();
	
	/**
	 * Same as $site_ip_array, but this array of IPs will apply in Network Safe Mode.
	 * Only applicable if WP_Safe_Mode::$network_safe_mode_on is set to true.
	 * @var array
	 */
	public static $network_ip_array = array();
	
	// The following options will allow you to disable/enable plugins and switch themes whilst the safe modes above have been enabled. Edit accordingly.
	
	/**
	 * Whether to disable the theme and use the default WP theme.
	 * @var boolean
	 */
	public static $disable_themes = true;
	
	/**
	 * Array of themes that'll be allowed as a default theme.
	 * @var array
	 */
	public static $default_themes = array('twentyseventeen', 'twentysixteen', 'twentyfifteen', 'twentyfourteen', 'twentythirteen', 'twentytwelve');
	
	/**
	 * Whether to disable plugins and only enabled allowed plugins in WP_Safe_Mode::$plugins_to_keep
	 * @var boolean
	 */
	public static $disable_plugins = true;
	
	/**
	 * Array of plugins to keep in format of 'plugin-folder/plugin-file' per array item.
	 * Edit this to your liking and it'll be added to any settings currently stored in db.
	 * @var array
	 */
	public static $plugins_to_keep = array();
	/**
	 * Array of plugins to force-enable, so if they're disabled when safe mode is off, then they'll be enabled in safe mode
	 * @var array
	 */
	public static $plugins_to_enable = array( 'wp-safe-mode/wp-safe-mode.php' );
	
	/**
	 * In a MultiSite environment, when in Site Safe Mode, any plugins in this array will be disabled if network enabled.
	 * @var array
	 */
	public static $network_plugins_to_disable = array();
	
	/**
	 * Whether or not to attempt to load MU plugins from the WP_Safe_Mode::$mu_plugins_dir (mu-plugins) directory, if this file is not located in the mu-plugins folder already.
	 * @var array
	 */
	public static $load_mu_plugins = false;
	
	// END Editable Options: These next options are set by the plugin during loading time and editing will have no beneficial effect.
	
	/**
	 * If turned on and user is logged in, safe mode will be enabled.
	 * @var boolean
	 */
	public static $user_safe_mode_on = false;
	
	/**
	 * Same as $user_safe_mode_on but used for multisite environments where safe mode is enabled for a specific user on a specific site.
	 * In non-multisite environments this will be set to the same value as $user_safe_mode_on
	 * @var boolean
	 */
	public static $user_site_safe_mode_on = false;
	
	/**
	 * Salt used to verify user login and whether safe mode is turned on for the viewing user.
	 * @var string
	 */
	public static $user_safe_mode_salt;
	
	/**
	 * Will be set to true during the init process if the current user is not being put into network safe mode due to IP restrictions
	 * @var boolean
	 */
	private static $network_ip_restricted = false;
	
	/**
	 * Will be set to true during the init process if the current user is not being put into site safe mode due to IP restrictions
	 * @var boolean
	 */
	private static $site_ip_restricted = false;
	
	/**
	 * Name of the original mu-plugins foloder on this site. In 99.9% of cases, this should remain null and will become /path/to/wp-content/mu-plugins in init().
	 * However, if a site is already using a custom mu-plugins folder by defining WPMU_PLUGINS_DIR then this should be modified to match that directory name.
	 * @var string
	 */
	public static $mu_plugins_dir;
	
	/**
	 * If enabled in MultiSite, network admins can enable Site Safe Mode specifically.
	 * This enables user site safe mode (i.e. user safe mode for a speific site only), and also allows for
	 * the settings page in the dashboard if the WP Safe Mode plugin is also active/installed.
	 * @var boolean
	 */
	public static $multisite_single_site = false;
	
	/**
	 * Same as $multisite_single_site, but also allows site admins to enable safe mode on their own site.
	 * @var boolean
	 */
	public static $multisite_single_site_admins = false;
	
	/**
	 * Decide whether to run this script and add actions to initiate safe mode. 
	 */
	public static function init(){
		//check if user is logged in and turned safe mode on specifically for them, since this may affect settings loading
		self::$user_safe_mode_salt = sha1( $_SERVER['REMOTE_ADDR'] . NONCE_SALT );
		self::$user_safe_mode_on = !empty($_COOKIE['safe_mode']) && self::$user_safe_mode_salt == $_COOKIE['safe_mode'];
		//check if safe mode is being requested to be turned on/off
		if( !empty($_REQUEST['safe_mode_toggle_user']) && $_REQUEST['safe_mode_toggle_user'] == self::$user_safe_mode_salt ){
			if( self::$user_safe_mode_on ){
				self::$user_safe_mode_on = false;
				setcookie('safe_mode', 0, time() - DAY_IN_SECONDS, '/', null, is_ssl());
			}else{
				self::$user_safe_mode_on = true;
				setcookie('safe_mode', self::$user_safe_mode_salt, time() + DAY_IN_SECONDS, '/', null, is_ssl());
			}
			WP_Safe_Mode::redirect();
		}
		//check if safe mode is being requested to be turned on/off in MS mode
		if( is_multisite() && !is_network_admin() ){
			$blog_id = get_current_blog_id();
			$blog_salt = sha1(self::$user_safe_mode_salt . $blog_id);
			self::$user_site_safe_mode_on = !empty($_COOKIE['safe_mode_'.$blog_id]) && $blog_salt == $_COOKIE['safe_mode_'.$blog_id];
			if( !empty($_REQUEST['safe_mode_toggle_user_site']) && $_REQUEST['safe_mode_toggle_user_site'] == $blog_salt ){
				if( self::$user_site_safe_mode_on ){
					self::$user_site_safe_mode_on = false;
					setcookie('safe_mode_'.$blog_id, 0, time() - DAY_IN_SECONDS, '/', null, is_ssl());
				}else{
					self::$user_site_safe_mode_on = true;
					setcookie('safe_mode_'.$blog_id, $blog_salt, time() + DAY_IN_SECONDS, '/', null, is_ssl());
				}
				WP_Safe_Mode::redirect();
			}
		}elseif( !is_network_admin() ){
			self::$user_site_safe_mode_on = self::$user_safe_mode_on;
		}
		//next we load other settings into the object
		if( !self::$hardcoded_override ){
			if( is_multisite() && is_network_admin() ){
				$settings = get_site_option('wp_safe_mode_settings', array());
			}else{
				$settings = self::get_settings();
			}
			foreach( $settings as $setting_key => $setting_val ){
				if( property_exists('WP_Safe_Mode', $setting_key) ){
					if( is_array($setting_val) && is_array(self::${$setting_key}) ){
						//any hard-coded options here get tacked to the end of the resulting array
						if( is_array($setting_val) ) self::${$setting_key} = array_unique(array_merge($setting_val, self::${$setting_key}));
					}else{
						self::${$setting_key} = $setting_val;
					}
				}
			}
		}
		if( !self::$multisite_single_site) self::$multisite_single_site_admins = false; //so we can assume this argument can only be true if single_site is also true
		//check whether Safe Mode is active in one way or another
		if( !self::$safe_mode_on ){
			//check network safe mode
			if( is_multisite() ){
				if( self::$network_safe_mode_on ){
					//safe mode is on unless IP restrictions don't match
					self::$safe_mode_on = true;
					if( !empty(self::$network_ip_array) ){
						if( !in_array($_SERVER['REMOTE_ADDR'], self::$network_ip_array) ){
							self::$safe_mode_on = false;
							self::$network_ip_restricted = true;
						}
					}
				}
			}
			//check site safe mode which an override network safe mode
			if( !is_multisite() || self::$multisite_single_site ){
				if( self::$site_safe_mode_on ){
					//check safe mode with IP Restriction
					self::$safe_mode_on = true;
					if( !empty(self::$site_ip_array) ){
						if( !in_array($_SERVER['REMOTE_ADDR'], self::$site_ip_array) ){
							self::$safe_mode_on = false;
							self::$site_ip_restricted = true;
						}
					}
				}
			}
			if( self::$user_safe_mode_on || self::$user_site_safe_mode_on ) self::$safe_mode_on = true;
			//safe mode is now on or off - whether we proceed with disabling plugins and themes remains to be seen further down
		}
		//load other mu-plugins if required, or if safe mode is off and this file is not in the mu-plugins folder (i.e. a custom mu-plugins folder)
		if( empty($mu_plugins_dir) ) self::$mu_plugins_dir = WP_CONTENT_DIR . '/mu-plugins';
		if( !self::$safe_mode_on ) self::$load_mu_plugins = true; //we always load mu plugins if we are not in safe mode but with a custom mu-plugins folder
		if( self::$load_mu_plugins ){
			//the following is a mix of the mu-plugins folder loading in wp-settings.php and the wp_get_mu_plugins() function in wp-settings.php
			foreach ( self::wp_get_mu_plugins() as $mu_plugin ) {
				include_once( $mu_plugin );
			}
		}
		//check activation toggles for IP, network-wide and sitewide safe mode
		if( !empty($_REQUEST['safe_mode_toggle_site']) || !empty($_REQUEST['safe_mode_toggle_network']) ){
			add_action('init', 'WP_Safe_Mode::safe_mode_toggle');
		}
		//admin bar stuff
		add_action( 'admin_bar_menu', 'WP_Safe_Mode::admin_bar_menu', 999, 1 );
		add_action( 'admin_head', 'WP_Safe_Mode::css' );
		add_action( 'wp_head', 'WP_Safe_Mode::css' );
		//skip plugins and themes page to avoid deactivations
		if( (is_admin() || is_network_admin()) && preg_match('/\/(plugins|themes)\.php\??/', $_SERVER['REQUEST_URI']) ){
			add_action('admin_notices', 'WP_Safe_Mode::admin_notices');
			add_action('network_admin_notices', 'WP_Safe_Mode::admin_notices');
			add_action('plugins_loaded', 'WP_Safe_Mode::disable_plugin_safe_mode');
			add_action('after_setup_theme', 'WP_Safe_Mode::disable_theme_safe_mode');
		}
		//check if we're in safe mode and return if not
		if ( !self::$safe_mode_on ) return false;
		//plugins
		if( self::$disable_plugins ){
			add_filter( 'site_option_active_sitewide_plugins', 'WP_Safe_Mode::disable_multisite_plugins' );
			add_filter( 'option_active_plugins', 'WP_Safe_Mode::disable_plugins' );
		}
		//themes
		if( self::$disable_themes ){
			add_filter( 'site_option_allowedthemes', 'WP_Safe_Mode::disable_theme_multisite' );
			add_filter( 'stylesheet', 'WP_Safe_Mode::disable_theme');
			add_filter( 'template', 'WP_Safe_Mode::disable_theme');
			add_filter( 'option_stylesheet', 'WP_Safe_Mode::disable_theme' );
			add_filter( 'option_template', 'WP_Safe_Mode::disable_theme' );
		}
	}
	
	/**
	 * Add safe mode menu item to admin bar.
	 * @param WP_Admin_Bar $wp_admin_bar
	 */
	public static function admin_bar_menu( $wp_admin_bar ) {
		//users without ability to manage mode will just see a safe mode notice if enabled and admin bars show
		if( !self::can_user_enable() ){
			if( self::$safe_mode_on ){
				$wp_admin_bar->add_node( array(
					'id'    => 'wp-safe-mode',
					'title' => esc_html(sprintf(__('Safe Mode (%s)', 'wp-safe-mode'), __('Enabled', 'wp-safe-mode'))),
					'meta'  => array( 'class' => 'safe-mode safe-mode-on' )
				) );
			}
			return;
		}
		//Build extended top-level menu descriptor for admin bar, depending on safe mode status
		$ip_restricted = self::$site_ip_restricted || (self::$network_ip_restricted && current_user_can('install_plugins')); //safe mode is enabled, but restricted by IP and not enabled for current user
		if( $ip_restricted || self::$safe_mode_on ){
			// regular safe mode, it's an on-off thing
			$status = esc_html__('Enabled', 'wp-safe-mode');
			//now multisite considerations
			if( is_multisite() ){
				$status_meta = array();
				// multisite needs to be a little more informative since we have cascading safe modes
				if( self::$site_safe_mode_on || self::$user_site_safe_mode_on ){
					$site_text = __('Site', 'wp-safe-mode');
					//add site to desc along with user only asterisk if applicable
					if( !self::$site_safe_mode_on || self::$site_ip_restricted ) $site_text .= '*';
					$status_type_meta[] =  $site_text;
				}
				if( is_multisite() && (self::$network_safe_mode_on || self::$user_safe_mode_on) ){
					//if in multisite, single-site admins will only see this if network safe mode is being imposed on them
					if( !self::$network_ip_restricted || current_user_can('install_plugins') ){
						$network_text = __('Network', 'wp-safe-mode');
						//add network desc along with user only asterisk if applicable
						if( !self::$network_safe_mode_on || self::$network_ip_restricted ) $network_text .= '*';
						$status_type_meta[] = $network_text;
					}
				}
				if( !empty($status_type_meta) ){
					switch( count($status_type_meta) ){
						case 2 : $status_type_ph = _x('%s & %s', 'enabled safe mode types', 'wp-safe-mode'); break;
						case 1 : $status_type_ph = _x('%s', 'enabled safe mode types', 'wp-safe-mode'); break;
					}
					$status_meta[] = vsprintf( $status_type_ph, $status_type_meta);
				}
			}else{
				if( self::$user_site_safe_mode_on && (!self::$site_safe_mode_on || self::$site_ip_restricted) ){
					$status .= ' *';
				}elseif( self::$site_safe_mode_on && !$ip_restricted && !empty(self::$site_ip_array) ){
					$ip_restricted = true;
				}
			}
			//IP Restriction Warning
			if( $ip_restricted ){
				$status_meta[] = esc_html__('IP Restricted', 'wp-safe-mode');
			}
		}else{
			$status = esc_html__('Disabled', 'wp-safe-mode');
		}
		$safe_mode_status_text = sprintf(esc_html__('Safe Mode %s', 'wp-safe-mode'), $status);
		if( !empty($status_meta) ) $safe_mode_status_text .= ' - ' . implode(' - ', $status_meta);
		//Main Menu Item
		$args = array(
				'id'    => 'wp-safe-mode',
				'title' => $safe_mode_status_text,
				'meta'  => array( 'class' => 'safe-mode ' )
		);
		//restrict links
		if( !is_multisite() || (self::$multisite_single_site_admins || current_user_can('install_plugins')) ){
			$safe_mode_user_url = ( !is_multisite() || is_network_admin() ) ? self::safe_mode_toggle_user_url() : self::safe_mode_toggle_user_site_url();
			$args['href']  = $safe_mode_user_url['href'];
		}
		if( !self::$safe_mode_on && $ip_restricted ){
			$args['meta']['class'] .= 'safe-mode-on-others';
		}else{
			$args['meta']['class'] .= self::$safe_mode_on ? 'safe-mode-on':'safe-mode-off';
		}
		$wp_admin_bar->add_node( $args );
		//Submenu Action Items
		if( is_multisite() ){
			//we assume users at this point have the right caps, it's now whether settings allow them to toggle certain modes
			$can_view_site_toggle = self::$multisite_single_site && (self::$multisite_single_site_admins || current_user_can('install_plugins'));
			if( !is_network_admin() && $can_view_site_toggle )  $wp_admin_bar->add_node( self::safe_mode_toggle_user_site_url() );
			if( current_user_can('install_plugins') ) $wp_admin_bar->add_node( self::safe_mode_toggle_user_url() );
			if( !is_network_admin() && $can_view_site_toggle ) $wp_admin_bar->add_node( self::safe_mode_toggle_site_url() );
			if( current_user_can('install_plugins') ) $wp_admin_bar->add_node( self::safe_mode_toggle_network_url() );
		}else{
			$wp_admin_bar->add_node( $safe_mode_user_url );
			$wp_admin_bar->add_node( self::safe_mode_toggle_site_url() );
		}
	}
	
	/**
	 * Handles requests to toggle site and network safe modes, returns false on failure or redirects on success.
	 * @return bool
	 */
	public static function safe_mode_toggle(){
		if( !self::can_user_enable() ) return false;
		if( !empty($_REQUEST['safe_mode_toggle_site']) && wp_verify_nonce($_REQUEST['safe_mode_toggle_site'], 'safe_mode_toggle_site') ){
			$settings = get_option('wp_safe_mode_settings', array());
			$settings['site_safe_mode_on'] = self::$safe_mode_on = empty($settings['site_safe_mode_on']);
			update_option('wp_safe_mode_settings', $settings);
			self::redirect( is_network_admin() );
		}elseif( !empty($_REQUEST['safe_mode_toggle_network']) && wp_verify_nonce($_REQUEST['safe_mode_toggle_network'], 'safe_mode_toggle_network') ){
			$settings = get_site_option('wp_safe_mode_settings', array());
			$settings['network_safe_mode_on'] = self::$safe_mode_on = empty($settings['network_safe_mode_on']);
			update_site_option('wp_safe_mode_settings', $settings);
			self::redirect( true );
		}
	}
	
	/**
	 * Provides array of meta for generating link to toggle user safe mode with network context if in MultiSite
	 * @return array
	 */
	public static function safe_mode_toggle_user_url(){
		$status = !self::$user_safe_mode_on ? __('Enable', 'wp-safe-mode') : __('Disable', 'wp-safe-mode');
		$status_class = self::$user_safe_mode_on ? 'on':'off';
		$title = is_multisite() ? __('%s Safe Mode (Only Me - Network)', 'wp-safe-mode') : __('%s Safe Mode (Only Me)', 'wp-safe-mode');
		return array(
			'id'    => 'wp-safe-mode-toggle-user',
			'parent' => 'wp-safe-mode',
			'title' => esc_html( sprintf($title, $status) ),
			'href'  => esc_url( add_query_arg('safe_mode_toggle_user', self::$user_safe_mode_salt) ),
			'meta' => array(
				'class' => 'user-safe-mode safe-mode-type-'.$status_class,
				'title' => esc_html__('You can enable Safe Mode only for yourself, all other visitors will see the site with Safe Mode disabled.', 'wp-safe-mode')
			),
			'status' => self::$user_safe_mode_on
		);
	}
	
	/**
	 * Provides array of meta for generating link to toggle user site safe mode with network context if in MultiSite
	 * @return array
	 */
	public static function safe_mode_toggle_user_site_url(){
		$status = !self::$user_site_safe_mode_on ? __('Enable', 'wp-safe-mode') : __('Disable', 'wp-safe-mode');
		$status_class = self::$user_site_safe_mode_on ? 'on':'off';
		$blog_salt = sha1( self::$user_safe_mode_salt . get_current_blog_id() );
		return array(
			'id'    => 'wp-safe-mode-toggle-user-site',
			'parent' => 'wp-safe-mode',
			'title' => esc_html( sprintf(__('%s Safe Mode (Only Me - Site)', 'wp-safe-mode'), $status) ),
			'href'  => esc_url( add_query_arg('safe_mode_toggle_user_site', $blog_salt) ),
			'meta' => array(
				'class' => 'user-site-safe-mode safe-mode-type-'.$status_class,
				'title' => esc_html__('You can enable Safe Mode only for yourself, all other visitors will see the site with Safe Mode disabled.', 'wp-safe-mode')
			),
			'status' => self::$user_site_safe_mode_on
		);
	}
	
	/**
	 * Provides array of meta for generating link to toggle network safe mode if in MultiSite
	 * @return array
	 */
	public static function safe_mode_toggle_site_url(){
		$status = !self::$site_safe_mode_on ? __('Enable', 'wp-safe-mode') : __('Disable', 'wp-safe-mode');
		$status_class = self::$site_safe_mode_on ? 'on':'off';
		if( self::$site_safe_mode_on && self::$site_ip_restricted ) $status_class = 'on-others';
		return array(
			'id'    => 'wp-safe-mode-toggle',
			'parent' => 'wp-safe-mode',
			'title' => esc_html( sprintf(__('%s Safe Mode (Site)', 'wp-safe-mode'), $status) ),
			'href'  => esc_url(add_query_arg('safe_mode_toggle_site', wp_create_nonce('safe_mode_toggle_site'))),
			'meta' => array(
				'class' => 'safe-mode safe-mode-type-'.$status_class,
				'title' => esc_html__('Enable Safe Mode for this site.', 'wp-safe-mode')
			),
			'status' => self::$site_safe_mode_on
		);
	}
	
	/**
	 * Provides array of meta for generating link to toggle network safe mode in MultiSite
	 * @return array
	 */
	public static function safe_mode_toggle_network_url(){
		$status = !self::$network_safe_mode_on ? __('Enable', 'wp-safe-mode') : __('Disable', 'wp-safe-mode');
		$status_class = self::$network_safe_mode_on ? 'on':'off';
		if( self::$network_safe_mode_on && self::$network_ip_restricted ) $status_class = 'on-others';
		return array(
			'id'    => 'wp-safe-mode-toggle-network',
			'parent' => 'wp-safe-mode',
			'title' => esc_html( sprintf(__('%s Safe Mode (Network)', 'wp-safe-mode'), $status) ),
			'href'  => esc_url(add_query_arg('safe_mode_toggle_network', wp_create_nonce('safe_mode_toggle_network'))),
			'meta' => array(
				'class' => 'total-safe-mode safe-mode-type-'.$status_class,
				'title' => esc_html__('Safe Mode will be enabled on all sites accross the entire network.', 'wp-safe-mode')
			),
			'status' => self::$network_safe_mode_on
		);
	}
	
	/**
	 * Generates admin notices in the theme and plugin pages of the admin dashboard.
	 */
	public static function admin_notices(){
		if( preg_match('/\/(themes)\.php\??/', $_SERVER['REQUEST_URI']) ){
			//we're in a theme page
			if( self::$safe_mode_on ){
				$notices = array();
				if( self::$disable_themes ){
					$default_theme = self::get_safe_theme();
					if( $default_theme ){
						$WP_Theme = wp_get_theme($default_theme);
						if( is_multisite() && is_network_admin() ){
							$notices[] = sprintf( esc_html__('Safe Mode is currently enabled, which will load the %s theme on sites in this network.', 'wp-safe-mode'), '<code>'.$WP_Theme->get('Name').'</code>' );
						}else{
							$notices[] = sprintf( esc_html__('Safe Mode is currently enabled, which will load the %s theme on any other page. You can switch themes here which will be used when Safe Mode is deactivated.', 'wp-safe-mode'), '<code>'.$WP_Theme->get('Name').'</code>' );
						}
						echo '<div class="notice notice-info"><p>'. implode('</p><p>', $notices) .'</p></div>';
					}else{
						$notice = esc_html__('You do not have a default theme to revert to wihlst in Safe Mode, so the current theme will remain active. To enable a \'Safe Mode\' theme please select a default theme from the %s page or install any of the default Twenty-Something WordPress themes.', 'wp-safe-mode');
						$notices[] = sprintf( $notice, '<a href="'.admin_url('admin.php?page=wp-safe-mode').'">'.esc_html__('WP Safe Mode Settings', 'wp-safe-mode').'</a>' );
						echo '<div class="notice notice-warning"><p>'. implode('</p><p>', $notices) .'</p></div>';
					}
				}else{
					$notices[] = esc_html__('Safe mode is currently enabled, but themes are not set to be disabled.', 'wp-safe-mode');
					if( !is_multisite() || is_network_admin() || current_user_can('install_plugins') || self::$multisite_single_site_admins ){
						if( class_exists('WP_Safe_Mode_Admin') ){
							$notice = esc_html__('You can modify this behaviour via the %s page.', 'wp-safe-mode');
							$admin_url = is_multisite() ? network_admin_url('admin.php?page=wp-safe-mode') : admin_url('admin.php?page=wp-safe-mode');
							$notices[] = sprintf($notice, '<a href="'.$admin_url.'">'.esc_html__('WP Safe Mode Settings', 'wp-safe-mode').'</a>');
						}else{
							//counterpart plugin not installed, so we can also hard-code this text since it wouldn't get translated anyway.
							$notice = 'You can modify this behaviour by editing the wp-safe-mode.php file in %1$s or installing/activating the %2$s plugin and visiting the settings page.';
							$notices[] = sprintf($notice, '<code>'.__FILE__.'</code>', 'WP Safe Mode');
						}
					}
					echo '<div class="notice notice-warning"><p>'. implode('</p><p>', $notices) .'</p></div>';
				}
			}
		}else{
			$notices = array( 'warning' => array(), 'info' => array() );
			//we're in a plugin page
			if( self::$safe_mode_on ){
				$notices['info'][] = esc_html__('You are currently in Safe Mode, you can deactivate or activate the plugins below which will take effect when Safe Mode is disabled.', 'wp-safe-mode');
			}
			//check for mu-plugins folder installation warnings
			if( WPMU_PLUGIN_DIR == self::_DIR_() ){
				//check if we're in the custom mu-plugins folder
				if( WPMU_PLUGIN_DIR == WP_CONTENT_DIR . '/wp-safe-mode' ){
					//if so, is there another mu-plugins folder and are there any plugins there
					$mu_plugins = self::wp_get_mu_plugins();
					if( !empty($mu_plugins) ){
						//we warn them about whether or not these mu-plugins are being loaded
						$notice = esc_html__('You are currently loading a custom must-use directory which contains the WP Safe Mode loader file. Your default mu-plugins directory is %1$s which contains %2$d must-use plugins.', 'wp-safe-mode');
						$notices['info'][] = sprintf( str_replace('%2$d', '%2$s', $notice), '<code>'.self::$mu_plugins_dir.'</code>', '<code>'.count($mu_plugins).'</code>' ); //we replace digit placeholder with string to allow html code
						if( self::$load_mu_plugins ){
							//warn user that mu plugins are still being loaded, with varying text depending on whether safe mode is on or off
							if( self::$safe_mode_on ){
								$notices['warning'][] = esc_html__('Must-Use plugins are currently being loaded even though Safe Mode is enabled.', 'wp-safe-mode');
								//modify warning based on whether user only uploaded mu-plugin loader file or installed the entire WP Safe Mode plugin
								if( !is_multisite() || is_network_admin() || self::can_user_enable() ){ //show this only to admins with permission if in multisite
									if( class_exists('WP_Safe_Mode_Admin') ){
										$notice = esc_html__('You can modify this behaviour via the %s page.', 'wp-safe-mode');
										$admin_url = is_multisite() ? network_admin_url('admin.php?page=wp-safe-mode') : admin_url('admin.php?page=wp-safe-mode');
										$notices['warning'][] = sprintf($notice, '<a href="'.$admin_url.'">'.esc_html__('WP Safe Mode Settings', 'wp-safe-mode').'</a>');
									}else{
										//counterpart plugin not installed, so we can also hard-code this text since it wouldn't get translated anyway.
										$notice = 'You can modify this behaviour by editing the wp-safe-mode.php file in %1$s or installing/activating the %2$s plugin and visiting the settings page.';
										$notices['warning'][] = sprintf($notice, '<code>'.__FILE__.'</code>', 'WP Safe Mode');
									}
								}
							}else{
								$notices['info'][] = esc_html__('Must-Use plugins are currently being loaded even if Safe Mode is disabled.', 'wp-safe-mode');
							}
						}
						if( !empty($_REQUEST['plugin_status']) && $_REQUEST['plugin_status'] == 'mustuse' ){
							$notices['info'][] = esc_html__('Currently loaded mu-plugins are :', 'wp-safe-mode');
							ob_start();
							echo '<ul>';
							foreach( $mu_plugins as $mu_plugin ){
								$plugin_data = get_plugin_data( $mu_plugin, false, false );
								$plugin_name = !empty($plugin_data['Name']) ? $plugin_data['Name'] : basename($mu_plugin);
								echo '<li> - <em>'. $plugin_name . '</em></li>';
							}
							echo '</ul>';
							$notices['info'][] = ob_get_clean();
						}else{
							$notices['info'][] = esc_html__('You can still view a list of loaded plugins in the "Must Use" plugins list below.', 'wp-safe-mode');
						}
					}
				}else{
					//not in a custom mu-plugins folder
					$mu_plugins = wp_get_mu_plugins();
					if( count($mu_plugins) > 1 ){
						//there's at least one more mu-plugin file being loaded, so we will issue a warning about that
						$notices['warning'][] = esc_html__('You have installed the WP Safe Mode loader file in the default mu-plugins folder, which contains more plugins that will get loaded even in Safe Mode.', 'wp-safe-mode');//modify warning based on whether user only uploaded mu-plugin loader file or installed the entire WP Safe Mode plugin
						if( class_exists('WP_Safe_Mode_Admin') ){
							$notice = esc_html__('To prevent Must-Use plugins from being loaded in Safe Mode, you need to install the loader via the %s page.', 'wp-safe-mode');
							$notices['warning'][] = sprintf($notice, '<a href="'.admin_url('admin.php?page=wp-safe-mode').'">'.esc_html__('WP Safe Mode Settings', 'wp-safe-mode').'</a>');
						}else{
							//counterpart plugin not installed, so we can also hard-code this text since it wouldn't get translated anyway.
							$notices['warning'][] = 'To prevent Must-Use plugins from being loaded in Safe Mode, please refer to our <a href="https://wordpress.org/plugins/wp-safe-mode/#installation">installation instructions</a>, since you do not have the main \'WP Safe Mode\' plugin active.';
						}
					}
				}
			}
			$notices[] = '';
			foreach( $notices as $notice_type => $type_notices){
				if( !empty($type_notices) ){
					echo '<div class="notice notice-'.$notice_type.'"><p>'. implode('</p><p>', $type_notices) .'</p></div>';
				}
			}
		}
	}
	
	/**
	 * Output CSS for admin bar 
	 */
	public static function css(){
		if( !is_user_logged_in() ) return;
		?>
		<style type="text/css">
			#wpadminbar #wp-admin-bar-wp-safe-mode.safe-mode:hover > .ab-item { color:white; }
			#wpadminbar #wp-admin-bar-wp-safe-mode.safe-mode-on, #wpadminbar #wp-admin-bar-wp-safe-mode.safe-mode-on:hover > .ab-item { background-color:green; }
			#wpadminbar #wp-admin-bar-wp-safe-mode.safe-mode-on-others, #wpadminbar #wp-admin-bar-wp-safe-mode.safe-mode-on-others:hover > .ab-item { background-color:orange; }
			#wpadminbar #wp-admin-bar-wp-safe-mode.safe-mode-off, #wpadminbar #wp-admin-bar-wp-safe-mode.safe-mode-off:hover > .ab-item { background-color: rgba(255, 255, 255, 0.3); }
			#wpadminbar #wp-admin-bar-wp-safe-mode .safe-mode-type-on a { color:green !important; }
			#wpadminbar #wp-admin-bar-wp-safe-mode .safe-mode-type-on-others a { color:orange !important; }
		</style>
		<?php
	}
	
	/**
	 * Use to redirect user after toggling safe mode on/off. Redirects users to home page or admin page when turning safe mode on, 
	 * given they may be on a plugin-specific page that got deactivated.
	 * @param $network_admin
	 */
	public static function redirect( $network_admin = "" ){
		if( self::$safe_mode_on && is_admin() ){
			$url = defined('WP_SAFE_MODE_VERSION') ? 'admin.php?page=wp-safe-mode':'';
			if( $network_admin === "" ) $network_admin = is_network_admin();
			$url = $network_admin ? network_admin_url($url) : admin_url($url);
		}else{
			$url = esc_url_raw( add_query_arg( array(
					'safe_mode_toggle_user' => false,
					'safe_mode_toggle_user_site' => false,
					'safe_mode_toggle_site' => false,
					'safe_mode_toggle_network' => false,
					'settings_updated' => false,
					'errors' => false,
				)
			));
		}
		header("Location: $url", true);
		exit();
	}
	
	/**
	 * Takes an array of plugins and filters out only the plugins we want to keep
	 * @param array $plugins
	 * @return array
	 */
	public static function disable_plugins( $plugins ) {
		$network_plugins = array();
		if( is_multisite() ){
			//prevent network-active plugins from getting removed or kept, that's handled in a separate filter
			remove_filter( 'site_option_active_sitewide_plugins', 'WP_Safe_Mode::disable_multisite_plugins' ); //deactivate
			//deactivated filter to allow us to get original list of network-active plugins
			$network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() );
			add_filter( 'site_option_active_sitewide_plugins', 'WP_Safe_Mode::disable_multisite_plugins' ); //reactivate
		}
		//first remove all plugins we haven't decided to keep
		foreach( $plugins as $key => $plugin ){
			if( !in_array($plugin, self::$plugins_to_keep) && !array_key_exists($plugin, $network_plugins)){
				unset( $plugins[$key] );
			}
		}
		//now add plugins we want to enable, active or not
        foreach( self::$plugins_to_enable as $plugin ){
            if( !in_array($plugin, $plugins) && !array_key_exists($plugin, $network_plugins) ){
                $plugins[] = $plugin;
            }
        }
		return $plugins;
	}
	
	/**
	 * Same as disable_multisite_plugins but checks the key of array, as supplied in MultiSite network active plugins
	 * @param array $plugins
	 * @return array
	 */
	public static function disable_multisite_plugins( $plugins ){
		//disable/enable network plugins if in network safe mode
		if( !is_array($plugins) ) return $plugins;
		if( self::$network_safe_mode_on || self::$user_safe_mode_on ){
			foreach( $plugins as $key => $val ){
				//multisite stores network activated sites in key, normal sites in value
				if( !in_array($key, self::$plugins_to_keep) ){
					unset( $plugins[$key] );
				}
			}
	        foreach( self::$plugins_to_enable as $plugin ){
		        if( empty($plugins[$plugin]) ){
	                $plugins[$plugin] = 1;
	            }
	        }
		}
		//disable network-activated plugins in site safe mode only
		if( self::$site_safe_mode_on || self::$user_site_safe_mode_on ){
			foreach( self::$network_plugins_to_disable as $plugin ){
				if( !empty($plugins[$plugin]) ) unset($plugins[$plugin]);
			}
		}
		return $plugins;
	}
	
	/**
	 * Disables filters for disabling/enabling plugins in safe mode.
	 */
	public static function disable_plugin_safe_mode(){
		remove_filter( 'site_option_active_sitewide_plugins', 'WP_Safe_Mode::disable_multisite_plugins' );
		remove_filter( 'option_active_plugins', 'WP_Safe_Mode::disable_plugins' );
	}
	
	/**
	 * Takes a list of themes and returns the list of allowed themes.
	 * @param array $themes
	 * @return array
	 */
	public static function disable_theme_multisite( $themes ){
		foreach( $themes as $theme => $active){
			if( !preg_match('/^twenty[a-z]+$/', $theme) ){
				unset($themes[$theme]);
			}
		}
		return $themes;
	}
	
	/**
	 * Disables the current theme if not a chosen 'safe' theme and approves the current one  
	 * @param string $theme
	 * @return string
	 */
	public static function disable_theme( $theme ){
		$safe_theme = self::get_safe_theme();
		if( !empty($safe_theme) ){
			$theme = $safe_theme;
		}
		return $theme;
	}
	
	/**
	 * Disables filters for disabling/enabling themes in safe mode.
	 */
	public static function disable_theme_safe_mode(){
		remove_filter( 'site_option_allowedthemes', 'WP_Safe_Mode::disable_theme_multisite' );
		remove_filter( 'stylesheet', 'WP_Safe_Mode::disable_theme');
		remove_filter( 'template', 'WP_Safe_Mode::disable_theme');
		remove_filter( 'option_stylesheet', 'WP_Safe_Mode::disable_theme' );
		remove_filter( 'option_template', 'WP_Safe_Mode::disable_theme' );
	}
	
	/**
	 * Obtains the 'safe' theme to be loaded during safe mode, if none is availble then false is returned.
	 * @return string|false
	 */
	public static function get_safe_theme(){
		foreach( self::$default_themes as $default_theme ){
			$theme_root = get_theme_root($default_theme);
			if( file_exists("$theme_root/$default_theme/style.css") ){
				return $default_theme;
			}
		}
		return false;
	}
	
	/**
	 * Whether or not the user can manage and enable safe mode.
	 * @param $user_id - ID of the user, defaults to the current user.
	 * @return boolean
	 */
	public static function can_user_enable( $user_id = 0 ){
		if( empty($user_id) ) $user_id = get_current_user_id();
		//unless we're in multisite or admin, user can't enable safe mode
		if( is_multisite() ){
			if( user_can( $user_id, 'install_plugins' ) ) return true;
			//check if user can enable/disable
			if( self::$multisite_single_site_admins ){
				return user_can( $user_id, 'switch_themes' );
			}
		}else{
			return user_can( $user_id, 'install_plugins' );
		}
		return false;
	}
	
	/**
	 * Returns the required capability required for an admin to manage safe mode a specific site.
	 * @return bool|string
	 */
	public static function get_admin_capability(){
		$capability = 'install_plugins';
		if( is_multisite() && !is_network_admin() ){
			// we ensure that single-site admin menu is enabled and single-site admins have permission to see it
			if( !empty(self::$multisite_single_site) ){
				//display safe mode settings to super admins, and single-site admins if settings allow it
				if( !empty(self::$multisite_single_site_admins) ) $capability = 'switch_themes';
			}else{
				return false; //don't load a Safe Mode admin menu on single-site
			}
		}
		return $capability;
	}
	
	/**
	 * Gets the list from the site's original mu-plugins folder, provided this plugin isn't already in that folder.
	 * This function replaces the default wp_get_mu_plugins() function in WordPress so that we can check a different folder
	 * than the WPMU_PLUGINS_DIR which is hard-coded in that function.
	 * @return array
	 */
	public static function wp_get_mu_plugins(){
		//check if this file is located within the mu-plugins directory, if not then load mu-plugin files
		if ( self::$mu_plugins_dir != self::_DIR_() && WPMU_PLUGIN_DIR == self::_DIR_() ){
			//this is an adaptation of wp_get_mu_plugins() function in wp-settings.php
			if( is_dir( self::$mu_plugins_dir ) && $dh = opendir( self::$mu_plugins_dir ) ){
				$mu_plugins = array();
				while ( ( $plugin = readdir( $dh ) ) !== false ) {
					if ( substr( $plugin, -4 ) == '.php' )
						$mu_plugins[] = self::$mu_plugins_dir . '/' . $plugin;
				}
				closedir( $dh );
				sort( $mu_plugins );
				return $mu_plugins;
			}
		}
		return array();
	}
	
	/**
	 * Returns the array of settings for the site.
	 * @param boolean $network
	 * @return array
	 */
	public static function get_settings( $network = null ){
		//load settings and override these settings
		if( $network && is_multisite() ){
			$settings = get_site_option('wp_safe_mode_settings', array());
		}elseif( $network === false ){
			$settings = get_option('wp_safe_mode_settings', array());
		}else{
			//load site settings, or for MultiSite mix in network settings with single-site settings accordingly
			if( is_multisite() ){
				$network_settings = get_site_option('wp_safe_mode_settings', array()); // by default, network options are used
				$settings = !empty($network_settings['network_safe_mode_on']) || self::$user_safe_mode_on ? $network_settings : array();
				$settings['multisite_single_site'] = !empty($network_settings['multisite_single_site']);
				$settings['multisite_single_site_admins'] = !empty($network_settings['multisite_single_site_admins']);
				if( !empty($settings['multisite_single_site']) ){
					// single-site overrides allowed, so we merge array but also merge in arrays within the array
					$site_settings = get_option('wp_safe_mode_settings', array()); // so we can check if we're in site safe mode to override options
					if( self::$user_site_safe_mode_on || !empty($site_settings['site_safe_mode_on']) ){
						// if plugins or themes aren't to be disabled on site, don't override that setting on the network-level
						if( empty($site_settings['disable_plugins']) ){
							unset($site_settings['plugins_to_keep'], $site_settings['plugins_to_enable'], $site_settings['network_plugins_to_disable']);
							if( isset($settings['disable_plugins']) ){
								unset($site_settings['disable_plugins']);
							}
						}
						if( empty($site_settings['disable_themes']) ){
							unset($site_settings['default_themes']);
							if( isset($settings['disable_themes']) ){
								unset($site_settings['disable_themes']);
							}
						}
						// merge the rest in, overriding network settings
						foreach( $site_settings as $setting_key => $setting ){
							if( isset($settings[$setting_key]) && is_array($settings[$setting_key]) ){
								//merge in array and make it unique
								$settings[$setting_key] = array_unique( array_merge($settings[$setting_key], $setting) );
								if( $setting_key == 'default_themes' ) $settings['default_themes']  = array_reverse($settings['default_themes']); //flip themes since currently network setting takes precedence
							}else{
								$settings[$setting_key] = $setting;
							}
						}
					}
				}
			}else{
				$settings = get_option('wp_safe_mode_settings');
			}
		}
		if( !is_array($settings) ) $settings = array();
		return $settings;
	}
	
	/**
	 * Returns an array containing directory and filname of this loader file.
	 * @return array
	 */
	public static function where(){
		if( function_exists('wp_safe_mode_loader_location') ){
			return wp_safe_mode_loader_location();
		}
		return array( '__DIR__' => dirname(__FILE__), '__FILE__' => __FILE__ );
	}
	
	/**
	 * Shortcut for getting directory name via where() function.
	 * @return string
	 * @uses WP_Safe_Mode::where()
	 */
	private static function _DIR_(){
		$where = self::where();
		return $where['__DIR__'];
	}
	
	/**
	 * Shortcut for getting file name via where() function.
	 * @return string
	 * @uses WP_Safe_Mode::where()
	 */
	private static function _FILE_(){
		$where = self::where();
		return $where['__FILE__'];
	}
}
WP_Safe_Mode::init();