Skip to content

Commit f340419

Browse files
authored
Merge pull request #93 from topcoder-platform/develop
v1.6
2 parents 2d695e1 + bd4e4f4 commit f340419

19 files changed

+1140
-6
lines changed

DebugPlugin/controllers/api/CacheApiController.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,79 @@ public function get_flush() {
6262
return false;
6363
}
6464
}
65+
66+
public function get_extendedstats(array $query) {
67+
$this->permission('Garden.Settings.Manage');
68+
$in = $this->schema([
69+
'type:s' => 'The type of statistics to fetch(stats, detail, cachedump, slabs, items, sizes)',
70+
'slabid:i?' => 'Slab ID',
71+
'limit:i?' => 'Limit the number of entries to dump'
72+
], 'in')->setDescription('Get server statistics');
73+
74+
$query = $in->validate($query);
75+
$type = $query['type'];
76+
$slabID = $query['slabid'];
77+
$limit = $query['limit'];
78+
79+
if(Gdn_Cache::activeCache()) {
80+
$pos = strrpos(getenv('MEMCACHED_SERVER'), ':');
81+
$server = substr(getenv('MEMCACHED_SERVER'), 0, $pos);
82+
$port = substr(getenv('MEMCACHED_SERVER'), $pos+1);
83+
switch ($type) {
84+
case 'slabs':
85+
return $this->sendMemcacheCommand($server, $port,'stats slabs');
86+
case 'stats':
87+
return $this->sendMemcacheCommand($server, $port,'stats');
88+
case 'items':
89+
return $this->sendMemcacheCommand($server, $port,'stats items');
90+
case 'sizes':
91+
return $this->sendMemcacheCommand($server, $port,'stats sizes');
92+
case 'detail_on':
93+
return $this->sendMemcacheCommand($server, $port,'stats detail on');
94+
case 'detail_off':
95+
return $this->sendMemcacheCommand($server, $port,'stats detail off');
96+
case 'detail_dump':
97+
return $this->sendMemcacheCommand($server, $port,'stats detail dump');
98+
case 'cachedump':
99+
if(!$slabID) {
100+
return 'Missing slabid';
101+
}
102+
$limit = isset($limit)? $limit:100;
103+
return $this->sendMemcacheCommand($server, $port,'stats cachedump '.$slabID.' '.$limit);
104+
default:
105+
return 'Not supported';
106+
}
107+
} else {
108+
return 'Cached disabled';
109+
}
110+
}
111+
112+
function sendMemcacheCommand($server,$port,$command){
113+
114+
$s = @fsockopen($server,$port);
115+
if (!$s){
116+
die("Cant connect to:".$server.':'.$port);
117+
}
118+
119+
fwrite($s, $command."\r\n");
120+
121+
$buf='';
122+
while ((!feof($s))) {
123+
$buf .= fgets($s, 256);
124+
if (strpos($buf,"END\r\n")!==false){ // stat says end
125+
break;
126+
}
127+
if (strpos($buf,"DELETED\r\n")!==false || strpos($buf,"NOT_FOUND\r\n")!==false){ // delete says these
128+
break;
129+
}
130+
if (strpos($buf,"OK\r\n")!==false){
131+
break;
132+
}
133+
if (strpos($buf,"ERROR\r\n")!==false){
134+
break;
135+
}
136+
}
137+
fclose($s);
138+
return $buf;
139+
}
65140
}

DebugPlugin/openapi/cache.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,30 @@ paths:
2929
tags:
3030
- Cache
3131
summary: Invalidate all items in the cache
32+
/cache/extendedstats:
33+
get:
34+
parameters:
35+
- description: The type of statistics to fetch
36+
in: query
37+
name: type
38+
schema:
39+
type: string
40+
- description: Slab ID
41+
in: query
42+
name: slabid
43+
schema:
44+
type: int
45+
- description: Limit
46+
in: query
47+
name: limit
48+
schema:
49+
type: int
50+
responses:
51+
'200':
52+
description: Memcached stats
53+
tags:
54+
- Cache
55+
summary: Get Memcached stats
3256
components:
3357
schemas:
3458
Records:

ReplyTo/design/replyto.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
}
77
.MessageList .ItemDiscussion .Item-Body .Controls a.ReplyViewOptionLink, .ReplyViewOptions .ReplyViewOptionLink {
88
color: #137d60;
9+
font-weight:500;
910
}
1011

1112
.MessageList .ItemDiscussion .Item-Body .Controls a.ReplyViewOptionLink.Active, .ReplyViewOptions .ReplyViewOptionLink.Active {
1213
color: #555555;
14+
font-weight:500;
1315
}
1416
/*
1517
Indent comments according to their depth.

Topcoder/class.topcoder.plugin.php

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ class TopcoderPlugin extends Gdn_Plugin {
3535
const CACHE_TOPCODER_KEY_TOPCODER_ROLE_RESOURCES = 'topcoder.roleresources';
3636
const CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE_RESOURCES = 'topcoder.challenge.{ChallengeID}.resources';
3737

38+
const CACHE_DEFAULT_EXPIRY_TIME = 60*60*3; //The default expiration time in Memcached is in seconds, 10800 = 3 hours
39+
const CACHE_TOPCODER_PROFILE_EXPIRY_TIME = 60*60*24*7; // 1 week
40+
const CACHE_ONE_DAY_EXPIRY_TIME = 60*60*24; // 1 day
41+
3842
const ROLE_TYPE_TOPCODER = 'topcoder';
3943
const ROLE_TOPCODER_CONNECT_ADMIN = 'Connect Admin';
4044
const ROLE_TOPCODER_ADMINISTRATOR = 'administrator';
@@ -679,6 +683,21 @@ public function base_afterSignIn_handler($sender, $args) {
679683
}
680684
self::log('base_afterSignIn_handler', ['Session Permissions' => Gdn::session()->getPermissionsArray()]);
681685

686+
if(Gdn_Cache::activeEnabled()) {
687+
$currentUser = Gdn::session()->User;
688+
$lastVisit = val('DateLastActive', $currentUser, false);
689+
if ($lastVisit) {
690+
$seconds = now() - Gdn_Format::toTimestamp($lastVisit);
691+
if ($seconds > self::CACHE_ONE_DAY_EXPIRY_TIME) { // Update the current User once a day
692+
// remove from Topcoder cache by UserID and Topcoder handle
693+
self::removeTopcoderUserFromCache($currentUser->UserID);
694+
self::removeUserFromTopcoderCache($currentUser->Name);
695+
// update cache
696+
self::getTopcoderUserFromTopcoderCache($currentUser->Name);
697+
self::getTopcoderUserFromCache($currentUser->UserID);
698+
}
699+
}
700+
}
682701
}
683702

684703
/**
@@ -1362,7 +1381,7 @@ public function getRoleResources() {
13621381
private static function topcoderRoleResourcesCache($roleResources) {
13631382
return Gdn::cache()->store(self::CACHE_TOPCODER_KEY_TOPCODER_ROLE_RESOURCES,
13641383
$roleResources, [
1365-
Gdn_Cache::FEATURE_EXPIRY => 3600
1384+
Gdn_Cache::FEATURE_EXPIRY => self::CACHE_ONE_DAY_EXPIRY_TIME
13661385
]);
13671386
}
13681387

@@ -1426,9 +1445,20 @@ public function getChallengeResources($challengeId) {
14261445
return $challengeResources;
14271446
}
14281447

1448+
$expirationTime = self::CACHE_DEFAULT_EXPIRY_TIME;
1449+
$challenge = self::loadChallenge($challengeId);
1450+
if($challenge && count($challenge) > 0) {
1451+
// Set expiration time for Challenge roles
1452+
$endDate = strtotime($challenge[0]->endDate);
1453+
$startDate = strtotime($challenge[0]->startDate);
1454+
// $duration = $endDate > -1 && $startDate > -1 ? $endDate - $startDate: 0;
1455+
// archived
1456+
$isEnded = $endDate > -1 && now() - $endDate > 0;
1457+
$expirationTime = $isEnded ? self::CACHE_DEFAULT_EXPIRY_TIME: self::CACHE_ONE_DAY_EXPIRY_TIME;
1458+
}
14291459
$challengeResources = self::loadChallengeResources($challengeId);
14301460
if(Gdn_Cache::activeEnabled() && $challengeResources) {
1431-
self::topcoderChallengeResourcesCache( $challengeId, $challengeResources);
1461+
self::topcoderChallengeResourcesCache( $challengeId, $challengeResources, $expirationTime);
14321462
}
14331463
return $challengeResources;
14341464
}
@@ -1454,10 +1484,10 @@ private static function getChallengeResourcesFromCache($challengeID) {
14541484
return $challengeResources;
14551485
}
14561486

1457-
private static function topcoderChallengeResourcesCache($challengeID, $challengeResources) {
1487+
private static function topcoderChallengeResourcesCache($challengeID, $challengeResources, $expirationTime = self::CACHE_DEFAULT_EXPIRY_TIME) {
14581488
$challengeKey = formatString(self::CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE_RESOURCES, ['ChallengeID' => $challengeID]);
14591489
return Gdn::cache()->store($challengeKey , $challengeResources, [
1460-
Gdn_Cache::FEATURE_EXPIRY => 3600
1490+
Gdn_Cache::FEATURE_EXPIRY => $expirationTime
14611491
]);
14621492
}
14631493

@@ -1490,6 +1520,35 @@ private static function loadChallengeResources($challengeId) {
14901520
return null;
14911521
}
14921522

1523+
/**
1524+
* Load Topcoder Challenge by Challenge ID
1525+
* @param $challengeId
1526+
* @return mixed|null
1527+
*/
1528+
private static function loadChallenge($challengeId) {
1529+
$token = TopcoderPlugin::getM2MToken();
1530+
if ($token) {
1531+
$challengeURI = c('Plugins.Topcoder.ChallengeApiURI', '/v5/challenges/');
1532+
$topcoderChallengeApiUrl = c('Plugins.Topcoder.BaseApiURL') . $challengeURI;
1533+
$options = array('http' => array(
1534+
'method' => 'GET',
1535+
'header' => 'Authorization: Bearer ' .$token
1536+
));
1537+
$context = stream_context_create($options);
1538+
$data = file_get_contents($topcoderChallengeApiUrl . '?challengeId=' . $challengeId, false, $context);
1539+
if ($data === false) {
1540+
// Handle errors (e.g. 404 and others)
1541+
self::log('Couldn\'t get challenge: no token', ['headers'=> json_encode($http_response_header)]);
1542+
logMessage(__FILE__, __LINE__, 'TopcoderPlugin', 'loadChallenge', "Couldn't load Topcoder challenge".json_encode($http_response_header));
1543+
return null;
1544+
}
1545+
1546+
return json_decode($data);
1547+
}
1548+
self::log('Couldn\'t load challenge: no token', []);
1549+
return null;
1550+
}
1551+
14931552
/**
14941553
* Get a Topcoder Roles
14951554
*
@@ -1795,7 +1854,7 @@ private static function topcoderUserCache($userFields) {
17951854
$userID = val('UserID', $userFields);
17961855
$userKey = formatString(self::CACHE_KEY_TOPCODER_PROFILE, ['UserID' => $userID]);
17971856
$cached = $cached & Gdn::cache()->store($userKey, $userFields, [
1798-
Gdn_Cache::FEATURE_EXPIRY => 3600
1857+
Gdn_Cache::FEATURE_EXPIRY => self::CACHE_TOPCODER_PROFILE_EXPIRY_TIME
17991858
]);
18001859
return $cached;
18011860
}
@@ -1826,7 +1885,26 @@ public static function getTopcoderUserByHandle($topcoderHandle) {
18261885
return $topcoderUser;
18271886
}
18281887

1888+
private static function removeTopcoderUserFromCache($userID) {
1889+
if(!Gdn_Cache::activeEnabled()) {
1890+
return false;
1891+
}
1892+
1893+
$handleKey = formatString(self::CACHE_KEY_TOPCODER_PROFILE, ['UserID' => $userID]);
1894+
return Gdn::cache()->remove($handleKey);
1895+
}
1896+
18291897
// This cache includes Topcoder which might not exist in Vanilla
1898+
private static function removeUserFromTopcoderCache($topcoderHandle) {
1899+
if (!Gdn_Cache::activeEnabled()) {
1900+
return false;
1901+
}
1902+
1903+
$handleKey = formatString(self::CACHE_TOPCODER_KEY_TOPCODER_PROFILE, ['Handle' => $topcoderHandle]);
1904+
return Gdn::cache()->remove($handleKey);
1905+
}
1906+
1907+
// This cache includes Topcoder which might not exist in Vanilla
18301908
private static function getTopcoderUserFromTopcoderCache($topcoderHandle) {
18311909
if(!Gdn_Cache::activeEnabled()) {
18321910
return false;
@@ -1887,7 +1965,7 @@ private static function topcoderUserTopcoderCache($userFields) {
18871965
$handle = val('Handle', $userFields);
18881966
$userKey = formatString(self::CACHE_TOPCODER_KEY_TOPCODER_PROFILE, ['Handle' => $handle]);
18891967
$cached = $cached & Gdn::cache()->store($userKey, $userFields, [
1890-
Gdn_Cache::FEATURE_EXPIRY => 3600
1968+
Gdn_Cache::FEATURE_EXPIRY => self::CACHE_TOPCODER_PROFILE_EXPIRY_TIME
18911969
]);
18921970
return $cached;
18931971
}
@@ -2163,6 +2241,7 @@ function userAnchor($user, $cssClass = null, $options = null) {
21632241

21642242
Gdn::controller()->EventArguments['User'] = $user;
21652243
Gdn::controller()->EventArguments['IsTopcoderAdmin'] =$isTopcoderAdmin;
2244+
Gdn::controller()->EventArguments['HideRoles'] = val('HideRoles', $options, false);
21662245
Gdn::controller()->EventArguments['Text'] =& $text;
21672246
Gdn::controller()->EventArguments['Attributes'] =& $attributes;
21682247
Gdn::controller()->fireEvent('UserAnchor');

Voting/addon.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"key": "Voting",
3+
"name": "Voting",
4+
"description": "This plugin allows users to vote for comments and discussions.",
5+
"version": "1.0.0",
6+
"documentationUrl": "http://discussions.topcoder.com",
7+
"type": "addon",
8+
"priority": "100",
9+
"icon": "topcoder-logo.jpeg",
10+
"mobileFriendly": true,
11+
"hasLocale": false,
12+
"authors": [
13+
{
14+
"name": "Topcoder",
15+
"email": "[email protected]",
16+
"homepage": "https://topcoder.com"
17+
}
18+
],
19+
"require": {
20+
"vanilla": ">=3.0"
21+
}
22+
}

0 commit comments

Comments
 (0)