Rewritten Special:Recentchangeslinked, now using a subclass of SpecialRecentchanges:
authorAlexandre Emsenhuber <ialex@users.mediawiki.org>
Thu, 26 Jun 2008 19:12:52 +0000 (19:12 +0000)
committerAlexandre Emsenhuber <ialex@users.mediawiki.org>
Thu, 26 Jun 2008 19:12:52 +0000 (19:12 +0000)
* (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
includes/AutoLoader.php
includes/SpecialPage.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php

index 953d72f..526fbb6 100644 (file)
@@ -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 ===
 
index fa4efb8..c66ef59 100644 (file)
@@ -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',
index e092980..4a50b81 100644 (file)
@@ -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' ),
index 7395789..01b2909 100644 (file)
@@ -1,15 +1,20 @@
 <?php
+
 /**
- * @file
+ * Implements Special:Recentchanges
  * @ingroup SpecialPage
  */
-
 class SpecialRecentChanges extends SpecialPage {
        public function __construct() {
-       SpecialPage::SpecialPage( 'Recentchanges' );
+               SpecialPage::SpecialPage( 'Recentchanges' );
                $this->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( '<div class="rcoptions">' . $panelString . '</div>' );
+
+               $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<br />$rclinks<br />$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<br />$rclinks<br />$rclistfrom";
-
-}
\ No newline at end of file
+}
index 00d5d29..fca94c7 100644 (file)
 <?php
+
 /**
  * This is to display changes made to all articles linked in an article.
- * @file
  * @ingroup SpecialPage
  */
+class SpecialRecentchangeslinked extends SpecialRecentchanges {
 
-require_once( 'SpecialRecentchanges.php' );
-
-/**
- * Entrypoint
- * @param string $par parent page we will look at
- */
-function wfSpecialRecentchangeslinked( $par = NULL ) {
-       global $wgUser, $wgOut, $wgLang, $wgContLang, $wgRequest, $wgTitle, $wgScript;
-
-       $days = $wgRequest->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 ) .
-               "&nbsp;&nbsp;&nbsp;<span style='white-space: nowrap'>" .
-               Xml::check( 'showlinkedto', $showlinkedto, array('id' => 'showlinkedto') ) . ' ' .
-               Xml::label( wfMsg("recentchangeslinked-to"), 'showlinkedto' ) .
-               "</span><br/>\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("&lt; ".$sk->makeLinkObj($nt, "", "redirect=no" )."<br />\n");
-       $note = wfMsgExt( "rcnote", array ( 'parseinline' ), $limit, $days, $wgLang->timeAndDate( wfTimestampNow(), true ) );
-       $wgOut->addHTML( "<hr />\n{$note}\n<br />" );
-
-       $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("&lt; ".$wgUser->getSkin()->makeLinkObj( Title::newFromUrl( $target ), "", "redirect=no" )."<hr />\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 );
 }