robotstxt-telemetry/includes/class-robotstxt-telemetry-network.php
2026-08-18 19:06:22 +00:00

301 lines
7.8 KiB
PHP

<?php
/**
* Configuration mode and settings helper.
*
* On Multisite the plugin can run in two modes, controlled from the
* Network Admin by a super administrator:
*
* - "per-site" (default): every site keeps its own settings and its own
* log table, managed by each site administrator.
* - "global": one shared configuration and one central log table (on the
* main site) collect the outbound requests of the whole network.
*
* @package RobotstxtTelemetry
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'Robotstxt_Telemetry_Network' ) ) {
/**
* Class Robotstxt_Telemetry_Network
*/
class Robotstxt_Telemetry_Network {
/**
* Option name storing the configuration mode.
*
* @var string
*/
const MODE_OPTION = 'robotstxt_telemetry_config_mode';
/**
* Plugin settings and their defaults.
*
* @var array
*/
const SETTINGS = array(
'robotstxt_telemetry_useragent_url' => 'hash',
'robotstxt_telemetry_wp_version' => 'actual',
'robotstxt_telemetry_mask_locale' => false,
'robotstxt_telemetry_replace_news_feed' => true,
'robotstxt_telemetry_replace_events_api' => true,
'robotstxt_telemetry_disable_browse_happy' => true,
'robotstxt_telemetry_wp_core_check' => 'safe',
'robotstxt_telemetry_wp_themes_check' => 'safe',
'robotstxt_telemetry_wp_plugins_check' => 'safe',
'robotstxt_telemetry_hidden_plugins' => array(),
'robotstxt_telemetry_plugin_modes' => array(),
'robotstxt_telemetry_retention_period' => '12hours',
'robotstxt_telemetry_delete_on_uninstall' => false,
);
/**
* Whether the network runs in global mode.
*
* @return bool
*/
public static function is_global() {
return is_multisite() && 'global' === get_site_option( self::MODE_OPTION, 'per-site' );
}
/**
* Whether the current request is a Network Admin screen.
*
* @return bool
*/
public static function is_network_admin() {
return is_network_admin();
}
/**
* Capability required to manage the plugin on the current screen.
*
* @return string
*/
public static function manage_capability() {
return self::is_network_admin() ? 'manage_network_options' : 'manage_options';
}
/**
* Base admin URL for the current screen context.
*
* @return string
*/
public static function base_url() {
return self::is_network_admin() ? network_admin_url( 'admin.php' ) : admin_url( 'admin.php' );
}
/**
* Read a plugin setting honoring the configuration mode.
*
* @param string $name Option name.
* @param mixed $default_value Default value.
* @return mixed
*/
public static function get_setting( $name, $default_value = null ) {
if ( self::is_global() ) {
return get_site_option( $name, $default_value );
}
return get_option( $name, $default_value );
}
/**
* Write a plugin setting honoring the configuration mode.
*
* @param string $name Option name.
* @param mixed $value Value.
* @return bool
*/
public static function update_setting( $name, $value ) {
if ( self::is_global() ) {
return update_site_option( $name, $value );
}
return update_option( $name, $value );
}
/**
* Delete a plugin setting honoring the configuration mode.
*
* @param string $name Option name.
* @return bool
*/
public static function delete_setting( $name ) {
if ( self::is_global() ) {
return delete_site_option( $name );
}
return delete_option( $name );
}
/**
* The main site ID of the current network.
*
* @return int
*/
public static function main_site_id() {
if ( function_exists( 'get_main_site_id' ) ) {
return (int) get_main_site_id();
}
$current_site = get_current_site();
return max( 1, (int) $current_site->blog_id );
}
/**
* All site IDs of the network.
*
* @return int[]
*/
public static function get_site_ids() {
if ( function_exists( 'get_sites' ) ) {
$sites = get_sites(
array(
'fields' => 'ids',
'number' => 0,
)
);
return array_map( 'intval', $sites );
}
// WordPress 3.7 - 4.5 fallback.
$site_ids = array();
// phpcs:ignore WordPress.WP.DeprecatedFunctions.wp_get_sitesFound -- Only called when get_sites() is unavailable (WordPress 3.7 - 4.5).
foreach ( (array) wp_get_sites( array( 'limit' => 100000 ) ) as $site ) {
if ( ! empty( $site['blog_id'] ) ) {
$site_ids[] = (int) $site['blog_id'];
}
}
return $site_ids;
}
/**
* Sanitize the configuration mode value.
*
* @param mixed $value Raw input value.
* @return string
*/
public static function sanitize_mode( $value ) {
return 'global' === $value ? 'global' : 'per-site';
}
/**
* The plugin basenames never sent to the WordPress.org update check.
*
* The telemetry plugin itself is always included.
*
* @return string[]
*/
public static function get_hidden_plugins() {
$hidden = self::get_setting( 'robotstxt_telemetry_hidden_plugins', array() );
if ( ! is_array( $hidden ) ) {
$hidden = array();
}
$hidden[] = 'robotstxt-telemetry/robotstxt-telemetry.php';
return array_values( array_unique( $hidden ) );
}
/**
* Get the per-plugin safe-mode map (slug => safe|original).
*
* @return array
*/
public static function get_plugin_modes() {
$modes = self::get_setting( 'robotstxt_telemetry_plugin_modes', array() );
return is_array( $modes ) ? $modes : array();
}
/**
* Get the mode of a single plugin (Safe by default).
*
* @param string $slug Plugin slug.
* @return string
*/
public static function get_plugin_mode( $slug ) {
$modes = self::get_plugin_modes();
return isset( $modes[ $slug ] ) && 'original' === $modes[ $slug ] ? 'original' : 'safe';
}
/**
* The User-Agent URL mode: 'url', 'hash' (default), or 'none'.
*
* Falls back to the legacy boolean option when the new option is
* not set yet, so existing installations keep their behavior.
*
* @return string
*/
public static function get_useragent_url_mode() {
$mode = self::get_setting( 'robotstxt_telemetry_useragent_url', '' );
if ( in_array( $mode, array( 'url', 'hash', 'none' ), true ) ) {
return $mode;
}
$legacy = self::get_setting( 'robotstxt_telemetry_hash_useragent_url', true );
return (bool) $legacy ? 'hash' : 'url';
}
/**
* The WordPress version mode: 'actual' (default), 'major', or 'nulled'.
*
* @return string
*/
public static function get_wp_version_mode() {
$mode = self::get_setting( 'robotstxt_telemetry_wp_version', 'actual' );
return in_array( $mode, array( 'actual', 'major', 'nulled' ), true ) ? $mode : 'actual';
}
/**
* Mask a WordPress version value according to the mode.
*
* 'major' keeps the first two segments and replaces the rest with
* an "n" (for example "6.9.1" becomes "6.9.n", "7.0" becomes
* "7.0.n"); 'nulled' always returns "0.0.0".
*
* @param string $version Version value.
* @param string $mode Version mode.
* @return string
*/
public static function mask_wp_version( $version, $mode ) {
if ( 'nulled' === $mode ) {
return '0.0.0';
}
if ( 'major' === $mode && preg_match( '/^(\d+\.\d+)/', (string) $version, $matches ) ) {
return $matches[1] . '.n';
}
return (string) $version;
}
/**
* Seed the network settings from the main site when switching to
* global mode, so the network keeps behaving like the main site.
*
* @return void
*/
public static function seed_network_settings() {
$main_site_id = self::main_site_id();
foreach ( self::SETTINGS as $name => $default ) {
if ( false === get_site_option( $name, false ) ) {
update_site_option( $name, get_blog_option( $main_site_id, $name, $default ) );
}
}
}
}
}