get_value('classes'); if (!$Classes || !$ClassLevels) { $QueryID = G::$DB->get_query_id(); G::$DB->query(' SELECT ID, Name, Level, Secondary FROM permissions ORDER BY Level'); $Classes = G::$DB->to_array('ID'); $ClassLevels = G::$DB->to_array('Level'); G::$DB->set_query_id($QueryID); G::$Cache->cache_value('classes', [$Classes, $ClassLevels], 0); } $Debug->set_flag('Loaded permissions'); return [$Classes, $ClassLevels]; } public static function user_stats($UserID, $refresh = false) { global $Cache, $DB; if ($refresh) { $Cache->delete_value('user_stats_'.$UserID); } $UserStats = $Cache->get_value('user_stats_'.$UserID); if (!is_array($UserStats)) { $DB->prepared_query(' SELECT uls.Uploaded AS BytesUploaded, uls.Downloaded AS BytesDownloaded, um.BonusPoints, um.RequiredRatio FROM users_main um INNER JOIN users_leech_stats AS uls ON (uls.UserID = um.ID) WHERE um.ID = ? ', $UserID ); $UserStats = $DB->next_record(MYSQLI_ASSOC); $Cache->cache_value('user_stats_'.$UserID, $UserStats, 3600); } return $UserStats; } /** * Get user info, is used for the current user and usernames all over the site. * * @param $UserID int The UserID to get info for * @return array with the following keys: * int ID * string Username * int PermissionID * array Paranoia - $Paranoia array sent to paranoia.class * boolean Artist * boolean Donor * string Warned - When their warning expires in international time format * string Avatar - URL * boolean Enabled * string Title * string CatchupTime - When they last caught up on forums * boolean Visible - If false, they don't show up on peer lists * array ExtraClasses - Secondary classes. * int EffectiveClass - the highest level of their main and secondary classes */ public static function user_info($UserID) { global $Classes, $SSL; $UserInfo = G::$Cache->get_value("user_info_$UserID"); // the !isset($UserInfo['Paranoia']) can be removed after a transition period if (empty($UserInfo) || empty($UserInfo['ID']) || !isset($UserInfo['Paranoia']) || empty($UserInfo['Class'])) { $OldQueryID = G::$DB->get_query_id(); G::$DB->query(" SELECT m.ID, m.Username, m.PermissionID, m.Paranoia, i.Artist, i.Donor, i.Warned, i.Avatar, m.Enabled, m.Title, i.CatchupTime, m.Visible, la.Type AS LockedAccount, GROUP_CONCAT(ul.PermissionID SEPARATOR ',') AS Levels FROM users_main AS m INNER JOIN users_info AS i ON i.UserID = m.ID LEFT JOIN locked_accounts AS la ON la.UserID = m.ID LEFT JOIN users_levels AS ul ON ul.UserID = m.ID WHERE m.ID = '$UserID' GROUP BY m.ID"); if (!G::$DB->has_results()) { // Deleted user, maybe? $UserInfo = [ 'ID' => $UserID, 'Username' => '', 'PermissionID' => 0, 'Paranoia' => [], 'Artist' => false, 'Donor' => false, 'Warned' => '0000-00-00 00:00:00', 'Avatar' => '', 'Enabled' => 0, 'Title' => '', 'CatchupTime' => 0, 'Visible' => '1', 'Levels' => '', 'Class' => 0]; } else { $UserInfo = G::$DB->next_record(MYSQLI_ASSOC, ['Paranoia', 'Title']); $UserInfo['CatchupTime'] = strtotime($UserInfo['CatchupTime']); $UserInfo['Paranoia'] = unserialize_array($UserInfo['Paranoia']); if ($UserInfo['Paranoia'] === false) { $UserInfo['Paranoia'] = []; } $UserInfo['Class'] = $Classes[$UserInfo['PermissionID']]['Level']; } if ($UserInfo['LockedAccount'] == "") { unset($UserInfo['LockedAccount']); } if (!empty($UserInfo['Levels'])) { $UserInfo['ExtraClasses'] = array_fill_keys(explode(',', $UserInfo['Levels']), 1); } else { $UserInfo['ExtraClasses'] = []; } unset($UserInfo['Levels']); $EffectiveClass = $UserInfo['Class']; foreach ($UserInfo['ExtraClasses'] as $Class => $Val) { $EffectiveClass = max($EffectiveClass, $Classes[$Class]['Level']); } $UserInfo['EffectiveClass'] = $EffectiveClass; G::$Cache->cache_value("user_info_$UserID", $UserInfo, 2592000); G::$DB->set_query_id($OldQueryID); } if (strtotime($UserInfo['Warned']) < time()) { $UserInfo['Warned'] = '0000-00-00 00:00:00'; G::$Cache->cache_value("user_info_$UserID", $UserInfo, 2592000); } return $UserInfo; } /** * Gets the heavy user info * Only used for current user * * @param string $UserID The userid to get the information for * @return array fetched heavy info. * Just read the goddamn code, I don't have time to comment this shit. */ public static function user_heavy_info($UserID) { $HeavyInfo = G::$Cache->get_value("user_info_heavy_$UserID"); if (empty($HeavyInfo)) { $QueryID = G::$DB->get_query_id(); G::$DB->query(" SELECT m.Invites, m.torrent_pass, m.IP, m.CustomPermissions, m.can_leech AS CanLeech, m.IRCKey, i.AuthKey, i.RatioWatchEnds, i.RatioWatchDownload, i.StyleID, i.StyleURL, i.DisableInvites, i.DisablePosting, i.DisableUpload, i.DisablePoints, i.DisableWiki, i.DisableAvatar, i.DisablePM, i.DisableRequests, i.DisableForums, i.DisableIRC, i.DisableTagging," . " i.SiteOptions, i.DownloadAlt, i.LastReadNews, i.LastReadBlog, i.RestrictedForums, i.PermittedForums, i.NavItems, m.FLTokens, m.PermissionID FROM users_main AS m INNER JOIN users_info AS i ON i.UserID = m.ID WHERE m.ID = '$UserID'"); $HeavyInfo = G::$DB->next_record(MYSQLI_ASSOC, ['CustomPermissions', 'SiteOptions']); $HeavyInfo['CustomPermissions'] = unserialize_array($HeavyInfo['CustomPermissions']); if (!empty($HeavyInfo['RestrictedForums'])) { $RestrictedForums = array_map('trim', explode(',', $HeavyInfo['RestrictedForums'])); } else { $RestrictedForums = []; } unset($HeavyInfo['RestrictedForums']); if (!empty($HeavyInfo['PermittedForums'])) { $PermittedForums = array_map('trim', explode(',', $HeavyInfo['PermittedForums'])); } else { $PermittedForums = []; } unset($HeavyInfo['PermittedForums']); if (!empty($HeavyInfo['NavItems'])) { $NavItems = array_map('trim', explode(',', $HeavyInfo['NavItems'])); } else { $NavItems = []; } $HeavyInfo['NavItems'] = $NavItems; G::$DB->query(" SELECT PermissionID FROM users_levels WHERE UserID = $UserID"); $PermIDs = G::$DB->collect('PermissionID'); foreach ($PermIDs AS $PermID) { $Perms = Permissions::get_permissions($PermID); if (!empty($Perms['PermittedForums'])) { $PermittedForums = array_merge($PermittedForums, array_map('trim', explode(',', $Perms['PermittedForums']))); } } $Perms = Permissions::get_permissions($HeavyInfo['PermissionID']); unset($HeavyInfo['PermissionID']); if (!empty($Perms['PermittedForums'])) { $PermittedForums = array_merge($PermittedForums, array_map('trim', explode(',', $Perms['PermittedForums']))); } if (!empty($PermittedForums) || !empty($RestrictedForums)) { $HeavyInfo['CustomForums'] = []; foreach ($RestrictedForums as $ForumID) { $HeavyInfo['CustomForums'][$ForumID] = 0; } foreach ($PermittedForums as $ForumID) { $HeavyInfo['CustomForums'][$ForumID] = 1; } } else { $HeavyInfo['CustomForums'] = null; } if (isset($HeavyInfo['CustomForums'][''])) { unset($HeavyInfo['CustomForums']['']); } $HeavyInfo['SiteOptions'] = unserialize_array($HeavyInfo['SiteOptions']); $HeavyInfo['SiteOptions'] = array_merge(static::default_site_options(), $HeavyInfo['SiteOptions']); $HeavyInfo = array_merge($HeavyInfo, $HeavyInfo['SiteOptions']); unset($HeavyInfo['SiteOptions']); G::$DB->set_query_id($QueryID); G::$Cache->cache_value("user_info_heavy_$UserID", $HeavyInfo, 0); } return $HeavyInfo; } public static function get_user_nav_items($userId) { $Info = self::user_heavy_info($userId); $UserIds = !empty($Info['NavItems']) ? $Info['NavItems'] : []; $NavItems = self::get_nav_items(); $UserItems = []; foreach ($NavItems as $n) { if (($n['mandatory'] || in_array($n['id'], $UserIds)) || (!count($UserIds) && $n['initial'])) { $UserItems[] = $n; } } return $UserItems; } public static function get_nav_items() { $Items = G::$Cache->get_value("nav_items"); if (!$Items) { $QueryID = G::$DB->get_query_id(); G::$DB->prepared_query(" SELECT id, tag, title, target, tests, test_user, mandatory, initial FROM nav_items"); $Items = G::$DB->to_array("id", MYSQLI_ASSOC); G::$Cache->cache_value("nav_items", $Items, 0); G::$DB->set_query_id($QueryID); } return $Items; } /** * Return the ID of a Username * @param string Username * @return userID if exists, null otherwise */ public static function ID_from_username($name) { $digest = base64_encode(md5($name, true)); $key = "username_id_$digest"; $ID = G::$Cache->get_value($key); if ($ID == -1) { return null; } elseif ($ID === false) { G::$DB->prepared_query("SELECT ID FROM users_main WHERE Username=?", $name); if (!G::$DB->has_results()) { // cache negative hits for a while G::$Cache->cache_value($key, -1, 300); return null; } list($ID) = G::$DB->next_record(); G::$Cache->cache_value($key, $ID, 300); } return $ID; } /** * Does this ID point to an existing user? * @param integer ID * @return boolean */ public static function exists($ID) { G::$DB->prepared_query("SELECT 1 FROM users_main WHERE ID = ?", $ID); return G::$DB->has_results(); } /** * Default settings to use for SiteOptions * @return array */ public static function default_site_options() { return [ 'HttpsTracker' => true ]; } /** * Updates the site options in the database * * @param int $UserID the UserID to set the options for * @param array $NewOptions the new options to set * @return false if $NewOptions is empty, true otherwise */ public static function update_site_options($UserID, $NewOptions) { if (!is_number($UserID)) { error(0); } if (empty($NewOptions)) { return false; } $QueryID = G::$DB->get_query_id(); // Get SiteOptions G::$DB->query(" SELECT SiteOptions FROM users_info WHERE UserID = $UserID"); list($SiteOptions) = G::$DB->next_record(MYSQLI_NUM, false); $SiteOptions = unserialize_array($SiteOptions); $SiteOptions = array_merge(static::default_site_options(), $SiteOptions); // Get HeavyInfo $HeavyInfo = Users::user_heavy_info($UserID); // Insert new/replace old options $SiteOptions = array_merge($SiteOptions, $NewOptions); $HeavyInfo = array_merge($HeavyInfo, $NewOptions); // Update DB G::$DB->query(" UPDATE users_info SET SiteOptions = '".db_string(serialize($SiteOptions))."' WHERE UserID = $UserID"); G::$DB->set_query_id($QueryID); // Update cache G::$Cache->cache_value("user_info_heavy_$UserID", $HeavyInfo, 0); // Update G::$LoggedUser if the options are changed for the current if (G::$LoggedUser['ID'] == $UserID) { G::$LoggedUser = array_merge(G::$LoggedUser, $NewOptions); G::$LoggedUser['ID'] = $UserID; // We don't want to allow userid switching } return true; } /** * Generates a check list of release types, ordered by the user or default * @param array $SiteOptions * @param boolean $Default Returns the default list if true */ public static function release_order(&$SiteOptions, $Default = false) { global $ReleaseTypes; $RT = $ReleaseTypes + [ 1024 => 'Guest Appearance', 1023 => 'Remixed By', 1022 => 'Composition', 1021 => 'Produced By']; if ($Default || empty($SiteOptions['SortHide'])) { $Sort =& $RT; $Defaults = !empty($SiteOptions['HideTypes']); } else { $Sort =& $SiteOptions['SortHide']; $MissingTypes = array_diff_key($RT, $Sort); if (!empty($MissingTypes)) { foreach (array_keys($MissingTypes) as $Missing) { $Sort[$Missing] = 0; } } } foreach ($Sort as $Key => $Val) { if (isset($Defaults)) { $Checked = $Defaults && isset($SiteOptions['HideTypes'][$Key]) ? ' checked="checked"' : ''; } else { if (!isset($RT[$Key])) { continue; } $Checked = $Val ? ' checked="checked"' : ''; $Val = $RT[$Key]; } $ID = $Key. '_' . (int)(!!$Checked); // The HTML is indented this far for proper indentation in the generated HTML // on user.php?action=edit ?>
  • ShortForm $SecondaryClasses = [ '23' => 'FLS', // First Line Support '30' => 'IN', // Interviewer '31' => 'TC', // Torrent Celebrity '32' => 'D', // Designer '33' => 'ST', // Security Team '37' => 'AR', // Archive Team '36' => 'AT', // Alpha Team '48' => 'BT', // Beta TEam '38' => 'CT', // Charlie Team '39' => 'DT', // Delta Team ]; if ($UserID == 0) { return 'System'; } $UserInfo = self::user_info($UserID); if ($UserInfo['Username'] == '') { return "Unknown [$UserID]"; } $Str = ''; $Username = $UserInfo['Username']; $Paranoia = $UserInfo['Paranoia']; if ($UserInfo['Class'] < $Classes[MOD]['Level']) { $OverrideParanoia = check_perms('users_override_paranoia', $UserInfo['Class']); } else { // Don't override paranoia for mods who don't want to show their donor heart $OverrideParanoia = false; } $ShowDonorIcon = (!in_array('hide_donor_heart', $Paranoia) || $OverrideParanoia); if ($IsDonorForum) { list($Prefix, $Suffix, $HasComma) = Donations::get_titles($UserID); $Username = "$Prefix $Username" . ($HasComma ? ', ' : ' ') . "$Suffix "; } if ($Title) { $Str .= "$Username"; } else { $Str .= "$Username"; } if ($Badges) { $DonorRank = Donations::get_rank($UserID); if ($DonorRank == 0 && $UserInfo['Donor'] == 1) { $DonorRank = 1; } if ($ShowDonorIcon && $DonorRank > 0) { $IconLink = 'donate.php'; $IconImage = 'donor.png'; $IconText = 'Donor'; $DonorHeart = $DonorRank; $SpecialRank = Donations::get_special_rank($UserID); $EnabledRewards = Donations::get_enabled_rewards($UserID); $DonorRewards = Donations::get_rewards($UserID); if ($EnabledRewards['HasDonorIconMouseOverText'] && !empty($DonorRewards['IconMouseOverText'])) { $IconText = display_str($DonorRewards['IconMouseOverText']); } if ($EnabledRewards['HasDonorIconLink'] && !empty($DonorRewards['CustomIconLink'])) { $IconLink = display_str($DonorRewards['CustomIconLink']); } if ($EnabledRewards['HasCustomDonorIcon'] && !empty($DonorRewards['CustomIcon'])) { $IconImage = ImageTools::process($DonorRewards['CustomIcon'], false, 'donoricon', $UserID); } else { if ($SpecialRank === MAX_SPECIAL_RANK) { $DonorHeart = 6; } elseif ($DonorRank === 5) { $DonorHeart = 4; // Two points between rank 4 and 5 } elseif ($DonorRank >= MAX_RANK) { $DonorHeart = 5; } if ($DonorHeart === 1) { $IconImage = STATIC_SERVER . 'common/symbols/donor.png'; } else { $IconImage = STATIC_SERVER . "common/symbols/donor_{$DonorHeart}.png"; } } $Str .= "\"$IconText\""; } } $Str .= ($IsWarned && $UserInfo['Warned'] != '0000-00-00 00:00:00') ? 'Warned' : ''; $Str .= ($IsEnabled && $UserInfo['Enabled'] == 2) ? 'Banned' : ''; if ($Badges) { $ClassesDisplay = []; foreach (array_intersect_key($SecondaryClasses, $UserInfo['ExtraClasses']) as $PermID => $PermShort) { $ClassesDisplay[] = ''.$PermShort.''; } if (!empty($ClassesDisplay)) { $Str .= ' '.implode(' ', $ClassesDisplay); } } if ($Class) { if ($Title) { $Str .= ' ('.Users::make_class_string($UserInfo['PermissionID']).')'; } else { $Str .= ' ('.Users::make_class_string($UserInfo['PermissionID']).')'; } } if ($Title) { // Image proxy CTs if (check_perms('site_proxy_images') && !empty($UserInfo['Title'])) { $UserInfo['Title'] = preg_replace_callback('~src=("?)(http.+?)(["\s>])~', function($Matches) { return 'src=' . $Matches[1] . ImageTools::process($Matches[2]) . $Matches[3]; }, $UserInfo['Title']); } if ($UserInfo['Title']) { $Str .= ' ('.$UserInfo['Title'].')'; } } return $Str; } /** * Given a class ID, return its name. * * @param int $ClassID * @return string name */ public static function make_class_string($ClassID) { global $Classes; return $Classes[$ClassID]['Name']; } /** * Returns an array with User Bookmark data: group IDs, collage data, torrent data * @param string|int $UserID * @return array Group IDs, Bookmark Data, Torrent List */ public static function get_bookmarks($UserID) { $UserID = (int)$UserID; if (($Data = G::$Cache->get_value("bookmarks_group_ids_$UserID"))) { list($GroupIDs, $BookmarkData) = $Data; } else { $QueryID = G::$DB->get_query_id(); G::$DB->query(" SELECT GroupID, Sort, `Time` FROM bookmarks_torrents WHERE UserID = $UserID ORDER BY Sort, `Time` ASC"); $GroupIDs = G::$DB->collect('GroupID'); $BookmarkData = G::$DB->to_array('GroupID', MYSQLI_ASSOC); G::$DB->set_query_id($QueryID); G::$Cache->cache_value("bookmarks_group_ids_$UserID", [$GroupIDs, $BookmarkData], 3600); } $TorrentList = Torrents::get_groups($GroupIDs); return [$GroupIDs, $BookmarkData, $TorrentList]; } /** * Generate HTML for a user's avatar or just return the avatar URL * @param unknown $Avatar * @param unknown $UserID * @param unknown $Username * @param unknown $Setting * @param number $Size * @param string $ReturnHTML * @return string */ public static function show_avatar($Avatar, $UserID, $Username, $Setting, $Size = 150, $ReturnHTML = True) { $Avatar = ImageTools::process($Avatar, false, 'avatar', $UserID); $AvatarMouseOverText = ''; $FirstAvatar = ''; $SecondAvatar = ''; $EnabledRewards = Donations::get_enabled_rewards($UserID); if ($EnabledRewards['HasAvatarMouseOverText']) { $Rewards = Donations::get_rewards($UserID); $AvatarMouseOverText = $Rewards['AvatarMouseOverText']; } if (!empty($AvatarMouseOverText)) { $AvatarMouseOverText = "title=\"$AvatarMouseOverText\" alt=\"$AvatarMouseOverText\""; } else { $AvatarMouseOverText = "alt=\"$Username's avatar\""; } if ($EnabledRewards['HasSecondAvatar'] && !empty($Rewards['SecondAvatar'])) { $SecondAvatar = ImageTools::process($Rewards['SecondAvatar'], false, 'avatar2', $UserID); } $Attrs = "width=\"$Size\" $AvatarMouseOverText"; // purpose of the switch is to set $FirstAvatar (URL) // case 1 is avatars disabled switch ($Setting) { case 0: if (!empty($Avatar)) { $FirstAvatar = $Avatar; } else { $FirstAvatar = STATIC_SERVER.'common/avatars/default.png'; } break; case 2: $ShowAvatar = true; // Fallthrough case 3: if ($ShowAvatar && !empty($Avatar)) { $FirstAvatar = $Avatar; break; } switch (G::$LoggedUser['Identicons']) { case 0: $Type = 'identicon'; break; case 1: $Type = 'monsterid'; break; case 2: $Type = 'wavatar'; break; case 3: $Type = 'retro'; break; case 4: $Type = '1'; $Robot = true; break; case 5: $Type = '2'; $Robot = true; break; case 6: $Type = '3'; $Robot = true; break; default: $Type = 'identicon'; } $Rating = 'pg'; if (!$Robot) { $FirstAvatar = 'https://secure.gravatar.com/avatar/'.md5(strtolower(trim($Username)))."?s=$Size&d=$Type&r=$Rating"; } else { $FirstAvatar = 'https://robohash.org/'.md5($Username)."?set=set$Type&size={$Size}x$Size"; } break; default: $FirstAvatar = STATIC_SERVER.'common/avatars/default.png'; } // in this case, $Attrs is actually just a URL if (!$ReturnHTML) { return $FirstAvatar; } $ToReturn = '
    '; foreach ([$FirstAvatar, $SecondAvatar] as $AvatarNum => $CurAvatar) { if ($CurAvatar) { $ToReturn .= "
    "; } } $ToReturn .= '
    '; return $ToReturn; } public static function has_avatars_enabled() { global $HeavyInfo; return $HeavyInfo['DisableAvatars'] != 1; } /** * Checks whether user has autocomplete enabled * * 0 - Enabled everywhere (default), 1 - Disabled, 2 - Searches only * * @param string $Type the type of the input. * @param boolean $Output echo out HTML * @return boolean */ public static function has_autocomplete_enabled($Type, $Output = true) { $Enabled = false; if (empty(G::$LoggedUser['AutoComplete'])) { $Enabled = true; } elseif (G::$LoggedUser['AutoComplete'] !== 1) { switch ($Type) { case 'search': if (G::$LoggedUser['AutoComplete'] == 2) { $Enabled = true; } break; case 'other': if (G::$LoggedUser['AutoComplete'] != 2) { $Enabled = true; } break; } } if ($Enabled && $Output) { echo ' data-gazelle-autocomplete="true"'; } if (!$Output) { // don't return a boolean if you're echoing HTML return $Enabled; } } /** * Initiate a password reset * * @param int $UserID The user ID * @param string $Username The username * @param string $Email The email address */ public static function resetPassword($UserID, $Username, $Email) { $ResetKey = Users::make_secret(); G::$DB->prepared_query(" UPDATE users_info SET ResetKey = ?, ResetExpires = ? WHERE UserID = ?", $ResetKey, time_plus(60 * 60), $UserID); $template = G::$Twig->render('emails/password_reset.twig', [ 'Username' => $Username, 'ResetKey' => $ResetKey, 'IP' => $_SERVER['REMOTE_ADDR'], 'SITE_NAME' => SITE_NAME, 'SITE_URL' => SITE_URL ]); Misc::send_email($Email, 'Password reset information for ' . SITE_NAME, $template, 'noreply'); } /** * Removes the custom title of a user * * @param integer $ID The id of the user in users_main */ public static function removeCustomTitle($ID) { G::$DB->prepared_query("UPDATE users_main SET Title='' WHERE ID = ? ", $ID); G::$Cache->delete_value("user_info_{$ID}"); G::$Cache->delete_value("user_stats_{$ID}"); } /** * Purchases the custom title for a user * * @param integer $ID The id of the user in users_main * @param string $Title The text of the title (may contain BBcode) * @return boolean false if insufficient funds, otherwise true */ public static function setCustomTitle($ID, $Title) { G::$DB->prepared_query("UPDATE users_main SET Title = ? WHERE ID = ?", $Title, $ID); if (G::$DB->affected_rows() == 1) { G::$Cache->delete_value("user_info_{$ID}"); G::$Cache->delete_value("user_stats_{$ID}"); return true; } return false; } /** * Checks whether a user is allowed to purchase an invite. User classes up to Elite are capped, * users above this class will always return true. * * @param integer $ID The id of the user in users_main * @param integer $MinClass Minimum class level necessary to purchase invites * @return boolean false if insufficient funds, otherwise true */ public static function canPurchaseInvite($ID, $MinClass) { $heavy = self::user_heavy_info($ID); if ($heavy['DisableInvites']) { return false; } $info = self::user_info($ID); return $info['EffectiveClass'] >= $MinClass; } /** * Get the count of enabled users. * * @return integer Number of enabled users (this is cached). */ public static function get_enabled_users_count() { $count = G::$Cache->get_value('stats_user_count'); if (!$count) { G::$DB->query("SELECT count(*) FROM users_main WHERE Enabled = '1'"); list($count) = G::$DB->next_record(); G::$Cache->cache_value('stats_user_count', $count, 0); } return $count; } /** * Flush the count of enabled users. Call a user is enabled or disabled. */ public static function flush_enabled_users_count() { G::$Cache->delete_value('stats_user_count'); } }