@@ -33,6 +33,7 @@ class TopcoderPlugin extends Gdn_Plugin {
3333 const CACHE_KEY_TOPCODER_PROFILE = 'topcoder.{UserID} ' ;
3434 const CACHE_TOPCODER_KEY_TOPCODER_PROFILE = 'topcoder.{Handle} ' ;
3535 const CACHE_TOPCODER_KEY_TOPCODER_ROLE_RESOURCES = 'topcoder.roleresources ' ;
36+ const CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE = 'topcoder.challenge.{ChallengeID} ' ;
3637 const CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE_RESOURCES = 'topcoder.challenge.{ChallengeID}.resources ' ;
3738
3839 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){
866867 ]);
867868
868869 $ groupID = false ;
870+ $ categoryModel = new CategoryModel ();
869871 if ($ args ['Controller ' ] instanceof DiscussionController) {
870872 if (array_key_exists ('discussionid ' , $ methodArgs )) {
871873 $ discussionID = $ methodArgs ['discussionid ' ];
@@ -887,7 +889,6 @@ function gdn_dispatcher_beforeControllerMethod_handler($sender, $args){
887889 $ discussionModel = new DiscussionModel ();
888890 $ discussion = $ discussionModel ->getID ($ discussionID );
889891 if ($ discussion ->CategoryID ){
890- $ categoryModel = new CategoryModel ();
891892 $ category = $ categoryModel ->getID ($ discussion ->CategoryID );
892893 $ groupID = $ category ->GroupID ;
893894 }
@@ -898,16 +899,28 @@ function gdn_dispatcher_beforeControllerMethod_handler($sender, $args){
898899 $ discussionModel = new DiscussionModel ();
899900 $ discussion = $ discussionModel ->getID ($ comment ->DiscussionID );
900901 if ($ discussion ->CategoryID ){
901- $ categoryModel = new CategoryModel ();
902902 $ category = $ categoryModel ->getID ($ discussion ->CategoryID );
903903 $ groupID = $ category ->GroupID ;
904904 }
905905 }
906+ } else if ($ args ['Controller ' ] instanceof CategoriesController) {
907+ if (array_key_exists ('categoryidentifier ' , $ methodArgs )) {
908+ $ categoryUrlCode = $ methodArgs ['categoryidentifier ' ];
909+ if ($ categoryUrlCode ) {
910+ $ category = $ categoryModel ->getByCode ($ categoryUrlCode );
911+ $ groupID = val ('GroupID ' , $ category );
912+ }
913+ }
906914 }
907915
908916 if ($ groupID && $ groupID > 0 ) {
909917 $ groupModel = new GroupModel ();
910918 $ group = $ groupModel ->getByGroupID ($ groupID );
919+ $ category = $ categoryModel ->getByCode ($ group ->ChallengeID );
920+ $ categoryID = val ('CategoryID ' , $ category );
921+ Gdn::controller ()->setData ('Breadcrumbs.Options.GroupCategoryID ' , $ categoryID );
922+ Gdn::controller ()->setData ('Breadcrumbs.Options.GroupID ' , $ groupID );
923+ Gdn::controller ()->setData ('Breadcrumbs.Options.ChallengeID ' , $ group ->ChallengeID );
911924 if ($ group ->ChallengeID ) {
912925 $ this ->setTopcoderProjectData ($ args ['Controller ' ], $ group ->ChallengeID );
913926 }
@@ -1446,11 +1459,11 @@ public function getChallengeResources($challengeId) {
14461459 }
14471460
14481461 $ expirationTime = self ::CACHE_DEFAULT_EXPIRY_TIME ;
1449- $ challenge = self ::loadChallenge ($ challengeId );
1450- if ($ challenge && count ( $ challenge ) > 0 ) {
1462+ $ challenge = self ::getChallenge ($ challengeId );
1463+ if ($ challenge ) {
14511464 // Set expiration time for Challenge roles
1452- $ endDate = strtotime ( $ challenge [0 ]-> endDate ) ;
1453- $ startDate = strtotime ( $ challenge [0 ]-> startDate ) ;
1465+ $ endDate = $ challenge [' EndDate ' ] ;
1466+ $ startDate =$ challenge [' StartDate ' ] ;
14541467 // $duration = $endDate > -1 && $startDate > -1 ? $endDate - $startDate: 0;
14551468 // archived
14561469 $ isEnded = $ endDate > -1 && now () - $ endDate > 0 ;
@@ -1520,6 +1533,44 @@ private static function loadChallengeResources($challengeId) {
15201533 return null ;
15211534 }
15221535
1536+ /**
1537+ * Get Topcoder Challenge by ChallengeId
1538+ * @param $challengeId
1539+ * @return mixed|null
1540+ */
1541+ public function getChallenge ($ challengeId ) {
1542+ $ challenge = self ::getChallengeFromCache ($ challengeId );
1543+ if ($ challenge ) {
1544+ return $ challenge ;
1545+ }
1546+
1547+ $ cachedChallenge = ['ChallengeID ' => $ challengeId ];
1548+ $ challenge = self ::loadChallenge ($ challengeId );
1549+
1550+ $ expirationTime = self ::CACHE_DEFAULT_EXPIRY_TIME ;
1551+ if ($ challenge ) {
1552+ // Set expiration time for Challenge roles
1553+ $ startDate = strtotime ($ challenge ->startDate );
1554+ $ endDate = strtotime ($ challenge ->endDate );
1555+ // archived
1556+ $ isEnded = $ endDate > -1 && now () - $ endDate > 0 ;
1557+ if (!$ isEnded ) {
1558+ $ expirationTime = self ::CACHE_ONE_DAY_EXPIRY_TIME ;
1559+ }
1560+ $ cachedChallenge ['StartDate ' ] = $ startDate ;
1561+ $ cachedChallenge ['EndDate ' ] = $ endDate ;
1562+ $ cachedChallenge ['Track ' ] = $ challenge ->track ;
1563+ $ termIDs = array_column ($ challenge ->terms , 'id ' );
1564+ $ NDA_UUID = c ('Plugins.Topcoder.NDA_UUID ' );
1565+ $ cachedChallenge ['IsNDA ' ] = in_array ($ NDA_UUID , $ termIDs );
1566+ }
1567+ if (Gdn_Cache::activeEnabled ()) {
1568+ self ::topcoderChallengeCache ($ challengeId , $ cachedChallenge , $ expirationTime );
1569+ }
1570+ return $ cachedChallenge ;
1571+ }
1572+
1573+
15231574 /**
15241575 * Load Topcoder Challenge by Challenge ID
15251576 * @param $challengeId
@@ -1535,7 +1586,7 @@ private static function loadChallenge($challengeId) {
15351586 'header ' => 'Authorization: Bearer ' .$ token
15361587 ));
15371588 $ context = stream_context_create ($ options );
1538- $ data = file_get_contents ($ topcoderChallengeApiUrl . ' ?challengeId= ' . $ challengeId , false , $ context );
1589+ $ data = file_get_contents ($ topcoderChallengeApiUrl . $ challengeId , false , $ context );
15391590 if ($ data === false ) {
15401591 // Handle errors (e.g. 404 and others)
15411592 self ::log ('Couldn \'t get challenge: no token ' , ['headers ' => json_encode ($ http_response_header )]);
@@ -1549,6 +1600,35 @@ private static function loadChallenge($challengeId) {
15491600 return null ;
15501601 }
15511602
1603+ /**
1604+ * Load challenge from cache
1605+ * @param $challengeID
1606+ * @return false|mixed
1607+ */
1608+ private static function getChallengeFromCache ($ challengeID ) {
1609+ if (!Gdn_Cache::activeEnabled ()) {
1610+ return false ;
1611+ }
1612+
1613+ $ handleKey = formatString (self ::CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE , ['ChallengeID ' => $ challengeID ]);
1614+ if (!Gdn::cache ()->exists ($ handleKey )) {
1615+ return false ;
1616+ }
1617+ $ challenge = Gdn::cache ()->get ($ handleKey );
1618+ if ($ challenge === Gdn_Cache::CACHEOP_FAILURE ) {
1619+ return false ;
1620+ }
1621+ return $ challenge ;
1622+ }
1623+
1624+ private static function topcoderChallengeCache ($ challengeID , $ challenge , $ expirationTime = self ::CACHE_DEFAULT_EXPIRY_TIME ) {
1625+ $ challengeKey = formatString (self ::CACHE_TOPCODER_KEY_TOPCODER_CHALLENGE , ['ChallengeID ' => $ challengeID ]);
1626+ return Gdn::cache ()->store ($ challengeKey , $ challenge , [
1627+ Gdn_Cache::FEATURE_EXPIRY => $ expirationTime
1628+ ]);
1629+ }
1630+
1631+
15521632 /**
15531633 * Get a Topcoder Roles
15541634 *
@@ -1761,13 +1841,15 @@ public static function getUserPhotoUrl($user) {
17611841 // Set Topcoder Project Roles Data for a challenge
17621842 private function setTopcoderProjectData ($ sender , $ challengeID ) {
17631843 if ($ challengeID ) {
1844+ $ challenge = $ this ->getChallenge ($ challengeID );
17641845 $ resources = $ this ->getChallengeResources ($ challengeID );
17651846 $ roleResources = $ this ->getRoleResources ();
17661847 $ currentProjectRoles = $ this ->getTopcoderProjectRoles (Gdn::session ()->User , $ resources , $ roleResources );
17671848 if ($ currentProjectRoles ) {
17681849 $ currentProjectRoles = array_map ('strtolower ' ,$ currentProjectRoles );
17691850 }
17701851
1852+ $ sender ->Data ['Challenge ' ] = $ challenge ;
17711853 $ sender ->Data ['ChallengeResources ' ] = $ resources ;
17721854 $ sender ->Data ['ChallengeRoleResources ' ] = $ roleResources ;
17731855 $ sender ->Data ['ChallengeCurrentUserProjectRoles ' ] = $ currentProjectRoles ;
@@ -1777,7 +1859,7 @@ private function setTopcoderProjectData($sender, $challengeID) {
17771859 // }
17781860 self ::log ('setTopcoderProjectData ' , ['ChallengeID ' => $ challengeID , 'currentUser ' => $ currentProjectRoles ,
17791861 'Topcoder Resources ' => $ resources , 'Topcoder RoleResources '
1780- => $ roleResources ,]);
1862+ => $ roleResources , ' challenge ' => $ challenge ]);
17811863 }
17821864 }
17831865
@@ -1984,6 +2066,62 @@ public static function log($message, $data = []) {
19842066 );
19852067 }
19862068 }
2069+
2070+ // MAGIC EVENTS TO OVERRIDE VANILLA CONTROLLER METHODS
2071+
2072+ /**
2073+ * Allows user to announce or unannounce a discussion.
2074+ * FIX: https://github.com/topcoder-platform/forums/issues/456
2075+ * @param int $discussionID Unique discussion ID.
2076+ * @param string $TransientKey Single-use hash to prove intent.
2077+ */
2078+ public function discussionController_announce_create ($ sender , $ discussionID = '' , $ announce =true ,$ target = '' ) {
2079+ // Make sure we are posting back.
2080+ if (!$ sender ->Request ->isAuthenticatedPostBack ()) {
2081+ throw permissionException ('Javascript ' );
2082+ }
2083+
2084+ $ discussion = $ sender ->DiscussionModel ->getID ($ discussionID );
2085+ if (!$ discussion ) {
2086+ throw notFoundException ('Discussion ' );
2087+ }
2088+
2089+ //$sender->categoryPermission($discussion->CategoryID, 'Vanilla.Discussions.Announce');// protected
2090+ if (!CategoryModel::checkPermission ($ discussion ->CategoryID , 'Vanilla.Discussions.Announce ' )) {
2091+ $ sender ->permission ('Vanilla.Discussions.Announce ' , true , 'Category ' , $ discussion ->CategoryID );
2092+ }
2093+
2094+ // Save the property.
2095+ // 0 - Don't Announce Discussion
2096+ // 2 - Announce Discussion in the current category
2097+ $ newAnnounceValue = (bool )$ announce ? 2 : 0 ;
2098+ $ sender ->DiscussionModel ->setField ($ discussionID , 'Announce ' , $ newAnnounceValue );
2099+ $ discussion ->Announce = $ newAnnounceValue ;
2100+
2101+ // Redirect to the front page
2102+ if ($ sender ->_DeliveryType === DELIVERY_TYPE_ALL ) {
2103+ $ target = getIncomingValue ('Target ' , 'discussions ' );
2104+ redirectTo ($ target );
2105+ }
2106+
2107+ $ sender ->sendOptions ($ discussion );
2108+ if ($ newAnnounceValue == 2 ) {
2109+ require_once $ sender ->fetchViewLocation ('helper_functions ' , 'Discussions ' , 'vanilla ' );
2110+ $ dataHtml = tag ($ discussion , 'Announce ' , 'Announcement ' );
2111+ // Remove if exists
2112+ $ sender ->jsonTarget (".Section-DiscussionList #Discussion_ $ discussionID .Meta-Discussion " , $ dataHtml , 'Prepend ' );
2113+ $ sender ->jsonTarget (".Section-DiscussionList #Discussion_ $ discussionID " , 'Announcement ' , 'AddClass ' );
2114+ } else {
2115+ $ sender ->jsonTarget (".Section-DiscussionList #Discussion_ $ discussionID .Tag-Announcement " , null , 'Remove ' );
2116+ $ sender ->jsonTarget (".Section-DiscussionList #Discussion_ $ discussionID " , 'Announcement ' , 'RemoveClass ' );
2117+
2118+ }
2119+
2120+ $ sender ->jsonTarget ("#Discussion_ $ discussionID " , null , 'Highlight ' );
2121+ $ sender ->jsonTarget (".Discussion #Item_0 " , null , 'Highlight ' );
2122+
2123+ $ sender ->render ('Blank ' , 'Utility ' , 'Dashboard ' );
2124+ }
19872125}
19882126
19892127if (!function_exists ('topcoderRatingCssClass ' )) {
@@ -2483,3 +2621,50 @@ function topcoderMentionAnchor($mention, $cssClass = null, $options = null) {
24832621 }
24842622}
24852623
2624+ if (!function_exists ('watchingSorts ' )) {
2625+ /**
2626+ * Returns watching sorting.
2627+ *
2628+ * @param string $extraClasses any extra classes you add to the drop down
2629+ * @return string
2630+ */
2631+ function watchingSorts ($ extraClasses = '' ) {
2632+ if (!Gdn::session ()->isValid ()) {
2633+ return ;
2634+ }
2635+
2636+ $ baseUrl = preg_replace ('/\?.*/ ' , '' , Gdn::request ()->getFullPath ());
2637+ $ transientKey = Gdn::session ()->transientKey ();
2638+ $ filters = [
2639+ [
2640+ 'name ' => t ('New ' ),
2641+ 'param ' => 'sort ' ,
2642+ 'value ' => 'new ' ,
2643+ 'extra ' => ['TransientKey ' => $ transientKey , 'save ' => 1 ]
2644+ ],
2645+
2646+ [
2647+ 'name ' => t ('Old ' ),
2648+ 'param ' => 'sort ' ,
2649+ 'value ' => 'old ' ,
2650+ 'extra ' => ['TransientKey ' => $ transientKey , 'save ' => 1 ]
2651+ ]
2652+ ];
2653+
2654+ $ defaultParams = [];
2655+ if (!empty ($ defaultParams )) {
2656+ $ defaultUrl = $ baseUrl .'? ' .http_build_query ($ defaultParams );
2657+ } else {
2658+ $ defaultUrl = $ baseUrl ;
2659+ }
2660+
2661+ return sortsDropDown ('WatchingSort ' ,
2662+ $ baseUrl ,
2663+ $ filters ,
2664+ $ extraClasses ,
2665+ null ,
2666+ $ defaultUrl ,
2667+ 'Sort '
2668+ );
2669+ }
2670+ }
0 commit comments