| Index: trunk/extensions/catfeed/catfeed.php |
| — | — | @@ -34,58 +34,71 @@ |
| 35 | 35 | |
| 36 | 36 | global $IP; |
| 37 | 37 | require_once( "$IP/includes/CategoryPage.php" ); |
| | 38 | + require_once("Feed.php"); |
| 38 | 39 | |
| 39 | 40 | global $wgHooks; |
| 40 | 41 | |
| 41 | | - $wgHooks['CategoryPageView'][] = 'viewCatRSS'; |
| | 42 | + $wgHooks['CategoryPageView'][] = 'viewCatFeed'; |
| | 43 | + global $wgParser; |
| | 44 | + $wgParser->setHook( "catnews", "viewCatNewslist" ); |
| 42 | 45 | |
| 43 | | - class CategoryFeed extends CategoryPage { |
| | 46 | + |
| | 47 | + class CategoryByDate extends CategoryPage { |
| 44 | 48 | /** |
| 45 | 49 | * Feed for recently-added members of a category based on cl_timestamp |
| 46 | 50 | * Uses bits of the recentchanges feeds (caching and formatting) |
| 47 | 51 | * @package MediaWiki |
| 48 | 52 | */ |
| 49 | 53 | |
| 50 | | - function CategoryFeed( &$CategoryPage ) { |
| 51 | | - $this->mTitle = $CategoryPage->mTitle; |
| | 54 | + function CategoryByDate( &$title, $tarray = false ) { |
| | 55 | + global $wgRequest; |
| | 56 | + $this->mTitle = $title; |
| | 57 | + $this->mFeedFormat = $wgRequest->getVal( 'feed', '' ); |
| | 58 | + $this->mTitleStrings = array(); |
| | 59 | + if ( is_array($tarray) ) { |
| | 60 | + foreach($tarray as $title) { |
| | 61 | + $this->mTitleStrings[] = $title->getDBKey(); |
| | 62 | + } |
| | 63 | + } else { |
| | 64 | + $this->mTitleStrings[] = $title->getDBKey(); |
| | 65 | + } |
| 52 | 66 | } |
| 53 | 67 | |
| 54 | 68 | function view() { |
| 55 | | - global $wgRequest; |
| 56 | | - require_once("Feed.php"); |
| 57 | | - $this->mFeedFormat = $wgRequest->getVal( 'feed', '' ); |
| 58 | | - if ( $this->mFeedFormat == '') return true; # let CategoryPage::view continue, no feed requested |
| | 69 | + // cache handling in subclass |
| | 70 | + return $this->getData(); |
| | 71 | + } |
| | 72 | + function getData() { |
| 59 | 73 | $this->mMaxTimeStamp = 0; |
| | 74 | + |
| 60 | 75 | $limit = 50; |
| 61 | 76 | $dbr =& wfGetDB( DB_SLAVE ); |
| 62 | 77 | $res = $dbr->select( |
| 63 | 78 | array( 'cur', 'categorylinks' ), |
| 64 | 79 | array( 'cur_title', 'cur_namespace', 'cur_text', 'cur_user_text', 'cl_sortkey', 'cl_timestamp' ), |
| 65 | 80 | array( 'cl_from = cur_id', |
| 66 | | - 'cl_to' => $this->mTitle->getDBKey(), |
| | 81 | + 'cl_to IN ("'.implode('","', $this->mTitleStrings).'")', |
| 67 | 82 | 'cur_is_redirect' => 0), |
| 68 | 83 | $fname, |
| 69 | 84 | array( 'ORDER BY' => 'cl_timestamp DESC, cl_sortkey ASC', |
| 70 | 85 | 'LIMIT' => $limit )); |
| 71 | 86 | $rows = array(); |
| 72 | | - while( $row = $dbr->fetchObject ( $res ) ) { |
| 73 | | - $rows[] = $row; |
| 74 | | - if ( $row->cl_timestamp > $this->mMaxTimeStamp ) { |
| 75 | | - $this->mMaxTimeStamp = $row->cl_timestamp; |
| 76 | | - } |
| | 87 | + while( $row = $dbr->fetchObject ( $res ) ) { |
| | 88 | + $rows[] = $row; |
| | 89 | + if ( $row->cl_timestamp > $this->mMaxTimeStamp ) { |
| | 90 | + $this->mMaxTimeStamp = $row->cl_timestamp; |
| 77 | 91 | } |
| 78 | | - $this->categoryOutputFeed( &$rows, $limit ); |
| 79 | | - # stop CategoryPage::view from continuing |
| 80 | | - return false; |
| | 92 | + } |
| | 93 | + return $this->formatRows( &$rows ); |
| 81 | 94 | } |
| 82 | 95 | |
| 83 | 96 | # strip images, links, tags |
| 84 | | - function formatSummary ( $row ) { |
| | 97 | + function formatSummary ( $text ) { |
| 85 | 98 | global $wgContLang; |
| 86 | 99 | $prefixes = array_keys($wgContLang->getLanguageNames()); |
| 87 | 100 | $prefixes[] = $wgContLang->getNsText(NS_CATEGORY); |
| 88 | 101 | $imgprefix = $wgContLang->getNsText(NS_IMAGE); |
| 89 | | - $text = "\n".$row->cur_text; |
| | 102 | + $text = "\n".$text; |
| 90 | 103 | |
| 91 | 104 | $rules = array( |
| 92 | 105 | "/\[\[(".implode('|',$prefixes)."):[^\]]*\]\]/i" => "", # interwiki links, cat links |
| — | — | @@ -115,87 +128,137 @@ |
| 116 | 129 | return htmlspecialchars( $shorttext.'...'); |
| 117 | 130 | } |
| 118 | 131 | |
| 119 | | - function categoryOutputFeed( $rows, $limit ) { |
| 120 | | - global $messageMemc, $wgDBname, $wgFeedCacheTimeout; |
| | 132 | + } |
| | 133 | + |
| | 134 | + class CategoryByDateFeed extends CategoryByDate { |
| | 135 | + |
| | 136 | + function view() { |
| | 137 | + global $wgRequest; |
| | 138 | + global $messageMemc, $wgDBname; |
| 121 | 139 | global $wgFeedClasses, $wgTitle, $wgSitename, $wgContLanguageCode; |
| 122 | 140 | |
| 123 | 141 | if( !isset( $wgFeedClasses[$this->mFeedFormat] ) ) { |
| 124 | 142 | wfHttpError( 500, "Internal Server Error", "Unsupported feed type." ); |
| 125 | 143 | return false; |
| 126 | 144 | } |
| 127 | | - |
| 128 | | - $timekey = "$wgDBname:catfeed:" . $this->mTitle->getDBKey() . ":timestamp"; |
| 129 | | - $key = "$wgDBname:catfeed:" . $this->mTitle->getDBKey() . ":$this->mFeedFormat:limit:$limit"; |
| 130 | | - |
| 131 | 145 | $feedTitle = $this->mTitle->getPrefixedText() . ' - ' . $wgSitename; |
| 132 | | - $feed = new $wgFeedClasses[$this->mFeedFormat]( |
| | 146 | + $this->feed = new $wgFeedClasses[$this->mFeedFormat]( |
| 133 | 147 | $feedTitle, |
| 134 | 148 | htmlspecialchars( wfMsgForContent( 'catfeedsummary' ) ), |
| 135 | 149 | $wgTitle->getFullUrl() ); |
| 136 | 150 | |
| 137 | | - /** |
| 138 | | - * Loading and parsing cur_text for all added pages is slow, so we cache it |
| 139 | | - */ |
| 140 | | - $cachedFeed = false; |
| 141 | | - if( $feedLastmod = $messageMemc->get( $timekey ) ) { |
| 142 | | - /** |
| 143 | | - * If the cached feed was rendered very recently, we may |
| 144 | | - * go ahead and use it even if there have been edits made |
| 145 | | - * since it was rendered. This keeps a swarm of requests |
| 146 | | - * from being too bad on a super-frequently edited wiki. |
| 147 | | - */ |
| 148 | | - if( time() - wfTimestamp( TS_UNIX, $feedLastmod ) |
| 149 | | - < $wgFeedCacheTimeout |
| 150 | | - || wfTimestamp( TS_UNIX, $feedLastmod ) |
| 151 | | - > wfTimestamp( TS_UNIX, $this->mMaxTimeStamp ) ) { |
| 152 | | - wfDebug( "CatFeed: loading feed from cache ($key; $feedLastmod; $this->mMaxTimeStamp)...\n" ); |
| 153 | | - $cachedFeed = $messageMemc->get( $key ); |
| 154 | | - } else { |
| 155 | | - wfDebug( "CatFeed: cached feed timestamp check failed ($feedLastmod; $this->mMaxTimeStamp)\n" ); |
| 156 | | - } |
| 157 | | - } |
| 158 | | - if( is_string( $cachedFeed ) ) { |
| 159 | | - wfDebug( "CatFeed: Outputting cached feed\n" ); |
| 160 | | - $feed->httpHeaders(); |
| 161 | | - echo $cachedFeed; |
| 162 | | - } else { |
| 163 | | - wfDebug( "CatFeed: rendering new feed and caching it\n" ); |
| 164 | | - ob_start(); |
| 165 | | - $this->catDoOutputFeed( $rows, $feed ); |
| 166 | | - $cachedFeed = ob_get_contents(); |
| 167 | | - ob_end_flush(); |
| | 151 | + $timekey = "$wgDBname:catfeed:" . $this->mTitle->getDBKey() . ":$this->mFeedFormat:limit:$limit:timestamp"; |
| | 152 | + $key = "$wgDBname:catfeed:" . $this->mTitle->getDBKey() . ":$this->mFeedFormat:limit:$limit"; |
| | 153 | + $cachedFeed = false; |
| | 154 | + $adddeltimestamp = $wgDBname.':Category:'.$wgTitle->getDBkey().':adddeltimestamp'; |
| | 155 | + |
| | 156 | + $catLastAddDel = $messageMemc->get( $adddeltimestamp ); |
| 168 | 157 | |
| 169 | | - $expire = 3600 * 24; # One day |
| 170 | | - $messageMemc->set( $key, $cachedFeed ); |
| 171 | | - $messageMemc->set( $timekey, wfTimestamp( TS_MW ), $expire ); |
| 172 | | - } |
| 173 | | - return true; |
| | 158 | + if( $feedLastmod = $messageMemc->get( $timekey ) |
| | 159 | + and $catLastAddDel <= $feedLastmod ) { |
| | 160 | + wfDebug( "CatFeed: loading feed from cache ($key; $feedLastmod; $catLastAddDel )...\n" ); |
| | 161 | + $cachedFeed = $messageMemc->get( $key ); |
| | 162 | + } else { |
| | 163 | + wfDebug( "CatFeed: cached feed timestamp check failed ($feedLastmod; $catLastAddDel) timekey: $timekey; adddel: $adddeltimestamp \n" ); |
| | 164 | + |
| | 165 | + } |
| | 166 | + if( is_string( $cachedFeed ) ) { |
| | 167 | + wfDebug( "CatFeed: Outputting cached feed\n" ); |
| | 168 | + $this->feed->httpHeaders(); |
| | 169 | + echo $cachedFeed; |
| | 170 | + } else { |
| | 171 | + wfDebug( "CatFeed: rendering new feed and caching it\n" ); |
| | 172 | + ob_start(); |
| | 173 | + $this->getData(); |
| | 174 | + $cachedFeed = ob_get_contents(); |
| | 175 | + ob_end_flush(); |
| | 176 | + |
| | 177 | + $expire = 3600 * 24; # One day |
| | 178 | + $messageMemc->set( $key, $cachedFeed ); |
| | 179 | + $messageMemc->set( $timekey, $catLastAddDel , $expire ); |
| | 180 | + } |
| | 181 | + return true; |
| 174 | 182 | } |
| 175 | 183 | |
| 176 | | - function catDoOutputFeed( $rows, &$feed ) { |
| | 184 | + function formatRows( $rows ) { |
| 177 | 185 | global $wgSitename, $wgFeedClasses, $wgContLanguageCode; |
| 178 | 186 | |
| 179 | | - $feed->outHeader(); |
| | 187 | + $this->feed->outHeader(); |
| 180 | 188 | foreach( $rows as $row ) { |
| 181 | 189 | $title = Title::makeTitle( $row->cur_namespace, $row->cur_title ); |
| 182 | 190 | $item = new FeedItem( |
| 183 | 191 | $title->getPrefixedText(), |
| 184 | | - $this->formatSummary( &$row ), |
| | 192 | + $this->formatSummary( $row->cur_text ), |
| 185 | 193 | $title->getFullURL(), |
| 186 | | - $row->lc_timestamp, |
| | 194 | + $row->cl_timestamp, |
| 187 | 195 | $row->cur_user_text, |
| 188 | 196 | '' #$talkpage->getFullURL() |
| 189 | 197 | ); |
| 190 | | - $feed->outItem( $item ); |
| | 198 | + $this->feed->outItem( $item ); |
| 191 | 199 | } |
| 192 | | - $feed->outFooter(); |
| | 200 | + $this->feed->outFooter(); |
| 193 | 201 | } |
| | 202 | + |
| 194 | 203 | } |
| 195 | 204 | |
| | 205 | + class CategoryByDateNewslist extends CategoryByDate { |
| | 206 | + |
| | 207 | + function formatRows( $rows ) { |
| | 208 | + # format members of a category as 'news list' within a page |
| | 209 | + # useful for portals, probably wikinews etc |
| | 210 | + # todo: allow multiple categories to be merged ('or' in sql) |
| | 211 | + global $wgUser; |
| | 212 | + $skin = &$wgUser->getSkin(); |
| | 213 | + $list = ''; |
| | 214 | + $ts = $closedl = $date = $oldns = $oldtitle = ''; |
| | 215 | + foreach( $rows as $row ) { |
| | 216 | + # check for duplicates, cheaper than in the db |
| | 217 | + if($row->cur_namespace != $oldns or $row->cur_title != $oldtitle) { |
| | 218 | + $oldns = $row->cur_namespace; |
| | 219 | + $oldtitle = $row->cur_title; |
| | 220 | + $title = Title::makeTitle( $row->cur_namespace, $row->cur_title ); |
| | 221 | + $ts = $row->cl_timestamp; |
| | 222 | + $newdate = gmdate( 'D, d M Y', wfTimestamp( TS_UNIX, $ts ) ); |
| | 223 | + if( $date != $newdate ) { |
| | 224 | + $date = $newdate; |
| | 225 | + $list .= "$closedl\n<h2> ".$date." </h2>\n<dl>"; |
| | 226 | + $closedl = '</dl>'; |
| | 227 | + } |
| | 228 | + $list .= '<dt>' . $skin->makeKnownLinkObj($title) . |
| | 229 | + ' <span style="font-size: 0.76em;font-weight:normal;">' . |
| | 230 | + gmdate( 'H:i:s \G\M\T', wfTimestamp( TS_UNIX, $ts ) ) . '</span></dt><dd> ' . |
| | 231 | + $this->formatSummary( $row->cur_text ).'</dd>'; |
| | 232 | + } |
| | 233 | + } |
| | 234 | + return $list . $closedl; |
| | 235 | + } |
| | 236 | + } |
| | 237 | + |
| 196 | 238 | } |
| 197 | 239 | |
| 198 | | -function viewCatRSS( &$CategoryPage ) { |
| 199 | | - $catfeed = new CategoryFeed($CategoryPage); |
| 200 | | - return $catfeed->view(); |
| | 240 | +function viewCatFeed( &$CategoryPage ) { |
| | 241 | + global $wgRequest; |
| | 242 | + $catfeed = new CategoryByDateFeed($CategoryPage->mTitle); |
| | 243 | + # nothing to do,CategoryPage::view continues |
| | 244 | + if(!$wgRequest->getBool('feed',false)) return true; |
| | 245 | + |
| | 246 | + # else continue |
| | 247 | + $catfeed->view(); |
| | 248 | + # stop CategoryPage::view from continuing |
| | 249 | + return false; |
| 201 | 250 | } |
| | 251 | +function viewCatNewslist( $input ) { |
| | 252 | + $text = ''; |
| | 253 | + $iptitles = split("\n",trim($input)); |
| | 254 | + $dbtitles = array(); |
| | 255 | + foreach ( $iptitles as $title ) { |
| | 256 | + $dbtitles[] = Title::newFromUrl($title); |
| | 257 | + } |
| | 258 | + # search for 5 categories max for now |
| | 259 | + $dbtitles = array_slice($dbtitles, 0, 4); |
| | 260 | + $catnews = new CategoryByDateNewslist($dbtitles[0], $dbtitles); |
| | 261 | + $text .= $catnews->view(); |
| | 262 | + |
| | 263 | + return $text; |
| | 264 | +} |
| 202 | 265 | ?> |