From: Roan Kattouw Date: Wed, 22 Dec 2010 14:16:25 +0000 (+0000) Subject: Merge querypage-work2 branch from trunk. The most relevant changes are: X-Git-Tag: 1.31.0-rc.0~33197 X-Git-Url: http://git.cyclocoop.org/%24href?a=commitdiff_plain;h=56b5aa18ed5b4faa5a4652eec87195f86170a286;p=lhc%2Fweb%2Fwiklou.git Merge querypage-work2 branch from trunk. The most relevant changes are: * 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. --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 6ab0b93850..8f78b496b0 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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 diff --git a/docs/hooks.txt b/docs/hooks.txt index 2fc81d9211..2b3a9db6eb 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -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 diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index f90dbdd75b..d3fbf1dc6d 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -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', diff --git a/includes/ImageQueryPage.php b/includes/ImageQueryPage.php index 180201a2f2..c50b0cc516 100644 --- a/includes/ImageQueryPage.php +++ b/includes/ImageQueryPage.php @@ -7,7 +7,7 @@ * @ingroup SpecialPage * @author Rob Church */ -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 diff --git a/includes/Namespace.php b/includes/Namespace.php index 48a93dde91..3db1938b64 100644 --- a/includes/Namespace.php +++ b/includes/Namespace.php @@ -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? * diff --git a/includes/PageQueryPage.php b/includes/PageQueryPage.php index 892ff259f3..8390241f59 100644 --- a/includes/PageQueryPage.php +++ b/includes/PageQueryPage.php @@ -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 diff --git a/includes/QueryPage.php b/includes/QueryPage.php index d9fb3bb278..7dfe3fdc97 100644 --- a/includes/QueryPage.php +++ b/includes/QueryPage.php @@ -15,20 +15,22 @@ 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( '

' . wfShowingResults( $offset, $num ) . '

' ); + if ( $this->numRows > 0 ) { + $wgOut->addHTML( '

' . wfShowingResults( $this->offset, $this->numRows ) . '

' ); # 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( '

' . $paging . '

' ); } 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( '

' . $paging . '

' ); } $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() ? '' . $skin->link( $title ) . '' : $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 * diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php index 2f9e264206..ef8caa59c2 100644 --- a/includes/SpecialPage.php +++ b/includes/SpecialPage.php @@ -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 ) { diff --git a/includes/specials/SpecialAncientpages.php b/includes/specials/SpecialAncientpages.php index 2d5047d2d2..cbb5df8025 100644 --- a/includes/specials/SpecialAncientpages.php +++ b/includes/specials/SpecialAncientpages.php @@ -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 ); -} diff --git a/includes/specials/SpecialBrokenRedirects.php b/includes/specials/SpecialBrokenRedirects.php index 98b0212640..ddd6d48cd6 100644 --- a/includes/specials/SpecialBrokenRedirects.php +++ b/includes/specials/SpecialBrokenRedirects.php @@ -28,38 +28,44 @@ * @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 ); -} diff --git a/includes/specials/SpecialDeadendpages.php b/includes/specials/SpecialDeadendpages.php index dfa053aa84..bb9625afa6 100644 --- a/includes/specials/SpecialDeadendpages.php +++ b/includes/specials/SpecialDeadendpages.php @@ -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 ); } diff --git a/includes/specials/SpecialDisambiguations.php b/includes/specials/SpecialDisambiguations.php index 3e706189e4..314a7fbb7f 100644 --- a/includes/specials/SpecialDisambiguations.php +++ b/includes/specials/SpecialDisambiguations.php @@ -28,32 +28,27 @@ */ 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 ); -} diff --git a/includes/specials/SpecialDoubleRedirects.php b/includes/specials/SpecialDoubleRedirects.php index c7f6321031..d25de5ddc7 100644 --- a/includes/specials/SpecialDoubleRedirects.php +++ b/includes/specials/SpecialDoubleRedirects.php @@ -29,63 +29,63 @@ */ 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 ); - -} diff --git a/includes/specials/SpecialFewestrevisions.php b/includes/specials/SpecialFewestrevisions.php index c265ed3861..10d8e6ba83 100644 --- a/includes/specials/SpecialFewestrevisions.php +++ b/includes/specials/SpecialFewestrevisions.php @@ -29,10 +29,10 @@ */ 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 ); -} diff --git a/includes/specials/SpecialFileDuplicateSearch.php b/includes/specials/SpecialFileDuplicateSearch.php index b0a6b39f15..6eccb90c91 100644 --- a/includes/specials/SpecialFileDuplicateSearch.php +++ b/includes/specials/SpecialFileDuplicateSearch.php @@ -29,34 +29,94 @@ * @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( '
' . + $thumb->toHtml( array( 'desc-link' => false ) ) . '
' . + wfMsgExt( 'fileduplicatesearch-info', array( 'parse' ), + $wgLang->formatNum( $img->getWidth() ), + $wgLang->formatNum( $img->getHeight() ), + $wgLang->formatSize( $img->getSize() ), + $img->getMimeType() + ) . + '
' ); + } + } - 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( + "

\n$1\n

", + array( 'fileduplicatesearch-result-1', $this->filename ) + ); + } elseif ( $this->numRows > 1 ) { + $wgOut->wrapWikiMsg( + "

\n$1\n

", + 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( '
' . - $thumb->toHtml( array( 'desc-link' => false ) ) . '
' . - wfMsgExt( 'fileduplicatesearch-info', array( 'parse' ), - $wgLang->formatNum( $img->getWidth() ), - $wgLang->formatNum( $img->getHeight() ), - $wgLang->formatSize( $img->getSize() ), - $img->getMimeType() - ) . - '
' ); - } - } - - # 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( - "

\n$1\n

", - array( 'fileduplicatesearch-result-1', $filename ) - ); - } elseif ( $count > 1 ) { - $wgOut->wrapWikiMsg( - "

\n$1\n

", - array( 'fileduplicatesearch-result-n', $filename, $wgLang->formatNum( $count - 1 ) ) - ); - } - } -} diff --git a/includes/specials/SpecialLinkSearch.php b/includes/specials/SpecialLinkSearch.php index 124ede1fcf..c97566f09e 100644 --- a/includes/specials/SpecialLinkSearch.php +++ b/includes/specials/SpecialLinkSearch.php @@ -25,69 +25,6 @@ /** * 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', '' . $wgLang->commaList( $wgUrlProtocols ) . '' ); - $s = Xml::openElement( 'form', array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => $GLOBALS['wgScript'] ) ) . - Html::hidden( 'title', $self->getPrefixedDbKey() ) . - '
' . - 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' ) ) . - '
' . - 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', '' . $wgLang->commaList( $wgUrlProtocols ) . '' ); + $s = Xml::openElement( 'form', array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => $GLOBALS['wgScript'] ) ) . + Html::hidden( 'title', $self->getPrefixedDbKey() ) . + '
' . + 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' ) ) . + '
' . + 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(); } } diff --git a/includes/specials/SpecialListfiles.php b/includes/specials/SpecialListfiles.php index bbea46da17..638d2de9b9 100644 --- a/includes/specials/SpecialListfiles.php +++ b/includes/specials/SpecialListfiles.php @@ -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 ); diff --git a/includes/specials/SpecialListredirects.php b/includes/specials/SpecialListredirects.php index 315047daba..850466272a 100644 --- a/includes/specials/SpecialListredirects.php +++ b/includes/specials/SpecialListredirects.php @@ -30,17 +30,33 @@ */ 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 ); -} diff --git a/includes/specials/SpecialLonelypages.php b/includes/specials/SpecialLonelypages.php index 0788037f36..a4d0ec5d39 100644 --- a/includes/specials/SpecialLonelypages.php +++ b/includes/specials/SpecialLonelypages.php @@ -29,9 +29,10 @@ */ 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 ); } diff --git a/includes/specials/SpecialLongpages.php b/includes/specials/SpecialLongpages.php index cd0f309020..dd60e37d6f 100644 --- a/includes/specials/SpecialLongpages.php +++ b/includes/specials/SpecialLongpages.php @@ -27,22 +27,11 @@ */ 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 ); -} diff --git a/includes/specials/SpecialMIMEsearch.php b/includes/specials/SpecialMIMEsearch.php index 79683a35d8..c9e8898d0c 100644 --- a/includes/specials/SpecialMIMEsearch.php +++ b/includes/specials/SpecialMIMEsearch.php @@ -28,49 +28,60 @@ * @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 ); } diff --git a/includes/specials/SpecialMostcategories.php b/includes/specials/SpecialMostcategories.php index 124f0bd5d0..41279e35ff 100644 --- a/includes/specials/SpecialMostcategories.php +++ b/includes/specials/SpecialMostcategories.php @@ -31,26 +31,25 @@ */ 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 ); -} diff --git a/includes/specials/SpecialMostimages.php b/includes/specials/SpecialMostimages.php index 411a281b07..2fdbd1bb9c 100644 --- a/includes/specials/SpecialMostimages.php +++ b/includes/specials/SpecialMostimages.php @@ -31,24 +31,22 @@ */ 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 ); -} diff --git a/includes/specials/SpecialMostlinked.php b/includes/specials/SpecialMostlinked.php index c731588a76..aaa07d7a62 100644 --- a/includes/specials/SpecialMostlinked.php +++ b/includes/specials/SpecialMostlinked.php @@ -32,39 +32,27 @@ */ 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 ); -} diff --git a/includes/specials/SpecialMostlinkedcategories.php b/includes/specials/SpecialMostlinkedcategories.php index e1fc1d9550..75ecd93d61 100644 --- a/includes/specials/SpecialMostlinkedcategories.php +++ b/includes/specials/SpecialMostlinkedcategories.php @@ -31,24 +31,20 @@ */ 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 ); -} diff --git a/includes/specials/SpecialMostlinkedtemplates.php b/includes/specials/SpecialMostlinkedtemplates.php index 822d6bc919..78f806f487 100644 --- a/includes/specials/SpecialMostlinkedtemplates.php +++ b/includes/specials/SpecialMostlinkedtemplates.php @@ -28,15 +28,10 @@ * * @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 ); -} diff --git a/includes/specials/SpecialMostrevisions.php b/includes/specials/SpecialMostrevisions.php index f9bafabc7c..6c30e54530 100644 --- a/includes/specials/SpecialMostrevisions.php +++ b/includes/specials/SpecialMostrevisions.php @@ -23,64 +23,12 @@ * @ingroup SpecialPage * @author Ævar Arnfjörð Bjarmason */ - -/** - * 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 ); -} diff --git a/includes/specials/SpecialNewpages.php b/includes/specials/SpecialNewpages.php index 498fa235f2..5142d047c6 100644 --- a/includes/specials/SpecialNewpages.php +++ b/includes/specials/SpecialNewpages.php @@ -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( diff --git a/includes/specials/SpecialPopularpages.php b/includes/specials/SpecialPopularpages.php index 375cefdf39..1ef204f702 100644 --- a/includes/specials/SpecialPopularpages.php +++ b/includes/specials/SpecialPopularpages.php @@ -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 ); -} diff --git a/includes/specials/SpecialShortpages.php b/includes/specials/SpecialShortpages.php index 989e4c07ba..c53ab6a9bd 100644 --- a/includes/specials/SpecialShortpages.php +++ b/includes/specials/SpecialShortpages.php @@ -29,10 +29,11 @@ */ 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 ''; } @@ -110,14 +104,3 @@ class ShortPagesPage extends QueryPage { : "({$hlink}) {$dm}{$plink} {$dm}[{$size}]"; } } - -/** - * constructor - */ -function wfSpecialShortpages() { - list( $limit, $offset ) = wfCheckLimits(); - - $spp = new ShortPagesPage(); - - return $spp->doQuery( $offset, $limit ); -} diff --git a/includes/specials/SpecialTags.php b/includes/specials/SpecialTags.php index c2aecf4731..164565cdb1 100644 --- a/includes/specials/SpecialTags.php +++ b/includes/specials/SpecialTags.php @@ -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 ); diff --git a/includes/specials/SpecialUncategorizedcategories.php b/includes/specials/SpecialUncategorizedcategories.php index 9574af7018..70d98df92b 100644 --- a/includes/specials/SpecialUncategorizedcategories.php +++ b/includes/specials/SpecialUncategorizedcategories.php @@ -27,22 +27,8 @@ * @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 ); } diff --git a/includes/specials/SpecialUncategorizedimages.php b/includes/specials/SpecialUncategorizedimages.php index c425403908..d6a7fefa74 100644 --- a/includes/specials/SpecialUncategorizedimages.php +++ b/includes/specials/SpecialUncategorizedimages.php @@ -27,10 +27,11 @@ * * @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 ); -} diff --git a/includes/specials/SpecialUncategorizedpages.php b/includes/specials/SpecialUncategorizedpages.php index c7fef5d259..26eb36a41b 100644 --- a/includes/specials/SpecialUncategorizedpages.php +++ b/includes/specials/SpecialUncategorizedpages.php @@ -26,11 +26,12 @@ * * @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 ); } diff --git a/includes/specials/SpecialUncategorizedtemplates.php b/includes/specials/SpecialUncategorizedtemplates.php index aa4e979d96..af038fa88d 100644 --- a/includes/specials/SpecialUncategorizedtemplates.php +++ b/includes/specials/SpecialUncategorizedtemplates.php @@ -29,20 +29,8 @@ * @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 ); } diff --git a/includes/specials/SpecialUnusedcategories.php b/includes/specials/SpecialUnusedcategories.php index a20efe0963..ac17b87ac3 100644 --- a/includes/specials/SpecialUnusedcategories.php +++ b/includes/specials/SpecialUnusedcategories.php @@ -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 ); -} diff --git a/includes/specials/SpecialUnusedimages.php b/includes/specials/SpecialUnusedimages.php index 091ec3a3df..6c8f6c51e7 100644 --- a/includes/specials/SpecialUnusedimages.php +++ b/includes/specials/SpecialUnusedimages.php @@ -27,41 +27,53 @@ * @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 ); -} diff --git a/includes/specials/SpecialUnusedtemplates.php b/includes/specials/SpecialUnusedtemplates.php index 68bf95a27d..eaf05442ea 100644 --- a/includes/specials/SpecialUnusedtemplates.php +++ b/includes/specials/SpecialUnusedtemplates.php @@ -31,24 +31,29 @@ */ 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 ); -} diff --git a/includes/specials/SpecialUnwatchedpages.php b/includes/specials/SpecialUnwatchedpages.php index ecd62cb7c9..5ad9bb5b2f 100644 --- a/includes/specials/SpecialUnwatchedpages.php +++ b/includes/specials/SpecialUnwatchedpages.php @@ -31,28 +31,34 @@ */ 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 ); -} diff --git a/includes/specials/SpecialWantedcategories.php b/includes/specials/SpecialWantedcategories.php index b588dbf07f..635b9b6347 100644 --- a/includes/specials/SpecialWantedcategories.php +++ b/includes/specials/SpecialWantedcategories.php @@ -30,26 +30,22 @@ */ 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 ); -} diff --git a/includes/specials/SpecialWantedfiles.php b/includes/specials/SpecialWantedfiles.php index d6c1157b00..81170e2f7b 100644 --- a/includes/specials/SpecialWantedfiles.php +++ b/includes/specials/SpecialWantedfiles.php @@ -31,10 +31,10 @@ */ 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 ); -} diff --git a/includes/specials/SpecialWantedpages.php b/includes/specials/SpecialWantedpages.php index 4e1611bce4..2610069f46 100644 --- a/includes/specials/SpecialWantedpages.php +++ b/includes/specials/SpecialWantedpages.php @@ -27,60 +27,53 @@ * @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; + } } diff --git a/includes/specials/SpecialWantedtemplates.php b/includes/specials/SpecialWantedtemplates.php index ae43c23716..ab9d604673 100644 --- a/includes/specials/SpecialWantedtemplates.php +++ b/includes/specials/SpecialWantedtemplates.php @@ -33,35 +33,23 @@ */ 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 ); -} diff --git a/includes/specials/SpecialWithoutinterwiki.php b/includes/specials/SpecialWithoutinterwiki.php index 90c1f441e5..3aa1b77959 100644 --- a/includes/specials/SpecialWithoutinterwiki.php +++ b/includes/specials/SpecialWithoutinterwiki.php @@ -30,8 +30,14 @@ 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 ); } diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index 4507da1df8..8953ebf005 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -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', diff --git a/languages/messages/MessagesQqq.php b/languages/messages/MessagesQqq.php index b57ca2c296..203a11d970 100644 --- a/languages/messages/MessagesQqq.php +++ b/languages/messages/MessagesQqq.php @@ -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]]', diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc index db6b7f15d0..620a3539e2 100644 --- a/maintenance/language/messages.inc +++ b/maintenance/language/messages.inc @@ -1623,6 +1623,7 @@ $wgMessageStructure = array( 'pager-newer-n', 'pager-older-n', 'suppress', + 'querypage-disabled', ), 'booksources' => array( 'booksources',