Index: trunk/extensions/LiquidThreads/i18n/Lqt.i18n.php |
— | — | @@ -191,6 +191,7 @@ |
192 | 192 | 'lqt-movethread' => 'Move', |
193 | 193 | 'lqt-menu-trigger' => 'More', |
194 | 194 | 'lqt-newmessages-from' => 'From $1', |
| 195 | + 'lqt-hot-topics' => 'Hot Topics', |
195 | 196 | |
196 | 197 | // Rights |
197 | 198 | 'right-lqt-split' => 'Split threads', |
Index: trunk/extensions/LiquidThreads/LiquidThreads.php |
— | — | @@ -98,6 +98,7 @@ |
99 | 99 | $wgSpecialPages['NewMessages'] = 'SpecialNewMessages'; |
100 | 100 | $wgSpecialPages['SplitThread'] = 'SpecialSplitThread'; |
101 | 101 | $wgSpecialPages['MergeThread'] = 'SpecialMergeThread'; |
| 102 | +//$wgSpecialPages['HotTopics'] = 'SpecialHotTopics'; |
102 | 103 | $wgSpecialPageGroups['NewMessages'] = 'wiki'; |
103 | 104 | |
104 | 105 | // Classes |
— | — | @@ -115,6 +116,7 @@ |
116 | 117 | $wgAutoloadClasses['SynchroniseThreadArticleDataJob'] = "$dir/classes/SynchroniseThreadArticleDataJob.php"; |
117 | 118 | $wgAutoloadClasses['ThreadHistoryPager'] = "$dir/classes/ThreadHistoryPager.php"; |
118 | 119 | $wgAutoloadClasses['TalkpageHistoryView'] = "$dir/pages/TalkpageHistoryView.php"; |
| 120 | +$wgAutoloadClasses['LqtHotTopicsController'] = "$dir/classes/HotTopics.php"; |
119 | 121 | |
120 | 122 | // View classes |
121 | 123 | $wgAutoloadClasses['TalkpageView'] = $dir . 'pages/TalkpageView.php'; |
— | — | @@ -135,6 +137,7 @@ |
136 | 138 | $wgAutoloadClasses['SpecialNewMessages'] = $dir . 'pages/SpecialNewMessages.php'; |
137 | 139 | $wgAutoloadClasses['SpecialSplitThread'] = $dir . 'pages/SpecialSplitThread.php'; |
138 | 140 | $wgAutoloadClasses['SpecialMergeThread'] = $dir . 'pages/SpecialMergeThread.php'; |
| 141 | +$wgAutoloadClasses['SpecialHotTopics'] = "$dir/pages/SpecialHotTopics.php"; |
139 | 142 | |
140 | 143 | // Job queue |
141 | 144 | $wgJobClasses['synchroniseThreadArticleData'] = 'SynchroniseThreadArticleDataJob'; |
Index: trunk/extensions/LiquidThreads/classes/View.php |
— | — | @@ -40,6 +40,11 @@ |
41 | 41 | $this->user_colors = array(); |
42 | 42 | $this->user_color_index = 1; |
43 | 43 | } |
| 44 | + |
| 45 | + static function getView() { |
| 46 | + global $wgOut, $wgArticle, $wgTitle, $wgUser, $wgRequest; |
| 47 | + return new LqtView( $wgOut, $wgArticle, $wgTitle, $wgUser, $wgRequest ); |
| 48 | + } |
44 | 49 | |
45 | 50 | function setHeaderLevel( $int ) { |
46 | 51 | $this->headerLevel = $int; |
Index: trunk/extensions/LiquidThreads/classes/HotTopics.php |
— | — | @@ -0,0 +1,122 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +/* Test |
| 5 | +print_r( LqtHotTopicsController::generateHotTopics() ) |
| 6 | +*/ |
| 7 | +class LqtHotTopicsController { |
| 8 | + public static function generateHotTopics( $count = 10 ) { |
| 9 | + $dbr = wfGetDB( DB_SLAVE ); |
| 10 | + |
| 11 | + $now = wfTimestamp( TS_UNIX, wfTimestampNow() ); |
| 12 | + $dateCutoff = $dbr->addQuotes( $dbr->timestamp( $now - (7*86400) ) ); |
| 13 | + |
| 14 | + $conds = array(); |
| 15 | +// $conds[] ='th_timestamp>'.$dateCutoff; |
| 16 | + |
| 17 | + // Grab the ID cutoff |
| 18 | + $idCutoff = $dbr->selectField( 'thread_history', 'th_id', |
| 19 | + array( 'th_timestamp>'.$dateCutoff ), |
| 20 | + __METHOD__, |
| 21 | + array( 'ORDER BY' => 'th_id desc', |
| 22 | + 'OFFSET' => 10000 ) |
| 23 | + ); |
| 24 | + |
| 25 | + if ( $idCutoff ) { |
| 26 | + $idCutoff = $dbr->addQuotes( $idCutoff ); |
| 27 | + $conds[] = 'th_id>'.$idCutoff; |
| 28 | + } |
| 29 | + |
| 30 | + $res = $dbr->select( array( 'thread_history' ), |
| 31 | + array( 'th_id', 'th_thread', 'th_timestamp' ), |
| 32 | + $conds, |
| 33 | + __METHOD__, |
| 34 | + array( 'LIMIT' => 10000 ) |
| 35 | + ); |
| 36 | + |
| 37 | + $threads = array(); |
| 38 | + |
| 39 | + foreach( $res as $row ) { |
| 40 | + if ( isset( $threads[$row->th_thread] ) ) { |
| 41 | + $thread =& $threads[$row->th_thread]; |
| 42 | + $thread['count']++; |
| 43 | + |
| 44 | + if ( $thread['firstpost'] > $row->th_timestamp ) { |
| 45 | + $thread['firstpost'] = $row->th_timestamp; |
| 46 | + } |
| 47 | + |
| 48 | + if ( $thread['lastpost'] < $row->th_timestamp ) { |
| 49 | + $thread['lastpost'] = $row->th_timestamp; |
| 50 | + } |
| 51 | + unset($thread); |
| 52 | + } else { |
| 53 | + $thread = array(); |
| 54 | + |
| 55 | + $thread['id'] = $row->th_thread; |
| 56 | + $thread['count'] = 1; |
| 57 | + $thread['firstpost'] = $row->th_timestamp; |
| 58 | + $thread['lastpost'] = $row->th_timestamp; |
| 59 | + |
| 60 | + $threads[$row->th_thread] = $thread; |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + foreach( $threads as &$thread ) { |
| 65 | + $thread['rate'] = self::getThreadPostRate( $thread ); |
| 66 | + } |
| 67 | + |
| 68 | + // Filter out useless stuff |
| 69 | + $threads = array_filter( $threads, array( __CLASS__, 'threadFilterCallback' ) ); |
| 70 | + |
| 71 | + // Sort |
| 72 | + usort( $threads, array( __CLASS__, 'threadSortCallback' ) ); |
| 73 | + |
| 74 | + $threads = array_slice( $threads, 0, $count, true ); |
| 75 | + |
| 76 | + $outputThreads = array(); |
| 77 | + |
| 78 | + foreach( $threads as $thread ) { |
| 79 | + $outputThreads[$thread['id']] = $thread['id']; |
| 80 | + } |
| 81 | + |
| 82 | + return $outputThreads; |
| 83 | + } |
| 84 | + |
| 85 | + public static function getHotThreads( $count = 10 ) { |
| 86 | + $topics = array_values( self::generateHotTopics( $count ) ); |
| 87 | + |
| 88 | + return Threads::where( array( 'thread_id' => $topics ) ); |
| 89 | + } |
| 90 | + |
| 91 | + public static function threadFilterCallback( $entry ) { |
| 92 | + return $entry['count'] > 3; |
| 93 | + } |
| 94 | + |
| 95 | + public static function threadSortCallback( $a, $b ) { |
| 96 | + $rateA = floatval($a['rate']); |
| 97 | + $rateB = floatval($b['rate']); |
| 98 | + |
| 99 | + if ( $rateA == $rateB ) { |
| 100 | + $val = 0; |
| 101 | + } elseif ( $rateA < $rateB ) { |
| 102 | + $val = 1; |
| 103 | + } elseif ( $rateA > $rateB ) { |
| 104 | + $val = -1; |
| 105 | + } |
| 106 | + |
| 107 | + return $val; |
| 108 | + } |
| 109 | + |
| 110 | + public static function getThreadPostRate( $entry ) { |
| 111 | + if ( $entry['count'] < 2 ) { |
| 112 | + return 0; |
| 113 | + } |
| 114 | + |
| 115 | + $startTime = wfTimestamp( TS_UNIX, $entry['firstpost'] ); |
| 116 | + $endTime = wfTimestamp( TS_UNIX, wfTimestampNow() ); |
| 117 | + $duration = $endTime - $startTime; |
| 118 | + |
| 119 | + // Get count over duration, multiply out to give posts per day |
| 120 | + |
| 121 | + return ( $entry['count'] / $duration ) * 86400; |
| 122 | + } |
| 123 | +} |
Index: trunk/extensions/LiquidThreads/lqt.sql |
— | — | @@ -80,6 +80,6 @@ |
81 | 81 | |
82 | 82 | PRIMARY KEY (th_id), |
83 | 83 | KEY (th_thread,th_timestamp), |
84 | | - KEY (th_timestamp), |
| 84 | + KEY (th_timestamp,th_thread), |
85 | 85 | KEY (th_user,th_user_text) |
86 | 86 | ) /*$wgDBTableOptions*/; |
Index: trunk/extensions/LiquidThreads/pages/SpecialHotTopics.php |
— | — | @@ -0,0 +1,31 @@ |
| 2 | +<?php |
| 3 | + |
| 4 | +class SpecialHotTopics extends SpecialPage { |
| 5 | + function __construct() { |
| 6 | + parent::__construct( 'HotTopics' ); |
| 7 | + } |
| 8 | + |
| 9 | + function execute( $par ) { |
| 10 | + global $wgOut; |
| 11 | + |
| 12 | + wfLoadExtensionMessages( 'LiquidThreads' ); |
| 13 | + |
| 14 | + $this->setHeaders(); |
| 15 | + |
| 16 | + $wgOut->setPageTitle( wfMsg( 'lqt-hot-topics' ) ); |
| 17 | + $view = LqtView::getView(); |
| 18 | + |
| 19 | + LqtView::addJsAndCss(); |
| 20 | + |
| 21 | + // Get hot topics |
| 22 | + $topics = LqtHotTopicsController::getHotThreads(); |
| 23 | + |
| 24 | + foreach( $topics as $thread ) { |
| 25 | + $view->showThread( $thread ); |
| 26 | + } |
| 27 | + } |
| 28 | + |
| 29 | + function getPageName() { |
| 30 | + return wfMsg( 'lqt-hot-topics' ); |
| 31 | + } |
| 32 | +} |