diff --git a/Topcoder/class.topcoder.plugin.php b/Topcoder/class.topcoder.plugin.php index 8251a9d..c658e27 100644 --- a/Topcoder/class.topcoder.plugin.php +++ b/Topcoder/class.topcoder.plugin.php @@ -33,6 +33,7 @@ class TopcoderPlugin extends Gdn_Plugin { const CACHE_KEY_TOPCODER_PROFILE = 'topcoder.{UserID}'; const CACHE_TOPCODER_KEY_TOPCODER_PROFILE = 'topcoder.{Handle}'; const CACHE_TOPCODER_KEY_TOPCODER_ROLE_RESOURCES = 'topcoder.roleresources'; + const CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE = 'topcoder.challenge.{ChallengeID}'; const CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE_RESOURCES = 'topcoder.challenge.{ChallengeID}.resources'; const CACHE_DEFAULT_EXPIRY_TIME = 60*60*3; //The default expiration time in Memcached is in seconds, 10800 = 3 hours @@ -866,6 +867,7 @@ function gdn_dispatcher_beforeControllerMethod_handler($sender, $args){ ]); $groupID = false; + $categoryModel = new CategoryModel(); if($args['Controller'] instanceof DiscussionController) { if(array_key_exists('discussionid', $methodArgs)) { $discussionID = $methodArgs['discussionid']; @@ -887,7 +889,6 @@ function gdn_dispatcher_beforeControllerMethod_handler($sender, $args){ $discussionModel = new DiscussionModel(); $discussion = $discussionModel->getID($discussionID); if($discussion->CategoryID){ - $categoryModel = new CategoryModel(); $category = $categoryModel->getID($discussion->CategoryID); $groupID = $category->GroupID; } @@ -898,16 +899,28 @@ function gdn_dispatcher_beforeControllerMethod_handler($sender, $args){ $discussionModel = new DiscussionModel(); $discussion = $discussionModel->getID($comment->DiscussionID); if($discussion->CategoryID){ - $categoryModel = new CategoryModel(); $category = $categoryModel->getID($discussion->CategoryID); $groupID = $category->GroupID; } } + } else if($args['Controller'] instanceof CategoriesController) { + if (array_key_exists('categoryidentifier', $methodArgs)) { + $categoryUrlCode = $methodArgs['categoryidentifier']; + if($categoryUrlCode) { + $category = $categoryModel->getByCode($categoryUrlCode); + $groupID = val('GroupID', $category); + } + } } if($groupID && $groupID > 0) { $groupModel = new GroupModel(); $group = $groupModel->getByGroupID($groupID); + $category = $categoryModel->getByCode($group->ChallengeID); + $categoryID= val('CategoryID', $category); + Gdn::controller()->setData('Breadcrumbs.Options.GroupCategoryID', $categoryID); + Gdn::controller()->setData('Breadcrumbs.Options.GroupID', $groupID); + Gdn::controller()->setData('Breadcrumbs.Options.ChallengeID', $group->ChallengeID); if ($group->ChallengeID) { $this->setTopcoderProjectData($args['Controller'], $group->ChallengeID); } @@ -1446,11 +1459,11 @@ public function getChallengeResources($challengeId) { } $expirationTime = self::CACHE_DEFAULT_EXPIRY_TIME; - $challenge = self::loadChallenge($challengeId); - if($challenge && count($challenge) > 0) { + $challenge = self::getChallenge($challengeId); + if($challenge) { // Set expiration time for Challenge roles - $endDate = strtotime($challenge[0]->endDate); - $startDate = strtotime($challenge[0]->startDate); + $endDate = $challenge['EndDate']; + $startDate =$challenge['StartDate']; // $duration = $endDate > -1 && $startDate > -1 ? $endDate - $startDate: 0; // archived $isEnded = $endDate > -1 && now() - $endDate > 0; @@ -1520,6 +1533,44 @@ private static function loadChallengeResources($challengeId) { return null; } + /** + * Get Topcoder Challenge by ChallengeId + * @param $challengeId + * @return mixed|null + */ + public function getChallenge($challengeId) { + $challenge = self::getChallengeFromCache($challengeId); + if ($challenge) { + return $challenge; + } + + $cachedChallenge = ['ChallengeID' => $challengeId]; + $challenge = self::loadChallenge($challengeId); + + $expirationTime = self::CACHE_DEFAULT_EXPIRY_TIME; + if($challenge) { + // Set expiration time for Challenge roles + $startDate = strtotime($challenge->startDate); + $endDate = strtotime($challenge->endDate); + // archived + $isEnded = $endDate > -1 && now() - $endDate > 0; + if(!$isEnded) { + $expirationTime = self::CACHE_ONE_DAY_EXPIRY_TIME; + } + $cachedChallenge['StartDate'] = $startDate; + $cachedChallenge['EndDate'] = $endDate; + $cachedChallenge['Track'] = $challenge->track; + $termIDs = array_column($challenge->terms, 'id'); + $NDA_UUID = c('Plugins.Topcoder.NDA_UUID'); + $cachedChallenge['IsNDA'] = in_array($NDA_UUID, $termIDs); + } + if (Gdn_Cache::activeEnabled()) { + self::topcoderChallengeCache($challengeId, $cachedChallenge, $expirationTime); + } + return $cachedChallenge; + } + + /** * Load Topcoder Challenge by Challenge ID * @param $challengeId @@ -1535,7 +1586,7 @@ private static function loadChallenge($challengeId) { 'header' => 'Authorization: Bearer ' .$token )); $context = stream_context_create($options); - $data = file_get_contents($topcoderChallengeApiUrl . '?challengeId=' . $challengeId, false, $context); + $data = file_get_contents($topcoderChallengeApiUrl . $challengeId, false, $context); if ($data === false) { // Handle errors (e.g. 404 and others) self::log('Couldn\'t get challenge: no token', ['headers'=> json_encode($http_response_header)]); @@ -1549,6 +1600,35 @@ private static function loadChallenge($challengeId) { return null; } + /** + * Load challenge from cache + * @param $challengeID + * @return false|mixed + */ + private static function getChallengeFromCache($challengeID) { + if(!Gdn_Cache::activeEnabled()) { + return false; + } + + $handleKey = formatString(self::CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE, ['ChallengeID' => $challengeID]); + if(!Gdn::cache()->exists($handleKey)) { + return false; + } + $challenge = Gdn::cache()->get($handleKey); + if ($challenge === Gdn_Cache::CACHEOP_FAILURE) { + return false; + } + return $challenge; + } + + private static function topcoderChallengeCache($challengeID, $challenge, $expirationTime = self::CACHE_DEFAULT_EXPIRY_TIME) { + $challengeKey = formatString(self::CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE, ['ChallengeID' => $challengeID]); + return Gdn::cache()->store($challengeKey , $challenge, [ + Gdn_Cache::FEATURE_EXPIRY => $expirationTime + ]); + } + + /** * Get a Topcoder Roles * @@ -1761,6 +1841,7 @@ public static function getUserPhotoUrl($user) { // Set Topcoder Project Roles Data for a challenge private function setTopcoderProjectData($sender, $challengeID) { if($challengeID) { + $challenge = $this->getChallenge($challengeID); $resources = $this->getChallengeResources($challengeID); $roleResources = $this->getRoleResources(); $currentProjectRoles = $this->getTopcoderProjectRoles(Gdn::session()->User, $resources, $roleResources); @@ -1768,6 +1849,7 @@ private function setTopcoderProjectData($sender, $challengeID) { $currentProjectRoles = array_map('strtolower',$currentProjectRoles); } + $sender->Data['Challenge'] = $challenge; $sender->Data['ChallengeResources'] = $resources; $sender->Data['ChallengeRoleResources'] = $roleResources; $sender->Data['ChallengeCurrentUserProjectRoles'] = $currentProjectRoles; @@ -1777,7 +1859,7 @@ private function setTopcoderProjectData($sender, $challengeID) { // } self::log('setTopcoderProjectData', ['ChallengeID' => $challengeID, 'currentUser' => $currentProjectRoles, 'Topcoder Resources' => $resources , 'Topcoder RoleResources' - => $roleResources,]); + => $roleResources, 'challenge' =>$challenge]); } } @@ -1984,6 +2066,62 @@ public static function log($message, $data = []) { ); } } + + // MAGIC EVENTS TO OVERRIDE VANILLA CONTROLLER METHODS + + /** + * Allows user to announce or unannounce a discussion. + * FIX: https://github.com/topcoder-platform/forums/issues/456 + * @param int $discussionID Unique discussion ID. + * @param string $TransientKey Single-use hash to prove intent. + */ + public function discussionController_announce_create($sender, $discussionID = '', $announce=true ,$target = '') { + // Make sure we are posting back. + if (!$sender->Request->isAuthenticatedPostBack()) { + throw permissionException('Javascript'); + } + + $discussion = $sender->DiscussionModel->getID($discussionID); + if (!$discussion) { + throw notFoundException('Discussion'); + } + + //$sender->categoryPermission($discussion->CategoryID, 'Vanilla.Discussions.Announce');// protected + if (!CategoryModel::checkPermission($discussion->CategoryID, 'Vanilla.Discussions.Announce')) { + $sender->permission('Vanilla.Discussions.Announce', true, 'Category', $discussion->CategoryID); + } + + // Save the property. + // 0 - Don't Announce Discussion + // 2 - Announce Discussion in the current category + $newAnnounceValue = (bool)$announce? 2 : 0; + $sender->DiscussionModel->setField($discussionID, 'Announce', $newAnnounceValue); + $discussion->Announce = $newAnnounceValue; + + // Redirect to the front page + if ($sender->_DeliveryType === DELIVERY_TYPE_ALL) { + $target = getIncomingValue('Target', 'discussions'); + redirectTo($target); + } + + $sender->sendOptions($discussion); + if ($newAnnounceValue == 2) { + require_once $sender->fetchViewLocation('helper_functions', 'Discussions', 'vanilla'); + $dataHtml = tag($discussion, 'Announce', 'Announcement'); + // Remove if exists + $sender->jsonTarget(".Section-DiscussionList #Discussion_$discussionID .Meta-Discussion", $dataHtml , 'Prepend'); + $sender->jsonTarget(".Section-DiscussionList #Discussion_$discussionID", 'Announcement', 'AddClass'); + } else { + $sender->jsonTarget(".Section-DiscussionList #Discussion_$discussionID .Tag-Announcement", null, 'Remove'); + $sender->jsonTarget(".Section-DiscussionList #Discussion_$discussionID", 'Announcement', 'RemoveClass'); + + } + + $sender->jsonTarget("#Discussion_$discussionID", null, 'Highlight'); + $sender->jsonTarget(".Discussion #Item_0", null, 'Highlight'); + + $sender->render('Blank', 'Utility', 'Dashboard'); + } } if(!function_exists('topcoderRatingCssClass')) { @@ -2483,3 +2621,50 @@ function topcoderMentionAnchor($mention, $cssClass = null, $options = null) { } } +if (!function_exists('watchingSorts')) { + /** + * Returns watching sorting. + * + * @param string $extraClasses any extra classes you add to the drop down + * @return string + */ + function watchingSorts($extraClasses = '') { + if (!Gdn::session()->isValid()) { + return; + } + + $baseUrl = preg_replace('/\?.*/', '', Gdn::request()->getFullPath()); + $transientKey = Gdn::session()->transientKey(); + $filters = [ + [ + 'name' => t('New'), + 'param' => 'sort', + 'value' => 'new', + 'extra' => ['TransientKey' => $transientKey, 'save' => 1] + ], + + [ + 'name' => t('Old'), + 'param' => 'sort', + 'value' => 'old', + 'extra' => ['TransientKey' => $transientKey, 'save' => 1] + ] + ]; + + $defaultParams = []; + if (!empty($defaultParams)) { + $defaultUrl = $baseUrl.'?'.http_build_query($defaultParams); + } else { + $defaultUrl = $baseUrl; + } + + return sortsDropDown('WatchingSort', + $baseUrl, + $filters, + $extraClasses, + null, + $defaultUrl, + 'Sort' + ); + } +} \ No newline at end of file diff --git a/Topcoder/controllers/class.watchingcontroller.php b/Topcoder/controllers/class.watchingcontroller.php index d3cd725..7c3d101 100644 --- a/Topcoder/controllers/class.watchingcontroller.php +++ b/Topcoder/controllers/class.watchingcontroller.php @@ -56,10 +56,10 @@ public function index($cp = '', $dp = '') { $sort = Gdn::request()->get('sort', null); $saveSorting = $sort !== null && Gdn::request()->get('save') && Gdn::session()->validateTransientKey(Gdn::request()->get('TransientKey', '')); if($saveSorting) { - Gdn::session()->setPreference('CategorySort', $sort); + Gdn::session()->setPreference('WatchingSort', $sort); } - $sort = Gdn::session()->getPreference('CategorySort', false); - $this->setData('CategorySort', $sort); + $sort = Gdn::session()->getPreference('WatchingSort', false); + $this->setData('WatchingSort', $sort); $userMetaModel = new UserMetaModel(); list($cp, $categoryLimit) = offsetLimit($cp, 30);