* * @param string $role Role name. */ function remove_role( $role ) { wp_roles()->remove_role( $role ); } /** * Retrieves a list of super admins. * * @since 3.0.0 * * @global array $super_admins * * @return string[] List of super admin logins. */ function get_super_admins() { global $super_admins; return $super_admins ?? get_site_option( 'site_admins', array( 'admin' ) ); } /** * Determines whether user is a site admin. * * @since 3.0.0 * * @param int|false $user_id Optional. The ID of a user. Defaults to false, to check the current user. * @return bool Whether the user is a site admin. */ function is_super_admin( $user_id = false ) { if ( ! $user_id ) { $user = wp_get_current_user(); } else { $user = get_userdata( $user_id ); } if ( ! $user || ! $user->exists() ) { return false; } if ( is_multisite() ) { $super_admins = get_super_admins(); if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins, true ) ) { return true; } } elseif ( $user->has_cap( 'delete_users' ) ) { return true; } return false; } /** * Grants Super Admin privileges. * * @since 3.0.0 * * @global array $super_admins * * @param int $user_id ID of the user to be granted Super Admin privileges. * @return bool True on success, false on failure. This can fail when the user is * already a super admin or when the `$super_admins` global is defined. */ function grant_super_admin( $user_id ) { // If global super_admins override is defined, there is nothing to do here. if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) { return false; } /** * Fires before the user is granted Super Admin privileges. * * @since 3.0.0 * * @param int $user_id ID of the user that is about to be granted Super Admin privileges. */ do_action( 'grant_super_admin', $user_id ); // Directly fetch site_admins instead of using get_super_admins(). $super_admins = get_site_option( 'site_admins', array( 'admin' ) ); $user = get_userdata( $user_id ); if ( $user && ! in_array( $user->user_login, $super_admins, true ) ) { $super_admins[] = $user->user_login; update_site_option( 'site_admins', $super_admins ); /** * Fires after the user is granted Super Admin privileges. * * @since 3.0.0 * * @param int $user_id ID of the user that was granted Super Admin privileges. */ do_action( 'granted_super_admin', $user_id ); return true; } return false; } /** * Revokes Super Admin privileges. * * @since 3.0.0 * @since 6.9.0 Super admin privileges can be revoked regardless of email address. * * @global array $super_admins * * @param int $user_id ID of the user Super Admin privileges to be revoked from. * @return bool True on success, false on failure. This can fail when the user's email * is the network admin email or when the `$super_admins` global is defined. */ function revoke_super_admin( $user_id ) { // If global super_admins override is defined, there is nothing to do here. if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) { return false; } /** * Fires before the user's Super Admin privileges are revoked. * * @since 3.0.0 * * @param int $user_id ID of the user Super Admin privileges are being revoked from. */ do_action( 'revoke_super_admin', $user_id ); // Directly fetch site_admins instead of using get_super_admins(). $super_admins = get_site_option( 'site_admins', array( 'admin' ) ); $user = get_userdata( $user_id ); if ( $user ) { $key = array_search( $user->user_login, $super_admins, true ); if ( false !== $key ) { unset( $super_admins[ $key ] ); update_site_option( 'site_admins', $super_admins ); /** * Fires after the user's Super Admin privileges are revoked. * * @since 3.0.0 * * @param int $user_id ID of the user Super Admin privileges were revoked from. */ do_action( 'revoked_super_admin', $user_id ); return true; } } return false; } /** * Filters the user capabilities to grant the 'install_languages' capability as necessary. * * A user must have at least one out of the 'update_core', 'install_plugins', and * 'install_themes' capabilities to qualify for 'install_languages'. * * @since 4.9.0 * * @param bool[] $allcaps An array of all the user's capabilities. * @return bool[] Filtered array of the user's capabilities. */ function wp_maybe_grant_install_languages_cap( $allcaps ) { if ( ! empty( $allcaps['update_core'] ) || ! empty( $allcaps['install_plugins'] ) || ! empty( $allcaps['install_themes'] ) ) { $allcaps['install_languages'] = true; } return $allcaps; } /** * Filters the user capabilities to grant the 'resume_plugins' and 'resume_themes' capabilities as necessary. * * @since 5.2.0 * * @param bool[] $allcaps An array of all the user's capabilities. * @return bool[] Filtered array of the user's capabilities. */ function wp_maybe_grant_resume_extensions_caps( $allcaps ) { // Even in a multisite, regular administrators should be able to resume plugins. if ( ! empty( $allcaps['activate_plugins'] ) ) { $allcaps['resume_plugins'] = true; } // Even in a multisite, regular administrators should be able to resume themes. if ( ! empty( $allcaps['switch_themes'] ) ) { $allcaps['resume_themes'] = true; } return $allcaps; } /** * Filters the user capabilities to grant the 'view_site_health_checks' capabilities as necessary. * * @since 5.2.2 * * @param bool[] $allcaps An array of all the user's capabilities. * @param string[] $caps Required primitive capabilities for the requested capability. * @param array $args { * Arguments that accompany the requested capability check. * * @type string $0 Requested capability. * @type int $1 Concerned user ID. * @type mixed ...$2 Optional second and further parameters, typically object ID. * } * @param WP_User $user The user object. * @return bool[] Filtered array of the user's capabilities. */ function wp_maybe_grant_site_health_caps( $allcaps, $caps, $args, $user ) { if ( ! empty( $allcaps['install_plugins'] ) && ( ! is_multisite() || is_super_admin( $user->ID ) ) ) { $allcaps['view_site_health_checks'] = true; } return $allcaps; } return; // Dummy gettext calls to get strings in the catalog. /* translators: User role for administrators. */ _x( 'Administrator', 'User role' ); /* translators: User role for editors. */ _x( 'Editor', 'User role' ); /* translators: User role for authors. */ _x( 'Author', 'User role' ); /* translators: User role for contributors. */ _x( 'Contributor', 'User role' ); /* translators: User role for subscribers. */ _x( 'Subscriber', 'User role' ); s); if ( $checkerClass === null ) { //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error trigger_error( esc_html(sprintf( 'PUC %s does not support updates for %ss %s', self::$latestCompatibleVersion, strtolower($type), $service ? ('hosted on ' . $service) : 'using JSON metadata' )), E_USER_ERROR ); } if ( !isset($apiClass) ) { //Plain old update checker. return new $checkerClass($metadataUrl, $id, $slug, $checkPeriod, $optionName, $muPluginFile); } else { //VCS checker + an API client. $apiClass = self::getCompatibleClassVersion($apiClass); if ( $apiClass === null ) { //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error trigger_error(esc_html(sprintf( 'PUC %s does not support %s', self::$latestCompatibleVersion, $service )), E_USER_ERROR); } return new $checkerClass( new $apiClass($metadataUrl), $id, $slug, $checkPeriod, $optionName, $muPluginFile ); } } /** * * Normalize a filesystem path. Introduced in WP 3.9. * Copying here allows use of the class on earlier versions. * This version adapted from WP 4.8.2 (unchanged since 4.5.6) * * @param string $path Path to normalize. * @return string Normalized path. */ public static function normalizePath($path) { if ( function_exists('wp_normalize_path') ) { return wp_normalize_path($path); } $path = str_replace('\\', '/', $path); $path = preg_replace('|(?<=.)/+|', '/', $path); if ( substr($path, 1, 1) === ':' ) { $path = ucfirst($path); } return $path; } /** * Check if the path points to a plugin file. * * @param string $absolutePath Normalized path. * @return bool */ protected static function isPluginFile($absolutePath) { //Is the file inside the "plugins" or "mu-plugins" directory? $pluginDir = self::normalizePath(WP_PLUGIN_DIR); $muPluginDir = self::normalizePath(WPMU_PLUGIN_DIR); if ( (strpos($absolutePath, $pluginDir) === 0) || (strpos($absolutePath, $muPluginDir) === 0) ) { return true; } //Is it a file at all? Caution: is_file() can fail if the parent dir. doesn't have the +x permission set. if ( !is_file($absolutePath) ) { return false; } //Does it have a valid plugin header? //This is a last-ditch check for plugins symlinked from outside the WP root. if ( function_exists('get_file_data') ) { $headers = get_file_data($absolutePath, array('Name' => 'Plugin Name'), 'plugin'); return !empty($headers['Name']); } return false; } /** * Get the name of the theme's directory from a full path to a file inside that directory. * E.g. "/abc/public_html/wp-content/themes/foo/whatever.php" => "foo". * * Note that subdirectories are currently not supported. For example, * "/xyz/wp-content/themes/my-theme/includes/whatever.php" => NULL. * * @param string $absolutePath Normalized path. * @return string|null Directory name, or NULL if the path doesn't point to a theme. */ protected static function getThemeDirectoryName($absolutePath) { if ( is_file($absolutePath) ) { $absolutePath = dirname($absolutePath); } if ( file_exists($absolutePath . '/style.css') ) { return basename($absolutePath); } return null; } /** * Get the service URI from the file header. * * @param string $fullPath * @return string */ private static function getServiceURI($fullPath) { //Look for the URI if ( is_readable($fullPath) ) { $seek = array( 'github' => 'GitHub URI', 'gitlab' => 'GitLab URI', 'bucket' => 'BitBucket URI', ); $seek = apply_filters('puc_get_source_uri', $seek); $data = get_file_data($fullPath, $seek); foreach ($data as $key => $uri) { if ( $uri ) { return $uri; } } } //URI was not found so throw an error. throw new \RuntimeException( sprintf('Unable to locate URI in header of "%s"', htmlentities($fullPath)) ); } /** * Get the name of the hosting service that the URL points to. * * @param string $metadataUrl * @return string|null */ private static function getVcsService($metadataUrl) { $service = null; //Which hosting service does the URL point to? $host = (string)(wp_parse_url($metadataUrl, PHP_URL_HOST)); $path = (string)(wp_parse_url($metadataUrl, PHP_URL_PATH)); //Check if the path looks like "/user-name/repository". //For GitLab.com it can also be "/user/group1/group2/.../repository". $repoRegex = '@^/?([^/]+?)/([^/#?&]+?)/?$@'; if ( $host === 'gitlab.com' ) { $repoRegex = '@^/?(?:[^/#?&]++/){1,20}(?:[^/#?&]++)/?$@'; } if ( preg_match($repoRegex, $path) ) { $knownServices = array( 'github.com' => 'GitHub', 'bitbucket.org' => 'BitBucket', 'gitlab.com' => 'GitLab', ); if ( isset($knownServices[$host]) ) { $service = $knownServices[$host]; } } return apply_filters('puc_get_vcs_service', $service, $host, $path, $metadataUrl); } /** * Get the latest version of the specified class that has the same major version number * as this factory class. * * @param string $class Partial class name. * @return string|null Full class name. */ protected static function getCompatibleClassVersion($class) { if ( isset(self::$classVersions[$class][self::$latestCompatibleVersion]) ) { return self::$classVersions[$class][self::$latestCompatibleVersion]; } return null; } /** * Get the specific class name for the latest available version of a class. * * @param string $class * @return null|string */ public static function getLatestClassVersion($class) { if ( !self::$sorted ) { self::sortVersions(); } if ( isset(self::$classVersions[$class]) ) { return reset(self::$classVersions[$class]); } else { return null; } } /** * Sort available class versions in descending order (i.e. newest first). */ protected static function sortVersions() { foreach ( self::$classVersions as $class => $versions ) { uksort($versions, array(__CLASS__, 'compareVersions')); self::$classVersions[$class] = $versions; } self::$sorted = true; } protected static function compareVersions($a, $b) { return -version_compare($a, $b); } /** * Register a version of a class. * * @access private This method is only for internal use by the library. * * @param string $generalClass Class name without version numbers, e.g. 'PluginUpdateChecker'. * @param string $versionedClass Actual class name, e.g. 'PluginUpdateChecker_1_2'. * @param string $version Version number, e.g. '1.2'. */ public static function addVersion($generalClass, $versionedClass, $version) { if ( empty(self::$myMajorVersion) ) { $lastNamespaceSegment = substr(__NAMESPACE__, strrpos(__NAMESPACE__, '\\') + 1); self::$myMajorVersion = substr(ltrim($lastNamespaceSegment, 'v'), 0, 1); } //Store the greatest version number that matches our major version. $components = explode('.', $version); if ( $components[0] === self::$myMajorVersion ) { if ( empty(self::$latestCompatibleVersion) || version_compare($version, self::$latestCompatibleVersion, '>') ) { self::$latestCompatibleVersion = $version; } } if ( !isset(self::$classVersions[$generalClass]) ) { self::$classVersions[$generalClass] = array(); } self::$classVersions[$generalClass][$version] = $versionedClass; self::$sorted = false; } } endif;