From 4611030ce9ed9f0b995c9e1c6bc9f43236da3418 Mon Sep 17 00:00:00 2001 From: Alexandre Emsenhuber Date: Thu, 26 Jun 2008 19:12:52 +0000 Subject: [PATCH] Rewritten Special:Recentchangeslinked, now using a subclass of SpecialRecentchanges: * (bugs 4832, 9481, 12890) Now has all options present in Special:Recentchanges (hide myself, bots, ...) * Using "showlinkedto" on a template now uses the templatelinks table to fetch links instead of pagelinks * [[Special:Recentchanges/0]] now works as expected, thanks to Nikerabbit for pointing this out --- RELEASE-NOTES | 10 +- includes/AutoLoader.php | 3 +- includes/SpecialPage.php | 2 +- includes/specials/SpecialRecentchanges.php | 518 ++++++++++-------- .../specials/SpecialRecentchangeslinked.php | 275 ++++------ 5 files changed, 400 insertions(+), 408 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 953d72f62e..526fbb6c30 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -162,10 +162,14 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN * (bug 14558) New system message (emailuserfooter) is now added to the footer of e-mails sent with Special:Emailuser * Add support for Hijri (Islamic) calendar -* Add a new hook LinkerMakeExternalImage to allow extensions to modify the output of - external (hotlinked) images. -* (bug 14604) Introduced the following features for the LanguageConverter: Multi-tag support, single conversion flag, remove conversion flag on a single page, description flag, variant name, multi-variant fallbacks. +* Add a new hook LinkerMakeExternalImage to allow extensions to modify the output + of external (hotlinked) images. +* (bug 14604) Introduced the following features for the LanguageConverter: + Multi-tag support, single conversion flag, remove conversion flag on a single + page, description flag, variant name, multi-variant fallbacks. * Add zh-mo and zh-my variants for the zh language +* (bugs 4832, 9481, 12890) Special:Recentchangeslinked now has all options that + are in Special:Recentchanges === Bug fixes in 1.13 === diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index fa4efb8ce3..c66ef5976c 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -423,7 +423,8 @@ class AutoLoader { 'SpecialMostlinkedtemplates' => 'includes/specials/SpecialMostlinkedtemplates.php', 'SpecialPrefixindex' => 'includes/specials/SpecialPrefixindex.php', 'SpecialRandomredirect' => 'includes/specials/SpecialRandomredirect.php', - 'SpecialRecentChanges' => 'includes/specials/SpecialRecentchanges.php', + 'SpecialRecentchanges' => 'includes/specials/SpecialRecentchanges.php', + 'SpecialRecentchangeslinked' => 'includes/specials/SpecialRecentchangeslinked.php', 'SpecialSearch' => 'includes/specials/SpecialSearch.php', 'SpecialVersion' => 'includes/specials/SpecialVersion.php', 'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php', diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php index e092980c4f..4a50b81a83 100644 --- a/includes/SpecialPage.php +++ b/includes/SpecialPage.php @@ -128,7 +128,7 @@ class SpecialPage 'Contributions' => array( 'SpecialPage', 'Contributions' ), 'Emailuser' => array( 'UnlistedSpecialPage', 'Emailuser' ), 'Whatlinkshere' => array( 'SpecialPage', 'Whatlinkshere' ), - 'Recentchangeslinked' => array( 'SpecialPage', 'Recentchangeslinked' ), + 'Recentchangeslinked' => 'SpecialRecentchangeslinked', 'Movepage' => array( 'UnlistedSpecialPage', 'Movepage' ), 'Blockme' => array( 'UnlistedSpecialPage', 'Blockme' ), 'Resetpass' => array( 'UnlistedSpecialPage', 'Resetpass' ), diff --git a/includes/specials/SpecialRecentchanges.php b/includes/specials/SpecialRecentchanges.php index 7395789fed..01b290927f 100644 --- a/includes/specials/SpecialRecentchanges.php +++ b/includes/specials/SpecialRecentchanges.php @@ -1,15 +1,20 @@ includable( true ); } + /** + * Get a FormOptions object containing the default options + * + * @return FormOptions + */ public function getDefaultOptions() { $opts = new FormOptions(); @@ -31,8 +36,13 @@ class SpecialRecentChanges extends SpecialPage { $opts->add( 'categories_any', false ); return $opts; -} + } + /** + * Get a FormOptions object with options as specified by the user + * + * @return FormOptions + */ public function setup( $parameters ) { global $wgUser, $wgRequest; @@ -51,6 +61,11 @@ class SpecialRecentChanges extends SpecialPage { return $opts; } + /** + * Get a FormOptions object sepcific for feed requests + * + * @return FormOptions + */ public function feedSetup() { global $wgFeedLimit, $wgRequest; $opts = $this->getDefaultOptions(); @@ -59,6 +74,11 @@ class SpecialRecentChanges extends SpecialPage { return $opts; } + /** + * Main execution point + * + * @param $parameters string + */ public function execute( $parameters ) { global $wgRequest, $wgOut; $feedFormat = $wgRequest->getVal( 'feed' ); @@ -73,12 +93,17 @@ class SpecialRecentChanges extends SpecialPage { $opts = $feedFormat ? $this->feedSetup() : $this->setup( $parameters ); $this->setHeaders(); + $this->outputHeader(); // Fetch results, prepare a batch link existence check query $rows = array(); $batch = new LinkBatch; $conds = $this->buildMainQueryConds( $opts ); $res = $this->doMainQuery( $conds, $opts ); + if( $res === false ){ + $this->doHeader( $opts ); + return; + } $dbr = wfGetDB( DB_SLAVE ); while( $row = $dbr->fetchObject( $res ) ){ $rows[] = $row; @@ -92,11 +117,7 @@ class SpecialRecentChanges extends SpecialPage { $dbr->freeResult( $res ); if ( $feedFormat ) { - $feed = new ChangesFeed( $feedFormat, 'rcfeed' ); - $feedObj = $feed->getFeedObject( - wfMsgForContent( 'recentchanges' ), - wfMsgForContent( 'recentchanges-feed-description' ) - ); + list( $feed, $feedObj ) = $this->getFeedObject( $feedFormat ); $feed->execute( $feedObj, $rows, $opts['limit'], $opts['hideminor'], $lastmod ); } else { $batch->execute(); @@ -104,6 +125,27 @@ class SpecialRecentChanges extends SpecialPage { } } + /** + * Return an array with a ChangesFeed object and ChannelFeed object + * + * @return array + */ + public function getFeedObject( $feedFormat ){ + $feed = new ChangesFeed( $feedFormat, 'rcfeed' ); + $feedObj = $feed->getFeedObject( + wfMsgForContent( 'recentchanges' ), + wfMsgForContent( 'recentchanges-feed-description' ) + ); + return array( $feed, $feedObj ); + } + + /** + * Process $par and put options found if $opts + * Mainly used when including the page + * + * @param $par String + * @param $opts FormOptions + */ public function parseParameters( $par, FormOptions $opts ) { $bits = preg_split( '/\s*,\s*/', trim( $par ) ); foreach ( $bits as $bit ) { @@ -124,8 +166,14 @@ class SpecialRecentChanges extends SpecialPage { } } - # Get last modified date, for client caching - # Don't use this if we are using the patrol feature, patrol changes don't update the timestamp + /** + * Get last modified date, for client caching + * Don't use this if we are using the patrol feature, patrol changes don't + * update the timestamp + * + * @param $feedFormat String + * @return int or false + */ public function checkLastModified( $feedFormat ) { global $wgUseRCPatrol, $wgOut; $dbr = wfGetDB( DB_SLAVE ); @@ -139,6 +187,12 @@ class SpecialRecentChanges extends SpecialPage { return $lastmod; } + /** + * Return an array of conditions depending of options set in $opts + * + * @param $opts FormOptions + * @return array + */ public function buildMainQueryConds( FormOptions $opts ) { global $wgUser; @@ -191,7 +245,7 @@ class SpecialRecentChanges extends SpecialPage { $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $wgUser->getName() ); } } - + # Namespace filtering if ( $opts['namespace'] !== '' ) { if ( !$opts['invert'] ) { @@ -204,6 +258,13 @@ class SpecialRecentChanges extends SpecialPage { return $conds; } + /** + * Process the query + * + * @param $conds array + * @param $opts FormOptions + * @return database result or false (for Recentchangeslinked only) + */ public function doMainQuery( $conds, $opts ) { global $wgUser; @@ -217,7 +278,7 @@ class SpecialRecentChanges extends SpecialPage { $invert = $opts['invert']; // JOIN on watchlist for users - if( $wgUser->getId() ) { + if( $uid ) { $tables[] = 'watchlist'; $join_conds = array( 'watchlist' => array('LEFT JOIN',"wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace") ); } @@ -228,7 +289,7 @@ class SpecialRecentChanges extends SpecialPage { // Also, if this is "all" or main namespace, just use timestamp index. if( is_null($namespace) || $invert || $namespace == NS_MAIN ) { $res = $dbr->select( $tables, '*', $conds, __METHOD__, - array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, + array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, 'USE INDEX' => array('recentchanges' => 'rc_timestamp') ), $join_conds ); // We have a new_namespace_time index! UNION over new=(0,1) and sort result set! @@ -237,14 +298,14 @@ class SpecialRecentChanges extends SpecialPage { $sqlNew = $dbr->selectSQLText( $tables, '*', array( 'rc_new' => 1 ) + $conds, __METHOD__, - array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, + array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, 'USE INDEX' => array('recentchanges' => 'new_name_timestamp') ), $join_conds ); // Old pages $sqlOld = $dbr->selectSQLText( $tables, '*', array( 'rc_new' => 0 ) + $conds, __METHOD__, - array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, + array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit, 'USE INDEX' => array('recentchanges' => 'new_name_timestamp') ), $join_conds ); # Join the two fast queries, and sort the result set @@ -255,6 +316,12 @@ class SpecialRecentChanges extends SpecialPage { return $res; } + /** + * Send output to $wgOut, only called if not used feeds + * + * @param $rows array of database rows + * @param $opts FormOptions + */ public function webOutput( $rows, $opts ) { global $wgOut, $wgUser, $wgRCShowWatchingUsers, $wgShowUpdatedMarker; global $wgAllowCategorizedRecentChanges; @@ -272,7 +339,7 @@ class SpecialRecentChanges extends SpecialPage { $list = ChangesList::newFromUser( $wgUser ); if ( $wgAllowCategorizedRecentChanges ) { - rcFilterByCategories( $rows, $opts ); + $this->filterByCategories( $rows, $opts ); } $s = $list->beginRecentChangesList(); @@ -323,27 +390,25 @@ class SpecialRecentChanges extends SpecialPage { $wgOut->addHTML( $s ); } + /** + * Return the text to be displayed above the changes + * + * @param $opts FormOptions + * @return String: XHTML + */ public function doHeader( $opts ) { global $wgScript, $wgOut; - $wgOut->addWikiText( wfMsgForContentNoTrans( 'recentchangestext' ) ); + + $this->setTopText( $wgOut, $opts ); $defaults = $opts->getAllValues(); $nondefaults = $opts->getChangedValues(); $opts->consumeValues( array( 'namespace', 'invert' ) ); $panel = array(); - $panel[] = rcOptionsPanel( $defaults, $nondefaults ); + $panel[] = $this->optionsPanel( $defaults, $nondefaults ); - $extraOpts = array(); - $extraOpts['namespace'] = $this->namespaceFilterForm( $opts ); - - global $wgAllowCategorizedRecentChanges; - if ( $wgAllowCategorizedRecentChanges ) { - $extraOpts['category'] = $this->categoryFilterForm( $opts ); - } - - wfRunHooks( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) ); - $extraOpts['submit'] = Xml::submitbutton( wfMsg('allpagessubmit') ); + $extraOpts = $this->getExtraOptions( $opts ); $out = Xml::openElement( 'table' ); foreach ( $extraOpts as $optionRow ) { @@ -370,13 +435,55 @@ class SpecialRecentChanges extends SpecialPage { $panelString = implode( "\n", $panel ); $wgOut->addHTML( '
' . $panelString . '
' ); + + $this->setBottomText( $wgOut, $opts ); + } + + /** + * Get options to be displayed in a form + * + * @param $opts FormOptions + * @return array + */ + function getExtraOptions( $opts ){ + $extraOpts = array(); + $extraOpts['namespace'] = $this->namespaceFilterForm( $opts ); + + global $wgAllowCategorizedRecentChanges; + if ( $wgAllowCategorizedRecentChanges ) { + $extraOpts['category'] = $this->categoryFilterForm( $opts ); + } + + wfRunHooks( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) ); + $extraOpts['submit'] = Xml::submitbutton( wfMsg('allpagessubmit') ); + return $extraOpts; + } + + /** + * Send the text to be displayed above the options + * + * @param $out OutputPage + * @param $opts FormOptions + */ + function setTopText( &$out, $opts ){ + $out->addWikiText( wfMsgForContentNoTrans( 'recentchangestext' ) ); } /** - * Creates the choose namespace selection - * - * @return string - */ + * Send the text to be displayed after the options, for use in + * Recentchangeslinked + * + * @param $out OutputPage + * @param $opts FormOptions + */ + function setBottomText( &$out, $opts ){} + + /** + * Creates the choose namespace selection + * + * @param $opts FormOptions + * @return string + */ protected function namespaceFilterForm( FormOptions $opts ) { $nsSelect = HTMLnamespaceselector( $opts['namespace'], '' ); $nsLabel = Xml::label( wfMsg('namespace'), 'namespace' ); @@ -384,6 +491,12 @@ class SpecialRecentChanges extends SpecialPage { return array( $nsLabel, "$nsSelect $invert" ); } + /** + * Create a input to filter changes by categories + * + * @param $opts FormOptions + * @return array + */ protected function categoryFilterForm( FormOptions $opts ) { list( $label, $input ) = Xml::inputLabelSep( wfMsg('rc_categories'), 'categories', 'mw-categories', false, $opts['categories'] ); @@ -394,216 +507,151 @@ class SpecialRecentChanges extends SpecialPage { return array( $label, $input ); } -} - -function rcFilterByCategories ( &$rows, FormOptions $opts ) { - $categories = array_map( 'trim', explode( "|" , $opts['categories'] ) ); - - if( empty($categories) ) { - return; - } - - # Filter categories - $cats = array(); - foreach ( $categories as $cat ) { - $cat = trim( $cat ); - if ( $cat == "" ) continue; - $cats[] = $cat; - } + /** + * Filter $rows by categories set in $opts + * + * @param $rows array of database rows + * @param $opts FormOptions + */ + function filterByCategories( &$rows, FormOptions $opts ) { + $categories = array_map( 'trim', explode( "|" , $opts['categories'] ) ); + + if( empty($categories) ) { + return; + } - # Filter articles - $articles = array(); - $a2r = array(); - foreach ( $rows AS $k => $r ) { - $nt = Title::makeTitle( $r->rc_namespace, $r->rc_title ); - $id = $nt->getArticleID(); - if ( $id == 0 ) continue; # Page might have been deleted... - if ( !in_array($id, $articles) ) { - $articles[] = $id; + # Filter categories + $cats = array(); + foreach ( $categories as $cat ) { + $cat = trim( $cat ); + if ( $cat == "" ) continue; + $cats[] = $cat; } - if ( !isset($a2r[$id]) ) { - $a2r[$id] = array(); + + # Filter articles + $articles = array(); + $a2r = array(); + foreach ( $rows AS $k => $r ) { + $nt = Title::makeTitle( $r->rc_namespace, $r->rc_title ); + $id = $nt->getArticleID(); + if ( $id == 0 ) continue; # Page might have been deleted... + if ( !in_array($id, $articles) ) { + $articles[] = $id; + } + if ( !isset($a2r[$id]) ) { + $a2r[$id] = array(); + } + $a2r[$id][] = $k; } - $a2r[$id][] = $k; - } - # Shortcut? - if ( !count($articles) || !count($cats) ) - return ; - - # Look up - $c = new Categoryfinder ; - $c->seed( $articles, $cats, $opts['categories_any'] ? "OR" : "AND" ) ; - $match = $c->run(); - - # Filter - $newrows = array(); - foreach ( $match AS $id ) { - foreach ( $a2r[$id] AS $rev ) { - $k = $rev; - $newrows[$k] = $rows[$k]; + # Shortcut? + if ( !count($articles) || !count($cats) ) + return ; + + # Look up + $c = new Categoryfinder ; + $c->seed( $articles, $cats, $opts['categories_any'] ? "OR" : "AND" ) ; + $match = $c->run(); + + # Filter + $newrows = array(); + foreach ( $match AS $id ) { + foreach ( $a2r[$id] AS $rev ) { + $k = $rev; + $newrows[$k] = $rows[$k]; + } } + $rows = $newrows; } - $rows = $newrows; -} - -/** - * - */ -function rcCountLink( $lim, $d, $page='Recentchanges', $more='', $active = false ) { - global $wgUser, $wgLang, $wgContLang; - $sk = $wgUser->getSkin(); - $s = $sk->makeKnownLink( $wgContLang->specialPage( $page ), - ($lim ? $wgLang->formatNum( "{$lim}" ) : wfMsg( 'recentchangesall' ) ), "{$more}" . - ($d ? "days={$d}&" : '') . 'limit='.$lim, '', '', - $active ? 'style="font-weight: bold;"' : '' ); - return $s; -} -/** - * - */ -function rcDaysLink( $lim, $d, $page='Recentchanges', $more='', $active = false ) { - global $wgUser, $wgLang, $wgContLang; - $sk = $wgUser->getSkin(); - $s = $sk->makeKnownLink( $wgContLang->specialPage( $page ), - ($d ? $wgLang->formatNum( "{$d}" ) : wfMsg( 'recentchangesall' ) ), $more.'days='.$d . - ($lim ? '&limit='.$lim : ''), '', '', - $active ? 'style="font-weight: bold;"' : '' ); - return $s; -} - -/** - * Used by Recentchangeslinked - */ -function rcDayLimitLinks( $days, $limit, $page='Recentchanges', $more='', $doall = false, $minorLink = '', - $botLink = '', $liuLink = '', $patrLink = '', $myselfLink = '' ) { - global $wgRCLinkLimits, $wgRCLinkDays; - if ($more != '') $more .= '&'; - - # Sort data for display and make sure it's unique after we've added user data. - # FIXME: why does this piss around with globals like this? Why is $limit added on globally? - $wgRCLinkLimits[] = $limit; - $wgRCLinkDays[] = $days; - sort($wgRCLinkLimits); - sort($wgRCLinkDays); - $wgRCLinkLimits = array_unique($wgRCLinkLimits); - $wgRCLinkDays = array_unique($wgRCLinkDays); - - $cl = array(); - foreach( $wgRCLinkLimits as $countLink ) { - $cl[] = rcCountLink( $countLink, $days, $page, $more, $countLink == $limit ); - } - if( $doall ) $cl[] = rcCountLink( 0, $days, $page, $more ); - $cl = implode( ' | ', $cl); - - $dl = array(); - foreach( $wgRCLinkDays as $daysLink ) { - $dl[] = rcDaysLink( $limit, $daysLink, $page, $more, $daysLink == $days ); - } - if( $doall ) $dl[] = rcDaysLink( $limit, 0, $page, $more ); - $dl = implode( ' | ', $dl); - - $linkParts = array( 'minorLink' => 'minor', 'botLink' => 'bots', 'liuLink' => 'liu', 'patrLink' => 'patr', 'myselfLink' => 'mine' ); - foreach( $linkParts as $linkVar => $linkMsg ) { - if( $$linkVar != '' ) - $links[] = wfMsgHtml( 'rcshowhide' . $linkMsg, $$linkVar ); + /** + * Makes change an option link which carries all the other options + * @param $title see Title + * @param $override + * @param $options + */ + function makeOptionsLink( $title, $override, $options, $active = false ) { + global $wgUser, $wgContLang; + $sk = $wgUser->getSkin(); + return $sk->makeKnownLinkObj( $this->getTitle(), + htmlspecialchars( $title ), wfArrayToCGI( $override, $options ), '', '', + $active ? 'style="font-weight: bold;"' : '' ); } - $shm = implode( ' | ', $links ); - $note = wfMsg( 'rclinks', $cl, $dl, $shm ); - return $note; -} - - -/** - * Makes change an option link which carries all the other options - * @param $title see Title - * @param $override - * @param $options - */ -function makeOptionsLink( $title, $override, $options, $active = false ) { - global $wgUser, $wgContLang; - $sk = $wgUser->getSkin(); - return $sk->makeKnownLink( $wgContLang->specialPage( 'Recentchanges' ), - htmlspecialchars( $title ), wfArrayToCGI( $override, $options ), '', '', - $active ? 'style="font-weight: bold;"' : '' ); -} - -/** - * Creates the options panel. - * @param $defaults - * @param $nondefaults - */ -function rcOptionsPanel( $defaults, $nondefaults ) { - global $wgLang, $wgUser, $wgRCLinkLimits, $wgRCLinkDays; - - $options = $nondefaults + $defaults; - - if( $options['from'] ) - $note = wfMsgExt( 'rcnotefrom', array( 'parseinline' ), - $wgLang->formatNum( $options['limit'] ), - $wgLang->timeanddate( $options['from'], true ) ); - else - $note = wfMsgExt( 'rcnote', array( 'parseinline' ), - $wgLang->formatNum( $options['limit'] ), - $wgLang->formatNum( $options['days'] ), - $wgLang->timeAndDate( wfTimestampNow(), true ) ); - - # Sort data for display and make sure it's unique after we've added user data. - $wgRCLinkLimits[] = $options['limit']; - $wgRCLinkDays[] = $options['days']; - sort($wgRCLinkLimits); - sort($wgRCLinkDays); - $wgRCLinkLimits = array_unique($wgRCLinkLimits); - $wgRCLinkDays = array_unique($wgRCLinkDays); - - // limit links - foreach( $wgRCLinkLimits as $value ) { - $cl[] = makeOptionsLink( $wgLang->formatNum( $value ), - array( 'limit' => $value ), $nondefaults, $value == $options['limit'] ) ; - } - $cl = implode( ' | ', $cl); + /** + * Creates the options panel. + * @param $defaults array + * @param $nondefaults array + */ + function optionsPanel( $defaults, $nondefaults ) { + global $wgLang, $wgUser, $wgRCLinkLimits, $wgRCLinkDays; + + $options = $nondefaults + $defaults; + + if( $options['from'] ) + $note = wfMsgExt( 'rcnotefrom', array( 'parseinline' ), + $wgLang->formatNum( $options['limit'] ), + $wgLang->timeanddate( $options['from'], true ) ); + else + $note = wfMsgExt( 'rcnote', array( 'parseinline' ), + $wgLang->formatNum( $options['limit'] ), + $wgLang->formatNum( $options['days'] ), + $wgLang->timeAndDate( wfTimestampNow(), true ) ); + + # Sort data for display and make sure it's unique after we've added user data. + $wgRCLinkLimits[] = $options['limit']; + $wgRCLinkDays[] = $options['days']; + sort($wgRCLinkLimits); + sort($wgRCLinkDays); + $wgRCLinkLimits = array_unique($wgRCLinkLimits); + $wgRCLinkDays = array_unique($wgRCLinkDays); + + // limit links + foreach( $wgRCLinkLimits as $value ) { + $cl[] = $this->makeOptionsLink( $wgLang->formatNum( $value ), + array( 'limit' => $value ), $nondefaults, $value == $options['limit'] ) ; + } + $cl = implode( ' | ', $cl); - // day links, reset 'from' to none - foreach( $wgRCLinkDays as $value ) { - $dl[] = makeOptionsLink( $wgLang->formatNum( $value ), - array( 'days' => $value, 'from' => '' ), $nondefaults, $value == $options['days'] ) ; + // day links, reset 'from' to none + foreach( $wgRCLinkDays as $value ) { + $dl[] = $this->makeOptionsLink( $wgLang->formatNum( $value ), + array( 'days' => $value, 'from' => '' ), $nondefaults, $value == $options['days'] ) ; + } + $dl = implode( ' | ', $dl); + + + // show/hide links + $showhide = array( wfMsg( 'show' ), wfMsg( 'hide' )); + $minorLink = $this->makeOptionsLink( $showhide[1-$options['hideminor']], + array( 'hideminor' => 1-$options['hideminor'] ), $nondefaults); + $botLink = $this->makeOptionsLink( $showhide[1-$options['hidebots']], + array( 'hidebots' => 1-$options['hidebots'] ), $nondefaults); + $anonsLink = $this->makeOptionsLink( $showhide[ 1 - $options['hideanons'] ], + array( 'hideanons' => 1 - $options['hideanons'] ), $nondefaults ); + $liuLink = $this->makeOptionsLink( $showhide[1-$options['hideliu']], + array( 'hideliu' => 1-$options['hideliu'] ), $nondefaults); + $patrLink = $this->makeOptionsLink( $showhide[1-$options['hidepatrolled']], + array( 'hidepatrolled' => 1-$options['hidepatrolled'] ), $nondefaults); + $myselfLink = $this->makeOptionsLink( $showhide[1-$options['hidemyself']], + array( 'hidemyself' => 1-$options['hidemyself'] ), $nondefaults); + + $links[] = wfMsgHtml( 'rcshowhideminor', $minorLink ); + $links[] = wfMsgHtml( 'rcshowhidebots', $botLink ); + $links[] = wfMsgHtml( 'rcshowhideanons', $anonsLink ); + $links[] = wfMsgHtml( 'rcshowhideliu', $liuLink ); + if( $wgUser->useRCPatrol() ) + $links[] = wfMsgHtml( 'rcshowhidepatr', $patrLink ); + $links[] = wfMsgHtml( 'rcshowhidemine', $myselfLink ); + $hl = implode( ' | ', $links ); + + // show from this onward link + $now = $wgLang->timeanddate( wfTimestampNow(), true ); + $tl = $this->makeOptionsLink( $now, array( 'from' => wfTimestampNow()), $nondefaults ); + + $rclinks = wfMsgExt( 'rclinks', array( 'parseinline', 'replaceafter'), + $cl, $dl, $hl ); + $rclistfrom = wfMsgExt( 'rclistfrom', array( 'parseinline', 'replaceafter'), $tl ); + return "$note
$rclinks
$rclistfrom"; } - $dl = implode( ' | ', $dl); - - - // show/hide links - $showhide = array( wfMsg( 'show' ), wfMsg( 'hide' )); - $minorLink = makeOptionsLink( $showhide[1-$options['hideminor']], - array( 'hideminor' => 1-$options['hideminor'] ), $nondefaults); - $botLink = makeOptionsLink( $showhide[1-$options['hidebots']], - array( 'hidebots' => 1-$options['hidebots'] ), $nondefaults); - $anonsLink = makeOptionsLink( $showhide[ 1 - $options['hideanons'] ], - array( 'hideanons' => 1 - $options['hideanons'] ), $nondefaults ); - $liuLink = makeOptionsLink( $showhide[1-$options['hideliu']], - array( 'hideliu' => 1-$options['hideliu'] ), $nondefaults); - $patrLink = makeOptionsLink( $showhide[1-$options['hidepatrolled']], - array( 'hidepatrolled' => 1-$options['hidepatrolled'] ), $nondefaults); - $myselfLink = makeOptionsLink( $showhide[1-$options['hidemyself']], - array( 'hidemyself' => 1-$options['hidemyself'] ), $nondefaults); - - $links[] = wfMsgHtml( 'rcshowhideminor', $minorLink ); - $links[] = wfMsgHtml( 'rcshowhidebots', $botLink ); - $links[] = wfMsgHtml( 'rcshowhideanons', $anonsLink ); - $links[] = wfMsgHtml( 'rcshowhideliu', $liuLink ); - if( $wgUser->useRCPatrol() ) - $links[] = wfMsgHtml( 'rcshowhidepatr', $patrLink ); - $links[] = wfMsgHtml( 'rcshowhidemine', $myselfLink ); - $hl = implode( ' | ', $links ); - - // show from this onward link - $now = $wgLang->timeanddate( wfTimestampNow(), true ); - $tl = makeOptionsLink( $now, array( 'from' => wfTimestampNow()), $nondefaults ); - - $rclinks = wfMsgExt( 'rclinks', array( 'parseinline', 'replaceafter'), - $cl, $dl, $hl ); - $rclistfrom = wfMsgExt( 'rclistfrom', array( 'parseinline', 'replaceafter'), $tl ); - return "$note
$rclinks
$rclistfrom"; - -} \ No newline at end of file +} diff --git a/includes/specials/SpecialRecentchangeslinked.php b/includes/specials/SpecialRecentchangeslinked.php index 00d5d29583..fca94c7ddc 100644 --- a/includes/specials/SpecialRecentchangeslinked.php +++ b/includes/specials/SpecialRecentchangeslinked.php @@ -1,191 +1,130 @@ getInt( 'days' ); - $target = isset($par) ? $par : $wgRequest->getVal( 'target' ); - $hideminor = $wgRequest->getBool( 'hideminor' ) ? 1 : 0; - $showlinkedto = $wgRequest->getBool( 'showlinkedto' ) ? 1 : 0; - - $title = Title::newFromURL( $target ); - $target = $title ? $title->getPrefixedText() : ''; - - $wgOut->setPagetitle( wfMsg( 'recentchangeslinked' ) ); - $sk = $wgUser->getSkin(); - - $wgOut->addHTML( - Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) . - Xml::openElement( 'fieldset' ) . - Xml::element( 'legend', array(), wfMsg( 'recentchangeslinked' ) ) . "\n" . - Xml::inputLabel( wfMsg( 'recentchangeslinked-page' ), 'target', 'recentchangeslinked-target', 40, $target ) . - "   " . - Xml::check( 'showlinkedto', $showlinkedto, array('id' => 'showlinkedto') ) . ' ' . - Xml::label( wfMsg("recentchangeslinked-to"), 'showlinkedto' ) . - "
\n" . - Xml::hidden( 'title', $wgTitle->getPrefixedText() ). "\n" . - Xml::submitButton( wfMsg( 'allpagessubmit' ) ) . "\n" . - Xml::closeElement( 'fieldset' ) . - Xml::closeElement( 'form' ) . "\n" - ); - - if ( !$target ) { - return; - } - $nt = Title::newFromURL( $target ); - if( !$nt ) { - $wgOut->showErrorPage( 'notargettitle', 'notargettext' ); - return; + function __construct(){ + SpecialPage::SpecialPage( 'Recentchangeslinked' ); } - $id = $nt->getArticleId(); - $wgOut->setPageTitle( wfMsg( 'recentchangeslinked-title', $nt->getPrefixedText() ) ); - $wgOut->setSyndicated(); - $wgOut->setFeedAppendQuery( "target=" . urlencode( $target ) ); + public function getDefaultOptions() { + $opts = parent::getDefaultOptions(); + $opts->add( 'target', '' ); + $opts->add( 'showlinkedto', false ); + return $opts; + } - if ( !$days ) { - $days = (int)$wgUser->getOption( 'rcdays', 7 ); + public function parseParameters( $par, FormOptions $opts ) { + $opts['target'] = $par; } - list( $limit, /* offset */ ) = wfCheckLimits( 100, 'rclimit' ); - - $dbr = wfGetDB( DB_SLAVE,'recentchangeslinked' ); - $cutoff = $dbr->timestamp( time() - ( $days * 86400 ) ); - - $hideminor = ($hideminor ? 1 : 0); - if ( $hideminor ) { - $mlink = $sk->makeKnownLink( $wgContLang->specialPage( 'Recentchangeslinked' ), - wfMsg( 'show' ), 'target=' . htmlspecialchars( $nt->getPrefixedURL() ) . - "&days={$days}&limit={$limit}&hideminor=0&showlinkedto={$showlinkedto}" ); - } else { - $mlink = $sk->makeKnownLink( $wgContLang->specialPage( "Recentchangeslinked" ), - wfMsg( "hide" ), "target=" . htmlspecialchars( $nt->getPrefixedURL() ) . - "&days={$days}&limit={$limit}&hideminor=1&showlinkedto={$showlinkedto}" ); + + public function feedSetup(){ + global $wgRequest; + $opts = parent::feedSetup(); + $opts['target'] = $wgRequest->getVal( 'target' ); + return $opts; } - if ( $hideminor ) { - $cmq = 'AND rc_minor=0'; - } else { $cmq = ''; } - - list($recentchanges, $categorylinks, $pagelinks, $watchlist) = - $dbr->tableNamesN( 'recentchanges', 'categorylinks', 'pagelinks', "watchlist" ); - - $uid = $wgUser->getId(); - // The fields we are selecting - $fields = "rc_cur_id,rc_namespace,rc_title, - rc_user,rc_comment,rc_user_text,rc_timestamp,rc_minor,rc_log_type,rc_log_action,rc_params,rc_deleted, - rc_new, rc_id, rc_this_oldid, rc_last_oldid, rc_bot, rc_patrolled, rc_type, rc_old_len, rc_new_len"; - $fields .= $uid ? ",wl_user" : ""; - - // Check if this should be a feed - - $feed = false; - global $wgFeedLimit; - $feedFormat = $wgRequest->getVal( 'feed' ); - if( $feedFormat ) { + + public function getFeedObject( $feedFormat ){ $feed = new ChangesFeed( $feedFormat, false ); - # Sanity check - if( $limit > $wgFeedLimit ) { - $limit = $wgFeedLimit; - } + $feedObj = $feed->getFeedObject( + wfMsgForContent( 'recentchangeslinked-title', $this->mTargetTitle->getPrefixedText() ), + wfMsgForContent( 'recentchangeslinked' ) + ); + return array( $feed, $feedObj ); } - // If target is a Category, use categorylinks and invert from and to - if( $nt->getNamespace() == NS_CATEGORY ) { - $catkey = $dbr->addQuotes( $nt->getDBkey() ); - # The table clauses - $tables = "$categorylinks, $recentchanges"; - $tables .= $uid ? " LEFT JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : ""; - - $sql = "SELECT /* wfSpecialRecentchangeslinked */ $fields FROM $tables - WHERE rc_timestamp > '{$cutoff}' {$cmq} - AND cl_from=rc_cur_id - AND cl_to=$catkey - GROUP BY $fields ORDER BY rc_timestamp DESC LIMIT {$limit}"; // Shitty-ass GROUP BY by for postgres apparently - } else { - if( $showlinkedto ) { - $ns = $dbr->addQuotes( $nt->getNamespace() ); - $dbkey = $dbr->addQuotes( $nt->getDBkey() ); - $joinConds = "AND pl_namespace={$ns} AND pl_title={$dbkey} AND pl_from=rc_cur_id"; - } else { - $joinConds = "AND pl_namespace=rc_namespace AND pl_title=rc_title AND pl_from=$id"; + public function doMainQuery( $conds, $opts ) { + global $wgUser, $wgOut; + + $title = Title::newFromURL( $opts['target'] ); + $showlinkedto = $opts['showlinkedto']; + $limit = $opts['limit']; + + $target = $title ? $title->getPrefixedText() : ''; + if ( $target === '' ) { + return false; } - # The table clauses - $tables = "$pagelinks, $recentchanges"; - $tables .= $uid ? " LEFT JOIN $watchlist ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : ""; - - $sql = "SELECT /* wfSpecialRecentchangeslinked */ $fields FROM $tables - WHERE rc_timestamp > '{$cutoff}' {$cmq} - {$joinConds} - GROUP BY $fields ORDER BY rc_timestamp DESC LIMIT {$limit}"; // Shitty-ass GROUP BY by for postgres apparently - } - # Actually do the query - $res = $dbr->query( $sql, __METHOD__ ); - $count = $dbr->numRows( $res ); - $rchanges = array(); - # Output feeds now and be done with it! - if( $feed ) { - if( $count ) { - $counter = 1; - while ( $limit ) { - if ( 0 == $count ) { break; } - $obj = $dbr->fetchObject( $res ); - --$count; - $rc = RecentChange::newFromRow( $obj ); - $rc->counter = $counter++; - --$limit; - $rchanges[] = $obj; + if( !$title ){ + global $wgOut; + $wgOut->showErrorPage( 'notargettitle', 'notargettext' ); + return false; + } + + $wgOut->setPageTitle( wfMsg( 'recentchangeslinked-title', $target ) ); + $this->mTargetTitle = $title; + + $dbr = wfGetDB( DB_SLAVE, 'recentchangeslinked' ); + $id = $title->getArticleId(); + + $tables = array( 'recentchanges' ); + $select = array( $dbr->tableName( 'recentchanges' ) . '.*' ); + $join_conds = array(); + + if( $title->getNamespace() == NS_CATEGORY ) { + $tables[] = 'categorylinks'; + $conds['cl_to'] = $title->getDBkey(); + $join_conds['categorylinks'] = array( 'LEFT JOIN', 'cl_from=rc_cur_id' ); + } else { + if( $showlinkedto ) { + if( $title->getNamespace() == NS_TEMPLATE ){ + $tables[] = 'templatelinks'; + $conds['tl_namespace'] = $title->getNamespace(); + $conds['tl_title'] = $title->getDBkey(); + $join_conds['templatelinks'] = array( 'LEFT JOIN', 'tl_from=rc_cur_id' ); + } else { + $tables[] = 'pagelinks'; + $conds['pl_namespace'] = $title->getNamespace(); + $conds['pl_title'] = $title->getDBkey(); + $join_conds['pagelinks'] = array( 'LEFT JOIN', 'pl_from=rc_cur_id' ); + } + } else { + $tables[] = 'pagelinks'; + $conds['pl_from'] = $id; + $join_conds['pagelinks'] = array( 'LEFT JOIN', 'pl_namespace = rc_namespace AND pl_title = rc_title' ); } } - $wgOut->disable(); - $feedObj = $feed->getFeedObject( - wfMsgForContent( 'recentchangeslinked-title', $nt->getPrefixedText() ), - wfMsgForContent( 'recentchangeslinked' ) - ); - ChangesFeed::generateFeed( $rchanges, $feedObj ); - return; + if( $uid = $wgUser->getId() ) { + $tables[] = 'watchlist'; + $join_conds['watchlist'] = array( 'LEFT JOIN', "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace" ); + $select[] = 'wl_user'; + } + + $res = $dbr->select( $tables, $select, $conds, __METHOD__, + array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit ), $join_conds ); + + if( $dbr->numRows( $res ) == 0 ) + $this->mResultEmpty = true; + + return $res; + } + + function getExtraOptions( $opts ){ + $opts->consumeValues( array( 'showlinkedto', 'target' ) ); + $extraOpts = array(); + $extraOpts['namespace'] = $this->namespaceFilterForm( $opts ); + $extraOpts['target'] = array( wfMsg( 'recentchangeslinked-page' ), + Xml::input( 'target', 40, $opts['target'] ) . + Xml::check( 'showlinkedto', $opts['showlinkedto'], array('id' => 'showlinkedto') ) . ' ' . + Xml::label( wfMsg("recentchangeslinked-to"), 'showlinkedto' ) ); + $extraOpts['submit'] = Xml::submitbutton( wfMsg('allpagessubmit') ); + return $extraOpts; } - # Otherwise, carry on with regular output... - $wgOut->addHTML("< ".$sk->makeLinkObj($nt, "", "redirect=no" )."
\n"); - $note = wfMsgExt( "rcnote", array ( 'parseinline' ), $limit, $days, $wgLang->timeAndDate( wfTimestampNow(), true ) ); - $wgOut->addHTML( "
\n{$note}\n
" ); - - $note = rcDayLimitlinks( $days, $limit, "Recentchangeslinked", - "target=" . $nt->getPrefixedURL() . "&hideminor={$hideminor}&showlinkedto={$showlinkedto}", - false, $mlink ); - - $wgOut->addHTML( $note."\n" ); - - $list = ChangesList::newFromUser( $wgUser ); - $s = $list->beginRecentChangesList(); - - if ( $count ) { - $counter = 1; - while ( $limit ) { - if ( 0 == $count ) { break; } - $obj = $dbr->fetchObject( $res ); - --$count; - $rc = RecentChange::newFromRow( $obj ); - $rc->counter = $counter++; - $s .= $list->recentChangesLine( $rc , !empty( $obj->wl_user) ); - --$limit; + function setTopText( &$out, $opts ){} + + function setBottomText( &$out, $opts ){ + if( $target = $opts['target'] ){ + global $wgUser; + $out->setFeedAppendQuery( "target=" . urlencode( $target ) ); + $out->addHTML("< ".$wgUser->getSkin()->makeLinkObj( Title::newFromUrl( $target ), "", "redirect=no" )."
\n"); + } + if( isset( $this->mResultEmpty ) && $this->mResultEmpty ){ + $out->addWikiMsg( 'recentchangeslinked-noresult' ); } - } else { - $wgOut->addWikiMsg('recentchangeslinked-noresult'); } - $s .= $list->endRecentChangesList(); - - $dbr->freeResult( $res ); - $wgOut->addHTML( $s ); } -- 2.20.1