diff options
author | Pierre Schmitz <pierre@archlinux.de> | 2013-08-12 09:28:15 +0200 |
---|---|---|
committer | Pierre Schmitz <pierre@archlinux.de> | 2013-08-12 09:28:15 +0200 |
commit | 08aa4418c30cfc18ccc69a0f0f9cb9e17be6c196 (patch) | |
tree | 577a29fb579188d16003a209ce2a2e9c5b0aa2bd /includes/specials | |
parent | cacc939b34e315b85e2d72997811eb6677996cc1 (diff) |
Update to MediaWiki 1.21.1
Diffstat (limited to 'includes/specials')
90 files changed, 2236 insertions, 1226 deletions
diff --git a/includes/specials/SpecialActiveusers.php b/includes/specials/SpecialActiveusers.php index c5aa2389..c9c82ada 100644 --- a/includes/specials/SpecialActiveusers.php +++ b/includes/specials/SpecialActiveusers.php @@ -50,7 +50,7 @@ class ActiveUsersPager extends UsersPager { /** * @param $context IContextSource * @param $group null Unused - * @param $par string Parameter passed to the page + * @param string $par Parameter passed to the page */ function __construct( IContextSource $context = null, $group = null, $par = null ) { global $wgActiveUserDays; @@ -93,37 +93,38 @@ class ActiveUsersPager extends UsersPager { function getQueryInfo() { $dbr = wfGetDB( DB_SLAVE ); $conds = array( 'rc_user > 0' ); // Users - no anons - $conds[] = 'ipb_deleted IS NULL'; // don't show hidden names + if( !$this->getUser()->isAllowed( 'hideuser' ) ) { + $conds[] = 'ipb_deleted IS NULL OR ipb_deleted = 0'; // don't show hidden names + } $conds[] = 'rc_log_type IS NULL OR rc_log_type != ' . $dbr->addQuotes( 'newusers' ); - $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes( $dbr->timestamp( wfTimestamp( TS_UNIX ) - $this->RCMaxAge*24*3600 ) ); + $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes( + $dbr->timestamp( wfTimestamp( TS_UNIX ) - $this->RCMaxAge*24*3600 ) ); if( $this->requestedUser != '' ) { $conds[] = 'rc_user_text >= ' . $dbr->addQuotes( $this->requestedUser ); } - $query = array( - 'tables' => array( 'recentchanges', 'user', 'ipblocks' ), - 'fields' => array( 'user_name' => 'rc_user_text', // inheritance + return array( + 'tables' => array( 'recentchanges', 'ipblocks' ), + 'fields' => array( + 'user_name' => 'rc_user_text', // for Pager inheritance 'rc_user_text', // for Pager - 'user_id', + 'user_id' => 'rc_user', 'recentedits' => 'COUNT(*)', - 'blocked' => 'MAX(ipb_user)' + 'ipb_deleted' => 'MAX(ipb_deleted)' ), 'options' => array( 'GROUP BY' => array( 'rc_user_text', 'user_id' ), 'USE INDEX' => array( 'recentchanges' => 'rc_user_text' ) ), - 'join_conds' => array( - 'user' => array( 'INNER JOIN', 'rc_user_text=user_name' ), + 'join_conds' => array( // check for suppression blocks 'ipblocks' => array( 'LEFT JOIN', array( - 'user_id=ipb_user', - 'ipb_auto' => 0, - 'ipb_deleted' => 1 + 'rc_user=ipb_user', + 'ipb_auto' => 0 # avoid duplicate blocks )), ), 'conds' => $conds ); - return $query; } function formatRow( $row ) { @@ -162,9 +163,12 @@ class ActiveUsersPager extends UsersPager { $groups = $lang->commaList( $list ); $item = $lang->specialList( $ulinks, $groups ); + if( $row->ipb_deleted ) { + $item = "<span class=\"deleted\">$item</span>"; + } $count = $this->msg( 'activeusers-count' )->numParams( $row->recentedits ) ->params( $userName )->numParams( $this->RCMaxAge )->escaped(); - $blocked = $row->blocked ? ' ' . $this->msg( 'listusers-blocked', $userName )->escaped() : ''; + $blocked = !is_null( $row->ipb_deleted ) ? ' ' . $this->msg( 'listusers-blocked', $userName )->escaped() : ''; return Html::rawElement( 'li', array(), "{$item} [{$count}]{$blocked}" ); } @@ -240,4 +244,7 @@ class SpecialActiveUsers extends SpecialPage { } } + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialAllmessages.php b/includes/specials/SpecialAllmessages.php index fe9d41e5..a60c8efe 100644 --- a/includes/specials/SpecialAllmessages.php +++ b/includes/specials/SpecialAllmessages.php @@ -77,6 +77,9 @@ class SpecialAllmessages extends SpecialPage { } + protected function getGroupName() { + return 'wiki'; + } } /** @@ -113,12 +116,12 @@ class AllmessagesTablePager extends TablePager { $this->lang = ( $langObj ? $langObj : $wgContLang ); $this->langcode = $this->lang->getCode(); - $this->foreign = $this->langcode != $wgContLang->getCode(); + $this->foreign = $this->langcode != $wgContLang->getCode(); $request = $this->getRequest(); $this->filter = $request->getVal( 'filter', 'all' ); - if( $this->filter === 'all' ){ + if( $this->filter === 'all' ) { $this->custom = null; // So won't match in either case } else { $this->custom = ($this->filter == 'unmodified'); @@ -126,7 +129,7 @@ class AllmessagesTablePager extends TablePager { $prefix = $this->getLanguage()->ucfirst( $request->getVal( 'prefix', '' ) ); $prefix = $prefix != '' ? Title::makeTitleSafe( NS_MEDIAWIKI, $request->getVal( 'prefix', null ) ) : null; - if( $prefix !== null ){ + if( $prefix !== null ) { $this->displayPrefix = $prefix->getDBkey(); $this->prefix = '/^' . preg_quote( $this->displayPrefix ) . '/i'; } else { @@ -150,7 +153,7 @@ class AllmessagesTablePager extends TablePager { $msg = wfMessage( 'allmessages-language' ); $langSelect = Xml::languageSelector( $this->langcode, false, null, $attrs, $msg ); - $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-allmessages-form' ) ) . + $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-allmessages-form' ) ) . Xml::fieldset( $this->msg( 'allmessages-filter-legend' )->text() ) . Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . Xml::openElement( 'table', array( 'class' => 'mw-allmessages-table' ) ) . "\n" . @@ -216,7 +219,7 @@ class AllmessagesTablePager extends TablePager { function getAllMessages( $descending ) { wfProfileIn( __METHOD__ ); $messageNames = Language::getLocalisationCache()->getSubitemList( 'en', 'messages' ); - if( $descending ){ + if( $descending ) { rsort( $messageNames ); } else { asort( $messageNames ); @@ -331,15 +334,13 @@ class AllmessagesTablePager extends TablePager { </tr></thead><tbody>\n"; } - function formatValue( $field, $value ){ - switch( $field ){ - + function formatValue( $field, $value ) { + switch( $field ) { case 'am_title' : - $title = Title::makeTitle( NS_MEDIAWIKI, $value . $this->suffix ); - $talk = Title::makeTitle( NS_MEDIAWIKI_TALK, $value . $this->suffix ); + $talk = Title::makeTitle( NS_MEDIAWIKI_TALK, $value . $this->suffix ); - if( $this->mCurrentRow->am_customised ){ + if( $this->mCurrentRow->am_customised ) { $title = Linker::linkKnown( $title, $this->getLanguage()->lcfirst( $value ) ); } else { $title = Linker::link( @@ -351,7 +352,7 @@ class AllmessagesTablePager extends TablePager { ); } if ( $this->mCurrentRow->am_talk_exists ) { - $talk = Linker::linkKnown( $talk , $this->talk ); + $talk = Linker::linkKnown( $talk, $this->talk ); } else { $talk = Linker::link( $talk, @@ -370,12 +371,12 @@ class AllmessagesTablePager extends TablePager { return ''; } - function formatRow( $row ){ + function formatRow( $row ) { // Do all the normal stuff $s = parent::formatRow( $row ); // But if there's a customised message, add that too. - if( $row->am_customised ){ + if( $row->am_customised ) { $s .= Xml::openElement( 'tr', $this->getRowAttrs( $row, true ) ); $formatted = strval( $this->formatValue( 'am_actual', $row->am_actual ) ); if ( $formatted == '' ) { @@ -387,19 +388,19 @@ class AllmessagesTablePager extends TablePager { return $s; } - function getRowAttrs( $row, $isSecond = false ){ + function getRowAttrs( $row, $isSecond = false ) { $arr = array(); - if( $row->am_customised ){ + if( $row->am_customised ) { $arr['class'] = 'allmessages-customised'; } - if( !$isSecond ){ + if( !$isSecond ) { $arr['id'] = Sanitizer::escapeId( 'msg_' . $this->getLanguage()->lcfirst( $row->am_title ) ); } return $arr; } - function getCellAttrs( $field, $value ){ - if( $this->mCurrentRow->am_customised && $field == 'am_title' ){ + function getCellAttrs( $field, $value ) { + if( $this->mCurrentRow->am_customised && $field == 'am_title' ) { return array( 'rowspan' => '2', 'class' => $field ); } elseif( $field == 'am_title' ) { return array( 'class' => $field ); @@ -420,16 +421,15 @@ class AllmessagesTablePager extends TablePager { return SpecialPage::getTitleFor( 'Allmessages', false ); } - function isFieldSortable( $x ){ + function isFieldSortable( $x ) { return false; } - function getDefaultSort(){ + function getDefaultSort() { return ''; } - function getQueryInfo(){ + function getQueryInfo() { return ''; } } - diff --git a/includes/specials/SpecialAllpages.php b/includes/specials/SpecialAllpages.php index 0f8b2557..f9cb5cd8 100644 --- a/includes/specials/SpecialAllpages.php +++ b/includes/specials/SpecialAllpages.php @@ -59,16 +59,16 @@ class SpecialAllpages extends IncludableSpecialPage { /** * Constructor * - * @param $name string: name of the special page, as seen in links and URLs (default: 'Allpages') + * @param string $name name of the special page, as seen in links and URLs (default: 'Allpages') */ - function __construct( $name = 'Allpages' ){ + function __construct( $name = 'Allpages' ) { parent::__construct( $name ); } /** * Entry point : initialise variables and call subfunctions. * - * @param $par String: becomes "FOO" when called like Special:Allpages/FOO (default NULL) + * @param string $par becomes "FOO" when called like Special:Allpages/FOO (default NULL) */ function execute( $par ) { global $wgContLang; @@ -107,16 +107,16 @@ class SpecialAllpages extends IncludableSpecialPage { * HTML for the top form * * @param $namespace Integer: a namespace constant (default NS_MAIN). - * @param $from String: dbKey we are starting listing at. - * @param $to String: dbKey we are ending listing at. - * @param $hideredirects Bool: dont show redirects (default FALSE) + * @param string $from dbKey we are starting listing at. + * @param string $to dbKey we are ending listing at. + * @param bool $hideredirects dont show redirects (default FALSE) * @return string */ function namespaceForm( $namespace = NS_MAIN, $from = '', $to = '', $hideredirects = false ) { global $wgScript; $t = $this->getTitle(); - $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) ); + $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) ); $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); $out .= Html::hidden( 'title', $t->getPrefixedText() ); $out .= Xml::openElement( 'fieldset' ); @@ -127,7 +127,7 @@ class SpecialAllpages extends IncludableSpecialPage { Xml::label( $this->msg( 'allpagesfrom' )->text(), 'nsfrom' ) . " </td> <td class='mw-input'>" . - Xml::input( 'from', 30, str_replace('_',' ',$from), array( 'id' => 'nsfrom' ) ) . + Xml::input( 'from', 30, str_replace( '_', ' ', $from ), array( 'id' => 'nsfrom' ) ) . " </td> </tr> <tr> @@ -135,7 +135,7 @@ class SpecialAllpages extends IncludableSpecialPage { Xml::label( $this->msg( 'allpagesto' )->text(), 'nsto' ) . " </td> <td class='mw-input'>" . - Xml::input( 'to', 30, str_replace('_',' ',$to), array( 'id' => 'nsto' ) ) . + Xml::input( 'to', 30, str_replace( '_', ' ', $to ), array( 'id' => 'nsto' ) ) . " </td> </tr> <tr> @@ -165,9 +165,9 @@ class SpecialAllpages extends IncludableSpecialPage { /** * @param $namespace Integer (default NS_MAIN) - * @param $from String: list all pages from this name - * @param $to String: list all pages to this name - * @param $hideredirects Bool: dont show redirects (default FALSE) + * @param string $from list all pages from this name + * @param string $to list all pages to this name + * @param bool $hideredirects dont show redirects (default FALSE) */ function showToplevel( $namespace = NS_MAIN, $from = '', $to = '', $hideredirects = false ) { $output = $this->getOutput(); @@ -180,7 +180,7 @@ class SpecialAllpages extends IncludableSpecialPage { $where = array( 'page_namespace' => $namespace ); if ( $hideredirects ) { - $where[ 'page_is_redirect' ] = 0; + $where['page_is_redirect'] = 0; } $from = Title::makeTitleSafe( $namespace, $from ); @@ -188,18 +188,18 @@ class SpecialAllpages extends IncludableSpecialPage { $from = ( $from && $from->isLocal() ) ? $from->getDBkey() : null; $to = ( $to && $to->isLocal() ) ? $to->getDBkey() : null; - if( isset($from) ) - $where[] = 'page_title >= '.$dbr->addQuotes( $from ); - if( isset($to) ) - $where[] = 'page_title <= '.$dbr->addQuotes( $to ); + if( isset( $from ) ) + $where[] = 'page_title >= ' . $dbr->addQuotes( $from ); + if( isset( $to ) ) + $where[] = 'page_title <= ' . $dbr->addQuotes( $to ); global $wgMemc; - $key = wfMemcKey( 'allpages', 'ns', $namespace, $from, $to ); + $key = wfMemcKey( 'allpages', 'ns', $namespace, sha1( $from ), sha1( $to ) ); $lines = $wgMemc->get( $key ); $count = $dbr->estimateRowCount( 'page', '*', $where, __METHOD__ ); - $maxPerSubpage = intval($count/$this->maxLineCount); - $maxPerSubpage = max($maxPerSubpage,$this->maxPerPage); + $maxPerSubpage = intval( $count / $this->maxLineCount ); + $maxPerSubpage = max( $maxPerSubpage, $this->maxPerPage ); if( !is_array( $lines ) ) { $options = array( 'LIMIT' => 1 ); @@ -217,9 +217,9 @@ class SpecialAllpages extends IncludableSpecialPage { : array( 'page_title >= ' . $dbr->addQuotes( $lastTitle ) ); $res = $dbr->select( 'page', /* FROM */ 'page_title', /* WHAT */ - array_merge($where,$chunk), + array_merge( $where, $chunk ), __METHOD__, - array ('LIMIT' => 2, 'OFFSET' => $maxPerSubpage - 1, 'ORDER BY' => 'page_title ASC') + array( 'LIMIT' => 2, 'OFFSET' => $maxPerSubpage - 1, 'ORDER BY' => 'page_title ASC' ) ); $s = $dbr->fetchObject( $res ); @@ -228,7 +228,7 @@ class SpecialAllpages extends IncludableSpecialPage { } else { // Final chunk, but ended prematurely. Go back and find the end. $endTitle = $dbr->selectField( 'page', 'MAX(page_title)', - array_merge($where,$chunk), + array_merge( $where, $chunk ), __METHOD__ ); array_push( $lines, $endTitle ); $done = true; @@ -250,7 +250,7 @@ class SpecialAllpages extends IncludableSpecialPage { // If there are only two or less sections, don't even display them. // Instead, display the first section directly. if( count( $lines ) <= 2 ) { - if( !empty($lines) ) { + if( !empty( $lines ) ) { $this->showChunk( $namespace, $from, $to, $hideredirects ); } else { $output->addHTML( $this->namespaceForm( $namespace, $from, $to, $hideredirects ) ); @@ -272,7 +272,7 @@ class SpecialAllpages extends IncludableSpecialPage { if( $this->including() ) { $out2 = ''; } else { - if( isset($from) || isset($to) ) { + if( isset( $from ) || isset( $to ) ) { $out2 = Xml::openElement( 'table', array( 'class' => 'mw-allpages-table-form' ) ). '<tr> <td>' . @@ -294,10 +294,10 @@ class SpecialAllpages extends IncludableSpecialPage { /** * Show a line of "ABC to DEF" ranges of articles * - * @param $inpoint String: lower limit of pagenames - * @param $outpoint String: upper limit of pagenames + * @param string $inpoint lower limit of pagenames + * @param string $outpoint upper limit of pagenames * @param $namespace Integer (Default NS_MAIN) - * @param $hideredirects Bool: dont show redirects (default FALSE) + * @param bool $hideredirects dont show redirects (default FALSE) * @return string */ function showline( $inpoint, $outpoint, $namespace = NS_MAIN, $hideredirects ) { @@ -311,12 +311,12 @@ class SpecialAllpages extends IncludableSpecialPage { $queryparams = $namespace ? "namespace=$namespace&" : ''; $queryhideredirects = array(); - if ($hideredirects) { - $queryhideredirects[ 'hideredirects' ] = 1; + if ( $hideredirects ) { + $queryhideredirects['hideredirects'] = 1; } $special = $this->getTitle(); - $link = htmlspecialchars( $special->getLocalUrl( $queryparams . 'from=' . urlencode($inpoint) . '&to=' . urlencode($outpoint), $queryhideredirects ) ); + $link = htmlspecialchars( $special->getLocalUrl( $queryparams . 'from=' . urlencode( $inpoint ) . '&to=' . urlencode( $outpoint ), $queryhideredirects ) ); $out = $this->msg( 'alphaindexline' )->rawParams( "<a href=\"$link\">$inpointf</a></td><td>", @@ -327,15 +327,15 @@ class SpecialAllpages extends IncludableSpecialPage { /** * @param $namespace Integer (Default NS_MAIN) - * @param $from String: list all pages from this name (default FALSE) - * @param $to String: list all pages to this name (default FALSE) - * @param $hideredirects Bool: dont show redirects (default FALSE) + * @param string $from list all pages from this name (default FALSE) + * @param string $to list all pages to this name (default FALSE) + * @param bool $hideredirects dont show redirects (default FALSE) */ function showChunk( $namespace = NS_MAIN, $from = false, $to = false, $hideredirects = false ) { global $wgContLang; $output = $this->getOutput(); - $fromList = $this->getNamespaceKeyAndText($namespace, $from); + $fromList = $this->getNamespaceKeyAndText( $namespace, $from ); $toList = $this->getNamespaceKeyAndText( $namespace, $to ); $namespaces = $wgContLang->getNamespaces(); $n = 0; @@ -357,7 +357,7 @@ class SpecialAllpages extends IncludableSpecialPage { ); if ( $hideredirects ) { - $conds[ 'page_is_redirect' ] = 0; + $conds['page_is_redirect'] = 0; } if( $toKey !== "" ) { @@ -416,10 +416,10 @@ class SpecialAllpages extends IncludableSpecialPage { $res_prev = $dbr->select( 'page', 'page_title', - array( 'page_namespace' => $namespace, 'page_title < '.$dbr->addQuotes($from) ), + array( 'page_namespace' => $namespace, 'page_title < ' . $dbr->addQuotes( $from ) ), __METHOD__, array( 'ORDER BY' => 'page_title DESC', - 'LIMIT' => $this->maxPerPage, 'OFFSET' => ($this->maxPerPage - 1 ) + 'LIMIT' => $this->maxPerPage, 'OFFSET' => ( $this->maxPerPage - 1 ) ) ); @@ -438,7 +438,7 @@ class SpecialAllpages extends IncludableSpecialPage { array( 'page_namespace' => $namespace ), __METHOD__, $options ); # Show the previous link if it s not the current requested chunk if( $from != $reallyFirstPage_title ) { - $prevTitle = Title::makeTitle( $namespace, $reallyFirstPage_title ); + $prevTitle = Title::makeTitle( $namespace, $reallyFirstPage_title ); } else { $prevTitle = null; } @@ -457,7 +457,7 @@ class SpecialAllpages extends IncludableSpecialPage { Linker::link( $self, $this->msg( 'allpages' )->escaped() ); # Do we put a previous link ? - if( isset( $prevTitle ) && $pt = $prevTitle->getText() ) { + if( isset( $prevTitle ) && $pt = $prevTitle->getText() ) { $query = array( 'from' => $prevTitle->getText() ); if( $namespace ) @@ -477,7 +477,7 @@ class SpecialAllpages extends IncludableSpecialPage { if( $n == $this->maxPerPage && $s = $res->fetchObject() ) { # $s is the first link of the next chunk - $t = Title::makeTitle($namespace, $s->page_title); + $t = Title::makeTitle( $namespace, $s->page_title ); $query = array( 'from' => $t->getText() ); if( $namespace ) @@ -515,14 +515,14 @@ class SpecialAllpages extends IncludableSpecialPage { /** * @param $ns Integer: the namespace of the article - * @param $text String: the name of the article + * @param string $text the name of the article * @return array( int namespace, string dbkey, string pagename ) or NULL on error */ - protected function getNamespaceKeyAndText($ns, $text) { + protected function getNamespaceKeyAndText( $ns, $text ) { if ( $text == '' ) return array( $ns, '', '' ); # shortcut for common case - $t = Title::makeTitleSafe($ns, $text); + $t = Title::makeTitleSafe( $ns, $text ); if ( $t && $t->isLocal() ) { return array( $t->getNamespace(), $t->getDBkey(), $t->getText() ); } elseif ( $t ) { @@ -530,12 +530,16 @@ class SpecialAllpages extends IncludableSpecialPage { } # try again, in case the problem was an empty pagename - $text = preg_replace('/(#|$)/', 'X$1', $text); - $t = Title::makeTitleSafe($ns, $text); + $text = preg_replace( '/(#|$)/', 'X$1', $text ); + $t = Title::makeTitleSafe( $ns, $text ); if ( $t && $t->isLocal() ) { return array( $t->getNamespace(), '', '' ); } else { return null; } } + + protected function getGroupName() { + return 'pages'; + } } diff --git a/includes/specials/SpecialAncientpages.php b/includes/specials/SpecialAncientpages.php index 6e3d49bd..b0f333c4 100644 --- a/includes/specials/SpecialAncientpages.php +++ b/includes/specials/SpecialAncientpages.php @@ -36,7 +36,9 @@ class AncientPagesPage extends QueryPage { return true; } - function isSyndicated() { return false; } + function isSyndicated() { + return false; + } function getQueryInfo() { return array( @@ -69,4 +71,8 @@ class AncientPagesPage extends QueryPage { ); return $this->getLanguage()->specialList( $link, htmlspecialchars( $d ) ); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialBlankpage.php b/includes/specials/SpecialBlankpage.php index 42d33779..bfa2f951 100644 --- a/includes/specials/SpecialBlankpage.php +++ b/includes/specials/SpecialBlankpage.php @@ -33,6 +33,6 @@ class SpecialBlankpage extends UnlistedSpecialPage { } public function execute( $par ) { $this->setHeaders(); - $this->getOutput()->addWikiMsg('intentionallyblankpage'); + $this->getOutput()->addWikiMsg( 'intentionallyblankpage' ); } } diff --git a/includes/specials/SpecialBlock.php b/includes/specials/SpecialBlock.php index 1d6656ab..50fdbc26 100644 --- a/includes/specials/SpecialBlock.php +++ b/includes/specials/SpecialBlock.php @@ -62,7 +62,7 @@ class SpecialBlock extends FormSpecialPage { * @throws ErrorPageError */ protected function checkExecutePermissions( User $user ) { - parent::checkExecutePermissions( $user ); + parent::checkExecutePermissions( $user ); # bug 15810: blocked admins should have limited access here $status = self::checkUnblockSelf( $this->target, $user ); @@ -134,6 +134,7 @@ class SpecialBlock extends FormSpecialPage { 'tabindex' => '1', 'id' => 'mw-bi-target', 'size' => '45', + 'autofocus' => true, 'required' => true, 'validation-callback' => array( __CLASS__, 'validateTargetField' ), ), @@ -224,7 +225,7 @@ class SpecialBlock extends FormSpecialPage { /** * If the user has already been blocked with similar settings, load that block * and change the defaults for the form fields to match the existing settings. - * @param $fields Array HTMLForm descriptor array + * @param array $fields HTMLForm descriptor array * @return Bool whether fields were altered (that is, whether the target is * already blocked) */ @@ -239,7 +240,7 @@ class SpecialBlock extends FormSpecialPage { if ( $block instanceof Block && !$block->mAuto # The block exists and isn't an autoblock && ( $this->type != Block::TYPE_RANGE # The block isn't a rangeblock - || $block->getTarget() == $this->target ) # or if it is, the range is what we're about to block + || $block->getTarget() == $this->target ) # or if it is, the range is what we're about to block ) { $fields['HardBlock']['default'] = $block->isHardblock(); @@ -386,7 +387,7 @@ class SpecialBlock extends FormSpecialPage { ); } - $text = Html::rawElement( + $text = Html::rawElement( 'p', array( 'class' => 'mw-ipb-conveniencelinks' ), $this->getLanguage()->pipeList( $links ) @@ -450,7 +451,7 @@ class SpecialBlock extends FormSpecialPage { /** * Determine the target of the block, and the type of target * TODO: should be in Block.php? - * @param $par String subpage parameter passed to setup, or data value from + * @param string $par subpage parameter passed to setup, or data value from * the HTMLForm * @param $request WebRequest optionally try and get data from a request too * @return array( User|string|null, Block::TYPE_ constant|null ) @@ -507,53 +508,74 @@ class SpecialBlock extends FormSpecialPage { * @return Message */ public static function validateTargetField( $value, $alldata, $form ) { + $status = self::validateTarget( $value, $form->getUser() ); + if ( !$status->isOK() ) { + $errors = $status->getErrorsArray(); + return call_user_func_array( array( $form, 'msg' ), $errors[0] ); + } else { + return true; + } + } + + /** + * Validate a block target. + * + * @since 1.21 + * @param string $value Block target to check + * @param User $user Performer of the block + * @return Status + */ + public static function validateTarget( $value, User $user ) { global $wgBlockCIDRLimit; list( $target, $type ) = self::getTargetAndType( $value ); + $status = Status::newGood( $target ); if ( $type == Block::TYPE_USER ) { - # TODO: why do we not have a User->exists() method? - if ( !$target->getId() ) { - return $form->msg( 'nosuchusershort', - wfEscapeWikiText( $target->getName() ) ); + if ( $target->isAnon() ) { + $status->fatal( + 'nosuchusershort', + wfEscapeWikiText( $target->getName() ) + ); } - $status = self::checkUnblockSelf( $target, $form->getUser() ); - if ( $status !== true ) { - return $form->msg( 'badaccess', $status ); + $unblockStatus = self::checkUnblockSelf( $target, $user ); + if ( $unblockStatus !== true ) { + $status->fatal( 'badaccess', $unblockStatus ); } - } elseif ( $type == Block::TYPE_RANGE ) { list( $ip, $range ) = explode( '/', $target, 2 ); - if ( ( IP::isIPv4( $ip ) && $wgBlockCIDRLimit['IPv4'] == 32 ) - || ( IP::isIPv6( $ip ) && $wgBlockCIDRLimit['IPv6'] == 128 ) ) - { - # Range block effectively disabled - return $form->msg( 'range_block_disabled' ); + if ( + ( IP::isIPv4( $ip ) && $wgBlockCIDRLimit['IPv4'] == 32 ) || + ( IP::isIPv6( $ip ) && $wgBlockCIDRLimit['IPv6'] == 128 ) + ) { + // Range block effectively disabled + $status->fatal( 'range_block_disabled' ); } - if ( ( IP::isIPv4( $ip ) && $range > 32 ) - || ( IP::isIPv6( $ip ) && $range > 128 ) ) - { - # Dodgy range - return $form->msg( 'ip_range_invalid' ); + if ( + ( IP::isIPv4( $ip ) && $range > 32 ) || + ( IP::isIPv6( $ip ) && $range > 128 ) + ) { + // Dodgy range + $status->fatal( 'ip_range_invalid' ); } if ( IP::isIPv4( $ip ) && $range < $wgBlockCIDRLimit['IPv4'] ) { - return $form->msg( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv4'] ); + $status->fatal( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv4'] ); } if ( IP::isIPv6( $ip ) && $range < $wgBlockCIDRLimit['IPv6'] ) { - return $form->msg( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv6'] ); + $status->fatal( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv6'] ); } } elseif ( $type == Block::TYPE_IP ) { # All is well } else { - return $form->msg( 'badipaddress' ); + $status->fatal( 'badipaddress' ); } - return true; + return $status; } /** @@ -629,7 +651,7 @@ class SpecialBlock extends FormSpecialPage { } if ( $data['HideUser'] ) { - if ( !$performer->isAllowed('hideuser') ) { + if ( !$performer->isAllowed( 'hideuser' ) ) { # this codepath is unreachable except by a malicious user spoofing forms, # or by race conditions (user has oversight and sysop, loads block form, # and is de-oversighted before submission); so need to fail completely @@ -672,10 +694,16 @@ class SpecialBlock extends FormSpecialPage { # Try to insert block. Is there a conflicting block? $status = $block->insert(); if ( !$status ) { + # Indicates whether the user is confirming the block and is aware of + # the conflict (did not change the block target in the meantime) + $blockNotConfirmed = !$data['Confirm'] || ( array_key_exists( 'PreviousTarget', $data ) + && $data['PreviousTarget'] !== $target ); + + # Special case for API - bug 32434 + $reblockNotAllowed = ( array_key_exists( 'Reblock', $data ) && !$data['Reblock'] ); + # Show form unless the user is already aware of this... - if ( !$data['Confirm'] || ( array_key_exists( 'PreviousTarget', $data ) - && $data['PreviousTarget'] !== $target ) ) - { + if( $blockNotConfirmed || $reblockNotAllowed ) { return array( array( 'ipb_already_blocked', $block->getTarget() ) ); # Otherwise, try to update the block... } else { @@ -742,7 +770,7 @@ class SpecialBlock extends FormSpecialPage { $logParams ); # Relate log ID to block IDs (bug 25763) - $blockIds = array_merge( array( $status['id'] ), $status['autoIds'] ); + $blockIds = array_merge( array( $status['id'] ), $status['autoIds'] ); $log->addRelations( 'ipb_id', $blockIds, $log_id ); # Report to the user @@ -782,7 +810,7 @@ class SpecialBlock extends FormSpecialPage { /** * Convert a submitted expiry time, which may be relative ("2 weeks", etc) or absolute * ("24 May 2034", etc), into an absolute timestamp we can put into the database. - * @param $expiry String: whatever was typed into the form + * @param string $expiry whatever was typed into the form * @return String: timestamp or "infinity" string for the DB implementation */ public static function parseExpiryInput( $expiry ) { @@ -855,7 +883,7 @@ class SpecialBlock extends FormSpecialPage { /** * Return a comma-delimited list of "flags" to be passed to the log * reader for this block, to provide more information in the logs - * @param $data Array from HTMLForm data + * @param array $data from HTMLForm data * @param $type Block::TYPE_ constant (USER, RANGE, or IP) * @return string */ @@ -918,6 +946,10 @@ class SpecialBlock extends FormSpecialPage { $out->setPageTitle( $this->msg( 'blockipsuccesssub' ) ); $out->addWikiMsg( 'blockipsuccesstext', wfEscapeWikiText( $this->target ) ); } + + protected function getGroupName() { + return 'users'; + } } # BC @since 1.18 diff --git a/includes/specials/SpecialBlockList.php b/includes/specials/SpecialBlockList.php index 7143d5bc..e10df4fe 100644 --- a/includes/specials/SpecialBlockList.php +++ b/includes/specials/SpecialBlockList.php @@ -37,7 +37,7 @@ class SpecialBlockList extends SpecialPage { /** * Main execution point * - * @param $par String title fragment + * @param string $par title fragment */ public function execute( $par ) { $this->setHeaders(); @@ -113,14 +113,14 @@ class SpecialBlockList extends SpecialPage { $conds = array(); # Is the user allowed to see hidden blocks? - if ( !$this->getUser()->isAllowed( 'hideuser' ) ){ + if ( !$this->getUser()->isAllowed( 'hideuser' ) ) { $conds['ipb_deleted'] = 0; } - if ( $this->target !== '' ){ + if ( $this->target !== '' ) { list( $target, $type ) = Block::parseTarget( $this->target ); - switch( $type ){ + switch( $type ) { case Block::TYPE_ID: case Block::TYPE_AUTO: $conds['ipb_id'] = $target; @@ -205,6 +205,10 @@ class SpecialBlockList extends SpecialPage { $out->addHTML( Html::rawElement( 'ul', array( 'class' => 'mw-ipblocklist-otherblocks' ), $list ) . "\n" ); } } + + protected function getGroupName() { + return 'users'; + } } class BlockListPager extends TablePager { @@ -269,11 +273,11 @@ class BlockListPager extends TablePager { break; case 'ipb_target': - if( $row->ipb_auto ){ + if( $row->ipb_auto ) { $formatted = $this->msg( 'autoblockid', $row->ipb_id )->parse(); } else { list( $target, $type ) = Block::parseTarget( $row->ipb_address ); - switch( $type ){ + switch( $type ) { case Block::TYPE_USER: case Block::TYPE_IP: $formatted = Linker::userLink( $target->getId(), $target ); @@ -292,8 +296,8 @@ class BlockListPager extends TablePager { case 'ipb_expiry': $formatted = $this->getLanguage()->formatExpiry( $value, /* User preference timezone */ true ); - if( $this->getUser()->isAllowed( 'block' ) ){ - if( $row->ipb_auto ){ + if( $this->getUser()->isAllowed( 'block' ) ) { + if( $row->ipb_auto ) { $links[] = Linker::linkKnown( SpecialPage::getTitleFor( 'Unblock' ), $msg['unblocklink'], @@ -329,7 +333,7 @@ class BlockListPager extends TablePager { break; case 'ipb_reason': - $formatted = Linker::commentBlock( $value ); + $formatted = Linker::formatComment( $value ); break; case 'ipb_params': @@ -391,14 +395,14 @@ class BlockListPager extends TablePager { ); # Is the user allowed to see hidden blocks? - if ( !$this->getUser()->isAllowed( 'hideuser' ) ){ + if ( !$this->getUser()->isAllowed( 'hideuser' ) ) { $info['conds']['ipb_deleted'] = 0; } return $info; } - public function getTableClass(){ + public function getTableClass() { return 'TablePager mw-blocklist'; } @@ -418,7 +422,7 @@ class BlockListPager extends TablePager { * Do a LinkBatch query to minimise database load when generating all these links * @param $result */ - function preprocessResults( $result ){ + function preprocessResults( $result ) { wfProfileIn( __METHOD__ ); # Do a link batch query $lb = new LinkBatch; @@ -437,11 +441,11 @@ class BlockListPager extends TablePager { } $ua = UserArray::newFromIDs( $userids ); - foreach( $ua as $user ){ + foreach( $ua as $user ) { $name = str_replace( ' ', '_', $user->getName() ); $lb->add( NS_USER, $name ); $lb->add( NS_USER_TALK, $name ); - } + } $lb->execute(); wfProfileOut( __METHOD__ ); @@ -472,7 +476,7 @@ class HTMLBlockedUsersItemSelect extends HTMLSelectField { // This adds the explicitly requested limit value to the drop-down, // then makes sure it's sorted correctly so when we output the list // later, the custom option doesn't just show up last. - $this->mParams['options'][ $this->mParent->getLanguage()->formatNum( $value ) ] = intval($value); + $this->mParams['options'][$this->mParent->getLanguage()->formatNum( $value )] = intval( $value ); asort( $this->mParams['options'] ); } diff --git a/includes/specials/SpecialBlockme.php b/includes/specials/SpecialBlockme.php index 3840b2ff..85a3019e 100644 --- a/includes/specials/SpecialBlockme.php +++ b/includes/specials/SpecialBlockme.php @@ -22,7 +22,7 @@ */ /** - * A special page called by proxy_check.php to block open proxies + * A special page called by proxyCheck.php to block open proxies * * @ingroup SpecialPage */ @@ -59,4 +59,8 @@ class SpecialBlockme extends UnlistedSpecialPage { $this->getOutput()->addWikiMsg( 'proxyblocksuccess' ); } + + protected function getGroupName() { + return 'other'; + } } diff --git a/includes/specials/SpecialBooksources.php b/includes/specials/SpecialBooksources.php index bf7de3f5..bdbd77b8 100644 --- a/includes/specials/SpecialBooksources.php +++ b/includes/specials/SpecialBooksources.php @@ -46,7 +46,7 @@ class SpecialBookSources extends SpecialPage { /** * Show the special page * - * @param $isbn string ISBN passed as a subpage parameter + * @param string $isbn ISBN passed as a subpage parameter */ public function execute( $isbn ) { $this->setHeaders(); @@ -62,8 +62,8 @@ class SpecialBookSources extends SpecialPage { } /** - * Returns whether a given ISBN (10 or 13) is valid. True indicates validity. - * @param isbn string ISBN passed for check + * Returns whether a given ISBN (10 or 13) is valid. True indicates validity. + * @param string $isbn ISBN passed for check * @return bool */ public static function isValidISBN( $isbn ) { @@ -71,7 +71,7 @@ class SpecialBookSources extends SpecialPage { $sum = 0; if( strlen( $isbn ) == 13 ) { for( $i = 0; $i < 12; $i++ ) { - if($i % 2 == 0) { + if( $i % 2 == 0 ) { $sum += $isbn[$i]; } else { $sum += 3 * $isbn[$i]; @@ -79,19 +79,19 @@ class SpecialBookSources extends SpecialPage { } $check = (10 - ($sum % 10)) % 10; - if ($check == $isbn[12]) { + if ( $check == $isbn[12] ) { return true; } } elseif( strlen( $isbn ) == 10 ) { - for($i = 0; $i < 9; $i++) { + for( $i = 0; $i < 9; $i++ ) { $sum += $isbn[$i] * ($i + 1); } $check = $sum % 11; - if($check == 10) { + if( $check == 10 ) { $check = "X"; } - if($check == $isbn[9]) { + if( $check == $isbn[9] ) { return true; } } @@ -101,7 +101,7 @@ class SpecialBookSources extends SpecialPage { /** * Trim ISBN and remove characters which aren't required * - * @param $isbn string Unclean ISBN + * @param string $isbn Unclean ISBN * @return string */ private static function cleanIsbn( $isbn ) { @@ -116,7 +116,7 @@ class SpecialBookSources extends SpecialPage { private function makeForm() { global $wgScript; - $form = '<fieldset><legend>' . $this->msg( 'booksources-search-legend' )->escaped() . '</legend>'; + $form = '<fieldset><legend>' . $this->msg( 'booksources-search-legend' )->escaped() . '</legend>'; $form .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); $form .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ); $form .= '<p>' . Xml::inputLabel( $this->msg( 'booksources-isbn' )->text(), 'isbn', 'isbn', 20, $this->isbn ); @@ -130,6 +130,7 @@ class SpecialBookSources extends SpecialPage { * Determine where to get the list of book sources from, * format and output them * + * @throws MWException * @return string */ private function showList() { @@ -144,8 +145,17 @@ class SpecialBookSources extends SpecialPage { $title = Title::makeTitleSafe( NS_PROJECT, $page ); # Show list in content language if( is_object( $title ) && $title->exists() ) { $rev = Revision::newFromTitle( $title, false, Revision::READ_NORMAL ); - $this->getOutput()->addWikiText( str_replace( 'MAGICNUMBER', $this->isbn, $rev->getText() ) ); - return true; + $content = $rev->getContent(); + + if ( $content instanceof TextContent ) { + //XXX: in the future, this could be stored as structured data, defining a list of book sources + + $text = $content->getNativeData(); + $this->getOutput()->addWikiText( str_replace( 'MAGICNUMBER', $this->isbn, $text ) ); + return true; + } else { + throw new MWException( "Unexpected content type for book sources: " . $content->getModel() ); + } } # Fall back to the defaults given in the language file @@ -161,12 +171,16 @@ class SpecialBookSources extends SpecialPage { /** * Format a book source list item * - * @param $label string Book source label - * @param $url string Book source URL + * @param string $label Book source label + * @param string $url Book source URL * @return string */ private function makeListItem( $label, $url ) { $url = str_replace( '$1', $this->isbn, $url ); return '<li><a href="' . htmlspecialchars( $url ) . '" class="external">' . htmlspecialchars( $label ) . '</a></li>'; } + + protected function getGroupName() { + return 'other'; + } } diff --git a/includes/specials/SpecialBrokenRedirects.php b/includes/specials/SpecialBrokenRedirects.php index 8119e6d1..fac41236 100644 --- a/includes/specials/SpecialBrokenRedirects.php +++ b/includes/specials/SpecialBrokenRedirects.php @@ -33,35 +33,54 @@ class BrokenRedirectsPage extends QueryPage { parent::__construct( $name ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } - function sortDescending() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function sortDescending() { + return false; + } function getPageHeader() { return $this->msg( 'brokenredirectstext' )->parseAsBlock(); } function getQueryInfo() { + $dbr = wfGetDB( DB_SLAVE ); return array( - 'tables' => array( 'redirect', 'p1' => 'page', - 'p2' => 'page' ), - 'fields' => array( 'namespace' => 'p1.page_namespace', - 'title' => 'p1.page_title', - 'value' => 'p1.page_title', - 'rd_namespace', - 'rd_title' + 'tables' => array( + 'redirect', + 'p1' => 'page', + 'p2' => 'page', ), - 'conds' => array( 'rd_namespace >= 0', - 'p2.page_namespace IS NULL' + 'fields' => array( + 'namespace' => 'p1.page_namespace', + 'title' => 'p1.page_title', + 'value' => 'p1.page_title', + 'rd_namespace', + 'rd_title', + ), + 'conds' => array( + // Exclude pages that don't exist locally as wiki pages, + // but aren't "broken" either. + // Special pages and interwiki links + 'rd_namespace >= 0', + 'rd_interwiki IS NULL OR rd_interwiki = ' . $dbr->addQuotes( '' ), + 'p2.page_namespace IS NULL', + ), + 'join_conds' => array( + 'p1' => array( 'JOIN', array( + 'rd_from=p1.page_id', + ) ), + 'p2' => array( 'LEFT JOIN', array( + 'rd_namespace=p2.page_namespace', + 'rd_title=p2.page_title' + ) ), ), - 'join_conds' => array( 'p1' => array( 'JOIN', array( - 'rd_from=p1.page_id', - ) ), - 'p2' => array( 'LEFT JOIN', array( - 'rd_namespace=p2.page_namespace', - 'rd_title=p2.page_title' - ) ) - ) ); } @@ -132,4 +151,8 @@ class BrokenRedirectsPage extends QueryPage { $out .= " {$arr} {$to}"; return $out; } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialCachedPage.php b/includes/specials/SpecialCachedPage.php index b3f6c720..ddd11ad0 100644 --- a/includes/specials/SpecialCachedPage.php +++ b/includes/specials/SpecialCachedPage.php @@ -119,7 +119,7 @@ abstract class SpecialCachedPage extends SpecialPage implements ICacheHelper { * * @since 1.20 * - * @param {function} $computeFunction + * @param callable $computeFunction * @param array|mixed $args * @param string|null $key * @@ -137,7 +137,7 @@ abstract class SpecialCachedPage extends SpecialPage implements ICacheHelper { * * @since 1.20 * - * @param {function} $computeFunction + * @param callable $computeFunction * @param array $args * @param string|null $key */ diff --git a/includes/specials/SpecialCategories.php b/includes/specials/SpecialCategories.php index 1232e3fa..9040c640 100644 --- a/includes/specials/SpecialCategories.php +++ b/includes/specials/SpecialCategories.php @@ -50,6 +50,10 @@ class SpecialCategories extends SpecialPage { Html::closeElement( 'div' ) ); } + + protected function getGroupName() { + return 'pages'; + } } /** @@ -72,7 +76,7 @@ class CategoryPager extends AlphabeticPager { function getQueryInfo() { return array( 'tables' => array( 'category' ), - 'fields' => array( 'cat_title','cat_pages' ), + 'fields' => array( 'cat_title', 'cat_pages' ), 'conds' => array( 'cat_pages > 0' ), 'options' => array( 'USE INDEX' => 'cat_title' ), ); @@ -112,7 +116,7 @@ class CategoryPager extends AlphabeticPager { return parent::getBody(); } - function formatRow($result) { + function formatRow( $result ) { $title = Title::makeTitle( NS_CATEGORY, $result->cat_title ); $titleText = Linker::link( $title, htmlspecialchars( $title->getText() ) ); $count = $this->msg( 'nmembers' )->numParams( $result->cat_pages )->escaped(); diff --git a/includes/specials/SpecialChangeEmail.php b/includes/specials/SpecialChangeEmail.php index fc726106..59a02578 100644 --- a/includes/specials/SpecialChangeEmail.php +++ b/includes/specials/SpecialChangeEmail.php @@ -151,10 +151,10 @@ class SpecialChangeEmail extends UnlistedSpecialPage { $items = array( array( 'wpName', 'username', 'text', $user->getName() ), array( 'wpOldEmail', 'changeemail-oldemail', 'text', $oldEmailText ), - array( 'wpNewEmail', 'changeemail-newemail', 'input', $this->mNewEmail ), + array( 'wpNewEmail', 'changeemail-newemail', 'email', $this->mNewEmail ), ); if ( $wgRequirePasswordforEmailChange ) { - $items[] = array( 'wpPassword', 'yourpassword', 'password', $this->mPassword ); + $items[] = array( 'wpPassword', 'changeemail-password', 'password', $this->mPassword ); } $this->getOutput()->addHTML( @@ -195,7 +195,7 @@ class SpecialChangeEmail extends UnlistedSpecialPage { if ( $type != 'text' ) { $out .= Xml::label( $this->msg( $label )->text(), $name ); } else { - $out .= $this->msg( $label )->escaped(); + $out .= $this->msg( $label )->escaped(); } $out .= "</td>\n"; $out .= "\t<td class='mw-input'>"; @@ -213,6 +213,8 @@ class SpecialChangeEmail extends UnlistedSpecialPage { * @return bool|string true or string on success, false on failure */ protected function attemptChange( User $user, $pass, $newaddr ) { + global $wgAuth; + if ( $newaddr != '' && !Sanitizer::validateEmail( $newaddr ) ) { $this->error( 'invalidemailaddress' ); return false; @@ -248,6 +250,12 @@ class SpecialChangeEmail extends UnlistedSpecialPage { $user->saveSettings(); + $wgAuth->updateExternalDB( $user ); + return $status->value; } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialChangePassword.php b/includes/specials/SpecialChangePassword.php index ba728ac2..e538caca 100644 --- a/includes/specials/SpecialChangePassword.php +++ b/includes/specials/SpecialChangePassword.php @@ -27,6 +27,9 @@ * @ingroup SpecialPage */ class SpecialChangePassword extends UnlistedSpecialPage { + + protected $mUserName, $mOldpass, $mNewpass, $mRetype, $mDomain; + public function __construct() { parent::__construct( 'ChangePassword' ); } @@ -70,8 +73,10 @@ class SpecialChangePassword extends UnlistedSpecialPage { } $this->attemptReset( $this->mNewpass, $this->mRetype ); - $this->getOutput()->addWikiMsg( 'resetpass_success' ); - if( !$user->isLoggedIn() ) { + + if( $user->isLoggedIn() ) { + $this->doReturnTo(); + } else { LoginForm::setLoginToken(); $token = LoginForm::getLoginToken(); $data = array( @@ -79,17 +84,13 @@ class SpecialChangePassword extends UnlistedSpecialPage { 'wpName' => $this->mUserName, 'wpDomain' => $this->mDomain, 'wpLoginToken' => $token, - 'wpPassword' => $this->mNewpass, - 'returnto' => $request->getVal( 'returnto' ), - ); - if( $request->getCheck( 'wpRemember' ) ) { - $data['wpRemember'] = 1; - } + 'wpPassword' => $request->getVal( 'wpNewPassword' ), + ) + $request->getValues( 'wpRemember', 'returnto', 'returntoquery' ); $login = new LoginForm( new FauxRequest( $data, true ) ); $login->setContext( $this->getContext() ); $login->execute( null ); } - $this->doReturnTo(); + return; } catch( PasswordError $e ) { $this->error( $e->getMessage() ); } @@ -98,15 +99,20 @@ class SpecialChangePassword extends UnlistedSpecialPage { } function doReturnTo() { - $titleObj = Title::newFromText( $this->getRequest()->getVal( 'returnto' ) ); + $request = $this->getRequest(); + $titleObj = Title::newFromText( $request->getVal( 'returnto' ) ); if ( !$titleObj instanceof Title ) { $titleObj = Title::newMainPage(); } - $this->getOutput()->redirect( $titleObj->getFullURL() ); + $query = $request->getVal( 'returntoquery' ); + $this->getOutput()->redirect( $titleObj->getFullURL( $query ) ); } + /** + * @param $msg string + */ function error( $msg ) { - $this->getOutput()->addHTML( Xml::element('p', array( 'class' => 'error' ), $msg ) ); + $this->getOutput()->addHTML( Xml::element( 'p', array( 'class' => 'error' ), $msg ) ); } function showForm() { @@ -142,6 +148,15 @@ class SpecialChangePassword extends UnlistedSpecialPage { array( 'wpRetype', 'retypenew', 'password', null ), ); $prettyFields = array_merge( $prettyFields, $extraFields ); + $hiddenFields = array( + 'token' => $user->getEditToken(), + 'wpName' => $this->mUserName, + 'wpDomain' => $this->mDomain, + ) + $this->getRequest()->getValues( 'returnto', 'returntoquery' ); + $hiddenFieldsStr = ''; + foreach( $hiddenFields as $fieldname => $fieldvalue ) { + $hiddenFieldsStr .= Html::hidden( $fieldname, $fieldvalue ) . "\n"; + } $this->getOutput()->addHTML( Xml::fieldset( $this->msg( 'resetpass_header' )->text() ) . Xml::openElement( 'form', @@ -149,10 +164,7 @@ class SpecialChangePassword extends UnlistedSpecialPage { 'method' => 'post', 'action' => $this->getTitle()->getLocalUrl(), 'id' => 'mw-resetpass-form' ) ) . "\n" . - Html::hidden( 'token', $user->getEditToken() ) . "\n" . - Html::hidden( 'wpName', $this->mUserName ) . "\n" . - Html::hidden( 'wpDomain', $this->mDomain ) . "\n" . - Html::hidden( 'returnto', $this->getRequest()->getVal( 'returnto' ) ) . "\n" . + $hiddenFieldsStr . $this->msg( 'resetpass_text' )->parseAsBlock() . "\n" . Xml::openElement( 'table', array( 'id' => 'mw-resetpass-table' ) ) . "\n" . $this->pretty( $prettyFields ) . "\n" . @@ -170,6 +182,10 @@ class SpecialChangePassword extends UnlistedSpecialPage { ); } + /** + * @param $fields array + * @return string + */ function pretty( $fields ) { $out = ''; foreach ( $fields as $list ) { @@ -192,7 +208,7 @@ class SpecialChangePassword extends UnlistedSpecialPage { if ( $type != 'text' ) $out .= Xml::label( $this->msg( $label )->text(), $name ); else - $out .= $this->msg( $label )->escaped(); + $out .= $this->msg( $label )->escaped(); $out .= "</td>\n"; $out .= "\t<td class='mw-input'>"; $out .= $field; @@ -206,7 +222,13 @@ class SpecialChangePassword extends UnlistedSpecialPage { * @throws PasswordError when cannot set the new password because requirements not met. */ protected function attemptReset( $newpass, $retype ) { - $user = User::newFromName( $this->mUserName ); + $isSelf = ( $this->mUserName === $this->getUser()->getName() ); + if ( $isSelf ) { + $user = $this->getUser(); + } else { + $user = User::newFromName( $this->mUserName ); + } + if( !$user || $user->isAnon() ) { throw new PasswordError( $this->msg( 'nosuchusershort', $this->mUserName )->text() ); } @@ -227,7 +249,7 @@ class SpecialChangePassword extends UnlistedSpecialPage { throw new PasswordError( $this->msg( $abortMsg )->text() ); } - if( !$user->checkTemporaryPassword($this->mOldpass) && !$user->checkPassword($this->mOldpass) ) { + if( !$user->checkTemporaryPassword( $this->mOldpass ) && !$user->checkPassword( $this->mOldpass ) ) { wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) ); throw new PasswordError( $this->msg( 'resetpass-wrong-oldpass' )->text() ); } @@ -240,13 +262,22 @@ class SpecialChangePassword extends UnlistedSpecialPage { try { $user->setPassword( $this->mNewpass ); wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) ); - $this->mNewpass = $this->mOldpass = $this->mRetypePass = ''; + $this->mNewpass = $this->mOldpass = $this->mRetype = ''; } catch( PasswordError $e ) { wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) ); throw new PasswordError( $e->getMessage() ); } - $user->setCookies(); + if ( $isSelf ) { + // This is needed to keep the user connected since + // changing the password also modifies the user's token. + $user->setCookies(); + } + $user->saveSettings(); } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialComparePages.php b/includes/specials/SpecialComparePages.php index 9e3c52b9..c3bd3fec 100644 --- a/includes/specials/SpecialComparePages.php +++ b/includes/specials/SpecialComparePages.php @@ -106,28 +106,33 @@ class SpecialComparePages extends SpecialPage { $form->trySubmit(); } - public static function showDiff( $data, HTMLForm $form ){ + public static function showDiff( $data, HTMLForm $form ) { $rev1 = self::revOrTitle( $data['Revision1'], $data['Page1'] ); $rev2 = self::revOrTitle( $data['Revision2'], $data['Page2'] ); if( $rev1 && $rev2 ) { - $de = new DifferenceEngine( $form->getContext(), - $rev1, - $rev2, - null, // rcid - ( $data['Action'] == 'purge' ), - ( $data['Unhide'] == '1' ) - ); - $de->showDiffPage( true ); + $revision = Revision::newFromId( $rev1 ); + + if ( $revision ) { // NOTE: $rev1 was already checked, should exist. + $contentHandler = $revision->getContentHandler(); + $de = $contentHandler->createDifferenceEngine( $form->getContext(), + $rev1, + $rev2, + null, // rcid + ( $data['Action'] == 'purge' ), + ( $data['Unhide'] == '1' ) + ); + $de->showDiffPage( true ); + } } } public static function revOrTitle( $revision, $title ) { - if( $revision ){ + if( $revision ) { return $revision; } elseif( $title ) { $title = Title::newFromText( $title ); - if( $title instanceof Title ){ + if( $title instanceof Title ) { return $title->getLatestRevID(); } } @@ -158,4 +163,8 @@ class SpecialComparePages extends SpecialPage { } return true; } + + protected function getGroupName() { + return 'pagetools'; + } } diff --git a/includes/specials/SpecialConfirmemail.php b/includes/specials/SpecialConfirmemail.php index 3e9ce128..078c3865 100644 --- a/includes/specials/SpecialConfirmemail.php +++ b/includes/specials/SpecialConfirmemail.php @@ -98,7 +98,7 @@ class EmailConfirmation extends UnlistedSpecialPage { $out->wrapWikiMsg( "<div class=\"error mw-confirmemail-pending\">\n$1\n</div>", 'confirmemail_pending' ); } $out->addWikiMsg( 'confirmemail_text' ); - $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalUrl() ) ); + $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalUrl() ) ); $form .= Html::hidden( 'token', $user->getEditToken() ); $form .= Xml::submitButton( $this->msg( 'confirmemail_send' )->text() ); $form .= Xml::closeElement( 'form' ); @@ -110,7 +110,7 @@ class EmailConfirmation extends UnlistedSpecialPage { * Attempt to confirm the user's email address and show success or failure * as needed; if successful, take the user to log in * - * @param $code string Confirmation code + * @param string $code Confirmation code */ function attemptConfirm( $code ) { $user = User::newFromConfirmationCode( $code ); @@ -145,9 +145,7 @@ class EmailInvalidation extends UnlistedSpecialPage { function execute( $code ) { $this->setHeaders(); - if ( wfReadOnly() ) { - throw new ReadOnlyError; - } + $this->checkReadOnly(); $this->attemptInvalidate( $code ); } @@ -156,7 +154,7 @@ class EmailInvalidation extends UnlistedSpecialPage { * Attempt to invalidate the user's email address and show success or failure * as needed; if successful, link to main page * - * @param $code string Confirmation code + * @param string $code Confirmation code */ function attemptInvalidate( $code ) { $user = User::newFromConfirmationCode( $code ); diff --git a/includes/specials/SpecialContributions.php b/includes/specials/SpecialContributions.php index 54f8e261..b118059c 100644 --- a/includes/specials/SpecialContributions.php +++ b/includes/specials/SpecialContributions.php @@ -152,7 +152,7 @@ class SpecialContributions extends SpecialPage { $apiParams['month'] = $this->opts['month']; } - $url = wfScript( 'api' ) . '?' . wfArrayToCGI( $apiParams ); + $url = wfScript( 'api' ) . '?' . wfArrayToCgi( $apiParams ); $out->redirect( $url, '301' ); return; @@ -192,7 +192,6 @@ class SpecialContributions extends SpecialPage { } $out->preventClickjacking( $pager->getPreventClickjacking() ); - # Show the appropriate "footer" message - WHOIS tools, etc. if ( $this->opts['contribs'] == 'newbie' ) { $message = 'sp-contributions-footer-newbies'; @@ -360,7 +359,7 @@ class SpecialContributions extends SpecialPage { if ( !isset( $this->opts['target'] ) ) { $this->opts['target'] = ''; } else { - $this->opts['target'] = str_replace( '_' , ' ' , $this->opts['target'] ); + $this->opts['target'] = str_replace( '_', ' ', $this->opts['target'] ); } if ( !isset( $this->opts['namespace'] ) ) { @@ -399,7 +398,7 @@ class SpecialContributions extends SpecialPage { $this->opts['topOnly'] = false; } - $form = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'class' => 'mw-contributions-form' ) ); + $form = Html::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'class' => 'mw-contributions-form' ) ); # Add hidden params for tracking except for parameters in $skipParameters $skipParameters = array( 'namespace', 'nsInvert', 'deletedOnly', 'target', 'contribs', 'year', 'month', 'topOnly', 'associated' ); @@ -414,17 +413,17 @@ class SpecialContributions extends SpecialPage { if ( $tagFilter ) { $filterSelection = - Xml::tags( 'td', array( 'class' => 'mw-label' ), array_shift( $tagFilter ) ) . - Xml::tags( 'td', array( 'class' => 'mw-input' ), implode( ' ', $tagFilter ) ); + Html::rawElement( 'td', array( 'class' => 'mw-label' ), array_shift( $tagFilter ) ) . + Html::rawElement( 'td', array( 'class' => 'mw-input' ), implode( ' ', $tagFilter ) ); } else { - $filterSelection = Xml::tags( 'td', array( 'colspan' => 2 ), '' ); + $filterSelection = Html::rawElement( 'td', array( 'colspan' => 2 ), '' ); } - $targetSelection = Xml::tags( 'td', array( 'colspan' => 2 ), + $targetSelection = Html::rawElement( 'td', array( 'colspan' => 2 ), Xml::radioLabel( $this->msg( 'sp-contributions-newbies' )->text(), 'contribs', - 'newbie' , + 'newbie', 'newbie', $this->opts['contribs'] == 'newbie', array( 'class' => 'mw-input' ) @@ -445,7 +444,7 @@ class SpecialContributions extends SpecialPage { ( $this->opts['target'] ? array() : array( 'autofocus' ) ) ) . ' ' - ) ; + ); $namespaceSelection = Xml::tags( 'td', array( 'class' => 'mw-label' ), @@ -455,7 +454,7 @@ class SpecialContributions extends SpecialPage { '' ) ) . - Xml::tags( 'td', null, + Html::rawElement( 'td', null, Html::namespaceSelector( array( 'selected' => $this->opts['namespace'], 'all' => '', @@ -483,10 +482,10 @@ class SpecialContributions extends SpecialPage { array( 'title' => $this->msg( 'tooltip-namespace_association' )->text(), 'class' => 'mw-input' ) ) . ' ' ) - ) ; + ); - $extraOptions = Xml::tags( 'td', array( 'colspan' => 2 ), - Html::rawElement( 'span', array( 'style' => 'white-space: nowrap' ), + if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) { + $deletedOnlyCheck = Html::rawElement( 'span', array( 'style' => 'white-space: nowrap' ), Xml::checkLabel( $this->msg( 'history-show-deleted' )->text(), 'deletedOnly', @@ -494,7 +493,13 @@ class SpecialContributions extends SpecialPage { $this->opts['deletedOnly'], array( 'class' => 'mw-input' ) ) - ) . + ); + } else { + $deletedOnlyCheck = ''; + } + + $extraOptions = Html::rawElement( 'td', array( 'colspan' => 2 ), + $deletedOnlyCheck . Html::rawElement( 'span', array( 'style' => 'white-space: nowrap' ), Xml::checkLabel( $this->msg( 'sp-contributions-toponly' )->text(), @@ -504,7 +509,7 @@ class SpecialContributions extends SpecialPage { array( 'class' => 'mw-input' ) ) ) - ) ; + ); $dateSelectionAndSubmit = Xml::tags( 'td', array( 'colspan' => 2 ), Xml::dateMenu( @@ -515,36 +520,30 @@ class SpecialContributions extends SpecialPage { $this->msg( 'sp-contributions-submit' )->text(), array( 'class' => 'mw-submit' ) ) - ) ; + ); $form .= Xml::fieldset( $this->msg( 'sp-contributions-search' )->text() ) . - Xml::openElement( 'table', array( 'class' => 'mw-contributions-table' ) ) . - Xml::openElement( 'tr' ) . - $targetSelection . - Xml::closeElement( 'tr' ) . - Xml::openElement( 'tr' ) . - $namespaceSelection . - Xml::closeElement( 'tr' ) . - Xml::openElement( 'tr' ) . - $filterSelection . - Xml::closeElement( 'tr' ) . - Xml::openElement( 'tr' ) . - $extraOptions . - Xml::closeElement( 'tr' ) . - Xml::openElement( 'tr' ) . - $dateSelectionAndSubmit . - Xml::closeElement( 'tr' ) . - Xml::closeElement( 'table' ); + Html::rawElement( 'table', array( 'class' => 'mw-contributions-table' ), "\n" . + Html::rawElement( 'tr', array(), $targetSelection ) . "\n" . + Html::rawElement( 'tr', array(), $namespaceSelection ) . "\n" . + Html::rawElement( 'tr', array(), $filterSelection ) . "\n" . + Html::rawElement( 'tr', array(), $extraOptions ) . "\n" . + Html::rawElement( 'tr', array(), $dateSelectionAndSubmit ) . "\n" + ); $explain = $this->msg( 'sp-contributions-explain' ); - if ( $explain->exists() ) { - $form .= "<p id='mw-sp-contributions-explain'>{$explain}</p>"; + if ( !$explain->isBlank() ) { + $form .= "<p id='mw-sp-contributions-explain'>{$explain->parse()}</p>"; } $form .= Xml::closeElement( 'fieldset' ) . Xml::closeElement( 'form' ); return $form; } + + protected function getGroupName() { + return 'users'; + } } /** @@ -598,7 +597,7 @@ class ContribsPager extends ReverseChronologicalPager { * This method basically executes the exact same code as the parent class, though with * a hook added, to allow extentions to add additional queries. * - * @param $offset String: index offset, inclusive + * @param string $offset index offset, inclusive * @param $limit Integer: exact query limit * @param $descending Boolean: query direction, false for ascending, true for descending * @return ResultWrapper @@ -631,7 +630,7 @@ class ContribsPager extends ReverseChronologicalPager { $result = array(); // loop all results and collect them in an array - foreach ( $data as $j => $query ) { + foreach ( $data as $query ) { foreach ( $query as $i => $row ) { // use index column as key, allowing us to easily sort in PHP $result[$row->{$this->getIndexField()} . "-$i"] = $row; @@ -731,10 +730,10 @@ class ContribsPager extends ReverseChronologicalPager { } } if ( $this->deletedOnly ) { - $condition[] = "rev_deleted != '0'"; + $condition[] = 'rev_deleted != 0'; } if ( $this->topOnly ) { - $condition[] = "rev_id = page_latest"; + $condition[] = 'rev_id = page_latest'; } return array( $tables, $index, $condition, $join_conds ); } @@ -831,7 +830,7 @@ class ContribsPager extends ReverseChronologicalPager { */ wfSuppressWarnings(); $rev = new Revision( $row ); - $validRevision = $rev->getParentId() !== null; + $validRevision = (bool) $rev->getId(); wfRestoreWarnings(); if ( $validRevision ) { @@ -950,8 +949,12 @@ class ContribsPager extends ReverseChronologicalPager { // Let extensions add data wfRunHooks( 'ContributionsLineEnding', array( $this, &$ret, $row, &$classes ) ); - $classes = implode( ' ', $classes ); - $ret = "<li class=\"$classes\">$ret</li>\n"; + if ( $classes === array() && $ret === '' ) { + wfDebug( 'Dropping Special:Contribution row that could not be formatted' ); + $ret = "<!-- Could not format Special:Contribution row. -->\n"; + } else { + $ret = Html::rawElement( 'li', array( 'class' => $classes ), $ret ) . "\n"; + } wfProfileOut( __METHOD__ ); return $ret; diff --git a/includes/specials/SpecialDeadendpages.php b/includes/specials/SpecialDeadendpages.php index f4904a50..6978d6bb 100644 --- a/includes/specials/SpecialDeadendpages.php +++ b/includes/specials/SpecialDeadendpages.php @@ -82,4 +82,8 @@ class DeadendPagesPage extends PageQueryPage { return array( 'page_title' ); } } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialDeletedContributions.php b/includes/specials/SpecialDeletedContributions.php index c880b617..e374979e 100644 --- a/includes/specials/SpecialDeletedContributions.php +++ b/includes/specials/SpecialDeletedContributions.php @@ -135,7 +135,10 @@ class DeletedContribsPager extends IndexPager { function formatRow( $row ) { wfProfileIn( __METHOD__ ); + $page = Title::makeTitle( $row->ar_namespace, $row->ar_title ); + $rev = new Revision( array( + 'title' => $page, 'id' => $row->ar_rev_id, 'comment' => $row->ar_comment, 'user' => $row->ar_user, @@ -145,8 +148,6 @@ class DeletedContribsPager extends IndexPager { 'deleted' => $row->ar_deleted, ) ); - $page = Title::makeTitle( $row->ar_namespace, $row->ar_title ); - $undelete = SpecialPage::getTitleFor( 'Undelete' ); $logs = SpecialPage::getTitleFor( 'Log' ); @@ -167,7 +168,7 @@ class DeletedContribsPager extends IndexPager { $user = $this->getUser(); - if( $user->isAllowed('deletedtext') ) { + if( $user->isAllowed( 'deletedtext' ) ) { $last = Linker::linkKnown( $undelete, $this->messages['diff'], @@ -260,7 +261,7 @@ class DeletedContributionsPage extends SpecialPage { * Special page "deleted user contributions". * Shows a list of the deleted contributions of a user. * - * @param $par String: (optional) user name of the user for which to show the contributions + * @param string $par (optional) user name of the user for which to show the contributions */ function execute( $par ) { global $wgQueryPageDefaultLimit; @@ -464,7 +465,7 @@ class DeletedContributionsPage extends SpecialPage { /** * Generates the namespace selector form with hidden attributes. - * @param $options Array: the options to be included. + * @param array $options the options to be included. * @return string */ function getForm( $options ) { @@ -474,7 +475,7 @@ class DeletedContributionsPage extends SpecialPage { if ( !isset( $options['target'] ) ) { $options['target'] = ''; } else { - $options['target'] = str_replace( '_' , ' ' , $options['target'] ); + $options['target'] = str_replace( '_', ' ', $options['target'] ); } if ( !isset( $options['namespace'] ) ) { @@ -498,7 +499,7 @@ class DeletedContributionsPage extends SpecialPage { $f .= "\t" . Html::hidden( $name, $value ) . "\n"; } - $f .= Xml::openElement( 'fieldset' ) . + $f .= Xml::openElement( 'fieldset' ) . Xml::element( 'legend', array(), $this->msg( 'sp-contributions-search' )->text() ) . Xml::tags( 'label', array( 'for' => 'target' ), $this->msg( 'sp-contributions-username' )->parse() ) . ' ' . Html::input( 'target', $options['target'], 'text', array( @@ -521,4 +522,8 @@ class DeletedContributionsPage extends SpecialPage { Xml::closeElement( 'form' ); return $f; } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialDisambiguations.php b/includes/specials/SpecialDisambiguations.php index 48180a77..2126ca52 100644 --- a/includes/specials/SpecialDisambiguations.php +++ b/includes/specials/SpecialDisambiguations.php @@ -59,20 +59,20 @@ class DisambiguationsPage extends QueryPage { if( $dp->getNamespace() != NS_TEMPLATE ) { # @todo 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"); + wfDebug( "Mediawiki:disambiguationspage message does not refer to a template!\n" ); } $linkBatch->addObj( $dp ); } else { # Get all the templates linked from the Mediawiki:Disambiguationspage $disPageObj = Title::makeTitleSafe( NS_MEDIAWIKI, 'disambiguationspage' ); $res = $dbr->select( - array('pagelinks', 'page'), + array( 'pagelinks', 'page' ), 'pl_title', - array('page_id = pl_from', + array( 'page_id = pl_from', 'pl_namespace' => NS_TEMPLATE, 'page_namespace' => $disPageObj->getNamespace(), - 'page_title' => $disPageObj->getDBkey()), - __METHOD__ ); + 'page_title' => $disPageObj->getDBkey() + ), __METHOD__ ); foreach ( $res as $row ) { $linkBatch->addObj( Title::makeTitle( NS_TEMPLATE, $row->pl_title )); @@ -158,4 +158,8 @@ class DisambiguationsPage extends QueryPage { return "$from $edit $arr $to"; } + + protected function getGroupName() { + return 'pages'; + } } diff --git a/includes/specials/SpecialDoubleRedirects.php b/includes/specials/SpecialDoubleRedirects.php index 5864ca9f..5a5d749c 100644 --- a/includes/specials/SpecialDoubleRedirects.php +++ b/includes/specials/SpecialDoubleRedirects.php @@ -33,9 +33,17 @@ class DoubleRedirectsPage extends QueryPage { parent::__construct( $name ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } - function sortDescending() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function sortDescending() { + return false; + } function getPageHeader() { return $this->msg( 'doubleredirectstext' )->parseAsBlock(); @@ -43,23 +51,45 @@ class DoubleRedirectsPage extends QueryPage { function reallyGetQueryInfo( $namespace = null, $title = null ) { $limitToTitle = !( $namespace === null && $title === null ); + $dbr = wfGetDB( DB_SLAVE ); $retval = array ( - 'tables' => array ( 'ra' => 'redirect', - 'rb' => 'redirect', 'pa' => 'page', - 'pb' => 'page', 'pc' => 'page' ), - 'fields' => array ( 'namespace' => 'pa.page_namespace', - 'title' => 'pa.page_title', - 'value' => 'pa.page_title', - 'nsb' => 'pb.page_namespace', - 'tb' => 'pb.page_title', - 'nsc' => 'pc.page_namespace', - 'tc' => 'pc.page_title' ), - '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' ) + 'tables' => array ( + 'ra' => 'redirect', + 'rb' => 'redirect', + 'pa' => 'page', + 'pb' => 'page' + ), + 'fields' => array( + 'namespace' => 'pa.page_namespace', + 'title' => 'pa.page_title', + 'value' => 'pa.page_title', + + 'nsb' => 'pb.page_namespace', + 'tb' => 'pb.page_title', + + // Select fields from redirect instead of page. Because there may + // not actually be a page table row for this target (e.g. for interwiki redirects) + 'nsc' => 'rb.rd_namespace', + 'tc' => 'rb.rd_title', + 'iwc' => 'rb.rd_interwiki', + ), + 'conds' => array( + 'ra.rd_from = pa.page_id', + + // Filter out redirects where the target goes interwiki (bug 40353). + // This isn't an optimization, it is required for correct results, + // otherwise a non-double redirect like Bar -> w:Foo will show up + // like "Bar -> Foo -> w:Foo". + + // Need to check both NULL and "" for some reason, + // apparently either can be stored for non-iw entries. + 'ra.rd_interwiki IS NULL OR ra.rd_interwiki = ' . $dbr->addQuotes( '' ), + + 'pb.page_namespace = ra.rd_namespace', + 'pb.page_title = ra.rd_title', + + 'rb.rd_from = pb.page_id', + ) ); if ( $limitToTitle ) { $retval['conds']['pa.page_namespace'] = $namespace; @@ -79,11 +109,16 @@ class DoubleRedirectsPage extends QueryPage { function formatResult( $skin, $result ) { $titleA = Title::makeTitle( $result->namespace, $result->title ); + // If only titleA is in the query, it means this came from + // querycache (which only saves 3 columns). + // That does save the bulk of the query cost, but now we need to + // get a little more detail about each individual entry quickly + // using the filter of reallyGetQueryInfo. if ( $result && !isset( $result->nsb ) ) { $dbr = wfGetDB( DB_SLAVE ); $qi = $this->reallyGetQueryInfo( $result->namespace, $result->title ); - $res = $dbr->select($qi['tables'], $qi['fields'], + $res = $dbr->select( $qi['tables'], $qi['fields'], $qi['conds'], __METHOD__ ); if ( $res ) { $result = $dbr->fetchObject( $res ); @@ -94,7 +129,7 @@ class DoubleRedirectsPage extends QueryPage { } $titleB = Title::makeTitle( $result->nsb, $result->tb ); - $titleC = Title::makeTitle( $result->nsc, $result->tc ); + $titleC = Title::makeTitle( $result->nsc, $result->tc, '', $result->iwc ); $linkA = Linker::linkKnown( $titleA, @@ -127,4 +162,8 @@ class DoubleRedirectsPage extends QueryPage { return( "{$linkA} {$edit} {$arr} {$linkB} {$arr} {$linkC}" ); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialEditWatchlist.php b/includes/specials/SpecialEditWatchlist.php index 23cd9aa6..d2838e01 100644 --- a/includes/specials/SpecialEditWatchlist.php +++ b/includes/specials/SpecialEditWatchlist.php @@ -49,7 +49,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { private $badItems = array(); - public function __construct(){ + public function __construct() { parent::__construct( 'EditWatchlist' ); } @@ -77,6 +77,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { } $this->checkPermissions(); + $this->checkReadOnly(); $this->outputHeader(); @@ -85,9 +86,9 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { # B/C: $mode used to be waaay down the parameter list, and the first parameter # was $wgUser - if( $mode instanceof User ){ + if( $mode instanceof User ) { $args = func_get_args(); - if( count( $args >= 4 ) ){ + if( count( $args >= 4 ) ) { $mode = $args[3]; } } @@ -101,7 +102,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { case self::EDIT_RAW: $out->setPageTitle( $this->msg( 'watchlistedit-raw-title' ) ); $form = $this->getRawForm(); - if( $form->show() ){ + if( $form->show() ) { $out->addHTML( $this->successMessage ); $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) ); } @@ -111,7 +112,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { default: $out->setPageTitle( $this->msg( 'watchlistedit-normal-title' ) ); $form = $this->getNormalForm(); - if( $form->show() ){ + if( $form->show() ) { $out->addHTML( $this->successMessage ); $out->addReturnTo( SpecialPage::getTitleFor( 'Watchlist' ) ); } elseif ( $this->toc !== false ) { @@ -153,7 +154,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { return array_unique( $list ); } - public function submitRaw( $data ){ + public function submitRaw( $data ) { $wanted = $this->extractTitles( $data['Titles'] ); $current = $this->getWatchlist(); @@ -164,7 +165,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { $this->unwatchTitles( $toUnwatch ); $this->getUser()->invalidateCache(); - if( count( $toWatch ) > 0 || count( $toUnwatch ) > 0 ){ + if( count( $toWatch ) > 0 || count( $toUnwatch ) > 0 ) { $this->successMessage = $this->msg( 'watchlistedit-raw-done' )->parse(); } else { return false; @@ -185,7 +186,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { $this->clearWatchlist(); $this->getUser()->invalidateCache(); - if( count( $current ) > 0 ){ + if( count( $current ) > 0 ) { $this->successMessage = $this->msg( 'watchlistedit-raw-done' )->parse(); } else { return false; @@ -204,7 +205,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { * $titles can be an array of strings or Title objects; the former * is preferred, since Titles are very memory-heavy * - * @param $titles array of strings, or Title objects + * @param array $titles of strings, or Title objects * @param $output String */ private function showTitles( $titles, &$output ) { @@ -289,7 +290,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { $res = $dbr->select( array( 'watchlist' ), - array( 'wl_namespace', 'wl_title' ), + array( 'wl_namespace', 'wl_title' ), array( 'wl_user' => $this->getUser()->getId() ), __METHOD__, array( 'ORDER BY' => array( 'wl_namespace', 'wl_title' ) ) @@ -312,7 +313,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { * * @param Title $title * @param int $namespace - * @param String $dbKey + * @param string $dbKey * @return bool: Whether this item is valid */ private function checkTitle( $title, $namespace, $dbKey ) { @@ -381,7 +382,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { * $titles can be an array of strings or Title objects; the former * is preferred, since Titles are very memory-heavy * - * @param $titles Array of strings, or Title objects + * @param array $titles of strings, or Title objects */ private function watchTitles( $titles ) { $dbw = wfGetDB( DB_MASTER ); @@ -393,13 +394,13 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { if( $title instanceof Title ) { $rows[] = array( 'wl_user' => $this->getUser()->getId(), - 'wl_namespace' => ( $title->getNamespace() & ~1 ), + 'wl_namespace' => MWNamespace::getSubject( $title->getNamespace() ), 'wl_title' => $title->getDBkey(), 'wl_notificationtimestamp' => null, ); $rows[] = array( 'wl_user' => $this->getUser()->getId(), - 'wl_namespace' => ( $title->getNamespace() | 1 ), + 'wl_namespace' => MWNamespace::getTalk( $title->getNamespace() ), 'wl_title' => $title->getDBkey(), 'wl_notificationtimestamp' => null, ); @@ -414,7 +415,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { * $titles can be an array of strings or Title objects; the former * is preferred, since Titles are very memory-heavy * - * @param $titles Array of strings, or Title objects + * @param array $titles of strings, or Title objects */ private function unwatchTitles( $titles ) { $dbw = wfGetDB( DB_MASTER ); @@ -427,7 +428,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { 'watchlist', array( 'wl_user' => $this->getUser()->getId(), - 'wl_namespace' => ( $title->getNamespace() & ~1 ), + 'wl_namespace' => MWNamespace::getSubject( $title->getNamespace() ), 'wl_title' => $title->getDBkey(), ), __METHOD__ @@ -436,7 +437,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { 'watchlist', array( 'wl_user' => $this->getUser()->getId(), - 'wl_namespace' => ( $title->getNamespace() | 1 ), + 'wl_namespace' => MWNamespace::getTalk( $title->getNamespace() ), 'wl_title' => $title->getDBkey(), ), __METHOD__ @@ -470,26 +471,26 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { * * @return HTMLForm */ - protected function getNormalForm(){ + protected function getNormalForm() { global $wgContLang; $fields = array(); $count = 0; - foreach( $this->getWatchlistInfo() as $namespace => $pages ){ + foreach( $this->getWatchlistInfo() as $namespace => $pages ) { if ( $namespace >= 0 ) { - $fields['TitlesNs'.$namespace] = array( + $fields['TitlesNs' . $namespace] = array( 'class' => 'EditWatchlistCheckboxSeriesField', 'options' => array(), 'section' => "ns$namespace", ); } - foreach( array_keys( $pages ) as $dbkey ){ + foreach( array_keys( $pages ) as $dbkey ) { $title = Title::makeTitleSafe( $namespace, $dbkey ); if ( $this->checkTitle( $title, $namespace, $dbkey ) ) { $text = $this->buildRemoveLine( $title ); - $fields['TitlesNs'.$namespace]['options'][$text] = htmlspecialchars( $title->getPrefixedText() ); + $fields['TitlesNs' . $namespace]['options'][$text] = $title->getPrefixedText(); $count++; } } @@ -519,7 +520,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { $form->setTitle( $this->getTitle() ); $form->setSubmitTextMsg( 'watchlistedit-normal-submit' ); # Used message keys: 'accesskey-watchlistedit-normal-submit', 'tooltip-watchlistedit-normal-submit' - $form->setSubmitTooltip('watchlistedit-normal-submit'); + $form->setSubmitTooltip( 'watchlistedit-normal-submit' ); $form->setWrapperLegendMsg( 'watchlistedit-normal-legend' ); $form->addHeaderText( $this->msg( 'watchlistedit-normal-explain' )->parse() ); $form->setSubmitCallback( array( $this, 'submitNormal' ) ); @@ -564,7 +565,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { * * @return HTMLForm */ - protected function getRawForm(){ + protected function getRawForm() { $titles = implode( $this->getWatchlist(), "\n" ); $fields = array( 'Titles' => array( @@ -577,7 +578,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage { $form->setTitle( $this->getTitle( 'raw' ) ); $form->setSubmitTextMsg( 'watchlistedit-raw-submit' ); # Used message keys: 'accesskey-watchlistedit-raw-submit', 'tooltip-watchlistedit-raw-submit' - $form->setSubmitTooltip('watchlistedit-raw-submit'); + $form->setSubmitTooltip( 'watchlistedit-raw-submit' ); $form->setWrapperLegendMsg( 'watchlistedit-raw-legend' ); $form->addHeaderText( $this->msg( 'watchlistedit-raw-explain' )->parse() ); $form->setSubmitCallback( array( $this, 'submitRaw' ) ); @@ -648,7 +649,7 @@ class WatchlistEditor extends SpecialEditWatchlist {} * Extend HTMLForm purely so we can have a more sane way of getting the section headers */ class EditWatchlistNormalHTMLForm extends HTMLForm { - public function getLegend( $namespace ){ + public function getLegend( $namespace ) { $namespace = substr( $namespace, 2 ); return $namespace == NS_MAIN ? $this->msg( 'blanknamespace' )->escaped() @@ -667,8 +668,8 @@ class EditWatchlistCheckboxSeriesField extends HTMLMultiSelectField { * form is open (bug 32126), but we know that invalid items will * be harmless so we can override it here. * - * @param $value String the value the field was submitted with - * @param $alldata Array the data collected from the form + * @param string $value the value the field was submitted with + * @param array $alldata the data collected from the form * @return Mixed Bool true on success, or String error to display. */ function validate( $value, $alldata ) { diff --git a/includes/specials/SpecialEmailuser.php b/includes/specials/SpecialEmailuser.php index 4d875e6e..b5ad589e 100644 --- a/includes/specials/SpecialEmailuser.php +++ b/includes/specials/SpecialEmailuser.php @@ -139,7 +139,8 @@ class SpecialEmailUser extends UnlistedSpecialPage { $this->mTargetObj = $ret; $form = new HTMLForm( $this->getFormFields(), $this->getContext() ); - $form->addPreText( $this->msg( 'emailpagetext' )->parse() ); + // By now we are supposed to be sure that $this->mTarget is a user name + $form->addPreText( $this->msg( 'emailpagetext', $this->mTarget )->parse() ); $form->setSubmitTextMsg( 'emailsend' ); $form->setTitle( $this->getTitle() ); $form->setSubmitCallback( array( __CLASS__, 'uiSubmit' ) ); @@ -162,7 +163,7 @@ class SpecialEmailUser extends UnlistedSpecialPage { /** * Validate target User * - * @param $target String: target user name + * @param string $target target user name * @return User object on success or a string on error */ public static function getTarget( $target ) { @@ -190,7 +191,7 @@ class SpecialEmailUser extends UnlistedSpecialPage { * Check whether a user is allowed to send email * * @param $user User object - * @param $editToken String: edit token + * @param string $editToken edit token * @return null on success or string on error */ public static function getPermissionsError( $user, $editToken ) { @@ -230,7 +231,7 @@ class SpecialEmailUser extends UnlistedSpecialPage { /** * Form to ask for target user name. * - * @param $name String: user name submitted. + * @param string $name user name submitted. * @return String: form asking for user name. */ protected function userForm( $name ) { @@ -336,4 +337,8 @@ class SpecialEmailUser extends UnlistedSpecialPage { return $status; } } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialExport.php b/includes/specials/SpecialExport.php index b4294b32..7abfefe2 100644 --- a/includes/specials/SpecialExport.php +++ b/includes/specials/SpecialExport.php @@ -80,7 +80,7 @@ class SpecialExport extends SpecialPage { $page = $request->getText( 'pages' ); $nsindex = $request->getText( 'nsindex', '' ); - if ( strval( $nsindex ) !== '' ) { + if ( strval( $nsindex ) !== '' ) { /** * Same implementation as above, so same @todo */ @@ -124,7 +124,7 @@ class SpecialExport extends SpecialPage { if ( $this->curonly ) { $history = WikiExporter::CURRENT; } elseif ( !$historyCheck ) { - if ( $limit > 0 && ($wgExportMaxHistory == 0 || $limit < $wgExportMaxHistory ) ) { + if ( $limit > 0 && ( $wgExportMaxHistory == 0 || $limit < $wgExportMaxHistory ) ) { $history['limit'] = $limit; } if ( !is_null( $offset ) ) { @@ -161,7 +161,7 @@ class SpecialExport extends SpecialPage { $list_authors = $request->getCheck( 'listauthors' ); if ( !$this->curonly || !$wgExportAllowListContributors ) { - $list_authors = false ; + $list_authors = false; } if ( $this->doExport ) { @@ -272,7 +272,7 @@ class SpecialExport extends SpecialPage { /** * Do the actual page exporting * - * @param $page String: user input on what page(s) to export + * @param string $page user input on what page(s) to export * @param $history Mixed: one of the WikiExporter history export constants * @param $list_authors Boolean: Whether to add distinct author list (when * not returning full history) @@ -286,7 +286,7 @@ class SpecialExport extends SpecialPage { } else { $pageSet = array(); // Inverted index of all pages to look up - + // Split up and normalize input foreach( explode( "\n", $page ) as $pageName ) { $pageName = trim( $pageName ); @@ -339,7 +339,7 @@ class SpecialExport extends SpecialPage { // This might take a while... :D wfSuppressWarnings(); - set_time_limit(0); + set_time_limit( 0 ); wfRestoreWarnings(); } @@ -405,7 +405,7 @@ class SpecialExport extends SpecialPage { foreach ( $res as $row ) { $n = $row->page_title; - if ($row->page_namespace) { + if ( $row->page_namespace ) { $ns = $wgContLang->getNsText( $row->page_namespace ); $n = $ns . ':' . $n; } @@ -561,4 +561,7 @@ class SpecialExport extends SpecialPage { return $pageSet; } + protected function getGroupName() { + return 'pagetools'; + } } diff --git a/includes/specials/SpecialFewestrevisions.php b/includes/specials/SpecialFewestrevisions.php index 7e4bc9ce..5b7f353d 100644 --- a/includes/specials/SpecialFewestrevisions.php +++ b/includes/specials/SpecialFewestrevisions.php @@ -60,7 +60,6 @@ class FewestrevisionsPage extends QueryPage { ); } - function sortDescending() { return false; } @@ -94,4 +93,8 @@ class FewestrevisionsPage extends QueryPage { return $this->getLanguage()->specialList( $plink, $nlink ); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialFileDuplicateSearch.php b/includes/specials/SpecialFileDuplicateSearch.php index ccf8ba17..3fe64e6f 100644 --- a/includes/specials/SpecialFileDuplicateSearch.php +++ b/includes/specials/SpecialFileDuplicateSearch.php @@ -40,9 +40,17 @@ class FileDuplicateSearchPage extends QueryPage { parent::__construct( $name ); } - function isSyndicated() { return false; } - function isCacheable() { return false; } - function isCached() { return false; } + function isSyndicated() { + return false; + } + + function isCacheable() { + return false; + } + + function isCached() { + return false; + } function linkParameters() { return array( 'filename' => $this->filename ); @@ -59,7 +67,7 @@ class FileDuplicateSearchPage extends QueryPage { /** * - * @param $dupes Array of File objects + * @param array $dupes of File objects */ function showList( $dupes ) { $html = array(); @@ -93,7 +101,7 @@ class FileDuplicateSearchPage extends QueryPage { $this->setHeaders(); $this->outputHeader(); - $this->filename = isset( $par ) ? $par : $this->getRequest()->getText( 'filename' ); + $this->filename = isset( $par ) ? $par : $this->getRequest()->getText( 'filename' ); $this->file = null; $this->hash = ''; $title = Title::newFromText( $this->filename, NS_FILE ); @@ -106,7 +114,7 @@ class FileDuplicateSearchPage extends QueryPage { # Create the input form $out->addHTML( Xml::openElement( 'form', array( 'id' => 'fileduplicatesearch', 'method' => 'get', 'action' => $wgScript ) ) . - Html::hidden( 'title', $this->getTitle()->getPrefixedDbKey() ) . + Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . Xml::openElement( 'fieldset' ) . Xml::element( 'legend', null, $this->msg( 'fileduplicatesearch-legend' )->text() ) . Xml::inputLabel( $this->msg( 'fileduplicatesearch-filename' )->text(), 'filename', 'filename', 50, $this->filename ) . ' ' . @@ -207,4 +215,8 @@ class FileDuplicateSearchPage extends QueryPage { return "$plink . . $user . . $time"; } + + protected function getGroupName() { + return 'media'; + } } diff --git a/includes/specials/SpecialFilepath.php b/includes/specials/SpecialFilepath.php index e0866504..bbcced26 100644 --- a/includes/specials/SpecialFilepath.php +++ b/includes/specials/SpecialFilepath.php @@ -47,7 +47,7 @@ class SpecialFilepath extends SpecialPage { $file = wfFindFile( $title ); if ( $file && $file->exists() ) { - // Default behaviour: Use the direct link to the file. + // Default behavior: Use the direct link to the file. $url = $file->getURL(); $width = $request->getInt( 'width', -1 ); $height = $request->getInt( 'height', -1 ); @@ -86,4 +86,8 @@ class SpecialFilepath extends SpecialPage { Html::closeElement( 'form' ) ); } + + protected function getGroupName() { + return 'media'; + } } diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php index 362fc5cf..aa56041b 100644 --- a/includes/specials/SpecialImport.php +++ b/includes/specials/SpecialImport.php @@ -101,7 +101,7 @@ class SpecialImport extends SpecialPage { $this->logcomment = $request->getText( 'log-comment' ); $this->pageLinkDepth = $wgExportMaxLinkDepth == 0 ? 0 : $request->getIntOrNull( 'pagelink-depth' ); - $this->rootpage = $request->getText( 'rootpage' );
+ $this->rootpage = $request->getText( 'rootpage' ); $user = $this->getUser(); if ( !$user->matchEditToken( $request->getVal( 'editToken' ) ) ) { @@ -114,7 +114,7 @@ class SpecialImport extends SpecialPage { throw new PermissionsError( 'importupload' ); } } elseif ( $sourceName == "interwiki" ) { - if( !$user->isAllowed( 'import' ) ){ + if( !$user->isAllowed( 'import' ) ) { throw new PermissionsError( 'import' ); } $this->interwiki = $request->getVal( 'interwiki' ); @@ -153,7 +153,7 @@ class SpecialImport extends SpecialPage { $out->addWikiMsg( "importstart" ); - $reporter = new ImportReporter( $importer, $isUpload, $this->interwiki , $this->logcomment); + $reporter = new ImportReporter( $importer, $isUpload, $this->interwiki, $this->logcomment ); $reporter->setContext( $this->getContext() ); $exception = false; @@ -194,14 +194,14 @@ class SpecialImport extends SpecialPage { $this->msg( 'importtext' )->parseAsBlock() . Html::hidden( 'action', 'submit' ) . Html::hidden( 'source', 'upload' ) . - Xml::openElement( 'table', array( 'id' => 'mw-import-table' ) ) . + Xml::openElement( 'table', array( 'id' => 'mw-import-table-upload' ) ) . "<tr> <td class='mw-label'>" . Xml::label( $this->msg( 'import-upload-filename' )->text(), 'xmlimport' ) . "</td> <td class='mw-input'>" . - Xml::input( 'xmlimport', 50, '', array( 'type' => 'file' ) ) . ' ' . + Html::input( 'xmlimport', '', 'file', array( 'id' => 'xmlimport' ) ) . ' ' . "</td> </tr> <tr> @@ -215,11 +215,11 @@ class SpecialImport extends SpecialPage { </tr> <tr> <td class='mw-label'>" . - Xml::label( $this->msg( 'import-interwiki-rootpage' )->text(), 'mw-interwiki-rootpage' ) . + Xml::label( $this->msg( 'import-interwiki-rootpage' )->text(), 'mw-interwiki-rootpage-upload' ) . "</td> <td class='mw-input'>" . Xml::input( 'rootpage', 50, $this->rootpage, - array( 'id' => 'mw-interwiki-rootpage', 'type' => 'text' ) ) . ' ' . + array( 'id' => 'mw-interwiki-rootpage-upload', 'type' => 'text' ) ) . ' ' . "</td> </tr> <tr> @@ -260,13 +260,13 @@ class SpecialImport extends SpecialPage { Html::hidden( 'action', 'submit' ) . Html::hidden( 'source', 'interwiki' ) . Html::hidden( 'editToken', $user->getEditToken() ) . - Xml::openElement( 'table', array( 'id' => 'mw-import-table' ) ) . + Xml::openElement( 'table', array( 'id' => 'mw-import-table-interwiki' ) ) . "<tr> <td class='mw-label'>" . Xml::label( $this->msg( 'import-interwiki-source' )->text(), 'interwiki' ) . "</td> <td class='mw-input'>" . - Xml::openElement( 'select', array( 'name' => 'interwiki' ) ) + Xml::openElement( 'select', array( 'name' => 'interwiki', 'id' => 'interwiki' ) ) ); foreach( $wgImportSources as $prefix ) { $selected = ( $this->interwiki === $prefix ) ? ' selected="selected"' : ''; @@ -275,7 +275,7 @@ class SpecialImport extends SpecialPage { $out->addHTML( Xml::closeElement( 'select' ) . - Xml::input( 'frompage', 50, $this->frompage ) . + Xml::input( 'frompage', 50, $this->frompage, array( 'id' => 'frompage' ) ) . "</td> </tr> <tr> @@ -321,11 +321,11 @@ class SpecialImport extends SpecialPage { </tr> <tr> <td class='mw-label'>" . - Xml::label( $this->msg( 'import-interwiki-rootpage' )->text(), 'mw-interwiki-rootpage' ) . + Xml::label( $this->msg( 'import-interwiki-rootpage' )->text(), 'mw-interwiki-rootpage-interwiki' ) . "</td> <td class='mw-input'>" . Xml::input( 'rootpage', 50, $this->rootpage, - array( 'id' => 'mw-interwiki-rootpage', 'type' => 'text' ) ) . ' ' . + array( 'id' => 'mw-interwiki-rootpage-interwiki', 'type' => 'text' ) ) . ' ' . "</td> </tr> <tr> @@ -341,6 +341,10 @@ class SpecialImport extends SpecialPage { ); } } + + protected function getGroupName() { + return 'pagetools'; + } } /** @@ -353,7 +357,7 @@ class ImportReporter extends ContextSource { private $mOriginalPageOutCallback = null; private $mLogItemCount = 0; - function __construct( $importer, $upload, $interwiki , $reason=false ) { + function __construct( $importer, $upload, $interwiki, $reason = false ) { $this->mOriginalPageOutCallback = $importer->setPageOutCallback( array( $this, 'reportPage' ) ); $this->mOriginalLogCallback = @@ -410,7 +414,7 @@ class ImportReporter extends ContextSource { $detail = $this->msg( 'import-logentry-upload-detail' )->numParams( $successCount )->inContentLanguage()->text(); if ( $this->reason ) { - $detail .= $this->msg( 'colon-separator' )->inContentLanguage()->text() . $this->reason; + $detail .= $this->msg( 'colon-separator' )->inContentLanguage()->text() . $this->reason; } $log->addEntry( 'upload', $title, $detail ); } else { @@ -419,7 +423,7 @@ class ImportReporter extends ContextSource { $detail = $this->msg( 'import-logentry-interwiki-detail' )->numParams( $successCount )->params( $interwiki )->inContentLanguage()->text(); if ( $this->reason ) { - $detail .= $this->msg( 'colon-separator' )->inContentLanguage()->text() . $this->reason; + $detail .= $this->msg( 'colon-separator' )->inContentLanguage()->text() . $this->reason; } $log->addEntry( 'interwiki', $title, $detail ); } @@ -428,7 +432,7 @@ class ImportReporter extends ContextSource { $dbw = wfGetDB( DB_MASTER ); $latest = $title->getLatestRevID(); $nullRevision = Revision::newNullRevision( $dbw, $title->getArticleID(), $comment, true ); - if (!is_null($nullRevision)) { + if ( !is_null( $nullRevision ) ) { $nullRevision->insertOn( $dbw ); $page = WikiPage::factory( $title ); # Update page record diff --git a/includes/specials/SpecialJavaScriptTest.php b/includes/specials/SpecialJavaScriptTest.php index c217eccb..d204d50c 100644 --- a/includes/specials/SpecialJavaScriptTest.php +++ b/includes/specials/SpecialJavaScriptTest.php @@ -40,19 +40,11 @@ class SpecialJavaScriptTest extends SpecialPage { } public function execute( $par ) { - global $wgEnableJavaScriptTest; - $out = $this->getOutput(); $this->setHeaders(); $out->disallowUserJs(); - // Abort early if we're disabled - if ( $wgEnableJavaScriptTest !== true ) { - $out->addWikiMsg( 'javascripttest-disabled' ); - return; - } - $out->addModules( 'mediawiki.special.javaScriptTest' ); // Determine framework @@ -110,8 +102,9 @@ class SpecialJavaScriptTest extends SpecialPage { * Function to wrap the summary. * It must be given a valid state as a second parameter or an exception will * be thrown. - * @param $html String: The raw HTML. - * @param $state String: State, one of 'noframework', 'unknownframework' or 'frameworkfound' + * @param string $html The raw HTML. + * @param string $state State, one of 'noframework', 'unknownframework' or 'frameworkfound' + * @throws MWException * @return string */ private function wrapSummaryHtml( $html, $state ) { @@ -165,9 +158,7 @@ HTML; $out->addJsConfigVars( 'QUnitTestSwarmInjectJSPath', $wgJavaScriptTestConfig['qunit']['testswarm-injectjs'] ); } - public function isListed(){ - global $wgEnableJavaScriptTest; - return $wgEnableJavaScriptTest === true; + protected function getGroupName() { + return 'other'; } - } diff --git a/includes/specials/SpecialLinkSearch.php b/includes/specials/SpecialLinkSearch.php index 0810ee77..030416fb 100644 --- a/includes/specials/SpecialLinkSearch.php +++ b/includes/specials/SpecialLinkSearch.php @@ -22,7 +22,6 @@ * @author Brion Vibber */ - /** * Special:LinkSearch to search the external-links table. * @ingroup SpecialPage @@ -43,7 +42,7 @@ class LinkSearchPage extends QueryPage { } function execute( $par ) { - global $wgUrlProtocols, $wgMiserMode; + global $wgUrlProtocols, $wgMiserMode, $wgScript; $this->setHeaders(); $this->outputHeader(); @@ -64,15 +63,15 @@ class LinkSearchPage extends QueryPage { $target2 = $target; $protocol = ''; - $pr_sl = strpos($target2, '//' ); - $pr_cl = strpos($target2, ':' ); + $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 ); + $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 ); + $protocol = substr( $target2, 0, $pr_cl + 1 ); $target2 = substr( $target2, $pr_cl+1 ); } elseif ( $protocol == '' && $target2 != '' ) { // default @@ -84,12 +83,16 @@ class LinkSearchPage extends QueryPage { $protocol = ''; } - $out->addWikiMsg( 'linksearch-text', '<nowiki>' . $this->getLanguage()->commaList( $protocols_list ) . '</nowiki>' ); - $s = Xml::openElement( 'form', array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => $GLOBALS['wgScript'] ) ) . - Html::hidden( 'title', $this->getTitle()->getPrefixedDbKey() ) . - '<fieldset>' . - Xml::element( 'legend', array(), $this->msg( 'linksearch' )->text() ) . - Xml::inputLabel( $this->msg( 'linksearch-pat' )->text(), 'target', 'target', 50, $target ) . ' '; + $out->addWikiMsg( + 'linksearch-text', + '<nowiki>' . $this->getLanguage()->commaList( $protocols_list ) . '</nowiki>', + count( $protocols_list ) + ); + $s = Html::openElement( 'form', array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => $wgScript ) ) . "\n" . + Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . "\n" . + Html::openElement( 'fieldset' ) . "\n" . + Html::element( 'legend', array(), $this->msg( 'linksearch' )->text() ) . "\n" . + Xml::inputLabel( $this->msg( 'linksearch-pat' )->text(), 'target', 'target', 50, $target ) . "\n"; if ( !$wgMiserMode ) { $s .= Html::namespaceSelector( array( @@ -103,9 +106,9 @@ class LinkSearchPage extends QueryPage { ) ); } - $s .= Xml::submitButton( $this->msg( 'linksearch-ok' )->text() ) . - '</fieldset>' . - Xml::closeElement( 'form' ); + $s .= Xml::submitButton( $this->msg( 'linksearch-ok' )->text() ) . "\n" . + Html::closeElement( 'fieldset' ) . "\n" . + Html::closeElement( 'form' ) . "\n"; $out->addHTML( $s ); if( $target != '' ) { @@ -134,10 +137,10 @@ class LinkSearchPage extends QueryPage { */ static function mungeQuery( $query, $prot ) { $field = 'el_index'; - $rv = LinkFilter::makeLikeArray( $query , $prot ); + $rv = LinkFilter::makeLikeArray( $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)) { + if ( preg_match( '/^(:?[0-9]{1,3}\.)+\*\s*$|^(:?[0-9]{1,3}\.){3}[0-9]{1,3}:[0-9]*\*\s*$/', $query ) ) { $dbr = wfGetDB( DB_SLAVE ); $rv = array( $prot . rtrim( $query, " \t*" ), $dbr->anyString() ); $field = 'el_to'; @@ -197,7 +200,7 @@ class LinkSearchPage extends QueryPage { * Override to check query validity. */ function doQuery( $offset = false, $limit = false ) { - list( $this->mMungedQuery, ) = LinkSearchPage::mungeQuery( $this->mQuery, $this->mProt ); + list( $this->mMungedQuery, ) = LinkSearchPage::mungeQuery( $this->mQuery, $this->mProt ); if( $this->mMungedQuery === false ) { $this->getOutput()->addWikiMsg( 'linksearch-error' ); } else { @@ -218,4 +221,8 @@ class LinkSearchPage extends QueryPage { function getOrderFields() { return array(); } + + protected function getGroupName() { + return 'redirects'; + } } diff --git a/includes/specials/SpecialListfiles.php b/includes/specials/SpecialListfiles.php index cc055221..c864ae2a 100644 --- a/includes/specials/SpecialListfiles.php +++ b/includes/specials/SpecialListfiles.php @@ -23,11 +23,11 @@ class SpecialListFiles extends IncludableSpecialPage { - public function __construct(){ + public function __construct() { parent::__construct( 'Listfiles' ); } - public function execute( $par ){ + public function execute( $par ) { $this->setHeaders(); $this->outputHeader(); @@ -51,6 +51,10 @@ class SpecialListFiles extends IncludableSpecialPage { } $this->getOutput()->addHTML( $html ); } + + protected function getGroupName() { + return 'media'; + } } /** @@ -137,7 +141,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 @@ -223,7 +227,7 @@ class ImageListPager extends TablePager { case 'img_size': return htmlspecialchars( $this->getLanguage()->formatSize( $value ) ); case 'img_description': - return Linker::commentBlock( $value ); + return Linker::formatComment( $value ); case 'count': return intval( $value ) + 1; } diff --git a/includes/specials/SpecialListgrouprights.php b/includes/specials/SpecialListgrouprights.php index 1f95c225..7cccf887 100644 --- a/includes/specials/SpecialListgrouprights.php +++ b/includes/specials/SpecialListgrouprights.php @@ -139,15 +139,15 @@ class SpecialListGroupRights extends SpecialPage { /** * Create a user-readable list of permissions from the given array. * - * @param $permissions Array of permission => bool (from $wgGroupPermissions items) - * @param $revoke Array of permission => bool (from $wgRevokePermissions items) - * @param $add Array of groups this group is allowed to add or true - * @param $remove Array of groups this group is allowed to remove or true - * @param $addSelf Array of groups this group is allowed to add to self or true - * @param $removeSelf Array of group this group is allowed to remove from self or true + * @param array $permissions of permission => bool (from $wgGroupPermissions items) + * @param array $revoke of permission => bool (from $wgRevokePermissions items) + * @param array $add of groups this group is allowed to add or true + * @param array $remove of groups this group is allowed to remove or true + * @param array $addSelf of groups this group is allowed to add to self or true + * @param array $removeSelf of group this group is allowed to remove from self or true * @return string List of all granted permissions, separated by comma separator */ - private function formatPermissions( $permissions, $revoke, $add, $remove, $addSelf, $removeSelf ) { + private function formatPermissions( $permissions, $revoke, $add, $remove, $addSelf, $removeSelf ) { $r = array(); foreach( $permissions as $permission => $granted ) { //show as granted only if it isn't revoked to prevent duplicate display of permissions @@ -170,7 +170,7 @@ class SpecialListGroupRights extends SpecialPage { } sort( $r ); $lang = $this->getLanguage(); - if( $add === true ){ + if( $add === true ) { $r[] = $this->msg( 'listgrouprights-addgroup-all' )->escaped(); } elseif( is_array( $add ) && count( $add ) ) { $add = array_values( array_unique( $add ) ); @@ -179,7 +179,7 @@ class SpecialListGroupRights extends SpecialPage { count( $add ) )->parse(); } - if( $remove === true ){ + if( $remove === true ) { $r[] = $this->msg( 'listgrouprights-removegroup-all' )->escaped(); } elseif( is_array( $remove ) && count( $remove ) ) { $remove = array_values( array_unique( $remove ) ); @@ -188,7 +188,7 @@ class SpecialListGroupRights extends SpecialPage { count( $remove ) )->parse(); } - if( $addSelf === true ){ + if( $addSelf === true ) { $r[] = $this->msg( 'listgrouprights-addgroup-self-all' )->escaped(); } elseif( is_array( $addSelf ) && count( $addSelf ) ) { $addSelf = array_values( array_unique( $addSelf ) ); @@ -197,7 +197,7 @@ class SpecialListGroupRights extends SpecialPage { count( $addSelf ) )->parse(); } - if( $removeSelf === true ){ + if( $removeSelf === true ) { $r[] = $this->msg( 'listgrouprights-removegroup-self-all' )->parse(); } elseif( is_array( $removeSelf ) && count( $removeSelf ) ) { $removeSelf = array_values( array_unique( $removeSelf ) ); @@ -212,4 +212,8 @@ class SpecialListGroupRights extends SpecialPage { return '<ul><li>' . implode( "</li>\n<li>", $r ) . '</li></ul>'; } } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialListredirects.php b/includes/specials/SpecialListredirects.php index fe338a08..0283767a 100644 --- a/includes/specials/SpecialListredirects.php +++ b/includes/specials/SpecialListredirects.php @@ -34,9 +34,17 @@ class ListredirectsPage extends QueryPage { parent::__construct( $name ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } - function sortDescending() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function sortDescending() { + return false; + } function getQueryInfo() { return array( @@ -77,7 +85,7 @@ class ListredirectsPage extends QueryPage { $batch->execute(); // Back to start for display - if ( $db->numRows( $res ) > 0 ) { + if ( $res->numRows() > 0 ) { // If there are no rows we get an error seeking. $db->dataSeek( $res, 0 ); } @@ -118,4 +126,8 @@ class ListredirectsPage extends QueryPage { return "<del>$rd_link</del>"; } } + + protected function getGroupName() { + return 'pages'; + } } diff --git a/includes/specials/SpecialListusers.php b/includes/specials/SpecialListusers.php index 1089fbbe..d253a4d3 100644 --- a/includes/specials/SpecialListusers.php +++ b/includes/specials/SpecialListusers.php @@ -36,7 +36,9 @@ class UsersPager extends AlphabeticPager { /** * @param $context IContextSource - * @param $par null|array + * @param array $par (Default null) + * @param $including boolean Whether this page is being transcluded in + * another page */ function __construct( IContextSource $context = null, $par = null, $including = null ) { if ( $context ) { @@ -89,7 +91,7 @@ class UsersPager extends AlphabeticPager { $conds = array(); // Don't show hidden names if( !$this->getUser()->isAllowed( 'hideuser' ) ) { - $conds[] = 'ipb_deleted IS NULL'; + $conds[] = 'ipb_deleted IS NULL OR ipb_deleted = 0'; } $options = array(); @@ -114,7 +116,7 @@ class UsersPager extends AlphabeticPager { $options['GROUP BY'] = $this->creationSort ? 'user_id' : 'user_name'; $query = array( - 'tables' => array( 'user', 'user_groups', 'ipblocks'), + 'tables' => array( 'user', 'user_groups', 'ipblocks' ), 'fields' => array( 'user_name' => $this->creationSort ? 'MAX(user_name)' : 'user_name', 'user_id' => $this->creationSort ? 'user_id' : 'MAX(user_id)', @@ -129,7 +131,6 @@ class UsersPager extends AlphabeticPager { 'user_groups' => array( 'LEFT JOIN', 'user_id=ug_user' ), 'ipblocks' => array( 'LEFT JOIN', array( 'user_id=ipb_user', - 'ipb_deleted' => 1, 'ipb_auto' => 0 )), ), @@ -152,7 +153,7 @@ class UsersPager extends AlphabeticPager { $userName = $row->user_name; $ulinks = Linker::userLink( $row->user_id, $userName ); - $ulinks .= Linker::userToolLinks( $row->user_id, $userName ); + $ulinks .= Linker::userToolLinksRedContribs( $row->user_id, $userName, intval( $row->edits ) ); $lang = $this->getLanguage(); @@ -185,9 +186,10 @@ class UsersPager extends AlphabeticPager { $created = $this->msg( 'usercreated', $d, $t, $row->user_name )->escaped(); $created = ' ' . $this->msg( 'parentheses' )->rawParams( $created )->escaped(); } + $blocked = !is_null( $row->ipb_deleted ) ? ' ' . $this->msg( 'listusers-blocked', $userName )->escaped() : ''; wfRunHooks( 'SpecialListusersFormatRow', array( &$item, $row ) ); - return Html::rawElement( 'li', array(), "{$item}{$edits}{$created}" ); + return Html::rawElement( 'li', array(), "{$item}{$edits}{$created}{$blocked}" ); } function doBatchLookups() { @@ -204,23 +206,32 @@ class UsersPager extends AlphabeticPager { /** * @return string */ - function getPageHeader( ) { + function getPageHeader() { global $wgScript; list( $self ) = explode( '/', $this->getTitle()->getPrefixedDBkey() ); # Form tag - $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-listusers-form' ) ) . + $out = Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-listusers-form' ) ) . Xml::fieldset( $this->msg( 'listusers' )->text() ) . Html::hidden( 'title', $self ); # Username field $out .= Xml::label( $this->msg( 'listusersfrom' )->text(), 'offset' ) . ' ' . - Xml::input( 'username', 20, $this->requestedUser, array( 'id' => 'offset' ) ) . ' '; + Html::input( + 'username', + $this->requestedUser, + 'text', + array( + 'id' => 'offset', + 'size' => 20, + 'autofocus' => $this->requestedUser === '' + ) + ) . ' '; # Group drop-down list $out .= Xml::label( $this->msg( 'group' )->text(), 'group' ) . ' ' . - Xml::openElement('select', array( 'name' => 'group', 'id' => 'group' ) ) . + Xml::openElement( 'select', array( 'name' => 'group', 'id' => 'group' ) ) . Xml::option( $this->msg( 'group-all' )->text(), '' ); foreach( $this->getAllGroups() as $group => $groupText ) $out .= Xml::option( $groupText, $group, $group == $this->requestedGroup ); @@ -286,8 +297,8 @@ class UsersPager extends AlphabeticPager { /** * Format a link to a group description page * - * @param $group String: group name - * @param $username String Username + * @param string $group group name + * @param string $username Username * @return string */ protected static function buildGroupLink( $group, $username ) { @@ -298,20 +309,19 @@ class UsersPager extends AlphabeticPager { /** * @ingroup SpecialPage */ -class SpecialListUsers extends SpecialPage { +class SpecialListUsers extends IncludableSpecialPage { /** * Constructor */ public function __construct() { parent::__construct( 'Listusers' ); - $this->mIncludable = true; } /** * Show the special page * - * @param $par string (optional) A group to list users from + * @param string $par (optional) A group to list users from */ public function execute( $par ) { $this->setHeaders(); @@ -337,4 +347,8 @@ class SpecialListUsers extends SpecialPage { $this->getOutput()->addHTML( $s ); } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialLockdb.php b/includes/specials/SpecialLockdb.php index d71ac6e1..95ef9510 100644 --- a/includes/specials/SpecialLockdb.php +++ b/includes/specials/SpecialLockdb.php @@ -102,4 +102,8 @@ class SpecialLockdb extends FormSpecialPage { $out->addSubtitle( $this->msg( 'lockdbsuccesssub' ) ); $out->addWikiMsg( 'lockdbsuccesstext' ); } + + protected function getGroupName() { + return 'wiki'; + } } diff --git a/includes/specials/SpecialLog.php b/includes/specials/SpecialLog.php index 7800e566..4fc0f6e8 100644 --- a/includes/specials/SpecialLog.php +++ b/includes/specials/SpecialLog.php @@ -182,7 +182,6 @@ class SpecialLog extends SpecialPage { return $s; } - /** * Set page title and show header for this log type * @param $type string @@ -194,4 +193,7 @@ class SpecialLog extends SpecialPage { $this->getOutput()->addHTML( $page->getDescription()->parseAsBlock() ); } + protected function getGroupName() { + return 'changes'; + } } diff --git a/includes/specials/SpecialLonelypages.php b/includes/specials/SpecialLonelypages.php index 763bbdb1..8c6a88ac 100644 --- a/includes/specials/SpecialLonelypages.php +++ b/includes/specials/SpecialLonelypages.php @@ -44,7 +44,10 @@ class LonelyPagesPage extends PageQueryPage { function isExpensive() { return true; } - function isSyndicated() { return false; } + + function isSyndicated() { + return false; + } function getQueryInfo() { return array ( @@ -78,4 +81,8 @@ class LonelyPagesPage extends PageQueryPage { return array( 'page_title' ); } } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialLongpages.php b/includes/specials/SpecialLongpages.php index dd60e37d..c045f9e9 100644 --- a/includes/specials/SpecialLongpages.php +++ b/includes/specials/SpecialLongpages.php @@ -34,4 +34,8 @@ class LongPagesPage extends ShortPagesPage { function sortDescending() { return true; } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialMIMEsearch.php b/includes/specials/SpecialMIMEsearch.php index 104c653f..c5a109d4 100644 --- a/includes/specials/SpecialMIMEsearch.php +++ b/includes/specials/SpecialMIMEsearch.php @@ -34,9 +34,17 @@ class MIMEsearchPage extends QueryPage { parent::__construct( $name ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } - function isCacheable() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function isCacheable() { + return false; + } function linkParameters() { return array( 'mime' => "{$this->major}/{$this->minor}" ); @@ -84,7 +92,6 @@ class MIMEsearchPage extends QueryPage { parent::execute( $par ); } - function formatResult( $skin, $result ) { global $wgContLang; @@ -126,4 +133,8 @@ class MIMEsearchPage extends QueryPage { ); return in_array( $type, $types ); } + + protected function getGroupName() { + return 'media'; + } } diff --git a/includes/specials/SpecialMergeHistory.php b/includes/specials/SpecialMergeHistory.php index 1f057499..1476e156 100644 --- a/includes/specials/SpecialMergeHistory.php +++ b/includes/specials/SpecialMergeHistory.php @@ -141,7 +141,7 @@ class SpecialMergeHistory extends SpecialPage { '<fieldset>' . Xml::element( 'legend', array(), $this->msg( 'mergehistory-box' )->text() ) . - Html::hidden( 'title', $this->getTitle()->getPrefixedDbKey() ) . + Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . Html::hidden( 'submitted', '1' ) . Html::hidden( 'mergepoint', $this->mTimestamp ) . Xml::openElement( 'table' ) . @@ -373,26 +373,33 @@ class SpecialMergeHistory extends SpecialPage { $destTitle->getPrefixedText() )->inContentLanguage()->text(); } - $mwRedir = MagicWord::get( 'redirect' ); - $redirectText = $mwRedir->getSynonym( 0 ) . ' [[' . $destTitle->getPrefixedText() . "]]\n"; - $redirectPage = WikiPage::factory( $targetTitle ); - $redirectRevision = new Revision( array( - 'page' => $this->mTargetID, - 'comment' => $comment, - 'text' => $redirectText ) ); - $redirectRevision->insertOn( $dbw ); - $redirectPage->updateRevisionOn( $dbw, $redirectRevision ); - - # Now, we record the link from the redirect to the new title. - # It should have no other outgoing links... - $dbw->delete( 'pagelinks', array( 'pl_from' => $this->mDestID ), __METHOD__ ); - $dbw->insert( 'pagelinks', - array( - 'pl_from' => $this->mDestID, - 'pl_namespace' => $destTitle->getNamespace(), - 'pl_title' => $destTitle->getDBkey() ), - __METHOD__ - ); + + $contentHandler = ContentHandler::getForTitle( $targetTitle ); + $redirectContent = $contentHandler->makeRedirectContent( $destTitle ); + + if ( $redirectContent ) { + $redirectPage = WikiPage::factory( $targetTitle ); + $redirectRevision = new Revision( array( + 'title' => $targetTitle, + 'page' => $this->mTargetID, + 'comment' => $comment, + 'content' => $redirectContent ) ); + $redirectRevision->insertOn( $dbw ); + $redirectPage->updateRevisionOn( $dbw, $redirectRevision ); + + # Now, we record the link from the redirect to the new title. + # It should have no other outgoing links... + $dbw->delete( 'pagelinks', array( 'pl_from' => $this->mDestID ), __METHOD__ ); + $dbw->insert( 'pagelinks', + array( + 'pl_from' => $this->mDestID, + 'pl_namespace' => $destTitle->getNamespace(), + 'pl_title' => $destTitle->getDBkey() ), + __METHOD__ + ); + } else { + // would be nice to show a warning if we couldn't create a redirect + } } else { $targetTitle->invalidateCache(); // update histories } @@ -416,6 +423,10 @@ class SpecialMergeHistory extends SpecialPage { return true; } + + protected function getGroupName() { + return 'pagetools'; + } } class MergeHistoryPager extends ReverseChronologicalPager { diff --git a/includes/specials/SpecialMostcategories.php b/includes/specials/SpecialMostcategories.php index 3f0bafa3..11f26bd7 100644 --- a/includes/specials/SpecialMostcategories.php +++ b/includes/specials/SpecialMostcategories.php @@ -35,8 +35,13 @@ class MostcategoriesPage extends QueryPage { parent::__construct( $name ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } function getQueryInfo() { return array ( @@ -94,4 +99,8 @@ class MostcategoriesPage extends QueryPage { return $this->getLanguage()->specialList( $link, $count ); } + + protected function getGroupName() { + return 'highuse'; + } } diff --git a/includes/specials/SpecialMostimages.php b/includes/specials/SpecialMostimages.php index 3d797908..78b2d911 100644 --- a/includes/specials/SpecialMostimages.php +++ b/includes/specials/SpecialMostimages.php @@ -35,8 +35,13 @@ class MostimagesPage extends ImageQueryPage { parent::__construct( $name ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } function getQueryInfo() { return array ( @@ -53,4 +58,7 @@ class MostimagesPage extends ImageQueryPage { return $this->msg( 'nimagelinks' )->numParams( $row->value )->escaped() . '<br />'; } + protected function getGroupName() { + return 'highuse'; + } } diff --git a/includes/specials/SpecialMostinterwikis.php b/includes/specials/SpecialMostinterwikis.php index 894d697b..574a9afb 100644 --- a/includes/specials/SpecialMostinterwikis.php +++ b/includes/specials/SpecialMostinterwikis.php @@ -35,8 +35,13 @@ class MostinterwikisPage extends QueryPage { parent::__construct( $name ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } function getQueryInfo() { return array ( @@ -109,4 +114,8 @@ class MostinterwikisPage extends QueryPage { return $this->getLanguage()->specialList( $link, $count ); } + + protected function getGroupName() { + return 'highuse'; + } } diff --git a/includes/specials/SpecialMostlinked.php b/includes/specials/SpecialMostlinked.php index 89c43509..4b6e5670 100644 --- a/includes/specials/SpecialMostlinked.php +++ b/includes/specials/SpecialMostlinked.php @@ -36,8 +36,13 @@ class MostlinkedPage extends QueryPage { parent::__construct( $name ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } function getQueryInfo() { return array ( @@ -76,7 +81,7 @@ class MostlinkedPage extends QueryPage { * Make a link to "what links here" for the specified title * * @param $title Title being queried - * @param $caption String: text to display on the link + * @param string $caption text to display on the link * @return String */ function makeWlhLink( $title, $caption ) { @@ -102,4 +107,8 @@ class MostlinkedPage extends QueryPage { $this->msg( 'nlinks' )->numParams( $result->value )->escaped() ); return $this->getLanguage()->specialList( $link, $wlh ); } + + protected function getGroupName() { + return 'highuse'; + } } diff --git a/includes/specials/SpecialMostlinkedcategories.php b/includes/specials/SpecialMostlinkedcategories.php index dadef8bf..a1bce45d 100644 --- a/includes/specials/SpecialMostlinkedcategories.php +++ b/includes/specials/SpecialMostlinkedcategories.php @@ -35,7 +35,9 @@ class MostlinkedCategoriesPage extends QueryPage { parent::__construct( $name ); } - function isSyndicated() { return false; } + function isSyndicated() { + return false; + } function getQueryInfo() { return array ( @@ -46,7 +48,9 @@ class MostlinkedCategoriesPage extends QueryPage { ); } - function sortDescending() { return true; } + function sortDescending() { + return true; + } /** * Fetch user page links and cache their existence @@ -90,4 +94,8 @@ class MostlinkedCategoriesPage extends QueryPage { $nlinks = $this->msg( 'nmembers' )->numParams( $result->value )->escaped(); return $this->getLanguage()->specialList( $plink, $nlinks ); } + + protected function getGroupName() { + return 'highuse'; + } } diff --git a/includes/specials/SpecialMostlinkedtemplates.php b/includes/specials/SpecialMostlinkedtemplates.php index 22932e5c..506e6b22 100644 --- a/includes/specials/SpecialMostlinkedtemplates.php +++ b/includes/specials/SpecialMostlinkedtemplates.php @@ -124,5 +124,8 @@ class MostlinkedTemplatesPage extends QueryPage { $label = $this->msg( 'ntransclusions' )->numParams( $result->value )->escaped(); return Linker::link( $wlh, $label ); } -} + protected function getGroupName() { + return 'highuse'; + } +} diff --git a/includes/specials/SpecialMostrevisions.php b/includes/specials/SpecialMostrevisions.php index b0253316..ad6b788d 100644 --- a/includes/specials/SpecialMostrevisions.php +++ b/includes/specials/SpecialMostrevisions.php @@ -31,4 +31,8 @@ class MostrevisionsPage extends FewestrevisionsPage { function sortDescending() { return true; } + + protected function getGroupName() { + return 'highuse'; + } } diff --git a/includes/specials/SpecialMovepage.php b/includes/specials/SpecialMovepage.php index af3dbf3e..4adb0371 100644 --- a/includes/specials/SpecialMovepage.php +++ b/includes/specials/SpecialMovepage.php @@ -71,7 +71,6 @@ class MovePageForm extends UnlistedSpecialPage { ? Title::newFromText( $newTitleText_bc ) : Title::makeTitleSafe( $newTitleTextNs, $newTitleTextMain ); - $user = $this->getUser(); # Check rights @@ -104,7 +103,7 @@ class MovePageForm extends UnlistedSpecialPage { /** * Show the form * - * @param $err Array: error messages. Each item is an error message. + * @param array $err error messages. Each item is an error message. * It may either be a string message name or array message name and * parameters, like the second argument to OutputPage::wrapWikiMsg(). */ @@ -152,7 +151,7 @@ class MovePageForm extends UnlistedSpecialPage { </tr>"; $err = array(); } else { - if ($this->oldTitle->getNamespace() == NS_USER && !$this->oldTitle->isSubpage() ) { + if ( $this->oldTitle->getNamespace() == NS_USER && !$this->oldTitle->isSubpage() ) { $out->wrapWikiMsg( "<div class=\"error mw-moveuserpage-warning\">\n$1\n</div>", 'moveuserpage-warning' ); } $out->addWikiMsg( $wgFixDoubleRedirects ? 'movepagetext' : @@ -189,7 +188,7 @@ class MovePageForm extends UnlistedSpecialPage { array( 'rd_namespace' => $this->oldTitle->getNamespace(), 'rd_title' => $this->oldTitle->getDBkey(), - ) , __METHOD__ ); + ), __METHOD__ ); } else { $hasRedirects = false; } @@ -254,12 +253,14 @@ class MovePageForm extends UnlistedSpecialPage { } } + $handler = ContentHandler::getForTitle( $this->oldTitle ); + $out->addHTML( - Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalURL( 'action=submit' ), 'id' => 'movepage' ) ) . - Xml::openElement( 'fieldset' ) . - Xml::element( 'legend', null, $this->msg( 'move-page-legend' )->text() ) . - Xml::openElement( 'table', array( 'id' => 'mw-movepage-table' ) ) . - "<tr> + Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalURL( 'action=submit' ), 'id' => 'movepage' ) ) . + Xml::openElement( 'fieldset' ) . + Xml::element( 'legend', null, $this->msg( 'move-page-legend' )->text() ) . + Xml::openElement( 'table', array( 'id' => 'mw-movepage-table' ) ) . + "<tr> <td class='mw-label'>" . $this->msg( 'movearticle' )->escaped() . "</td> @@ -309,7 +310,7 @@ class MovePageForm extends UnlistedSpecialPage { ); } - if ( $user->isAllowed( 'suppressredirect' ) ) { + if ( $user->isAllowed( 'suppressredirect' ) && $handler->supportsRedirects() ) { $out->addHTML( " <tr> <td></td> @@ -342,7 +343,7 @@ class MovePageForm extends UnlistedSpecialPage { 'wpMovesubpages', # Don't check the box if we only have talk subpages to # move and we aren't moving the talk page. - $this->moveSubpages && ($this->oldTitle->hasSubpages() || $this->moveTalk), + $this->moveSubpages && ( $this->oldTitle->hasSubpages() || $this->moveTalk ), array( 'id' => 'wpMovesubpages' ) ) . ' ' . Xml::tags( 'label', array( 'for' => 'wpMovesubpages' ), @@ -357,7 +358,7 @@ class MovePageForm extends UnlistedSpecialPage { ); } - $watchChecked = $user->isLoggedIn() && ($this->watch || $user->getBoolOption( 'watchmoves' ) + $watchChecked = $user->isLoggedIn() && ( $this->watch || $user->getBoolOption( 'watchmoves' ) || $user->isWatched( $this->oldTitle ) ); # Don't allow watching if user is not logged in if( $user->isLoggedIn() ) { @@ -447,7 +448,11 @@ class MovePageForm extends UnlistedSpecialPage { } } - if ( $user->isAllowed( 'suppressredirect' ) ) { + $handler = ContentHandler::getForTitle( $ot ); + + if ( !$handler->supportsRedirects() ) { + $createRedirect = false; + } elseif ( $user->isAllowed( 'suppressredirect' ) ) { $createRedirect = $this->leaveRedirect; } else { $createRedirect = true; @@ -464,8 +469,6 @@ class MovePageForm extends UnlistedSpecialPage { DoubleRedirectJob::fixRedirects( 'move', $ot, $nt ); } - wfRunHooks( 'SpecialMovepageAfterMove', array( &$this, &$ot, &$nt ) ); - $out = $this->getOutput(); $out->setPageTitle( $this->msg( 'pagemovedsub' ) ); @@ -479,11 +482,23 @@ class MovePageForm extends UnlistedSpecialPage { $oldText = $ot->getPrefixedText(); $newText = $nt->getPrefixedText(); - $msgName = $createRedirect ? 'movepage-moved-redirect' : 'movepage-moved-noredirect'; + if ( $ot->exists() ) { + //NOTE: we assume that if the old title exists, it's because it was re-created as + // a redirect to the new title. This is not safe, but what we did before was + // even worse: we just determined whether a redirect should have been created, + // and reported that it was created if it should have, without any checks. + // Also note that isRedirect() is unreliable because of bug 37209. + $msgName = 'movepage-moved-redirect'; + } else { + $msgName = 'movepage-moved-noredirect'; + } + $out->addHTML( $this->msg( 'movepage-moved' )->rawParams( $oldLink, $newLink )->params( $oldText, $newText )->parseAsBlock() ); $out->addWikiMsg( $msgName ); + wfRunHooks( 'SpecialMovepageAfterMove', array( &$this, &$ot, &$nt ) ); + # Now we move extra pages we've been asked to move: subpages and talk # pages. First, if the old page or the new page is a talk page, we # can't move any talk pages: cancel that. @@ -521,10 +536,10 @@ class MovePageForm extends UnlistedSpecialPage { ); $conds['page_namespace'] = array(); if( MWNamespace::hasSubpages( $nt->getNamespace() ) ) { - $conds['page_namespace'] []= $ot->getNamespace(); + $conds['page_namespace'][] = $ot->getNamespace(); } if( $this->moveTalk && MWNamespace::hasSubpages( $nt->getTalkPage()->getNamespace() ) ) { - $conds['page_namespace'] []= $ot->getTalkPage()->getNamespace(); + $conds['page_namespace'][] = $ot->getTalkPage()->getNamespace(); } } elseif( $this->moveTalk ) { $conds = array( @@ -570,7 +585,7 @@ class MovePageForm extends UnlistedSpecialPage { $newSubpage = Title::makeTitleSafe( $newNs, $newPageName ); if( !$newSubpage ) { $oldLink = Linker::linkKnown( $oldSubpage ); - $extraOutput []= $this->msg( 'movepage-page-unmoved' )->rawParams( $oldLink + $extraOutput[] = $this->msg( 'movepage-page-unmoved' )->rawParams( $oldLink )->params( Title::makeName( $newNs, $newPageName ) )->escaped(); continue; } @@ -578,7 +593,7 @@ class MovePageForm extends UnlistedSpecialPage { # This was copy-pasted from Renameuser, bleh. if ( $newSubpage->exists() && !$oldSubpage->isValidMoveTarget( $newSubpage ) ) { $link = Linker::linkKnown( $newSubpage ); - $extraOutput []= $this->msg( 'movepage-page-exists' )->rawParams( $link )->escaped(); + $extraOutput[] = $this->msg( 'movepage-page-exists' )->rawParams( $link )->escaped(); } else { $success = $oldSubpage->moveTo( $newSubpage, true, $this->reason, $createRedirect ); if( $success === true ) { @@ -592,16 +607,16 @@ class MovePageForm extends UnlistedSpecialPage { array( 'redirect' => 'no' ) ); $newLink = Linker::linkKnown( $newSubpage ); - $extraOutput []= $this->msg( 'movepage-page-moved' )->rawParams( $oldLink, $newLink )->escaped(); + $extraOutput[] = $this->msg( 'movepage-page-moved' )->rawParams( $oldLink, $newLink )->escaped(); ++$count; if( $count >= $wgMaximumMovedPages ) { - $extraOutput []= $this->msg( 'movepage-max-pages' )->numParams( $wgMaximumMovedPages )->escaped(); + $extraOutput[] = $this->msg( 'movepage-max-pages' )->numParams( $wgMaximumMovedPages )->escaped(); break; } } else { $oldLink = Linker::linkKnown( $oldSubpage ); $newLink = Linker::link( $newSubpage ); - $extraOutput []= $this->msg( 'movepage-page-unmoved' )->rawParams( $oldLink, $newLink )->escaped(); + $extraOutput[] = $this->msg( 'movepage-page-unmoved' )->rawParams( $oldLink, $newLink )->escaped(); } } @@ -660,4 +675,8 @@ class MovePageForm extends UnlistedSpecialPage { } $out->addHTML( "</ul>\n" ); } + + protected function getGroupName() { + return 'pagetools'; + } } diff --git a/includes/specials/SpecialNewimages.php b/includes/specials/SpecialNewimages.php index 350aac63..52cbc3aa 100644 --- a/includes/specials/SpecialNewimages.php +++ b/includes/specials/SpecialNewimages.php @@ -22,11 +22,11 @@ */ class SpecialNewFiles extends IncludableSpecialPage { - public function __construct(){ + public function __construct() { parent::__construct( 'Newimages' ); } - public function execute( $par ){ + public function execute( $par ) { $this->setHeaders(); $this->outputHeader(); @@ -42,8 +42,11 @@ class SpecialNewFiles extends IncludableSpecialPage { $this->getOutput()->addHTML( $pager->getNavigationBar() ); } } -} + protected function getGroupName() { + return 'changes'; + } +} /** * @ingroup SpecialPage Pager @@ -57,7 +60,7 @@ class NewFilesPager extends ReverseChronologicalPager { function __construct( IContextSource $context, $par = null ) { $this->like = $context->getRequest()->getText( 'like' ); - $this->showbots = $context->getRequest()->getBool( 'showbots' , 0 ); + $this->showbots = $context->getRequest()->getBool( 'showbots', 0 ); if ( is_numeric( $par ) ) { $this->setLimit( $par ); } @@ -85,10 +88,10 @@ class NewFilesPager extends ReverseChronologicalPager { } } - if( !$wgMiserMode && $this->like !== null ){ + if( !$wgMiserMode && $this->like !== null ) { $dbr = wfGetDB( DB_SLAVE ); $likeObj = Title::newFromURL( $this->like ); - if( $likeObj instanceof Title ){ + if( $likeObj instanceof Title ) { $like = $dbr->buildLike( $dbr->anyString(), strtolower( $likeObj->getDBkey() ), $dbr->anyString() ); $conds[] = "LOWER(img_name) $like"; } @@ -104,18 +107,18 @@ class NewFilesPager extends ReverseChronologicalPager { return $query; } - function getIndexField(){ + function getIndexField() { return 'img_timestamp'; } - function getStartBody(){ + function getStartBody() { if ( !$this->gallery ) { $this->gallery = new ImageGallery(); } return ''; } - function getEndBody(){ + function getEndBody() { return $this->gallery->toHTML(); } @@ -161,7 +164,7 @@ class NewFilesPager extends ReverseChronologicalPager { ), ); - if( $wgMiserMode ){ + if( $wgMiserMode ) { unset( $fields['like'] ); } diff --git a/includes/specials/SpecialNewpages.php b/includes/specials/SpecialNewpages.php index 8e15d554..ebb3021d 100644 --- a/includes/specials/SpecialNewpages.php +++ b/includes/specials/SpecialNewpages.php @@ -53,12 +53,13 @@ class SpecialNewpages extends IncludableSpecialPage { $opts->add( 'hidepatrolled', $this->getUser()->getBoolOption( 'newpageshidepatrolled' ) ); $opts->add( 'hidebots', false ); $opts->add( 'hideredirs', true ); - $opts->add( 'limit', (int)$this->getUser()->getOption( 'rclimit' ) ); + $opts->add( 'limit', $this->getUser()->getIntOption( 'rclimit' ) ); $opts->add( 'offset', '' ); $opts->add( 'namespace', '0' ); $opts->add( 'username', '' ); $opts->add( 'feed', '' ); $opts->add( 'tagfilter', '' ); + $opts->add( 'invert', false ); $this->customFilters = array(); wfRunHooks( 'SpecialNewPagesFilters', array( $this, &$this->customFilters ) ); @@ -105,7 +106,7 @@ class SpecialNewpages extends IncludableSpecialPage { } // PG offsets not just digits! if ( preg_match( '/^offset=([^=]+)$/', $bit, $m ) ) { - $this->opts->setValue( 'offset', intval( $m[1] ) ); + $this->opts->setValue( 'offset', intval( $m[1] ) ); } if ( preg_match( '/^username=(.*)$/', $bit, $m ) ) { $this->opts->setValue( 'username', $m[1] ); @@ -113,7 +114,7 @@ class SpecialNewpages extends IncludableSpecialPage { if ( preg_match( '/^namespace=(.*)$/', $bit, $m ) ) { $ns = $this->getLanguage()->getNsIndex( $m[1] ); if( $ns !== false ) { - $this->opts->setValue( 'namespace', $ns ); + $this->opts->setValue( 'namespace', $ns ); } } } @@ -140,12 +141,13 @@ class SpecialNewpages extends IncludableSpecialPage { $feedType = $this->opts->getValue( 'feed' ); if( $feedType ) { - return $this->feed( $feedType ); + $this->feed( $feedType ); + return; } $allValues = $this->opts->getAllValues(); unset( $allValues['feed'] ); - $out->setFeedAppendQuery( wfArrayToCGI( $allValues ) ); + $out->setFeedAppendQuery( wfArrayToCgi( $allValues ) ); } $pager = new NewPagesPager( $this, $this->opts ); @@ -164,8 +166,6 @@ class SpecialNewpages extends IncludableSpecialPage { } protected function filterLinks() { - global $wgGroupPermissions; - // show/hide links $showhide = array( $this->msg( 'show' )->escaped(), $this->msg( 'hide' )->escaped() ); @@ -181,8 +181,7 @@ class SpecialNewpages extends IncludableSpecialPage { } // Disable some if needed - # @todo FIXME: Throws E_NOTICEs if not set; and doesn't obey hooks etc. - if ( $wgGroupPermissions['*']['createpage'] !== true ) { + if ( !User::groupHasPermission( '*', 'createpage' ) ) { unset( $filters['hideliu'] ); } if ( !$this->getUser()->useNPPatrol() ) { @@ -213,6 +212,7 @@ class SpecialNewpages extends IncludableSpecialPage { $namespace = $this->opts->consumeValue( 'namespace' ); $username = $this->opts->consumeValue( 'username' ); $tagFilterVal = $this->opts->consumeValue( 'tagfilter' ); + $nsinvert = $this->opts->consumeValue( 'invert' ); // Check username input validity $ut = Title::makeTitleSafe( NS_USER, $username ); @@ -248,6 +248,13 @@ class SpecialNewpages extends IncludableSpecialPage { 'id' => 'namespace', 'class' => 'namespaceselector', ) + ) . ' ' . + Xml::checkLabel( + $this->msg( 'invert' )->text(), + 'invert', + 'nsinvert', + $nsinvert, + array( 'title' => $this->msg( 'tooltip-invert' )->text() ) ) . '</td> </tr>' . ( $tagFilter ? ( @@ -298,11 +305,11 @@ class SpecialNewpages extends IncludableSpecialPage { # Revision deletion works on revisions, so we should cast one $row = array( - 'comment' => $result->rc_comment, - 'deleted' => $result->rc_deleted, - 'user_text' => $result->rc_user_text, - 'user' => $result->rc_user, - ); + 'comment' => $result->rc_comment, + 'deleted' => $result->rc_deleted, + 'user_text' => $result->rc_user_text, + 'user' => $result->rc_user, + ); $rev = new Revision( $row ); $rev->setTitle( $title ); @@ -328,12 +335,13 @@ class SpecialNewpages extends IncludableSpecialPage { $query['rcid'] = $result->rc_id; } - $plink = Linker::linkKnown( + // Linker::linkKnown() uses 'known' and 'noclasses' options. This breaks the colouration for stubs. + $plink = Linker::link( $title, null, array( 'class' => 'mw-newpages-pagename' ), $query, - array( 'known' ) // Set explicitly to avoid the default of 'known','noclasses'. This breaks the colouration for stubs + array( 'known' ) ); $histLink = Linker::linkKnown( $title, @@ -459,14 +467,19 @@ class SpecialNewpages extends IncludableSpecialPage { protected function feedItemDesc( $row ) { $revision = Revision::newFromId( $row->rev_id ); if( $revision ) { + //XXX: include content model/type in feed item? return '<p>' . htmlspecialchars( $revision->getUserText() ) . $this->msg( 'colon-separator' )->inContentLanguage()->escaped() . htmlspecialchars( FeedItem::stripComment( $revision->getComment() ) ) . "</p>\n<hr />\n<div>" . - nl2br( htmlspecialchars( $revision->getText() ) ) . "</div>"; + nl2br( htmlspecialchars( $revision->getContent()->serialize() ) ) . "</div>"; } return ''; } + + protected function getGroupName() { + return 'changes'; + } } /** @@ -488,7 +501,7 @@ class NewPagesPager extends ReverseChronologicalPager { } function getQueryInfo() { - global $wgEnableNewpagesUserFilter, $wgGroupPermissions; + global $wgEnableNewpagesUserFilter; $conds = array(); $conds['rc_new'] = 1; @@ -499,7 +512,11 @@ class NewPagesPager extends ReverseChronologicalPager { $user = Title::makeTitleSafe( NS_USER, $username ); if( $namespace !== false ) { - $conds['rc_namespace'] = $namespace; + if ( $this->opts->getValue( 'invert' ) ) { + $conds[] = 'rc_namespace != ' . $this->mDb->addQuotes( $namespace ); + } else { + $conds['rc_namespace'] = $namespace; + } $rcIndexes = array( 'new_name_timestamp' ); } else { $rcIndexes = array( 'rc_timestamp' ); @@ -510,7 +527,7 @@ class NewPagesPager extends ReverseChronologicalPager { $conds['rc_user_text'] = $user->getText(); $rcIndexes = 'rc_user_text'; # If anons cannot make new pages, don't "exclude logged in users"! - } elseif( $wgGroupPermissions['*']['createpage'] && $this->opts->getValue( 'hideliu' ) ) { + } elseif( User::groupHasPermission( '*', 'createpage' ) && $this->opts->getValue( 'hideliu' ) ) { $conds['rc_user'] = 0; } # If this user cannot see patrolled edits or they are off, don't do dumb queries! diff --git a/includes/specials/SpecialPagesWithProp.php b/includes/specials/SpecialPagesWithProp.php new file mode 100644 index 00000000..8f8c981e --- /dev/null +++ b/includes/specials/SpecialPagesWithProp.php @@ -0,0 +1,138 @@ +<?php +/** + * Implements Special:PagesWithProp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @since 1.21 + * @file + * @ingroup SpecialPage + * @author Brad Jorsch + */ + + +/** + * Special:PagesWithProp to search the page_props table + * @ingroup SpecialPage + * @since 1.21 + */ +class SpecialPagesWithProp extends QueryPage { + private $propName = null; + + function __construct( $name = 'PagesWithProp' ) { + parent::__construct( $name ); + } + + function isCacheable() { + return false; + } + + function execute( $par ) { + $this->setHeaders(); + $this->outputHeader(); + + $request = $this->getRequest(); + $propname = $request->getVal( 'propname', $par ); + + $dbr = wfGetDB( DB_SLAVE ); + $res = $dbr->select( + 'page_props', + 'pp_propname', + '', + __METHOD__, + array( 'DISTINCT', 'ORDER BY' => 'pp_propname' ) + ); + foreach ( $res as $row ) { + $propnames[$row->pp_propname] = $row->pp_propname; + } + + $form = new HTMLForm( array( + 'propname' => array( + 'type' => 'selectorother', + 'name' => 'propname', + 'options' => $propnames, + 'default' => $propname, + 'label-message' => 'pageswithprop-prop', + 'required' => true, + ), + ), $this->getContext() ); + $form->setMethod( 'get' ); + $form->setAction( $this->getTitle()->getFullUrl() ); + $form->setSubmitCallback( array( $this, 'onSubmit' ) ); + $form->setWrapperLegend( $this->msg( 'pageswithprop-legend' ) ); + $form->addHeaderText( $this->msg( 'pageswithprop-text' )->parseAsBlock() ); + $form->setSubmitTextMsg( 'pageswithprop-submit' ); + + $form->prepareForm(); + $form->displayForm( false ); + if ( $propname !== '' && $propname !== null ) { + $form->trySubmit(); + } + } + + public function onSubmit( $data, $form ) { + $this->propName = $data['propname']; + parent::execute( $data['propname'] ); + } + + /** + * Disable RSS/Atom feeds + * @return bool + */ + function isSyndicated() { + return false; + } + + function getQueryInfo() { + return array( + 'tables' => array( 'page_props', 'page' ), + 'fields' => array( + 'page_id' => 'pp_page', + 'page_namespace', + 'page_title', + 'page_len', + 'page_is_redirect', + 'page_latest', + 'pp_value', + ), + 'conds' => array( + 'page_id = pp_page', + 'pp_propname' => $this->propName, + ), + 'options' => array() + ); + } + + function getOrderFields() { + return array( 'page_id' ); + } + + function formatResult( $skin, $result ) { + $title = Title::newFromRow( $result ); + $ret = Linker::link( $title, null, array(), array(), array( 'known' ) ); + if ( $result->pp_value !== '' ) { + $value = $this->msg( 'parentheses' ) + ->rawParams( Xml::span( $result->pp_value, 'prop-value' ) ) + ->escaped(); + $ret .= " $value"; + } + return $ret; + } + + protected function getGroupName() { + return 'pages'; + } +} diff --git a/includes/specials/SpecialPasswordReset.php b/includes/specials/SpecialPasswordReset.php index efb57657..90b0ac80 100644 --- a/includes/specials/SpecialPasswordReset.php +++ b/includes/specials/SpecialPasswordReset.php @@ -86,7 +86,7 @@ class SpecialPasswordReset extends FormSpecialPage { ); } - if( $this->getUser()->isAllowed( 'passwordreset' ) ){ + if( $this->getUser()->isAllowed( 'passwordreset' ) ) { $a['Capture'] = array( 'type' => 'check', 'label-message' => 'passwordreset-capture', @@ -121,6 +121,8 @@ class SpecialPasswordReset extends FormSpecialPage { * userCanExecute(), and if the data array contains 'Username', etc, then Username * resets are allowed. * @param $data array + * @throws MWException + * @throws ThrottledError|PermissionsError * @return Bool|Array */ public function onSubmit( array $data ) { @@ -134,7 +136,7 @@ class SpecialPasswordReset extends FormSpecialPage { } } - if( isset( $data['Capture'] ) && !$this->getUser()->isAllowed( 'passwordreset' ) ){ + if( isset( $data['Capture'] ) && !$this->getUser()->isAllowed( 'passwordreset' ) ) { // The user knows they don't have the passwordreset permission, but they tried to spoof the form. That's naughty throw new PermissionsError( 'passwordreset' ); } @@ -160,7 +162,7 @@ class SpecialPasswordReset extends FormSpecialPage { ); if ( $res ) { $users = array(); - foreach( $res as $row ){ + foreach( $res as $row ) { $users[] = User::newFromRow( $row ); } } else { @@ -178,8 +180,8 @@ class SpecialPasswordReset extends FormSpecialPage { return array( $error ); } - if( count( $users ) == 0 ){ - if( $method == 'email' ){ + if( count( $users ) == 0 ) { + if( $method == 'email' ) { // Don't reveal whether or not an email address is in use return true; } else { @@ -247,13 +249,13 @@ class SpecialPasswordReset extends FormSpecialPage { $username, $passwordBlock, count( $passwords ), - Title::newMainPage()->getCanonicalUrl(), + '<' . Title::newMainPage()->getCanonicalUrl() . '>', round( $wgNewPasswordExpiry / 86400 ) ); $title = $this->msg( 'passwordreset-emailtitle' ); - $this->result = $firstUser->sendMail( $title->escaped(), $this->email->escaped() ); + $this->result = $firstUser->sendMail( $title->escaped(), $this->email->text() ); // Blank the email if the user is not supposed to see it if( !isset( $data['Capture'] ) || !$data['Capture'] ) { @@ -262,7 +264,7 @@ class SpecialPasswordReset extends FormSpecialPage { if ( $this->result->isGood() ) { return true; - } elseif( isset( $data['Capture'] ) && $data['Capture'] ){ + } elseif( isset( $data['Capture'] ) && $data['Capture'] ) { // The email didn't send, but maybe they knew that and that's why they captured it return true; } else { @@ -273,10 +275,10 @@ class SpecialPasswordReset extends FormSpecialPage { } public function onSuccess() { - if( $this->getUser()->isAllowed( 'passwordreset' ) && $this->email != null ){ + if( $this->getUser()->isAllowed( 'passwordreset' ) && $this->email != null ) { // @todo: Logging - if( $this->result->isGood() ){ + if( $this->result->isGood() ) { $this->getOutput()->addWikiMsg( 'passwordreset-emailsent-capture' ); } else { $this->getOutput()->addWikiMsg( 'passwordreset-emailerror-capture', $this->result->getMessage() ); @@ -324,4 +326,8 @@ class SpecialPasswordReset extends FormSpecialPage { return false; } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialPopularpages.php b/includes/specials/SpecialPopularpages.php index 448d1799..7ce8c13f 100644 --- a/includes/specials/SpecialPopularpages.php +++ b/includes/specials/SpecialPopularpages.php @@ -37,14 +37,16 @@ class PopularPagesPage extends QueryPage { return true; } - function isSyndicated() { return false; } + function isSyndicated() { + return false; + } function getQueryInfo() { return array ( 'tables' => array( 'page' ), 'fields' => array( 'namespace' => 'page_namespace', 'title' => 'page_title', - 'value' => 'page_counter'), + 'value' => 'page_counter' ), 'conds' => array( 'page_is_redirect' => 0, 'page_namespace' => MWNamespace::getContentNamespaces() ) ); } @@ -70,4 +72,8 @@ class PopularPagesPage extends QueryPage { $nv = $this->msg( 'nviews' )->numParams( $result->value )->escaped(); return $this->getLanguage()->specialList( $link, $nv ); } + + protected function getGroupName() { + return 'wiki'; + } } diff --git a/includes/specials/SpecialPreferences.php b/includes/specials/SpecialPreferences.php index c6b2bb6b..a50e7c18 100644 --- a/includes/specials/SpecialPreferences.php +++ b/includes/specials/SpecialPreferences.php @@ -78,7 +78,7 @@ class SpecialPreferences extends SpecialPage { public function submitReset( $formData ) { $user = $this->getUser(); - $user->resetOptions(); + $user->resetOptions( 'all' ); $user->saveSettings(); $url = $this->getTitle()->getFullURL( 'success' ); @@ -87,4 +87,8 @@ class SpecialPreferences extends SpecialPage { return true; } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialPrefixindex.php b/includes/specials/SpecialPrefixindex.php index 7740b320..6affa735 100644 --- a/includes/specials/SpecialPrefixindex.php +++ b/includes/specials/SpecialPrefixindex.php @@ -29,13 +29,13 @@ class SpecialPrefixindex extends SpecialAllpages { // Inherit $maxPerPage - function __construct(){ + function __construct() { parent::__construct( 'Prefixindex' ); } /** * Entry point : initialise variables and call subfunctions. - * @param $par String: becomes "FOO" when called like Special:Prefixindex/FOO (default null) + * @param string $par becomes "FOO" when called like Special:Prefixindex/FOO (default null) */ function execute( $par ) { global $wgContLang; @@ -83,14 +83,14 @@ class SpecialPrefixindex extends SpecialAllpages { /** * HTML for the top form * @param $namespace Integer: a namespace constant (default NS_MAIN). - * @param $from String: dbKey we are starting listing at. - * @param $hideredirects Bool: hide redirects (default FALSE) + * @param string $from dbKey we are starting listing at. + * @param bool $hideredirects hide redirects (default FALSE) * @return string */ function namespacePrefixForm( $namespace = NS_MAIN, $from = '', $hideredirects = false ) { global $wgScript; - $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) ); + $out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) ); $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ); $out .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ); $out .= Xml::openElement( 'fieldset' ); @@ -101,7 +101,7 @@ class SpecialPrefixindex extends SpecialAllpages { Xml::label( $this->msg( 'allpagesprefix' )->text(), 'nsfrom' ) . "</td> <td class='mw-input'>" . - Xml::input( 'prefix', 30, str_replace('_',' ',$from), array( 'id' => 'nsfrom' ) ) . + Xml::input( 'prefix', 30, str_replace( '_', ' ', $from ), array( 'id' => 'nsfrom' ) ) . "</td> </tr> <tr> @@ -135,8 +135,8 @@ class SpecialPrefixindex extends SpecialAllpages { /** * @param $namespace Integer, default NS_MAIN * @param $prefix String - * @param $from String: list all pages from this name (default FALSE) - * @param $hideredirects Bool: hide redirects (default FALSE) + * @param string $from list all pages from this name (default FALSE) + * @param bool $hideredirects hide redirects (default FALSE) */ function showPrefixChunk( $namespace = NS_MAIN, $prefix, $from = null, $hideredirects = false ) { global $wgContLang; @@ -145,8 +145,8 @@ class SpecialPrefixindex extends SpecialAllpages { $from = $prefix; } - $fromList = $this->getNamespaceKeyAndText($namespace, $from); - $prefixList = $this->getNamespaceKeyAndText($namespace, $prefix); + $fromList = $this->getNamespaceKeyAndText( $namespace, $from ); + $prefixList = $this->getNamespaceKeyAndText( $namespace, $prefix ); $namespaces = $wgContLang->getNamespaces(); if ( !$prefixList || !$fromList ) { @@ -227,7 +227,7 @@ class SpecialPrefixindex extends SpecialAllpages { } else { $nsForm = $this->namespacePrefixForm( $namespace, $prefix, $hideredirects ); $self = $this->getTitle(); - $out2 = Xml::openElement( 'table', array( 'id' => 'mw-prefixindex-nav-table' ) ) . + $out2 = Xml::openElement( 'table', array( 'id' => 'mw-prefixindex-nav-table' ) ) . '<tr> <td>' . $nsForm . @@ -241,14 +241,14 @@ class SpecialPrefixindex extends SpecialAllpages { 'hideredirects' => $hideredirects, ); - if( $namespace || ($prefix == '')) { + if( $namespace || $prefix == '' ) { // Keep the namespace even if it's 0 for empty prefixes. // This tells us we're not just a holdover from old links. $query['namespace'] = $namespace; } $nextLink = Linker::linkKnown( $self, - $this->msg( 'nextpage', str_replace( '_',' ', $s->page_title ) )->escaped(), + $this->msg( 'nextpage', str_replace( '_', ' ', $s->page_title ) )->escaped(), array(), $query ); @@ -263,4 +263,8 @@ class SpecialPrefixindex extends SpecialAllpages { $this->getOutput()->addHTML( $out2 . $out . $footer ); } + + protected function getGroupName() { + return 'pages'; + } } diff --git a/includes/specials/SpecialProtectedpages.php b/includes/specials/SpecialProtectedpages.php index 74ed5378..cdf053ec 100644 --- a/includes/specials/SpecialProtectedpages.php +++ b/includes/specials/SpecialProtectedpages.php @@ -29,7 +29,7 @@ class SpecialProtectedpages extends SpecialPage { protected $IdLevel = 'level'; - protected $IdType = 'type'; + protected $IdType = 'type'; public function __construct() { parent::__construct( 'Protectedpages' ); @@ -51,7 +51,7 @@ class SpecialProtectedpages extends SpecialPage { $size = $request->getIntOrNull( 'size' ); $NS = $request->getIntOrNull( 'namespace' ); $indefOnly = $request->getBool( 'indefonly' ) ? 1 : 0; - $cascadeOnly = $request->getBool('cascadeonly') ? 1 : 0; + $cascadeOnly = $request->getBool( 'cascadeonly' ) ? 1 : 0; $pager = new ProtectedPagesPager( $this, array(), $type, $level, $NS, $sizetype, $size, $indefOnly, $cascadeOnly ); @@ -78,11 +78,17 @@ class SpecialProtectedpages extends SpecialPage { static $infinity = null; - if( is_null( $infinity ) ){ + if( is_null( $infinity ) ) { $infinity = wfGetDB( DB_SLAVE )->getInfinity(); } $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title ); + if( !$title ) { + return Html::rawElement( 'li', array(), + Html::element( 'span', array( 'class' => 'mw-invalidtitle' ), + Linker::getInvalidTitleDescription( $this->getContext(), $row->page_namespace, $row->page_title ) ) ) . "\n"; + } + $link = Linker::link( $title ); $description_items = array (); @@ -109,7 +115,7 @@ class SpecialProtectedpages extends SpecialPage { )->escaped(); } - if(!is_null($size = $row->page_len)) { + if( !is_null( $size = $row->page_len ) ) { $stxt = $lang->getDirMark() . ' ' . Linker::formatRevisionSize( $size ); } @@ -146,15 +152,15 @@ class SpecialProtectedpages extends SpecialPage { /** * @param $namespace Integer - * @param $type String: restriction type - * @param $level String: restriction level - * @param $sizetype String: "min" or "max" + * @param string $type restriction type + * @param string $level restriction level + * @param string $sizetype "min" or "max" * @param $size Integer * @param $indefOnly Boolean: only indefinie protection * @param $cascadeOnly Boolean: only cascading protection * @return String: input form */ - protected function showOptions( $namespace, $type='edit', $level, $sizetype, $size, $indefOnly, $cascadeOnly ) { + protected function showOptions( $namespace, $type = 'edit', $level, $sizetype, $size, $indefOnly, $cascadeOnly ) { global $wgScript; $title = $this->getTitle(); return Xml::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript ) ) . @@ -272,7 +278,7 @@ class SpecialProtectedpages extends SpecialPage { // First pass to load the log names foreach( $wgRestrictionLevels as $type ) { // Messages used can be 'restriction-level-sysop' and 'restriction-level-autoconfirmed' - if( $type !='' && $type !='*') { + if( $type != '' && $type != '*' ) { $text = $this->msg( "restriction-level-$type" )->text(); $m[$text] = $type; } @@ -290,6 +296,10 @@ class SpecialProtectedpages extends SpecialPage { array( 'id' => $this->IdLevel, 'name' => $this->IdLevel ), implode( "\n", $options ) ) . "</span>"; } + + protected function getGroupName() { + return 'maintenance'; + } } /** @@ -300,7 +310,7 @@ class ProtectedPagesPager extends AlphabeticPager { public $mForm, $mConds; private $type, $level, $namespace, $sizetype, $size, $indefonly; - function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype='', $size=0, + function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype = '', $size = 0, $indefonly = false, $cascadeonly = false ) { $this->mForm = $form; @@ -309,7 +319,7 @@ class ProtectedPagesPager extends AlphabeticPager { $this->level = $level; $this->namespace = $namespace; $this->sizetype = $sizetype; - $this->size = intval($size); + $this->size = intval( $size ); $this->indefonly = (bool)$indefonly; $this->cascadeonly = (bool)$cascadeonly; parent::__construct( $form->getContext() ); @@ -336,27 +346,27 @@ class ProtectedPagesPager extends AlphabeticPager { $conds[] = 'page_id=pr_page'; $conds[] = 'pr_type=' . $this->mDb->addQuotes( $this->type ); - if( $this->sizetype=='min' ) { + if( $this->sizetype == 'min' ) { $conds[] = 'page_len>=' . $this->size; - } elseif( $this->sizetype=='max' ) { + } elseif( $this->sizetype == 'max' ) { $conds[] = 'page_len<=' . $this->size; } if( $this->indefonly ) { - $db = wfGetDB( DB_SLAVE ); - $conds[] = "pr_expiry = {$db->addQuotes( $db->getInfinity() )} OR pr_expiry IS NULL"; + $conds[] = "pr_expiry = {$this->mDb->addQuotes( $this->mDb->getInfinity() )} OR pr_expiry IS NULL"; } if( $this->cascadeonly ) { - $conds[] = "pr_cascade = '1'"; + $conds[] = 'pr_cascade = 1'; } if( $this->level ) $conds[] = 'pr_level=' . $this->mDb->addQuotes( $this->level ); - if( !is_null($this->namespace) ) + if( !is_null( $this->namespace ) ) $conds[] = 'page_namespace=' . $this->mDb->addQuotes( $this->namespace ); return array( 'tables' => array( 'page_restrictions', 'page' ), - 'fields' => 'pr_id,page_namespace,page_title,page_len,pr_type,pr_level,pr_expiry,pr_cascade', + 'fields' => array( 'pr_id', 'page_namespace', 'page_title', 'page_len', + 'pr_type', 'pr_level', 'pr_expiry', 'pr_cascade' ), 'conds' => $conds ); } diff --git a/includes/specials/SpecialProtectedtitles.php b/includes/specials/SpecialProtectedtitles.php index a80f0d0a..0cba5ebd 100644 --- a/includes/specials/SpecialProtectedtitles.php +++ b/includes/specials/SpecialProtectedtitles.php @@ -29,7 +29,7 @@ class SpecialProtectedtitles extends SpecialPage { protected $IdLevel = 'level'; - protected $IdType = 'type'; + protected $IdType = 'type'; public function __construct() { parent::__construct( 'Protectedtitles' ); @@ -76,11 +76,17 @@ class SpecialProtectedtitles extends SpecialPage { static $infinity = null; - if( is_null( $infinity ) ){ + if( is_null( $infinity ) ) { $infinity = wfGetDB( DB_SLAVE )->getInfinity(); } $title = Title::makeTitleSafe( $row->pt_namespace, $row->pt_title ); + if( !$title ) { + return Html::rawElement( 'li', array(), + Html::element( 'span', array( 'class' => 'mw-invalidtitle' ), + Linker::getInvalidTitleDescription( $this->getContext(), $row->pt_namespace, $row->pt_title ) ) ) . "\n"; + } + $link = Linker::link( $title ); $description_items = array (); @@ -113,7 +119,7 @@ class SpecialProtectedtitles extends SpecialPage { * @return string * @private */ - function showOptions( $namespace, $type='edit', $level ) { + function showOptions( $namespace, $type = 'edit', $level ) { global $wgScript; $action = htmlspecialchars( $wgScript ); $title = $this->getTitle(); @@ -161,13 +167,13 @@ class SpecialProtectedtitles extends SpecialPage { // First pass to load the log names foreach( $wgRestrictionLevels as $type ) { - if ( $type !='' && $type !='*') { + if ( $type != '' && $type != '*' ) { $text = $this->msg( "restriction-level-$type" )->text(); $m[$text] = $type; } } // Is there only one level (aside from "all")? - if( count($m) <= 2 ) { + if( count( $m ) <= 2 ) { return ''; } // Third pass generates sorted XHTML content @@ -182,6 +188,10 @@ class SpecialProtectedtitles extends SpecialPage { array( 'id' => $this->IdLevel, 'name' => $this->IdLevel ), implode( "\n", $options ) ); } + + protected function getGroupName() { + return 'maintenance'; + } } /** @@ -191,7 +201,7 @@ class SpecialProtectedtitles extends SpecialPage { class ProtectedTitlesPager extends AlphabeticPager { public $mForm, $mConds; - function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype='', $size=0 ) { + function __construct( $form, $conds = array(), $type, $level, $namespace, $sizetype = '', $size = 0 ) { $this->mForm = $form; $this->mConds = $conds; $this->level = $level; @@ -234,11 +244,12 @@ class ProtectedTitlesPager extends AlphabeticPager { $conds[] = 'pt_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() ); if( $this->level ) $conds['pt_create_perm'] = $this->level; - if( !is_null($this->namespace) ) + if( !is_null( $this->namespace ) ) $conds[] = 'pt_namespace=' . $this->mDb->addQuotes( $this->namespace ); return array( 'tables' => 'protected_titles', - 'fields' => 'pt_namespace,pt_title,pt_create_perm,pt_expiry,pt_timestamp', + 'fields' => array( 'pt_namespace', 'pt_title', 'pt_create_perm', + 'pt_expiry', 'pt_timestamp' ), 'conds' => $conds ); } @@ -247,4 +258,3 @@ class ProtectedTitlesPager extends AlphabeticPager { return 'pt_timestamp'; } } - diff --git a/includes/specials/SpecialRandompage.php b/includes/specials/SpecialRandompage.php index 307088ed..b59f8349 100644 --- a/includes/specials/SpecialRandompage.php +++ b/includes/specials/SpecialRandompage.php @@ -28,11 +28,11 @@ * @ingroup SpecialPage */ class RandomPage extends SpecialPage { - private $namespaces; // namespaces to select pages from + private $namespaces; // namespaces to select pages from protected $isRedir = false; // should the result be a redirect? protected $extra = array(); // Extra SQL statements - public function __construct( $name = 'Randompage' ){ + public function __construct( $name = 'Randompage' ) { $this->namespaces = MWNamespace::getContentNamespaces(); parent::__construct( $name ); } @@ -49,7 +49,7 @@ class RandomPage extends SpecialPage { } // select redirects instead of normal pages? - public function isRedirect(){ + public function isRedirect() { return $this->isRedir; } @@ -159,4 +159,8 @@ class RandomPage extends SpecialPage { return $dbr->fetchObject( $res ); } + + protected function getGroupName() { + return 'redirects'; + } } diff --git a/includes/specials/SpecialRandomredirect.php b/includes/specials/SpecialRandomredirect.php index 88c81b31..51783a2f 100644 --- a/includes/specials/SpecialRandomredirect.php +++ b/includes/specials/SpecialRandomredirect.php @@ -28,7 +28,7 @@ * @ingroup SpecialPage */ class SpecialRandomredirect extends RandomPage { - function __construct(){ + function __construct() { parent::__construct( 'Randomredirect' ); $this->isRedir = true; } diff --git a/includes/specials/SpecialRecentchanges.php b/includes/specials/SpecialRecentchanges.php index 2bd8b0a9..008678f7 100644 --- a/includes/specials/SpecialRecentchanges.php +++ b/includes/specials/SpecialRecentchanges.php @@ -42,16 +42,16 @@ class SpecialRecentChanges extends IncludableSpecialPage { public function getDefaultOptions() { $opts = new FormOptions(); - $opts->add( 'days', (int)$this->getUser()->getOption( 'rcdays' ) ); - $opts->add( 'limit', (int)$this->getUser()->getOption( 'rclimit' ) ); + $opts->add( 'days', $this->getUser()->getIntOption( 'rcdays' ) ); + $opts->add( 'limit', $this->getUser()->getIntOption( 'rclimit' ) ); $opts->add( 'from', '' ); - $opts->add( 'hideminor', $this->getUser()->getBoolOption( 'hideminor' ) ); - $opts->add( 'hidebots', true ); - $opts->add( 'hideanons', false ); - $opts->add( 'hideliu', false ); + $opts->add( 'hideminor', $this->getUser()->getBoolOption( 'hideminor' ) ); + $opts->add( 'hidebots', true ); + $opts->add( 'hideanons', false ); + $opts->add( 'hideliu', false ); $opts->add( 'hidepatrolled', $this->getUser()->getBoolOption( 'hidepatrolled' ) ); - $opts->add( 'hidemyself', false ); + $opts->add( 'hidemyself', false ); $opts->add( 'namespace', '', FormOptions::INTNULL ); $opts->add( 'invert', false ); @@ -109,8 +109,7 @@ class SpecialRecentChanges extends IncludableSpecialPage { public function feedSetup() { global $wgFeedLimit; $opts = $this->getDefaultOptions(); - # Feed is cached on limit,hideminor,namespace; other params would randomly not work - $opts->fetchValuesFromRequest( $this->getRequest(), array( 'limit', 'hideminor', 'namespace' ) ); + $opts->fetchValuesFromRequest( $this->getRequest() ); $opts->validateIntBounds( 'limit', 0, $wgFeedLimit ); return $opts; } @@ -130,7 +129,6 @@ class SpecialRecentChanges extends IncludableSpecialPage { return $this->rcOptions; } - /** * Main execution point * @@ -156,7 +154,7 @@ class SpecialRecentChanges extends IncludableSpecialPage { // Fetch results, prepare a batch link existence check query $conds = $this->buildMainQueryConds( $opts ); $rows = $this->doMainQuery( $conds, $opts ); - if( $rows === false ){ + if( $rows === false ) { if( !$this->including() ) { $this->doHeader( $opts ); } @@ -187,7 +185,7 @@ class SpecialRecentChanges extends IncludableSpecialPage { * * @return Array */ - public function getFeedObject( $feedFormat ){ + public function getFeedObject( $feedFormat ) { $changesFeed = new ChangesFeed( $feedFormat, 'rcfeed' ); $formatter = $changesFeed->getFeedObject( $this->msg( 'recentchanges' )->inContentLanguage()->text(), @@ -233,7 +231,7 @@ class SpecialRecentChanges extends IncludableSpecialPage { } if( is_numeric( $bit ) ) { - $opts['limit'] = $bit; + $opts['limit'] = $bit; } $m = array(); @@ -282,9 +280,9 @@ class SpecialRecentChanges extends IncludableSpecialPage { # It makes no sense to hide both anons and logged-in users # Where this occurs, force anons to be shown $forcebot = false; - if( $opts['hideanons'] && $opts['hideliu'] ){ + if( $opts['hideanons'] && $opts['hideliu'] ) { # Check if the user wants to show bots only - if( $opts['hidebots'] ){ + if( $opts['hidebots'] ) { $opts['hideanons'] = false; } else { $forcebot = true; @@ -297,9 +295,9 @@ class SpecialRecentChanges extends IncludableSpecialPage { $cutoff_unixtime = $cutoff_unixtime - ($cutoff_unixtime % 86400); $cutoff = $dbr->timestamp( $cutoff_unixtime ); - $fromValid = preg_match('/^[0-9]{14}$/', $opts['from']); - if( $fromValid && $opts['from'] > wfTimestamp(TS_MW,$cutoff) ) { - $cutoff = $dbr->timestamp($opts['from']); + $fromValid = preg_match( '/^[0-9]{14}$/', $opts['from'] ); + if( $fromValid && $opts['from'] > wfTimestamp( TS_MW, $cutoff ) ) { + $cutoff = $dbr->timestamp( $opts['from'] ); } else { $opts->reset( 'from' ); } @@ -341,7 +339,7 @@ class SpecialRecentChanges extends IncludableSpecialPage { if( $opts['namespace'] !== '' ) { $selectedNS = $dbr->addQuotes( $opts['namespace'] ); $operator = $opts['invert'] ? '!=' : '='; - $boolean = $opts['invert'] ? 'AND' : 'OR'; + $boolean = $opts['invert'] ? 'AND' : 'OR'; # namespace association (bug 2429) if( !$opts['associated'] ) { @@ -352,8 +350,8 @@ class SpecialRecentChanges extends IncludableSpecialPage { MWNamespace::getAssociated( $opts['namespace'] ) ); $condition = "(rc_namespace $operator $selectedNS " - . $boolean - . " rc_namespace $operator $associatedNS)"; + . $boolean + . " rc_namespace $operator $associatedNS)"; } $conds[] = $condition; @@ -382,19 +380,22 @@ class SpecialRecentChanges extends IncludableSpecialPage { $invert = $opts['invert']; $associated = $opts['associated']; - $fields = array( $dbr->tableName( 'recentchanges' ) . '.*' ); // all rc columns + $fields = RecentChange::selectFields(); // JOIN on watchlist for users if ( $uid ) { $tables[] = 'watchlist'; $fields[] = 'wl_user'; $fields[] = 'wl_notificationtimestamp'; - $join_conds['watchlist'] = array('LEFT JOIN', - "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace"); + $join_conds['watchlist'] = array( 'LEFT JOIN', array( + 'wl_user' => $uid, + 'wl_title=rc_title', + 'wl_namespace=rc_namespace' + )); } if ( $this->getUser()->isAllowed( 'rollback' ) ) { $tables[] = 'page'; $fields[] = 'page_latest'; - $join_conds['page'] = array('LEFT JOIN', 'rc_cur_id=page_id'); + $join_conds['page'] = array( 'LEFT JOIN', 'rc_cur_id=page_id' ); } // Tag stuff. ChangeTags::modifyDisplayQuery( @@ -467,7 +468,7 @@ class SpecialRecentChanges extends IncludableSpecialPage { /** * Send output to the OutputPage object, only called if not used feeds * - * @param $rows Array of database rows + * @param array $rows of database rows * @param $opts FormOptions */ public function webOutput( $rows, $opts ) { @@ -481,7 +482,12 @@ class SpecialRecentChanges extends IncludableSpecialPage { } // And now for the content - $this->getOutput()->setFeedAppendQuery( $this->getFeedQuery() ); + $feedQuery = $this->getFeedQuery(); + if ( $feedQuery !== '' ) { + $this->getOutput()->setFeedAppendQuery( $feedQuery ); + } else { + $this->getOutput()->setFeedAppendQuery( false ); + } if( $wgAllowCategorizedRecentChanges ) { $this->filterByCategories( $rows, $opts ); @@ -525,8 +531,12 @@ class SpecialRecentChanges extends IncludableSpecialPage { } $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title]; } - $s .= $list->recentChangesLine( $rc, !empty( $obj->wl_user ), $counter ); - --$limit; + + $changeLine = $list->recentChangesLine( $rc, !empty( $obj->wl_user ), $counter ); + if ( $changeLine !== false ) { + $s .= $changeLine; + --$limit; + } } $s .= $list->endRecentChangesList(); $this->getOutput()->addHTML( $s ); @@ -534,11 +544,24 @@ class SpecialRecentChanges extends IncludableSpecialPage { /** * Get the query string to append to feed link URLs. - * This is overridden by RCL to add the target parameter - * @return bool + * + * @return string */ public function getFeedQuery() { - return false; + global $wgFeedLimit; + + $this->getOptions()->validateIntBounds( 'limit', 0, $wgFeedLimit ); + $options = $this->getOptions()->getChangedValues(); + + // wfArrayToCgi() omits options set to null or false + foreach ( $options as &$value ) { + if ( $value === false ) { + $value = '0'; + } + } + unset( $value ); + + return wfArrayToCgi( $options ); } /** @@ -701,11 +724,11 @@ class SpecialRecentChanges extends IncludableSpecialPage { /** * Filter $rows by categories set in $opts * - * @param $rows Array of database rows + * @param array $rows of database rows * @param $opts FormOptions */ function filterByCategories( &$rows, FormOptions $opts ) { - $categories = array_map( 'trim', explode( '|' , $opts['categories'] ) ); + $categories = array_map( 'trim', explode( '|', $opts['categories'] ) ); if( !count( $categories ) ) { return; @@ -766,8 +789,8 @@ class SpecialRecentChanges extends IncludableSpecialPage { * Makes change an option link which carries all the other options * * @param $title Title - * @param $override Array: options to override - * @param $options Array: current options + * @param array $override options to override + * @param array $options current options * @param $active Boolean: whether to show the link in bold * @return string */ @@ -839,7 +862,6 @@ class SpecialRecentChanges extends IncludableSpecialPage { } $dl = $lang->pipeList( $dl ); - // show/hide links $showhide = array( $this->msg( 'show' )->text(), $this->msg( 'hide' )->text() ); $filters = array( @@ -885,4 +907,8 @@ class SpecialRecentChanges extends IncludableSpecialPage { 'mediawiki.special.recentchanges', ) ); } + + protected function getGroupName() { + return 'changes'; + } } diff --git a/includes/specials/SpecialRecentchangeslinked.php b/includes/specials/SpecialRecentchangeslinked.php index 862736d3..391c4a7f 100644 --- a/includes/specials/SpecialRecentchangeslinked.php +++ b/includes/specials/SpecialRecentchangeslinked.php @@ -29,7 +29,7 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { var $rclTargetTitle; - function __construct(){ + function __construct() { parent::__construct( 'Recentchangeslinked' ); } @@ -37,7 +37,6 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { $opts = parent::getDefaultOptions(); $opts->add( 'target', '' ); $opts->add( 'showlinkedto', false ); - $opts->add( 'tagfilter', '' ); return $opts; } @@ -51,7 +50,7 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { return $opts; } - public function getFeedObject( $feedFormat ){ + public function getFeedObject( $feedFormat ) { $feed = new ChangesFeed( $feedFormat, false ); $feedObj = $feed->getFeedObject( $this->msg( 'recentchangeslinked-title', $this->getTargetTitle()->getPrefixedText() ) @@ -72,7 +71,7 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { } $outputPage = $this->getOutput(); $title = Title::newFromURL( $target ); - if( !$title || $title->getInterwiki() != '' ){ + if( !$title || $title->getInterwiki() != '' ) { $outputPage->wrapWikiMsg( "<div class=\"errorbox\">\n$1\n</div><br style=\"clear: both\" />", 'allpagesbadtitle' ); return false; } @@ -94,7 +93,7 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { $dbkey = $title->getDBkey(); $tables = array( 'recentchanges' ); - $select = array( $dbr->tableName( 'recentchanges' ) . '.*' ); + $select = RecentChange::selectFields(); $join_conds = array(); $query_options = array(); @@ -103,11 +102,15 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { if( $uid ) { $tables[] = 'watchlist'; $select[] = 'wl_user'; - $join_conds['watchlist'] = array( 'LEFT JOIN', "wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace" ); + $join_conds['watchlist'] = array( 'LEFT JOIN', array( + 'wl_user' => $uid, + 'wl_title=rc_title', + 'wl_namespace=rc_namespace' + )); } if ( $this->getUser()->isAllowed( 'rollback' ) ) { $tables[] = 'page'; - $join_conds['page'] = array('LEFT JOIN', 'rc_cur_id=page_id'); + $join_conds['page'] = array( 'LEFT JOIN', 'rc_cur_id=page_id' ); $select[] = 'page_latest'; } ChangeTags::modifyDisplayQuery( @@ -125,7 +128,7 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { if( $ns == NS_CATEGORY && !$showlinkedto ) { // special handling for categories - // XXX: should try to make this less klugy + // XXX: should try to make this less kludgy $link_tables = array( 'categorylinks' ); $showlinkedto = true; } else { @@ -176,7 +179,7 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { $subconds["rc_namespace"] = $link_ns; $subjoin = "rc_title = {$pfx}_to"; } else { - $subjoin = "rc_namespace = {$pfx}_namespace AND rc_title = {$pfx}_title"; + $subjoin = array( "rc_namespace = {$pfx}_namespace", "rc_title = {$pfx}_title" ); } } @@ -201,15 +204,15 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { $subsql[] = $query; } - if( count($subsql) == 0 ) { + if( count( $subsql ) == 0 ) { return false; // should never happen } - if( count($subsql) == 1 && $dbr->unionSupportsOrderAndLimit() ) { + if( count( $subsql ) == 1 && $dbr->unionSupportsOrderAndLimit() ) { $sql = $subsql[0]; } else { // need to resort and relimit after union - $sql = $dbr->unionQueries($subsql, false).' ORDER BY rc_timestamp DESC'; - $sql = $dbr->limitResult($sql, $limit, false); + $sql = $dbr->unionQueries( $subsql, false ) . ' ORDER BY rc_timestamp DESC'; + $sql = $dbr->limitResult( $sql, $limit, false ); } $res = $dbr->query( $sql, __METHOD__ ); @@ -225,16 +228,16 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { * @param $opts FormOptions * @return array */ - function getExtraOptions( $opts ){ + function getExtraOptions( $opts ) { $opts->consumeValues( array( 'showlinkedto', 'target', 'tagfilter' ) ); $extraOpts = array(); $extraOpts['namespace'] = $this->namespaceFilterForm( $opts ); $extraOpts['target'] = array( $this->msg( 'recentchangeslinked-page' )->escaped(), - Xml::input( 'target', 40, str_replace('_',' ',$opts['target']) ) . - Xml::check( 'showlinkedto', $opts['showlinkedto'], array('id' => 'showlinkedto') ) . ' ' . + Xml::input( 'target', 40, str_replace( '_', ' ', $opts['target'] ) ) . + Xml::check( 'showlinkedto', $opts['showlinkedto'], array( 'id' => 'showlinkedto' ) ) . ' ' . Xml::label( $this->msg( 'recentchangeslinked-to' )->text(), 'showlinkedto' ) ); $tagFilter = ChangeTags::buildTagFilterSelector( $opts['tagfilter'] ); - if ($tagFilter) { + if ( $tagFilter ) { $extraOpts['tagfilter'] = $tagFilter; } return $extraOpts; @@ -262,15 +265,6 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges { } } - public function getFeedQuery() { - $target = $this->getTargetTitle(); - if( $target ) { - return "target=" . urlencode( $target->getPrefixedDBkey() ); - } else { - return false; - } - } - function setBottomText( FormOptions $opts ) { if( isset( $this->mResultEmpty ) && $this->mResultEmpty ) { $this->getOutput()->addWikiMsg( 'recentchangeslinked-noresult' ); diff --git a/includes/specials/SpecialRevisiondelete.php b/includes/specials/SpecialRevisiondelete.php index aba90cf8..5a5f8ffb 100644 --- a/includes/specials/SpecialRevisiondelete.php +++ b/includes/specials/SpecialRevisiondelete.php @@ -133,7 +133,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { $this->ids = explode( ',', $ids ); } else { # Array input - $this->ids = array_keys( $request->getArray('ids',array()) ); + $this->ids = array_keys( $request->getArray( 'ids', array() ) ); } // $this->ids = array_map( 'intval', $this->ids ); $this->ids = array_unique( array_filter( $this->ids ) ); @@ -147,7 +147,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { } else { $this->typeName = $request->getVal( 'type' ); $this->targetObj = Title::newFromText( $request->getText( 'target' ) ); - if ( $this->targetObj->isSpecial( 'Log' ) ) { + if ( $this->targetObj && $this->targetObj->isSpecial( 'Log' ) && count( $this->ids ) !== 0 ) { $result = wfGetDB( DB_SLAVE )->select( 'logging', 'log_type', array( 'log_id' => $this->ids ), @@ -155,14 +155,9 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { array( 'DISTINCT' ) ); - $logTypes = array(); - foreach ( $result as $row ) { - $logTypes[] = $row->log_type; - } - - if ( count( $logTypes ) == 1 ) { + if ( $result->numRows() == 1 ) { // If there's only one type, the target can be set to include it. - $this->targetObj = SpecialPage::getTitleFor( 'Log', $logTypes[0] ); + $this->targetObj = SpecialPage::getTitleFor( 'Log', $result->current()->log_type ); } } } @@ -196,7 +191,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { $this->otherReason = $request->getVal( 'wpReason' ); # We need a target page! - if( is_null($this->targetObj) ) { + if( is_null( $this->targetObj ) ) { $output->addWikiMsg( 'undelete-header' ); return; } @@ -209,7 +204,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { array( 'revdelete-hide-comment', 'wpHideComment', Revision::DELETED_COMMENT ), array( 'revdelete-hide-user', 'wpHideUser', Revision::DELETED_USER ) ); - if( $user->isAllowed('suppressrevision') ) { + if( $user->isAllowed( 'suppressrevision' ) ) { $this->checks[] = array( 'revdelete-hide-restricted', 'wpHideRestricted', Revision::DELETED_RESTRICTED ); } @@ -230,7 +225,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { # Show relevant lines from the suppression log if( $user->isAllowed( 'suppressionlog' ) ) { $suppressLogPage = new LogPage( 'suppress' ); - $output->addHTML( "<h2>" . $suppressLogPage->getName()->escaped() . "</h2>\n" ); + $output->addHTML( "<h2>" . $suppressLogPage->getName()->escaped() . "</h2>\n" ); LogEventsList::showLogExtract( $output, 'suppress', $this->targetObj, '', array( 'lim' => 25, 'conds' => $qc ) ); } @@ -258,7 +253,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { array( 'action' => 'history' ) ); # Link to deleted edits - if( $this->getUser()->isAllowed('undelete') ) { + if( $this->getUser()->isAllowed( 'undelete' ) ) { $undelete = SpecialPage::getTitleFor( 'Undelete' ); $links[] = Linker::linkKnown( $undelete, @@ -361,7 +356,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { $UserAllowed = true; if ( $this->typeName == 'logging' ) { - $this->getOutput()->addWikiMsg( 'logdelete-selected', $this->getLanguage()->formatNum( count($this->ids) ) ); + $this->getOutput()->addWikiMsg( 'logdelete-selected', $this->getLanguage()->formatNum( count( $this->ids ) ) ); } else { $this->getOutput()->addWikiMsg( 'revdelete-selected', $this->targetObj->getPrefixedText(), count( $this->ids ) ); @@ -469,8 +464,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { } /** - * @return String: HTML - */ + * @return String: HTML + */ protected function buildCheckBoxes() { $html = '<table>'; // If there is just one item, use checkboxes @@ -522,11 +517,12 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { /** * UI entry point for form submission. + * @throws PermissionsError * @return bool */ protected function submit() { # Check edit token on submission - $token = $this->getRequest()->getVal('wpEditToken'); + $token = $this->getRequest()->getVal( 'wpEditToken' ); if( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) { $this->getOutput()->addWikiMsg( 'sessionfailure' ); return false; @@ -541,7 +537,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { $comment = $this->otherReason; } # Can the user set this field? - if( $bitParams[Revision::DELETED_RESTRICTED]==1 && !$this->getUser()->isAllowed('suppressrevision') ) { + if( $bitParams[Revision::DELETED_RESTRICTED] == 1 && !$this->getUser()->isAllowed( 'suppressrevision' ) ) { throw new PermissionsError( 'suppressrevision' ); } # If the save went through, go to success message... @@ -583,14 +579,14 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { protected function extractBitParams() { $bitfield = array(); foreach( $this->checks as $item ) { - list( /* message */ , $name, $field ) = $item; + list( /* message */, $name, $field ) = $item; $val = $this->getRequest()->getInt( $name, 0 /* unchecked */ ); if( $val < -1 || $val > 1) { $val = -1; // -1 for existing value } $bitfield[$field] = $val; } - if( !isset($bitfield[Revision::DELETED_RESTRICTED]) ) { + if( !isset( $bitfield[Revision::DELETED_RESTRICTED] ) ) { $bitfield[Revision::DELETED_RESTRICTED] = 0; } return $bitfield; @@ -598,8 +594,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { /** * Put together a rev_deleted bitfield - * @param $bitPars array extractBitParams() params - * @param $oldfield int current bitfield + * @param array $bitPars extractBitParams() params + * @param int $oldfield current bitfield * @return array */ public static function extractBitfield( $bitPars, $oldfield ) { @@ -627,5 +623,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage { array( 'value' => $bitfield, 'comment' => $reason ) ); } -} + protected function getGroupName() { + return 'pagetools'; + } +} diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index 5f5b6b4d..6c401486 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -78,7 +78,7 @@ class SpecialSearch extends SpecialPage { /** * Entry point * - * @param $par String or null + * @param string $par or null */ public function execute( $par ) { $this->setHeaders(); @@ -138,7 +138,7 @@ class SpecialSearch extends SpecialPage { // BC with old request format $profile = 'advanced'; foreach( $profiles as $key => $data ) { - if ( $nslist === $data['namespaces'] && $key !== 'advanced') { + if ( $nslist === $data['namespaces'] && $key !== 'advanced' ) { $profile = $key; } } @@ -159,7 +159,7 @@ class SpecialSearch extends SpecialPage { $default = $request->getBool( 'profile' ) ? 0 : 1; $this->searchRedirects = $request->getBool( 'redirs', $default ) ? 1 : 0; $this->didYouMeanHtml = ''; # html of did you mean... link - $this->fulltext = $request->getVal('fulltext'); + $this->fulltext = $request->getVal( 'fulltext' ); $this->profile = $profile; } @@ -218,7 +218,7 @@ class SpecialSearch extends SpecialPage { $search->showRedirects = $this->searchRedirects; // BC $search->setFeatureData( 'list-redirects', $this->searchRedirects ); $search->prefix = $this->mPrefix; - $term = $search->transformSearchTerm($term); + $term = $search->transformSearchTerm( $term ); wfRunHooks( 'SpecialSearchSetupEngine', array( $this, $this->profile, $search ) ); @@ -250,7 +250,7 @@ class SpecialSearch extends SpecialPage { $t = Title::newFromText( $term ); // fetch search results - $rewritten = $search->replacePrefixes($term); + $rewritten = $search->replacePrefixes( $term ); $titleMatches = $search->searchTitle( $rewritten ); if( !( $titleMatches instanceof SearchResultTooMany ) ) { @@ -261,7 +261,7 @@ class SpecialSearch extends SpecialPage { if( $textMatches && $textMatches->hasSuggestion() ) { $st = SpecialPage::getTitleFor( 'Search' ); - # mirror Go/Search behaviour of original request .. + # mirror Go/Search behavior of original request .. $didYouMeanParams = array( 'search' => $textMatches->getSuggestionQuery() ); if( $this->fulltext != null ) { @@ -288,6 +288,13 @@ class SpecialSearch extends SpecialPage { $this->didYouMeanHtml = '<div class="searchdidyoumean">' . $this->msg( 'search-suggest' )->rawParams( $suggestLink )->text() . '</div>'; } + + if ( !wfRunHooks( 'SpecialSearchResultsPrepend', array( $this, $out, $term ) ) ) { + # Hook requested termination + wfProfileOut( __METHOD__ ); + return; + } + // start rendering the page $out->addHtml( Xml::openElement( @@ -304,9 +311,9 @@ class SpecialSearch extends SpecialPage { Xml::openElement( 'tr' ) . Xml::openElement( 'td' ) . "\n" . $this->shortDialog( $term ) . - Xml::closeElement('td') . - Xml::closeElement('tr') . - Xml::closeElement('table') + Xml::closeElement( 'td' ) . + Xml::closeElement( 'tr' ) . + Xml::closeElement( 'table' ) ); // Sometimes the search engine knows there are too many hits @@ -316,7 +323,7 @@ class SpecialSearch extends SpecialPage { return; } - $filePrefix = $wgContLang->getFormattedNsText(NS_FILE).':'; + $filePrefix = $wgContLang->getFormattedNsText( NS_FILE ) . ':'; if( trim( $term ) === '' || $filePrefix === trim( $term ) ) { $out->addHTML( $this->formHeader( $term, 0, 0 ) ); $out->addHtml( $this->getProfileForm( $this->profile, $term ) ); @@ -340,16 +347,15 @@ class SpecialSearch extends SpecialPage { // get total number of results if backend can calculate it $totalRes = 0; - if($titleMatches && !is_null( $titleMatches->getTotalHits() ) ) + if( $titleMatches && !is_null( $titleMatches->getTotalHits() ) ) $totalRes += $titleMatches->getTotalHits(); - if($textMatches && !is_null( $textMatches->getTotalHits() )) + if( $textMatches && !is_null( $textMatches->getTotalHits() ) ) $totalRes += $textMatches->getTotalHits(); // show number of results and current offset $out->addHTML( $this->formHeader( $term, $num, $totalRes ) ); $out->addHtml( $this->getProfileForm( $this->profile, $term ) ); - $out->addHtml( Xml::closeElement( 'form' ) ); $out->addHtml( "<div class='searchresults'>" ); @@ -404,6 +410,7 @@ class SpecialSearch extends SpecialPage { if( $num || $this->offset ) { $out->addHTML( "<p class='mw-search-pager-bottom'>{$prevnext}</p>\n" ); } + wfRunHooks( 'SpecialSearchResultsAppend', array( $this, $out, $term ) ); wfProfileOut( __METHOD__ ); } @@ -423,7 +430,7 @@ class SpecialSearch extends SpecialPage { if( $t->isKnown() ) { $messageName = 'searchmenu-exists'; - } elseif( $t->userCan( 'create' ) ) { + } elseif( $t->userCan( 'create', $this->getUser() ) ) { $messageName = 'searchmenu-new'; } else { $messageName = 'searchmenu-new-nocreate'; @@ -447,9 +454,11 @@ class SpecialSearch extends SpecialPage { # Should advanced UI be used? $this->searchAdvanced = ($this->profile === 'advanced'); $out = $this->getOutput(); - if( strval( $term ) !== '' ) { + if( strval( $term ) !== '' ) { $out->setPageTitle( $this->msg( 'searchresults' ) ); - $out->setHTMLTitle( $this->msg( 'pagetitle', $this->msg( 'searchresults-title', $term )->plain() ) ); + $out->setHTMLTitle( $this->msg( 'pagetitle' )->rawParams( + $this->msg( 'searchresults-title' )->rawParams( $term )->text() + ) ); } // add javascript specific to special:search $out->addModules( 'mediawiki.special.search' ); @@ -506,7 +515,7 @@ class SpecialSearch extends SpecialPage { $out = ""; $infoLine = $matches->getInfo(); - if( !is_null($infoLine) ) { + if( !is_null( $infoLine ) ) { $out .= "\n<!-- {$infoLine} -->\n"; } $out .= "<ul class='mw-search-results'>\n"; @@ -527,7 +536,7 @@ class SpecialSearch extends SpecialPage { * Format a single hit result * * @param $result SearchResult - * @param $terms Array: terms to highlight + * @param array $terms terms to highlight * * @return string */ @@ -541,7 +550,7 @@ class SpecialSearch extends SpecialPage { $t = $result->getTitle(); - $titleSnippet = $result->getTitleSnippet($terms); + $titleSnippet = $result->getTitleSnippet( $terms ); if( $titleSnippet == '' ) $titleSnippet = null; @@ -559,7 +568,7 @@ class SpecialSearch extends SpecialPage { //If page content is not readable, just return the title. //This is not quite safe, but better than showing excerpts from non-readable pages //Note that hiding the entry entirely would screw up paging. - if( !$t->userCan( 'read' ) ) { + if( !$t->userCan( 'read', $this->getUser() ) ) { wfProfileOut( __METHOD__ ); return "<li>{$link}</li>\n"; } @@ -574,12 +583,12 @@ class SpecialSearch extends SpecialPage { // format redirects / relevant sections $redirectTitle = $result->getRedirectTitle(); - $redirectText = $result->getRedirectSnippet($terms); + $redirectText = $result->getRedirectSnippet( $terms ); $sectionTitle = $result->getSectionTitle(); - $sectionText = $result->getSectionSnippet($terms); + $sectionText = $result->getSectionSnippet( $terms ); $redirect = ''; - if( !is_null($redirectTitle) ) { + if( !is_null( $redirectTitle ) ) { if( $redirectText == '' ) $redirectText = null; @@ -591,7 +600,7 @@ class SpecialSearch extends SpecialPage { $section = ''; - if( !is_null($sectionTitle) ) { + if( !is_null( $sectionTitle ) ) { if( $sectionText == '' ) $sectionText = null; @@ -602,7 +611,7 @@ class SpecialSearch extends SpecialPage { } // format text extract - $extract = "<div class='searchresult'>".$result->getTextSnippet($terms)."</div>"; + $extract = "<div class='searchresult'>" . $result->getTextSnippet( $terms ) . "</div>"; $lang = $this->getLanguage(); @@ -667,7 +676,7 @@ class SpecialSearch extends SpecialPage { return "<li>" . '<table class="searchResultImage">' . '<tr>' . - '<td width="120" style="text-align: center; vertical-align: top;">' . + '<td style="width: 120px; text-align: center; vertical-align: top;">' . $thumb->toHtml( array( 'desc-link' => true ) ) . '</td>' . '<td style="vertical-align: top;">' . @@ -682,11 +691,21 @@ class SpecialSearch extends SpecialPage { } } - wfProfileOut( __METHOD__ ); - return "<li><div class='mw-search-result-heading'>{$link} {$redirect} {$section}</div> {$extract}\n" . - "<div class='mw-search-result-data'>{$score}{$size} - {$date}{$related}</div>" . - "</li>\n"; + $html = null; + + if ( wfRunHooks( 'ShowSearchHit', array ( + $this, $result, $terms, + &$link, &$redirect, &$section, &$extract, + &$score, &$size, &$date, &$related, + &$html + ) ) ) { + $html = "<li><div class='mw-search-result-heading'>{$link} {$redirect} {$section}</div> {$extract}\n" . + "<div class='mw-search-result-data'>{$score}{$size} - {$date}{$related}</div>" . + "</li>\n"; + } + wfProfileOut( __METHOD__ ); + return $html; } /** @@ -703,16 +722,17 @@ class SpecialSearch extends SpecialPage { $terms = $wgContLang->convertForSearchResult( $matches->termMatches() ); $out = "<div id='mw-search-interwiki'><div id='mw-search-interwiki-caption'>". - $this->msg( 'search-interwiki-caption' )->text() . "</div>\n"; + $this->msg( 'search-interwiki-caption' )->text() . "</div>\n"; $out .= "<ul class='mw-search-iwresults'>\n"; // work out custom project captions $customCaptions = array(); $customLines = explode( "\n", $this->msg( 'search-interwiki-custom' )->text() ); // format per line <iwprefix>:<caption> - foreach($customLines as $line) { - $parts = explode(":",$line,2); - if(count($parts) == 2) // validate line + foreach( $customLines as $line ) { + $parts = explode( ":", $line, 2 ); + if( count( $parts ) == 2 ) { // validate line $customCaptions[$parts[0]] = $parts[1]; + } } $prev = null; @@ -738,7 +758,7 @@ class SpecialSearch extends SpecialPage { * @param $lastInterwiki String * @param $terms Array * @param $query String - * @param $customCaptions Array: iw prefix -> caption + * @param array $customCaptions iw prefix -> caption * * @return string */ @@ -752,7 +772,7 @@ class SpecialSearch extends SpecialPage { $t = $result->getTitle(); - $titleSnippet = $result->getTitleSnippet($terms); + $titleSnippet = $result->getTitleSnippet( $terms ); if( $titleSnippet == '' ) $titleSnippet = null; @@ -764,9 +784,9 @@ class SpecialSearch extends SpecialPage { // format redirect if any $redirectTitle = $result->getRedirectTitle(); - $redirectText = $result->getRedirectSnippet($terms); + $redirectText = $result->getRedirectSnippet( $terms ); $redirect = ''; - if( !is_null($redirectTitle) ) { + if( !is_null( $redirectTitle ) ) { if( $redirectText == '' ) $redirectText = null; @@ -778,8 +798,8 @@ class SpecialSearch extends SpecialPage { $out = ""; // display project name - if(is_null($lastInterwiki) || $lastInterwiki != $t->getInterwiki()) { - if( array_key_exists($t->getInterwiki(),$customCaptions) ) { + if( is_null( $lastInterwiki ) || $lastInterwiki != $t->getInterwiki() ) { + if( array_key_exists( $t->getInterwiki(), $customCaptions ) ) { // captions from 'search-interwiki-custom' $caption = $customCaptions[$t->getInterwiki()]; } else { @@ -789,7 +809,7 @@ class SpecialSearch extends SpecialPage { $caption = $this->msg( 'search-interwiki-default', $parsed['host'] )->text(); } // "more results" link (special page stuff could be localized, but we might not know target lang) - $searchTitle = Title::newFromText($t->getInterwiki().":Special:Search"); + $searchTitle = Title::newFromText( $t->getInterwiki() . ":Special:Search" ); $searchLink = Linker::linkKnown( $searchTitle, $this->msg( 'search-interwiki-more' )->text(), @@ -831,7 +851,7 @@ class SpecialSearch extends SpecialPage { /** * Generates the power search box at [[Special:Search]] * - * @param $term String: search term + * @param string $term search term * @param $opts array * @return String: HTML form */ @@ -897,7 +917,7 @@ class SpecialSearch extends SpecialPage { 'fieldset', array( 'id' => 'mw-searchoptions', 'style' => 'margin:0em;' ) ) . - Xml::element( 'legend', null, $this->msg('powersearch-legend' )->text() ) . + Xml::element( 'legend', null, $this->msg( 'powersearch-legend' )->text() ) . Xml::tags( 'h4', null, $this->msg( 'powersearch-ns' )->parse() ) . Html::element( 'div', array( 'id' => 'mw-search-togglebox' ) ) . Xml::element( 'div', array( 'class' => 'divider' ), '', false ) . @@ -964,7 +984,7 @@ class SpecialSearch extends SpecialPage { * @return string */ protected function formHeader( $term, $resultsShown, $totalNum ) { - $out = Xml::openElement('div', array( 'class' => 'mw-search-formheader' ) ); + $out = Xml::openElement( 'div', array( 'class' => 'mw-search-formheader' ) ); $bareterm = $term; if( $this->startsWithImage( $term ) ) { @@ -1001,11 +1021,11 @@ class SpecialSearch extends SpecialPage { ); } $out .= Xml::closeElement( 'ul' ); - $out .= Xml::closeElement('div') ; + $out .= Xml::closeElement( 'div' ); // Results-info if ( $resultsShown > 0 ) { - if ( $totalNum > 0 ){ + if ( $totalNum > 0 ) { $top = $this->msg( 'showingresultsheader' ) ->numParams( $this->offset + 1, $this->offset + $resultsShown, $totalNum ) ->params( wfEscapeWikiText( $term ) ) @@ -1026,7 +1046,7 @@ class SpecialSearch extends SpecialPage { } $out .= Xml::element( 'div', array( 'style' => 'clear:both' ), '', false ); - $out .= Xml::closeElement('div'); + $out .= Xml::closeElement( 'div' ); return $out; } @@ -1053,10 +1073,10 @@ class SpecialSearch extends SpecialPage { * Make a search link with some target namespaces * * @param $term String - * @param $namespaces Array ignored - * @param $label String: link's text - * @param $tooltip String: link's tooltip - * @param $params Array: query string parameters + * @param array $namespaces ignored + * @param string $label link's text + * @param string $tooltip link's tooltip + * @param array $params query string parameters * @return String: HTML fragment */ protected function makeSearchLink( $term, $namespaces, $label, $tooltip, $params = array() ) { @@ -1086,7 +1106,7 @@ class SpecialSearch extends SpecialPage { /** * Check if query starts with image: prefix * - * @param $term String: the string to check + * @param string $term the string to check * @return Boolean */ protected function startsWithImage( $term ) { @@ -1102,7 +1122,7 @@ class SpecialSearch extends SpecialPage { /** * Check if query starts with all: prefix * - * @param $term String: the string to check + * @param string $term the string to check * @return Boolean */ protected function startsWithAll( $term ) { @@ -1111,7 +1131,7 @@ class SpecialSearch extends SpecialPage { $p = explode( ':', $term ); if( count( $p ) > 1 ) { - return $p[0] == $allkeyword; + return $p[0] == $allkeyword; } return false; } @@ -1141,4 +1161,7 @@ class SpecialSearch extends SpecialPage { $this->extraParams[$key] = $value; } + protected function getGroupName() { + return 'redirects'; + } } diff --git a/includes/specials/SpecialShortpages.php b/includes/specials/SpecialShortpages.php index 5a4e8f03..1be7fbed 100644 --- a/includes/specials/SpecialShortpages.php +++ b/includes/specials/SpecialShortpages.php @@ -110,4 +110,8 @@ class ShortPagesPage extends QueryPage { ? "${hlinkInParentheses} {$dm}{$plink} {$dm}[{$size}]" : "<del>${hlinkInParentheses} {$dm}{$plink} {$dm}[{$size}]</del>"; } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialSpecialpages.php b/includes/specials/SpecialSpecialpages.php index e973ddc8..57fffb84 100644 --- a/includes/specials/SpecialSpecialpages.php +++ b/includes/specials/SpecialSpecialpages.php @@ -62,11 +62,15 @@ class SpecialSpecialpages extends UnlistedSpecialPage { $groups = array(); foreach ( $pages as $page ) { if ( $page->isListed() ) { - $group = SpecialPageFactory::getGroup( $page ); + $group = $page->getFinalGroupName(); if( !isset( $groups[$group] ) ) { $groups[$group] = array(); } - $groups[$group][$page->getDescription()] = array( $page->getTitle(), $page->isRestricted(), $page->isExpensive() ); + $groups[$group][$page->getDescription()] = array( + $page->getTitle(), + $page->isRestricted(), + $page->isCached() + ); } } @@ -88,15 +92,14 @@ class SpecialSpecialpages extends UnlistedSpecialPage { } private function outputPageList( $groups ) { - global $wgMiserMode; $out = $this->getOutput(); $includesRestrictedPages = false; $includesCachedPages = false; foreach ( $groups as $group => $sortedPages ) { - $middle = ceil( count( $sortedPages )/2 ); $total = count( $sortedPages ); + $middle = ceil( $total / 2 ); $count = 0; $out->wrapWikiMsg( "<h2 class=\"mw-specialpagesgroup\" id=\"mw-specialpagesgroup-$group\">$1</h2>\n", "specialpages-group-$group" ); @@ -107,10 +110,10 @@ class SpecialSpecialpages extends UnlistedSpecialPage { Html::openElement( 'ul' ) . "\n" ); foreach( $sortedPages as $desc => $specialpage ) { - list( $title, $restricted, $expensive) = $specialpage; + list( $title, $restricted, $cached ) = $specialpage; $pageClasses = array(); - if ( $expensive && $wgMiserMode ){ + if ( $cached ) { $includesCachedPages = true; $pageClasses[] = 'mw-specialpagecached'; } @@ -119,7 +122,7 @@ class SpecialSpecialpages extends UnlistedSpecialPage { $pageClasses[] = 'mw-specialpagerestricted'; } - $link = Linker::linkKnown( $title , htmlspecialchars( $desc ) ); + $link = Linker::linkKnown( $title, htmlspecialchars( $desc ) ); $out->addHTML( Html::rawElement( 'li', array( 'class' => implode( ' ', $pageClasses ) ), $link ) . "\n" ); # Split up the larger groups diff --git a/includes/specials/SpecialStatistics.php b/includes/specials/SpecialStatistics.php index 46881ec4..ee768263 100644 --- a/includes/specials/SpecialStatistics.php +++ b/includes/specials/SpecialStatistics.php @@ -61,7 +61,7 @@ class SpecialStatistics extends SpecialPage { if( !$wgMiserMode ) { $key = wfMemcKey( 'sitestats', 'activeusers-updated' ); // Re-calculate the count if the last tally is old... - if( !$wgMemc->get($key) ) { + if( !$wgMemc->get( $key ) ) { $dbw = wfGetDB( DB_MASTER ); SiteStatsUpdate::cacheUpdate( $dbw ); $wgMemc->set( $key, '1', 24*3600 ); // don't update for 1 day @@ -222,7 +222,7 @@ class SpecialStatistics extends SpecialPage { } $text .= $this->formatRow( $grouppage . ' ' . $grouplink, $this->getLanguage()->formatNum( $countUsers ), - array( 'class' => 'statistics-group-' . Sanitizer::escapeClass( $group ) . $classZero ) ); + array( 'class' => 'statistics-group-' . Sanitizer::escapeClass( $group ) . $classZero ) ); } return $text; } @@ -277,21 +277,60 @@ class SpecialStatistics extends SpecialPage { return $text; } - private function getOtherStats( $stats ) { - if ( !count( $stats ) ) - return ''; + /** + * Conversion of external statistics into an internal representation + * Following a ([<header-message>][<item-message>] = number) pattern + * + * @param array $stats + * @return string + */ + private function getOtherStats( array $stats ) { + $return = ''; - $return = Xml::openElement( 'tr' ) . - Xml::tags( 'th', array( 'colspan' => '2' ), $this->msg( 'statistics-header-hooks' )->parse() ) . - Xml::closeElement( 'tr' ); + foreach( $stats as $header => $items ) { + + // Identify the structure used + if ( is_array( $items ) ) { - foreach( $stats as $name => $number ) { - $name = htmlspecialchars( $name ); - $number = htmlspecialchars( $number ); + // Ignore headers that are recursively set as legacy header + if ( $header !== 'statistics-header-hooks' ) { + $return .= $this->formatRowHeader( $header ); + } + + // Collect all items that belong to the same header + foreach( $items as $key => $value ) { + $name = $this->msg( $key )->parse(); + $number = htmlspecialchars( $value ); + + $return .= $this->formatRow( $name, $this->getLanguage()->formatNum( $number ), array( 'class' => 'mw-statistics-hook' ) ); + } + } else { + // Create the legacy header only once + if ( $return === '' ) { + $return .= $this->formatRowHeader( 'statistics-header-hooks' ); + } - $return .= $this->formatRow( $name, $this->getLanguage()->formatNum( $number ), array( 'class' => 'mw-statistics-hook' ) ); + // Recursively remap the legacy structure + $return .= $this->getOtherStats( array( 'statistics-header-hooks' => array( $header => $items ) ) ); + } } return $return; } + + /** + * Format row header + * + * @param string $header + * @return string + */ + private function formatRowHeader( $header ) { + return Xml::openElement( 'tr' ) . + Xml::tags( 'th', array( 'colspan' => '2' ), $this->msg( $header )->parse() ) . + Xml::closeElement( 'tr' ); + } + + protected function getGroupName() { + return 'wiki'; + } } diff --git a/includes/specials/SpecialTags.php b/includes/specials/SpecialTags.php index 4036ebb2..6d161031 100644 --- a/includes/specials/SpecialTags.php +++ b/includes/specials/SpecialTags.php @@ -92,4 +92,8 @@ class SpecialTags extends SpecialPage { return Xml::tags( 'tr', null, $newRow ) . "\n"; } + + protected function getGroupName() { + return 'changes'; + } } diff --git a/includes/specials/SpecialUnblock.php b/includes/specials/SpecialUnblock.php index fb2005b5..c4a53cf0 100644 --- a/includes/specials/SpecialUnblock.php +++ b/includes/specials/SpecialUnblock.php @@ -32,11 +32,11 @@ class SpecialUnblock extends SpecialPage { protected $type; protected $block; - public function __construct(){ + public function __construct() { parent::__construct( 'Unblock', 'block' ); } - public function execute( $par ){ + public function execute( $par ) { $this->checkPermissions(); $this->checkReadOnly(); @@ -56,8 +56,8 @@ class SpecialUnblock extends SpecialPage { $form->setSubmitTextMsg( 'ipusubmit' ); $form->addPreText( $this->msg( 'unblockiptext' )->parseAsBlock() ); - if( $form->show() ){ - switch( $this->type ){ + if( $form->show() ) { + switch( $this->type ) { case Block::TYPE_USER: case Block::TYPE_IP: $out->addWikiMsg( 'unblocked', wfEscapeWikiText( $this->target ) ); @@ -73,7 +73,7 @@ class SpecialUnblock extends SpecialPage { } } - protected function getFields(){ + protected function getFields() { $fields = array( 'Target' => array( 'type' => 'text', @@ -92,21 +92,21 @@ class SpecialUnblock extends SpecialPage { ) ); - if( $this->block instanceof Block ){ + if( $this->block instanceof Block ) { list( $target, $type ) = $this->block->getTargetAndType(); # Autoblocks are logged as "autoblock #123 because the IP was recently used by # User:Foo, and we've just got any block, auto or not, that applies to a target # the user has specified. Someone could be fishing to connect IPs to autoblocks, # so don't show any distinction between unblocked IPs and autoblocked IPs - if( $type == Block::TYPE_AUTO && $this->type == Block::TYPE_IP ){ + if( $type == Block::TYPE_AUTO && $this->type == Block::TYPE_IP ) { $fields['Target']['default'] = $this->target; unset( $fields['Name'] ); } else { $fields['Target']['default'] = $target; $fields['Target']['type'] = 'hidden'; - switch( $type ){ + switch( $type ) { case Block::TYPE_USER: case Block::TYPE_IP: $fields['Name']['default'] = Linker::link( @@ -149,14 +149,15 @@ class SpecialUnblock extends SpecialPage { * * @param $data Array * @param $context IContextSource + * @throws ErrorPageError * @return Array( Array(message key, parameters) ) on failure, True on success */ - public static function processUnblock( array $data, IContextSource $context ){ + public static function processUnblock( array $data, IContextSource $context ) { $performer = $context->getUser(); $target = $data['Target']; $block = Block::newFromTarget( $data['Target'] ); - if( !$block instanceof Block ){ + if( !$block instanceof Block ) { return array( array( 'ipb_cant_unblock', $target ) ); } @@ -172,8 +173,8 @@ class SpecialUnblock extends SpecialPage { # unblock the whole range. list( $target, $type ) = SpecialBlock::getTargetAndType( $target ); if( $block->getType() == Block::TYPE_RANGE && $type == Block::TYPE_IP ) { - $range = $block->getTarget(); - return array( array( 'ipb_blocked_as_range', $target, $range ) ); + $range = $block->getTarget(); + return array( array( 'ipb_blocked_as_range', $target, $range ) ); } # If the name was hidden and the blocking user cannot hide @@ -212,4 +213,8 @@ class SpecialUnblock extends SpecialPage { return true; } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialUncategorizedcategories.php b/includes/specials/SpecialUncategorizedcategories.php index 70d98df9..54b20dde 100644 --- a/includes/specials/SpecialUncategorizedcategories.php +++ b/includes/specials/SpecialUncategorizedcategories.php @@ -31,4 +31,17 @@ class UncategorizedCategoriesPage extends UncategorizedPagesPage { parent::__construct( $name ); $this->requestedNamespace = NS_CATEGORY; } + + /** + * Formats the result + * @param $skin The current skin + * @param $result The query result + * @return string The category link + */ + function formatResult ( $skin, $result ) { + $title = Title::makeTitle( NS_CATEGORY, $result->title ); + $text = $title->getText(); + + return Linker::linkKnown( $title, htmlspecialchars( $text ) ); + } } diff --git a/includes/specials/SpecialUncategorizedimages.php b/includes/specials/SpecialUncategorizedimages.php index 5865bf62..53aa3f34 100644 --- a/includes/specials/SpecialUncategorizedimages.php +++ b/includes/specials/SpecialUncategorizedimages.php @@ -60,4 +60,7 @@ class UncategorizedImagesPage extends ImageQueryPage { ); } + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialUncategorizedpages.php b/includes/specials/SpecialUncategorizedpages.php index 1226a6ca..b518e6fb 100644 --- a/includes/specials/SpecialUncategorizedpages.php +++ b/includes/specials/SpecialUncategorizedpages.php @@ -41,7 +41,10 @@ class UncategorizedPagesPage extends PageQueryPage { function isExpensive() { return true; } - function isSyndicated() { return false; } + + function isSyndicated() { + return false; + } function getQueryInfo() { return array ( @@ -52,7 +55,7 @@ class UncategorizedPagesPage extends PageQueryPage { // 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_namespace' => ( $this->requestedNamespace !== false ? $this->requestedNamespace : MWNamespace::getContentNamespaces() ), 'page_is_redirect' => 0 ), 'join_conds' => array ( 'categorylinks' => array ( 'LEFT JOIN', 'cl_from = page_id' ) ) @@ -66,4 +69,8 @@ class UncategorizedPagesPage extends PageQueryPage { return array( 'page_namespace', 'page_title' ); return array( 'page_title' ); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php index d8e0b97c..e0363481 100644 --- a/includes/specials/SpecialUndelete.php +++ b/includes/specials/SpecialUndelete.php @@ -32,7 +32,16 @@ class PageArchive { * @var Title */ protected $title; - var $fileStatus; + + /** + * @var Status + */ + protected $fileStatus; + + /** + * @var Status + */ + protected $revisionStatus; function __construct( $title ) { if( is_null( $title ) ) { @@ -58,7 +67,7 @@ class PageArchive { * given title prefix. * Returns result wrapper with (ar_namespace, ar_title, count) fields. * - * @param $prefix String: title prefix + * @param string $prefix title prefix * @return ResultWrapper */ public static function listPagesByPrefix( $prefix ) { @@ -112,14 +121,24 @@ class PageArchive { * @return ResultWrapper */ function listRevisions() { + global $wgContentHandlerUseDB; + $dbr = wfGetDB( DB_SLAVE ); + + $fields = array( + 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', + 'ar_comment', 'ar_len', 'ar_deleted', 'ar_rev_id', 'ar_sha1', + ); + + if ( $wgContentHandlerUseDB ) { + $fields[] = 'ar_content_format'; + $fields[] = 'ar_content_model'; + } + $res = $dbr->select( 'archive', - array( - 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', - 'ar_comment', 'ar_len', 'ar_deleted', 'ar_rev_id', 'ar_sha1' - ), + $fields, array( 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey() ), + 'ar_title' => $this->title->getDBkey() ), __METHOD__, array( 'ORDER BY' => 'ar_timestamp DESC' ) ); $ret = $dbr->resultObject( $res ); @@ -137,26 +156,9 @@ class PageArchive { function listFiles() { if( $this->title->getNamespace() == NS_FILE ) { $dbr = wfGetDB( DB_SLAVE ); - $res = $dbr->select( 'filearchive', - array( - 'fa_id', - 'fa_name', - 'fa_archive_name', - 'fa_storage_key', - 'fa_storage_group', - 'fa_size', - 'fa_width', - 'fa_height', - 'fa_bits', - 'fa_metadata', - 'fa_media_type', - 'fa_major_mime', - 'fa_minor_mime', - 'fa_description', - 'fa_user', - 'fa_user_text', - 'fa_timestamp', - 'fa_deleted' ), + $res = $dbr->select( + 'filearchive', + ArchivedFile::selectFields(), array( 'fa_name' => $this->title->getDBkey() ), __METHOD__, array( 'ORDER BY' => 'fa_timestamp DESC' ) ); @@ -174,28 +176,38 @@ class PageArchive { * @return Revision */ function getRevision( $timestamp ) { + global $wgContentHandlerUseDB; + $dbr = wfGetDB( DB_SLAVE ); + + $fields = array( + 'ar_rev_id', + 'ar_text', + 'ar_comment', + 'ar_user', + 'ar_user_text', + 'ar_timestamp', + 'ar_minor_edit', + 'ar_flags', + 'ar_text_id', + 'ar_deleted', + 'ar_len', + 'ar_sha1', + ); + + if ( $wgContentHandlerUseDB ) { + $fields[] = 'ar_content_format'; + $fields[] = 'ar_content_model'; + } + $row = $dbr->selectRow( 'archive', - array( - 'ar_rev_id', - 'ar_text', - 'ar_comment', - 'ar_user', - 'ar_user_text', - 'ar_timestamp', - 'ar_minor_edit', - 'ar_flags', - 'ar_text_id', - 'ar_deleted', - 'ar_len', - 'ar_sha1', - ), + $fields, array( 'ar_namespace' => $this->title->getNamespace(), 'ar_title' => $this->title->getDBkey(), 'ar_timestamp' => $dbr->timestamp( $timestamp ) ), __METHOD__ ); if( $row ) { - return Revision::newFromArchiveRow( $row, array( 'page' => $this->title->getArticleID() ) ); + return Revision::newFromArchiveRow( $row, array( 'title' => $this->title ) ); } else { return null; } @@ -218,9 +230,9 @@ class PageArchive { $row = $dbr->selectRow( 'archive', 'ar_timestamp', array( 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey(), - 'ar_timestamp < ' . - $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ), + 'ar_title' => $this->title->getDBkey(), + 'ar_timestamp < ' . + $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ), __METHOD__, array( 'ORDER BY' => 'ar_timestamp DESC', @@ -289,7 +301,7 @@ class PageArchive { $row = $dbr->selectRow( 'archive', array( 'ar_text', 'ar_flags', 'ar_text_id' ), array( 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey() ), + 'ar_title' => $this->title->getDBkey() ), __METHOD__, array( 'ORDER BY' => 'ar_timestamp DESC' ) ); if( $row ) { @@ -308,7 +320,7 @@ class PageArchive { $dbr = wfGetDB( DB_SLAVE ); $n = $dbr->selectField( 'archive', 'COUNT(ar_title)', array( 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey() ), + 'ar_title' => $this->title->getDBkey() ), __METHOD__ ); return ( $n > 0 ); @@ -319,7 +331,7 @@ class PageArchive { * Once restored, the items will be removed from the archive tables. * The deletion log will be updated with an undeletion notice. * - * @param $timestamps Array: pass an empty array to restore all revisions, otherwise list the ones to undelete. + * @param array $timestamps pass an empty array to restore all revisions, otherwise list the ones to undelete. * @param $comment String * @param $fileVersions Array * @param $unsuppress Boolean @@ -329,8 +341,6 @@ class PageArchive { * on success, false on failure */ function undelete( $timestamps, $comment = '', $fileVersions = array(), $unsuppress = false, User $user = null ) { - global $wgUser; - // If both the set of text revisions and file revisions are empty, // restore everything. Otherwise, just restore the requested items. $restoreAll = empty( $timestamps ) && empty( $fileVersions ); @@ -341,7 +351,7 @@ class PageArchive { if( $restoreFiles && $this->title->getNamespace() == NS_FILE ) { $img = wfLocalFile( $this->title ); $this->fileStatus = $img->restore( $fileVersions, $unsuppress ); - if ( !$this->fileStatus->isOk() ) { + if ( !$this->fileStatus->isOK() ) { return false; } $filesRestored = $this->fileStatus->successCount; @@ -350,10 +360,12 @@ class PageArchive { } if( $restoreText ) { - $textRestored = $this->undeleteRevisions( $timestamps, $unsuppress, $comment ); - if( $textRestored === false ) { // It must be one of UNDELETE_* + $this->revisionStatus = $this->undeleteRevisions( $timestamps, $unsuppress, $comment ); + if( !$this->revisionStatus->isOK() ) { return false; } + + $textRestored = $this->revisionStatus->getValue(); } else { $textRestored = 0; } @@ -379,6 +391,7 @@ class PageArchive { } if ( $user === null ) { + global $wgUser; $user = $wgUser; } @@ -386,6 +399,9 @@ class PageArchive { $logEntry->setPerformer( $user ); $logEntry->setTarget( $this->title ); $logEntry->setComment( $reason ); + + wfRunHooks( 'ArticleUndeleteLogEntry', array( $this, &$logEntry, $user ) ); + $logid = $logEntry->insert(); $logEntry->publish( $logid ); @@ -397,15 +413,18 @@ class PageArchive { * to the cur/old tables. If the page currently exists, all revisions will * be stuffed into old, otherwise the most recent will go into cur. * - * @param $timestamps Array: pass an empty array to restore all revisions, otherwise list the ones to undelete. - * @param $comment String + * @param array $timestamps pass an empty array to restore all revisions, otherwise list the ones to undelete. * @param $unsuppress Boolean: remove all ar_deleted/fa_deleted restrictions of seletected revs * - * @return Mixed: number of revisions restored or false on failure + * @param $comment String + * @throws ReadOnlyError + * @return Status, containing the number of revisions restored on success */ private function undeleteRevisions( $timestamps, $unsuppress = false, $comment = '' ) { + global $wgContentHandlerUseDB; + if ( wfReadOnly() ) { - return false; + throw new ReadOnlyError(); } $restoreAll = empty( $timestamps ); @@ -420,7 +439,7 @@ class PageArchive { $page = $dbw->selectRow( 'page', array( 'page_id', 'page_latest' ), array( 'page_namespace' => $this->title->getNamespace(), - 'page_title' => $this->title->getDBkey() ), + 'page_title' => $this->title->getDBkey() ), __METHOD__, array( 'FOR UPDATE' ) // lock page ); @@ -428,16 +447,21 @@ class PageArchive { $makepage = false; # Page already exists. Import the history, and if necessary # we'll update the latest revision field in the record. - $newid = 0; - $pageId = $page->page_id; - $previousRevId = $page->page_latest; + + $previousRevId = $page->page_latest; + # Get the time span of this page $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp', array( 'rev_id' => $previousRevId ), __METHOD__ ); + if( $previousTimestamp === false ) { - wfDebug( __METHOD__.": existing page refers to a page_latest that does not exist\n" ); - return 0; + wfDebug( __METHOD__ . ": existing page refers to a page_latest that does not exist\n" ); + + $status = Status::newGood( 0 ); + $status->warning( 'undeleterevision-missing' ); + + return $status; } } else { # Have to create a new article... @@ -457,24 +481,32 @@ class PageArchive { $oldones = "ar_timestamp IN ( {$oldts} )"; } + $fields = array( + 'ar_rev_id', + 'ar_text', + 'ar_comment', + 'ar_user', + 'ar_user_text', + 'ar_timestamp', + 'ar_minor_edit', + 'ar_flags', + 'ar_text_id', + 'ar_deleted', + 'ar_page_id', + 'ar_len', + 'ar_sha1' + ); + + if ( $wgContentHandlerUseDB ) { + $fields[] = 'ar_content_format'; + $fields[] = 'ar_content_model'; + } + /** * Select each archived revision... */ $result = $dbw->select( 'archive', - /* fields */ array( - 'ar_rev_id', - 'ar_text', - 'ar_comment', - 'ar_user', - 'ar_user_text', - 'ar_timestamp', - 'ar_minor_edit', - 'ar_flags', - 'ar_text_id', - 'ar_deleted', - 'ar_page_id', - 'ar_len', - 'ar_sha1' ), + $fields, /* WHERE */ array( 'ar_namespace' => $this->title->getNamespace(), 'ar_title' => $this->title->getDBkey(), @@ -486,29 +518,51 @@ class PageArchive { $rev_count = $dbw->numRows( $result ); if( !$rev_count ) { wfDebug( __METHOD__ . ": no revisions to restore\n" ); - return false; // ??? + + $status = Status::newGood( 0 ); + $status->warning( "undelete-no-results" ); + return $status; } $ret->seek( $rev_count - 1 ); // move to last $row = $ret->fetchObject(); // get newest archived rev $ret->seek( 0 ); // move back + // grab the content to check consistency with global state before restoring the page. + $revision = Revision::newFromArchiveRow( $row, + array( + 'title' => $article->getTitle(), // used to derive default content model + ) + ); + $user = User::newFromName( $revision->getRawUserText(), false ); + $content = $revision->getContent( Revision::RAW ); + + //NOTE: article ID may not be known yet. prepareSave() should not modify the database. + $status = $content->prepareSave( $article, 0, -1, $user ); + + if ( !$status->isOK() ) { + return $status; + } + if( $makepage ) { // Check the state of the newest to-be version... if( !$unsuppress && ( $row->ar_deleted & Revision::DELETED_TEXT ) ) { - return false; // we can't leave the current revision like this! + return Status::newFatal( "undeleterevdel" ); } // Safe to insert now... - $newid = $article->insertOn( $dbw ); + $newid = $article->insertOn( $dbw ); $pageId = $newid; } else { // Check if a deleted revision will become the current revision... if( $row->ar_timestamp > $previousTimestamp ) { // Check the state of the newest to-be version... if( !$unsuppress && ( $row->ar_deleted & Revision::DELETED_TEXT ) ) { - return false; // we can't leave the current revision like this! + return Status::newFatal( "undeleterevdel" ); } } + + $newid = false; + $pageId = $article->getId(); } $revision = null; @@ -528,6 +582,7 @@ class PageArchive { $revision = Revision::newFromArchiveRow( $row, array( 'page' => $pageId, + 'title' => $this->title, 'deleted' => $unsuppress ? 0 : $row->ar_deleted ) ); @@ -546,7 +601,7 @@ class PageArchive { // Was anything restored at all? if ( $restored == 0 ) { - return 0; + return Status::newGood( 0 ); } $created = (bool)$newid; @@ -566,13 +621,18 @@ class PageArchive { $update->doUpdate(); } - return $restored; + return Status::newGood( $restored ); } /** * @return Status */ function getFileStatus() { return $this->fileStatus; } + + /** + * @return Status + */ + function getRevisionStatus() { return $this->revisionStatus; } } /** @@ -721,7 +781,7 @@ class SpecialUndelete extends SpecialPage { 'action' => $wgScript ) ) . Xml::fieldset( $this->msg( 'undelete-search-box' )->text() ) . Html::hidden( 'title', - $this->getTitle()->getPrefixedDbKey() ) . + $this->getTitle()->getPrefixedDBkey() ) . Xml::inputLabel( $this->msg( 'undelete-search-prefix' )->text(), 'prefix', 'prefix', 20, $this->mSearchPrefix ) . ' ' . @@ -748,7 +808,7 @@ class SpecialUndelete extends SpecialPage { if( $result->numRows() == 0 ) { $out->addWikiMsg( 'undelete-no-results' ); - return; + return false; } $out->addWikiMsg( 'undeletepagetext', $this->getLanguage()->formatNum( $result->numRows() ) ); @@ -780,11 +840,13 @@ class SpecialUndelete extends SpecialPage { private function showRevision( $timestamp ) { if( !preg_match( '/[0-9]{14}/', $timestamp ) ) { - return 0; + return; } $archive = new PageArchive( $this->mTargetObj ); - wfRunHooks( 'UndeleteForm::showRevision', array( &$archive, $this->mTargetObj ) ); + if ( !wfRunHooks( 'UndeleteForm::showRevision', array( &$archive, $this->mTargetObj ) ) ) { + return; + } $rev = $archive->getRevision( $timestamp ); $out = $this->getOutput(); @@ -834,7 +896,11 @@ class SpecialUndelete extends SpecialPage { $t = $lang->userTime( $timestamp, $user ); $userLink = Linker::revUserTools( $rev ); - if( $this->mPreview ) { + $content = $rev->getContent( Revision::FOR_THIS_USER, $user ); + + $isText = ( $content instanceof TextContent ); + + if( $this->mPreview || $isText ) { $openDiv = '<div id="mw-undelete-revision" class="mw-warning">'; } else { $openDiv = '<div id="mw-undelete-revision">'; @@ -851,30 +917,55 @@ class SpecialUndelete extends SpecialPage { $out->addHTML( $this->msg( 'undelete-revision' )->rawParams( $link )->params( $time )->rawParams( $userLink )->params( $d, $t )->parse() . '</div>' ); - wfRunHooks( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) ); - if( $this->mPreview ) { + if ( !wfRunHooks( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) ) ) { + return; + } + + if( $this->mPreview || !$isText ) { + // NOTE: non-text content has no source view, so always use rendered preview + // Hide [edit]s $popts = $out->parserOptions(); $popts->setEditSection( false ); - $out->parserOptions( $popts ); - $out->addWikiTextTitleTidy( $rev->getText( Revision::FOR_THIS_USER, $user ), $this->mTargetObj, true ); + + $pout = $content->getParserOutput( $this->mTargetObj, $rev->getId(), $popts, true ); + $out->addParserOutput( $pout ); } + if ( $isText ) { + // source view for textual content + $sourceView = Xml::element( 'textarea', array( + 'readonly' => 'readonly', + 'cols' => $user->getIntOption( 'cols' ), + 'rows' => $user->getIntOption( 'rows' ) ), + $content->getNativeData() . "\n" ); + + $previewButton = Xml::element( 'input', array( + 'type' => 'submit', + 'name' => 'preview', + 'value' => $this->msg( 'showpreview' )->text() ) ); + } else { + $sourceView = ''; + $previewButton = ''; + } + + $diffButton = Xml::element( 'input', array( + 'name' => 'diff', + 'type' => 'submit', + 'value' => $this->msg( 'showdiff' )->text() ) ); + $out->addHTML( - Xml::element( 'textarea', array( - 'readonly' => 'readonly', - 'cols' => intval( $user->getOption( 'cols' ) ), - 'rows' => intval( $user->getOption( 'rows' ) ) ), - $rev->getText( Revision::FOR_THIS_USER, $user ) . "\n" ) . - Xml::openElement( 'div' ) . + $sourceView . + Xml::openElement( 'div', array( + 'style' => 'clear: both' ) ) . Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalURL( array( 'action' => 'submit' ) ) ) ) . Xml::element( 'input', array( 'type' => 'hidden', 'name' => 'target', - 'value' => $this->mTargetObj->getPrefixedDbKey() ) ) . + 'value' => $this->mTargetObj->getPrefixedDBkey() ) ) . Xml::element( 'input', array( 'type' => 'hidden', 'name' => 'timestamp', @@ -883,14 +974,8 @@ class SpecialUndelete extends SpecialPage { 'type' => 'hidden', 'name' => 'wpEditToken', 'value' => $user->getEditToken() ) ) . - Xml::element( 'input', array( - 'type' => 'submit', - 'name' => 'preview', - 'value' => $this->msg( 'showpreview' )->text() ) ) . - Xml::element( 'input', array( - 'name' => 'diff', - 'type' => 'submit', - 'value' => $this->msg( 'showdiff' )->text() ) ) . + $previewButton . + $diffButton . Xml::closeElement( 'form' ) . Xml::closeElement( 'div' ) ); } @@ -904,26 +989,30 @@ class SpecialUndelete extends SpecialPage { * @return String: HTML */ function showDiff( $previousRev, $currentRev ) { - $diffEngine = new DifferenceEngine( $this->getContext() ); + $diffContext = clone $this->getContext(); + $diffContext->setTitle( $currentRev->getTitle() ); + $diffContext->setWikiPage( WikiPage::factory( $currentRev->getTitle() ) ); + + $diffEngine = $currentRev->getContentHandler()->createDifferenceEngine( $diffContext ); $diffEngine->showDiffStyle(); $this->getOutput()->addHTML( "<div>" . - "<table width='98%' cellpadding='0' cellspacing='4' class='diff'>" . + "<table style='width: 98%;' cellpadding='0' cellspacing='4' class='diff'>" . "<col class='diff-marker' />" . "<col class='diff-content' />" . "<col class='diff-marker' />" . "<col class='diff-content' />" . "<tr>" . - "<td colspan='2' width='50%' style='text-align: center' class='diff-otitle'>" . + "<td colspan='2' style='width: 50%; text-align: center' class='diff-otitle'>" . $this->diffHeader( $previousRev, 'o' ) . "</td>\n" . - "<td colspan='2' width='50%' style='text-align: center' class='diff-ntitle'>" . + "<td colspan='2' style='width: 50%; text-align: center' class='diff-ntitle'>" . $this->diffHeader( $currentRev, 'n' ) . "</td>\n" . "</tr>" . - $diffEngine->generateDiffBody( - $previousRev->getText( Revision::FOR_THIS_USER, $this->getUser() ), - $currentRev->getText( Revision::FOR_THIS_USER, $this->getUser() ) ) . + $diffEngine->generateContentDiffBody( + $previousRev->getContent( Revision::FOR_THIS_USER, $this->getUser() ), + $currentRev->getContent( Revision::FOR_THIS_USER, $this->getUser() ) ) . "</table>" . "</div>\n" ); @@ -967,10 +1056,10 @@ class SpecialUndelete extends SpecialPage { $targetQuery ) . '</strong></div>' . - '<div id="mw-diff-'.$prefix.'title2">' . + '<div id="mw-diff-' . $prefix . 'title2">' . Linker::revUserTools( $rev ) . '<br />' . '</div>' . - '<div id="mw-diff-'.$prefix.'title3">' . + '<div id="mw-diff-' . $prefix . 'title3">' . Linker::revComment( $rev ) . $rdel . '<br />' . '</div>'; } @@ -1122,7 +1211,7 @@ class SpecialUndelete extends SpecialPage { Xml::label( $this->msg( 'undeletecomment' )->text(), 'wpComment' ) . "</td> <td class='mw-input'>" . - Xml::input( 'wpComment', 50, $this->mComment, array( 'id' => 'wpComment' ) ) . + Xml::input( 'wpComment', 50, $this->mComment, array( 'id' => 'wpComment' ) ) . "</td> </tr> <tr> @@ -1169,7 +1258,7 @@ class SpecialUndelete extends SpecialPage { if ( $this->mAllowed ) { # Slip in the hidden controls here - $misc = Html::hidden( 'target', $this->mTarget ); + $misc = Html::hidden( 'target', $this->mTarget ); $misc .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ); $misc .= Xml::closeElement( 'form' ); $out->addHTML( $misc ); @@ -1180,7 +1269,10 @@ class SpecialUndelete extends SpecialPage { private function formatRevisionRow( $row, $earliestLiveTime, $remaining ) { $rev = Revision::newFromArchiveRow( $row, - array( 'page' => $this->mTargetObj->getArticleID() ) ); + array( + 'title' => $this->mTargetObj + ) ); + $revTextSize = ''; $ts = wfTimestamp( TS_MW, $row->ar_timestamp ); // Build checkboxen... @@ -1237,7 +1329,7 @@ class SpecialUndelete extends SpecialPage { // Revision delete links $revdlink = Linker::getRevDeleteLink( $user, $rev, $this->mTargetObj ); - $revisionRow = $this->msg( 'undelete-revisionrow' )->rawParams( $checkBox, $revdlink, $last, $pageLink , $userLink, $revTextSize, $comment )->escaped(); + $revisionRow = $this->msg( 'undelete-revisionrow' )->rawParams( $checkBox, $revdlink, $last, $pageLink, $userLink, $revTextSize, $comment )->escaped(); return "<li>$revisionRow</li>"; } @@ -1286,7 +1378,7 @@ class SpecialUndelete extends SpecialPage { * * @param $rev Revision * @param $titleObj Title - * @param $ts string Timestamp + * @param string $ts Timestamp * @return string */ function getPageLink( $rev, $titleObj, $ts ) { @@ -1317,8 +1409,8 @@ class SpecialUndelete extends SpecialPage { * * @param $file File * @param $titleObj Title - * @param $ts string A timestamp - * @param $key String: a storage key + * @param string $ts A timestamp + * @param string $key a storage key * * @return String: HTML fragment */ @@ -1417,14 +1509,22 @@ class SpecialUndelete extends SpecialPage { $out->addHTML( $this->msg( 'undeletedpage' )->rawParams( $link )->parse() ); } else { $out->setPageTitle( $this->msg( 'undelete-error' ) ); - $out->addWikiMsg( 'cannotundelete' ); - $out->addWikiMsg( 'undeleterevdel' ); } - // Show file deletion warnings and errors + // Show revision undeletion warnings and errors + $status = $archive->getRevisionStatus(); + if( $status && !$status->isGood() ) { + $out->addWikiText( '<div class="error">' . $status->getWikiText( 'cannotundelete', 'cannotundelete' ) . '</div>' ); + } + + // Show file undeletion warnings and errors $status = $archive->getFileStatus(); if( $status && !$status->isGood() ) { $out->addWikiText( '<div class="error">' . $status->getWikiText( 'undelete-error-short', 'undelete-error-long' ) . '</div>' ); } } + + protected function getGroupName() { + return 'pagetools'; + } } diff --git a/includes/specials/SpecialUnlockdb.php b/includes/specials/SpecialUnlockdb.php index 2e772540..35141d80 100644 --- a/includes/specials/SpecialUnlockdb.php +++ b/includes/specials/SpecialUnlockdb.php @@ -84,4 +84,8 @@ class SpecialUnlockdb extends FormSpecialPage { $out->addSubtitle( $this->msg( 'unlockdbsuccesssub' ) ); $out->addWikiMsg( 'unlockdbsuccesstext' ); } + + protected function getGroupName() { + return 'wiki'; + } } diff --git a/includes/specials/SpecialUnusedcategories.php b/includes/specials/SpecialUnusedcategories.php index 1bd38e17..6b91dd39 100644 --- a/includes/specials/SpecialUnusedcategories.php +++ b/includes/specials/SpecialUnusedcategories.php @@ -26,7 +26,9 @@ */ class UnusedCategoriesPage extends QueryPage { - function isExpensive() { return true; } + function isExpensive() { + return true; + } function __construct( $name = 'Unusedcategories' ) { parent::__construct( $name ); @@ -62,4 +64,8 @@ class UnusedCategoriesPage extends QueryPage { $title = Title::makeTitle( NS_CATEGORY, $result->title ); return Linker::link( $title, htmlspecialchars( $title->getText() ) ); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialUnusedimages.php b/includes/specials/SpecialUnusedimages.php index cdab557e..69553282 100644 --- a/includes/specials/SpecialUnusedimages.php +++ b/includes/specials/SpecialUnusedimages.php @@ -80,4 +80,7 @@ class UnusedimagesPage extends ImageQueryPage { return $this->msg( 'unusedimagestext' )->parseAsBlock(); } + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialUnusedtemplates.php b/includes/specials/SpecialUnusedtemplates.php index 06077d1f..493e936a 100644 --- a/includes/specials/SpecialUnusedtemplates.php +++ b/includes/specials/SpecialUnusedtemplates.php @@ -35,9 +35,17 @@ class UnusedtemplatesPage extends QueryPage { parent::__construct( $name ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } - function sortDescending() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } + + function sortDescending() { + return false; + } function getQueryInfo() { return array ( @@ -77,4 +85,8 @@ class UnusedtemplatesPage extends QueryPage { function getPageHeader() { return $this->msg( 'unusedtemplatestext' )->parseAsBlock(); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialUnwatchedpages.php b/includes/specials/SpecialUnwatchedpages.php index e5a79413..05ec6b02 100644 --- a/includes/specials/SpecialUnwatchedpages.php +++ b/includes/specials/SpecialUnwatchedpages.php @@ -35,8 +35,13 @@ class UnwatchedpagesPage extends QueryPage { parent::__construct( $name, 'unwatchedpages' ); } - function isExpensive() { return true; } - function isSyndicated() { return false; } + function isExpensive() { + return true; + } + + function isSyndicated() { + return false; + } function getQueryInfo() { return array ( @@ -54,7 +59,9 @@ class UnwatchedpagesPage extends QueryPage { ); } - function sortDescending() { return false; } + function sortDescending() { + return false; + } function getOrderFields() { return array( 'page_namespace', 'page_title' ); @@ -87,4 +94,8 @@ class UnwatchedpagesPage extends QueryPage { return $this->getLanguage()->specialList( $plink, $wlink ); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialUpload.php b/includes/specials/SpecialUpload.php index 43ea345b..89c06b2a 100644 --- a/includes/specials/SpecialUpload.php +++ b/includes/specials/SpecialUpload.php @@ -39,7 +39,7 @@ class SpecialUpload extends SpecialPage { } /** Misc variables **/ - public $mRequest; // The WebRequest or FauxRequest this form is supposed to handle + public $mRequest; // The WebRequest or FauxRequest this form is supposed to handle public $mSourceType; /** @@ -54,7 +54,7 @@ class SpecialUpload extends SpecialPage { public $mUploadClicked; /** User input variables from the "description" section **/ - public $mDesiredDestName; // The requested target file name + public $mDesiredDestName; // The requested target file name public $mComment; public $mLicense; @@ -66,10 +66,10 @@ class SpecialUpload extends SpecialPage { /** Hidden variables **/ public $mDestWarningAck; - public $mForReUpload; // The user followed an "overwrite this file" link - public $mCancelUpload; // The user clicked "Cancel and return to upload form" button + public $mForReUpload; // The user followed an "overwrite this file" link + public $mCancelUpload; // The user clicked "Cancel and return to upload form" button public $mTokenOk; - public $mUploadSuccessful = false; // Subclasses can use this to determine whether a file was uploaded + public $mUploadSuccessful = false; // Subclasses can use this to determine whether a file was uploaded /** Text injection points for hooks not using HTMLForm **/ public $uploadFormTextTop; @@ -82,32 +82,30 @@ class SpecialUpload extends SpecialPage { */ protected function loadRequest() { $this->mRequest = $request = $this->getRequest(); - $this->mSourceType = $request->getVal( 'wpSourceType', 'file' ); - $this->mUpload = UploadBase::createFromRequest( $request ); - $this->mUploadClicked = $request->wasPosted() + $this->mSourceType = $request->getVal( 'wpSourceType', 'file' ); + $this->mUpload = UploadBase::createFromRequest( $request ); + $this->mUploadClicked = $request->wasPosted() && ( $request->getCheck( 'wpUpload' ) || $request->getCheck( 'wpUploadIgnoreWarning' ) ); // Guess the desired name from the filename if not provided - $this->mDesiredDestName = $request->getText( 'wpDestFile' ); + $this->mDesiredDestName = $request->getText( 'wpDestFile' ); if( !$this->mDesiredDestName && $request->getFileName( 'wpUploadFile' ) !== null ) { $this->mDesiredDestName = $request->getFileName( 'wpUploadFile' ); } - $this->mComment = $request->getText( 'wpUploadDescription' ); - $this->mLicense = $request->getText( 'wpLicense' ); + $this->mComment = $request->getText( 'wpUploadDescription' ); + $this->mLicense = $request->getText( 'wpLicense' ); - - $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' ); - $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ) + $this->mDestWarningAck = $request->getText( 'wpDestFileWarningAck' ); + $this->mIgnoreWarning = $request->getCheck( 'wpIgnoreWarning' ) || $request->getCheck( 'wpUploadIgnoreWarning' ); - $this->mWatchthis = $request->getBool( 'wpWatchthis' ) && $this->getUser()->isLoggedIn(); - $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); - $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); - + $this->mWatchthis = $request->getBool( 'wpWatchthis' ) && $this->getUser()->isLoggedIn(); + $this->mCopyrightStatus = $request->getText( 'wpUploadCopyStatus' ); + $this->mCopyrightSource = $request->getText( 'wpUploadSource' ); - $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file - $this->mCancelUpload = $request->getCheck( 'wpCancelUpload' ) - || $request->getCheck( 'wpReUpload' ); // b/w compat + $this->mForReUpload = $request->getBool( 'wpForReUpload' ); // updating a file + $this->mCancelUpload = $request->getCheck( 'wpCancelUpload' ) + || $request->getCheck( 'wpReUpload' ); // b/w compat // If it was posted check for the token (no remote POST'ing with user credentials) $token = $request->getVal( 'wpEditToken' ); @@ -209,8 +207,8 @@ class SpecialUpload extends SpecialPage { /** * Get an UploadForm instance with title and text properly set. * - * @param $message String: HTML string to add to the form - * @param $sessionKey String: session key in case this is a stashed upload + * @param string $message HTML string to add to the form + * @param string $sessionKey session key in case this is a stashed upload * @param $hideIgnoreWarning Boolean: whether to hide "ignore warning" check box * @return UploadForm */ @@ -246,9 +244,9 @@ class SpecialUpload extends SpecialPage { LogEventsList::showLogExtract( $delNotice, array( 'delete', 'move' ), $desiredTitleObj, '', array( 'lim' => 10, - 'conds' => array( "log_action != 'revision'" ), - 'showIfEmpty' => false, - 'msgKey' => array( 'upload-recreate-warning' ) ) + 'conds' => array( "log_action != 'revision'" ), + 'showIfEmpty' => false, + 'msgKey' => array( 'upload-recreate-warning' ) ) ); } $form->addPreText( $delNotice ); @@ -300,7 +298,7 @@ class SpecialUpload extends SpecialPage { * essentially means that UploadBase::VERIFICATION_ERROR and * UploadBase::EMPTY_FILE should not be passed here. * - * @param $message String: HTML message to be passed to mainUploadForm + * @param string $message HTML message to be passed to mainUploadForm */ protected function showRecoverableUploadError( $message ) { $sessionKey = $this->mUpload->stashSession(); @@ -312,12 +310,12 @@ class SpecialUpload extends SpecialPage { $this->showUploadForm( $form ); } /** - * Stashes the upload, shows the main form, but adds an "continue anyway button". + * Stashes the upload, shows the main form, but adds a "continue anyway button". * Also checks whether there are actually warnings to display. * * @param $warnings Array * @return boolean true if warnings were displayed, false if there are no - * warnings and the should continue processing like there was no warning + * warnings and it should continue processing */ protected function showUploadWarning( $warnings ) { # If there are no warnings, or warnings we can ignore, return early. @@ -336,6 +334,9 @@ class SpecialUpload extends SpecialPage { $warningHtml = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" . '<ul class="warning">'; foreach( $warnings as $warning => $args ) { + if( $warning == 'badfilename' ) { + $this->mDesiredDestName = Title::makeTitle( NS_FILE, $args )->getText(); + } if( $warning == 'exists' ) { $msg = "\t<li>" . self::getExistsWarning( $args ) . "</li>\n"; } elseif( $warning == 'duplicate' ) { @@ -371,7 +372,7 @@ class SpecialUpload extends SpecialPage { /** * Show the upload form with error message, but do not stash the file. * - * @param $message string HTML string + * @param string $message HTML string */ protected function showUploadError( $message ) { $message = '<h2>' . $this->msg( 'uploadwarning' )->escaped() . "</h2>\n" . @@ -473,17 +474,17 @@ class SpecialUpload extends SpecialPage { if ( $wgUseCopyrightUpload ) { $licensetxt = ''; if ( $license != '' ) { - $licensetxt = '== ' . $msg[ 'license-header' ] . " ==\n" . '{{' . $license . '}}' . "\n"; + $licensetxt = '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n"; } - $pageText = '== ' . $msg[ 'filedesc' ] . " ==\n" . $comment . "\n" . - '== ' . $msg[ 'filestatus' ] . " ==\n" . $copyStatus . "\n" . + $pageText = '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n" . + '== ' . $msg['filestatus'] . " ==\n" . $copyStatus . "\n" . "$licensetxt" . - '== ' . $msg[ 'filesource' ] . " ==\n" . $source; + '== ' . $msg['filesource'] . " ==\n" . $source; } else { if ( $license != '' ) { - $filedesc = $comment == '' ? '' : '== ' . $msg[ 'filedesc' ] . " ==\n" . $comment . "\n"; + $filedesc = $comment == '' ? '' : '== ' . $msg['filedesc'] . " ==\n" . $comment . "\n"; $pageText = $filedesc . - '== ' . $msg[ 'license-header' ] . " ==\n" . '{{' . $license . '}}' . "\n"; + '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n"; } else { $pageText = $comment; } @@ -520,11 +521,11 @@ class SpecialUpload extends SpecialPage { } } - /** * Provides output to the user for a result of UploadBase::verifyUpload * - * @param $details Array: result of UploadBase::verifyUpload + * @param array $details result of UploadBase::verifyUpload + * @throws MWException */ protected function processVerificationError( $details ) { global $wgFileExtensions; @@ -622,7 +623,7 @@ class SpecialUpload extends SpecialPage { * Formats a result of UploadBase::getExistsWarning as HTML * This check is static and can be done pre-upload via AJAX * - * @param $exists Array: the result of UploadBase::getExistsWarning + * @param array $exists the result of UploadBase::getExistsWarning * @return String: empty string if there is no warning or an HTML fragment */ public static function getExistsWarning( $exists ) { @@ -645,7 +646,7 @@ class SpecialUpload extends SpecialPage { $exists['normalizedFile']->getTitle()->getPrefixedText() )->parse(); } elseif ( $exists['warning'] == 'thumb' ) { // Swapped argument order compared with other messages for backwards compatibility - $warning = wfMessage( 'fileexists-thumbnail-yes', + $warning = wfMessage( 'fileexists-thumbnail-yes', $exists['thumbFile']->getTitle()->getPrefixedText(), $filename )->parse(); } elseif ( $exists['warning'] == 'thumb-name' ) { // Image w/o '180px-' does not exists, but we do not like these filenames @@ -675,7 +676,7 @@ class SpecialUpload extends SpecialPage { /** * Get a list of warnings * - * @param $filename String: local filename, e.g. 'file exists', 'non-descriptive filename' + * @param string $filename local filename, e.g. 'file exists', 'non-descriptive filename' * @return Array: list of warning messages */ public static function ajaxGetExistsWarning( $filename ) { @@ -716,6 +717,9 @@ class SpecialUpload extends SpecialPage { $gallery->toHtml() . "</li>\n"; } + protected function getGroupName() { + return 'media'; + } } /** @@ -789,6 +793,8 @@ class UploadForm extends HTMLForm { * @return Array: descriptor array */ protected function getSourceSection() { + global $wgCopyUploadsFromSpecialUpload; + if ( $this->mSessionKey ) { return array( 'SessionKey' => array( @@ -802,7 +808,9 @@ class UploadForm extends HTMLForm { ); } - $canUploadByUrl = UploadFromUrl::isEnabled() && UploadFromUrl::isAllowed( $this->getUser() ); + $canUploadByUrl = UploadFromUrl::isEnabled() + && UploadFromUrl::isAllowed( $this->getUser() ) + && $wgCopyUploadsFromSpecialUpload; $radio = $canUploadByUrl; $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) ); @@ -946,7 +954,7 @@ class UploadForm extends HTMLForm { ? 'filereuploadsummary' : 'fileuploadsummary', 'default' => $this->mComment, - 'cols' => intval( $this->getUser()->getOption( 'cols' ) ), + 'cols' => $this->getUser()->getIntOption( 'cols' ), 'rows' => 8, ) ); @@ -1077,7 +1085,6 @@ class UploadForm extends HTMLForm { $out = $this->getOutput(); $out->addJsConfigVars( $scriptVars ); - $out->addModules( array( 'mediawiki.action.edit', // For <charinsert> support 'mediawiki.legacy.upload', // Old form stuff... @@ -1106,7 +1113,7 @@ class UploadSourceField extends HTMLTextField { * @return string */ function getLabelHtml( $cellAttributes = array() ) { - $id = "wpSourceType{$this->mParams['upload-type']}"; + $id = $this->mParams['id']; $label = Html::rawElement( 'label', array( 'for' => $id ), $this->mLabel ); if ( !empty( $this->mParams['radio'] ) ) { @@ -1134,4 +1141,3 @@ class UploadSourceField extends HTMLTextField { : 60; } } - diff --git a/includes/specials/SpecialUploadStash.php b/includes/specials/SpecialUploadStash.php index 1a00d731..ddf0c6da 100644 --- a/includes/specials/SpecialUploadStash.php +++ b/includes/specials/SpecialUploadStash.php @@ -57,7 +57,7 @@ class SpecialUploadStash extends UnlistedSpecialPage { /** * Execute page -- can output a file directly or show a listing of them. * - * @param $subPage String: subpage, e.g. in http://example.com/wiki/Special:UploadStash/foo.jpg, the "foo.jpg" part + * @param string $subPage subpage, e.g. in http://example.com/wiki/Special:UploadStash/foo.jpg, the "foo.jpg" part * @return Boolean: success */ public function execute( $subPage ) { @@ -73,7 +73,8 @@ class SpecialUploadStash extends UnlistedSpecialPage { * If file available in stash, cats it out to the client as a simple HTTP response. * n.b. Most sanity checking done in UploadStashLocalFile, so this is straightforward. * - * @param $key String: the key of a particular requested file + * @param string $key the key of a particular requested file + * @throws HttpError * @return bool */ public function showUpload( $key ) { @@ -113,6 +114,7 @@ class SpecialUploadStash extends UnlistedSpecialPage { * application the transform parameters * * @param string $key + * @throws UploadStashBadPathException * @return array */ private function parseKey( $key ) { @@ -164,10 +166,11 @@ class SpecialUploadStash extends UnlistedSpecialPage { /** * Scale a file (probably with a locally installed imagemagick, or similar) and output it to STDOUT. - * @param $file: File object - * @param $params: scaling parameters ( e.g. array( width => '50' ) ); - * @param $flags: scaling flags ( see File:: constants ) + * @param $file File + * @param array $params Scaling parameters ( e.g. array( width => '50' ) ); + * @param int $flags Scaling flags ( see File:: constants ) * @throws MWException + * @throws UploadStashFileNotFoundException * @return boolean success */ private function outputLocallyScaledThumb( $file, $params, $flags ) { @@ -189,7 +192,7 @@ class SpecialUploadStash extends UnlistedSpecialPage { // now we should construct a File, so we can get mime and other such info in a standard way // n.b. mimetype may be different from original (ogx original -> jpeg thumb) - $thumbFile = new UnregisteredLocalFile( false, + $thumbFile = new UnregisteredLocalFile( false, $this->stash->repo, $thumbnailImage->getStoragePath(), false ); if ( !$thumbFile ) { throw new UploadStashFileNotFoundException( "couldn't create local file object for thumbnail" ); @@ -258,6 +261,7 @@ class SpecialUploadStash extends UnlistedSpecialPage { * Side effect: writes HTTP response to STDOUT. * * @param $file File object with a local path (e.g. UnregisteredLocalFile, LocalFile. Oddly these don't share an ancestor!) + * @throws SpecialUploadStashTooLargeException * @return bool */ private function outputLocalFile( File $file ) { @@ -273,8 +277,9 @@ class SpecialUploadStash extends UnlistedSpecialPage { /** * Output HTTP response of raw content * Side effect: writes HTTP response to STDOUT. - * @param $content String content - * @param $contentType String mime type + * @param string $content content + * @param string $contentType mime type + * @throws SpecialUploadStashTooLargeException * @return bool */ private function outputContents( $content, $contentType ) { @@ -291,8 +296,8 @@ class SpecialUploadStash extends UnlistedSpecialPage { * Output headers for streaming * XXX unsure about encoding as binary; if we received from HTTP perhaps we should use that encoding, concatted with semicolon to mimeType as it usually is. * Side effect: preps PHP to write headers to STDOUT. - * @param String $contentType : string suitable for content-type header - * @param String $size: length in bytes + * @param string $contentType : string suitable for content-type header + * @param string $size: length in bytes */ private static function outputFileHeaders( $contentType, $size ) { header( "Content-Type: $contentType", true ); @@ -322,14 +327,9 @@ class SpecialUploadStash extends UnlistedSpecialPage { /** * Default action when we don't have a subpage -- just show links to the uploads we have, * Also show a button to clear stashed files - * @param $status [optional] Status: the result of processRequest * @return bool */ - private function showUploads( $status = null ) { - if ( $status === null ) { - $status = Status::newGood(); - } - + private function showUploads() { // sets the title, etc. $this->setHeaders(); $this->outputHeader(); @@ -344,7 +344,7 @@ class SpecialUploadStash extends UnlistedSpecialPage { 'name' => 'clear', ) ), $this->getContext(), 'clearStashedUploads' ); - $form->setSubmitCallback( array( __CLASS__ , 'tryClearStashedUploads' ) ); + $form->setSubmitCallback( array( __CLASS__, 'tryClearStashedUploads' ) ); $form->setTitle( $this->getTitle() ); $form->setSubmitTextMsg( 'uploadstash-clear' ); diff --git a/includes/specials/SpecialUserlogin.php b/includes/specials/SpecialUserlogin.php index 58da77da..eef66914 100644 --- a/includes/specials/SpecialUserlogin.php +++ b/includes/specials/SpecialUserlogin.php @@ -95,9 +95,9 @@ class LoginForm extends SpecialPage { $this->mReason = $request->getText( 'wpReason' ); $this->mCookieCheck = $request->getVal( 'wpCookieCheck' ); $this->mPosted = $request->wasPosted(); - $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' ); $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' ) && $wgEnableEmail; + $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' ) && !$this->mCreateaccountMail; $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' ); $this->mAction = $request->getVal( 'action' ); $this->mRemember = $request->getCheck( 'wpRemember' ); @@ -149,6 +149,23 @@ class LoginForm extends SpecialPage { $this->load(); $this->setHeaders(); + global $wgSecureLogin; + if ( + $this->mType !== 'signup' && + $wgSecureLogin && + WebRequest::detectProtocol() !== 'https' + ) { + $title = $this->getFullTitle(); + $query = array( + 'returnto' => $this->mReturnTo, + 'returntoquery' => $this->mReturnToQuery, + 'wpStickHTTPS' => $this->mStickHTTPS + ); + $url = $title->getFullURL( $query, false, PROTO_HTTPS ); + $this->getOutput()->redirect( $url ); + return; + } + if ( $par == 'signup' ) { # Check for [[Special:Userlogin/signup]] $this->mType = 'signup'; } @@ -180,19 +197,22 @@ class LoginForm extends SpecialPage { return; } - $u = $this->addNewaccountInternal(); - - if ( $u == null ) { + $status = $this->addNewaccountInternal(); + if( !$status->isGood() ) { + $error = $this->getOutput()->parse( $status->getWikiText() ); + $this->mainLoginForm( $error ); return; } + $u = $status->getValue(); + // Wipe the initial password and mail a temporary one $u->setPassword( null ); $u->saveSettings(); $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' ); wfRunHooks( 'AddNewAccount', array( $u, true ) ); - $u->addNewUserLogEntry( true, $this->mReason ); + $u->addNewUserLogEntry( 'byemail', $this->mReason ); $out = $this->getOutput(); $out->setPageTitle( $this->msg( 'accmailtitle' ) ); @@ -210,18 +230,34 @@ class LoginForm extends SpecialPage { * @return bool */ function addNewAccount() { - global $wgUser, $wgEmailAuthentication, $wgLoginLanguageSelector; + global $wgContLang, $wgUser, $wgEmailAuthentication, $wgLoginLanguageSelector; # Create the account and abort if there's a problem doing so - $u = $this->addNewAccountInternal(); - if( $u == null ) { + $status = $this->addNewAccountInternal(); + if( !$status->isGood() ) { + $error = $this->getOutput()->parse( $status->getWikiText() ); + $this->mainLoginForm( $error ); return false; } - # If we showed up language selection links, and one was in use, be - # smart (and sensible) and save that language as the user's preference - if( $wgLoginLanguageSelector && $this->mLanguage ) { - $u->setOption( 'language', $this->mLanguage ); + $u = $status->getValue(); + + # Only save preferences if the user is not creating an account for someone else. + if ( $this->getUser()->isAnon() ) { + # If we showed up language selection links, and one was in use, be + # smart (and sensible) and save that language as the user's preference + if( $wgLoginLanguageSelector && $this->mLanguage ) { + $u->setOption( 'language', $this->mLanguage ); + } else { + + # Otherwise the user's language preference defaults to $wgContLang, + # but it may be better to set it to their preferred $wgContLang variant, + # based on browser preferences or URL parameters. + $u->setOption( 'language', $wgContLang->getPreferredVariant() ); + } + if ( $wgContLang->hasVariants() ) { + $u->setOption( 'variant', $wgContLang->getPreferredVariant() ); + } } $out = $this->getOutput(); @@ -250,7 +286,7 @@ class LoginForm extends SpecialPage { // wrong. $this->getContext()->setUser( $u ); wfRunHooks( 'AddNewAccount', array( $u, false ) ); - $u->addNewUserLogEntry(); + $u->addNewUserLogEntry( 'create' ); if( $this->hasSessionCookie() ) { $this->successfulCreation(); } else { @@ -262,23 +298,24 @@ class LoginForm extends SpecialPage { $out->addWikiMsg( 'accountcreatedtext', $u->getName() ); $out->addReturnTo( $this->getTitle() ); wfRunHooks( 'AddNewAccount', array( $u, false ) ); - $u->addNewUserLogEntry( false, $this->mReason ); + $u->addNewUserLogEntry( 'create2', $this->mReason ); } return true; } /** + * Make a new user account using the loaded data. * @private - * @return bool|User + * @throws PermissionsError|ReadOnlyError + * @return Status */ - function addNewAccountInternal() { + public function addNewAccountInternal() { global $wgAuth, $wgMemc, $wgAccountCreationThrottle, $wgMinimalPasswordLength, $wgEmailConfirmToEdit; // If the user passes an invalid domain, something is fishy if( !$wgAuth->validDomain( $this->mDomain ) ) { - $this->mainLoginForm( $this->msg( 'wrongpassword' )->text() ); - return false; + return Status::newFatal( 'wrongpassword' ); } // If we are not allowing users to login locally, we should be checking @@ -287,10 +324,14 @@ class LoginForm extends SpecialPage { // create a local account and login as any domain user). We only need // to check this for domains that aren't local. if( 'local' != $this->mDomain && $this->mDomain != '' ) { - if( !$wgAuth->canCreateAccounts() && ( !$wgAuth->userExists( $this->mUsername ) - || !$wgAuth->authenticate( $this->mUsername, $this->mPassword ) ) ) { - $this->mainLoginForm( $this->msg( 'wrongpassword' )->text() ); - return false; + if( + !$wgAuth->canCreateAccounts() && + ( + !$wgAuth->userExists( $this->mUsername ) || + !$wgAuth->authenticate( $this->mUsername, $this->mPassword ) + ) + ) { + return Status::newFatal( 'wrongpassword' ); } } @@ -301,28 +342,28 @@ class LoginForm extends SpecialPage { # Request forgery checks. if ( !self::getCreateaccountToken() ) { self::setCreateaccountToken(); - $this->mainLoginForm( $this->msg( 'nocookiesfornew' )->parse() ); - return false; + return Status::newFatal( 'nocookiesfornew' ); } # The user didn't pass a createaccount token if ( !$this->mToken ) { - $this->mainLoginForm( $this->msg( 'sessionfailure' )->text() ); - return false; + return Status::newFatal( 'sessionfailure' ); } # Validate the createaccount token if ( $this->mToken !== self::getCreateaccountToken() ) { - $this->mainLoginForm( $this->msg( 'sessionfailure' )->text() ); - return false; + return Status::newFatal( 'sessionfailure' ); } # Check permissions $currentUser = $this->getUser(); + $creationBlock = $currentUser->isBlockedFromCreateAccount(); if ( !$currentUser->isAllowed( 'createaccount' ) ) { throw new PermissionsError( 'createaccount' ); - } elseif ( $currentUser->isBlockedFromCreateAccount() ) { - $this->userBlockedMessage( $currentUser->isBlockedFromCreateAccount() ); + } elseif ( $creationBlock instanceof Block ) { + // Throws an ErrorPageError. + $this->userBlockedMessage( $creationBlock ); + // This should never be reached. return false; } @@ -334,58 +375,45 @@ class LoginForm extends SpecialPage { $ip = $this->getRequest()->getIP(); if ( $currentUser->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) { - $this->mainLoginForm( $this->msg( 'sorbs_create_account_reason' )->text() . ' ' . $this->msg( 'parentheses', $ip )->escaped() ); - return false; + return Status::newFatal( 'sorbs_create_account_reason' ); } # Now create a dummy user ($u) and check if it is valid $name = trim( $this->mUsername ); $u = User::newFromName( $name, 'creatable' ); if ( !is_object( $u ) ) { - $this->mainLoginForm( $this->msg( 'noname' )->text() ); - return false; - } - - if ( 0 != $u->idForName() ) { - $this->mainLoginForm( $this->msg( 'userexists' )->text() ); - return false; + return Status::newFatal( 'noname' ); + } elseif ( 0 != $u->idForName() ) { + return Status::newFatal( 'userexists' ); } - if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) { - $this->mainLoginForm( $this->msg( 'badretype' )->text() ); - return false; - } + if ( $this->mCreateaccountMail ) { + # do not force a password for account creation by email + # set invalid password, it will be replaced later by a random generated password + $this->mPassword = null; + } else { + if ( $this->mPassword !== $this->mRetype ) { + return Status::newFatal( 'badretype' ); + } - # check for minimal password length - $valid = $u->getPasswordValidity( $this->mPassword ); - if ( $valid !== true ) { - if ( !$this->mCreateaccountMail ) { - if ( is_array( $valid ) ) { - $message = array_shift( $valid ); - $params = $valid; - } else { - $message = $valid; - $params = array( $wgMinimalPasswordLength ); + # check for minimal password length + $valid = $u->getPasswordValidity( $this->mPassword ); + if ( $valid !== true ) { + if ( !is_array( $valid ) ) { + $valid = array( $valid, $wgMinimalPasswordLength ); } - $this->mainLoginForm( $this->msg( $message, $params )->text() ); - return false; - } else { - # do not force a password for account creation by email - # set invalid password, it will be replaced later by a random generated password - $this->mPassword = null; + return call_user_func_array( 'Status::newFatal', $valid ); } } # if you need a confirmed email address to edit, then obviously you # need an email address. - if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) ) { - $this->mainLoginForm( $this->msg( 'noemailtitle' )->text() ); - return false; + if ( $wgEmailConfirmToEdit && strval( $this->mEmail ) === '' ) { + return Status::newFatal( 'noemailtitle' ); } - if( !empty( $this->mEmail ) && !Sanitizer::validateEmail( $this->mEmail ) ) { - $this->mainLoginForm( $this->msg( 'invalidemailaddress' )->text() ); - return false; + if ( strval( $this->mEmail ) !== '' && !Sanitizer::validateEmail( $this->mEmail ) ) { + return Status::newFatal( 'invalidemailaddress' ); } # Set some additional data so the AbortNewAccount hook can be used for @@ -397,8 +425,7 @@ class LoginForm extends SpecialPage { if( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) { // Hook point to add extra creation throttles and blocks wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" ); - $this->mainLoginForm( $abortError ); - return false; + return Status::newFatal( new RawMessage( $abortError ) ); } // Hook point to check for exempt from account creation throttle @@ -412,16 +439,14 @@ class LoginForm extends SpecialPage { $wgMemc->set( $key, 0, 86400 ); } if ( $value >= $wgAccountCreationThrottle ) { - $this->throttleHit( $wgAccountCreationThrottle ); - return false; + return Status::newFatal( 'acct_creation_throttle_hit', $wgAccountCreationThrottle ); } $wgMemc->incr( $key ); } } if( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) { - $this->mainLoginForm( $this->msg( 'externaldberror' )->text() ); - return false; + return Status::newFatal( 'externaldberror' ); } self::clearCreateaccountToken(); @@ -434,13 +459,16 @@ class LoginForm extends SpecialPage { * * @param $u User object. * @param $autocreate boolean -- true if this is an autocreation via auth plugin - * @return User object. + * @return Status object, with the User object in the value member on success * @private */ function initUser( $u, $autocreate ) { global $wgAuth; - $u->addToDatabase(); + $status = $u->addToDatabase(); + if ( !$status->isOK() ) { + return $status; + } if ( $wgAuth->allowPasswordChange() ) { $u->setPassword( $this->mPassword ); @@ -464,10 +492,9 @@ class LoginForm extends SpecialPage { $u->saveSettings(); # Update user count - $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 ); - $ssUpdate->doUpdate(); + DeferredUpdates::addUpdate( new SiteStatsUpdate( 0, 0, 0, 0, 1 ) ); - return $u; + return Status::newGood( $u ); } /** @@ -533,7 +560,7 @@ class LoginForm extends SpecialPage { } $isAutoCreated = false; - if ( 0 == $u->getID() ) { + if ( $u->getID() == 0 ) { $status = $this->attemptAutoCreate( $u ); if ( $status !== self::SUCCESS ) { return $status; @@ -543,8 +570,9 @@ class LoginForm extends SpecialPage { } else { global $wgExternalAuthType, $wgAutocreatePolicy; if ( $wgExternalAuthType && $wgAutocreatePolicy != 'never' - && is_object( $this->mExtUser ) - && $this->mExtUser->authenticate( $this->mPassword ) ) { + && is_object( $this->mExtUser ) + && $this->mExtUser->authenticate( $this->mPassword ) + ) { # The external user and local user have the same name and # password, so we assume they're the same. $this->mExtUser->linkToLocal( $u->getID() ); @@ -588,7 +616,7 @@ class LoginForm extends SpecialPage { // faces etc will probably just fail cleanly here. $retval = self::RESET_PASS; } else { - $retval = ( $this->mPassword == '' ) ? self::EMPTY_PASS : self::WRONG_PASS; + $retval = ( $this->mPassword == '' ) ? self::EMPTY_PASS : self::WRONG_PASS; } } elseif ( $wgBlockDisablesLogin && $u->isBlocked() ) { // If we've enabled it, make it so that a blocked user cannot login @@ -620,7 +648,7 @@ class LoginForm extends SpecialPage { /** * Increment the login attempt throttle hit count for the (username,current IP) * tuple unless the throttle was already reached. - * @param $username string The user name + * @param string $username The user name * @return Bool|Integer The integer hit count or True if it is already at the limit */ public static function incLoginThrottle( $username ) { @@ -648,7 +676,7 @@ class LoginForm extends SpecialPage { /** * Clear the login attempt throttle hit count for the (username,current IP) tuple. - * @param $username string The user name + * @param string $username The user name * @return void */ public static function clearLoginThrottle( $username ) { @@ -713,24 +741,36 @@ class LoginForm extends SpecialPage { } wfDebug( __METHOD__ . ": creating account\n" ); - $this->initUser( $user, true ); + $status = $this->initUser( $user, true ); + + if ( !$status->isOK() ) { + $errors = $status->getErrorsByType( 'error' ); + $this->mAbortLoginErrorMsg = $errors[0]['message']; + return self::ABORTED; + } + return self::SUCCESS; } function processLogin() { - global $wgMemc, $wgLang; + global $wgMemc, $wgLang, $wgSecureLogin; switch ( $this->authenticateUserData() ) { case self::SUCCESS: # We've verified now, update the real record $user = $this->getUser(); - if( (bool)$this->mRemember != (bool)$user->getOption( 'rememberpassword' ) ) { + if( (bool)$this->mRemember != $user->getBoolOption( 'rememberpassword' ) ) { $user->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 ); $user->saveSettings(); } else { $user->invalidateCache(); } - $user->setCookies(); + + if( $wgSecureLogin && !$this->mStickHTTPS ) { + $user->setCookies( null, false ); + } else { + $user->setCookies(); + } self::clearLoginToken(); // Reset the throttle @@ -803,8 +843,11 @@ class LoginForm extends SpecialPage { } } + /** + * @param $error string + */ function resetLoginForm( $error ) { - $this->getOutput()->addHTML( Xml::element('p', array( 'class' => 'error' ), $error ) ); + $this->getOutput()->addHTML( Xml::element( 'p', array( 'class' => 'error' ), $error ) ); $reset = new SpecialChangePassword(); $reset->setContext( $this->getContext() ); $reset->execute( null ); @@ -813,12 +856,12 @@ class LoginForm extends SpecialPage { /** * @param $u User object * @param $throttle Boolean - * @param $emailTitle String: message name of email title - * @param $emailText String: message name of email text + * @param string $emailTitle message name of email title + * @param string $emailText message name of email text * @return Status object */ function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext' ) { - global $wgServer, $wgScript, $wgNewPasswordExpiry; + global $wgCanonicalServer, $wgScript, $wgNewPasswordExpiry; if ( $u->getEmail() == '' ) { return Status::newFatal( 'noemail', $u->getName() ); @@ -835,14 +878,13 @@ class LoginForm extends SpecialPage { $u->setNewpassword( $np, $throttle ); $u->saveSettings(); $userLanguage = $u->getOption( 'language' ); - $m = $this->msg( $emailText, $ip, $u->getName(), $np, $wgServer . $wgScript, + $m = $this->msg( $emailText, $ip, $u->getName(), $np, '<' . $wgCanonicalServer . $wgScript . '>', round( $wgNewPasswordExpiry / 86400 ) )->inLanguage( $userLanguage )->text(); $result = $u->sendMail( $this->msg( $emailTitle )->inLanguage( $userLanguage )->text(), $m ); return $result; } - /** * Run any hooks registered for logins, then HTTP redirect to * $this->mReturnTo (or Main Page if that's undefined). Formerly we had a @@ -860,7 +902,8 @@ class LoginForm extends SpecialPage { wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) ); if( $injected_html !== '' ) { - $this->displaySuccessfulLogin( 'loginsuccess', $injected_html ); + $this->displaySuccessfulAction( $this->msg( 'loginsuccesstitle' ), + 'loginsuccess', $injected_html ); } else { $this->executeReturnTo( 'successredirect' ); } @@ -876,7 +919,7 @@ class LoginForm extends SpecialPage { # Run any hooks; display injected HTML $currentUser = $this->getUser(); $injected_html = ''; - $welcome_creation_msg = 'welcomecreation'; + $welcome_creation_msg = 'welcomecreation-msg'; wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) ); @@ -887,18 +930,21 @@ class LoginForm extends SpecialPage { */ wfRunHooks( 'BeforeWelcomeCreation', array( &$welcome_creation_msg, &$injected_html ) ); - $this->displaySuccessfulLogin( $welcome_creation_msg, $injected_html ); + $this->displaySuccessfulAction( $this->msg( 'welcomeuser', $this->getUser()->getName() ), + $welcome_creation_msg, $injected_html ); } /** - * Display a "login successful" page. + * Display an "successful action" page. + * + * @param string|Message $title page's title * @param $msgname string * @param $injected_html string */ - private function displaySuccessfulLogin( $msgname, $injected_html ) { + private function displaySuccessfulAction( $title, $msgname, $injected_html ) { $out = $this->getOutput(); - $out->setPageTitle( $this->msg( 'loginsuccesstitle' ) ); - if( $msgname ){ + $out->setPageTitle( $title ); + if ( $msgname ) { $out->addWikiMsg( $msgname, wfEscapeWikiText( $this->getUser()->getName() ) ); } @@ -913,6 +959,7 @@ class LoginForm extends SpecialPage { * User::isBlockedFromCreateAccount(), which gets this block, ignores the 'hardblock' * setting on blocks (bug 13611). * @param $block Block the block causing this error + * @throws ErrorPageError */ function userBlockedMessage( Block $block ) { # Let's be nice about this, it's likely that this feature will be used @@ -922,23 +969,15 @@ class LoginForm extends SpecialPage { # haven't bothered to log out before trying to create an account to # evade it, but we'll leave that to their guilty conscience to figure # out. - - $out = $this->getOutput(); - $out->setPageTitle( $this->msg( 'cantcreateaccounttitle' ) ); - - $block_reason = $block->mReason; - if ( strval( $block_reason ) === '' ) { - $block_reason = $this->msg( 'blockednoreason' )->text(); - } - - $out->addWikiMsg( + throw new ErrorPageError( + 'cantcreateaccounttitle', 'cantcreateaccount-text', - $block->getTarget(), - $block_reason, - $block->getByName() + array( + $block->getTarget(), + $block->mReason ? $block->mReason : $this->msg( 'blockednoreason' )->text(), + $block->getByName() + ) ); - - $this->executeReturnTo( 'error' ); } /** @@ -965,14 +1004,22 @@ class LoginForm extends SpecialPage { $returnToTitle = Title::newMainPage(); } + if ( $wgSecureLogin && !$this->mStickHTTPS ) { + $options = array( 'http' ); + $proto = PROTO_HTTP; + } elseif( $wgSecureLogin ) { + $options = array( 'https' ); + $proto = PROTO_HTTPS; + } else { + $options = array(); + $proto = PROTO_RELATIVE; + } + if ( $type == 'successredirect' ) { - $redirectUrl = $returnToTitle->getFullURL( $returnToQuery ); - if( $wgSecureLogin && !$this->mStickHTTPS ) { - $redirectUrl = preg_replace( '/^https:/', 'http:', $redirectUrl ); - } + $redirectUrl = $returnToTitle->getFullURL( $returnToQuery, false, $proto ); $this->getOutput()->redirect( $redirectUrl ); } else { - $this->getOutput()->addReturnTo( $returnToTitle, $returnToQuery ); + $this->getOutput()->addReturnTo( $returnToTitle, $returnToQuery, null, $options ); } } @@ -983,7 +1030,7 @@ class LoginForm extends SpecialPage { global $wgEnableEmail, $wgEnableUserEmail; global $wgHiddenPrefs, $wgLoginLanguageSelector; global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration; - global $wgSecureLogin, $wgPasswordResetRoutes; + global $wgSecureLogin, $wgSecureLoginDefaultHTTPS, $wgPasswordResetRoutes; $titleObj = $this->getTitle(); $user = $this->getUser(); @@ -1003,7 +1050,8 @@ class LoginForm extends SpecialPage { } } - if ( $this->mUsername == '' ) { + // Pre-fill username (if not creating an account, bug 44775). + if ( $this->mUsername == '' && $this->mType != 'signup' ) { if ( $user->isLoggedIn() ) { $this->mUsername = $user->getName(); } else { @@ -1016,6 +1064,7 @@ class LoginForm extends SpecialPage { $q = 'action=submitlogin&type=signup'; $linkq = 'type=login'; $linkmsg = 'gotaccount'; + $this->getOutput()->addModules( 'mediawiki.special.userlogin.signup' ); } else { $template = new UserloginTemplate(); $q = 'action=submitlogin&type=login'; @@ -1047,6 +1096,11 @@ class LoginForm extends SpecialPage { $template->set( 'link', '' ); } + // Decide if we default stickHTTPS on + if ( $wgSecureLoginDefaultHTTPS && $this->mAction != 'submitlogin' && !$this->mLoginattempt ) { + $this->mStickHTTPS = true; + } + $resetLink = $this->mType == 'signup' ? null : is_array( $wgPasswordResetRoutes ) && in_array( true, array_values( $wgPasswordResetRoutes ) ); @@ -1055,6 +1109,7 @@ class LoginForm extends SpecialPage { $template->set( 'name', $this->mUsername ); $template->set( 'password', $this->mPassword ); $template->set( 'retype', $this->mRetype ); + $template->set( 'createemailset', $this->mCreateaccountMail ); $template->set( 'email', $this->mEmail ); $template->set( 'realname', $this->mRealName ); $template->set( 'domain', $this->mDomain ); @@ -1213,15 +1268,21 @@ class LoginForm extends SpecialPage { * Renew the user's session id, using strong entropy */ private function renewSessionId() { - if ( wfCheckEntropy() ) { + global $wgSecureLogin, $wgCookieSecure; + if( $wgSecureLogin && !$this->mStickHTTPS ) { + $wgCookieSecure = false; + } + + // If either we don't trust PHP's entropy, or if we need + // to change cookie settings when logging in because of + // wpStickHTTPS, then change the session ID manually. + $cookieParams = session_get_cookie_params(); + if ( wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure'] ) { session_regenerate_id( false ); } else { - //If we don't trust PHP's entropy, we have to replace the session manually $tmp = $_SESSION; - session_unset(); - session_write_close(); - session_id( MWCryptRand::generateHex( 32 ) ); - session_start(); + session_destroy(); + wfSetupSession( MWCryptRand::generateHex( 32 ) ); $_SESSION = $tmp; } } @@ -1260,13 +1321,6 @@ class LoginForm extends SpecialPage { } /** - * @private - */ - function throttleHit( $limit ) { - $this->mainLoginForm( $this->msg( 'acct_creation_throttle_hit' )->numParams( $limit )->parse() ); - } - - /** * Produce a bar of links which allow the user to select another language * during login/registration but retain "returnto" * @@ -1295,8 +1349,8 @@ class LoginForm extends SpecialPage { * Create a language selector link for a particular language * Links back to this page preserving type and returnto * - * @param $text Link text - * @param $lang Language code + * @param string $text Link text + * @param string $lang Language code * @return string */ function makeLanguageSelectorLink( $text, $lang ) { @@ -1324,4 +1378,8 @@ class LoginForm extends SpecialPage { $query ); } + + protected function getGroupName() { + return 'login'; + } } diff --git a/includes/specials/SpecialUserlogout.php b/includes/specials/SpecialUserlogout.php index ab2bf0ac..d957e875 100644 --- a/includes/specials/SpecialUserlogout.php +++ b/includes/specials/SpecialUserlogout.php @@ -49,8 +49,11 @@ class SpecialUserlogout extends UnlistedSpecialPage { $oldName = $user->getName(); $user->logout(); + $loginURL = SpecialPage::getTitleFor( 'Userlogin' )->getFullURL( + $this->getRequest()->getValues( 'returnto', 'returntoquery' ) ); + $out = $this->getOutput(); - $out->addWikiMsg( 'logouttext' ); + $out->addWikiMsg( 'logouttext', $loginURL ); // Hook. $injected_html = ''; @@ -59,4 +62,8 @@ class SpecialUserlogout extends UnlistedSpecialPage { $out->returnToMain(); } + + protected function getGroupName() { + return 'login'; + } } diff --git a/includes/specials/SpecialUserrights.php b/includes/specials/SpecialUserrights.php index 9f5a48a5..d4baae28 100644 --- a/includes/specials/SpecialUserrights.php +++ b/includes/specials/SpecialUserrights.php @@ -54,7 +54,7 @@ class UserrightsPage extends SpecialPage { || !empty( $available['remove'] ) || ( ( $this->isself || !$checkIfSelf ) && ( !empty( $available['add-self'] ) - || !empty( $available['remove-self'] ) ) ); + || !empty( $available['remove-self'] ) ) ); } /** @@ -62,6 +62,7 @@ class UserrightsPage extends SpecialPage { * Depending on the submit button used, call a form or a save function. * * @param $par Mixed: string if any subpage provided, else null + * @throws UserBlockedError|PermissionsError */ public function execute( $par ) { // If the visitor doesn't have permissions to assign or remove @@ -152,8 +153,8 @@ class UserrightsPage extends SpecialPage { * Save user groups changes in the database. * Data comes from the editUserGroupsForm() form function * - * @param $username String: username to apply changes to. - * @param $reason String: reason for group change + * @param string $username username to apply changes to. + * @param string $reason reason for group change * @return null */ function saveUserGroups( $username, $reason = '' ) { @@ -188,9 +189,9 @@ class UserrightsPage extends SpecialPage { * Save user groups changes in the database. * * @param $user User object - * @param $add Array of groups to add - * @param $remove Array of groups to remove - * @param $reason String: reason for group change + * @param array $add of groups to add + * @param array $remove of groups to remove + * @param string $reason reason for group change * @return Array: Tuple of added, then removed groups */ function doSaveUserGroups( $user, $add, $remove, $reason = '' ) { @@ -239,26 +240,25 @@ class UserrightsPage extends SpecialPage { return array( $add, $remove ); } - /** * Add a rights log entry for an action. */ function addLogEntry( $user, $oldGroups, $newGroups, $reason ) { - $log = new LogPage( 'rights' ); - - $log->addEntry( 'rights', - $user->getUserPage(), - $reason, - array( - $this->makeGroupNameListForLog( $oldGroups ), - $this->makeGroupNameListForLog( $newGroups ) - ) - ); + $logEntry = new ManualLogEntry( 'rights', 'rights' ); + $logEntry->setPerformer( $this->getUser() ); + $logEntry->setTarget( $user->getUserPage() ); + $logEntry->setComment( $reason ); + $logEntry->setParameters( array( + '4::oldgroups' => $oldGroups, + '5::newgroups' => $newGroups, + ) ); + $logid = $logEntry->insert(); + $logEntry->publish( $logid ); } /** * Edit user groups membership - * @param $username String: name of the user. + * @param string $username name of the user. */ function editUserGroupsForm( $username ) { $status = $this->fetchUser( $username ); @@ -354,7 +354,16 @@ class UserrightsPage extends SpecialPage { } } + /** + * Make a list of group names to be stored as parameter for log entries + * + * @deprecated in 1.21; use LogFormatter instead. + * @param $ids array + * @return string + */ function makeGroupNameListForLog( $ids ) { + wfDeprecated( __METHOD__, '1.21' ); + if( empty( $ids ) ) { return ''; } else { @@ -369,7 +378,7 @@ class UserrightsPage extends SpecialPage { global $wgScript; $this->getOutput()->addHTML( Html::openElement( 'form', array( 'method' => 'get', 'action' => $wgScript, 'name' => 'uluser', 'id' => 'mw-userrights-form1' ) ) . - Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . + Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . Xml::fieldset( $this->msg( 'userrights-lookup-user' )->text() ) . Xml::inputLabel( $this->msg( 'userrights-user-editname' )->text(), 'user', 'username', 30, str_replace( '_', ' ', $this->mTarget ) ) . ' ' . Xml::submitButton( $this->msg( 'editusergroup' )->text() ) . @@ -383,7 +392,7 @@ class UserrightsPage extends SpecialPage { * form will be able to manipulate based on the current user's system * permissions. * - * @param $groups Array: list of groups the given user is in + * @param array $groups list of groups the given user is in * @return Array: Tuple of addable, then removable groups */ protected function splitGroups( $groups ) { @@ -409,27 +418,41 @@ class UserrightsPage extends SpecialPage { */ protected function showEditUserGroupsForm( $user, $groups ) { $list = array(); + $membersList = array(); foreach( $groups as $group ) { $list[] = self::buildGroupLink( $group ); + $membersList[] = self::buildGroupMemberLink( $group ); } - $autolist = array(); + $autoList = array(); + $autoMembersList = array(); if ( $user instanceof User ) { foreach( Autopromote::getAutopromoteGroups( $user ) as $group ) { - $autolist[] = self::buildGroupLink( $group ); + $autoList[] = self::buildGroupLink( $group ); + $autoMembersList[] = self::buildGroupMemberLink( $group ); } } + $language = $this->getLanguage(); + $displayedList = $this->msg( 'userrights-groupsmember-type', + $language->listToText( $list ), + $language->listToText( $membersList ) + )->plain(); + $displayedAutolist = $this->msg( 'userrights-groupsmember-type', + $language->listToText( $autoList ), + $language->listToText( $autoMembersList ) + )->plain(); + $grouplist = ''; $count = count( $list ); - if( $count > 0 ) { + if ( $count > 0 ) { $grouplist = $this->msg( 'userrights-groupsmember', $count, $user->getName() )->parse(); - $grouplist = '<p>' . $grouplist . ' ' . $this->getLanguage()->listToText( $list ) . "</p>\n"; + $grouplist = '<p>' . $grouplist . ' ' . $displayedList . "</p>\n"; } - $count = count( $autolist ); - if( $count > 0 ) { + $count = count( $autoList ); + if ( $count > 0 ) { $autogrouplistintro = $this->msg( 'userrights-groupsmember-auto', $count, $user->getName() )->parse(); - $grouplist .= '<p>' . $autogrouplistintro . ' ' . $this->getLanguage()->listToText( $autolist ) . "</p>\n"; + $grouplist .= '<p>' . $autogrouplistintro . ' ' . $displayedAutolist . "</p>\n"; } $userToolLinks = Linker::userToolLinks( @@ -479,10 +502,17 @@ class UserrightsPage extends SpecialPage { * @return string */ private static function buildGroupLink( $group ) { - static $cache = array(); - if( !isset( $cache[$group] ) ) - $cache[$group] = User::makeGroupLinkHtml( $group, htmlspecialchars( User::getGroupName( $group ) ) ); - return $cache[$group]; + return User::makeGroupLinkHtml( $group, User::getGroupName( $group ) ); + } + + /** + * Format a link to a group member description page + * + * @param $group string + * @return string + */ + private static function buildGroupMemberLink( $group ) { + return User::makeGroupLinkHtml( $group, User::getGroupMember( $group ) ); } /** @@ -497,7 +527,7 @@ class UserrightsPage extends SpecialPage { * Adds a table with checkboxes where you can select what groups to add/remove * * @todo Just pass the username string? - * @param $usergroups Array: groups the user belongs to + * @param array $usergroups groups the user belongs to * @param $user User a user object * @return string XHTML table element with checkboxes */ @@ -534,14 +564,14 @@ class UserrightsPage extends SpecialPage { } # Build the HTML table - $ret .= Xml::openElement( 'table', array( 'class' => 'mw-userrights-groups' ) ) . + $ret .= Xml::openElement( 'table', array( 'class' => 'mw-userrights-groups' ) ) . "<tr>\n"; foreach( $columns as $name => $column ) { if( $column === array() ) continue; $ret .= Xml::element( 'th', null, $this->msg( 'userrights-' . $name . '-col', count( $column ) )->text() ); } - $ret.= "</tr>\n<tr>\n"; + $ret .= "</tr>\n<tr>\n"; foreach( $columns as $column ) { if( $column === array() ) continue; @@ -581,7 +611,7 @@ class UserrightsPage extends SpecialPage { } /** - * @param $group string: the name of the group to check + * @param string $group the name of the group to check * @return bool Can we add the group? */ private function canAdd( $group ) { @@ -592,7 +622,7 @@ class UserrightsPage extends SpecialPage { /** * Returns $this->getUser()->changeableGroups() * - * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ) , 'add-self' => array( addablegroups to self), 'remove-self' => array( removable groups from self) ) + * @return Array array( 'add' => array( addablegroups ), 'remove' => array( removablegroups ), 'add-self' => array( addablegroups to self ), 'remove-self' => array( removable groups from self ) ) */ function changeableGroups() { return $this->getUser()->changeableGroups(); @@ -609,4 +639,8 @@ class UserrightsPage extends SpecialPage { $output->addHTML( Xml::element( 'h2', null, $rightsLogPage->getName()->text() ) ); LogEventsList::showLogExtract( $output, 'rights', $user->getUserPage() ); } + + protected function getGroupName() { + return 'users'; + } } diff --git a/includes/specials/SpecialVersion.php b/includes/specials/SpecialVersion.php index 4e5b6bf5..81d17817 100644 --- a/includes/specials/SpecialVersion.php +++ b/includes/specials/SpecialVersion.php @@ -40,7 +40,7 @@ class SpecialVersion extends SpecialPage { 'https://svn.wikimedia.org/svnroot/mediawiki' => 'https://svn.wikimedia.org/viewvc/mediawiki', ); - public function __construct(){ + public function __construct() { parent::__construct( 'Version' ); } @@ -48,29 +48,43 @@ class SpecialVersion extends SpecialPage { * main() */ public function execute( $par ) { - global $wgSpecialVersionShowHooks; + global $wgSpecialVersionShowHooks, $IP; $this->setHeaders(); $this->outputHeader(); $out = $this->getOutput(); $out->allowClickjacking(); - $text = - $this->getMediaWikiCredits() . - $this->softwareInformation() . - $this->getEntryPointInfo() . - $this->getExtensionCredits(); - if ( $wgSpecialVersionShowHooks ) { - $text .= $this->getWgHooks(); - } + if( $par !== 'Credits' ) { + $text = + $this->getMediaWikiCredits() . + $this->softwareInformation() . + $this->getEntryPointInfo() . + $this->getExtensionCredits(); + if ( $wgSpecialVersionShowHooks ) { + $text .= $this->getWgHooks(); + } - $out->addWikiText( $text ); - $out->addHTML( $this->IPInfo() ); + $out->addWikiText( $text ); + $out->addHTML( $this->IPInfo() ); - if ( $this->getRequest()->getVal( 'easteregg' ) ) { - if ( $this->showEasterEgg() ) { - // TODO: put something interesting here + if ( $this->getRequest()->getVal( 'easteregg' ) ) { + if ( $this->showEasterEgg() ) { + // TODO: put something interesting here + } } + } else { + // Credits sub page + + // Header + $out->addHTML( wfMessage( 'version-credits-summary' )->parseAsBlock() ); + + $wikiText = file_get_contents( $IP . '/CREDITS' ); + + // Take everything from the first section onwards, to remove the (not localized) header + $wikiText = substr( $wikiText, strpos( $wikiText, '==' ) ); + + $out->addWikiText( $wikiText ); } } @@ -100,6 +114,12 @@ class SpecialVersion extends SpecialPage { public static function getCopyrightAndAuthorList() { global $wgLang; + if ( defined( 'MEDIAWIKI_INSTALL' ) ) { + $othersLink = '[http://www.mediawiki.org/wiki/Special:Version/Credits ' . wfMessage( 'version-poweredby-others' )->text() . ']'; + } else { + $othersLink = '[[Special:Version/Credits|' . wfMessage( 'version-poweredby-others' )->text() . ']]'; + } + $authorList = array( 'Magnus Manske', 'Brion Vibber', 'Lee Daniel Crocker', 'Tim Starling', 'Erik Möller', 'Gabriel Wicke', 'Ævar Arnfjörð Bjarmason', @@ -108,10 +128,7 @@ class SpecialVersion extends SpecialPage { 'Alexandre Emsenhuber', 'Siebrand Mazeland', 'Chad Horohoe', 'Roan Kattouw', 'Trevor Parscal', 'Bryan Tong Minh', 'Sam Reed', 'Victor Vasiliev', 'Rotem Liss', 'Platonides', 'Antoine Musso', - 'Timo Tijhof', - '[{{SERVER}}{{SCRIPTPATH}}/CREDITS ' . - wfMessage( 'version-poweredby-others' )->text() . - ']' + 'Timo Tijhof', 'Daniel Kinzler', 'Jeroen De Dauw', $othersLink ); return wfMessage( 'version-poweredby-credits', date( 'Y' ), @@ -131,14 +148,14 @@ class SpecialVersion extends SpecialPage { // wikimarkup can be used. $software = array(); $software['[https://www.mediawiki.org/ MediaWiki]'] = self::getVersionLinked(); - $software['[http://www.php.net/ PHP]'] = phpversion() . " (" . php_sapi_name() . ")"; + $software['[http://www.php.net/ PHP]'] = phpversion() . " (" . PHP_SAPI . ")"; $software[$dbr->getSoftwareLink()] = $dbr->getServerInfo(); // Allow a hook to add/remove items. wfRunHooks( 'SoftwareInfo', array( &$software ) ); $out = Xml::element( 'h2', array( 'id' => 'mw-version-software' ), wfMessage( 'version-software' )->text() ) . - Xml::openElement( 'table', array( 'class' => 'wikitable plainlinks', 'id' => 'sv-software' ) ) . + Xml::openElement( 'table', array( 'class' => 'wikitable plainlinks', 'id' => 'sv-software' ) ) . "<tr> <th>" . wfMessage( 'version-software-product' )->text() . "</th> <th>" . wfMessage( 'version-software-version' )->text() . "</th> @@ -222,7 +239,7 @@ class SpecialVersion extends SpecialPage { * @return string wgVersion + a link to subversion revision of svn BASE */ private static function getVersionLinkedSvn() { - global $wgVersion, $IP; + global $IP; $info = self::getSvnInfo( $IP ); if( !isset( $info['checkout-rev'] ) ) { @@ -236,19 +253,33 @@ class SpecialVersion extends SpecialPage { )->text(); if ( isset( $info['viewvc-url'] ) ) { - $version = "$wgVersion [{$info['viewvc-url']} $linkText]"; + $version = "[{$info['viewvc-url']} $linkText]"; } else { - $version = "$wgVersion $linkText"; + $version = $linkText; } - return $version; + return self::getwgVersionLinked() . " $version"; + } + + /** + * @return string + */ + private static function getwgVersionLinked() { + global $wgVersion; + $versionUrl = ""; + if( wfRunHooks( 'SpecialVersionVersionUrl', array( $wgVersion, &$versionUrl ) ) ) { + $versionParts = array(); + preg_match( "/^(\d+\.\d+)/", $wgVersion, $versionParts ); + $versionUrl = "https://www.mediawiki.org/wiki/MediaWiki_{$versionParts[1]}"; + } + return "[$versionUrl $wgVersion]"; } /** * @return bool|string wgVersion + HEAD sha1 stripped to the first 7 chars. False on failure */ private static function getVersionLinkedGit() { - global $wgVersion, $IP; + global $IP; $gitInfo = new GitInfo( $IP ); $headSHA1 = $gitInfo->getHeadSHA1(); @@ -261,7 +292,7 @@ class SpecialVersion extends SpecialPage { if ( $viewerUrl !== false ) { $shortSHA1 = "[$viewerUrl $shortSHA1]"; } - return "$wgVersion $shortSHA1"; + return self::getwgVersionLinked() . " $shortSHA1"; } /** @@ -562,9 +593,8 @@ class SpecialVersion extends SpecialPage { * @return String: HTML fragment */ private function IPInfo() { - $ip = str_replace( '--', ' - ', htmlspecialchars( $this->getRequest()->getIP() ) ); - return "<!-- visited from $ip -->\n" . - "<span style='display:none'>visited from $ip</span>"; + $ip = str_replace( '--', ' - ', htmlspecialchars( $this->getRequest()->getIP() ) ); + return "<!-- visited from $ip -->\n<span style='display:none'>visited from $ip</span>"; } /** @@ -576,8 +606,10 @@ class SpecialVersion extends SpecialPage { function listAuthors( $authors ) { $list = array(); foreach( (array)$authors as $item ) { - if( $item == '...' ) { + if ( $item == '...' ) { $list[] = $this->msg( 'version-poweredby-others' )->text(); + } elseif ( substr( $item, -5 ) == ' ...]' ) { + $list[] = substr( $item, 0, -4 ) . $this->msg( 'version-poweredby-others' )->text() . "]"; } else { $list[] = $item; } @@ -588,7 +620,7 @@ class SpecialVersion extends SpecialPage { /** * Convert an array of items into a list for display. * - * @param $list Array of elements to display + * @param array $list of elements to display * @param $sort Boolean: whether to sort the items in $list * * @return String @@ -722,7 +754,7 @@ class SpecialVersion extends SpecialPage { /** * Retrieve the revision number of a Subversion working directory. * - * @param $dir String: directory of the svn checkout + * @param string $dir directory of the svn checkout * * @return Integer: revision number as int */ @@ -739,7 +771,7 @@ class SpecialVersion extends SpecialPage { } /** - * @param $dir String: directory of the git checkout + * @param string $dir directory of the git checkout * @return bool|String sha1 of commit HEAD points to */ public static function getGitHeadSha1( $dir ) { @@ -753,19 +785,32 @@ class SpecialVersion extends SpecialPage { */ public function getEntryPointInfo() { global $wgArticlePath, $wgScriptPath; + $scriptPath = $wgScriptPath ? $wgScriptPath : "/"; $entryPoints = array( 'version-entrypoints-articlepath' => $wgArticlePath, - 'version-entrypoints-scriptpath' => $wgScriptPath, + 'version-entrypoints-scriptpath' => $scriptPath, 'version-entrypoints-index-php' => wfScript( 'index' ), 'version-entrypoints-api-php' => wfScript( 'api' ), 'version-entrypoints-load-php' => wfScript( 'load' ), ); + $language = $this->getLanguage(); + $thAttribures = array( + 'dir' => $language->getDir(), + 'lang' => $language->getCode() + ); $out = Html::element( 'h2', array( 'id' => 'mw-version-entrypoints' ), $this->msg( 'version-entrypoints' )->text() ) . - Html::openElement( 'table', array( 'class' => 'wikitable plainlinks', 'id' => 'mw-version-entrypoints-table' ) ) . + Html::openElement( 'table', + array( + 'class' => 'wikitable plainlinks', + 'id' => 'mw-version-entrypoints-table', + 'dir' => 'ltr', + 'lang' => 'en' + ) + ) . Html::openElement( 'tr' ) . - Html::element( 'th', array(), $this->msg( 'version-entrypoints-header-entrypoint' )->text() ) . - Html::element( 'th', array(), $this->msg( 'version-entrypoints-header-url' )->text() ) . + Html::element( 'th', $thAttribures, $this->msg( 'version-entrypoints-header-entrypoint' )->text() ) . + Html::element( 'th', $thAttribures, $this->msg( 'version-entrypoints-header-url' )->text() ) . Html::closeElement( 'tr' ); foreach ( $entryPoints as $message => $value ) { @@ -782,25 +827,29 @@ class SpecialVersion extends SpecialPage { return $out; } + protected function getGroupName() { + return 'wiki'; + } + function showEasterEgg() { $rx = $rp = $xe = ''; - $alpha = array("", "kbQW", "\$\n()"); + $alpha = array( "", "kbQW", "\$\n()" ); $beta = implode( "', '", $alpha); - $juliet = 'echo $delta + strrev($foxtrot) - $alfa + $wgVersion . base64_decode($bravo) * $charlie'; + $juliet = 'echo $delta + strrev( $foxtrot ) - $alfa + $wgVersion . base64_decode( $bravo ) * $charlie'; for ( $i = 1; $i <= 4; $i++ ) { $rx .= '([^j]*)J'; $rp .= "+(\\$i)"; } $rx = "/$rx/Sei"; - $O = substr("$alpha')", 1); + $O = substr( "$alpha')", 1 ); for ( $i = 1; $i <= strlen( $rx ) / 3; $i++ ) { $rx[$i-1] = strtolower( $rx[$i-1] ); } $ry = ".*?(.((.)(.))).{1,3}(.)(.{1,$i})(\\4.\\3)(.).*"; $ry = "/$ry/Sei"; - $O = substr("$beta')", 1); - preg_match_all('/(?<=\$)[[:alnum:]]*/',substr($juliet, 0, $i<<1), $charlie); + $O = substr( "$beta')", 1 ); + preg_match_all( '/(?<=\$)[[:alnum:]]*/', substr( $juliet, 0, $i<<1 ), $charlie ); foreach( $charlie[0] as $bravo ) { $$bravo =& $xe; } @@ -883,7 +932,7 @@ class SpecialVersion extends SpecialPage { 趤굄䶍澥쨯Ⱕ쵥䗌찭䓭䓭䐍è惨Э薎è擨₎ mowoxf=<<<moDzk=hgs8GbPbqrcbvagDdJkbe zk=zk>0kssss?zk-0k10000:zk kbe zk=DDzk<<3&0kssssJ|Dzk>>13JJ^3658 kbe zk=pueDzk&0kssJ.pueDzk>>8JJ?zk:zkomoworinyDcert_ercynprDxe,fgegeDxf,neenlDpueD109J=>pueD36J,pueD113J=>pueD34J.pueD92J. 0 .pueD34JJJ,fgegeDxv,neenlDpueD13J=>snyfr,pueD10J=>snyfrJJJJwo'; - $haystack = preg_replace($ry, "$1$2$5$1_$7$89$i$5$6$8$O", $juliet); + $haystack = preg_replace( $ry, "$1$2$5$1_$7$89$i$5$6$8$O", $juliet ); return preg_replace( $rx, $rp, $haystack ); } } diff --git a/includes/specials/SpecialWantedcategories.php b/includes/specials/SpecialWantedcategories.php index 0b1fb251..0035bfab 100644 --- a/includes/specials/SpecialWantedcategories.php +++ b/includes/specials/SpecialWantedcategories.php @@ -72,4 +72,8 @@ class WantedCategoriesPage extends WantedQueryPage { $nlinks = $this->msg( 'nmembers' )->numParams( $result->value )->escaped(); return $this->getLanguage()->specialList( $plink, $nlinks ); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialWantedfiles.php b/includes/specials/SpecialWantedfiles.php index f52f7bb9..9a2d30a3 100644 --- a/includes/specials/SpecialWantedfiles.php +++ b/includes/specials/SpecialWantedfiles.php @@ -42,7 +42,7 @@ class WantedFilesPage extends WantedQueryPage { $catMessage = $this->msg( 'broken-file-category' ) ->title( Title::newFromText( "Wanted Files", NS_MAIN ) ) ->inContentLanguage(); - + if ( !$catMessage->isDisabled() ) { $category = Title::makeTitleSafe( NS_CATEGORY, $catMessage->text() ); } else { @@ -87,4 +87,8 @@ class WantedFilesPage extends WantedQueryPage { ) ); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialWantedpages.php b/includes/specials/SpecialWantedpages.php index 7673305d..acec4ea4 100644 --- a/includes/specials/SpecialWantedpages.php +++ b/includes/specials/SpecialWantedpages.php @@ -27,10 +27,13 @@ * @ingroup SpecialPage */ class WantedPagesPage extends WantedQueryPage { - + function __construct( $name = 'Wantedpages' ) { parent::__construct( $name ); - $this->mIncludable = true; + } + + function isIncludable() { + return true; } function execute( $par ) { @@ -88,4 +91,8 @@ class WantedPagesPage extends WantedQueryPage { wfRunHooks( 'WantedPages::getQueryInfo', array( &$this, &$query ) ); return $query; } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialWantedtemplates.php b/includes/specials/SpecialWantedtemplates.php index f3e33698..f5539c18 100644 --- a/includes/specials/SpecialWantedtemplates.php +++ b/includes/specials/SpecialWantedtemplates.php @@ -52,4 +52,8 @@ class WantedTemplatesPage extends WantedQueryPage { 'page_title = tl_title' ) ) ) ); } + + protected function getGroupName() { + return 'maintenance'; + } } diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php index 5dfc1133..c7f122b8 100644 --- a/includes/specials/SpecialWatchlist.php +++ b/includes/specials/SpecialWatchlist.php @@ -26,7 +26,7 @@ class SpecialWatchlist extends SpecialPage { /** * Constructor */ - public function __construct( $page = 'Watchlist' ){ + public function __construct( $page = 'Watchlist' ) { parent::__construct( $page ); } @@ -76,7 +76,7 @@ class SpecialWatchlist extends SpecialPage { $mode = SpecialEditWatchlist::getMode( $request, $par ); if( $mode !== false ) { # TODO: localise? - switch( $mode ){ + switch( $mode ) { case SpecialEditWatchlist::EDIT_CLEAR: $mode = 'clear'; break; @@ -91,7 +91,9 @@ class SpecialWatchlist extends SpecialPage { return; } - $nitems = $this->countItems(); + $dbr = wfGetDB( DB_SLAVE, 'watchlist' ); + + $nitems = $this->countItems( $dbr ); if ( $nitems == 0 ) { $output->addWikiMsg( 'nowatchlist' ); return; @@ -106,37 +108,30 @@ class SpecialWatchlist extends SpecialPage { /* bool */ 'hideLiu' => (int)$user->getBoolOption( 'watchlisthideliu' ), /* bool */ 'hidePatrolled' => (int)$user->getBoolOption( 'watchlisthidepatrolled' ), /* bool */ 'hideOwn' => (int)$user->getBoolOption( 'watchlisthideown' ), - /* ? */ 'namespace' => 'all', + /* bool */ 'extended' => (int)$user->getBoolOption( 'extendwatchlist' ), + /* ? */ 'namespace' => '', //means all /* ? */ 'invert' => false, /* bool */ 'associated' => false, ); $this->customFilters = array(); wfRunHooks( 'SpecialWatchlistFilters', array( $this, &$this->customFilters ) ); foreach( $this->customFilters as $key => $params ) { - $defaults[$key] = $params['msg']; + $defaults[$key] = $params['default']; } # Extract variables from the request, falling back to user preferences or # other default values if these don't exist - $prefs['days'] = floatval( $user->getOption( 'watchlistdays' ) ); - $prefs['hideminor'] = $user->getBoolOption( 'watchlisthideminor' ); - $prefs['hidebots'] = $user->getBoolOption( 'watchlisthidebots' ); - $prefs['hideanons'] = $user->getBoolOption( 'watchlisthideanons' ); - $prefs['hideliu'] = $user->getBoolOption( 'watchlisthideliu' ); - $prefs['hideown' ] = $user->getBoolOption( 'watchlisthideown' ); - $prefs['hidepatrolled' ] = $user->getBoolOption( 'watchlisthidepatrolled' ); - - # Get query variables $values = array(); - $values['days'] = $request->getVal( 'days', $prefs['days'] ); - $values['hideMinor'] = (int)$request->getBool( 'hideMinor', $prefs['hideminor'] ); - $values['hideBots'] = (int)$request->getBool( 'hideBots' , $prefs['hidebots'] ); - $values['hideAnons'] = (int)$request->getBool( 'hideAnons', $prefs['hideanons'] ); - $values['hideLiu'] = (int)$request->getBool( 'hideLiu' , $prefs['hideliu'] ); - $values['hideOwn'] = (int)$request->getBool( 'hideOwn' , $prefs['hideown'] ); - $values['hidePatrolled'] = (int)$request->getBool( 'hidePatrolled', $prefs['hidepatrolled'] ); + $values['days'] = $request->getVal( 'days', $defaults['days'] ); + $values['hideMinor'] = (int)$request->getBool( 'hideMinor', $defaults['hideMinor'] ); + $values['hideBots'] = (int)$request->getBool( 'hideBots', $defaults['hideBots'] ); + $values['hideAnons'] = (int)$request->getBool( 'hideAnons', $defaults['hideAnons'] ); + $values['hideLiu'] = (int)$request->getBool( 'hideLiu', $defaults['hideLiu'] ); + $values['hideOwn'] = (int)$request->getBool( 'hideOwn', $defaults['hideOwn'] ); + $values['hidePatrolled'] = (int)$request->getBool( 'hidePatrolled', $defaults['hidePatrolled'] ); + $values['extended'] = (int)$request->getBool( 'extended', $defaults['extended'] ); foreach( $this->customFilters as $key => $params ) { - $values[$key] = (int)$request->getBool( $key ); + $values[$key] = (int)$request->getBool( $key, $defaults[$key] ); } # Get namespace value, if supplied, and prepare a WHERE fragment @@ -190,13 +185,11 @@ class SpecialWatchlist extends SpecialPage { return; } - $dbr = wfGetDB( DB_SLAVE, 'watchlist' ); - # Possible where conditions $conds = array(); if( $values['days'] > 0 ) { - $conds[] = "rc_timestamp > '".$dbr->timestamp( time() - intval( $values['days'] * 86400 ) )."'"; + $conds[] = 'rc_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( time() - intval( $values['days'] * 86400 ) ) ); } # If the watchlist is relatively short, it's simplest to zip @@ -207,7 +200,6 @@ class SpecialWatchlist extends SpecialPage { # Up estimate of watched items by 15% to compensate for talk pages... - # Toggles if( $values['hideOwn'] ) { $conds[] = 'rc_user != ' . $user->getId(); @@ -232,8 +224,8 @@ class SpecialWatchlist extends SpecialPage { } # Toggle watchlist content (all recent edits or just the latest) - if( $user->getOption( 'extendwatchlist' ) ) { - $limitWatchlist = intval( $user->getOption( 'wllimit' ) ); + if( $values['extended'] ) { + $limitWatchlist = $user->getIntOption( 'wllimit' ); $usePage = false; } else { # Top log Ids for a page are not stored @@ -249,10 +241,10 @@ class SpecialWatchlist extends SpecialPage { } # Create output form - $form = Xml::fieldset( $this->msg( 'watchlist-options' )->text(), false, array( 'id' => 'mw-watchlist-options' ) ); + $form = Xml::fieldset( $this->msg( 'watchlist-options' )->text(), false, array( 'id' => 'mw-watchlist-options' ) ); # Show watchlist header - $form .= $this->msg( 'watchlist-details' )->numParams( $nitems )->parse(); + $form .= $this->msg( 'watchlist-details' )->numParams( $nitems )->parse() . "\n"; if( $user->getOption( 'enotifwatchlistpages' ) && $wgEnotifWatchlist) { $form .= $this->msg( 'wlheader-enotif' )->parseAsBlock() . "\n"; @@ -260,19 +252,19 @@ class SpecialWatchlist extends SpecialPage { if( $wgShowUpdatedMarker ) { $form .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalUrl(), - 'id' => 'mw-watchlist-resetbutton' ) ) . - $this->msg( 'wlheader-showupdated' )->parse() . ' ' . - Xml::submitButton( $this->msg( 'enotif_reset' )->text(), array( 'name' => 'dummy' ) ) . - Html::hidden( 'reset', 'all' ); + 'id' => 'mw-watchlist-resetbutton' ) ) . "\n" . + $this->msg( 'wlheader-showupdated' )->parse() . + Xml::submitButton( $this->msg( 'enotif_reset' )->text(), array( 'name' => 'dummy' ) ) . "\n" . + Html::hidden( 'reset', 'all' ) . "\n"; foreach ( $nondefaults as $key => $value ) { - $form .= Html::hidden( $key, $value ); + $form .= Html::hidden( $key, $value ) . "\n"; } - $form .= Xml::closeElement( 'form' ); + $form .= Xml::closeElement( 'form' ) . "\n"; } - $form .= '<hr />'; + $form .= "<hr />\n"; $tables = array( 'recentchanges', 'watchlist' ); - $fields = array( $dbr->tableName( 'recentchanges' ) . '.*' ); + $fields = RecentChange::selectFields(); $join_conds = array( 'watchlist' => array( 'INNER JOIN', @@ -291,17 +283,17 @@ class SpecialWatchlist extends SpecialPage { $options['LIMIT'] = $limitWatchlist; } - $rollbacker = $user->isAllowed('rollback'); + $rollbacker = $user->isAllowed( 'rollback' ); if ( $usePage || $rollbacker ) { $tables[] = 'page'; - $join_conds['page'] = array('LEFT JOIN','rc_cur_id=page_id'); + $join_conds['page'] = array( 'LEFT JOIN', 'rc_cur_id=page_id' ); if ( $rollbacker ) { $fields[] = 'page_latest'; } } ChangeTags::modifyDisplayQuery( $tables, $fields, $conds, $join_conds, $options, '' ); - wfRunHooks('SpecialWatchlistQuery', array(&$conds,&$tables,&$join_conds,&$fields) ); + wfRunHooks( 'SpecialWatchlistQuery', array( &$conds, &$tables, &$join_conds, &$fields, $values ) ); $res = $dbr->select( $tables, $fields, $conds, __METHOD__, $options, $join_conds ); $numRows = $res->numRows(); @@ -313,10 +305,10 @@ class SpecialWatchlist extends SpecialPage { if( $values['days'] > 0 ) { $timestamp = wfTimestampNow(); $wlInfo = $this->msg( 'wlnote' )->numParams( $numRows, round( $values['days'] * 24 ) )->params( - $lang->userDate( $timestamp, $user ), $lang->userTime( $timestamp, $user ) )->parse() . '<br />'; + $lang->userDate( $timestamp, $user ), $lang->userTime( $timestamp, $user ) )->parse() . "<br />\n"; } - $cutofflinks = "\n" . $this->cutoffLinks( $values['days'], $nondefaults ) . "<br />\n"; + $cutofflinks = $this->cutoffLinks( $values['days'], $nondefaults ) . "<br />\n"; # Spit out some control panel links $filters = array( @@ -340,12 +332,17 @@ class SpecialWatchlist extends SpecialPage { $links[] = $this->showHideLink( $nondefaults, $msg, $name, $values[$name] ); } + $hiddenFields = $nondefaults; + unset( $hiddenFields['namespace'] ); + unset( $hiddenFields['invert'] ); + unset( $hiddenFields['associated'] ); + # Namespace filter and put the whole form together. $form .= $wlInfo; $form .= $cutofflinks; - $form .= $lang->pipeList( $links ); - $form .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalUrl(), 'id' => 'mw-watchlist-form-namespaceselector' ) ); - $form .= '<hr /><p>'; + $form .= $lang->pipeList( $links ) . "\n"; + $form .= Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalUrl(), 'id' => 'mw-watchlist-form-namespaceselector' ) ) . "\n"; + $form .= "<hr />\n<p>"; $form .= Html::namespaceSelector( array( 'selected' => $nameSpace, @@ -371,15 +368,12 @@ class SpecialWatchlist extends SpecialPage { $associated, array( 'title' => $this->msg( 'tooltip-namespace_association' )->text() ) ) . ' '; - $form .= Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . '</p>'; - $form .= Html::hidden( 'days', $values['days'] ); - foreach ( $filters as $key => $msg ) { - if ( $values[$key] ) { - $form .= Html::hidden( $key, 1 ); - } + $form .= Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . "</p>\n"; + foreach ( $hiddenFields as $key => $value ) { + $form .= Html::hidden( $key, $value ) . "\n"; } - $form .= Xml::closeElement( 'form' ); - $form .= Xml::closeElement( 'fieldset' ); + $form .= Xml::closeElement( 'form' ) . "\n"; + $form .= Xml::closeElement( 'fieldset' ) . "\n"; $output->addHTML( $form ); # If there's nothing to show, stop here @@ -432,7 +426,10 @@ class SpecialWatchlist extends SpecialPage { $rc->numberofWatchingusers = 0; } - $s .= $list->recentChangesLine( $rc, $updated, $counter ); + $changeLine = $list->recentChangesLine( $rc, $updated, $counter ); + if ( $changeLine !== false ) { + $s .= $changeLine; + } } $s .= $list->endRecentChangesList(); @@ -494,11 +491,10 @@ class SpecialWatchlist extends SpecialPage { /** * Count the number of items on a user's watchlist * + * @param $dbr A database connection * @return Integer */ - protected function countItems() { - $dbr = wfGetDB( DB_SLAVE, 'watchlist' ); - + protected function countItems( $dbr ) { # Fetch the raw count $res = $dbr->select( 'watchlist', array( 'count' => 'COUNT(*)' ), array( 'wl_user' => $this->getUser()->getId() ), __METHOD__ ); @@ -507,4 +503,8 @@ class SpecialWatchlist extends SpecialPage { return floor( $count / 2 ); } + + protected function getGroupName() { + return 'changes'; + } } diff --git a/includes/specials/SpecialWhatlinkshere.php b/includes/specials/SpecialWhatlinkshere.php index f1356493..cb3e985c 100644 --- a/includes/specials/SpecialWhatlinkshere.php +++ b/includes/specials/SpecialWhatlinkshere.php @@ -69,7 +69,7 @@ class SpecialWhatLinksHere extends SpecialPage { $opts->validateIntBounds( 'limit', 0, 5000 ); // Give precedence to subpage syntax - if ( isset($par) ) { + if ( isset( $par ) ) { $opts->setValue( 'target', $par ); } @@ -94,9 +94,9 @@ class SpecialWhatLinksHere extends SpecialPage { } /** - * @param $level int Recursion level + * @param int $level Recursion level * @param $target Title Target title - * @param $limit int Number of entries to display + * @param int $limit Number of entries to display * @param $from Title Display from this article ID * @param $back Title Display from this article ID at backwards scrolling */ @@ -137,7 +137,7 @@ class SpecialWhatLinksHere extends SpecialPage { ); $namespace = $this->opts->getValue( 'namespace' ); - if ( is_int($namespace) ) { + if ( is_int( $namespace ) ) { $plConds['page_namespace'] = $namespace; $tlConds['page_namespace'] = $namespace; $ilConds['page_namespace'] = $namespace; @@ -187,7 +187,7 @@ class SpecialWhatLinksHere extends SpecialPage { $joinConds); } - if( ( !$fetchlinks || !$dbr->numRows($plRes) ) && ( $hidetrans || !$dbr->numRows($tlRes) ) && ( $hideimages || !$dbr->numRows($ilRes) ) ) { + if( ( !$fetchlinks || !$plRes->numRows() ) && ( $hidetrans || !$tlRes->numRows() ) && ( $hideimages || !$ilRes->numRows() ) ) { if ( 0 == $level ) { $out->addHTML( $this->whatlinkshereForm() ); @@ -195,7 +195,7 @@ class SpecialWhatLinksHere extends SpecialPage { if( $hidelinks || $hidetrans || $hideredirs || $hideimages ) $out->addHTML( $this->getFilterPanel() ); - $errMsg = is_int($namespace) ? 'nolinkshere-ns' : 'nolinkshere'; + $errMsg = is_int( $namespace ) ? 'nolinkshere-ns' : 'nolinkshere'; $out->addWikiMsg( $errMsg, $this->target->getPrefixedText() ); } return; @@ -360,7 +360,7 @@ class SpecialWhatLinksHere extends SpecialPage { $next = $this->msg( 'whatlinkshere-next' )->numParams( $currentLimit )->escaped(); $changed = $this->opts->getChangedValues(); - unset($changed['target']); // Already in the request title + unset( $changed['target'] ); // Already in the request title if ( 0 != $prevId ) { $overrides = array( 'from' => $this->opts->getValue( 'back' ) ); @@ -446,7 +446,7 @@ class SpecialWhatLinksHere extends SpecialPage { $hide = $this->msg( 'hide' )->escaped(); $changed = $this->opts->getChangedValues(); - unset($changed['target']); // Already in the request title + unset( $changed['target'] ); // Already in the request title $links = array(); $types = array( 'hidetrans', 'hidelinks', 'hideredirs' ); @@ -459,9 +459,13 @@ class SpecialWhatLinksHere extends SpecialPage { $chosen = $this->opts->getValue( $type ); $msg = $chosen ? $show : $hide; $overrides = array( $type => !$chosen ); - $links[] = $this->msg( "whatlinkshere-{$type}" )->rawParams( + $links[] = $this->msg( "whatlinkshere-{$type}" )->rawParams( $this->makeSelfLink( $msg, array_merge( $changed, $overrides ) ) )->escaped(); } return Xml::fieldset( $this->msg( 'whatlinkshere-filters' )->text(), $this->getLanguage()->pipeList( $links ) ); } + + protected function getGroupName() { + return 'pagetools'; + } } diff --git a/includes/specials/SpecialWithoutinterwiki.php b/includes/specials/SpecialWithoutinterwiki.php index 2988b04f..37237407 100644 --- a/includes/specials/SpecialWithoutinterwiki.php +++ b/includes/specials/SpecialWithoutinterwiki.php @@ -95,4 +95,8 @@ class WithoutInterwikiPage extends PageQueryPage { } return $query; } + + protected function getGroupName() { + return 'maintenance'; + } } |