/** * Initialize a new instance of the {@link CategoryCollection} class. * * @param Gdn_SQLDriver|null $sql The database layer dependency. * @param Gdn_Cache|null $cache The cache layer dependency. */ public function __construct(Gdn_SQLDriver $sql = null, Gdn_Cache $cache = null) { if ($sql === null) { $sql = Gdn::sql(); } $this->sql = $sql; if ($cache === null) { $cache = Gdn::cache(); } $this->cache = $cache; }
protected function getCount($Table) { // Try and get the count from the cache. $Key = "{$Table}.CountRows"; $Count = Gdn::cache()->get($Key); if ($Count !== Gdn_Cache::CACHEOP_FAILURE) { return $Count; } // The count wasn't in the cache so grab it from the table. $Count = Gdn::sql()->select($Table . 'ID', 'count', 'CountRows')->from($Table)->get()->value('CountRows'); // Save the value to the cache. Gdn::cache()->store($Key, $Count, array(Gdn_Cache::FEATURE_EXPIRY => 5 * 60 + mt_rand(0, 30))); return $Count; }
/** * Initialize a new instance of the {@link CategoryCollection} class. * * @param Gdn_SQLDriver|null $sql The database layer dependency. * @param Gdn_Cache|null $cache The cache layer dependency. */ public function __construct(Gdn_SQLDriver $sql = null, Gdn_Cache $cache = null) { if ($sql === null) { $sql = Gdn::sql(); } $this->sql = $sql; if ($cache === null) { $cache = Gdn::cache(); } $this->cache = $cache; $this->setStaticCalculator([$this, 'defaultCalculator']); $this->setUserCalculator(function (&$category) { // do nothing }); }
/** * Save a message. * * @param array $FormPostValues Message data. * @param bool $Settings * @return int The MessageID. */ public function save($FormPostValues, $Settings = false) { // The "location" is packed into a single input with a / delimiter. Need to explode it into three different fields for saving $Location = val('Location', $FormPostValues, ''); if ($Location != '') { $Location = explode('/', $Location); $Application = val(0, $Location, ''); if (in_array($Application, $this->_SpecialLocations)) { $FormPostValues['Application'] = null; $FormPostValues['Controller'] = $Application; $FormPostValues['Method'] = null; } else { $FormPostValues['Application'] = $Application; $FormPostValues['Controller'] = val(1, $Location, ''); $FormPostValues['Method'] = val(2, $Location, ''); } } Gdn::cache()->remove('Messages'); return parent::save($FormPostValues, $Settings); }
/** * Increments view count for the specified discussion. * * @since 2.0.0 * @access public * * @param int $DiscussionID Unique ID of discussion to get +1 view. */ public function addView($DiscussionID) { $IncrementBy = 0; if (c('Vanilla.Views.Denormalize', false) && Gdn::cache()->activeEnabled() && Gdn::cache()->Type() != Gdn_Cache::CACHE_TYPE_NULL) { $WritebackLimit = c('Vanilla.Views.DenormalizeWriteback', 10); $CacheKey = sprintf(DiscussionModel::CACHE_DISCUSSIONVIEWS, $DiscussionID); // Increment. If not success, create key. $Views = Gdn::cache()->increment($CacheKey); if ($Views === Gdn_Cache::CACHEOP_FAILURE) { Gdn::cache()->store($CacheKey, 1); } // Every X views, writeback to Discussions if ($Views % $WritebackLimit == 0) { $IncrementBy = floor($Views / $WritebackLimit) * $WritebackLimit; Gdn::cache()->Decrement($CacheKey, $IncrementBy); } } else { $IncrementBy = 1; } if ($IncrementBy) { $this->SQL->update('Discussion')->set('CountViews', "CountViews + {$IncrementBy}", false)->where('DiscussionID', $DiscussionID)->put(); } }
/** * Save the provided library's data to the on disk location. * * @param string $CacheName name of cache library * @return void */ public static function saveCache($CacheName) { if ($CacheName != 'locale') { return; } if (!array_key_exists($CacheName, self::$Caches)) { return false; } $UseCache = Gdn::cache()->type() == Gdn_Cache::CACHE_TYPE_MEMORY && Gdn::cache()->activeEnabled(); if ($UseCache) { $CacheKey = sprintf(Gdn_LibraryMap::CACHE_CACHE_NAME_FORMAT, $CacheName); $Stored = Gdn::cache()->store($CacheKey, self::$Caches[$CacheName]['cache']); } else { $FileName = self::$Caches[$CacheName]['ondisk']; $CacheContents = ""; foreach (self::$Caches[$CacheName]['cache'] as $SectionTitle => $SectionData) { $CacheContents .= "[{$SectionTitle}]\n"; foreach ($SectionData as $StoreKey => $StoreValue) { $CacheContents .= "{$StoreKey} = \"{$StoreValue}\"\n"; } } try { // Fix slashes to get around parse_ini_file issue that drops off \ when loading network file. $CacheContents = str_replace("\\", "/", $CacheContents); Gdn_FileSystem::saveFile(PATH_CACHE . DS . $FileName, $CacheContents, LOCK_EX); } catch (Exception $e) { } } }
/** * * * @return bool|null * @throws Exception */ public function save() { if (!$this->Dirty) { return null; } $this->EventArguments['ConfigDirty'] =& $this->Dirty; $this->EventArguments['ConfigNoSave'] = false; $this->EventArguments['ConfigType'] = $this->Type; $this->EventArguments['ConfigSource'] = $this->Source; $this->EventArguments['ConfigData'] = $this->Settings; $this->fireEvent('BeforeSave'); if ($this->EventArguments['ConfigNoSave']) { $this->Dirty = false; return true; } // Check for and fire callback if one exists if ($this->Callback && is_callable($this->Callback)) { $CallbackOptions = array(); if (!is_array($this->CallbackOptions)) { $this->CallbackOptions = array(); } $CallbackOptions = array_merge($CallbackOptions, $this->CallbackOptions, array('ConfigDirty' => $this->Dirty, 'ConfigType' => $this->Type, 'ConfigSource' => $this->Source, 'ConfigData' => $this->Settings, 'SourceObject' => $this)); $ConfigSaved = call_user_func($this->Callback, $CallbackOptions); if ($ConfigSaved) { $this->Dirty = false; return true; } } switch ($this->Type) { case 'file': if (empty($this->Source)) { trigger_error(errorMessage('You must specify a file path to be saved.', 'Configuration', 'Save'), E_USER_ERROR); } $CheckWrite = $this->Source; if (!file_exists($CheckWrite)) { $CheckWrite = dirname($CheckWrite); } if (!is_writable($CheckWrite)) { throw new Exception(sprintf(t("Unable to write to config file '%s' when saving."), $this->Source)); } $Group = $this->Group; $Data =& $this->Settings; ksort($Data); // Check for the case when the configuration is the group. if (is_array($Data) && count($Data) == 1 && array_key_exists($Group, $Data)) { $Data = $Data[$Group]; } // Do a sanity check on the config save. if ($this->Source == Gdn::config()->defaultPath()) { // Log root config changes try { $LogData = $this->Initial; $LogData['_New'] = $this->Settings; LogModel::insert('Edit', 'Configuration', $LogData); } catch (Exception $Ex) { } if (!isset($Data['Database'])) { if ($Pm = Gdn::pluginManager()) { $Pm->EventArguments['Data'] = $Data; $Pm->EventArguments['Backtrace'] = debug_backtrace(); $Pm->fireEvent('ConfigError'); } return false; } } // Write config data to string format, ready for saving $FileContents = Gdn_Configuration::format($Data, array('VariableName' => $Group, 'WrapPHP' => true, 'ByLine' => true)); if ($FileContents === false) { trigger_error(errorMessage('Failed to define configuration file contents.', 'Configuration', 'Save'), E_USER_ERROR); } // Save to cache if we're into that sort of thing $FileKey = sprintf(Gdn_Configuration::CONFIG_FILE_CACHE_KEY, $this->Source); if ($this->Configuration && $this->Configuration->caching() && Gdn::cache()->type() == Gdn_Cache::CACHE_TYPE_MEMORY && Gdn::cache()->activeEnabled()) { $CachedConfigData = Gdn::cache()->store($FileKey, $Data, array(Gdn_Cache::FEATURE_NOPREFIX => true, Gdn_Cache::FEATURE_EXPIRY => 3600)); } $TmpFile = tempnam(PATH_CONF, 'config'); $Result = false; if (file_put_contents($TmpFile, $FileContents) !== false) { chmod($TmpFile, 0775); $Result = rename($TmpFile, $this->Source); } if ($Result) { if (function_exists('apc_delete_file')) { // This fixes a bug with some configurations of apc. @apc_delete_file($this->Source); } elseif (function_exists('opcache_invalidate')) { @opcache_invalidate($this->Source); } } $this->Dirty = false; return $Result; break; case 'json': case 'array': case 'string': /** * How would these even save? String config data must be handled by * an event hook or callback, if at all. */ $this->Dirty = false; return false; break; } }
/** * Takes a set of discussion identifiers and returns their comment counts in the same order. */ public function getCommentCounts() { $this->AllowJSONP(true); $vanilla_identifier = val('vanilla_identifier', $_GET); if (!is_array($vanilla_identifier)) { $vanilla_identifier = array($vanilla_identifier); } $vanilla_identifier = array_unique($vanilla_identifier); $FinalData = array_fill_keys($vanilla_identifier, 0); $Misses = array(); $CacheKey = 'embed.comments.count.%s'; $OriginalIDs = array(); foreach ($vanilla_identifier as $ForeignID) { $HashedForeignID = ForeignIDHash($ForeignID); // Keep record of non-hashed identifiers for the reply $OriginalIDs[$HashedForeignID] = $ForeignID; $RealCacheKey = sprintf($CacheKey, $HashedForeignID); $Comments = Gdn::cache()->get($RealCacheKey); if ($Comments !== Gdn_Cache::CACHEOP_FAILURE) { $FinalData[$ForeignID] = $Comments; } else { $Misses[] = $HashedForeignID; } } if (sizeof($Misses)) { $CountData = Gdn::sql()->select('ForeignID, CountComments')->from('Discussion')->where('Type', 'page')->whereIn('ForeignID', $Misses)->get()->resultArray(); foreach ($CountData as $Row) { // Get original identifier to send back $ForeignID = $OriginalIDs[$Row['ForeignID']]; $FinalData[$ForeignID] = $Row['CountComments']; // Cache using the hashed identifier $RealCacheKey = sprintf($CacheKey, $Row['ForeignID']); Gdn::cache()->store($RealCacheKey, $Row['CountComments'], array(Gdn_Cache::FEATURE_EXPIRY => 60)); } } $this->setData('CountData', $FinalData); $this->DeliveryMethod = DELIVERY_METHOD_JSON; $this->DeliveryType = DELIVERY_TYPE_DATA; $this->render(); }
/** * Log the access of a resource. * * Since resources can be accessed with every page view this event will only log when the cache is enabled * and once every five minutes. * * @param string $event The name of the event to log. * @param string $level The log level of the event. * @param string $message The log message format. * @param array $context Additional information to pass to the event. */ public static function logAccess($event, $level, $message, $context = array()) { // Throttle the log access to 1 event every 5 minutes. if (Gdn::cache()->activeEnabled()) { $userID = Gdn::session()->UserID; $path = Gdn::request()->path(); $key = "log:{$event}:{$userID}:{$path}"; if (Gdn::cache()->get($key) === false) { self::event($event, $level, $message, $context); Gdn::cache()->store($key, time(), array(Gdn_Cache::FEATURE_EXPIRY => 300)); } } }
/** * * * @throws Exception */ public function ping() { $start = microtime(true); $this->setData('pong', true); $this->MasterView = 'empty'; $this->CssClass = 'Home'; $valid = true; // Test the cache. if (Gdn::cache()->activeEnabled()) { $k = betterRandomString(20); Gdn::cache()->store($k, 1); Gdn::cache()->increment($k, 1); $v = Gdn::cache()->get($k); if ($v !== 2) { $valid = false; $this->setData('cache', false); } else { $this->setData('cache', true); } } else { $this->setData('cache', 'disabled'); } // Test the db. try { $users = Gdn::sql()->get('User', 'UserID', 'asc', 1); $this->setData('database', true); } catch (Exception $ex) { $this->setData('database', false); $valid = false; } $this->EventArguments['Valid'] =& $valid; $this->fireEvent('Ping'); if (!$valid) { $this->statusCode(500); } $time = microtime(true) - $start; $this->setData('time', Gdn_Format::timespan($time)); $this->setData('time_s', $time); $this->setData('valid', $valid); $this->title('Ping'); $this->render(); }
/** * Sets UserMeta data to the UserMeta table. * * This method takes a UserID, Key, and Value, and attempts to set $Key = $Value for $UserID. * $Key can be an SQL wildcard, thereby allowing multiple variations of a $Key to be set. $UserID * can be an array, thereby allowing multiple users' $Keys to be set to the same $Value. * * ++ Before any queries are run, $Key is converted to its fully qualified format (Plugin.<PluginName> prepended) * ++ to prevent collisions in the meta table when multiple plugins have similar key names. * * If $Value == null, the matching row(s) are deleted instead of updated. * * @param $UserID int UserID or array of UserIDs * @param $Key string relative user key * @param $Value mixed optional value to set, null to delete * @return void */ public function setUserMeta($UserID, $Key, $Value = null) { if (Gdn::cache()->activeEnabled()) { if (is_array($UserID)) { foreach ($UserID as $ID) { $this->SetUserMeta($ID, $Key, $Value); } return; } $UserMeta = $this->GetUserMeta($UserID); if (!stristr($Key, '%')) { if ($Value === null) { unset($UserMeta[$Key]); } else { $UserMeta[$Key] = $Value; } } else { $MatchKey = str_replace('%', '*', $Key); foreach ($UserMeta as $UserMetaKey => $UserMetaValue) { if (fnmatch($MatchKey, $UserMetaKey)) { if ($Value === null) { unset($UserMeta[$UserMetaKey]); } else { $UserMeta[$UserMetaKey] = $Value; } } } } $CacheKey = 'UserMeta_' . $UserID; Gdn::cache()->store($CacheKey, $UserMeta, array(Gdn_Cache::FEATURE_EXPIRY => 3600)); // Update the DB. $this->SQL->reset(); if ($Value === null) { $Q = $this->SQL->where('UserID', $UserID); if (stristr($Key, '%')) { $Q->like('Name', $Key); } else { $Q->where('Name', $Key); } $Q->delete('UserMeta'); } else { $Px = $this->SQL->Database->DatabasePrefix; $Sql = "insert {$Px}UserMeta (UserID, Name, Value) values(:UserID, :Name, :Value) on duplicate key update Value = :Value1"; $Params = array(':UserID' => $UserID, ':Name' => $Key, ':Value' => $Value, ':Value1' => $Value); $this->Database->query($Sql, $Params); } return; } if (is_null($Value)) { // Delete $UserMetaQuery = Gdn::sql(); if (is_array($UserID)) { $UserMetaQuery->whereIn('UserID', $UserID); } else { $UserMetaQuery->where('UserID', $UserID); } if (stristr($Key, '%')) { $UserMetaQuery->like('Name', $Key); } else { $UserMetaQuery->where('Name', $Key); } $UserMetaQuery->delete('UserMeta'); } else { // Set if (!is_array($UserID)) { $UserID = array($UserID); } foreach ($UserID as $UID) { try { Gdn::sql()->insert('UserMeta', array('UserID' => $UID, 'Name' => $Key, 'Value' => $Value)); } catch (Exception $e) { Gdn::sql()->update('UserMeta', array('Value' => $Value), array('UserID' => $UID, 'Name' => $Key))->put(); } } } return; }
/** * Get the permissions for one or more roles. * * @param int $roleID The role to get the permissions for. * @return array Returns a permission array suitable for use in a session. */ public function getPermissionsByRole($roleID) { $inc = Gdn::userModel()->getPermissionsIncrement(); $key = "perms:{$inc}:role:{$roleID}"; $permissions = Gdn::cache()->get($key); if ($permissions === Gdn_Cache::CACHEOP_FAILURE) { $sql = clone $this->SQL; $sql->reset(); // Select all of the permission columns. $permissionColumns = $this->permissionColumns(); foreach ($permissionColumns as $columnName => $value) { $sql->select('p.`' . $columnName . '`', 'MAX'); } $sql->from('Permission p')->where('p.RoleID', $roleID)->select(array('p.JunctionTable', 'p.JunctionColumn', 'p.JunctionID'))->groupBy(array('p.JunctionTable', 'p.JunctionColumn', 'p.JunctionID')); $permissions = $sql->get()->resultArray(); $permissions = UserModel::compilePermissions($permissions); Gdn::cache()->store($key, $permissions); } return $permissions; }
/** * Select content based on its Score. * * @param array|int $Parameters * @return array|false */ protected function selectByScore($Parameters) { if (!is_array($Parameters)) { $MinScore = $Parameters; } else { $MinScore = val('Score', $Parameters, null); } if (!is_integer($MinScore)) { $MinScore = false; } // Check cache $SelectorScoreCacheKey = "modules.promotedcontent.score.{$MinScore}"; $Content = Gdn::cache()->get($SelectorScoreCacheKey); if ($Content == Gdn_Cache::CACHEOP_FAILURE) { // Get matching Discussions $Discussions = array(); if ($this->ShowDiscussions()) { $Discussions = Gdn::sql()->select('d.*')->from('Discussion d')->orderBy('DateInserted', 'DESC')->limit($this->Limit); if ($MinScore !== false) { $Discussions->where('Score >', $MinScore); } $Discussions = $Discussions->get()->result(DATASET_TYPE_ARRAY); } // Get matching Comments $Comments = array(); if ($this->ShowComments()) { $Comments = Gdn::sql()->select('c.*')->from('Comment c')->orderBy('DateInserted', 'DESC')->limit($this->Limit); if ($MinScore !== false) { $Comments->where('Score >', $MinScore); } $Comments = $Comments->get()->result(DATASET_TYPE_ARRAY); $this->JoinCategory($Comments); } // Interleave $Content = $this->Union('DateInserted', array('Discussion' => $Discussions, 'Comment' => $Comments)); $this->processContent($Content); // Add result to cache Gdn::cache()->store($SelectorScoreCacheKey, $Content, array(Gdn_Cache::FEATURE_EXPIRY => $this->Expiry)); } $this->Security($Content); $this->Condense($Content, $this->Limit); return $Content; }
/** * * * @param string $Sql * @param null $InputParameters * @param array $Options * @return Gdn_DataSet|object|string */ public function query($Sql, $InputParameters = null, $Options = array()) { $Trace = debug_backtrace(); $Method = ''; foreach ($Trace as $Info) { $Class = val('class', $Info, ''); if ($Class === '' || stringEndsWith($Class, 'Model', true) || stringEndsWith($Class, 'Plugin', true)) { $Type = val('type', $Info, ''); $Method = $Class . $Type . $Info['function'] . '(' . self::formatArgs($Info['args']) . ')'; break; } } // Save the query for debugging // echo '<br />adding to queries: '.$Sql; $Query = array('Sql' => $Sql, 'Parameters' => $InputParameters, 'Method' => $Method); $SaveQuery = true; if (isset($Options['Cache'])) { $CacheKeys = (array) $Options['Cache']; $Cache = array(); $AllSet = true; foreach ($CacheKeys as $CacheKey) { $Value = Gdn::cache()->get($CacheKey); $CacheValue = $Value !== Gdn_Cache::CACHEOP_FAILURE; $AllSet &= $CacheValue; $Cache[$CacheKey] = $CacheValue; } $SaveQuery = !$AllSet; $Query['Cache'] = $Cache; } // Start the Query Timer $TimeStart = now(); $Result = parent::query($Sql, $InputParameters, $Options); $Query = array_merge($this->LastInfo, $Query); // Aggregate the query times $TimeEnd = now(); $this->_ExecutionTime += $TimeEnd - $TimeStart; if ($SaveQuery && !stringBeginsWith($Sql, 'set names')) { $Query['Time'] = $TimeEnd - $TimeStart; $this->_Queries[] = $Query; } return $Result; }
/** * Discussion view counter. * * @param $Sender * @param $Args */ public function gdn_statistics_tick_handler($Sender, $Args) { $Path = Gdn::request()->post('Path'); $Args = Gdn::request()->post('Args'); parse_str($Args, $Args); $ResolvedPath = trim(Gdn::request()->post('ResolvedPath'), '/'); $ResolvedArgs = Gdn::request()->post('ResolvedArgs'); $DiscussionID = null; $DiscussionModel = new DiscussionModel(); // Comment permalink if ($ResolvedPath == 'vanilla/discussion/comment') { $CommentID = val('CommentID', $ResolvedArgs); $CommentModel = new CommentModel(); $Comment = $CommentModel->getID($CommentID); $DiscussionID = val('DiscussionID', $Comment); } elseif ($ResolvedPath == 'vanilla/discussion/index') { $DiscussionID = val('DiscussionID', $ResolvedArgs, null); } elseif ($ResolvedPath == 'vanilla/discussion/embed') { $ForeignID = val('vanilla_identifier', $Args); if ($ForeignID) { // This will be hit a lot so let's try caching it... $Key = "DiscussionID.ForeignID.page.{$ForeignID}"; $DiscussionID = Gdn::cache()->get($Key); if (!$DiscussionID) { $Discussion = $DiscussionModel->getForeignID($ForeignID, 'page'); $DiscussionID = val('DiscussionID', $Discussion); Gdn::cache()->store($Key, $DiscussionID, array(Gdn_Cache::FEATURE_EXPIRY, 1800)); } } } if ($DiscussionID) { $DiscussionModel->addView($DiscussionID); } }
/** * Return the number of unanswered questions. * * @return int */ public function getUnansweredCount() { // TODO: Dekludge this when category permissions are refactored (tburry). $cacheKey = Gdn::request()->webRoot() . '/QnA-UnansweredCount'; $questionCount = Gdn::cache()->get($cacheKey); if ($questionCount === Gdn_Cache::CACHEOP_FAILURE) { $questionCount = Gdn::sql()->beginWhereGroup()->where('QnA', null)->orWhereIn('QnA', array('Unanswered', 'Rejected'))->endWhereGroup()->getCount('Discussion', array('Type' => 'Question')); Gdn::cache()->store($cacheKey, $questionCount, array(Gdn_Cache::FEATURE_EXPIRY => 15 * 60)); } // Check to see if another plugin can handle this. $this->EventArguments['questionCount'] =& $questionCount; $this->fireEvent('unansweredCount'); return $questionCount; }
/** * Wrapper for GetCountWhere that takes care of caching specific operation counts. * * @param string $Operation Comma-delimited list of operation types to get (sum of) counts for. * @return int */ public function getOperationCount($Operation) { if ($Operation == 'edits') { $Operation = array('edit', 'delete'); } else { $Operation = explode(',', $Operation); } sort($Operation); array_map('ucfirst', $Operation); $CacheKey = 'Moderation.LogCount.' . implode('.', $Operation); $Count = Gdn::cache()->get($CacheKey); if ($Count === Gdn_Cache::CACHEOP_FAILURE) { $Count = $this->getCountWhere(array('Operation' => $Operation)); Gdn::cache()->store($CacheKey, $Count, array(Gdn_Cache::FEATURE_EXPIRY => 300)); } return $Count; }
/** * * * @param string $Key Cache key. * @param array $Options * @return mixed */ protected function fallback($Key, $Options) { $Fallback = val(Gdn_Cache::FEATURE_FALLBACK, $Options, null); if (is_null($Fallback)) { return Gdn_Cache::CACHEOP_FAILURE; } $FallbackType = array_shift($Fallback); switch ($FallbackType) { case 'query': $QueryFallbackField = array_shift($Fallback); $QueryFallbackCode = array_shift($Fallback); $FallbackResult = Gdn::database()->query($QueryFallbackCode); if ($FallbackResult->numRows()) { if (!is_null($QueryFallbackField)) { $FallbackResult = val($QueryFallbackField, $FallbackResult->firstRow(DATASET_TYPE_ARRAY)); } else { $FallbackResult = $FallbackResult->resultArray(); } } break; case 'callback': $CallbackFallbackMethod = array_shift($Fallback); $CallbackFallbackArgs = $Fallback; $FallbackResult = call_user_func_array($CallbackFallbackMethod, $CallbackFallbackArgs); break; } Gdn::cache()->store($Key, $FallbackResult); return $FallbackResult; }
/** * * * @param $Users * @param string $UserIDColumn * @param string $RolesColumn */ public static function setUserRoles(&$Users, $UserIDColumn = 'UserID', $RolesColumn = 'Roles') { $UserIDs = array_unique(ConsolidateArrayValuesByKey($Users, $UserIDColumn)); // Try and get all of the mappings from the cache. $Keys = array(); foreach ($UserIDs as $UserID) { $Keys[$UserID] = formatString(UserModel::USERROLES_KEY, array('UserID' => $UserID)); } $UserRoles = Gdn::cache()->get($Keys); if (!is_array($UserRoles)) { $UserRoles = array(); } // Grab all of the data that doesn't exist from the DB. $MissingIDs = array(); foreach ($Keys as $UserID => $Key) { if (!array_key_exists($Key, $UserRoles)) { $MissingIDs[$UserID] = $Key; } } if (count($MissingIDs) > 0) { $DbUserRoles = Gdn::sql()->select('ur.*')->from('UserRole ur')->whereIn('ur.UserID', array_keys($MissingIDs))->get()->resultArray(); $DbUserRoles = Gdn_DataSet::Index($DbUserRoles, 'UserID', array('Unique' => false)); // Store the user role mappings. foreach ($DbUserRoles as $UserID => $Rows) { $RoleIDs = consolidateArrayValuesByKey($Rows, 'RoleID'); $Key = $Keys[$UserID]; Gdn::cache()->store($Key, $RoleIDs); $UserRoles[$Key] = $RoleIDs; } } $AllRoles = self::roles(); // roles indexed by role id. // Skip personal info roles if (!checkPermission('Garden.PersonalInfo.View')) { $AllRoles = array_filter($AllRoles, 'self::FilterPersonalInfo'); } // Join the users. foreach ($Users as &$User) { $UserID = val($UserIDColumn, $User); $Key = $Keys[$UserID]; $RoleIDs = val($Key, $UserRoles, array()); $Roles = array(); foreach ($RoleIDs as $RoleID) { if (!array_key_exists($RoleID, $AllRoles)) { continue; } $Roles[$RoleID] = $AllRoles[$RoleID]['Name']; } setValue($RolesColumn, $User, $Roles); } }
/** * Grab and update the category cache * * @since 2.0.18 * @access public * @param int $ID * @param array $Data */ public static function setCache($ID = false, $Data = false) { self::instance()->collection->refreshCache((int) $ID); $Categories = Gdn::cache()->get(self::CACHE_KEY); self::$Categories = null; if (!$Categories) { return; } // Extract actual category list, remove key if malformed if (!$ID || !is_array($Categories) || !array_key_exists('categories', $Categories)) { Gdn::cache()->remove(self::CACHE_KEY); return; } $Categories = $Categories['categories']; // Check for category in list, otherwise remove key if not found if (!array_key_exists($ID, $Categories)) { Gdn::cache()->remove(self::CACHE_KEY); return; } $Category = $Categories[$ID]; $Category = array_merge($Category, $Data); $Categories[$ID] = $Category; // Update memcache entry self::$Categories = $Categories; unset($Categories); self::BuildCache($ID); self::JoinUserData(self::$Categories, true); }
<div id="Sql" class="DebugInfo"> <h2>Debug Information</h2> <?php // Add the canonical Url. if (method_exists($Sender, 'CanonicalUrl')) { $CanonicalUrl = htmlspecialchars($Sender->canonicalUrl(), ENT_COMPAT, c('Garden.Charset', 'UTF-8')); echo '<div class="CanonicalUrl"><b>' . t('Canonical Url') . "</b>: <a href=\"{$CanonicalUrl}\" accesskey=\"r\">{$CanonicalUrl}</a></div>"; } ?> <?php // Add some cache info. if (Gdn::cache()->activeEnabled()) { echo '<h3>Cache Information</h3>'; echo '<pre>'; echo '<b>Cache Revision</b>: ' . Gdn::cache()->GetRevision() . "\n"; echo '<b>Permissions Revision</b>: ' . Gdn::userModel()->GetPermissionsIncrement() . "\n"; if (property_exists('Gdn_Cache', 'GetCount')) { echo '<b>Cache Gets</b>: ' . sprintf('%s in %ss', Gdn_Cache::$GetCount, Gdn_Cache::$GetTime); } echo '</pre>'; if (property_exists('Gdn_Cache', 'trackGet') && sizeof(Gdn_Cache::$trackGet)) { uasort(Gdn_Cache::$trackGet, function ($a, $b) { return $b['hits'] - $a['hits']; }); $numKeys = sizeof(Gdn_Cache::$trackGet); $duplicateGets = 0; $wastedBytes = 0; $totalBytes = 0; foreach (Gdn_Cache::$trackGet as $key => $keyData) { if ($keyData['hits'] > 1) {
/** * * * @param null $SearchPaths */ public function clearPluginCache($SearchPaths = null) { if (!is_null($SearchPaths)) { if (!is_array($SearchPaths)) { $SearchPaths = array($SearchPaths); } } else { $SearchPaths = $this->searchPaths(); } foreach ($SearchPaths as $SearchPath => $SearchPathName) { $SearchPathCacheKey = "Garden.Plugins.PathCache.{$SearchPath}"; if ($this->Apc) { apc_delete($SearchPathCacheKey); } else { Gdn::cache()->remove($SearchPathCacheKey, array(Gdn_Cache::FEATURE_NOPREFIX => true)); } } }
/** * Increments overall pageview view count. * * @since 2.1a * @access public */ public function addView($ViewType = 'normal') { // Add a pageview entry. $TimeSlot = date('Ymd'); $Px = Gdn::database()->DatabasePrefix; $Views = 1; $EmbedViews = 0; try { if (C('Garden.Analytics.Views.Denormalize', false) && Gdn::cache()->activeEnabled() && Gdn::cache()->type() != Gdn_Cache::CACHE_TYPE_NULL) { $CacheKey = "QueryCache.Analytics.CountViews"; // Increment. If not success, create key. $Incremented = Gdn::cache()->increment($CacheKey); if ($Incremented === Gdn_Cache::CACHEOP_FAILURE) { Gdn::cache()->store($CacheKey, 1); } // Get current cache value $Views = Gdn::cache()->get($CacheKey); if ($ViewType == 'embed') { $EmbedCacheKey = "QueryCache.Analytics.CountEmbedViews"; // Increment. If not success, create key. $EmbedIncremented = Gdn::cache()->increment($EmbedCacheKey); if ($EmbedIncremented === Gdn_Cache::CACHEOP_FAILURE) { Gdn::cache()->store($EmbedCacheKey, 1); } // Get current cache value $EmbedViews = Gdn::cache()->get($EmbedCacheKey); } // Every X views, writeback to AnalyticsLocal $DenormalizeWriteback = C('Garden.Analytics.Views.DenormalizeWriteback', 10); if ($Views % $DenormalizeWriteback == 0) { Gdn::controller()->setData('WritebackViews', $Views); Gdn::controller()->setData('WritebackEmbed', $EmbedViews); Gdn::database()->query("insert into {$Px}AnalyticsLocal (TimeSlot, Views, EmbedViews) values (:TimeSlot, {$Views}, {$EmbedViews})\n on duplicate key update\n Views = COALESCE(Views, 0)+{$Views},\n EmbedViews = COALESCE(EmbedViews, 0)+{$EmbedViews}", array(':TimeSlot' => $TimeSlot)); // ... and get rid of those views from the keys if ($Views) { Gdn::cache()->decrement($CacheKey, $Views); } if ($EmbedViews) { Gdn::cache()->decrement($EmbedCacheKey, $EmbedViews); } } } else { $ExtraViews = 1; $ExtraEmbedViews = $ViewType == 'embed' ? 1 : 0; Gdn::database()->query("insert into {$Px}AnalyticsLocal (TimeSlot, Views, EmbedViews) values (:TimeSlot, {$ExtraViews}, {$ExtraEmbedViews})\n on duplicate key update\n Views = COALESCE(Views, 0)+{$ExtraViews},\n EmbedViews = COALESCE(EmbedViews, 0)+{$ExtraEmbedViews}", array(':TimeSlot' => $TimeSlot)); } } catch (Exception $Ex) { if (Gdn::session()->checkPermission('Garden.Settings.Manage')) { throw $Ex; } } }
/** * * * @return bool|int|mixed */ public function getPermissionsIncrement() { $PermissionsIncrementKey = self::INC_PERMISSIONS_KEY; $PermissionsKeyValue = Gdn::cache()->get($PermissionsIncrementKey); if (!$PermissionsKeyValue) { $Stored = Gdn::cache()->store($PermissionsIncrementKey, 1); return $Stored ? 1 : false; } return $PermissionsKeyValue; }
$Construct->Table('Media')->PrimaryKey('MediaID')->Column('Name', 'varchar(255)')->Column('Path', 'varchar(255)')->Column('Type', 'varchar(128)')->Column('Size', 'int(11)')->Column('InsertUserID', 'int(11)')->Column('DateInserted', 'datetime')->Column('ForeignID', 'int(11)', TRUE)->Column('ForeignTable', 'varchar(24)', TRUE)->Column('ImageWidth', 'usmallint', NULL)->Column('ImageHeight', 'usmallint', NULL)->Column('ThumbWidth', 'usmallint', NULL)->Column('ThumbHeight', 'usmallint', NULL)->Column('ThumbPath', 'varchar(255)', NULL)->Set(FALSE, FALSE); // Merge backup. $Construct->Table('UserMerge')->PrimaryKey('MergeID')->Column('OldUserID', 'int', FALSE, 'key')->Column('NewUserID', 'int', FALSE, 'key')->Column('DateInserted', 'datetime')->Column('InsertUserID', 'int')->Column('DateUpdated', 'datetime', TRUE)->Column('UpdateUserID', 'int', TRUE)->Column('Attributes', 'text', TRUE)->Set(); $Construct->Table('UserMergeItem')->Column('MergeID', 'int', FALSE, 'key')->Column('Table', 'varchar(30)')->Column('Column', 'varchar(30)')->Column('RecordID', 'int')->Column('OldUserID', 'int')->Column('NewUserID', 'int')->Set(); // Save the current input formatter to the user's config. // This will allow us to change the default later and grandfather existing forums in. SaveToConfig('Garden.InputFormatter', C('Garden.InputFormatter')); // We need to undo cleditor's bad behavior for our reformed users. // If you still need to manipulate this, do it in memory instead (SAVE = false). SaveToConfig('Garden.Html.SafeStyles', TRUE); // Make sure the smarty folders exist. if (!file_exists(PATH_CACHE . '/Smarty')) { @mkdir(PATH_CACHE . '/Smarty'); } if (!file_exists(PATH_CACHE . '/Smarty/cache')) { @mkdir(PATH_CACHE . '/Smarty/cache'); } if (!file_exists(PATH_CACHE . '/Smarty/compile')) { @mkdir(PATH_CACHE . '/Smarty/compile'); } // Disallow additional super-admin users. // Get admins' UserIDs, sort lowest to highest, & exempt lowest UserID. $users = Gdn::userModel()->getWhere(array('Admin' => 1))->resultArray(); $affect = ConsolidateArrayValuesByKey($users, 'UserID'); sort($affect); array_shift($affect); if (count($affect)) { // Remove admin power & flush cache. Gdn::userModel()->update(array('Admin' => 0), array('UserID' => $affect)); Gdn::cache()->flush(); }
/** * * * @param $Name * @param array $Properties * @return mixed|string */ public static function module($Name, $Properties = array()) { if (isset($Properties['cache'])) { $Key = isset($Properties['cachekey']) ? $Properties['cachekey'] : 'module.' . $Name; $Result = Gdn::cache()->get($Key); if ($Result !== Gdn_Cache::CACHEOP_FAILURE) { // Trace('Module: '.$Result, $Key); return $Result; } } try { if (!class_exists($Name)) { if (debug()) { $Result = "Error: {$Name} doesn't exist"; } else { $Result = "<!-- Error: {$Name} doesn't exist -->"; } } else { $Module = new $Name(Gdn::controller(), ''); $Module->Visible = true; // Add properties passed in from the controller. $ControllerProperties = Gdn::controller()->data('_properties.' . strtolower($Name), array()); $Properties = array_merge($ControllerProperties, $Properties); foreach ($Properties as $Name => $value) { // Check for a setter method if (method_exists($Module, $method = 'set' . ucfirst($Name))) { $Module->{$method}($value); } else { $Module->{$Name} = $value; } } $Result = $Module->toString(); } } catch (Exception $Ex) { if (debug()) { $Result = '<pre class="Exception">' . htmlspecialchars($Ex->getMessage() . "\n" . $Ex->getTraceAsString()) . '</pre>'; } else { $Result = $Ex->getMessage(); } } if (isset($Key)) { // Trace($Result, "Store $Key"); Gdn::cache()->store($Key, $Result, array(Gdn_Cache::FEATURE_EXPIRY => $Properties['cache'])); } return $Result; }
/** * Executes a string of SQL. Returns a @@DataSet object. * * @param string $Sql A string of SQL to be executed. * @param array $InputParameters An array of values with as many elements as there are bound parameters in the SQL statement being executed. */ public function query($Sql, $InputParameters = null, $Options = array()) { $this->LastInfo = array(); if ($Sql == '') { trigger_error(errorMessage('Database was queried with an empty string.', $this->ClassName, 'Query'), E_USER_ERROR); } // Get the return type. if (isset($Options['ReturnType'])) { $ReturnType = $Options['ReturnType']; } elseif (preg_match('/^\\s*"?(insert)\\s+/i', $Sql)) { $ReturnType = 'ID'; } elseif (!preg_match('/^\\s*"?(update|delete|replace|create|drop|load data|copy|alter|grant|revoke|lock|unlock)\\s+/i', $Sql)) { $ReturnType = 'DataSet'; } else { $ReturnType = null; } if (isset($Options['Cache'])) { // Check to see if the query is cached. $CacheKeys = (array) val('Cache', $Options, null); $CacheOperation = val('CacheOperation', $Options, null); if (is_null($CacheOperation)) { switch ($ReturnType) { case 'DataSet': $CacheOperation = 'get'; break; case 'ID': case null: $CacheOperation = 'remove'; break; } } switch ($CacheOperation) { case 'get': foreach ($CacheKeys as $CacheKey) { $Data = Gdn::cache()->get($CacheKey); } // Cache hit. Return. if ($Data !== Gdn_Cache::CACHEOP_FAILURE) { return new Gdn_DataSet($Data); } // Cache miss. Save later. $StoreCacheKey = $CacheKey; break; case 'increment': case 'decrement': $CacheMethod = ucfirst($CacheOperation); foreach ($CacheKeys as $CacheKey) { $CacheResult = Gdn::cache()->{$CacheMethod}($CacheKey); } break; case 'remove': foreach ($CacheKeys as $CacheKey) { $Res = Gdn::cache()->remove($CacheKey); } break; } } // We will retry this query a few times if it fails. $tries = $this->ConnectRetries + 1; if ($tries < 1) { $tries = 1; } for ($try = 0; $try < $tries; $try++) { if (val('Type', $Options) == 'select' && val('Slave', $Options, null) !== false) { $PDO = $this->slave(); $this->LastInfo['connection'] = 'slave'; } else { $PDO = $this->connection(); $this->LastInfo['connection'] = 'master'; } // Make sure other unbufferred queries are not open if (is_object($this->_CurrentResultSet)) { $this->_CurrentResultSet->result(); $this->_CurrentResultSet->freePDOStatement(false); } $PDOStatement = null; try { // Prepare / Execute if (!is_null($InputParameters) && count($InputParameters) > 0) { $PDOStatement = $PDO->prepare($Sql); if (!is_object($PDOStatement)) { trigger_error(errorMessage('PDO Statement failed to prepare', $this->ClassName, 'Query', $this->getPDOErrorMessage($PDO->errorInfo())), E_USER_ERROR); } elseif ($PDOStatement->execute($InputParameters) === false) { trigger_error(errorMessage($this->getPDOErrorMessage($PDOStatement->errorInfo()), $this->ClassName, 'Query', $Sql), E_USER_ERROR); } } else { $PDOStatement = $PDO->query($Sql); } if ($PDOStatement === false) { list($state, $code, $message) = $PDO->errorInfo(); // Detect mysql "server has gone away" and try to reconnect. if ($code == 2006 && $try < $tries) { $this->closeConnection(); continue; } else { throw new Gdn_UserException($message, $code); } } // If we get here then the pdo statement prepared properly. break; } catch (Gdn_UserException $uex) { trigger_error($uex->getMessage(), E_USER_ERROR); } catch (Exception $ex) { list($state, $code, $message) = $PDO->errorInfo(); // If the error code is consistent with a disconnect, attempt to retry if ($code == 2006 && $try < $tries) { $this->closeConnection(); continue; } if (!$message) { $message = $ex->getMessage(); } trigger_error($message, E_USER_ERROR); } } if ($PDOStatement instanceof PDOStatement) { $this->LastInfo['RowCount'] = $PDOStatement->rowCount(); } // Did this query modify data in any way? if ($ReturnType == 'ID') { $this->_CurrentResultSet = $PDO->lastInsertId(); if (is_a($PDOStatement, 'PDOStatement')) { $PDOStatement->closeCursor(); } } else { if ($ReturnType == 'DataSet') { // Create a DataSet to manage the resultset $this->_CurrentResultSet = new Gdn_DataSet(); $this->_CurrentResultSet->Connection = $PDO; $this->_CurrentResultSet->pdoStatement($PDOStatement); } elseif (is_a($PDOStatement, 'PDOStatement')) { $PDOStatement->closeCursor(); } } if (isset($StoreCacheKey)) { if ($CacheOperation == 'get') { Gdn::cache()->store($StoreCacheKey, $this->_CurrentResultSet instanceof Gdn_DataSet ? $this->_CurrentResultSet->resultArray() : $this->_CurrentResultSet, val('CacheOptions', $Options, array())); } } return $this->_CurrentResultSet; }
public function removePageCache($DiscussionID, $From = 1) { if (!$this->pageCache) { return; } $CountComments = $this->SQL->getWhere('Discussion', array('DiscussionID' => $DiscussionID))->value('CountComments'); $Limit = c('Vanilla.Comments.PerPage', 30); $PageCount = PageNumber($CountComments, $Limit) + 1; for ($Page = $From; $Page <= $PageCount; $Page++) { $CacheKey = "Comment.Page.{$Limit}.{$DiscussionID}.{$Page}"; Gdn::cache()->Remove($CacheKey); } }
/** * Proxy an RSS feed for Dashboard use across our kingdom. * * @param Gdn_Controller $sender * @param $Url * @return mixed|string * @throws Exception */ public function homeController_proxyFeed_create($sender, $Url) { $Key = 'Feed|' . $Url; $Feed = Gdn::cache()->get($Key); if (!$Feed) { $Feed = ProxyRequest($Url, 5); Gdn::cache()->store($Key, $Feed, array(Gdn_Cache::FEATURE_EXPIRY => 5 * 60)); } return $Feed; }