Merge querypage-work2 branch from trunk. The most relevant changes are:
authorRoan Kattouw <catrope@users.mediawiki.org>
Wed, 22 Dec 2010 14:16:25 +0000 (14:16 +0000)
committerRoan Kattouw <catrope@users.mediawiki.org>
Wed, 22 Dec 2010 14:16:25 +0000 (14:16 +0000)
* QueryPage now uses array-based query building instead of raw SQL
* Converted all QueryPage-based special pages that were using old-style wfSpecialFoo functions to new-style SpecialPage subclasses; this is possible because QueryPage is changed to extend SpecialPage
* Backward compatibility for extensions is partly preserved: getSQL() is fallen back on for QueryPage subclasses that don't implement getQueryInfo(), but getOrder() will be ignored (implement getOrderFields() instead). This also means that dual compatibility (1.18 compat and b/c with pre-1.18) is trivial

Extension changes will be merged after this commit.

These changes make it easier to write an API module for QueryPages (bug 14869); this wasn't done in the branch but will be done in trunk soon.

47 files changed:
RELEASE-NOTES
docs/hooks.txt
includes/AutoLoader.php
includes/ImageQueryPage.php
includes/Namespace.php
includes/PageQueryPage.php
includes/QueryPage.php
includes/SpecialPage.php
includes/specials/SpecialAncientpages.php
includes/specials/SpecialBrokenRedirects.php
includes/specials/SpecialDeadendpages.php
includes/specials/SpecialDisambiguations.php
includes/specials/SpecialDoubleRedirects.php
includes/specials/SpecialFewestrevisions.php
includes/specials/SpecialFileDuplicateSearch.php
includes/specials/SpecialLinkSearch.php
includes/specials/SpecialListfiles.php
includes/specials/SpecialListredirects.php
includes/specials/SpecialLonelypages.php
includes/specials/SpecialLongpages.php
includes/specials/SpecialMIMEsearch.php
includes/specials/SpecialMostcategories.php
includes/specials/SpecialMostimages.php
includes/specials/SpecialMostlinked.php
includes/specials/SpecialMostlinkedcategories.php
includes/specials/SpecialMostlinkedtemplates.php
includes/specials/SpecialMostrevisions.php
includes/specials/SpecialNewpages.php
includes/specials/SpecialPopularpages.php
includes/specials/SpecialShortpages.php
includes/specials/SpecialTags.php
includes/specials/SpecialUncategorizedcategories.php
includes/specials/SpecialUncategorizedimages.php
includes/specials/SpecialUncategorizedpages.php
includes/specials/SpecialUncategorizedtemplates.php
includes/specials/SpecialUnusedcategories.php
includes/specials/SpecialUnusedimages.php
includes/specials/SpecialUnusedtemplates.php
includes/specials/SpecialUnwatchedpages.php
includes/specials/SpecialWantedcategories.php
includes/specials/SpecialWantedfiles.php
includes/specials/SpecialWantedpages.php
includes/specials/SpecialWantedtemplates.php
includes/specials/SpecialWithoutinterwiki.php
languages/messages/MessagesEn.php
languages/messages/MessagesQqq.php
maintenance/language/messages.inc

index 6ab0b93..8f78b49 100644 (file)
@@ -19,6 +19,8 @@ Those wishing to use the latest code instead of a branch release can obtain
 it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
 
 === Configuration changes in 1.18 ===
+* The WantedPages::getSQL hook has been removed and replaced with
+  WantedPages::getQueryInfo . This may break older extensions.
 
 === New features in 1.18 ===
 * Added a special page, disabled by default, that allows users with the
index 2fc81d9..2b3a9db 100644 (file)
@@ -1900,10 +1900,10 @@ $user: User object
 &$timestamp: new timestamp, change this to override local email
 authentification timestamp
 
-'WantedPages::getSQL': called in WantedPagesPage::getSQL(), can be used to
-alter the SQL query which gets the list of wanted pages
+'WantedPages::getQueryInfo': called in WantedPagesPage::getQueryInfo(), can be
+used to alter the SQL query which gets the list of wanted pages
 &$wantedPages: WantedPagesPage object
-&$sql: raw SQL query used to get the list of wanted pages
+&$query: query array, see QueryPage::getQueryInfo() for format documentation
 
 'WatchArticle': before a watch is added to an article
 $user: user that will watch
index f90dbdd..d3fbf1d 100644 (file)
@@ -594,6 +594,7 @@ $wgAutoloadLocalClasses = array(
        'MostimagesPage' => 'includes/specials/SpecialMostimages.php',
        'MostlinkedCategoriesPage' => 'includes/specials/SpecialMostlinkedcategories.php',
        'MostlinkedPage' => 'includes/specials/SpecialMostlinked.php',
+       'MostlinkedTemplatesPage' => 'includes/specials/SpecialMostlinkedtemplates.php',
        'MostrevisionsPage' => 'includes/specials/SpecialMostrevisions.php',
        'MovePageForm' => 'includes/specials/SpecialMovepage.php',
        'SpecialNewpages' => 'includes/specials/SpecialNewpages.php',
@@ -653,8 +654,8 @@ $wgAutoloadLocalClasses = array(
        'SpecialUserlogout' => 'includes/specials/SpecialUserlogout.php',
        'SpecialVersion' => 'includes/specials/SpecialVersion.php',
        'SpecialWhatlinkshere' => 'includes/specials/SpecialWhatlinkshere.php',
-       'SpecialWhatLinksHere' => 'includes/specials/SpecialWhatlinkshere.php',
        'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
+       'UncategorizedImagesPage' => 'includes/specials/SpecialUncategorizedimages.php',
        'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php',
        'UncategorizedTemplatesPage' => 'includes/specials/SpecialUncategorizedtemplates.php',
        'UndeleteForm' => 'includes/specials/SpecialUndelete.php',
@@ -670,7 +671,6 @@ $wgAutoloadLocalClasses = array(
        'WantedFilesPage' => 'includes/specials/SpecialWantedfiles.php',
        'WantedPagesPage' => 'includes/specials/SpecialWantedpages.php',
        'WantedTemplatesPage' => 'includes/specials/SpecialWantedtemplates.php',
-       'WhatLinksHerePage' => 'includes/specials/SpecialWhatlinkshere.php',
        'WikiImporter' => 'includes/ImportXMLReader.php',
        'WikiRevision' => 'includes/Import.php',
        'WithoutInterwikiPage' => 'includes/specials/SpecialWithoutinterwiki.php',
index 180201a..c50b0cc 100644 (file)
@@ -7,7 +7,7 @@
  * @ingroup SpecialPage
  * @author Rob Church <robchur@gmail.com>
  */
-class ImageQueryPage extends QueryPage {
+abstract class ImageQueryPage extends QueryPage {
 
        /**
         * Format and output report results using the given information plus
@@ -37,6 +37,9 @@ class ImageQueryPage extends QueryPage {
                        $out->addHTML( $gallery->toHtml() );
                }
        }
+       
+       // Gotta override this since it's abstract
+       function formatResult( $skin, $result ) { }
 
        /**
         * Prepare an image object given a result row
index 48a93dd..3db1938 100644 (file)
@@ -238,6 +238,21 @@ class MWNamespace {
                return !empty( $wgNamespacesWithSubpages[$index] );
        }
 
+       /**
+        * Get a list of all namespace indices which are considered to contain content
+        * @return array of namespace indices
+        */
+       public static function getContentNamespaces() {
+               global $wgContentNamespaces;
+               if( !is_array( $wgContentNamespaces ) || $wgContentNamespaces === array() ) {
+                       return NS_MAIN;
+               } elseif ( !in_array( NS_MAIN, $wgContentNamespaces ) ) {
+                       // always force NS_MAIN to be part of array (to match the algorithm used by isContent)
+                       return array_merge( array( NS_MAIN ), $wgContentNamespaces );
+               } else {
+                       return $wgContentNamespaces;
+               }
+       }
        /**
         * Is the namespace first-letter capitalized?
         *
index 892ff25..8390241 100644 (file)
@@ -5,7 +5,7 @@
  *
  * @ingroup SpecialPage
  */
-class PageQueryPage extends QueryPage {
+abstract class PageQueryPage extends QueryPage {
 
        /**
         * Format the result as a simple link to the page
index d9fb3bb..7dfe3fd 100644 (file)
 global $wgQueryPages; // not redundant
 $wgQueryPages = array(
 //         QueryPage subclass           Special page name         Limit (false for none, none for the default)
-//----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
        array( 'AncientPagesPage',              'Ancientpages'                  ),
        array( 'BrokenRedirectsPage',           'BrokenRedirects'               ),
        array( 'DeadendPagesPage',              'Deadendpages'                  ),
        array( 'DisambiguationsPage',           'Disambiguations'               ),
        array( 'DoubleRedirectsPage',           'DoubleRedirects'               ),
+       array( 'FileDuplicateSearchPage',       'FileDuplicateSearch'           ),
        array( 'LinkSearchPage',                'LinkSearch'                    ),
        array( 'ListredirectsPage',             'Listredirects'                 ),
        array( 'LonelyPagesPage',               'Lonelypages'                   ),
        array( 'LongPagesPage',                 'Longpages'                     ),
+       array( 'MIMEsearchPage',                'MIMEsearch'                    ),
        array( 'MostcategoriesPage',            'Mostcategories'                ),
        array( 'MostimagesPage',                'Mostimages'                    ),
        array( 'MostlinkedCategoriesPage',      'Mostlinkedcategories'          ),
-       array( 'SpecialMostlinkedtemplates',    'Mostlinkedtemplates'           ),
+       array( 'MostlinkedtemplatesPage',       'Mostlinkedtemplates'           ),
        array( 'MostlinkedPage',                'Mostlinked'                    ),
        array( 'MostrevisionsPage',             'Mostrevisions'                 ),
        array( 'FewestrevisionsPage',           'Fewestrevisions'               ),
@@ -51,7 +53,7 @@ wfRunHooks( 'wgQueryPages', array( &$wgQueryPages ) );
 
 global $wgDisableCounters;
 if ( !$wgDisableCounters )
-       $wgQueryPages[] = array( 'PopularPagesPage', 'Popularpages'             );
+       $wgQueryPages[] = array( 'PopularPagesPage', 'Popularpages' );
 
 
 /**
@@ -60,7 +62,7 @@ if ( !$wgDisableCounters )
  * subclasses derive from it.
  * @ingroup SpecialPage
  */
-class QueryPage {
+abstract class QueryPage extends SpecialPage {
        /**
         * Whether or not we want plain listoutput rather than an ordered list
         *
@@ -76,6 +78,18 @@ class QueryPage {
        var $offset = 0;
        var $limit = 0;
 
+       /**
+        * The number of rows returned by the query. Reading this variable
+        * only makes sense in functions that are run after the query has been
+        * done, such as preprocessResults() and formatRow().
+        */
+       protected $numRows;
+
+       /**
+        * Wheter to show prev/next links
+        */
+       var $shownavigation = true;
+
        /**
         * A mutator for $this->listoutput;
         *
@@ -85,17 +99,6 @@ class QueryPage {
                $this->listoutput = $bool;
        }
 
-       /**
-        * Subclasses return their name here. Make sure the name is also
-        * specified in SpecialPage.php and in Language.php as a language message
-        * param.
-        *
-        * @return String
-        */
-       function getName() {
-               return '';
-       }
-
        /**
         * Return title object representing this page
         *
@@ -106,22 +109,68 @@ class QueryPage {
        }
 
        /**
-        * Subclasses return an SQL query here.
+        * Subclasses return an SQL query here, formatted as an array with the
+        * following keys:
+        *    tables => Table(s) for passing to Database::select()
+        *    fields => Field(s) for passing to Database::select(), may be *
+        *    conds => WHERE conditions
+        *    options => options
+        *    join_conds => JOIN conditions
         *
-        * Note that the query itself should return the following four columns:
-        * 'type' (your special page's name), 'namespace', 'title', and 'value'
+        * Note that the query itself should return the following three columns:
+        * 'namespace', 'title', and 'value'
         * *in that order*. 'value' is used for sorting.
         *
         * These may be stored in the querycache table for expensive queries,
         * and that cached data will be returned sometimes, so the presence of
         * extra fields can't be relied upon. The cached 'value' column will be
-        * an integer; non-numeric values are useful only for sorting the initial
-        * query.
+        * an integer; non-numeric values are useful only for sorting the
+        * initial query (except if they're timestamps, see usesTimestamps()).
         *
-        * Don't include an ORDER or LIMIT clause, this will be added.
+        * Don't include an ORDER or LIMIT clause, they will be added.
+        *
+        * If this function is not overridden or returns something other than
+        * an array, getSQL() will be used instead. This is for backwards
+        * compatibility only and is strongly deprecated.
+        * @return array
+        * @since 1.18
+        */
+       function getQueryInfo() {
+               return null;
+       }
+       
+       /**
+        * For back-compat, subclasses may return a raw SQL query here, as a string.
+        * This is stronly deprecated; getQueryInfo() should be overridden instead.
+        * @return string
+        * @deprecated since 1.18
         */
        function getSQL() {
-               return "SELECT 'sample' as type, 0 as namespace, 'Sample result' as title, 42 as value";
+               throw new MWException( "Bug in a QueryPage: doesn't implement getQueryInfo() nor getQuery() properly" );
+       }
+
+       /**
+        * Subclasses return an array of fields to order by here. Don't append
+        * DESC to the field names, that'll be done automatically if
+        * sortDescending() returns true.
+        * @return array
+        * @since 1.18
+        */
+       function getOrderFields() {
+               return array( 'value' );
+       }
+
+       /**
+        * Does this query return timestamps rather than integers in its
+        * 'value' field? If true, this class will convert 'value' to a
+        * UNIX timestamp for caching.
+        * NOTE: formatRow() may get timestamps in TS_MW (mysql), TS_DB (pgsql)
+        *       or TS_UNIX (querycache) format, so be sure to always run them
+        *       through wfTimestamp()
+        * @return bool
+        */
+       function usesTimestamps() {
+               return false;
        }
 
        /**
@@ -133,11 +182,6 @@ class QueryPage {
                return true;
        }
 
-       function getOrder() {
-               return ' ORDER BY value ' .
-                       ($this->sortDescending() ? 'DESC' : '');
-       }
-
        /**
         * Is this query expensive (for some definition of expensive)? Then we
         * don't let it run in miser mode. $wgDisableQueryPages causes all query
@@ -151,7 +195,17 @@ class QueryPage {
        }
 
        /**
-        * Whether or not the output of the page in question is retrived from
+        * Is the output of this query cacheable? Non-cacheable expensive pages
+        * will be disabled in miser mode and will not have their results written
+        * to the querycache table.
+        * @return Boolean
+        */
+       public function isCacheable() {
+               return true;
+       }
+
+       /**
+        * Whether or not the output of the page in question is retrieved from
         * the database cache.
         *
         * @return Boolean
@@ -175,14 +229,15 @@ class QueryPage {
         * Formats the results of the query for display. The skin is the current
         * skin; you can use it for making links. The result is a single row of
         * result data. You should be able to grab SQL results off of it.
-        * If the function return "false", the line output will be skipped.
+        * If the function returns false, the line output will be skipped.
+        * @param $skin Skin
+        * @param $result object Result row
+        * @return mixed String or false to skip
         *
         * @param $skin Skin object
         * @param $result Object: database row
         */
-       function formatResult( $skin, $result ) {
-               return '';
-       }
+       abstract function formatResult( $skin, $result );
 
        /**
         * The content returned by this function will be output before any result
@@ -207,8 +262,9 @@ class QueryPage {
        /**
         * Some special pages (for example SpecialListusers) might not return the
         * current object formatted, but return the previous one instead.
-        * Setting this to return true, will call one more time wfFormatResult to
-        * be sure that the very last result is formatted and shown.
+        * Setting this to return true will ensure formatResult() is called
+        * one more time to make sure that the very last result is formatted
+        * as well.
         */
        function tryLastResult() {
                return false;
@@ -224,7 +280,7 @@ class QueryPage {
                $fname = get_class( $this ) . '::recache';
                $dbw = wfGetDB( DB_MASTER );
                $dbr = wfGetDB( DB_SLAVE, array( $this->getName(), __METHOD__, 'vslow' ) );
-               if ( !$dbw || !$dbr ) {
+               if ( !$dbw || !$dbr || !$this->isCacheable() ) {
                        return false;
                }
 
@@ -236,10 +292,7 @@ class QueryPage {
                # Clear out any old cached data
                $dbw->delete( 'querycache', array( 'qc_type' => $this->getName() ), $fname );
                # Do query
-               $sql = $this->getSQL() . $this->getOrder();
-               if ( $limit !== false )
-                       $sql = $dbr->limitResult( $sql, $limit, 0 );
-               $res = $dbr->query( $sql, $fname );
+               $res = $this->reallyDoQuery( $limit, false );
                $num = false;
                if ( $res ) {
                        $num = $dbr->numRows( $res );
@@ -247,22 +300,27 @@ class QueryPage {
                        $vals = array();
                        while ( $res && $row = $dbr->fetchObject( $res ) ) {
                                if ( isset( $row->value ) ) {
-                                       $value = intval( $row->value ); // @bug 14414
+                                       if ( $this->usesTimestamps() ) {
+                                               $value = wfTimestamp( TS_UNIX,
+                                                       $row->value );
+                                       } else {
+                                               $value = intval( $row->value ); // @bug 14414
+                                       }
                                } else {
                                        $value = 0;
                                }
-                               
-                               $vals[] = array('qc_type' => $row->type,
+
+                               $vals[] = array( 'qc_type' => $this->getName(),
                                                'qc_namespace' => $row->namespace,
                                                'qc_title' => $row->title,
-                                               'qc_value' => $value);
+                                               'qc_value' => $value );
                        }
 
                        # Save results into the querycache table on the master
                        if ( count( $vals ) ) {
                                if ( !$dbw->insert( 'querycache', $vals, __METHOD__ ) ) {
                                        // Set result to false to indicate error
-                                       $res = false;
+                                       $num = false;
                                }
                        }
                        if ( $ignoreErrors ) {
@@ -278,43 +336,125 @@ class QueryPage {
                return $num;
        }
 
+       /**
+        * Run the query and return the result
+        * @param $limit mixed Numerical limit or false for no limit
+        * @param $offset mixed Numerical offset or false for no offset
+        * @return ResultWrapper
+        */
+       function reallyDoQuery( $limit, $offset = false ) {
+               $fname = get_class( $this ) . "::reallyDoQuery";
+               $query = $this->getQueryInfo();
+               $order = $this->getOrderFields();
+               if ( $this->sortDescending() ) {
+                       foreach ( $order as &$field ) {
+                               $field .= ' DESC';
+                       }
+               }
+               if ( is_array( $query ) ) {
+                       $tables = isset( $query['tables'] ) ? (array)$query['tables'] : array();
+                       $fields = isset( $query['fields'] ) ? (array)$query['fields'] : array();
+                       $conds = isset( $query['conds'] ) ? (array)$query['conds'] : array();
+                       $options = isset( $query['options'] ) ? (array)$query['options'] : array();
+                       $join_conds = isset( $query['join_conds'] ) ? (array)$query['join_conds'] : array();
+                       if ( count( $order ) ) {
+                               $options['ORDER BY'] = implode( ', ', $order );
+                       }
+                       if ( $limit !== false ) {
+                               $options['LIMIT'] = intval( $limit );
+                       }
+                       if ( $offset !== false ) {
+                               $options['OFFSET'] = intval( $offset );
+                       }
+
+                       $dbr = wfGetDB( DB_SLAVE );
+                       $res = $dbr->select( $tables, $fields, $conds, $fname,
+                                       $options, $join_conds
+                       );
+               } else {
+                       // Old-fashioned raw SQL style, deprecated
+                       $sql = $this->getSQL();
+                       $sql .= ' ORDER BY ' . implode( ', ', $order );
+                       $sql = $dbr->limitResult( $sql, $limit, $offset );
+                       $res = $dbr->query( $sql );
+               }
+               return $dbr->resultObject( $res );
+       }
+
+       function doQuery( $limit, $offset = false ) {
+               if ( $this->isCached() && $this->isCacheable() ) {
+                       return $this->fetchFromCache( $limit, $offset );
+               } else {
+                       return $this->reallyDoQuery( $limit, $offset );
+               }
+       }
+
+       /**
+        * Fetch the query results from the query cache
+        * @param $limit mixed Numerical limit or false for no limit
+        * @param $offset mixed Numerical offset or false for no offset
+        * @return ResultWrapper
+        */
+       function fetchFromCache( $limit, $offset = false ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $options = array ();
+               if ( $limit !== false ) {
+                       $options['LIMIT'] = intval( $limit );
+               }
+               if ( $offset !== false ) {
+                       $options['OFFSET'] = intval( $offset );
+               }
+               $res = $dbr->select( 'querycache', array( 'qc_type',
+                               'qc_namespace AS namespace',
+                               'qc_title AS title',
+                               'qc_value AS value' ),
+                               array( 'qc_type' => $this->getName() ),
+                               __METHOD__, $options
+               );
+               return $dbr->resultObject( $res );
+       }
+
        /**
         * This is the actual workhorse. It does everything needed to make a
         * real, honest-to-gosh query page.
-        *
-        * @param $offset database query offset
-        * @param $limit database query limit
-        * @param $shownavigation show navigation like "next 200"?
         */
-       function doQuery( $offset, $limit, $shownavigation=true ) {
-               global $wgUser, $wgOut, $wgLang, $wgContLang;
+       function execute( $par ) {
+               global $wgUser, $wgOut, $wgLang;
 
-               $this->offset = $offset;
-               $this->limit = $limit;
+               if ( !$this->userCanExecute( $wgUser ) ) {
+                       $this->displayRestrictionError();
+                       return;
+               }
 
+               if ( $this->limit == 0 && $this->offset == 0 )
+                       list( $this->limit, $this->offset ) = wfCheckLimits();
                $sname = $this->getName();
-               $fname = get_class($this) . '::doQuery';
+               $fname = get_class( $this ) . '::doQuery';
                $dbr = wfGetDB( DB_SLAVE );
 
+               $this->setHeaders();
                $wgOut->setSyndicated( $this->isSyndicated() );
 
+               if ( $this->isCached() && !$this->isCacheable() ) {
+                       $wgOut->setSyndicated( false );
+                       $wgOut->addWikiMsg( 'querypage-disabled' );
+                       return 0;
+               }
+
+               // TODO: Use doQuery()
+               // $res = null;
                if ( !$this->isCached() ) {
-                       $sql = $this->getSQL();
+                       $res = $this->reallyDoQuery( $this->limit, $this->offset );
                } else {
                        # Get the cached result
-                       $querycache = $dbr->tableName( 'querycache' );
-                       $type = $dbr->strencode( $sname );
-                       $sql =
-                               "SELECT qc_type as type, qc_namespace as namespace,qc_title as title, qc_value as value
-                                FROM $querycache WHERE qc_type='$type'";
-
-                       if( !$this->listoutput ) {
+                       $res = $this->fetchFromCache( $this->limit, $this->offset );
+                       if ( !$this->listoutput ) {
 
                                # Fetch the timestamp of this update
-                               $tRes = $dbr->select( 'querycache_info', array( 'qci_timestamp' ), array( 'qci_type' => $type ), $fname );
+                               $tRes = $dbr->select( 'querycache_info', array( 'qci_timestamp' ), array( 'qci_type' => $sname ), $fname );
                                $tRow = $dbr->fetchObject( $tRes );
 
-                               if( $tRow ) {
+                               if ( $tRow ) {
                                        $updated = $wgLang->timeanddate( $tRow->qci_timestamp, true, true );
                                        $updateddate = $wgLang->date( $tRow->qci_timestamp, true, true );
                                        $updatedtime = $wgLang->time( $tRow->qci_timestamp, true, true );
@@ -328,7 +468,7 @@ class QueryPage {
                                # If updates on this page have been disabled, let the user know
                                # that the data set won't be refreshed for now
                                global $wgDisableQueryPageUpdate;
-                               if( is_array( $wgDisableQueryPageUpdate ) && in_array( $this->getName(), $wgDisableQueryPageUpdate ) ) {
+                               if ( is_array( $wgDisableQueryPageUpdate ) && in_array( $this->getName(), $wgDisableQueryPageUpdate ) ) {
                                        $wgOut->addWikiMsg( 'querypage-no-updates' );
                                }
 
@@ -336,23 +476,21 @@ class QueryPage {
 
                }
 
-               $sql .= $this->getOrder();
-               $sql = $dbr->limitResult($sql, $limit, $offset);
-               $res = $dbr->query( $sql );
-               $num = $dbr->numRows($res);
+               $this->numRows = $dbr->numRows( $res );
 
                $this->preprocessResults( $dbr, $res );
 
-               $wgOut->addHTML( Xml::openElement( 'div', array('class' => 'mw-spcontent') ) );
+               $wgOut->addHTML( Xml::openElement( 'div', array( 'class' => 'mw-spcontent' ) ) );
 
                # Top header and navigation
-               if( $shownavigation ) {
+               if ( $this->shownavigation ) {
                        $wgOut->addHTML( $this->getPageHeader() );
-                       if( $num > 0 ) {
-                               $wgOut->addHTML( '<p>' . wfShowingResults( $offset, $num ) . '</p>' );
+                       if ( $this->numRows > 0 ) {
+                               $wgOut->addHTML( '<p>' . wfShowingResults( $this->offset, $this->numRows ) . '</p>' );
                                # Disable the "next" link when we reach the end
-                               $paging = wfViewPrevNext( $offset, $limit, $wgContLang->specialPage( $sname ),
-                                       wfArrayToCGI( $this->linkParameters() ), ( $num < $limit ) );
+                               $paging = wfViewPrevNext( $this->offset, $this->limit,
+                                       $this->getTitle( $par ),
+                                       wfArrayToCGI( $this->linkParameters() ), ( $this->numRows < $this->limit ) );
                                $wgOut->addHTML( '<p>' . $paging . '</p>' );
                        } else {
                                # No results to show, so don't bother with "showing X of Y" etc.
@@ -370,17 +508,17 @@ class QueryPage {
                        $wgUser->getSkin(),
                        $dbr, # Should use a ResultWrapper for this
                        $res,
-                       $dbr->numRows( $res ),
-                       $offset );
+                       $this->numRows,
+                       $this->offset );
 
                # Repeat the paging links at the bottom
-               if( $shownavigation ) {
+               if ( $this->shownavigation ) {
                        $wgOut->addHTML( '<p>' . $paging . '</p>' );
                }
 
                $wgOut->addHTML( Xml::closeElement( 'div' ) );
 
-               return $num;
+               return $this->numRows;
        }
 
        /**
@@ -397,16 +535,16 @@ class QueryPage {
        protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
                global $wgContLang;
 
-               if( $num > 0 ) {
+               if ( $num > 0 ) {
                        $html = array();
-                       if( !$this->listoutput )
+                       if ( !$this->listoutput )
                                $html[] = $this->openList( $offset );
 
                        # $res might contain the whole 1,000 rows, so we read up to
                        # $num [should update this to use a Pager]
-                       for( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
+                       for ( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
                                $line = $this->formatResult( $skin, $row );
-                               if( $line ) {
+                               if ( $line ) {
                                        $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
                                                ? ' class="not-patrolled"'
                                                : '';
@@ -417,10 +555,10 @@ class QueryPage {
                        }
 
                        # Flush the final result
-                       if( $this->tryLastResult() ) {
+                       if ( $this->tryLastResult() ) {
                                $row = null;
                                $line = $this->formatResult( $skin, $row );
-                               if( $line ) {
+                               if ( $line ) {
                                        $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
                                                ? ' class="not-patrolled"'
                                                : '';
@@ -430,7 +568,7 @@ class QueryPage {
                                }
                        }
 
-                       if( !$this->listoutput )
+                       if ( !$this->listoutput )
                                $html[] = $this->closeList();
 
                        $html = $this->listoutput
@@ -465,13 +603,13 @@ class QueryPage {
                        $wgOut->addWikiMsg( 'feed-unavailable' );
                        return;
                }
-               
+
                global $wgFeedLimit;
-               if( $limit > $wgFeedLimit ) {
+               if ( $limit > $wgFeedLimit ) {
                        $limit = $wgFeedLimit;
                }
 
-               if( isset($wgFeedClasses[$class]) ) {
+               if ( isset( $wgFeedClasses[$class] ) ) {
                        $feed = new $wgFeedClasses[$class](
                                $this->feedTitle(),
                                $this->feedDesc(),
@@ -479,12 +617,10 @@ class QueryPage {
                        $feed->outHeader();
 
                        $dbr = wfGetDB( DB_SLAVE );
-                       $sql = $this->getSQL() . $this->getOrder();
-                       $sql = $dbr->limitResult( $sql, $limit, 0 );
-                       $res = $dbr->query( $sql, 'QueryPage::doFeed' );
+                       $res = $this->reallyDoQuery( $limit, 0 );
                        foreach ( $res as $obj ) {
                                $item = $this->feedResult( $obj );
-                               if( $item ) {
+                               if ( $item ) {
                                        $feed->outItem( $item );
                                }
                        }
@@ -501,14 +637,14 @@ class QueryPage {
         * feedItemDesc()
         */
        function feedResult( $row ) {
-               if( !isset( $row->title ) ) {
+               if ( !isset( $row->title ) ) {
                        return null;
                }
                $title = Title::MakeTitle( intval( $row->namespace ), $row->title );
-               if( $title ) {
+               if ( $title ) {
                        $date = isset( $row->timestamp ) ? $row->timestamp : '';
                        $comments = '';
-                       if( $title ) {
+                       if ( $title ) {
                                $talkpage = $title->getTalkPage();
                                $comments = $talkpage->getFullURL();
                        }
@@ -519,7 +655,7 @@ class QueryPage {
                                $title->getFullURL(),
                                $date,
                                $this->feedItemAuthor( $row ),
-                               $comments);
+                               $comments );
                } else {
                        return null;
                }
@@ -579,7 +715,7 @@ abstract class WantedQueryPage extends QueryPage {
                        // If there are no rows we get an error seeking.
                        $db->dataSeek( $res, 0 );
        }
-       
+
        /**
         * Should formatResult() always check page existence, even if
         * the results are fresh?  This is a (hopefully temporary)
@@ -600,8 +736,8 @@ abstract class WantedQueryPage extends QueryPage {
         */
        public function formatResult( $skin, $result ) {
                $title = Title::makeTitleSafe( $result->namespace, $result->title );
-               if( $title instanceof Title ) {
-                       if( $this->isCached() || $this->forceExistenceCheck() ) {
+               if ( $title instanceof Title ) {
+                       if ( $this->isCached() || $this->forceExistenceCheck() ) {
                                $pageLink = $title->isKnown()
                                        ? '<del>' . $skin->link( $title ) . '</del>'
                                        : $skin->link(
@@ -626,7 +762,7 @@ abstract class WantedQueryPage extends QueryPage {
                        return wfMsgHtml( 'wantedpages-badtitle', $tsafe );
                }
        }
-       
+
        /**
         * Make a "what links here" link for a given title
         *
index 2f9e264..ef8caa5 100644 (file)
@@ -84,36 +84,36 @@ class SpecialPage {
         */
        static public $mList = array(
                # Maintenance Reports
-               'BrokenRedirects'           => array( 'SpecialPage', 'BrokenRedirects' ),
-               'Deadendpages'              => array( 'SpecialPage', 'Deadendpages' ),
-               'DoubleRedirects'           => array( 'SpecialPage', 'DoubleRedirects' ),
-               'Longpages'                 => array( 'SpecialPage', 'Longpages' ),
-               'Ancientpages'              => array( 'SpecialPage', 'Ancientpages' ),
-               'Lonelypages'               => array( 'SpecialPage', 'Lonelypages' ),
-               'Fewestrevisions'           => array( 'SpecialPage', 'Fewestrevisions' ),
-               'Withoutinterwiki'          => array( 'SpecialPage', 'Withoutinterwiki' ),
+               'BrokenRedirects'           => array( 'BrokenRedirectsPage' ),
+               'Deadendpages'              => array( 'DeadendpagesPage' ),
+               'DoubleRedirects'           => array( 'DoubleRedirectsPage' ),
+               'Longpages'                 => array( 'LongpagesPage' ),
+               'Ancientpages'              => array( 'AncientpagesPage' ),
+               'Lonelypages'               => array( 'LonelypagesPage' ),
+               'Fewestrevisions'           => array( 'FewestrevisionsPage' ),
+               'Withoutinterwiki'          => array( 'WithoutinterwikiPage' ),
                'Protectedpages'            => 'SpecialProtectedpages',
                'Protectedtitles'           => 'SpecialProtectedtitles',
-               'Shortpages'                => array( 'SpecialPage', 'Shortpages' ),
-               'Uncategorizedcategories'   => array( 'SpecialPage', 'Uncategorizedcategories' ),
-               'Uncategorizedimages'       => array( 'SpecialPage', 'Uncategorizedimages' ),
-               'Uncategorizedpages'        => array( 'SpecialPage', 'Uncategorizedpages' ),
-               'Uncategorizedtemplates'    => array( 'SpecialPage', 'Uncategorizedtemplates' ),
-               'Unusedcategories'          => array( 'SpecialPage', 'Unusedcategories' ),
-               'Unusedimages'              => array( 'SpecialPage', 'Unusedimages' ),
-               'Unusedtemplates'           => array( 'SpecialPage', 'Unusedtemplates' ),
-               'Unwatchedpages'            => array( 'SpecialPage', 'Unwatchedpages', 'unwatchedpages' ),
-               'Wantedcategories'          => array( 'SpecialPage', 'Wantedcategories' ),
-               'Wantedfiles'               => array( 'SpecialPage', 'Wantedfiles' ),
-               'Wantedpages'               => array( 'IncludableSpecialPage', 'Wantedpages' ),
-               'Wantedtemplates'           => array( 'SpecialPage', 'Wantedtemplates' ),
+               'Shortpages'                => array( 'ShortpagesPage' ),
+               'Uncategorizedcategories'   => array( 'UncategorizedcategoriesPage' ),
+               'Uncategorizedimages'       => array( 'UncategorizedimagesPage' ),
+               'Uncategorizedpages'        => array( 'UncategorizedpagesPage' ),
+               'Uncategorizedtemplates'    => array( 'UncategorizedtemplatesPage' ),
+               'Unusedcategories'          => array( 'UnusedcategoriesPage' ),
+               'Unusedimages'              => array( 'UnusedimagesPage' ),
+               'Unusedtemplates'           => array( 'UnusedtemplatesPage' ),
+               'Unwatchedpages'            => array( 'UnwatchedpagesPage' ),
+               'Wantedcategories'          => array( 'WantedcategoriesPage' ),
+               'Wantedfiles'               => array( 'WantedfilesPage' ),
+               'Wantedpages'               => array( 'WantedpagesPage' ),
+               'Wantedtemplates'           => array( 'WantedtemplatesPage' ),
 
                # List of pages
                'Allpages'                  => 'SpecialAllpages',
                'Prefixindex'               => 'SpecialPrefixindex',
                'Categories'                => 'SpecialCategories',
-               'Disambiguations'           => array( 'SpecialPage', 'Disambiguations' ),
-               'Listredirects'             => array( 'SpecialPage', 'Listredirects' ),
+               'Disambiguations'           => array( 'DisambiguationsPage' ),
+               'Listredirects'             => array( 'ListredirectsPage' ),
 
                # Login/create account
                'Userlogin'                 => 'LoginForm',
@@ -147,8 +147,8 @@ class SpecialPage {
                # Media reports and uploads
                'Listfiles'                 => array( 'SpecialPage', 'Listfiles' ),
                'Filepath'                  => 'SpecialFilepath',
-               'MIMEsearch'                => array( 'SpecialPage', 'MIMEsearch' ),
-               'FileDuplicateSearch'       => array( 'SpecialPage', 'FileDuplicateSearch' ),
+               'MIMEsearch'                => 'MIMEsearchPage',
+               'FileDuplicateSearch'       => 'FileDuplicateSearchPage',
                'Upload'                    => 'SpecialUpload',
                'UploadStash'               => 'SpecialUploadStash',
 
@@ -160,17 +160,17 @@ class SpecialPage {
                'Unlockdb'                  => 'SpecialUnlockdb',
 
                # Redirecting special pages
-               'LinkSearch'                => array( 'SpecialPage', 'LinkSearch' ),
+               'LinkSearch'                => array( 'LinkSearchPage' ),
                'Randompage'                => 'Randompage',
                'Randomredirect'            => 'SpecialRandomredirect',
 
                # High use pages
-               'Mostlinkedcategories'      => array( 'SpecialPage', 'Mostlinkedcategories' ),
-               'Mostimages'                => array( 'SpecialPage', 'Mostimages' ),
-               'Mostlinked'                => array( 'SpecialPage', 'Mostlinked' ),
-               'Mostlinkedtemplates'       => array( 'SpecialPage', 'Mostlinkedtemplates' ),
-               'Mostcategories'            => array( 'SpecialPage', 'Mostcategories' ),
-               'Mostrevisions'             => array( 'SpecialPage', 'Mostrevisions' ),
+               'Mostlinkedcategories'      => array( 'MostlinkedCategoriesPage' ),
+               'Mostimages'                => array( 'MostimagesPage' ),
+               'Mostlinked'                => array( 'MostlinkedPage' ),
+               'Mostlinkedtemplates'       => array( 'MostlinkedTemplatesPage' ),
+               'Mostcategories'            => array( 'MostcategoriesPage' ),
+               'Mostrevisions'             => array( 'MostrevisionsPage' ),
 
                # Page tools
                'ComparePages'              => 'SpecialComparePages',
@@ -220,7 +220,7 @@ class SpecialPage {
                self::$mListInitialised = true;
 
                if( !$wgDisableCounters ) {
-                       self::$mList['Popularpages'] = array( 'SpecialPage', 'Popularpages' );
+                       self::$mList['Popularpages'] = array( 'PopularpagesPage' );
                }
 
                if( !$wgDisableInternalSearch ) {
index 2d5047d..cbb5df8 100644 (file)
@@ -28,8 +28,8 @@
  */
 class AncientPagesPage extends QueryPage {
 
-       function getName() {
-               return "Ancientpages";
+       function __construct( $name = 'Ancientpages' ) {
+               parent::__construct( $name );
        }
 
        function isExpensive() {
@@ -38,20 +38,20 @@ class AncientPagesPage extends QueryPage {
 
        function isSyndicated() { return false; }
 
-       function getSQL() {
-               $db = wfGetDB( DB_SLAVE );
-               $page = $db->tableName( 'page' );
-               $revision = $db->tableName( 'revision' );
-               $epoch = $db->unixTimestamp( 'rev_timestamp' );
+       function getQueryInfo() {
+               return array(
+                       'tables' => array( 'page', 'revision' ),
+                       'fields' => array( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'rev_timestamp AS value' ),
+                       'conds' => array( 'page_namespace' => MWNamespace::getContentNamespaces(),
+                                       'page_is_redirect' => 0,
+                                       'page_latest=rev_id' )
+               );
+       }
 
-               return
-                       "SELECT 'Ancientpages' as type,
-                                       page_namespace as namespace,
-                               page_title as title,
-                               $epoch as value
-                       FROM $page, $revision
-                       WHERE page_namespace=".NS_MAIN." AND page_is_redirect=0
-                         AND page_latest=rev_id";
+       function usesTimestamps() {
+               return true;
        }
 
        function sortDescending() {
@@ -67,14 +67,6 @@ class AncientPagesPage extends QueryPage {
                        $title,
                        htmlspecialchars( $wgContLang->convert( $title->getPrefixedText() ) )
                );
-               return wfSpecialList($link, htmlspecialchars($d) );
+               return wfSpecialList( $link, htmlspecialchars($d) );
        }
 }
-
-function wfSpecialAncientpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $app = new AncientPagesPage();
-
-       $app->doQuery( $offset, $limit );
-}
index 98b0212..ddd6d48 100644 (file)
  * @ingroup SpecialPage
  */
 class BrokenRedirectsPage extends PageQueryPage {
-       var $targets = array();
 
-       function getName() {
-               return 'BrokenRedirects';
+       function __construct( $name = 'BrokenRedirects' ) {
+               parent::__construct( $name );
        }
-
-       function isExpensive( ) { return true; }
+       
+       function isExpensive() { return true; }
        function isSyndicated() { return false; }
+       function sortDescending() { return false; }
 
-       function getPageHeader( ) {
+       function getPageHeader() {
                return wfMsgExt( 'brokenredirectstext', array( 'parse' ) );
        }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $redirect ) = $dbr->tableNamesN( 'page', 'redirect' );
-
-               $sql = "SELECT 'BrokenRedirects'  AS type,
-                               p1.page_namespace AS namespace,
-                               p1.page_title     AS title,
-                               rd_namespace,
-                               rd_title
-                          FROM $redirect AS rd
-                     JOIN $page p1 ON (rd.rd_from=p1.page_id)
-                     LEFT JOIN $page AS p2 ON (rd_namespace=p2.page_namespace AND rd_title=p2.page_title )
-                                 WHERE rd_namespace >= 0
-                                   AND p2.page_namespace IS NULL";
-               return $sql;
+       function getQueryInfo() {
+               return array(
+                       'tables' => array( 'redirect', 'p1' => 'page',
+                                       'p2' => 'page' ),
+                       'fields' => array( 'p1.page_namespace AS namespace',
+                                       'p1.page_title AS title',
+                                       'rd_namespace',
+                                       'rd_title'
+                       ),
+                       'conds' => array( 'rd_namespace >= 0',
+                                       'p2.page_namespace IS NULL'
+                       ),
+                       'join_conds' => array( 'p1' => array( 'LEFT JOIN', array(
+                                               'rd_from=p1.page_id',
+                                       ) ),
+                                       'p2' => array( 'LEFT JOIN', array(
+                                               'rd_namespace=p2.page_namespace',
+                                               'rd_title=p2.page_title'
+                                       ) )
+                       )
+               );
        }
 
-       function getOrder() {
-               return '';
+       function getOrderFields() {
+               return array ( 'rd_namespace', 'rd_title', 'rd_from' );
        }
 
        function formatResult( $skin, $result ) {
@@ -120,14 +126,3 @@ class BrokenRedirectsPage extends PageQueryPage {
                return $out;
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialBrokenRedirects() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $sbr = new BrokenRedirectsPage();
-
-       return $sbr->doQuery( $offset, $limit );
-}
index dfa053a..bb9625a 100644 (file)
@@ -28,8 +28,8 @@
  */
 class DeadendPagesPage extends PageQueryPage {
 
-       function getName( ) {
-               return "Deadendpages";
+       function __construct( $name = 'Deadendpages' ) {
+               parent::__construct( $name );
        }
 
        function getPageHeader() {
@@ -41,11 +41,13 @@ class DeadendPagesPage extends PageQueryPage {
         *
         * @return true
         */
-       function isExpensive( ) {
-               return 1;
+       function isExpensive() {
+               return true;
        }
 
-       function isSyndicated() { return false; }
+       function isSyndicated() {
+               return false; 
+       }
 
        /**
         * @return false
@@ -54,28 +56,30 @@ class DeadendPagesPage extends PageQueryPage {
                return false;
        }
 
-       /**
-        * @return string an sqlquery
-        */
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
-               return "SELECT 'Deadendpages' as type, page_namespace AS namespace, page_title as title, page_title AS value " .
-       "FROM $page LEFT JOIN $pagelinks ON page_id = pl_from " .
-       "WHERE pl_from IS NULL " .
-       "AND page_namespace = 0 " .
-       "AND page_is_redirect = 0";
+       function getQueryInfo() {
+               return array(
+                       'tables' => array( 'page', 'pagelinks' ),
+                       'fields' => array( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_title AS value'
+                       ),
+                       'conds' => array( 'pl_from IS NULL',
+                                       'page_namespace' => MWNamespace::getContentNamespaces(),
+                                       'page_is_redirect' => 0
+                       ),
+                       'join_conds' => array( 'pagelinks' => array( 'LEFT JOIN', array(
+                                       'page_id=pl_from'
+                       ) ) )
+               );
+       }
+       
+       function getOrderFields() {
+               // For some crazy reason ordering by a constant
+               // causes a filesort
+               if( count( MWNamespace::getContentNamespaces() ) > 1 ) {
+                       return array( 'page_namespace', 'page_title' );
+               } else {
+                       return array( 'page_title' );
+               }
        }
-}
-
-/**
- * Constructor
- */
-function wfSpecialDeadendpages() {
-
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $depp = new DeadendPagesPage();
-
-       return $depp->doQuery( $offset, $limit );
 }
index 3e70618..314a7fb 100644 (file)
  */
 class DisambiguationsPage extends PageQueryPage {
 
-       function getName() {
-               return 'Disambiguations';
+       function __construct( $name = 'Disambiguations' ) {
+               parent::__construct( $name );
        }
 
-       function isExpensive( ) { return true; }
+       function isExpensive() { return true; }
        function isSyndicated() { return false; }
 
-
-       function getPageHeader( ) {
+       function getPageHeader() {
                return wfMsgExt( 'disambiguations-text', array( 'parse' ) );
        }
 
-       function getSQL() {
-               global $wgContentNamespaces;
-
+       function getQueryInfo() {
                $dbr = wfGetDB( DB_SLAVE );
-
-               $dMsgText = wfMsgForContent('disambiguationspage');
-
+               $dMsgText = wfMsgForContent( 'disambiguationspage' );
                $linkBatch = new LinkBatch;
 
                # If the text can be treated as a title, use it verbatim.
                # Otherwise, pull the titles from the links table
                $dp = Title::newFromText($dMsgText);
                if( $dp ) {
-                       if($dp->getNamespace() != NS_TEMPLATE) {
+                       if( $dp->getNamespace() != NS_TEMPLATE ) {
                                # FIXME we assume the disambiguation message is a template but
                                # the page can potentially be from another namespace :/
                                wfDebug("Mediawiki:disambiguationspage message does not refer to a template!\n");
@@ -65,47 +60,64 @@ class DisambiguationsPage extends PageQueryPage {
                                $res = $dbr->select(
                                        array('pagelinks', 'page'),
                                        'pl_title',
-                                       array('page_id = pl_from', 'pl_namespace' => NS_TEMPLATE,
-                                               'page_namespace' => $disPageObj->getNamespace(), 'page_title' => $disPageObj->getDBkey()),
+                                       array('page_id = pl_from',
+                                               'pl_namespace' => NS_TEMPLATE,
+                                               'page_namespace' => $disPageObj->getNamespace(),
+                                               'page_title' => $disPageObj->getDBkey()),
                                        __METHOD__ );
 
                                foreach ( $res as $row ) {
                                        $linkBatch->addObj( Title::makeTitle( NS_TEMPLATE, $row->pl_title ));
                                }
                }
-
-               $set = $linkBatch->constructSet( 'lb.tl', $dbr );
+               $set = $linkBatch->constructSet( 'tl', $dbr );
                if( $set === false ) {
-                       # We must always return a valid sql query, but this way DB will always quicly return an empty result
+                       # We must always return a valid SQL query, but this way
+                       # the DB will always quickly return an empty result
                        $set = 'FALSE';
                        wfDebug("Mediawiki:disambiguationspage message does not link to any templates!\n");
                }
+               
+               // FIXME: What are pagelinks and p2 doing here?
+               return array (
+                       'tables' => array( 'templatelinks', 'p1' => 'page', 'pagelinks', 'p2' => 'page' ),
+                       'fields' => array( 'p1.page_namespace AS namespace',
+                                       'p1.page_title AS title',
+                                       'pl_from AS value' ),
+                       'conds' => array( $set,
+                                       'p1.page_id = tl_from',
+                                       'pl_namespace = p1.page_namespace',
+                                       'pl_title = p1.page_title',
+                                       'p2.page_id = pl_from',
+                                       'p2.page_namespace' => MWNamespace::getContentNamespaces() )
+               );
+       }
 
-               list( $page, $pagelinks, $templatelinks) = $dbr->tableNamesN( 'page', 'pagelinks', 'templatelinks' );
-
-               if ( $wgContentNamespaces ) {
-                       $nsclause = 'IN (' . $dbr->makeList( $wgContentNamespaces ) . ')';
-               } else {
-                       $nsclause = '= ' . NS_MAIN;
+       function getOrderFields() {
+               return array( 'tl_namespace', 'tl_title', 'value' );
+       }
+       
+       function sortDescending() {
+               return false;
+       }
+       
+       /**
+        * Fetch  links and cache their existence
+        */
+       function preprocessResults( $db, $res ) {
+               $batch = new LinkBatch;
+               foreach ( $res as $row ) {
+                       $batch->add( $row->namespace, $row->title );
                }
+               $batch->execute();
 
-               $sql = "SELECT 'Disambiguations' AS \"type\", pb.page_namespace AS namespace,"
-                       ." pb.page_title AS title, la.pl_from AS value"
-                       ." FROM {$templatelinks} AS lb, {$page} AS pb, {$pagelinks} AS la, {$page} AS pa"
-                       ." WHERE $set"  # disambiguation template(s)
-                       .' AND pa.page_id = la.pl_from'
-                       .' AND pa.page_namespace ' . $nsclause
-                       .' AND pb.page_id = lb.tl_from'
-                       .' AND pb.page_namespace = la.pl_namespace'
-                       .' AND pb.page_title = la.pl_title'
-                       .' ORDER BY lb.tl_namespace, lb.tl_title';
-
-               return $sql;
+               // Back to start for display
+               if ( $db->numRows( $res ) > 0 ) {
+                       // If there are no rows we get an error seeking.
+                       $db->dataSeek( $res, 0 );
+               }
        }
 
-       function getOrder() {
-               return '';
-       }
 
        function formatResult( $skin, $result ) {
                global $wgContLang;
@@ -113,21 +125,11 @@ class DisambiguationsPage extends PageQueryPage {
                $dp = Title::makeTitle( $result->namespace, $result->title );
 
                $from = $skin->link( $title );
-               $edit = $skin->link( $title, wfMsgExt( 'parentheses', array( 'escape' ), wfMsg( 'editlink' ) ) , array(), array( 'redirect' => 'no', 'action' => 'edit' ) );
+               $edit = $skin->link( $title, wfMsgExt( 'parentheses', array( 'escape' ), wfMsg( 'editlink' ) ) ,
+                       array(), array( 'redirect' => 'no', 'action' => 'edit' ) );
                $arr  = $wgContLang->getArrow();
                $to   = $skin->link( $dp );
 
                return "$from $edit $arr $to";
        }
 }
-
-/**
- * Constructor
- */
-function wfSpecialDisambiguations() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $sd = new DisambiguationsPage();
-
-       return $sd->doQuery( $offset, $limit );
-}
index c7f6321..d25de5d 100644 (file)
  */
 class DoubleRedirectsPage extends PageQueryPage {
 
-       function getName() {
-               return 'DoubleRedirects';
+       function __construct( $name = 'DoubleRedirects' ) {
+               parent::__construct( $name );
        }
-
-       function isExpensive( ) { return true; }
+       
+       function isExpensive() { return true; }
        function isSyndicated() { return false; }
+       function sortDescending() { return false; }
 
-       function getPageHeader( ) {
+       function getPageHeader() {
                return wfMsgExt( 'doubleredirectstext', array( 'parse' ) );
        }
 
-       function getSQLText( &$dbr, $namespace = null, $title = null ) {
-
-               list( $page, $redirect ) = $dbr->tableNamesN( 'page', 'redirect' );
-
+       function reallyGetQueryInfo( $namespace = null, $title = null ) {
                $limitToTitle = !( $namespace === null && $title === null );
-               $sql = $limitToTitle ? "SELECT" : "SELECT 'DoubleRedirects' as type," ;
-               $sql .=
-                        " pa.page_namespace as namespace, pa.page_title as title," .
-                        " pb.page_namespace as nsb, pb.page_title as tb," .
-                        " pc.page_namespace as nsc, pc.page_title as tc" .
-                  " FROM $redirect AS ra, $redirect AS rb, $page AS pa, $page AS pb, $page AS pc" .
-                  " WHERE ra.rd_from=pa.page_id" .
-                        " AND ra.rd_namespace=pb.page_namespace" .
-                        " AND ra.rd_title=pb.page_title" .
-                        " AND rb.rd_from=pb.page_id" .
-                        " AND rb.rd_namespace=pc.page_namespace" .
-                        " AND rb.rd_title=pc.page_title";
-
-               if( $limitToTitle ) {
-                       $encTitle = $dbr->addQuotes( $title );
-                       $sql .= " AND pa.page_namespace=$namespace" .
-                                       " AND pa.page_title=$encTitle";
+               $retval = array (
+                       'tables' => array ( 'ra' => 'redirect',
+                                       'rb' => 'redirect', 'pa' => 'page',
+                                       'pb' => 'page', 'pc' => 'page' ),
+                       'fields' => array ( 'pa.page_namespace AS namespace',
+                                       'pa.page_title AS title',
+                                       'pb.page_namespace AS nsb',
+                                       'pb.page_title AS tb',
+                                       'pc.page_namespace AS nsc',
+                                       'pc.page_title AS tc' ),
+                       'conds' => array ( 'ra.rd_from = pa.page_id',
+                                       'pb.page_namespace = ra.rd_namespace',
+                                       'pb.page_title = ra.rd_title',
+                                       'rb.rd_from = pb.page_id',
+                                       'pc.page_namespace = rb.rd_namespace',
+                                       'pc.page_title = rb.rd_title' )
+               );
+               if ( $limitToTitle ) {
+                       $retval['conds']['pa.page_namespace'] = $namespace;
+                       $retval['conds']['pa.page_title'] = $title;
                }
-
-               return $sql;
+               return $retval;
        }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               return $this->getSQLText( $dbr );
+       function getQueryInfo() {
+               return $this->reallyGetQueryInfo();
        }
 
-       function getOrder() {
-               return '';
+       function getOrderFields() {
+               return array ( 'ra.rd_namespace', 'ra.rd_title' );
        }
 
        function formatResult( $skin, $result ) {
                global $wgContLang;
 
-               $fname = 'DoubleRedirectsPage::formatResult';
                $titleA = Title::makeTitle( $result->namespace, $result->title );
 
                if ( $result && !isset( $result->nsb ) ) {
                        $dbr = wfGetDB( DB_SLAVE );
-                       $sql = $this->getSQLText( $dbr, $result->namespace, $result->title );
-                       $res = $dbr->query( $sql, $fname );
+                       $qi = $this->reallyGetQueryInfo( $result->namespace,
+                                       $result->title );
+                       $res = $dbr->select($qi['tables'], $qi['fields'],
+                                       $qi['conds'], __METHOD__ );
                        if ( $res ) {
                                $result = $dbr->fetchObject( $res );
                        }
@@ -124,15 +124,3 @@ class DoubleRedirectsPage extends PageQueryPage {
                return( "{$linkA} {$edit} {$arr} {$linkB} {$arr} {$linkC}" );
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialDoubleRedirects() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $sdr = new DoubleRedirectsPage();
-
-       return $sdr->doQuery( $offset, $limit );
-
-}
index c265ed3..10d8e6b 100644 (file)
  */
 class FewestrevisionsPage extends QueryPage {
 
-       function getName() {
-               return 'Fewestrevisions';
+       function __construct( $name = 'Fewestrevisions' ) {
+               parent::__construct( $name );
        }
-
+       
        function isExpensive() {
                return true;
        }
@@ -41,31 +41,35 @@ class FewestrevisionsPage extends QueryPage {
                return false;
        }
 
-       function getSql() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $revision, $page ) = $dbr->tableNamesN( 'revision', 'page' );
-
-               return "SELECT 'Fewestrevisions' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               page_is_redirect as redirect,
-                               COUNT(*) as value
-                       FROM $revision
-                       JOIN $page ON page_id = rev_page
-                       WHERE page_namespace = " . NS_MAIN . "
-                       GROUP BY page_namespace, page_title, page_is_redirect
-                       HAVING COUNT(*) > 1";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'revision', 'page' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'COUNT(*) AS value',
+                                       'page_is_redirect AS redirect' ),
+                       'conds' => array ( 'page_namespace' => MWNamespace::getContentNamespaces(),
+                                       'page_id = rev_page' ),
+                       'options' => array ( 'HAVING' => 'COUNT(*) > 1',
                        // ^^^ This was probably here to weed out redirects.
                        // Since we mark them as such now, it might be
                        // useful to remove this. People _do_ create pages
                        // and never revise them, they aren't necessarily
                        // redirects.
+                               'GROUP BY' => 'page_namespace, page_title, ' .
+                                               'page_is_redirect' )
+               );
        }
 
+
        function sortDescending() {
                return false;
        }
 
+       /**
+        * @param $skin Skin object
+        * @param $result Object: database row
+        */
        function formatResult( $skin, $result ) {
                global $wgLang, $wgContLang;
 
@@ -94,9 +98,3 @@ class FewestrevisionsPage extends QueryPage {
                return wfSpecialList( $plink, $nlink );
        }
 }
-
-function wfSpecialFewestrevisions() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $frp = new FewestrevisionsPage();
-       $frp->doQuery( $offset, $limit );
-}
index b0a6b39..6eccb90 100644 (file)
  * @ingroup SpecialPage
  */
 class FileDuplicateSearchPage extends QueryPage {
-       var $hash, $filename;
+       protected $hash, $filename;
 
-       function __construct( $hash, $filename ) {
-               $this->hash = $hash;
-               $this->filename = $filename;
+       function __construct( $name = 'FileDuplicateSearch' ) {
+               parent::__construct( $name );
        }
 
-       function getName() { return 'FileDuplicateSearch'; }
-       function isExpensive() { return false; }
        function isSyndicated() { return false; }
+       function isCacheable() { return false; }
 
        function linkParameters() {
                return array( 'filename' => $this->filename );
        }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $image = $dbr->tableName( 'image' );
-               $hash = $dbr->addQuotes( $this->hash );
+       function getQueryInfo() {
+               return array(
+                       'tables' => array( 'image' ),
+                       'fields' => array(
+                               'img_name AS title',
+                               'img_sha1 AS value',
+                               'img_user_text',
+                               'img_timestamp'
+                       ),
+                       'conds' => array( 'img_sha1' => $this->hash )
+               );
+       }
+       
+       function execute( $par ) {
+               global $wgRequest, $wgOut, $wgLang, $wgContLang, $wgScript;
+               
+               $this->setHeaders();
+               $this->outputHeader();
+               
+               $this->filename =  isset( $par ) ?  $par : $wgRequest->getText( 'filename' );
+               $this->hash = '';
+               $title = Title::makeTitleSafe( NS_FILE, $this->filename );
+               if( $title && $title->getText() != '' ) {
+                       $dbr = wfGetDB( DB_SLAVE );
+                       $this->hash = $dbr->selectField( 'image', 'img_sha1', array( 'img_name' => $title->getDBkey() ), __METHOD__ );
+               }
+
+               # Create the input form
+               $wgOut->addHTML(
+                       Xml::openElement( 'form', array( 'id' => 'fileduplicatesearch', 'method' => 'get', 'action' => $wgScript ) ) .
+                       Html::hidden( 'title', $this->getTitle()->getPrefixedDbKey() ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Xml::element( 'legend', null, wfMsg( 'fileduplicatesearch-legend' ) ) .
+                       Xml::inputLabel( wfMsg( 'fileduplicatesearch-filename' ), 'filename', 'filename', 50, $this->filename ) . ' ' .
+                       Xml::submitButton( wfMsg( 'fileduplicatesearch-submit' ) ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' )
+               );
+
+               if( $this->hash != '' ) {
+                       $align = $wgContLang->alignEnd();
+
+                       # Show a thumbnail of the file
+                       $img = wfFindFile( $title );
+                       if ( $img ) {
+                               $thumb = $img->transform( array( 'width' => 120, 'height' => 120 ) );
+                               if( $thumb ) {
+                                       $wgOut->addHTML( '<div style="float:' . $align . '" id="mw-fileduplicatesearch-icon">' .
+                                               $thumb->toHtml( array( 'desc-link' => false ) ) . '<br />' .
+                                               wfMsgExt( 'fileduplicatesearch-info', array( 'parse' ),
+                                                       $wgLang->formatNum( $img->getWidth() ),
+                                                       $wgLang->formatNum( $img->getHeight() ),
+                                                       $wgLang->formatSize( $img->getSize() ),
+                                                       $img->getMimeType()
+                                               ) .
+                                               '</div>' );
+                               }
+                       }
 
-               return "SELECT 'FileDuplicateSearch' AS type,
-                               img_name AS title,
-                               img_sha1 AS value,
-                               img_user_text,
-                               img_timestamp
-                       FROM $image
-                       WHERE img_sha1 = $hash
-                       ";
+                       parent::execute( $par );
+
+                       # Show a short summary
+                       if( $this->numRows == 1 ) {
+                               $wgOut->wrapWikiMsg(
+                                       "<p class='mw-fileduplicatesearch-result-1'>\n$1\n</p>",
+                                       array( 'fileduplicatesearch-result-1', $this->filename )
+                               );
+                       } elseif ( $this->numRows > 1 ) {
+                               $wgOut->wrapWikiMsg(
+                                       "<p class='mw-fileduplicatesearch-result-n'>\n$1\n</p>",
+                                       array( 'fileduplicatesearch-result-n', $this->filename,
+                                               $wgLang->formatNum( $this->numRows - 1 ) )
+                               );
+                       }
+               }
        }
 
        function formatResult( $skin, $result ) {
@@ -75,77 +135,3 @@ class FileDuplicateSearchPage extends QueryPage {
                return "$plink . . $user . . $time";
        }
 }
-
-/**
- * Output the HTML search form, and constructs the FileDuplicateSearch object.
- */
-function wfSpecialFileDuplicateSearch( $par = null ) {
-       global $wgRequest, $wgOut, $wgLang, $wgContLang, $wgScript;
-
-       $hash = '';
-       $filename =  isset( $par ) ?  $par : $wgRequest->getText( 'filename' );
-
-       $title = Title::newFromText( $filename );
-       if( $title && $title->getText() != '' ) {
-               $dbr = wfGetDB( DB_SLAVE );
-               $image = $dbr->tableName( 'image' );
-               $encFilename = $dbr->addQuotes( htmlspecialchars( $title->getDBkey() ) );
-               $sql = "SELECT img_sha1 from $image where img_name = $encFilename";
-               $res = $dbr->query( $sql );
-               $row = $dbr->fetchRow( $res );
-               if( $row !== false ) {
-                       $hash = $row[0];
-               }
-       }
-
-       # Create the input form
-       $wgOut->addHTML(
-               Xml::openElement( 'form', array( 'id' => 'fileduplicatesearch', 'method' => 'get', 'action' => $wgScript ) ) .
-               Html::hidden( 'title', SpecialPage::getTitleFor( 'FileDuplicateSearch' )->getPrefixedDbKey() ) .
-               Xml::openElement( 'fieldset' ) .
-               Xml::element( 'legend', null, wfMsg( 'fileduplicatesearch-legend' ) ) .
-               Xml::inputLabel( wfMsg( 'fileduplicatesearch-filename' ), 'filename', 'filename', 50, $filename ) . ' ' .
-               Xml::submitButton( wfMsg( 'fileduplicatesearch-submit' ) ) .
-               Xml::closeElement( 'fieldset' ) .
-               Xml::closeElement( 'form' )
-       );
-
-       if( $hash != '' ) {
-               $align = $wgContLang->alignEnd();
-
-               # Show a thumbnail of the file
-               $img = wfFindFile( $title );
-               if ( $img ) {
-                       $thumb = $img->transform( array( 'width' => 120, 'height' => 120 ) );
-                       if( $thumb ) {
-                               $wgOut->addHTML( '<div style="float:' . $align . '" id="mw-fileduplicatesearch-icon">' .
-                                       $thumb->toHtml( array( 'desc-link' => false ) ) . '<br />' .
-                                       wfMsgExt( 'fileduplicatesearch-info', array( 'parse' ),
-                                               $wgLang->formatNum( $img->getWidth() ),
-                                               $wgLang->formatNum( $img->getHeight() ),
-                                               $wgLang->formatSize( $img->getSize() ),
-                                               $img->getMimeType()
-                                       ) .
-                                       '</div>' );
-                       }
-               }
-
-               # Do the query
-               $wpp = new FileDuplicateSearchPage( $hash, $filename );
-               list( $limit, $offset ) = wfCheckLimits();
-               $count = $wpp->doQuery( $offset, $limit );
-
-               # Show a short summary
-               if( $count == 1 ) {
-                       $wgOut->wrapWikiMsg(
-                               "<p class='mw-fileduplicatesearch-result-1'>\n$1\n</p>",
-                               array( 'fileduplicatesearch-result-1', $filename )
-                       );
-               } elseif ( $count > 1 ) {
-                       $wgOut->wrapWikiMsg(
-                               "<p class='mw-fileduplicatesearch-result-n'>\n$1\n</p>",
-                               array( 'fileduplicatesearch-result-n', $filename, $wgLang->formatNum( $count - 1 ) )
-                       );
-               }
-       }
-}
index 124ede1..c97566f 100644 (file)
 
 /**
  * Special:LinkSearch to search the external-links table.
- */
-function wfSpecialLinkSearch( $par ) {
-
-       list( $limit, $offset ) = wfCheckLimits();
-       global $wgOut, $wgUrlProtocols, $wgMiserMode, $wgLang;
-       $target = $GLOBALS['wgRequest']->getVal( 'target', $par );
-       $namespace = $GLOBALS['wgRequest']->getIntorNull( 'namespace', null );
-
-       $protocols_list[] = '';
-       foreach( $wgUrlProtocols as $prot ) {
-               $protocols_list[] = $prot;
-       }
-
-       $target2 = $target;
-       $protocol = '';
-       $pr_sl = strpos($target2, '//' );
-       $pr_cl = strpos($target2, ':' );
-       if ( $pr_sl ) {
-               // For protocols with '//'
-               $protocol = substr( $target2, 0 , $pr_sl+2 );
-               $target2 = substr( $target2, $pr_sl+2 );
-       } elseif ( !$pr_sl && $pr_cl ) {
-               // For protocols without '//' like 'mailto:'
-               $protocol = substr( $target2, 0 , $pr_cl+1 );
-               $target2 = substr( $target2, $pr_cl+1 );
-       } elseif ( $protocol == '' && $target2 != '' ) {
-               // default
-               $protocol = 'http://';
-       }
-       if ( !in_array( $protocol, $protocols_list ) ) {
-               // unsupported protocol, show original search request
-               $target2 = $target;
-               $protocol = '';
-       }
-
-       $self = Title::makeTitle( NS_SPECIAL, 'Linksearch' );
-
-       $wgOut->addWikiMsg( 'linksearch-text', '<nowiki>' . $wgLang->commaList( $wgUrlProtocols ) . '</nowiki>' );
-       $s = Xml::openElement( 'form', array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => $GLOBALS['wgScript'] ) ) .
-               Html::hidden( 'title', $self->getPrefixedDbKey() ) .
-               '<fieldset>' .
-               Xml::element( 'legend', array(), wfMsg( 'linksearch' ) ) .
-               Xml::inputLabel( wfMsg( 'linksearch-pat' ), 'target', 'target', 50, $target ) . ' ';
-       if ( !$wgMiserMode ) {
-               $s .= Xml::label( wfMsg( 'linksearch-ns' ), 'namespace' ) . ' ' .
-                       Xml::namespaceSelector( $namespace, '' );
-       }
-       $s .=   Xml::submitButton( wfMsg( 'linksearch-ok' ) ) .
-               '</fieldset>' .
-               Xml::closeElement( 'form' );
-       $wgOut->addHTML( $s );
-
-       if( $target != '' ) {
-               $searcher = new LinkSearchPage;
-               $searcher->setParams( array( 
-                       'query' => $target2, 
-                       'namespace' => $namespace, 
-                       'protocol' => $protocol ) );
-               $searcher->doQuery( $offset, $limit );
-       }
-}
-
-/**
  * @ingroup SpecialPage
  */
 class LinkSearchPage extends QueryPage {
@@ -97,8 +34,68 @@ class LinkSearchPage extends QueryPage {
                $this->mProt = $params['protocol'];
        }
 
-       function getName() {
-               return 'LinkSearch';
+       function __construct( $name = 'LinkSearch' ) {
+               parent::__construct( $name );
+       }
+       
+       function execute( $par ) {
+               global $wgOut, $wgRequest, $wgUrlProtocols, $wgMiserMode, $wgLang, $wgScript;
+               $target = $wgRequest->getVal( 'target', $par );
+               $namespace = $wgRequest->getIntorNull( 'namespace', null );
+
+               $protocols_list[] = '';
+               foreach( $wgUrlProtocols as $prot ) {
+                       $protocols_list[] = $prot;
+               }
+
+               $target2 = $target;
+               $protocol = '';
+               $pr_sl = strpos($target2, '//' );
+               $pr_cl = strpos($target2, ':' );
+               if ( $pr_sl ) {
+                       // For protocols with '//'
+                       $protocol = substr( $target2, 0 , $pr_sl+2 );
+                       $target2 = substr( $target2, $pr_sl+2 );
+               } elseif ( !$pr_sl && $pr_cl ) {
+                       // For protocols without '//' like 'mailto:'
+                       $protocol = substr( $target2, 0 , $pr_cl+1 );
+                       $target2 = substr( $target2, $pr_cl+1 );
+               } elseif ( $protocol == '' && $target2 != '' ) {
+                       // default
+                       $protocol = 'http://';
+               }
+               if ( !in_array( $protocol, $protocols_list ) ) {
+                       // unsupported protocol, show original search request
+                       $target2 = $target;
+                       $protocol = '';
+               }
+
+               $self = $this->getTitle();
+
+               $wgOut->addWikiMsg( 'linksearch-text', '<nowiki>' . $wgLang->commaList( $wgUrlProtocols ) . '</nowiki>' );
+               $s = Xml::openElement( 'form', array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => $GLOBALS['wgScript'] ) ) .
+                       Html::hidden( 'title', $self->getPrefixedDbKey() ) .
+                       '<fieldset>' .
+                       Xml::element( 'legend', array(), wfMsg( 'linksearch' ) ) .
+                       Xml::inputLabel( wfMsg( 'linksearch-pat' ), 'target', 'target', 50, $target ) . ' ';
+               if ( !$wgMiserMode ) {
+                       $s .= Xml::label( wfMsg( 'linksearch-ns' ), 'namespace' ) . ' ' .
+                               Xml::namespaceSelector( $namespace, '' );
+               }
+               $s .=   Xml::submitButton( wfMsg( 'linksearch-ok' ) ) .
+                       '</fieldset>' .
+                       Xml::closeElement( 'form' );
+               $wgOut->addHTML( $s );
+
+               if( $target != '' ) {
+                       $this->setParams( array( 
+                               'query' => $target2,
+                               'namespace' => $namespace,
+                               'protocol' => $protocol ) );
+                       parent::execute( $par );
+                       if( $this->mMungedQuery === false )
+                               $wgOut->addWikiText( wfMsg( 'linksearch-error' ) );
+               }
        }
 
        /**
@@ -113,11 +110,11 @@ class LinkSearchPage extends QueryPage {
         */
        static function mungeQuery( $query , $prot ) {
                $field = 'el_index';
-               $rv = LinkFilter::makeLikeArray( $query , $prot );
-               if ($rv === false) {
+               $rv = LinkFilter::makeLike( $query , $prot );
+               if ( $rv === false ) {
                        // LinkFilter doesn't handle wildcard in IP, so we'll have to munge here.
                        if (preg_match('/^(:?[0-9]{1,3}\.)+\*\s*$|^(:?[0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]*\*\s*$/', $query)) {
-                               $rv = array( $prot . rtrim($query, " \t*"), $dbr->anyString() );
+                               $rv = $prot . rtrim($query, " \t*") . '%';
                                $field = 'el_to';
                        }
                }
@@ -134,35 +131,32 @@ class LinkSearchPage extends QueryPage {
                return $params;
        }
 
-       function getSQL() {
+       function getQueryInfo() {
                global $wgMiserMode;
                $dbr = wfGetDB( DB_SLAVE );
-               $page = $dbr->tableName( 'page' );
-               $externallinks = $dbr->tableName( 'externallinks' );
-
-               /* strip everything past first wildcard, so that index-based-only lookup would be done */
-               list( $munged, $clause ) = self::mungeQuery( $this->mQuery, $this->mProt );
-               $stripped = LinkFilter::keepOneWildcard( $munged );
-               $like = $dbr->buildLike( $stripped );
-
-               $encSQL = '';
-               if ( isset ($this->mNs) && !$wgMiserMode )
-                       $encSQL = 'AND page_namespace=' . $dbr->addQuotes( $this->mNs );
-
-               $use_index = $dbr->useIndexClause( $clause );
-               return
-                       "SELECT
-                               page_namespace AS namespace,
-                               page_title AS title,
-                               el_index AS value,
-                               el_to AS url
-                       FROM
-                               $page,
-                               $externallinks $use_index
-                       WHERE
-                               page_id=el_from
-                               AND $clause $like
-                               $encSQL";
+               // strip everything past first wildcard, so that
+               // index-based-only lookup would be done
+               list( $this->mMungedQuery, $clause ) = self::mungeQuery(
+                               $this->mQuery, $this->mProt );
+               if( $this->mMungedQuery === false )
+                       // Invalid query; return no results
+                       return array( 'tables' => 'page', 'fields' => 'page_id', 'conds' => '0=1' );
+               
+               $stripped = substr( $this->mMungedQuery, 0, strpos( $this->mMungedQuery, '%' ) + 1 );
+               $encSearch = $dbr->addQuotes( $stripped );
+               $retval = array (
+                       'tables' => array ( 'page', 'externallinks' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'el_index AS value', 'el_to AS url' ),
+                       'conds' => array ( 'page_id = el_from',
+                                       "$clause LIKE $encSearch" ),
+                       'options' => array( 'USE INDEX' => $clause )
+               );
+               if ( isset( $this->mNs ) && !$wgMiserMode ) {
+                       $retval['conds']['page_namespace'] = $this->mNs;
+               }
+               return $retval;
        }
 
        function formatResult( $skin, $result ) {
@@ -196,7 +190,7 @@ class LinkSearchPage extends QueryPage {
         * it as good enough for optimizing sort. The implicit ordering
         * from the scan will usually do well enough for our needs.
         */
-       function getOrder() {
-               return '';
+       function getOrderFields() {
+               return array();
        }
 }
index bbea46d..638d2de 100644 (file)
@@ -101,7 +101,7 @@ class ImageListPager extends TablePager {
                $tables = array( 'image' );
                $fields = array_keys( $this->getFieldNames() );
                $fields[] = 'img_user';
-               $fields[array_search('thumb', $fields)] = 'img_name as thumb';
+               $fields[array_search('thumb', $fields)] = 'img_name AS thumb';
                $options = $join_conds = array();
 
                # Depends on $wgMiserMode
@@ -111,7 +111,7 @@ class ImageListPager extends TablePager {
                        # Need to rewrite this one
                        foreach ( $fields as &$field ) {
                                if ( $field == 'count' ) {
-                                       $field = 'COUNT(oi_archive_name) as count';
+                                       $field = 'COUNT(oi_archive_name) AS count';
                                }
                        }
                        unset( $field );
index 315047d..8504662 100644 (file)
  */
 class ListredirectsPage extends QueryPage {
 
-       function getName() { return( 'Listredirects' ); }
-       function isExpensive() { return( true ); }
-       function isSyndicated() { return( false ); }
-       function sortDescending() { return( false ); }
+       function __construct( $name = 'Listredirects' ) {
+               parent::__construct( $name );
+       }
+       
+       function isExpensive() { return true; }
+       function isSyndicated() { return false; }
+       function sortDescending() { return false; }
+
+       function getQueryInfo() {
+               return array(
+                       'tables' => array( 'p1' => 'page', 'redirect', 'p2' => 'page' ),
+                       'fields' => array( 'p1.page_namespace AS namespace',
+                                       'p1.page_title AS title',
+                                       'rd_namespace',
+                                       'rd_title',
+                                       'p2.page_id AS redirid' ),
+                       'conds' => array( 'p1.page_is_redirect' => 1 ),
+                       'join_conds' => array( 'redirect' => array(
+                                       'LEFT JOIN', 'rd_from=p1.page_id' ),
+                               'p2' => array( 'LEFT JOIN', array(
+                                       'p2.page_namespace=rd_namespace',
+                                       'p2.page_title=rd_title' ) ) )
+               );
+       }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $page = $dbr->tableName( 'page' );
-               $sql = "SELECT 'Listredirects' AS type, page_title AS title, page_namespace AS namespace, 
-                       0 AS value FROM $page WHERE page_is_redirect = 1";
-               return( $sql );
+       function getOrderFields() {
+               return array ( 'p1.page_namespace', 'p1.page_title' );
        }
 
        function formatResult( $skin, $result ) {
@@ -72,9 +88,3 @@ class ListredirectsPage extends QueryPage {
                }
        }
 }
-
-function wfSpecialListredirects() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $lrp = new ListredirectsPage();
-       $lrp->doQuery( $offset, $limit );
-}
index 0788037..a4d0ec5 100644 (file)
  */
 class LonelyPagesPage extends PageQueryPage {
 
-       function getName() {
-               return "Lonelypages";
+       function __construct( $name = 'Lonelypages' ) {
+               parent::__construct( $name );
        }
+       
        function getPageHeader() {
                return wfMsgExt( 'lonelypagestext', array( 'parse' ) );
        }
@@ -45,35 +46,36 @@ class LonelyPagesPage extends PageQueryPage {
        }
        function isSyndicated() { return false; }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $pagelinks, $templatelinks ) = $dbr->tableNamesN( 'page', 'pagelinks', 'templatelinks' );
-
-               return
-                 "SELECT 'Lonelypages'  AS type,
-                         page_namespace AS namespace,
-                         page_title     AS title,
-                         page_title     AS value
-                    FROM $page
-               LEFT JOIN $pagelinks
-                      ON page_namespace=pl_namespace AND page_title=pl_title
-               LEFT JOIN $templatelinks
-                               ON page_namespace=tl_namespace AND page_title=tl_title
-                   WHERE pl_namespace IS NULL
-                     AND page_namespace=".NS_MAIN."
-                     AND page_is_redirect=0
-                         AND tl_namespace IS NULL";
-
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'page', 'pagelinks',
+                                       'templatelinks' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_title AS value' ),
+                       'conds' => array ( 'pl_namespace IS NULL',
+                                       'page_namespace' => MWNamespace::getContentNamespaces(),
+                                       'page_is_redirect' => 0,
+                                       'tl_namespace IS NULL' ),
+                       'join_conds' => array (
+                                       'pagelinks' => array (
+                                               'LEFT JOIN', array (
+                                               'pl_namespace = page_namespace',
+                                               'pl_title = page_title' ) ),
+                                       'templatelinks' => array (
+                                               'LEFT JOIN', array (
+                                               'tl_namespace = page_namespace',
+                                               'tl_title = page_title' ) ) )
+               );
+       }
+       
+       function getOrderFields() {
+               // For some crazy reason ordering by a constant
+               // causes a filesort in MySQL 5
+               if( count( MWNamespace::getContentNamespaces() ) > 1 ) {
+                       return array( 'page_namespace', 'page_title' );
+               } else {
+                       return array( 'page_title' );
+               }
        }
-}
-
-/**
- * Constructor
- */
-function wfSpecialLonelypages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $lpp = new LonelyPagesPage();
-
-       return $lpp->doQuery( $offset, $limit );
 }
index cd0f309..dd60e37 100644 (file)
  */
 class LongPagesPage extends ShortPagesPage {
 
-       function getName() {
-               return "Longpages";
+       function __construct( $name = 'Longpages' ) {
+               parent::__construct( $name );
        }
 
        function sortDescending() {
                return true;
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialLongpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $lpp = new LongPagesPage();
-
-       $lpp->doQuery( $offset, $limit );
-}
index 79683a3..c9e8898 100644 (file)
  * @ingroup SpecialPage
  */
 class MIMEsearchPage extends QueryPage {
-       var $major, $minor;
+       protected $major, $minor;
 
-       function __construct( $major, $minor ) {
-               $this->major = $major;
-               $this->minor = $minor;
+       function __construct( $name = 'MIMEsearch' ) {
+               parent::__construct( $name );
        }
 
-       function getName() { return 'MIMEsearch'; }
-
-       /**
-        * Due to this page relying upon extra fields being passed in the SELECT it
-        * will fail if it's set as expensive and misermode is on
-        */
        function isExpensive() { return true; }
        function isSyndicated() { return false; }
+       function isCacheable() { return false; }
 
        function linkParameters() {
-               $arr = array( $this->major, $this->minor );
-               $mime = implode( '/', $arr );
-               return array( 'mime' => $mime );
+               return array( 'mime' => "{$this->major}/{$this->minor}" );
        }
+       
+       public function getQueryInfo() {
+               return array(
+                       'tables' => array( 'image' ),
+                       'fields' => array( "'" . NS_FILE . "' AS namespace",
+                                       'img_name AS title',
+                                       'img_major_mime AS value',
+                                       'img_size',
+                                       'img_width',
+                                       'img_height',
+                                       'img_user_text',
+                                       'img_timestamp' ),
+                       'conds' => array( 'img_major_mime' => $this->major,
+                                       'img_minor_mime' => $this->minor )
+               );
+       }
+       
+       function execute( $par ) {
+               global $wgRequest, $wgOut;
+               $mime = $par ? $par : $wgRequest->getText( 'mime' );
+               
+               $this->setHeaders();
+               $this->outputHeader();
+               $wgOut->addHTML(
+                       Xml::openElement( 'form', array( 'id' => 'specialmimesearch', 'method' => 'get', 'action' => SpecialPage::getTitleFor( 'MIMEsearch' )->getLocalUrl() ) ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Html::hidden( 'title', SpecialPage::getTitleFor( 'MIMEsearch' )->getPrefixedText() ) .
+                       Xml::element( 'legend', null, wfMsg( 'mimesearch' ) ) .
+                       Xml::inputLabel( wfMsg( 'mimetype' ), 'mime', 'mime', 20, $mime ) . ' ' .
+                       Xml::submitButton( wfMsg( 'ilsubmit' ) ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' )
+               );
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $image = $dbr->tableName( 'image' );
-               $major = $dbr->addQuotes( $this->major );
-               $minor = $dbr->addQuotes( $this->minor );
-
-               return
-                       "SELECT 'MIMEsearch' AS type,
-                               " . NS_FILE . " AS namespace,
-                               img_name AS title,
-                               img_major_mime AS value,
-
-                               img_size,
-                               img_width,
-                               img_height,
-                               img_user_text,
-                               img_timestamp
-                       FROM $image
-                       WHERE img_major_mime = $major AND img_minor_mime = $minor
-                       ";
+               list( $this->major, $this->minor ) = self::parseMIME( $mime );
+               if ( $this->major == '' || $this->minor == '' || !self::isValidType( $this->major ) ) {
+                       return;
+               }
+               parent::execute( $par );
        }
+               
 
        function formatResult( $skin, $result ) {
                global $wgContLang, $wgLang;
@@ -83,7 +94,7 @@ class MIMEsearchPage extends QueryPage {
                );
 
                $download = $skin->makeMediaLinkObj( $nt, wfMsgHtml( 'download' ) );
-               $bytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape'),
+               $bytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
                        $wgLang->formatNum( $result->img_size ) );
                $dimensions = htmlspecialchars( wfMsg( 'widthheight',
                        $wgLang->formatNum( $result->img_width ),
@@ -94,63 +105,34 @@ class MIMEsearchPage extends QueryPage {
 
                return "($download) $plink . . $dimensions . . $bytes . . $user . . $time";
        }
-}
-
-/**
- * Output the HTML search form, and constructs the MIMEsearchPage object.
- */
-function wfSpecialMIMEsearch( $par = null ) {
-       global $wgRequest, $wgOut;
-
-       $mime = isset( $par ) ? $par : $wgRequest->getText( 'mime' );
-
-       $wgOut->addHTML(
-               Xml::openElement( 'form', array( 'id' => 'specialmimesearch', 'method' => 'get', 'action' => SpecialPage::getTitleFor( 'MIMEsearch' )->getLocalUrl() ) ) .
-               Xml::openElement( 'fieldset' ) .
-               Html::hidden( 'title', SpecialPage::getTitleFor( 'MIMEsearch' )->getPrefixedText() ) .
-               Xml::element( 'legend', null, wfMsg( 'mimesearch' ) ) .
-               Xml::inputLabel( wfMsg( 'mimetype' ), 'mime', 'mime', 20, $mime ) . ' ' .
-               Xml::submitButton( wfMsg( 'ilsubmit' ) ) .
-               Xml::closeElement( 'fieldset' ) .
-               Xml::closeElement( 'form' )
-       );
-
-       list( $major, $minor ) = wfSpecialMIMEsearchParse( $mime );
-       if ( $major == '' or $minor == '' or !wfSpecialMIMEsearchValidType( $major ) )
-               return;
-       $wpp = new MIMEsearchPage( $major, $minor );
-
-       list( $limit, $offset ) = wfCheckLimits();
-       $wpp->doQuery( $offset, $limit );
-}
-
-function wfSpecialMIMEsearchParse( $str ) {
-       // searched for an invalid MIME type.
-       if( strpos( $str, '/' ) === false) {
-               return array ('', '');
+       
+       protected static function parseMIME( $str ) {
+               // searched for an invalid MIME type.
+               if( strpos( $str, '/' ) === false ) {
+                       return array( '', '' );
+               }
+
+               list( $major, $minor ) = explode( '/', $str, 2 );
+
+               return array(
+                       ltrim( $major, ' ' ),
+                       rtrim( $minor, ' ' )
+               );
+       }
+       
+       protected static function isValidType( $type ) {
+               // From maintenance/tables.sql => img_major_mime
+               $types = array(
+                       'unknown',
+                       'application',
+                       'audio',
+                       'image',
+                       'text',
+                       'video',
+                       'message',
+                       'model',
+                       'multipart'
+               );
+               return in_array( $type, $types );
        }
-
-       list( $major, $minor ) = explode( '/', $str, 2 );
-
-       return array(
-               ltrim( $major, ' ' ),
-               rtrim( $minor, ' ' )
-       );
-}
-
-function wfSpecialMIMEsearchValidType( $type ) {
-       // From maintenance/tables.sql => img_major_mime
-       $types = array(
-               'unknown',
-               'application',
-               'audio',
-               'image',
-               'text',
-               'video',
-               'message',
-               'model',
-               'multipart'
-       );
-
-       return in_array( $type, $types );
 }
index 124f0bd..41279e3 100644 (file)
  */
 class MostcategoriesPage extends QueryPage {
 
-       function getName() { return 'Mostcategories'; }
+       function __construct( $name = 'Mostcategories' ) {
+               parent::__construct( $name );
+       }
+       
        function isExpensive() { return true; }
        function isSyndicated() { return false; }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $categorylinks, $page) = $dbr->tableNamesN( 'categorylinks', 'page' );
-               return
-                       "
-                       SELECT
-                               'Mostcategories' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               COUNT(*) as value
-                       FROM $categorylinks
-                       LEFT JOIN $page ON cl_from = page_id
-                       WHERE page_namespace = " . NS_MAIN . "
-                       GROUP BY page_namespace, page_title
-                       HAVING COUNT(*) > 1
-                       ";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'categorylinks', 'page' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'COUNT(*) AS value' ),
+                       'conds' => array ( 'page_namespace' => MWNamespace::getContentNamespaces() ),
+                       'options' => array ( 'HAVING' => 'COUNT(*) > 1',
+                               'GROUP BY' => 'page_namespace, page_title' ),
+                       'join_conds' => array ( 'page' => array ( 'LEFT JOIN',
+                                       'page_id = cl_from' ) )
+               );
        }
 
        function formatResult( $skin, $result ) {
@@ -62,14 +61,3 @@ class MostcategoriesPage extends QueryPage {
                return wfSpecialList( $link, $count );
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialMostcategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostcategoriesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
index 411a281..2fdbd1b 100644 (file)
  */
 class MostimagesPage extends ImageQueryPage {
 
-       function getName() { return 'Mostimages'; }
+       function __construct( $name = 'Mostimages' ) {
+               parent::__construct( $name );
+       }
+       
        function isExpensive() { return true; }
        function isSyndicated() { return false; }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $imagelinks = $dbr->tableName( 'imagelinks' );
-               return
-                       "
-                       SELECT
-                               'Mostimages' as type,
-                               " . NS_FILE . " as namespace,
-                               il_to as title,
-                               COUNT(*) as value
-                       FROM $imagelinks
-                       GROUP BY il_to
-                       HAVING COUNT(*) > 1
-                       ";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'imagelinks' ),
+                       'fields' => array ( "'" . NS_FILE . "' AS namespace",
+                                       'il_to AS title',
+                                       'COUNT(*) AS value' ),
+                       'options' => array ( 'GROUP BY' => 'il_to',
+                                       'HAVING' => 'COUNT(*) > 1' )
+               );
        }
 
        function getCellHtml( $row ) {
@@ -58,14 +56,3 @@ class MostimagesPage extends ImageQueryPage {
        }
 
 }
-
-/**
- * Constructor
- */
-function wfSpecialMostimages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostimagesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
index c731588..aaa07d7 100644 (file)
  */
 class MostlinkedPage extends QueryPage {
 
-       function getName() { return 'Mostlinked'; }
+       function __construct( $name = 'Mostlinked' ) {
+               parent::__construct( $name );
+       }
+       
        function isExpensive() { return true; }
        function isSyndicated() { return false; }
 
-       function getSQL() {
-               global $wgMiserMode;
-
-               $dbr = wfGetDB( DB_SLAVE );
-
-               # In miser mode, reduce the query cost by adding a threshold for large wikis
-               if ( $wgMiserMode ) {
-                       $numPages = SiteStats::pages();
-                       if ( $numPages > 10000 ) {
-                               $cutoff = 100;
-                       } elseif ( $numPages > 100 ) {
-                               $cutoff = intval( sqrt( $numPages ) );
-                       } else {
-                               $cutoff = 1;
-                       }
-               } else {
-                       $cutoff = 1;
-               }
-
-               list( $pagelinks, $page ) = $dbr->tableNamesN( 'pagelinks', 'page' );
-               return
-                       "SELECT 'Mostlinked' AS type,
-                               pl_namespace AS namespace,
-                               pl_title AS title,
-                               COUNT(*) AS value
-                       FROM $pagelinks
-                       LEFT JOIN $page ON pl_namespace=page_namespace AND pl_title=page_title
-                       GROUP BY pl_namespace, pl_title
-                       HAVING COUNT(*) > $cutoff";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'pagelinks', 'page' ),
+                       'fields' => array ( 'pl_namespace AS namespace',
+                                       'pl_title AS title',
+                                       'COUNT(*) AS value',
+                                       'page_namespace' ),
+                       'options' => array ( 'HAVING' => 'COUNT(*) > 1',
+                               'GROUP BY' => 'pl_namespace, pl_title, '.
+                                               'page_namespace' ),
+                       'join_conds' => array ( 'page' => array ( 'LEFT JOIN',
+                                       array ( 'page_namespace = pl_namespace',
+                                               'page_title = pl_title' ) ) )
+               );
        }
 
        /**
@@ -114,14 +102,3 @@ class MostlinkedPage extends QueryPage {
                return wfSpecialList( $link, $wlh );
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialMostlinked() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostlinkedPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
index e1fc1d9..75ecd93 100644 (file)
  */
 class MostlinkedCategoriesPage extends QueryPage {
 
-       function getName() { return 'Mostlinkedcategories'; }
+       function __construct( $name = 'Mostlinkedcategories' ) {
+               parent::__construct( $name );
+       }
+       
        function isExpensive() { return true; }
        function isSyndicated() { return false; }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $categorylinks = $dbr->tableName( 'categorylinks' );
-               $name = $dbr->addQuotes( $this->getName() );
-               return
-                       "
-                       SELECT
-                               $name as type,
-                               " . NS_CATEGORY . " as namespace,
-                               cl_to as title,
-                               COUNT(*) as value
-                       FROM $categorylinks
-                       GROUP BY cl_to
-                       ";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'categorylinks' ),
+                       'fields' => array ( 'cl_to AS title',
+                                       'COUNT(*) AS value' ),
+                       'options' => array ( 'GROUP BY' => 'cl_to' )
+               );
        }
 
        function sortDescending() { return true; }
@@ -59,37 +55,27 @@ class MostlinkedCategoriesPage extends QueryPage {
        function preprocessResults( $db, $res ) {
                $batch = new LinkBatch;
                foreach ( $res as $row ) {
-                       $batch->add( $row->namespace, $row->title );
+                       $batch->add( NS_CATEGORY, $row->title );
                }
                $batch->execute();
 
                // Back to start for display
-               if ( $db->numRows( $res ) > 0 )
+               if ( $db->numRows( $res ) > 0 ) {
                        // If there are no rows we get an error seeking.
                        $db->dataSeek( $res, 0 );
+               }
        }
 
        function formatResult( $skin, $result ) {
                global $wgLang, $wgContLang;
 
-               $nt = Title::makeTitle( $result->namespace, $result->title );
+               $nt = Title::makeTitle( NS_CATEGORY, $result->title );
                $text = $wgContLang->convert( $nt->getText() );
 
                $plink = $skin->link( $nt, htmlspecialchars( $text ) );
 
-               $nlinks = wfMsgExt( 'nmembers', array( 'parsemag', 'escape'),
+               $nlinks = wfMsgExt( 'nmembers', array( 'parsemag', 'escape' ),
                        $wgLang->formatNum( $result->value ) );
-               return wfSpecialList($plink, $nlinks);
+               return wfSpecialList( $plink, $nlinks );
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialMostlinkedCategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostlinkedCategoriesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
index 822d6bc..78f806f 100644 (file)
  *
  * @ingroup SpecialPage
  */
-class SpecialMostlinkedtemplates extends QueryPage {
+class MostlinkedTemplatesPage extends QueryPage {
 
-       /**
-        * Name of the report
-        *
-        * @return String
-        */
-       public function getName() {
-               return 'Mostlinkedtemplates';
+       function __construct( $name = 'Mostlinkedtemplates' ) {
+               parent::__construct( $name );
        }
 
        /**
@@ -66,22 +61,15 @@ class SpecialMostlinkedtemplates extends QueryPage {
                return true;
        }
 
-       /**
-        * Generate SQL for the report
-        *
-        * @return String
-        */
-       public function getSql() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $templatelinks = $dbr->tableName( 'templatelinks' );
-               $name = $dbr->addQuotes( $this->getName() );
-               return "SELECT {$name} AS type,
-                       " . NS_TEMPLATE . " AS namespace,
-                       tl_title AS title,
-                       COUNT(*) AS value
-                       FROM {$templatelinks}
-                       WHERE tl_namespace = " . NS_TEMPLATE . "
-                       GROUP BY tl_title";
+       public function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'templatelinks' ),
+                       'fields' => array ( 'tl_namespace AS namespace',
+                                       'tl_title AS title',
+                                       'COUNT(*) AS value' ),
+                       'conds' => array ( 'tl_namespace' => NS_TEMPLATE ),
+                       'options' => array( 'GROUP BY' => 'tl_title' )
+               );
        }
 
        /**
@@ -108,7 +96,7 @@ class SpecialMostlinkedtemplates extends QueryPage {
         * @return String
         */
        public function formatResult( $skin, $result ) {
-               $title = Title::makeTitleSafe( $result->namespace, $result->title );
+               $title = Title::makeTitle( $result->namespace, $result->title );
 
                return wfSpecialList(
                        $skin->link( $title ),
@@ -133,13 +121,3 @@ class SpecialMostlinkedtemplates extends QueryPage {
        }
 }
 
-/**
- * Execution function
- *
- * @param $par Mixed: parameters passed to the page
- */
-function wfSpecialMostlinkedtemplates( $par = false ) {
-       list( $limit, $offset ) = wfCheckLimits();
-       $mlt = new SpecialMostlinkedtemplates();
-       $mlt->doQuery( $offset, $limit );
-}
index f9bafab..6c30e54 100644 (file)
  * @ingroup SpecialPage
  * @author Ã†var Arnfjörð Bjarmason <avarab@gmail.com>
  */
-
-/**
- * A special page to show pages with highest revision count
- *
- * @ingroup SpecialPage
- */
-class MostrevisionsPage extends QueryPage {
-
-       function getName() { return 'Mostrevisions'; }
-       function isExpensive() { return true; }
-       function isSyndicated() { return false; }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $revision, $page ) = $dbr->tableNamesN( 'revision', 'page' );
-               return
-                       "
-                       SELECT
-                               'Mostrevisions' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               COUNT(*) as value
-                       FROM $revision
-                       JOIN $page ON page_id = rev_page
-                       WHERE page_namespace = " . NS_MAIN . "
-                       GROUP BY page_namespace, page_title
-                       HAVING COUNT(*) > 1
-                       ";
+class MostrevisionsPage extends FewestrevisionsPage {
+       function __construct( $name = 'Mostrevisions' ) {
+               parent::__construct( $name );
        }
-
-       function formatResult( $skin, $result ) {
-               global $wgLang, $wgContLang;
-
-               $nt = Title::makeTitle( $result->namespace, $result->title );
-               $text = $wgContLang->convert( $nt->getPrefixedText() );
-
-               $plink = $skin->linkKnown( $nt, $text );
-
-               $nl = wfMsgExt( 'nrevisions', array( 'parsemag', 'escape'),
-                       $wgLang->formatNum( $result->value ) );
-               $nlink = $skin->linkKnown(
-                       $nt,
-                       $nl,
-                       array(),
-                       array( 'action' => 'history' )
-               );
-
-               return wfSpecialList($plink, $nlink);
+       
+       function sortDescending() {
+               return true;
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialMostrevisions() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new MostrevisionsPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
index 498fa23..5142d04 100644 (file)
@@ -477,8 +477,8 @@ class NewPagesPager extends ReverseChronologicalPager {
 
                $info = array(
                        'tables' => array( 'recentchanges', 'page' ),
-                       'fields' => 'rc_namespace,rc_title, rc_cur_id, rc_user,rc_user_text,rc_comment,
-                               rc_timestamp,rc_patrolled,rc_id,page_len as length, page_latest as rev_id, ts_tags',
+                       'fields' => array( 'rc_namespace', 'rc_title', 'rc_cur_id', 'rc_user', 'rc_user_text', 'rc_comment',
+                               'rc_timestamp', 'rc_patrolled', 'rc_id', 'page_len AS length', 'page_latest AS rev_id', 'ts_tags'),
                        'conds' => $conds,
                        'options' => array( 'USE INDEX' => array('recentchanges' => $rcIndexes) ),
                        'join_conds' => array(
index 375cefd..1ef204f 100644 (file)
@@ -28,8 +28,8 @@
  */
 class PopularPagesPage extends QueryPage {
 
-       function getName() {
-               return "Popularpages";
+       function __construct( $name = 'Popularpages' ) {
+               parent::__construct( $name );
        }
 
        function isExpensive() {
@@ -38,29 +38,14 @@ class PopularPagesPage extends QueryPage {
        }
        function isSyndicated() { return false; }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $page = $dbr->tableName( 'page' );
-
-               $query =
-                       "SELECT 'Popularpages' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               page_counter as value
-                       FROM $page ";
-               $where =
-                       "WHERE page_is_redirect=0 AND page_namespace";
-
-               global $wgContentNamespaces;
-               if( empty( $wgContentNamespaces ) ) {
-                       $where .= '='.NS_MAIN;
-               } else if( count( $wgContentNamespaces ) > 1 ) {
-                       $where .= ' in (' . implode( ', ', $wgContentNamespaces ) . ')';
-               } else {
-                       $where .= '='.$wgContentNamespaces[0];
-               }
-
-               return $query . $where;
+       function getQueryInfo() {
+               return array (
+                       'tables' => array( 'page' ),
+                       'fields' => array( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_counter AS value'),
+                       'conds' => array( 'page_is_redirect' => 0,
+                                       'page_namespace' => MWNamespace::getContentNamespaces() ) );
        }
 
        function formatResult( $skin, $result ) {
@@ -78,14 +63,3 @@ class PopularPagesPage extends QueryPage {
                return wfSpecialList($link, $nv);
        }
 }
-
-/**
- * Constructor
- */
-function wfSpecialPopularpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $ppp = new PopularPagesPage();
-
-       return $ppp->doQuery( $offset, $limit );
-}
index 989e4c0..c53ab6a 100644 (file)
  */
 class ShortPagesPage extends QueryPage {
 
-       function getName() {
-               return 'Shortpages';
+       function __construct( $name = 'Shortpages' ) {
+               parent::__construct( $name );
        }
 
+       // inexpensive?
        /**
         * This query is indexed as of 1.5
         */
@@ -44,27 +45,20 @@ class ShortPagesPage extends QueryPage {
                return false;
        }
 
-       function getSQL() {
-               global $wgContentNamespaces;
-
-               $dbr = wfGetDB( DB_SLAVE );
-               $page = $dbr->tableName( 'page' );
-               $name = $dbr->addQuotes( $this->getName() );
-
-               $forceindex = $dbr->useIndexClause("page_len");
-
-               if ($wgContentNamespaces)
-                       $nsclause = "page_namespace IN (" . $dbr->makeList($wgContentNamespaces) . ")";
-               else
-                       $nsclause = "page_namespace = " . NS_MAIN;
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'page' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_len AS value' ),
+                       'conds' => array ( 'page_namespace' => MWNamespace::getContentNamespaces(),
+                                       'page_is_redirect' => 0 ),
+                       'options' => array ( 'USE INDEX' => 'page_len' )
+               );
+       }
 
-               return
-                       "SELECT $name as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               page_len AS value
-                       FROM $page $forceindex
-                       WHERE $nsclause AND page_is_redirect=0";
+       function getOrderFields() {
+               return array( 'page_len' );
        }
 
        function preprocessResults( $db, $res ) {
@@ -90,7 +84,7 @@ class ShortPagesPage extends QueryPage {
                global $wgLang, $wgContLang;
                $dm = $wgContLang->getDirMark();
 
-               $title = Title::makeTitleSafe( $result->namespace, $result->title );
+               $title = Title::makeTitle( $result->namespace, $result->title );
                if ( !$title ) {
                        return '<!-- Invalid title ' .  htmlspecialchars( "{$result->namespace}:{$result->title}" ). '-->';
                }
@@ -110,14 +104,3 @@ class ShortPagesPage extends QueryPage {
                                : "<del>({$hlink}) {$dm}{$plink} {$dm}[{$size}]</del>";
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialShortpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $spp = new ShortPagesPage();
-
-       return $spp->doQuery( $offset, $limit );
-}
index c2aecf4..164565c 100644 (file)
@@ -48,7 +48,8 @@ class SpecialTags extends SpecialPage {
                                Xml::tags( 'th', null, wfMsgExt( 'tags-hitcount-header', 'parseinline' ) )
                        );
                $dbr = wfGetDB( DB_SLAVE );
-               $res = $dbr->select( 'change_tag', array( 'ct_tag', 'count(*) as hitcount' ), array(), __METHOD__, array( 'GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC' ) );
+               $res = $dbr->select( 'change_tag', array( 'ct_tag', 'count(*) AS hitcount' ),
+                       array(), __METHOD__, array( 'GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC' ) );
 
                foreach ( $res as $row ) {
                        $html .= $this->doTagRow( $row->ct_tag, $row->hitcount );
index 9574af7..70d98df 100644 (file)
  * @ingroup SpecialPage
  */
 class UncategorizedCategoriesPage extends UncategorizedPagesPage {
-       function __construct() {
+       function __construct( $name = 'Uncategorizedcategories' ) {
+               parent::__construct( $name );
                $this->requestedNamespace = NS_CATEGORY;
        }
-
-       function getName() {
-               return "Uncategorizedcategories";
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialUncategorizedcategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $lpp = new UncategorizedCategoriesPage();
-
-       return $lpp->doQuery( $offset, $limit );
 }
index c425403..d6a7fef 100644 (file)
  *
  * @ingroup SpecialPage
  */
+// FIXME: Use an instance of UncategorizedPagesPage or something
 class UncategorizedImagesPage extends ImageQueryPage {
 
-       function getName() {
-               return 'Uncategorizedimages';
+       function __construct( $name = 'Uncategorizedimages' ) {
+               parent::__construct( $name );
        }
 
        function sortDescending() {
@@ -44,22 +45,19 @@ class UncategorizedImagesPage extends ImageQueryPage {
        function isSyndicated() {
                return false;
        }
-
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
-               $ns = NS_FILE;
-
-               return "SELECT 'Uncategorizedimages' AS type, page_namespace AS namespace,
-                               page_title AS title, page_title AS value
-                               FROM {$page} LEFT JOIN {$categorylinks} ON page_id = cl_from
-                               WHERE cl_from IS NULL AND page_namespace = {$ns} AND page_is_redirect = 0";
+       
+       function getQueryInfo() {
+               return array (
+                       'tables' => array( 'page', 'categorylinks' ),
+                       'fields' => array( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_title AS value' ),
+                       'conds' => array( 'cl_from IS NULL',
+                                       'page_namespace' => NS_FILE,
+                                       'page_is_redirect' => 0 ),
+                       'join_conds' => array( 'categorylinks' => array(
+                                       'LEFT JOIN', 'cl_from=page_id' ) )
+               );
        }
 
 }
-
-function wfSpecialUncategorizedimages() {
-       $uip = new UncategorizedImagesPage();
-       list( $limit, $offset ) = wfCheckLimits();
-       return $uip->doQuery( $offset, $limit );
-}
index c7fef5d..26eb36a 100644 (file)
  *
  * @ingroup SpecialPage
  */
+// FIXME: Make $requestedNamespace selectable, unify all subclasses into one
 class UncategorizedPagesPage extends PageQueryPage {
-       var $requestedNamespace = NS_MAIN;
+       protected $requestedNamespace = false;
 
-       function getName() {
-               return "Uncategorizedpages";
+       function __construct( $name = 'Uncategorizedpages' ) {
+               parent::__construct( $name );
        }
 
        function sortDescending() {
@@ -42,32 +43,27 @@ class UncategorizedPagesPage extends PageQueryPage {
        }
        function isSyndicated() { return false; }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $categorylinks ) = $dbr->tableNamesN( 'page', 'categorylinks' );
-               $name = $dbr->addQuotes( $this->getName() );
-
-               return
-                       "
-                       SELECT
-                               $name as type,
-                               page_namespace AS namespace,
-                               page_title AS title,
-                               page_title AS value
-                       FROM $page
-                       LEFT JOIN $categorylinks ON page_id=cl_from
-                       WHERE cl_from IS NULL AND page_namespace={$this->requestedNamespace} AND page_is_redirect=0
-                       ";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'page', 'categorylinks' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_title AS value' ),
+                       // default for page_namespace is all content namespaces (if requestedNamespace is false)
+                       // otherwise, page_namespace is requestedNamespace
+                       'conds' => array ( 'cl_from IS NULL',
+                                       'page_namespace' => ( $this->requestedNamespace!==false ? $this->requestedNamespace : MWNamespace::getContentNamespaces() ),
+                                       'page_is_redirect' => 0 ),
+                       'join_conds' => array ( 'categorylinks' => array (
+                                       'LEFT JOIN', 'cl_from = page_id' ) )
+               );
+       }
+       
+       function getOrderFields() {
+               // For some crazy reason ordering by a constant
+               // causes a filesort
+               if( $this->requestedNamespace === false && count( MWNamespace::getContentNamespaces() ) > 1 )
+                       return array( 'page_namespace', 'page_title' );
+               return array( 'page_title' );
        }
-}
-
-/**
- * constructor
- */
-function wfSpecialUncategorizedpages() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $lpp = new UncategorizedPagesPage();
-
-       return $lpp->doQuery( $offset, $limit );
 }
index aa4e979..af038fa 100644 (file)
  * @ingroup SpecialPage
  */
 class UncategorizedTemplatesPage extends UncategorizedPagesPage {
-
-       var $requestedNamespace = NS_TEMPLATE;
-
-       public function getName() {
-               return 'Uncategorizedtemplates';
+       public function __construct( $name = 'Uncategorizedtemplates' ) {
+               parent::__construct( $name );
+               $this->requestedNamespace = NS_TEMPLATE;
        }
-
-}
-
-/**
- * Main execution point
- */
-function wfSpecialUncategorizedtemplates() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $utp = new UncategorizedTemplatesPage();
-       $utp->doQuery( $offset, $limit );
 }
index a20efe0..ac17b87 100644 (file)
@@ -28,25 +28,26 @@ class UnusedCategoriesPage extends QueryPage {
 
        function isExpensive() { return true; }
 
-       function getName() {
-               return 'Unusedcategories';
+       function __construct( $name = 'Unusedcategories' ) {
+               parent::__construct( $name );
        }
 
        function getPageHeader() {
                return wfMsgExt( 'unusedcategoriestext', array( 'parse' ) );
        }
 
-       function getSQL() {
-               $NScat = NS_CATEGORY;
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $categorylinks, $page ) = $dbr->tableNamesN( 'categorylinks', 'page' );
-               return "SELECT 'Unusedcategories' as type,
-                               {$NScat} as namespace, page_title as title, page_title as value
-                               FROM $page
-                               LEFT JOIN $categorylinks ON page_title=cl_to
-                               WHERE cl_from IS NULL
-                               AND page_namespace = {$NScat}
-                               AND page_is_redirect = 0";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'page', 'categorylinks' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_title AS value' ),
+                       'conds' => array ( 'cl_from IS NULL',
+                                       'page_namespace' => NS_CATEGORY,
+                                       'page_is_redirect' => 0 ),
+                       'join_conds' => array ( 'categorylinks' => array (
+                                       'LEFT JOIN', 'cl_to = page_title' ) )
+               );
        }
 
        function formatResult( $skin, $result ) {
@@ -54,10 +55,3 @@ class UnusedCategoriesPage extends QueryPage {
                return $skin->link( $title, $title->getText() );
        }
 }
-
-/** constructor */
-function wfSpecialUnusedCategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $uc = new UnusedCategoriesPage();
-       return $uc->doQuery( $offset, $limit );
-}
index 091ec3a..6c8f6c5 100644 (file)
  * @ingroup SpecialPage
  */
 class UnusedimagesPage extends ImageQueryPage {
-
-       function isExpensive() { return true; }
-
-       function getName() {
-               return 'Unusedimages';
+       function __construct( $name = 'Unusedimages' ) {
+               parent::__construct( $name );
        }
-
+       
+       function isExpensive() {
+               return true;
+       }
+       
        function sortDescending() {
                return false;
        }
-       function isSyndicated() { return false; }
+       
+       function isSyndicated() {
+               return false;
+       }
 
-       function getSQL() {
+       function getQueryInfo() {
                global $wgCountCategorizedImagesAsUsed;
-
-               $dbr = wfGetDB( DB_SLAVE );
-
-               $epoch = $dbr->unixTimestamp( 'img_timestamp' );
+               $retval = array (
+                       'tables' => array ( 'image', 'imagelinks' ),
+                       'fields' => array ( "'" . NS_FILE . "' AS namespace",
+                                       'img_name AS title',
+                                       'img_timestamp AS value',
+                                       'img_user', 'img_user_text',
+                                       'img_description' ),
+                       'conds' => array ( 'il_to IS NULL' ),
+                       'join_conds' => array ( 'imagelinks' => array (
+                                       'LEFT JOIN', 'il_to = img_name' ) )
+               );
 
                if ( $wgCountCategorizedImagesAsUsed ) {
-                       list( $page, $image, $imagelinks, $categorylinks ) = $dbr->tableNamesN( 'page', 'image', 'imagelinks', 'categorylinks' );
-
-                       return "SELECT 'Unusedimages' as type, 6 as namespace, img_name as title, $epoch as value,
-                                               img_user, img_user_text,  img_description
-                                       FROM ((($page AS I LEFT JOIN $categorylinks AS L ON I.page_id = L.cl_from)
-                                               LEFT JOIN $imagelinks AS P ON I.page_title = P.il_to)
-                                               INNER JOIN $image AS G ON I.page_title = G.img_name)
-                                       WHERE I.page_namespace = ".NS_FILE." AND L.cl_from IS NULL AND P.il_to IS NULL";
-               } else {
-                       list( $image, $imagelinks ) = $dbr->tableNamesN( 'image','imagelinks' );
-
-                       return "SELECT 'Unusedimages' as type, 6 as namespace, img_name as title, $epoch as value,
-                               img_user, img_user_text,  img_description
-                               FROM $image LEFT JOIN $imagelinks ON img_name=il_to WHERE il_to IS NULL ";
+                       // Order is significant
+                       $retval['tables'] = array ( 'image', 'page', 'categorylinks',
+                                       'imagelinks' );
+                       $retval['conds']['page_namespace'] = NS_FILE;
+                       $retval['conds'][] = 'cl_from IS NULL';
+                       $retval['conds'][] = 'img_name = page_title';
+                       $retval['join_conds']['categorylinks'] = array (
+                                       'LEFT JOIN', 'cl_from = page_id' );
+                       $retval['join_conds']['imagelinks'] = array (
+                                       'LEFT JOIN', 'il_to = page_title' );
                }
+               return $retval;
+       }
+
+       function usesTimestamps() {
+               return true;
        }
 
        function getPageHeader() {
@@ -69,13 +81,3 @@ class UnusedimagesPage extends ImageQueryPage {
        }
 
 }
-
-/**
- * Entry point
- */
-function wfSpecialUnusedimages() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $uip = new UnusedimagesPage();
-
-       return $uip->doQuery( $offset, $limit );
-}
index 68bf95a..eaf0544 100644 (file)
  */
 class UnusedtemplatesPage extends QueryPage {
 
-       function getName() { return( 'Unusedtemplates' ); }
+       function __construct( $name = 'Unusedtemplates' ) {
+               parent::__construct( $name );
+       }
+       
        function isExpensive() { return true; }
        function isSyndicated() { return false; }
        function sortDescending() { return false; }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $templatelinks) = $dbr->tableNamesN( 'page', 'templatelinks' );
-               $sql = "SELECT 'Unusedtemplates' AS type, page_title AS title,
-                       page_namespace AS namespace, 0 AS value
-                       FROM $page
-                       LEFT JOIN $templatelinks
-                       ON page_namespace = tl_namespace AND page_title = tl_title
-                       WHERE page_namespace = 10 AND tl_from IS NULL
-                       AND page_is_redirect = 0";
-               return $sql;
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'page', 'templatelinks' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_title AS value' ),
+                       'conds' => array ( 'page_namespace' => NS_TEMPLATE,
+                                       'tl_from IS NULL',
+                                       'page_is_redirect' => 0 ),
+                       'join_conds' => array ( 'templatelinks' => array (
+                               'LEFT JOIN', array ( 'tl_title = page_title',
+                                       'tl_namespace = page_namespace' ) ) )
+               );
        }
-
+       
        function formatResult( $skin, $result ) {
                $title = Title::makeTitle( NS_TEMPLATE, $result->title );
                $pageLink = $skin->linkKnown(
@@ -72,8 +77,3 @@ class UnusedtemplatesPage extends QueryPage {
 
 }
 
-function wfSpecialUnusedtemplates() {
-       list( $limit, $offset ) = wfCheckLimits();
-       $utp = new UnusedtemplatesPage();
-       $utp->doQuery( $offset, $limit );
-}
index ecd62cb..5ad9bb5 100644 (file)
  */
 class UnwatchedpagesPage extends QueryPage {
 
-       function getName() { return 'Unwatchedpages'; }
+       function __construct( $name = 'Unwatchedpages' ) {
+               parent::__construct( $name, 'unwatchedpages' );
+       }
+       
        function isExpensive() { return true; }
        function isSyndicated() { return false; }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $watchlist ) = $dbr->tableNamesN( 'page', 'watchlist' );
-               $mwns = NS_MEDIAWIKI;
-               return
-                       "
-                       SELECT
-                               'Unwatchedpages' as type,
-                               page_namespace as namespace,
-                               page_title as title,
-                               page_namespace as value
-                       FROM $page
-                       LEFT JOIN $watchlist ON wl_namespace = page_namespace AND page_title = wl_title
-                       WHERE wl_title IS NULL AND page_is_redirect = 0 AND page_namespace<>$mwns
-                       ";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'page', 'watchlist' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_namespace AS value' ),
+                       'conds' => array ( 'wl_title IS NULL',
+                                       'page_is_redirect' => 0,
+                                       "page_namespace != '" . NS_MEDIAWIKI .
+                                       "'" ),
+                       'join_conds' => array ( 'watchlist' => array (
+                               'LEFT JOIN', array ( 'wl_title = page_title',
+                                       'wl_namespace = page_namespace' ) ) )
+               );
        }
 
        function sortDescending() { return false; }
+       
+       function getOrderFields() {
+               return array( 'page_namespace', 'page_title' );
+       }
 
        function formatResult( $skin, $result ) {
                global $wgContLang;
@@ -74,19 +80,3 @@ class UnwatchedpagesPage extends QueryPage {
                return wfSpecialList( $plink, $wlink );
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialUnwatchedpages() {
-       global $wgUser, $wgOut;
-
-       if ( ! $wgUser->isAllowed( 'unwatchedpages' ) )
-               return $wgOut->permissionRequired( 'unwatchedpages' );
-
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new UnwatchedpagesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
index b588dbf..635b9b6 100644 (file)
  */
 class WantedCategoriesPage extends WantedQueryPage {
 
-       function getName() {
-               return 'Wantedcategories';
+       function __construct( $name = 'Wantedcategories' ) {
+               parent::__construct( $name );
        }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $categorylinks, $page ) = $dbr->tableNamesN( 'categorylinks', 'page' );
-               $name = $dbr->addQuotes( $this->getName() );
-               return
-                       "
-                       SELECT
-                               $name as type,
-                               " . NS_CATEGORY . " as namespace,
-                               cl_to as title,
-                               COUNT(*) as value
-                       FROM $categorylinks
-                       LEFT JOIN $page ON cl_to = page_title AND page_namespace = ". NS_CATEGORY ."
-                       WHERE page_title IS NULL
-                       GROUP BY cl_to
-                       ";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'categorylinks', 'page' ),
+                       'fields' => array ( "'" . NS_CATEGORY . "' AS namespace",
+                                       'cl_to AS title',
+                                       'COUNT(*) AS value' ),
+                       'conds' => array ( 'page_title IS NULL' ),
+                       'options' => array ( 'GROUP BY' => 'cl_to' ),
+                       'join_conds' => array ( 'page' => array ( 'LEFT JOIN',
+                               array ( 'page_title = cl_to',
+                                       'page_namespace' => NS_CATEGORY ) ) )
+               );
        }
 
        function formatResult( $skin, $result ) {
@@ -73,14 +69,3 @@ class WantedCategoriesPage extends WantedQueryPage {
                return wfSpecialList($plink, $nlinks);
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialWantedCategories() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new WantedCategoriesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
index d6c1157..81170e2 100644 (file)
  */
 class WantedFilesPage extends WantedQueryPage {
 
-       function getName() {
-               return 'Wantedfiles';
+       function __construct( $name = 'Wantedfiles' ) {
+               parent::__construct( $name );
        }
-
+       
        /**
         * KLUGE: The results may contain false positives for files
         * that exist e.g. in a shared repo.  Setting this at least
@@ -45,32 +45,19 @@ class WantedFilesPage extends WantedQueryPage {
                return true;
        }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $imagelinks, $image ) = $dbr->tableNamesN( 'imagelinks', 'image' );
-               $name = $dbr->addQuotes( $this->getName() );
-               return
-                       "
-                       SELECT
-                               $name as type,
-                               " . NS_FILE . " as namespace,
-                               il_to as title,
-                               COUNT(*) as value
-                       FROM $imagelinks
-                       LEFT JOIN $image ON il_to = img_name
-                       WHERE img_name IS NULL
-                       GROUP BY il_to
-                       ";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'imagelinks', 'image' ),
+                       'fields' => array ( "'" . NS_FILE . "' AS namespace",
+                                       'il_to AS title',
+                                       'COUNT(*) AS value' ),
+                       'conds' => array ( 'img_name IS NULL' ),
+                       'options' => array ( 'GROUP BY' => 'il_to' ),
+                       'join_conds' => array ( 'image' => 
+                               array ( 'LEFT JOIN',
+                                       array ( 'il_to = img_name' ) 
+                               ) 
+                       )
+               );
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialWantedFiles() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new WantedFilesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
index 4e1611b..2610069 100644 (file)
  * @ingroup SpecialPage
  */
 class WantedPagesPage extends WantedQueryPage {
-       var $nlinks;
-
-       function __construct( $inc = false, $nlinks = true ) {
-               $this->setListoutput( $inc );
-               $this->nlinks = $nlinks;
+       function __construct( $name = 'Wantedpages' ) {
+               parent::__construct( $name );
        }
+               
+       function execute( $par ) {
+               $inc = $this->including();
 
-       function getName() {
-               return 'Wantedpages';
+               if ( $inc ) {
+                       @list( $limit, $nlinks ) = explode( '/', $par, 2 );
+                       $this->limit = (int)$limit;
+                       // FIXME: nlinks is ignored
+                       $nlinks = $nlinks === 'nlinks';
+                       $this->offset = 0;
+               } else {
+                       $nlinks = true;
+               }
+               $this->setListOutput( $inc );
+               $this->shownavigation = !$inc;
+               parent::execute( $par );
        }
 
-       function getSQL() {
+       function getQueryInfo() {
                global $wgWantedPagesThreshold;
                $count = $wgWantedPagesThreshold - 1;
-               $dbr = wfGetDB( DB_SLAVE );
-               $pagelinks = $dbr->tableName( 'pagelinks' );
-               $page      = $dbr->tableName( 'page' );
-               $sql = "SELECT 'Wantedpages' AS type,
-                               pl_namespace AS namespace,
-                               pl_title AS title,
-                               COUNT(*) AS value
-                       FROM $pagelinks
-                       LEFT JOIN $page AS pg1
-                       ON pl_namespace = pg1.page_namespace AND pl_title = pg1.page_title
-                       LEFT JOIN $page AS pg2
-                       ON pl_from = pg2.page_id
-                       WHERE pg1.page_namespace IS NULL
-                       AND pl_namespace NOT IN ( " . NS_USER . ", ". NS_USER_TALK . ")
-                       AND pg2.page_namespace != " . NS_MEDIAWIKI . "
-                       GROUP BY pl_namespace, pl_title
-                       HAVING COUNT(*) > $count";
-
-               wfRunHooks( 'WantedPages::getSQL', array( &$this, &$sql ) );
-               return $sql;
-       }
-}
-
-/**
- * constructor
- */
-function wfSpecialWantedpages( $par = null, $specialPage ) {
-       $inc = $specialPage->including();
-
-       if ( $inc ) {
-               @list( $limit, $nlinks ) = explode( '/', $par, 2 );
-               $limit = (int)$limit;
-               $nlinks = $nlinks === 'nlinks';
-               $offset = 0;
-       } else {
-               list( $limit, $offset ) = wfCheckLimits();
-               $nlinks = true;
-       }
-
-       $wpp = new WantedPagesPage( $inc, $nlinks );
-
-       $wpp->doQuery( $offset, $limit, !$inc );
+               $query = array (
+                       'tables' => array ( 'pagelinks', 'pg1' => 'page',
+                                       'pg2' => 'page' ),
+                       'fields' => array ( 'pl_namespace AS namespace',
+                                       'pl_title AS title',
+                                       'COUNT(*) AS value' ),
+                       'conds' => array ( 'pg1.page_namespace IS NULL',
+                                       "pl_namespace NOT IN ( '" . NS_USER .
+                                               "', '" . NS_USER_TALK . "' )",
+                                       "pg2.page_namespace != '" .
+                                               NS_MEDIAWIKI . "'" ),
+                       'options' => array ( 'HAVING' => "COUNT(*) > $count",
+                               'GROUP BY' => 'pl_namespace, pl_title' ),
+                       'join_conds' => array ( 'page AS pg1' => array (
+                                       'LEFT JOIN', array (
+                                       'pg1.page_namespace = pl_namespace',
+                                       'pg1.page_title = pl_title' ) ),
+                               'page AS pg2' => array ( 'LEFT JOIN',
+                                       'pg2.page_id = pl_from' ) )
+               );
+               // Replacement WantedPages::getSQL
+               wfRunHooks( 'WantedPages::getQueryInfo',
+                               array( &$this, &$query ) );
+               return $query;
+       }       
 }
index ae43c23..ab9d604 100644 (file)
  */
 class WantedTemplatesPage extends WantedQueryPage {
 
-       function getName() {
-               return 'Wantedtemplates';
+       function __construct( $name = 'Wantedtemplates' ) {
+               parent::__construct( $name );
        }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $templatelinks, $page ) = $dbr->tableNamesN( 'templatelinks', 'page' );
-               $name = $dbr->addQuotes( $this->getName() );
-               return
-                       "
-                         SELECT $name as type, 
-                                tl_namespace as namespace,
-                                tl_title as title,
-                                COUNT(*) as value
-                           FROM $templatelinks LEFT JOIN
-                                $page ON tl_title = page_title AND tl_namespace = page_namespace
-                          WHERE page_title IS NULL AND tl_namespace = ". NS_TEMPLATE ."
-                       GROUP BY tl_namespace, tl_title
-                       ";
+       function getQueryInfo() {
+               return array (
+                       'tables' => array ( 'templatelinks', 'page' ),
+                       'fields' => array ( 'tl_namespace AS namespace',
+                                       'tl_title AS title',
+                                       'COUNT(*) AS value' ),
+                       'conds' => array ( 'page_title IS NULL',
+                                       'tl_namespace' => NS_TEMPLATE ),
+                       'options' => array (
+                               'GROUP BY' => 'tl_namespace, tl_title' ),
+                       'join_conds' => array ( 'page' => array ( 'LEFT JOIN',
+                                       array ( 'page_namespace = tl_namespace',
+                                               'page_title = tl_title' ) ) )
+               );
        }
 }
-
-/**
- * constructor
- */
-function wfSpecialWantedTemplates() {
-       list( $limit, $offset ) = wfCheckLimits();
-
-       $wpp = new WantedTemplatesPage();
-
-       $wpp->doQuery( $offset, $limit );
-}
index 90c1f44..3aa1b77 100644 (file)
 class WithoutInterwikiPage extends PageQueryPage {
        private $prefix = '';
 
-       function getName() {
-               return 'Withoutinterwiki';
+       function __construct( $name = 'Withoutinterwiki' ) {
+               parent::__construct( $name );
+       }
+       
+       function execute( $par ) {
+               global $wgRequest;
+               $this->prefix = Title::capitalize( $wgRequest->getVal( 'prefix', $par ), NS_MAIN );
+               parent::execute( $par );
        }
 
        function getPageHeader() {
@@ -58,6 +64,10 @@ class WithoutInterwikiPage extends PageQueryPage {
        function sortDescending() {
                return false;
        }
+       
+       function getOrderFields() {
+               return array( 'page_namespace', 'page_title' );
+       }
 
        function isExpensive() {
                return true;
@@ -67,36 +77,22 @@ class WithoutInterwikiPage extends PageQueryPage {
                return false;
        }
 
-       function getSQL() {
-               $dbr = wfGetDB( DB_SLAVE );
-               list( $page, $langlinks ) = $dbr->tableNamesN( 'page', 'langlinks' );
-               $prefix = $this->prefix ? 'AND page_title' . $dbr->buildLike( $this->prefix , $dbr->anyString() ) : '';
-               return
-                 "SELECT 'Withoutinterwiki'  AS type,
-                         page_namespace AS namespace,
-                         page_title     AS title,
-                         page_title     AS value
-                    FROM $page
-               LEFT JOIN $langlinks
-                      ON ll_from = page_id
-                   WHERE ll_title IS NULL
-                     AND page_namespace=" . NS_MAIN . "
-                     AND page_is_redirect = 0
-                         {$prefix}";
-       }
-
-       function setPrefix( $prefix = '' ) {
-               $this->prefix = $prefix;
+       function getQueryInfo() {
+               $query = array (
+                       'tables' => array ( 'page', 'langlinks' ),
+                       'fields' => array ( 'page_namespace AS namespace',
+                                       'page_title AS title',
+                                       'page_title AS value' ),
+                       'conds' => array ( 'll_title IS NULL',
+                                       'page_namespace' => NS_MAIN,
+                                       'page_is_redirect' => 0 ),
+                       'join_conds' => array ( 'langlinks' => array (
+                                       'LEFT JOIN', 'll_from = page_id' ) )
+               );
+               if ( $this->prefix ) {
+                       $dbr = wfGetDb( DB_SLAVE );
+                       $query['conds'][] = 'page_title ' . $dbr->buildLike( $this->prefix, $dbr->anyString() );
+               }
+               return $query;
        }
-
-}
-
-function wfSpecialWithoutinterwiki() {
-       global $wgRequest;
-       list( $limit, $offset ) = wfCheckLimits();
-       // Only searching the mainspace anyway
-       $prefix = Title::capitalize( $wgRequest->getVal( 'prefix' ), NS_MAIN );
-       $wip = new WithoutInterwikiPage();
-       $wip->setPrefix( $prefix );
-       $wip->doQuery( $offset, $limit );
 }
index 4507da1..8953ebf 100644 (file)
@@ -2520,6 +2520,7 @@ Please note that other web sites may link to a file with a direct URL, and so ma
 'pager-newer-n'                   => '{{PLURAL:$1|newer 1|newer $1}}',
 'pager-older-n'                   => '{{PLURAL:$1|older 1|older $1}}',
 'suppress'                        => 'Oversight',
+'querypage-disabled'              => 'This special page is disabled for performance reasons.',
 
 # Book sources
 'booksources'               => 'Book sources',
index b57ca2c..203a11d 100644 (file)
@@ -2120,6 +2120,7 @@ The title is {{msg-mw|nopagetitle}}.',
 'pager-newer-n'           => "This is part of the navigation message on the top and bottom of Special pages which are lists of things in date order, e.g. the User's contributions page. It is passed as the second argument of {{msg-mw|Viewprevnext}}. $1 is the number of items shown per page.",
 'pager-older-n'           => "This is part of the navigation message on the top and bottom of Special pages which are lists of things in date order, e.g. the User's contributions page. It is passed as the first argument of {{msg-mw|Viewprevnext}}. $1 is the number of items shown per page.",
 'suppress'                => '{{Identical|Oversight}}',
+'querypage-disabled'      => "On special pages that use expensive database queries but are not cacheable, this message is displayed when 'miser mode' is on (i.e. no expensive queries allowed).",
 
 # Book sources
 'booksources'               => 'Name of special page displayed in [[Special:SpecialPages]]',
index db6b7f1..620a353 100644 (file)
@@ -1623,6 +1623,7 @@ $wgMessageStructure = array(
                'pager-newer-n',
                'pager-older-n',
                'suppress',
+               'querypage-disabled',
        ),
        'booksources' => array(
                'booksources',