summaryrefslogtreecommitdiff
path: root/includes/specials
diff options
context:
space:
mode:
Diffstat (limited to 'includes/specials')
-rw-r--r--includes/specials/SpecialActiveusers.php174
-rw-r--r--includes/specials/SpecialAllMessages.php14
-rw-r--r--includes/specials/SpecialAllPages.php5
-rw-r--r--includes/specials/SpecialApiHelp.php93
-rw-r--r--includes/specials/SpecialBlock.php54
-rw-r--r--includes/specials/SpecialBlockList.php17
-rw-r--r--includes/specials/SpecialBooksources.php23
-rw-r--r--includes/specials/SpecialCategories.php6
-rw-r--r--includes/specials/SpecialChangeEmail.php34
-rw-r--r--includes/specials/SpecialChangePassword.php14
-rw-r--r--includes/specials/SpecialConfirmemail.php3
-rw-r--r--includes/specials/SpecialContributions.php59
-rw-r--r--includes/specials/SpecialDeletedContributions.php113
-rw-r--r--includes/specials/SpecialDiff.php1
-rw-r--r--includes/specials/SpecialEditTags.php460
-rw-r--r--includes/specials/SpecialEditWatchlist.php42
-rw-r--r--includes/specials/SpecialEmailuser.php12
-rw-r--r--includes/specials/SpecialExpandTemplates.php5
-rw-r--r--includes/specials/SpecialExport.php9
-rw-r--r--includes/specials/SpecialFileDuplicateSearch.php52
-rw-r--r--includes/specials/SpecialFilepath.php1
-rw-r--r--includes/specials/SpecialImport.php43
-rw-r--r--includes/specials/SpecialJavaScriptTest.php33
-rw-r--r--includes/specials/SpecialLinkSearch.php52
-rw-r--r--includes/specials/SpecialListDuplicatedFiles.php2
-rw-r--r--includes/specials/SpecialListfiles.php12
-rw-r--r--includes/specials/SpecialListgrouprights.php78
-rw-r--r--includes/specials/SpecialListredirects.php2
-rw-r--r--includes/specials/SpecialListusers.php71
-rw-r--r--includes/specials/SpecialLog.php115
-rw-r--r--includes/specials/SpecialLonelypages.php2
-rw-r--r--includes/specials/SpecialMediaStatistics.php22
-rw-r--r--includes/specials/SpecialMergeHistory.php27
-rw-r--r--includes/specials/SpecialMostcategories.php2
-rw-r--r--includes/specials/SpecialMostimages.php2
-rw-r--r--includes/specials/SpecialMostinterwikis.php2
-rw-r--r--includes/specials/SpecialMostlinked.php2
-rw-r--r--includes/specials/SpecialMostlinkedcategories.php2
-rw-r--r--includes/specials/SpecialMostlinkedtemplates.php2
-rw-r--r--includes/specials/SpecialMovepage.php226
-rw-r--r--includes/specials/SpecialMyLanguage.php5
-rw-r--r--includes/specials/SpecialNewimages.php14
-rw-r--r--includes/specials/SpecialNewpages.php135
-rw-r--r--includes/specials/SpecialPageLanguage.php8
-rw-r--r--includes/specials/SpecialPagesWithProp.php49
-rw-r--r--includes/specials/SpecialPasswordReset.php15
-rw-r--r--includes/specials/SpecialPopularpages.php89
-rw-r--r--includes/specials/SpecialPreferences.php2
-rw-r--r--includes/specials/SpecialPrefixindex.php5
-rw-r--r--includes/specials/SpecialProtectedpages.php17
-rw-r--r--includes/specials/SpecialProtectedtitles.php14
-rw-r--r--includes/specials/SpecialRandomInCategory.php10
-rw-r--r--includes/specials/SpecialRandompage.php2
-rw-r--r--includes/specials/SpecialRecentchanges.php23
-rw-r--r--includes/specials/SpecialRedirect.php15
-rw-r--r--includes/specials/SpecialResetTokens.php2
-rw-r--r--includes/specials/SpecialRevisiondelete.php34
-rw-r--r--includes/specials/SpecialRunJobs.php11
-rw-r--r--includes/specials/SpecialSearch.php131
-rw-r--r--includes/specials/SpecialShortpages.php2
-rw-r--r--includes/specials/SpecialSpecialpages.php1
-rw-r--r--includes/specials/SpecialStatistics.php100
-rw-r--r--includes/specials/SpecialTags.php336
-rw-r--r--includes/specials/SpecialTrackingCategories.php142
-rw-r--r--includes/specials/SpecialUnblock.php11
-rw-r--r--includes/specials/SpecialUndelete.php32
-rw-r--r--includes/specials/SpecialUpload.php99
-rw-r--r--includes/specials/SpecialUploadStash.php14
-rw-r--r--includes/specials/SpecialUserlogin.php103
-rw-r--r--includes/specials/SpecialUserlogout.php2
-rw-r--r--includes/specials/SpecialUserrights.php65
-rw-r--r--includes/specials/SpecialVersion.php151
-rw-r--r--includes/specials/SpecialWantedcategories.php7
-rw-r--r--includes/specials/SpecialWantedfiles.php4
-rw-r--r--includes/specials/SpecialWantedpages.php7
-rw-r--r--includes/specials/SpecialWatchlist.php60
-rw-r--r--includes/specials/SpecialWhatlinkshere.php67
77 files changed, 2377 insertions, 1295 deletions
diff --git a/includes/specials/SpecialActiveusers.php b/includes/specials/SpecialActiveusers.php
index f983b452..5e2ee1c2 100644
--- a/includes/specials/SpecialActiveusers.php
+++ b/includes/specials/SpecialActiveusers.php
@@ -130,6 +130,8 @@ class ActiveUsersPager extends UsersPager {
}
function doBatchLookups() {
+ parent::doBatchLookups();
+
$uids = array();
foreach ( $this->mResult as $row ) {
$uids[] = $row->user_id;
@@ -178,7 +180,8 @@ class ActiveUsersPager extends UsersPager {
// Note: This is a different loop than for user rights,
// because we're reusing it to build the group links
// at the same time
- foreach ( $user->getGroups() as $group ) {
+ $groups_list = self::getGroups( intval( $row->user_id ), $this->userGroupCache );
+ foreach ( $groups_list as $group ) {
if ( in_array( $group, $this->hideGroups ) ) {
return '';
}
@@ -211,7 +214,8 @@ class ActiveUsersPager extends UsersPager {
# Username field
$out .= Xml::inputLabel( $this->msg( 'activeusers-from' )->text(),
- 'username', 'offset', 20, $this->requestedUser, array( 'tabindex' => 1 ) ) . '<br />';
+ 'username', 'offset', 20, $this->requestedUser,
+ array( 'class' => 'mw-ui-input-inline', 'tabindex' => 1 ) ) . '<br />';
$out .= Xml::checkLabel( $this->msg( 'activeusers-hidebots' )->text(),
'hidebots', 'hidebots', $this->opts->getValue( 'hidebots' ), array( 'tabindex' => 2 ) );
@@ -263,11 +267,22 @@ class SpecialActiveUsers extends SpecialPage {
$out->wrapWikiMsg( "<div class='mw-activeusers-intro'>\n$1\n</div>",
array( 'activeusers-intro', $this->getLanguage()->formatNum( $days ) ) );
- // Occasionally merge in new updates
- $seconds = min( self::mergeActiveUsers( 600, $days ), $days * 86400 );
- // Mention the level of staleness
- $out->addWikiMsg( 'cachedspecial-viewing-cached-ttl',
- $this->getLanguage()->formatDuration( $seconds ) );
+ // Get the timestamp of the last cache update
+ $dbr = wfGetDB( DB_SLAVE, 'recentchanges' );
+ $cTime = $dbr->selectField( 'querycache_info',
+ 'qci_timestamp',
+ array( 'qci_type' => 'activeusers' )
+ );
+
+ $secondsOld = $cTime
+ ? time() - wfTimestamp( TS_UNIX, $cTime )
+ : $days * 86400; // fully stale :)
+
+ if ( $secondsOld > 0 ) {
+ // Mention the level of staleness
+ $out->addWikiMsg( 'cachedspecial-viewing-cached-ttl',
+ $this->getLanguage()->formatDuration( $secondsOld ) );
+ }
$up = new ActiveUsersPager( $this->getContext(), null, $par );
@@ -289,149 +304,4 @@ class SpecialActiveUsers extends SpecialPage {
protected function getGroupName() {
return 'users';
}
-
- /**
- * @param int $period Seconds (do updates no more often than this)
- * @param int $days How many days user must be idle before he is considered inactive
- * @return int How many seconds old the cache is
- */
- public static function mergeActiveUsers( $period, $days ) {
- $dbr = wfGetDB( DB_SLAVE );
- $cTime = $dbr->selectField( 'querycache_info',
- 'qci_timestamp',
- array( 'qci_type' => 'activeusers' )
- );
-
- if ( !wfReadOnly() ) {
- if ( !$cTime || ( time() - wfTimestamp( TS_UNIX, $cTime ) ) > $period ) {
- $dbw = wfGetDB( DB_MASTER );
- if ( $dbw->estimateRowCount( 'recentchanges' ) <= 10000 ) {
- $window = $days * 86400; // small wiki
- } else {
- $window = $period * 2;
- }
- $cTime = self::doQueryCacheUpdate( $dbw, $days, $window ) ?: $cTime;
- }
- }
-
- return ( time() -
- ( $cTime ? wfTimestamp( TS_UNIX, $cTime ) : $days * 86400 ) );
- }
-
- /**
- * @param DatabaseBase $dbw Passed in from updateSpecialPages.php
- * @return void
- */
- public static function cacheUpdate( DatabaseBase $dbw ) {
- global $wgActiveUserDays;
-
- self::doQueryCacheUpdate( $dbw, $wgActiveUserDays, $wgActiveUserDays * 86400 );
- }
-
- /**
- * Update the query cache as needed
- *
- * @param DatabaseBase $dbw
- * @param int $days How many days user must be idle before he is considered inactive
- * @param int $window Maximum time range of new data to scan (in seconds)
- * @return int|bool UNIX timestamp the cache is now up-to-date as of (false on error)
- */
- protected static function doQueryCacheUpdate( DatabaseBase $dbw, $days, $window ) {
- $lockKey = wfWikiID() . '-activeusers';
- if ( !$dbw->lock( $lockKey, __METHOD__, 1 ) ) {
- return false; // exclusive update (avoids duplicate entries)
- }
-
- $now = time();
- $cTime = $dbw->selectField( 'querycache_info',
- 'qci_timestamp',
- array( 'qci_type' => 'activeusers' )
- );
- $cTimeUnix = $cTime ? wfTimestamp( TS_UNIX, $cTime ) : 1;
-
- // Pick the date range to fetch from. This is normally from the last
- // update to till the present time, but has a limited window for sanity.
- // If the window is limited, multiple runs are need to fully populate it.
- $sTimestamp = max( $cTimeUnix, $now - $days * 86400 );
- $eTimestamp = min( $sTimestamp + $window, $now );
-
- // Get all the users active since the last update
- $res = $dbw->select(
- array( 'recentchanges' ),
- array( 'rc_user_text', 'lastedittime' => 'MAX(rc_timestamp)' ),
- array(
- 'rc_user > 0', // actual accounts
- 'rc_type != ' . $dbw->addQuotes( RC_EXTERNAL ), // no wikidata
- 'rc_log_type IS NULL OR rc_log_type != ' . $dbw->addQuotes( 'newusers' ),
- 'rc_timestamp >= ' . $dbw->addQuotes( $dbw->timestamp( $sTimestamp ) ),
- 'rc_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $eTimestamp ) )
- ),
- __METHOD__,
- array(
- 'GROUP BY' => array( 'rc_user_text' ),
- 'ORDER BY' => 'NULL' // avoid filesort
- )
- );
- $names = array();
- foreach ( $res as $row ) {
- $names[$row->rc_user_text] = $row->lastedittime;
- }
-
- // Rotate out users that have not edited in too long (according to old data set)
- $dbw->delete( 'querycachetwo',
- array(
- 'qcc_type' => 'activeusers',
- 'qcc_value < ' . $dbw->addQuotes( $now - $days * 86400 ) // TS_UNIX
- ),
- __METHOD__
- );
-
- // Find which of the recently active users are already accounted for
- if ( count( $names ) ) {
- $res = $dbw->select( 'querycachetwo',
- array( 'user_name' => 'qcc_title' ),
- array(
- 'qcc_type' => 'activeusers',
- 'qcc_namespace' => NS_USER,
- 'qcc_title' => array_keys( $names ) ),
- __METHOD__
- );
- foreach ( $res as $row ) {
- unset( $names[$row->user_name] );
- }
- }
-
- // Insert the users that need to be added to the list (which their last edit time
- if ( count( $names ) ) {
- $newRows = array();
- foreach ( $names as $name => $lastEditTime ) {
- $newRows[] = array(
- 'qcc_type' => 'activeusers',
- 'qcc_namespace' => NS_USER,
- 'qcc_title' => $name,
- 'qcc_value' => wfTimestamp( TS_UNIX, $lastEditTime ),
- 'qcc_namespacetwo' => 0, // unused
- 'qcc_titletwo' => '' // unused
- );
- }
- foreach ( array_chunk( $newRows, 500 ) as $rowBatch ) {
- $dbw->insert( 'querycachetwo', $rowBatch, __METHOD__ );
- if ( !$dbw->trxLevel() ) {
- wfWaitForSlaves();
- }
- }
- }
-
- // Touch the data freshness timestamp
- $dbw->replace( 'querycache_info',
- array( 'qci_type' ),
- array( 'qci_type' => 'activeusers',
- 'qci_timestamp' => $dbw->timestamp( $eTimestamp ) ), // not always $now
- __METHOD__
- );
-
- $dbw->unlock( $lockKey, __METHOD__ );
-
- return $eTimestamp;
- }
}
diff --git a/includes/specials/SpecialAllMessages.php b/includes/specials/SpecialAllMessages.php
index 96be4d03..7b596bb0 100644
--- a/includes/specials/SpecialAllMessages.php
+++ b/includes/specials/SpecialAllMessages.php
@@ -59,6 +59,7 @@ class SpecialAllMessages extends SpecialPage {
$this->outputHeader( 'allmessagestext' );
$out->addModuleStyles( 'mediawiki.special' );
+ $this->addHelpLink( 'Help:System message' );
$this->table = new AllmessagesTablePager(
$this,
@@ -223,19 +224,17 @@ class AllMessagesTablePager extends TablePager {
}
function getAllMessages( $descending ) {
- wfProfileIn( __METHOD__ );
$messageNames = Language::getLocalisationCache()->getSubitemList( 'en', 'messages' );
+
+ // Normalise message names so they look like page titles and sort correctly - T86139
+ $messageNames = array_map( array( $this->lang, 'ucfirst' ), $messageNames );
+
if ( $descending ) {
rsort( $messageNames );
} else {
asort( $messageNames );
}
- // Normalise message names so they look like page titles
- $messageNames = array_map( array( $this->lang, 'ucfirst' ), $messageNames );
-
- wfProfileOut( __METHOD__ );
-
return $messageNames;
}
@@ -252,7 +251,6 @@ class AllMessagesTablePager extends TablePager {
*/
public static function getCustomisedStatuses( $messageNames, $langcode = 'en', $foreign = false ) {
// FIXME: This function should be moved to Language:: or something.
- wfProfileIn( __METHOD__ . '-db' );
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'page',
@@ -288,8 +286,6 @@ class AllMessagesTablePager extends TablePager {
}
}
- wfProfileOut( __METHOD__ . '-db' );
-
return array( 'pages' => $pageFlags, 'talks' => $talkFlags );
}
diff --git a/includes/specials/SpecialAllPages.php b/includes/specials/SpecialAllPages.php
index 08b8761a..74b1f7bb 100644
--- a/includes/specials/SpecialAllPages.php
+++ b/includes/specials/SpecialAllPages.php
@@ -101,7 +101,10 @@ class SpecialAllPages extends IncludableSpecialPage {
$t = $this->getPageTitle();
$out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
- $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $this->getConfig()->get( 'Script' ) ) );
+ $out .= Xml::openElement(
+ 'form',
+ array( 'method' => 'get', 'action' => $this->getConfig()->get( 'Script' ) )
+ );
$out .= Html::hidden( 'title', $t->getPrefixedText() );
$out .= Xml::openElement( 'fieldset' );
$out .= Xml::element( 'legend', null, $this->msg( 'allpages' )->text() );
diff --git a/includes/specials/SpecialApiHelp.php b/includes/specials/SpecialApiHelp.php
new file mode 100644
index 00000000..b43911f5
--- /dev/null
+++ b/includes/specials/SpecialApiHelp.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Implements Special:ApiHelp
+ *
+ * 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
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Special page to redirect to API help pages, for situations where linking to
+ * the api.php endpoint is not wanted.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialApiHelp extends UnlistedSpecialPage {
+ public function __construct() {
+ parent::__construct( 'ApiHelp' );
+ }
+
+ public function execute( $par ) {
+ if ( empty( $par ) ) {
+ $par = 'main';
+ }
+
+ // These come from transclusions
+ $request = $this->getRequest();
+ $options = array(
+ 'action' => 'help',
+ 'nolead' => true,
+ 'submodules' => $request->getCheck( 'submodules' ),
+ 'recursivesubmodules' => $request->getCheck( 'recursivesubmodules' ),
+ 'title' => $request->getVal( 'title', $this->getPageTitle( '$1' )->getPrefixedText() ),
+ );
+
+ // These are for linking from wikitext, since url parameters are a pain
+ // to do.
+ while ( true ) {
+ if ( substr( $par, 0, 4 ) === 'sub/' ) {
+ $par = substr( $par, 4 );
+ $options['submodules'] = 1;
+ continue;
+ }
+
+ if ( substr( $par, 0, 5 ) === 'rsub/' ) {
+ $par = substr( $par, 5 );
+ $options['recursivesubmodules'] = 1;
+ continue;
+ }
+
+ $moduleName = $par;
+ break;
+ }
+
+ if ( !$this->including() ) {
+ unset( $options['nolead'], $options['title'] );
+ $options['modules'] = $moduleName;
+ $link = wfAppendQuery( wfExpandUrl( wfScript( 'api' ), PROTO_CURRENT ), $options );
+ $this->getOutput()->redirect( $link );
+ return;
+ }
+
+ $main = new ApiMain( $this->getContext(), false );
+ try {
+ $module = $main->getModuleFromPath( $moduleName );
+ } catch ( UsageException $ex ) {
+ $this->getOutput()->addHTML( Html::rawElement( 'span', array( 'class' => 'error' ),
+ $this->msg( 'apihelp-no-such-module', $moduleName )->inContentLanguage()->parse()
+ ) );
+ return;
+ }
+
+ ApiHelp::getHelp( $this->getContext(), $module, $options );
+ }
+
+ public function isIncludable() {
+ return true;
+ }
+}
diff --git a/includes/specials/SpecialBlock.php b/includes/specials/SpecialBlock.php
index 3297c17a..b80d921d 100644
--- a/includes/specials/SpecialBlock.php
+++ b/includes/specials/SpecialBlock.php
@@ -98,13 +98,16 @@ class SpecialBlock extends FormSpecialPage {
$form->setWrapperLegendMsg( 'blockip-legend' );
$form->setHeaderText( '' );
$form->setSubmitCallback( array( __CLASS__, 'processUIForm' ) );
+ $form->setSubmitDestructive();
$msg = $this->alreadyBlocked ? 'ipb-change-block' : 'ipbsubmit';
$form->setSubmitTextMsg( $msg );
+ $this->addHelpLink( 'Help:Blocking users' );
+
# Don't need to do anything if the form has been posted
if ( !$this->getRequest()->wasPosted() && $this->preErrors ) {
- $s = HTMLForm::formatErrors( $this->preErrors );
+ $s = $form->formatErrors( $this->preErrors );
if ( $s ) {
$form->addHeaderText( Html::rawElement(
'div',
@@ -135,6 +138,7 @@ class SpecialBlock extends FormSpecialPage {
'autofocus' => true,
'required' => true,
'validation-callback' => array( __CLASS__, 'validateTargetField' ),
+ 'cssclass' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
),
'Expiry' => array(
'type' => !count( $suggestedDurations ) ? 'text' : 'selectorother',
@@ -146,6 +150,7 @@ class SpecialBlock extends FormSpecialPage {
),
'Reason' => array(
'type' => 'selectandother',
+ 'maxlength' => 255,
'label-message' => 'ipbreason',
'options-message' => 'ipbreason-dropdown',
),
@@ -217,7 +222,7 @@ class SpecialBlock extends FormSpecialPage {
$this->maybeAlterFormDefaults( $a );
// Allow extensions to add more fields
- wfRunHooks( 'SpecialBlockModifyFormFields', array( $this, &$a ) );
+ Hooks::run( 'SpecialBlockModifyFormFields', array( $this, &$a ) );
return $a;
}
@@ -233,6 +238,14 @@ class SpecialBlock extends FormSpecialPage {
# This will be overwritten by request data
$fields['Target']['default'] = (string)$this->target;
+ if ( $this->target ) {
+ $status = self::validateTarget( $this->target, $this->getUser() );
+ if ( !$status->isOK() ) {
+ $errors = $status->getErrorsArray();
+ $this->preErrors = array_merge( $this->preErrors, $errors );
+ }
+ }
+
# This won't be
$fields['PreviousTarget']['default'] = (string)$this->target;
@@ -307,14 +320,14 @@ class SpecialBlock extends FormSpecialPage {
* @return string
*/
protected function preText() {
- $this->getOutput()->addModules( 'mediawiki.special.block' );
+ $this->getOutput()->addModules( array( 'mediawiki.special.block', 'mediawiki.userSuggest' ) );
$text = $this->msg( 'blockiptext' )->parse();
$otherBlockMessages = array();
if ( $this->target !== null ) {
# Get other blocks, i.e. from GlobalBlocking or TorBlock extension
- wfRunHooks( 'OtherBlockLogLink', array( &$otherBlockMessages, $this->target ) );
+ Hooks::run( 'OtherBlockLogLink', array( &$otherBlockMessages, $this->target ) );
if ( count( $otherBlockMessages ) ) {
$s = Html::rawElement(
@@ -623,7 +636,7 @@ class SpecialBlock extends FormSpecialPage {
# permission anyway, although the code does allow for it.
# Note: Important to use $target instead of $data['Target']
# since both $data['PreviousTarget'] and $target are normalized
- # but $data['target'] gets overriden by (non-normalized) request variable
+ # but $data['target'] gets overridden by (non-normalized) request variable
# from previous request.
if ( $target === $performer->getName() &&
( $data['PreviousTarget'] !== $target || !$data['Confirm'] )
@@ -668,7 +681,7 @@ class SpecialBlock extends FormSpecialPage {
# Recheck params here...
if ( $type != Block::TYPE_USER ) {
$data['HideUser'] = false; # IP users should not be hidden
- } elseif ( !in_array( $data['Expiry'], array( 'infinite', 'infinity', 'indefinite' ) ) ) {
+ } elseif ( !wfIsInfinity( $data['Expiry'] ) ) {
# Bad expiry.
return array( 'ipb_expiry_temp' );
} elseif ( $wgHideUserContribLimit !== false
@@ -698,7 +711,7 @@ class SpecialBlock extends FormSpecialPage {
$block->mHideName = $data['HideUser'];
$reason = array( 'hookaborted' );
- if ( !wfRunHooks( 'BlockIp', array( &$block, &$performer, &$reason ) ) ) {
+ if ( !Hooks::run( 'BlockIp', array( &$block, &$performer, &$reason ) ) ) {
return $reason;
}
@@ -759,7 +772,7 @@ class SpecialBlock extends FormSpecialPage {
$logaction = 'block';
}
- wfRunHooks( 'BlockIpComplete', array( $block, $performer ) );
+ Hooks::run( 'BlockIpComplete', array( $block, $performer ) );
# Set *_deleted fields if requested
if ( $data['HideUser'] ) {
@@ -781,22 +794,21 @@ class SpecialBlock extends FormSpecialPage {
# Prepare log parameters
$logParams = array();
- $logParams[] = $data['Expiry'];
- $logParams[] = self::blockLogFlags( $data, $type );
+ $logParams['5::duration'] = $data['Expiry'];
+ $logParams['6::flags'] = self::blockLogFlags( $data, $type );
# Make log entry, if the name is hidden, put it in the oversight log
$log_type = $data['HideUser'] ? 'suppress' : 'block';
- $log = new LogPage( $log_type );
- $log_id = $log->addEntry(
- $logaction,
- Title::makeTitle( NS_USER, $target ),
- $data['Reason'][0],
- $logParams,
- $performer
- );
+ $logEntry = new ManualLogEntry( $log_type, $logaction );
+ $logEntry->setTarget( Title::makeTitle( NS_USER, $target ) );
+ $logEntry->setComment( $data['Reason'][0] );
+ $logEntry->setPerformer( $performer );
+ $logEntry->setParameters( $logParams );
# Relate log ID to block IDs (bug 25763)
$blockIds = array_merge( array( $status['id'] ), $status['autoIds'] );
- $log->addRelations( 'ipb_id', $blockIds, $log_id );
+ $logEntry->setRelations( array( 'ipb_id' => $blockIds ) );
+ $logId = $logEntry->insert();
+ $logEntry->publish( $logId );
# Report to the user
return true;
@@ -826,7 +838,7 @@ class SpecialBlock extends FormSpecialPage {
}
list( $show, $value ) = explode( ':', $option );
- $a[htmlspecialchars( $show )] = htmlspecialchars( $value );
+ $a[$show] = $value;
}
return $a;
@@ -844,7 +856,7 @@ class SpecialBlock extends FormSpecialPage {
$infinity = wfGetDB( DB_SLAVE )->getInfinity();
}
- if ( $expiry == 'infinite' || $expiry == 'indefinite' ) {
+ if ( wfIsInfinity( $expiry ) ) {
$expiry = $infinity;
} else {
$expiry = strtotime( $expiry );
diff --git a/includes/specials/SpecialBlockList.php b/includes/specials/SpecialBlockList.php
index 456f4ecb..0ec144a2 100644
--- a/includes/specials/SpecialBlockList.php
+++ b/includes/specials/SpecialBlockList.php
@@ -47,6 +47,7 @@ class SpecialBlockList extends SpecialPage {
$lang = $this->getLanguage();
$out->setPageTitle( $this->msg( 'ipblocklist' ) );
$out->addModuleStyles( 'mediawiki.special' );
+ $out->addModules( 'mediawiki.userSuggest' );
$request = $this->getRequest();
$par = $request->getVal( 'ip', $par );
@@ -72,6 +73,7 @@ class SpecialBlockList extends SpecialPage {
'tabindex' => '1',
'size' => '45',
'default' => $this->target,
+ 'cssclass' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
),
'Options' => array(
'type' => 'multiselect',
@@ -103,6 +105,7 @@ class SpecialBlockList extends SpecialPage {
$form->setMethod( 'get' );
$form->setWrapperLegendMsg( 'ipblocklist-legend' );
$form->setSubmitTextMsg( 'ipblocklist-submit' );
+ $form->setSubmitProgressive();
$form->prepareForm();
$form->displayForm( '' );
@@ -110,11 +113,6 @@ class SpecialBlockList extends SpecialPage {
}
function showList() {
- # Purge expired entries on one in every 10 queries
- if ( !mt_rand( 0, 10 ) ) {
- Block::purgeExpired();
- }
-
$conds = array();
# Is the user allowed to see hidden blocks?
if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
@@ -167,7 +165,7 @@ class SpecialBlockList extends SpecialPage {
# Check for other blocks, i.e. global/tor blocks
$otherBlockLink = array();
- wfRunHooks( 'OtherBlockLogLink', array( &$otherBlockLink, $this->target ) );
+ Hooks::run( 'OtherBlockLogLink', array( &$otherBlockLink, $this->target ) );
$out = $this->getOutput();
@@ -259,7 +257,6 @@ class BlockListPager extends TablePager {
'blocklist-nousertalk',
'unblocklink',
'change-blocklink',
- 'infiniteblock',
);
$msg = array_combine( $msg, array_map( array( $this, 'msg' ), $msg ) );
}
@@ -396,6 +393,10 @@ class BlockListPager extends TablePager {
'join_conds' => array( 'user' => array( 'LEFT JOIN', 'user_id = ipb_by' ) )
);
+ # Filter out any expired blocks
+ $db = $this->getDatabase();
+ $info['conds'][] = 'ipb_expiry > ' . $db->addQuotes( $db->timestamp() );
+
# Is the user allowed to see hidden blocks?
if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
$info['conds']['ipb_deleted'] = 0;
@@ -425,7 +426,6 @@ class BlockListPager extends TablePager {
* @param ResultWrapper $result
*/
function preprocessResults( $result ) {
- wfProfileIn( __METHOD__ );
# Do a link batch query
$lb = new LinkBatch;
$lb->setCaller( __METHOD__ );
@@ -450,6 +450,5 @@ class BlockListPager extends TablePager {
}
$lb->execute();
- wfProfileOut( __METHOD__ );
}
}
diff --git a/includes/specials/SpecialBooksources.php b/includes/specials/SpecialBooksources.php
index 72f4e466..7028fdcb 100644
--- a/includes/specials/SpecialBooksources.php
+++ b/includes/specials/SpecialBooksources.php
@@ -26,7 +26,6 @@
* The parser creates links to this page when dealing with ISBNs in wikitext
*
* @author Rob Church <robchur@gmail.com>
- * @todo Validate ISBNs using the standard check-digit method
* @ingroup SpecialPage
*/
class SpecialBookSources extends SpecialPage {
@@ -73,7 +72,9 @@ class SpecialBookSources extends SpecialPage {
$sum = 0;
if ( strlen( $isbn ) == 13 ) {
for ( $i = 0; $i < 12; $i++ ) {
- if ( $i % 2 == 0 ) {
+ if ( $isbn[$i] === 'X' ) {
+ return false;
+ } elseif ( $i % 2 == 0 ) {
$sum += $isbn[$i];
} else {
$sum += 3 * $isbn[$i];
@@ -81,11 +82,14 @@ class SpecialBookSources extends SpecialPage {
}
$check = ( 10 - ( $sum % 10 ) ) % 10;
- if ( $check == $isbn[12] ) {
+ if ( (string)$check === $isbn[12] ) {
return true;
}
} elseif ( strlen( $isbn ) == 10 ) {
for ( $i = 0; $i < 9; $i++ ) {
+ if ( $isbn[$i] === 'X' ) {
+ return false;
+ }
$sum += $isbn[$i] * ( $i + 1 );
}
@@ -93,7 +97,7 @@ class SpecialBookSources extends SpecialPage {
if ( $check == 10 ) {
$check = "X";
}
- if ( $check == $isbn[9] ) {
+ if ( (string)$check === $isbn[9] ) {
return true;
}
}
@@ -131,9 +135,14 @@ class SpecialBookSources extends SpecialPage {
'isbn',
20,
$this->isbn,
- array( 'autofocus' => true )
+ array( 'autofocus' => '', 'class' => 'mw-ui-input-inline' )
);
- $form .= '&#160;' . Xml::submitButton( $this->msg( 'booksources-go' )->text() ) . "</p>\n";
+
+ $form .= '&#160;' . Html::submitButton(
+ $this->msg( 'booksources-search' )->text(),
+ array(), array( 'mw-ui-progressive' )
+ ) . "</p>\n";
+
$form .= Html::closeElement( 'form' ) . "\n";
$form .= Html::closeElement( 'fieldset' ) . "\n";
@@ -152,7 +161,7 @@ class SpecialBookSources extends SpecialPage {
# Hook to allow extensions to insert additional HTML,
# e.g. for API-interacting plugins and so on
- wfRunHooks( 'BookInformation', array( $this->isbn, $this->getOutput() ) );
+ Hooks::run( 'BookInformation', array( $this->isbn, $this->getOutput() ) );
# Check for a local page such as Project:Book_sources and use that if available
$page = $this->msg( 'booksources' )->inContentLanguage()->text();
diff --git a/includes/specials/SpecialCategories.php b/includes/specials/SpecialCategories.php
index 95f9efd2..3a13b7ed 100644
--- a/includes/specials/SpecialCategories.php
+++ b/includes/specials/SpecialCategories.php
@@ -188,9 +188,11 @@ class CategoryPager extends AlphabeticPager {
$this->msg( 'categories' )->text(),
Xml::inputLabel(
$this->msg( 'categoriesfrom' )->text(),
- 'from', 'from', 20, $from ) .
+ 'from', 'from', 20, $from, array( 'class' => 'mw-ui-input-inline' ) ) .
' ' .
- Xml::submitButton( $this->msg( 'allpagessubmit' )->text()
+ Html::submitButton(
+ $this->msg( 'allpagessubmit' )->text(),
+ array(), array( 'mw-ui-progressive' )
)
)
);
diff --git a/includes/specials/SpecialChangeEmail.php b/includes/specials/SpecialChangeEmail.php
index e0be838b..eca307d9 100644
--- a/includes/specials/SpecialChangeEmail.php
+++ b/includes/specials/SpecialChangeEmail.php
@@ -39,7 +39,7 @@ class SpecialChangeEmail extends FormSpecialPage {
/**
* @return bool
*/
- function isListed() {
+ public function isListed() {
global $wgAuth;
return $wgAuth->allowPropChange( 'emailaddress' );
@@ -54,7 +54,7 @@ class SpecialChangeEmail extends FormSpecialPage {
$out->disallowUserJs();
$out->addModules( 'mediawiki.special.changeemail' );
- return parent::execute( $par );
+ parent::execute( $par );
}
protected function checkExecutePermissions( User $user ) {
@@ -106,22 +106,20 @@ class SpecialChangeEmail extends FormSpecialPage {
return $fields;
}
+ protected function getDisplayFormat() {
+ return 'vform';
+ }
+
protected function alterForm( HTMLForm $form ) {
- $form->setDisplayFormat( 'vform' );
$form->setId( 'mw-changeemail-form' );
$form->setTableId( 'mw-changeemail-table' );
- $form->setWrapperLegend( false );
$form->setSubmitTextMsg( 'changeemail-submit' );
- $form->addHiddenField( 'returnto', $this->getRequest()->getVal( 'returnto' ) );
+ $form->addHiddenFields( $this->getRequest()->getValues( 'returnto', 'returntoquery' ) );
}
public function onSubmit( array $data ) {
- if ( $this->getRequest()->getBool( 'wpCancel' ) ) {
- $status = Status::newGood( true );
- } else {
- $password = isset( $data['Password'] ) ? $data['Password'] : null;
- $status = $this->attemptChange( $this->getUser(), $password, $data['NewEmail'] );
- }
+ $password = isset( $data['Password'] ) ? $data['Password'] : null;
+ $status = $this->attemptChange( $this->getUser(), $password, $data['NewEmail'] );
$this->status = $status;
@@ -129,18 +127,22 @@ class SpecialChangeEmail extends FormSpecialPage {
}
public function onSuccess() {
- $titleObj = Title::newFromText( $this->getRequest()->getVal( 'returnto' ) );
+ $request = $this->getRequest();
+
+ $titleObj = Title::newFromText( $request->getVal( 'returnto' ) );
if ( !$titleObj instanceof Title ) {
$titleObj = Title::newMainPage();
}
+ $query = $request->getVal( 'returntoquery' );
if ( $this->status->value === true ) {
- $this->getOutput()->redirect( $titleObj->getFullURL() );
+ $this->getOutput()->redirect( $titleObj->getFullURL( $query ) );
} elseif ( $this->status->value === 'eauth' ) {
# Notify user that a confirmation email has been sent...
$this->getOutput()->wrapWikiMsg( "<div class='error' style='clear: both;'>\n$1\n</div>",
'eauthentsent', $this->getUser()->getName() );
- $this->getOutput()->addReturnTo( $titleObj ); // just show the link to go back
+ // just show the link to go back
+ $this->getOutput()->addReturnTo( $titleObj, wfCgiToArray( $query ) );
}
}
@@ -150,7 +152,7 @@ class SpecialChangeEmail extends FormSpecialPage {
* @param string $newaddr
* @return Status
*/
- protected function attemptChange( User $user, $pass, $newaddr ) {
+ private function attemptChange( User $user, $pass, $newaddr ) {
global $wgAuth;
if ( $newaddr != '' && !Sanitizer::validateEmail( $newaddr ) ) {
@@ -184,7 +186,7 @@ class SpecialChangeEmail extends FormSpecialPage {
return $status;
}
- wfRunHooks( 'PrefsEmailAudit', array( $user, $oldaddr, $newaddr ) );
+ Hooks::run( 'PrefsEmailAudit', array( $user, $oldaddr, $newaddr ) );
$user->saveSettings();
diff --git a/includes/specials/SpecialChangePassword.php b/includes/specials/SpecialChangePassword.php
index 24664edb..168095f8 100644
--- a/includes/specials/SpecialChangePassword.php
+++ b/includes/specials/SpecialChangePassword.php
@@ -118,7 +118,7 @@ class SpecialChangePassword extends FormSpecialPage {
}
$extraFields = array();
- wfRunHooks( 'ChangePasswordForm', array( &$extraFields ) );
+ Hooks::run( 'ChangePasswordForm', array( &$extraFields ) );
foreach ( $extraFields as $extra ) {
list( $name, $label, $type, $default ) = $extra;
$fields[$name] = array(
@@ -248,7 +248,7 @@ class SpecialChangePassword extends FormSpecialPage {
}
if ( $newpass !== $retype ) {
- wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'badretype' ) );
+ Hooks::run( 'PrefsPasswordAudit', array( $user, $newpass, 'badretype' ) );
throw new PasswordError( $this->msg( 'badretype' )->text() );
}
@@ -264,7 +264,7 @@ class SpecialChangePassword extends FormSpecialPage {
// @todo Make these separate messages, since the message is written for both cases
if ( !$user->checkTemporaryPassword( $oldpass ) && !$user->checkPassword( $oldpass ) ) {
- wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) );
+ Hooks::run( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) );
throw new PasswordError( $this->msg( 'resetpass-wrong-oldpass' )->text() );
}
@@ -276,8 +276,8 @@ class SpecialChangePassword extends FormSpecialPage {
// Do AbortChangePassword after checking mOldpass, so we don't leak information
// by possibly aborting a new password before verifying the old password.
$abortMsg = 'resetpass-abort-generic';
- if ( !wfRunHooks( 'AbortChangePassword', array( $user, $oldpass, $newpass, &$abortMsg ) ) ) {
- wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'abortreset' ) );
+ if ( !Hooks::run( 'AbortChangePassword', array( $user, $oldpass, $newpass, &$abortMsg ) ) ) {
+ Hooks::run( 'PrefsPasswordAudit', array( $user, $newpass, 'abortreset' ) );
throw new PasswordError( $this->msg( $abortMsg )->text() );
}
@@ -288,9 +288,9 @@ class SpecialChangePassword extends FormSpecialPage {
try {
$user->setPassword( $newpass );
- wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) );
+ Hooks::run( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) );
} catch ( PasswordError $e ) {
- wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) );
+ Hooks::run( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) );
throw new PasswordError( $e->getMessage() );
}
diff --git a/includes/specials/SpecialConfirmemail.php b/includes/specials/SpecialConfirmemail.php
index d771589d..b6ab112b 100644
--- a/includes/specials/SpecialConfirmemail.php
+++ b/includes/specials/SpecialConfirmemail.php
@@ -38,6 +38,9 @@ class EmailConfirmation extends UnlistedSpecialPage {
* Main execution point
*
* @param null|string $code Confirmation code passed to the page
+ * @throws PermissionsError
+ * @throws ReadOnlyError
+ * @throws UserNotLoggedIn
*/
function execute( $code ) {
$this->setHeaders();
diff --git a/includes/specials/SpecialContributions.php b/includes/specials/SpecialContributions.php
index 32a887c4..c2cd8122 100644
--- a/includes/specials/SpecialContributions.php
+++ b/includes/specials/SpecialContributions.php
@@ -176,7 +176,7 @@ class SpecialContributions extends IncludableSpecialPage {
// Add RSS/atom links
$this->addFeedLinks( $feedParams );
- if ( wfRunHooks( 'SpecialContributionsBeforeMainOutput', array( $id, $userObj, $this ) ) ) {
+ if ( Hooks::run( 'SpecialContributionsBeforeMainOutput', array( $id, $userObj, $this ) ) ) {
if ( !$this->including() ) {
$out->addHTML( $this->getForm() );
}
@@ -386,7 +386,7 @@ class SpecialContributions extends IncludableSpecialPage {
);
}
- wfRunHooks( 'ContributionsToolLinks', array( $id, $userpage, &$tools ) );
+ Hooks::run( 'ContributionsToolLinks', array( $id, $userpage, &$tools ) );
return $tools;
}
@@ -478,18 +478,15 @@ class SpecialContributions extends IncludableSpecialPage {
if ( $tagFilter ) {
$filterSelection = Html::rawElement(
'td',
- array( 'class' => 'mw-label' ),
- array_shift( $tagFilter )
- );
- $filterSelection .= Html::rawElement(
- 'td',
- array( 'class' => 'mw-input' ),
- implode( '&#160', $tagFilter )
+ array(),
+ array_shift( $tagFilter ) . implode( '&#160', $tagFilter )
);
} else {
$filterSelection = Html::rawElement( 'td', array( 'colspan' => 2 ), '' );
}
+ $this->getOutput()->addModules( 'mediawiki.userSuggest' );
+
$labelNewbies = Xml::radioLabel(
$this->msg( 'sp-contributions-newbies' )->text(),
'contribs',
@@ -510,9 +507,15 @@ class SpecialContributions extends IncludableSpecialPage {
'target',
$this->opts['target'],
'text',
- array( 'size' => '40', 'required' => '', 'class' => 'mw-input' ) +
- ( $this->opts['target'] ? array() : array( 'autofocus' )
- )
+ array(
+ 'size' => '40',
+ 'required' => '',
+ 'class' => array(
+ 'mw-input',
+ 'mw-ui-input-inline',
+ 'mw-autocomplete-user', // used by mediawiki.userSuggest
+ ),
+ ) + ( $this->opts['target'] ? array() : array( 'autofocus' ) )
);
$targetSelection = Html::rawElement(
'td',
@@ -522,16 +525,12 @@ class SpecialContributions extends IncludableSpecialPage {
$namespaceSelection = Xml::tags(
'td',
- array( 'class' => 'mw-label' ),
+ array(),
Xml::label(
$this->msg( 'namespace' )->text(),
'namespace',
''
- )
- );
- $namespaceSelection .= Html::rawElement(
- 'td',
- null,
+ ) .
Html::namespaceSelector(
array( 'selected' => $this->opts['namespace'], 'all' => '' ),
array(
@@ -617,9 +616,9 @@ class SpecialContributions extends IncludableSpecialPage {
$this->opts['year'] === '' ? MWTimestamp::getInstance()->format( 'Y' ) : $this->opts['year'],
$this->opts['month']
) . ' ' .
- Xml::submitButton(
+ Html::submitButton(
$this->msg( 'sp-contributions-submit' )->text(),
- array( 'class' => 'mw-submit' )
+ array( 'class' => 'mw-submit' ), array( 'mw-ui-progressive' )
)
);
@@ -659,7 +658,7 @@ class ContribsPager extends ReverseChronologicalPager {
public $mDb;
public $preventClickjacking = false;
- /** @var DatabaseBase */
+ /** @var IDatabase */
public $mDbSecondary;
/**
@@ -673,10 +672,7 @@ class ContribsPager extends ReverseChronologicalPager {
$msgs = array(
'diff',
'hist',
- 'newarticle',
'pipe-separator',
- 'rev-delundel',
- 'rollbacklink',
'uctop'
);
@@ -715,7 +711,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.
+ * a hook added, to allow extensions to add additional queries.
*
* @param string $offset Index offset, inclusive
* @param int $limit Exact query limit
@@ -751,7 +747,7 @@ class ContribsPager extends ReverseChronologicalPager {
$data = array( $this->mDb->select(
$tables, $fields, $conds, $fname, $options, $join_conds
) );
- wfRunHooks(
+ Hooks::run(
'ContribsPager::reallyDoQuery',
array( &$data, $pager, $offset, $limit, $descending )
);
@@ -828,7 +824,7 @@ class ContribsPager extends ReverseChronologicalPager {
$this->tagFilter
);
- wfRunHooks( 'ContribsPager::getQueryInfo', array( &$this, &$queryInfo ) );
+ Hooks::run( 'ContribsPager::getQueryInfo', array( &$this, &$queryInfo ) );
return $queryInfo;
}
@@ -935,7 +931,7 @@ class ContribsPager extends ReverseChronologicalPager {
* @return string
*/
function getStartBody() {
- return "<ul>\n";
+ return "<ul class=\"mw-contributions-list\">\n";
}
/**
@@ -958,7 +954,6 @@ class ContribsPager extends ReverseChronologicalPager {
* @return string
*/
function formatRow( $row ) {
- wfProfileIn( __METHOD__ );
$ret = '';
$classes = array();
@@ -974,7 +969,7 @@ class ContribsPager extends ReverseChronologicalPager {
try {
$rev = new Revision( $row );
$validRevision = (bool)$rev->getId();
- } catch ( MWException $e ) {
+ } catch ( Exception $e ) {
$validRevision = false;
}
wfRestoreWarnings();
@@ -1113,7 +1108,7 @@ class ContribsPager extends ReverseChronologicalPager {
}
// Let extensions add data
- wfRunHooks( 'ContributionsLineEnding', array( $this, &$ret, $row, &$classes ) );
+ Hooks::run( 'ContributionsLineEnding', array( $this, &$ret, $row, &$classes ) );
if ( $classes === array() && $ret === '' ) {
wfDebug( "Dropping Special:Contribution row that could not be formatted\n" );
@@ -1122,8 +1117,6 @@ class ContribsPager extends ReverseChronologicalPager {
$ret = Html::rawElement( 'li', array( 'class' => $classes ), $ret ) . "\n";
}
- wfProfileOut( __METHOD__ );
-
return $ret;
}
diff --git a/includes/specials/SpecialDeletedContributions.php b/includes/specials/SpecialDeletedContributions.php
index 68f2c469..9e4bbbe5 100644
--- a/includes/specials/SpecialDeletedContributions.php
+++ b/includes/specials/SpecialDeletedContributions.php
@@ -78,6 +78,53 @@ class DeletedContribsPager extends IndexPager {
);
}
+ /**
+ * This method basically executes the exact same code as the parent class, though with
+ * a hook added, to allow extensions to add additional queries.
+ *
+ * @param string $offset Index offset, inclusive
+ * @param int $limit Exact query limit
+ * @param bool $descending Query direction, false for ascending, true for descending
+ * @return ResultWrapper
+ */
+ function reallyDoQuery( $offset, $limit, $descending ) {
+ $pager = $this;
+
+ $data = array( parent::reallyDoQuery( $offset, $limit, $descending ) );
+
+ // This hook will allow extensions to add in additional queries, nearly
+ // identical to ContribsPager::reallyDoQuery.
+ Hooks::run(
+ 'DeletedContribsPager::reallyDoQuery',
+ array( &$data, $pager, $offset, $limit, $descending )
+ );
+
+ $result = array();
+
+ // loop all results and collect them in an array
+ 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;
+ }
+ }
+
+ // sort results
+ if ( $descending ) {
+ ksort( $result );
+ } else {
+ krsort( $result );
+ }
+
+ // enforce limit
+ $result = array_slice( $result, 0, $limit );
+
+ // get rid of array keys
+ $result = array_values( $result );
+
+ return new FakeResultWrapper( $result );
+ }
+
function getUserCond() {
$condition = array();
@@ -141,6 +188,50 @@ class DeletedContribsPager extends IndexPager {
/**
* Generates each row in the contributions list.
*
+ * @todo This would probably look a lot nicer in a table.
+ * @param stdClass $row
+ * @return string
+ */
+ function formatRow( $row ) {
+ $ret = '';
+ $classes = array();
+
+ /*
+ * There may be more than just revision rows. To make sure that we'll only be processing
+ * revisions here, let's _try_ to build a revision out of our row (without displaying
+ * notices though) and then trying to grab data from the built object. If we succeed,
+ * we're definitely dealing with revision data and we may proceed, if not, we'll leave it
+ * to extensions to subscribe to the hook to parse the row.
+ */
+ wfSuppressWarnings();
+ try {
+ $rev = Revision::newFromArchiveRow( $row );
+ $validRevision = (bool)$rev->getId();
+ } catch ( Exception $e ) {
+ $validRevision = false;
+ }
+ wfRestoreWarnings();
+
+ if ( $validRevision ) {
+ $ret = $this->formatRevisionRow( $row );
+ }
+
+ // Let extensions add data
+ Hooks::run( 'DeletedContributionsLineEnding', array( $this, &$ret, $row, &$classes ) );
+
+ if ( $classes === array() && $ret === '' ) {
+ wfDebug( "Dropping Special:DeletedContribution row that could not be formatted\n" );
+ $ret = "<!-- Could not format Special:DeletedContribution row. -->\n";
+ } else {
+ $ret = Html::rawElement( 'li', array( 'class' => $classes ), $ret ) . "\n";
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Generates each row in the contributions list for archive entries.
+ *
* Contributions which are marked "top" are currently on top of the history.
* For these contributions, a [rollback] link is shown for users with sysop
* privileges. The rollback link restores the most recent version that was not
@@ -150,9 +241,7 @@ class DeletedContribsPager extends IndexPager {
* @param stdClass $row
* @return string
*/
- function formatRow( $row ) {
- wfProfileIn( __METHOD__ );
-
+ function formatRevisionRow( $row ) {
$page = Title::makeTitle( $row->ar_namespace, $row->ar_title );
$rev = new Revision( array(
@@ -256,17 +345,13 @@ class DeletedContribsPager extends IndexPager {
$ret .= " <strong>" . $this->msg( 'rev-deleted-user-contribs' )->escaped() . "</strong>";
}
- $ret = Html::rawElement( 'li', array(), $ret ) . "\n";
-
- wfProfileOut( __METHOD__ );
-
return $ret;
}
/**
* Get the Database object in use
*
- * @return DatabaseBase
+ * @return IDatabase
*/
public function getDatabase() {
return $this->mDb;
@@ -315,7 +400,8 @@ class DeletedContributionsPage extends SpecialPage {
return;
}
- $options['limit'] = $request->getInt( 'limit', $this->getConfig()->get( 'QueryPageDefaultLimit' ) );
+ $options['limit'] = $request->getInt( 'limit',
+ $this->getConfig()->get( 'QueryPageDefaultLimit' ) );
$options['target'] = $target;
$userObj = User::newFromName( $target, false );
@@ -465,7 +551,7 @@ class DeletedContributionsPage extends SpecialPage {
);
}
- wfRunHooks( 'ContributionsToolLinks', array( $id, $nt, &$tools ) );
+ Hooks::run( 'ContributionsToolLinks', array( $id, $nt, &$tools ) );
$links = $this->getLanguage()->pipeList( $tools );
@@ -533,6 +619,8 @@ class DeletedContributionsPage extends SpecialPage {
$f .= "\t" . Html::hidden( $name, $value ) . "\n";
}
+ $this->getOutput()->addModules( 'mediawiki.userSuggest' );
+
$f .= Xml::openElement( 'fieldset' );
$f .= Xml::element( 'legend', array(), $this->msg( 'sp-contributions-search' )->text() );
$f .= Xml::tags(
@@ -546,7 +634,10 @@ class DeletedContributionsPage extends SpecialPage {
'text',
array(
'size' => '20',
- 'required' => ''
+ 'required' => '',
+ 'class' => array(
+ 'mw-autocomplete-user', // used by mediawiki.userSuggest
+ ),
) + ( $options['target'] ? array() : array( 'autofocus' ) )
) . ' ';
$f .= Html::namespaceSelector(
diff --git a/includes/specials/SpecialDiff.php b/includes/specials/SpecialDiff.php
index 77d23173..89c1c021 100644
--- a/includes/specials/SpecialDiff.php
+++ b/includes/specials/SpecialDiff.php
@@ -53,6 +53,7 @@ class SpecialDiff extends RedirectSpecialPage {
$this->mAddedRedirectParams['diff'] = $parts[1];
} else {
// Wrong number of parameters, bail out
+ $this->addHelpLink( 'Help:Diff' );
throw new ErrorPageError( 'nopagetitle', 'nopagetext' );
}
diff --git a/includes/specials/SpecialEditTags.php b/includes/specials/SpecialEditTags.php
new file mode 100644
index 00000000..f41a1f1d
--- /dev/null
+++ b/includes/specials/SpecialEditTags.php
@@ -0,0 +1,460 @@
+<?php
+/**
+ * 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
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Special page for adding and removing change tags to individual revisions.
+ * A lot of this is copied out of SpecialRevisiondelete.
+ *
+ * @ingroup SpecialPage
+ * @since 1.25
+ */
+class SpecialEditTags extends UnlistedSpecialPage {
+ /** @var bool Was the DB modified in this request */
+ protected $wasSaved = false;
+
+ /** @var bool True if the submit button was clicked, and the form was posted */
+ private $submitClicked;
+
+ /** @var array Target ID list */
+ private $ids;
+
+ /** @var Title Title object for target parameter */
+ private $targetObj;
+
+ /** @var string Deletion type, may be revision or logentry */
+ private $typeName;
+
+ /** @var ChangeTagsList Storing the list of items to be tagged */
+ private $revList;
+
+ /** @var bool Whether user is allowed to perform the action */
+ private $isAllowed;
+
+ /** @var string */
+ private $reason;
+
+ public function __construct() {
+ parent::__construct( 'EditTags', 'changetags' );
+ }
+
+ public function execute( $par ) {
+ $this->checkPermissions();
+ $this->checkReadOnly();
+
+ $output = $this->getOutput();
+ $user = $this->getUser();
+ $request = $this->getRequest();
+
+ $this->setHeaders();
+ $this->outputHeader();
+
+ $this->getOutput()->addModules( array( 'mediawiki.special.edittags',
+ 'mediawiki.special.edittags.styles' ) );
+
+ $this->submitClicked = $request->wasPosted() && $request->getBool( 'wpSubmit' );
+
+ // Handle our many different possible input types
+ $ids = $request->getVal( 'ids' );
+ if ( !is_null( $ids ) ) {
+ // Allow CSV from the form hidden field, or a single ID for show/hide links
+ $this->ids = explode( ',', $ids );
+ } else {
+ // Array input
+ $this->ids = array_keys( $request->getArray( 'ids', array() ) );
+ }
+ $this->ids = array_unique( array_filter( $this->ids ) );
+
+ // No targets?
+ if ( count( $this->ids ) == 0 ) {
+ throw new ErrorPageError( 'tags-edit-nooldid-title', 'tags-edit-nooldid-text' );
+ }
+
+ $this->typeName = $request->getVal( 'type' );
+ $this->targetObj = Title::newFromText( $request->getText( 'target' ) );
+
+ // sanity check of parameter
+ switch ( $this->typeName ) {
+ case 'logentry':
+ case 'logging':
+ $this->typeName = 'logentry';
+ break;
+ default:
+ $this->typeName = 'revision';
+ break;
+ }
+
+ // Allow the list type to adjust the passed target
+ // Yuck! Copied straight out of SpecialRevisiondelete, but it does exactly
+ // what we want
+ $this->targetObj = RevisionDeleter::suggestTarget(
+ $this->typeName === 'revision' ? 'revision' : 'logging',
+ $this->targetObj,
+ $this->ids
+ );
+
+ $this->isAllowed = $user->isAllowed( 'changetags' );
+
+ $this->reason = $request->getVal( 'wpReason' );
+ // We need a target page!
+ if ( is_null( $this->targetObj ) ) {
+ $output->addWikiMsg( 'undelete-header' );
+ return;
+ }
+ // Give a link to the logs/hist for this page
+ $this->showConvenienceLinks();
+
+ // Either submit or create our form
+ if ( $this->isAllowed && $this->submitClicked ) {
+ $this->submit( $request );
+ } else {
+ $this->showForm();
+ }
+
+ // Show relevant lines from the tag log
+ $tagLogPage = new LogPage( 'tag' );
+ $output->addHTML( "<h2>" . $tagLogPage->getName()->escaped() . "</h2>\n" );
+ LogEventsList::showLogExtract(
+ $output,
+ 'tag',
+ $this->targetObj,
+ '', /* user */
+ array( 'lim' => 25, 'conds' => array(), 'useMaster' => $this->wasSaved )
+ );
+ }
+
+ /**
+ * Show some useful links in the subtitle
+ */
+ protected function showConvenienceLinks() {
+ // Give a link to the logs/hist for this page
+ if ( $this->targetObj ) {
+ // Also set header tabs to be for the target.
+ $this->getSkin()->setRelevantTitle( $this->targetObj );
+
+ $links = array();
+ $links[] = Linker::linkKnown(
+ SpecialPage::getTitleFor( 'Log' ),
+ $this->msg( 'viewpagelogs' )->escaped(),
+ array(),
+ array(
+ 'page' => $this->targetObj->getPrefixedText(),
+ 'hide_tag_log' => '0',
+ )
+ );
+ if ( !$this->targetObj->isSpecialPage() ) {
+ // Give a link to the page history
+ $links[] = Linker::linkKnown(
+ $this->targetObj,
+ $this->msg( 'pagehist' )->escaped(),
+ array(),
+ array( 'action' => 'history' )
+ );
+ }
+ // Link to Special:Tags
+ $links[] = Linker::linkKnown(
+ SpecialPage::getTitleFor( 'Tags' ),
+ $this->msg( 'tags-edit-manage-link' )->escaped()
+ );
+ // Logs themselves don't have histories or archived revisions
+ $this->getOutput()->addSubtitle( $this->getLanguage()->pipeList( $links ) );
+ }
+ }
+
+ /**
+ * Get the list object for this request
+ * @return ChangeTagsList
+ */
+ protected function getList() {
+ if ( is_null( $this->revList ) ) {
+ $this->revList = ChangeTagsList::factory( $this->typeName, $this->getContext(),
+ $this->targetObj, $this->ids );
+ }
+
+ return $this->revList;
+ }
+
+ /**
+ * Show a list of items that we will operate on, and show a form which allows
+ * the user to modify the tags applied to those items.
+ */
+ protected function showForm() {
+ $userAllowed = true;
+
+ $out = $this->getOutput();
+ // Messages: tags-edit-revision-selected, tags-edit-logentry-selected
+ $out->wrapWikiMsg( "<strong>$1</strong>", array(
+ "tags-edit-{$this->typeName}-selected",
+ $this->getLanguage()->formatNum( count( $this->ids ) ),
+ $this->targetObj->getPrefixedText()
+ ) );
+
+ $this->addHelpLink( 'Help:Tags' );
+ $out->addHTML( "<ul>" );
+
+ $numRevisions = 0;
+ // Live revisions...
+ $list = $this->getList();
+ // @codingStandardsIgnoreStart Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
+ for ( $list->reset(); $list->current(); $list->next() ) {
+ // @codingStandardsIgnoreEnd
+ $item = $list->current();
+ $numRevisions++;
+ $out->addHTML( $item->getHTML() );
+ }
+
+ if ( !$numRevisions ) {
+ throw new ErrorPageError( 'tags-edit-nooldid-title', 'tags-edit-nooldid-text' );
+ }
+
+ $out->addHTML( "</ul>" );
+ // Explanation text
+ $out->wrapWikiMsg( '<p>$1</p>', "tags-edit-{$this->typeName}-explanation" );
+
+ // Show form if the user can submit
+ if ( $this->isAllowed ) {
+ $form = Xml::openElement( 'form', array( 'method' => 'post',
+ 'action' => $this->getPageTitle()->getLocalURL( array( 'action' => 'submit' ) ),
+ 'id' => 'mw-revdel-form-revisions' ) ) .
+ Xml::fieldset( $this->msg( "tags-edit-{$this->typeName}-legend",
+ count( $this->ids ) )->text() ) .
+ $this->buildCheckBoxes() .
+ Xml::openElement( 'table' ) .
+ "<tr>\n" .
+ '<td class="mw-label">' .
+ Xml::label( $this->msg( 'tags-edit-reason' )->text(), 'wpReason' ) .
+ '</td>' .
+ '<td class="mw-input">' .
+ Xml::input(
+ 'wpReason',
+ 60,
+ $this->reason,
+ array( 'id' => 'wpReason', 'maxlength' => 100 )
+ ) .
+ '</td>' .
+ "</tr><tr>\n" .
+ '<td></td>' .
+ '<td class="mw-submit">' .
+ Xml::submitButton( $this->msg( "tags-edit-{$this->typeName}-submit",
+ $numRevisions )->text(), array( 'name' => 'wpSubmit' ) ) .
+ '</td>' .
+ "</tr>\n" .
+ Xml::closeElement( 'table' ) .
+ Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) .
+ Html::hidden( 'target', $this->targetObj->getPrefixedText() ) .
+ Html::hidden( 'type', $this->typeName ) .
+ Html::hidden( 'ids', implode( ',', $this->ids ) ) .
+ Xml::closeElement( 'fieldset' ) . "\n" .
+ Xml::closeElement( 'form' ) . "\n";
+ } else {
+ $form = '';
+ }
+ $out->addHTML( $form );
+ }
+
+ /**
+ * @return string HTML
+ */
+ protected function buildCheckBoxes() {
+ // If there is just one item, provide the user with a multi-select field
+ $list = $this->getList();
+ if ( $list->length() == 1 ) {
+ $list->reset();
+ $tags = $list->current()->getTags();
+ if ( $tags ) {
+ $tags = explode( ',', $tags );
+ } else {
+ $tags = array();
+ }
+
+ $html = '<table id="mw-edittags-tags-selector">';
+ $html .= '<tr><td>' . $this->msg( 'tags-edit-existing-tags' )->escaped() .
+ '</td><td>';
+ if ( $tags ) {
+ $html .= $this->getLanguage()->commaList( array_map( 'htmlspecialchars', $tags ) );
+ } else {
+ $html .= $this->msg( 'tags-edit-existing-tags-none' )->parse();
+ }
+ $html .= '</td></tr>';
+ $tagSelect = $this->getTagSelect( $tags, $this->msg( 'tags-edit-new-tags' )->plain() );
+ $html .= '<tr><td>' . $tagSelect[0] . '</td><td>' . $tagSelect[1];
+ // also output the tags currently applied as a hidden form field, so we
+ // know what to remove from the revision/log entry when the form is submitted
+ $html .= Html::hidden( 'wpExistingTags', implode( ',', $tags ) );
+ $html .= '</td></tr></table>';
+ } else {
+ // Otherwise, use a multi-select field for adding tags, and a list of
+ // checkboxes for removing them
+ $tags = array();
+
+ // @codingStandardsIgnoreStart Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
+ for ( $list->reset(); $list->current(); $list->next() ) {
+ // @codingStandardsIgnoreEnd
+ $currentTags = $list->current()->getTags();
+ if ( $currentTags ) {
+ $tags = array_merge( $tags, explode( ',', $currentTags ) );
+ }
+ }
+ $tags = array_unique( $tags );
+
+ $html = '<table id="mw-edittags-tags-selector-multi"><tr><td>';
+ $tagSelect = $this->getTagSelect( array(), $this->msg( 'tags-edit-add' )->plain() );
+ $html .= '<p>' . $tagSelect[0] . '</p>' . $tagSelect[1] . '</td><td>';
+ $html .= Xml::element( 'p', null, $this->msg( 'tags-edit-remove' )->plain() );
+ $html .= Xml::checkLabel( $this->msg( 'tags-edit-remove-all-tags' )->plain(),
+ 'wpRemoveAllTags', 'mw-edittags-remove-all' );
+ $i = 0; // used for generating checkbox IDs only
+ foreach ( $tags as $tag ) {
+ $html .= Xml::element( 'br' ) . "\n" . Xml::checkLabel( $tag,
+ 'wpTagsToRemove[]', 'mw-edittags-remove-' . $i++, false, array(
+ 'value' => $tag,
+ 'class' => 'mw-edittags-remove-checkbox',
+ ) );
+ }
+ $html .= '</td></tr></table>';
+ }
+
+ return $html;
+ }
+
+ /**
+ * Returns a <select multiple> element with a list of change tags that can be
+ * applied by users.
+ *
+ * @param array $selectedTags The tags that should be preselected in the
+ * list. Any tags in this list, but not in the list returned by
+ * ChangeTags::listExplicitlyDefinedTags, will be appended to the <select>
+ * element.
+ * @param string $label The text of a <label> to precede the <select>
+ * @return array HTML <label> element at index 0, HTML <select> element at
+ * index 1
+ */
+ protected function getTagSelect( $selectedTags, $label ) {
+ $result = array();
+ $result[0] = Xml::label( $label, 'mw-edittags-tag-list' );
+ $result[1] = Xml::openElement( 'select', array(
+ 'name' => 'wpTagList[]',
+ 'id' => 'mw-edittags-tag-list',
+ 'multiple' => 'multiple',
+ 'size' => '8',
+ ) );
+
+ $tags = ChangeTags::listExplicitlyDefinedTags();
+ $tags = array_unique( array_merge( $tags, $selectedTags ) );
+ foreach ( $tags as $tag ) {
+ $result[1] .= Xml::option( $tag, $tag, in_array( $tag, $selectedTags ) );
+ }
+
+ $result[1] .= Xml::closeElement( 'select' );
+ return $result;
+ }
+
+ /**
+ * UI entry point for form submission.
+ * @throws PermissionsError
+ * @return bool
+ */
+ protected function submit() {
+ // Check edit token on submission
+ $request = $this->getRequest();
+ $token = $request->getVal( 'wpEditToken' );
+ if ( $this->submitClicked && !$this->getUser()->matchEditToken( $token ) ) {
+ $this->getOutput()->addWikiMsg( 'sessionfailure' );
+ return false;
+ }
+
+ // Evaluate incoming request data
+ $tagList = $request->getArray( 'wpTagList' );
+ if ( is_null( $tagList ) ) {
+ $tagList = array();
+ }
+ $existingTags = $request->getVal( 'wpExistingTags' );
+ if ( is_null( $existingTags ) || $existingTags === '' ) {
+ $existingTags = array();
+ } else {
+ $existingTags = explode( ',', $existingTags );
+ }
+
+ if ( count( $this->ids ) > 1 ) {
+ // multiple revisions selected
+ $tagsToAdd = $tagList;
+ if ( $request->getBool( 'wpRemoveAllTags' ) ) {
+ $tagsToRemove = $existingTags;
+ } else {
+ $tagsToRemove = $request->getArray( 'wpTagsToRemove' );
+ }
+ } else {
+ // single revision selected
+ // The user tells us which tags they want associated to the revision.
+ // We have to figure out which ones to add, and which to remove.
+ $tagsToAdd = array_diff( $tagList, $existingTags );
+ $tagsToRemove = array_diff( $existingTags, $tagList );
+ }
+
+ if ( !$tagsToAdd && !$tagsToRemove ) {
+ $status = Status::newFatal( 'tags-edit-none-selected' );
+ } else {
+ $status = $this->getList()->updateChangeTagsOnAll( $tagsToAdd,
+ $tagsToRemove, null, $this->reason, $this->getUser() );
+ }
+
+ if ( $status->isGood() ) {
+ $this->success();
+ return true;
+ } else {
+ $this->failure( $status );
+ return false;
+ }
+ }
+
+ /**
+ * Report that the submit operation succeeded
+ */
+ protected function success() {
+ $this->getOutput()->setPageTitle( $this->msg( 'actioncomplete' ) );
+ $this->getOutput()->wrapWikiMsg( "<div class=\"successbox\">\n$1\n</div>",
+ 'tags-edit-success' );
+ $this->wasSaved = true;
+ $this->revList->reloadFromMaster();
+ $this->reason = ''; // no need to spew the reason back at the user
+ $this->showForm();
+ }
+
+ /**
+ * Report that the submit operation failed
+ * @param Status $status
+ */
+ protected function failure( $status ) {
+ $this->getOutput()->setPageTitle( $this->msg( 'actionfailed' ) );
+ $this->getOutput()->addWikiText( '<div class="errorbox">' .
+ $status->getWikiText( 'tags-edit-failure' ) .
+ '</div>'
+ );
+ $this->showForm();
+ }
+
+ public function getDescription() {
+ return $this->msg( 'tags-edit-title' )->text();
+ }
+
+ protected function getGroupName() {
+ return 'pagetools';
+ }
+}
diff --git a/includes/specials/SpecialEditWatchlist.php b/includes/specials/SpecialEditWatchlist.php
index 3656b9cc..910fe259 100644
--- a/includes/specials/SpecialEditWatchlist.php
+++ b/includes/specials/SpecialEditWatchlist.php
@@ -134,22 +134,17 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
}
/**
- * Return an array of subpages beginning with $search that this special page will accept.
+ * Return an array of subpages that this special page will accept.
*
- * @param string $search Prefix to search for
- * @param int $limit Maximum number of results to return
- * @return string[] Matching subpages
+ * @see also SpecialWatchlist::getSubpagesForPrefixSearch
+ * @return string[] subpages
*/
- public function prefixSearchSubpages( $search, $limit = 10 ) {
- return self::prefixSearchArray(
- $search,
- $limit,
- // SpecialWatchlist uses SpecialEditWatchlist::getMode, so new types should be added
- // here and there - no 'edit' here, because that the default for this page
- array(
- 'clear',
- 'raw',
- )
+ public function getSubpagesForPrefixSearch() {
+ // SpecialWatchlist uses SpecialEditWatchlist::getMode, so new types should be added
+ // here and there - no 'edit' here, because that the default for this page
+ return array(
+ 'clear',
+ 'raw',
);
}
@@ -261,7 +256,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
// Do a batch existence check
$batch = new LinkBatch();
if ( count( $titles ) >= 100 ) {
- $output = wfMessage( 'watchlistedit-too-many' )->parse();
+ $output = $this->msg( 'watchlistedit-too-many' )->parse();
return;
}
foreach ( $titles as $title ) {
@@ -349,7 +344,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
*/
protected function getWatchlistInfo() {
$titles = array();
- $dbr = wfGetDB( DB_MASTER );
+ $dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select(
array( 'watchlist' ),
@@ -518,7 +513,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
);
$page = WikiPage::factory( $title );
- wfRunHooks( 'UnwatchArticleComplete', array( $this->getUser(), &$page ) );
+ Hooks::run( 'UnwatchArticleComplete', array( $this->getUser(), &$page ) );
}
}
}
@@ -556,7 +551,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
// Allow subscribers to manipulate the list of watched pages (or use it
// to preload lots of details at once)
$watchlistInfo = $this->getWatchlistInfo();
- wfRunHooks(
+ Hooks::run(
'WatchlistEditorBeforeFormRender',
array( &$watchlistInfo )
);
@@ -609,6 +604,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
$context->setTitle( $this->getPageTitle() ); // Remove subpage
$form = new EditWatchlistNormalHTMLForm( $fields, $context );
$form->setSubmitTextMsg( 'watchlistedit-normal-submit' );
+ $form->setSubmitDestructive();
# Used message keys:
# 'accesskey-watchlistedit-normal-submit', 'tooltip-watchlistedit-normal-submit'
$form->setSubmitTooltip( 'watchlistedit-normal-submit' );
@@ -628,7 +624,10 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
private function buildRemoveLine( $title ) {
$link = Linker::link( $title );
- $tools['talk'] = Linker::link( $title->getTalkPage(), $this->msg( 'talkpagelinktext' )->escaped() );
+ $tools['talk'] = Linker::link(
+ $title->getTalkPage(),
+ $this->msg( 'talkpagelinktext' )->escaped()
+ );
if ( $title->exists() ) {
$tools['history'] = Linker::linkKnown(
@@ -646,7 +645,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
);
}
- wfRunHooks(
+ Hooks::run(
'WatchlistEditorBuildRemoveLine',
array( &$tools, $title, $title->isRedirect(), $this->getSkin(), &$link )
);
@@ -701,6 +700,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
$form->setWrapperLegendMsg( 'watchlistedit-clear-legend' );
$form->addHeaderText( $this->msg( 'watchlistedit-clear-explain' )->parse() );
$form->setSubmitCallback( array( $this, 'submitClear' ) );
+ $form->setSubmitDestructive();
return $form;
}
@@ -760,7 +760,7 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
return Html::rawElement(
'span',
array( 'class' => 'mw-watchlist-toollinks' ),
- wfMessage( 'parentheses', $wgLang->pipeList( $tools ) )->text()
+ wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $tools ) )->escaped()
);
}
}
diff --git a/includes/specials/SpecialEmailuser.php b/includes/specials/SpecialEmailuser.php
index 20532a92..c55fa94c 100644
--- a/includes/specials/SpecialEmailuser.php
+++ b/includes/specials/SpecialEmailuser.php
@@ -160,7 +160,7 @@ class SpecialEmailUser extends UnlistedSpecialPage {
$form->setWrapperLegendMsg( 'email-legend' );
$form->loadData();
- if ( !wfRunHooks( 'EmailUserForm', array( &$form ) ) ) {
+ if ( !Hooks::run( 'EmailUserForm', array( &$form ) ) ) {
return;
}
@@ -243,8 +243,8 @@ class SpecialEmailUser extends UnlistedSpecialPage {
$hookErr = false;
- wfRunHooks( 'UserCanSendEmail', array( &$user, &$hookErr ) );
- wfRunHooks( 'EmailUserPermissionsErrors', array( $user, $editToken, &$hookErr ) );
+ Hooks::run( 'UserCanSendEmail', array( &$user, &$hookErr ) );
+ Hooks::run( 'EmailUserPermissionsErrors', array( $user, $editToken, &$hookErr ) );
if ( $hookErr ) {
return $hookErr;
@@ -324,7 +324,7 @@ class SpecialEmailUser extends UnlistedSpecialPage {
$from->name, $to->name )->inContentLanguage()->text();
$error = '';
- if ( !wfRunHooks( 'EmailUser', array( &$to, &$from, &$subject, &$text, &$error ) ) ) {
+ if ( !Hooks::run( 'EmailUser', array( &$to, &$from, &$subject, &$text, &$error ) ) ) {
return $error;
}
@@ -367,12 +367,12 @@ class SpecialEmailUser extends UnlistedSpecialPage {
if ( $data['CCMe'] && $to != $from ) {
$cc_subject = $context->msg( 'emailccsubject' )->rawParams(
$target->getName(), $subject )->text();
- wfRunHooks( 'EmailUserCC', array( &$from, &$from, &$cc_subject, &$text ) );
+ Hooks::run( 'EmailUserCC', array( &$from, &$from, &$cc_subject, &$text ) );
$ccStatus = UserMailer::send( $from, $from, $cc_subject, $text );
$status->merge( $ccStatus );
}
- wfRunHooks( 'EmailUserComplete', array( $to, $from, $subject, $text ) );
+ Hooks::run( 'EmailUserComplete', array( $to, $from, $subject, $text ) );
return $status;
}
diff --git a/includes/specials/SpecialExpandTemplates.php b/includes/specials/SpecialExpandTemplates.php
index 62f957fc..b7582e6c 100644
--- a/includes/specials/SpecialExpandTemplates.php
+++ b/includes/specials/SpecialExpandTemplates.php
@@ -77,7 +77,7 @@ class SpecialExpandTemplates extends SpecialPage {
$options->setMaxIncludeSize( self::MAX_INCLUDE_SIZE );
if ( $this->generateXML ) {
- $wgParser->startExternalParse( $title, $options, OT_PREPROCESS );
+ $wgParser->startExternalParse( $title, $options, Parser::OT_PREPROCESS );
$dom = $wgParser->preprocessToDom( $input );
if ( method_exists( $dom, 'saveXML' ) ) {
@@ -154,7 +154,7 @@ class SpecialExpandTemplates extends SpecialPage {
'contexttitle',
60,
$title,
- array( 'autofocus' => true )
+ array( 'autofocus' => '', 'class' => 'mw-ui-input-inline' )
) . '</p>';
$form .= '<p>' . Xml::label(
$this->msg( 'expand_templates_input' )->text(),
@@ -278,6 +278,7 @@ class SpecialExpandTemplates extends SpecialPage {
) ) );
$out->addParserOutputContent( $pout );
$out->addHTML( Html::closeElement( 'div' ) );
+ $out->setCategoryLinks( $pout->getCategories() );
}
protected function getGroupName() {
diff --git a/includes/specials/SpecialExport.php b/includes/specials/SpecialExport.php
index 38c52a01..c30d962a 100644
--- a/includes/specials/SpecialExport.php
+++ b/includes/specials/SpecialExport.php
@@ -182,13 +182,20 @@ class SpecialExport extends SpecialPage {
$out = $this->getOutput();
$out->addWikiMsg( 'exporttext' );
+ if ( $page == '' ) {
+ $categoryName = $request->getText( 'catname' );
+ } else {
+ $categoryName = '';
+ }
+
$form = Xml::openElement( 'form', array( 'method' => 'post',
'action' => $this->getPageTitle()->getLocalURL( 'action=submit' ) ) );
$form .= Xml::inputLabel(
$this->msg( 'export-addcattext' )->text(),
'catname',
'catname',
- 40
+ 40,
+ $categoryName
) . '&#160;';
$form .= Xml::submitButton(
$this->msg( 'export-addcat' )->text(),
diff --git a/includes/specials/SpecialFileDuplicateSearch.php b/includes/specials/SpecialFileDuplicateSearch.php
index fc26c903..da79bb81 100644
--- a/includes/specials/SpecialFileDuplicateSearch.php
+++ b/includes/specials/SpecialFileDuplicateSearch.php
@@ -110,25 +110,31 @@ class FileDuplicateSearchPage extends QueryPage {
$out = $this->getOutput();
# Create the input form
- $out->addHTML(
- Html::openElement(
- 'form',
- array( 'id' => 'fileduplicatesearch', 'method' => 'get', 'action' => wfScript() )
- ) . "\n" .
- Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) . "\n" .
- Html::openElement( 'fieldset' ) . "\n" .
- Html::element( 'legend', null, $this->msg( 'fileduplicatesearch-legend' )->text() ) . "\n" .
- Xml::inputLabel(
- $this->msg( 'fileduplicatesearch-filename' )->text(),
- 'filename',
- 'filename',
- 50,
- $this->filename
- ) . "\n" .
- Xml::submitButton( $this->msg( 'fileduplicatesearch-submit' )->text() ) . "\n" .
- Html::closeElement( 'fieldset' ) . "\n" .
- Html::closeElement( 'form' )
+ $formFields = array(
+ 'filename' => array(
+ 'type' => 'text',
+ 'name' => 'filename',
+ 'label-message' => 'fileduplicatesearch-filename',
+ 'id' => 'filename',
+ 'size' => 50,
+ 'value' => $this->filename,
+ 'cssclass' => 'mw-ui-input-inline'
+ ),
+ );
+ $hiddenFields = array(
+ 'title' => $this->getPageTitle()->getPrefixedDBKey(),
);
+ $htmlForm = HTMLForm::factory( 'inline', $formFields, $this->getContext() );
+ $htmlForm->addHiddenFields( $hiddenFields );
+ $htmlForm->setAction( wfScript() );
+ $htmlForm->setMethod( 'get' );
+ $htmlForm->setSubmitProgressive();
+ $htmlForm->setSubmitTextMsg( $this->msg( 'fileduplicatesearch-submit' ) );
+ $htmlForm->setWrapperLegendMsg( 'fileduplicatesearch-legend' );
+
+ // The form should be visible always, even if it was submitted (e.g. to perform another action).
+ // To bypass the callback validation of HTMLForm, use prepareForm() and displayForm().
+ $htmlForm->prepareForm()->displayForm( false );
if ( $this->file ) {
$this->hash = $this->file->getSha1();
@@ -196,7 +202,7 @@ class FileDuplicateSearchPage extends QueryPage {
*
* @param Skin $skin
* @param File $result
- * @return string
+ * @return string HTML
*/
function formatResult( $skin, $result ) {
global $wgContLang;
@@ -204,15 +210,14 @@ class FileDuplicateSearchPage extends QueryPage {
$nt = $result->getTitle();
$text = $wgContLang->convert( $nt->getText() );
$plink = Linker::link(
- Title::newFromText( $nt->getPrefixedText() ),
- $text
+ $nt,
+ htmlspecialchars( $text )
);
$userText = $result->getUser( 'text' );
if ( $result->isLocal() ) {
$userId = $result->getUser( 'id' );
$user = Linker::userLink( $userId, $userText );
- $user .= $this->getContext()->msg( 'word-separator' )->plain();
$user .= '<span style="white-space: nowrap;">';
$user .= Linker::userToolLinks( $userId, $userText );
$user .= '</span>';
@@ -220,7 +225,8 @@ class FileDuplicateSearchPage extends QueryPage {
$user = htmlspecialchars( $userText );
}
- $time = $this->getLanguage()->userTimeAndDate( $result->getTimestamp(), $this->getUser() );
+ $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
+ $result->getTimestamp(), $this->getUser() ) );
return "$plink . . $user . . $time";
}
diff --git a/includes/specials/SpecialFilepath.php b/includes/specials/SpecialFilepath.php
index 5860f636..93232117 100644
--- a/includes/specials/SpecialFilepath.php
+++ b/includes/specials/SpecialFilepath.php
@@ -2,7 +2,6 @@
/**
* Implements Special:Filepath
*
- * @section LICENSE
* 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
diff --git a/includes/specials/SpecialImport.php b/includes/specials/SpecialImport.php
index eab4784c..af869647 100644
--- a/includes/specials/SpecialImport.php
+++ b/includes/specials/SpecialImport.php
@@ -30,6 +30,7 @@
* @ingroup SpecialPage
*/
class SpecialImport extends SpecialPage {
+ private $sourceName = false;
private $interwiki = false;
private $subproject;
private $fullInterwikiPrefix;
@@ -46,17 +47,20 @@ class SpecialImport extends SpecialPage {
*/
public function __construct() {
parent::__construct( 'Import', 'import' );
- $this->namespace = $this->getConfig()->get( 'ImportTargetNamespace' );
}
/**
* Execute
* @param string|null $par
+ * @throws PermissionsError
+ * @throws ReadOnlyError
*/
function execute( $par ) {
$this->setHeaders();
$this->outputHeader();
+ $this->namespace = $this->getConfig()->get( 'ImportTargetNamespace' );
+
$this->getOutput()->addModules( 'mediawiki.special.import' );
$user = $this->getUser();
@@ -98,7 +102,7 @@ class SpecialImport extends SpecialPage {
$isUpload = false;
$request = $this->getRequest();
$this->namespace = $request->getIntOrNull( 'namespace' );
- $sourceName = $request->getVal( "source" );
+ $this->sourceName = $request->getVal( "source" );
$this->logcomment = $request->getText( 'log-comment' );
$this->pageLinkDepth = $this->getConfig()->get( 'ExportMaxLinkDepth' ) == 0
@@ -109,14 +113,14 @@ class SpecialImport extends SpecialPage {
$user = $this->getUser();
if ( !$user->matchEditToken( $request->getVal( 'editToken' ) ) ) {
$source = Status::newFatal( 'import-token-mismatch' );
- } elseif ( $sourceName == 'upload' ) {
+ } elseif ( $this->sourceName == 'upload' ) {
$isUpload = true;
if ( $user->isAllowed( 'importupload' ) ) {
$source = ImportStreamSource::newFromUpload( "xmlimport" );
} else {
throw new PermissionsError( 'importupload' );
}
- } elseif ( $sourceName == "interwiki" ) {
+ } elseif ( $this->sourceName == "interwiki" ) {
if ( !$user->isAllowed( 'import' ) ) {
throw new PermissionsError( 'import' );
}
@@ -156,7 +160,7 @@ class SpecialImport extends SpecialPage {
array( 'importfailed', $source->getWikiText() )
);
} else {
- $importer = new WikiImporter( $source->value );
+ $importer = new WikiImporter( $source->value, $this->getConfig() );
if ( !is_null( $this->namespace ) ) {
$importer->setTargetNamespace( $this->namespace );
}
@@ -190,7 +194,7 @@ class SpecialImport extends SpecialPage {
$reporter->open();
try {
$importer->doImport();
- } catch ( MWException $e ) {
+ } catch ( Exception $e ) {
$exception = $e;
}
$result = $reporter->close();
@@ -250,7 +254,8 @@ class SpecialImport extends SpecialPage {
Xml::label( $this->msg( 'import-comment' )->text(), 'mw-import-comment' ) .
"</td>
<td class='mw-input'>" .
- Xml::input( 'log-comment', 50, '',
+ Xml::input( 'log-comment', 50,
+ ( $this->sourceName == 'upload' ? $this->logcomment : '' ),
array( 'id' => 'mw-import-comment', 'type' => 'text' ) ) . ' ' .
"</td>
</tr>
@@ -430,7 +435,8 @@ class SpecialImport extends SpecialPage {
Xml::label( $this->msg( 'import-comment' )->text(), 'mw-interwiki-comment' ) .
"</td>
<td class='mw-input'>" .
- Xml::input( 'log-comment', 50, '',
+ Xml::input( 'log-comment', 50,
+ ( $this->sourceName == 'interwiki' ? $this->logcomment : '' ),
array( 'id' => 'mw-interwiki-comment', 'type' => 'text' ) ) . ' ' .
"</td>
</tr>
@@ -515,13 +521,14 @@ class ImportReporter extends ContextSource {
/**
* @param Title $title
- * @param Title $origTitle
+ * @param ForeignTitle $foreignTitle
* @param int $revisionCount
* @param int $successCount
* @param array $pageInfo
* @return void
*/
- function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ) {
+ function reportPage( $title, $foreignTitle, $revisionCount,
+ $successCount, $pageInfo ) {
$args = func_get_args();
call_user_func_array( $this->mOriginalPageOutCallback, $args );
@@ -539,7 +546,6 @@ class ImportReporter extends ContextSource {
"</li>\n"
);
- $log = new LogPage( 'import' );
if ( $this->mIsUpload ) {
$detail = $this->msg( 'import-logentry-upload-detail' )->numParams(
$successCount )->inContentLanguage()->text();
@@ -547,19 +553,26 @@ class ImportReporter extends ContextSource {
$detail .= $this->msg( 'colon-separator' )->inContentLanguage()->text()
. $this->reason;
}
- $log->addEntry( 'upload', $title, $detail, array(), $this->getUser() );
+ $action = 'upload';
} else {
$interwiki = '[[:' . $this->mInterwiki . ':' .
- $origTitle->getPrefixedText() . ']]';
+ $foreignTitle->getFullText() . ']]';
$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;
}
- $log->addEntry( 'interwiki', $title, $detail, array(), $this->getUser() );
+ $action = 'interwiki';
}
+ $logEntry = new ManualLogEntry( 'import', $action );
+ $logEntry->setTarget( $title );
+ $logEntry->setComment( $detail );
+ $logEntry->setPerformer( $this->getUser() );
+ $logid = $logEntry->insert();
+ $logEntry->publish( $logid );
+
$comment = $detail; // quick
$dbw = wfGetDB( DB_MASTER );
$latest = $title->getLatestRevID();
@@ -576,7 +589,7 @@ class ImportReporter extends ContextSource {
$page = WikiPage::factory( $title );
# Update page record
$page->updateRevisionOn( $dbw, $nullRevision );
- wfRunHooks(
+ Hooks::run(
'NewRevisionFromEditComplete',
array( $page, $nullRevision, $latest, $this->getUser() )
);
diff --git a/includes/specials/SpecialJavaScriptTest.php b/includes/specials/SpecialJavaScriptTest.php
index 7d745a50..ecb166a4 100644
--- a/includes/specials/SpecialJavaScriptTest.php
+++ b/includes/specials/SpecialJavaScriptTest.php
@@ -150,12 +150,11 @@ class SpecialJavaScriptTest extends SpecialPage {
*/
private function viewQUnit() {
$out = $this->getOutput();
- $testConfig = $this->getConfig()->get( 'JavaScriptTestConfig' );
$modules = $out->getResourceLoader()->getTestModuleNames( 'qunit' );
$summary = $this->msg( 'javascripttest-qunit-intro' )
- ->params( $testConfig['qunit']['documentation'] )
+ ->params( 'https://www.mediawiki.org/wiki/Manual:JavaScript_unit_testing' )
->parseAsBlock();
$baseHtml = <<<HTML
@@ -164,13 +163,6 @@ class SpecialJavaScriptTest extends SpecialPage {
</div>
HTML;
- // Used in ./tests/qunit/data/testrunner.js, see also documentation of
- // $wgJavaScriptTestConfig in DefaultSettings.php
- $out->addJsConfigVars(
- 'QUnitTestSwarmInjectJSPath',
- $testConfig['qunit']['testswarm-injectjs']
- );
-
$out->addHtml( $this->wrapSummaryHtml( $summary ) . $baseHtml );
// The testrunner configures QUnit and essentially depends on it. However, test suites
@@ -200,7 +192,6 @@ HTML;
*/
private function exportQUnit() {
$out = $this->getOutput();
-
$out->disable();
$rl = $out->getResourceLoader();
@@ -251,9 +242,13 @@ HTML;
'debug' => ResourceLoader::inDebugMode() ? 'true' : 'false',
) );
- $styles = $out->makeResourceLoaderLink( 'jquery.qunit', ResourceLoaderModule::TYPE_STYLES, false );
+ $styles = $out->makeResourceLoaderLink(
+ 'jquery.qunit', ResourceLoaderModule::TYPE_STYLES, false
+ );
// Use 'raw' since this is a plain HTML page without ResourceLoader
- $scripts = $out->makeResourceLoaderLink( 'jquery.qunit', ResourceLoaderModule::TYPE_SCRIPTS, false, array( 'raw' => 'true' ) );
+ $scripts = $out->makeResourceLoaderLink(
+ 'jquery.qunit', ResourceLoaderModule::TYPE_SCRIPTS, false, array( 'raw' => 'true' )
+ );
$head = trim( $styles['html'] . $scripts['html'] );
$html = <<<HTML
@@ -269,18 +264,12 @@ HTML;
}
/**
- * Return an array of subpages beginning with $search that this special page will accept.
+ * Return an array of subpages that this special page will accept.
*
- * @param string $search Prefix to search for
- * @param int $limit Maximum number of results to return
- * @return string[] Matching subpages
+ * @return string[] subpages
*/
- public function prefixSearchSubpages( $search, $limit = 10 ) {
- return self::prefixSearchArray(
- $search,
- $limit,
- self::$frameworks
- );
+ public function getSubpagesForPrefixSearch() {
+ return self::$frameworks;
}
protected function getGroupName() {
diff --git a/includes/specials/SpecialLinkSearch.php b/includes/specials/SpecialLinkSearch.php
index 371469bb..75ff8f30 100644
--- a/includes/specials/SpecialLinkSearch.php
+++ b/includes/specials/SpecialLinkSearch.php
@@ -27,6 +27,8 @@
* @ingroup SpecialPage
*/
class LinkSearchPage extends QueryPage {
+ /** @var array|bool */
+ private $mungedQuery = false;
/**
* @var PageLinkRenderer
@@ -66,8 +68,9 @@ class LinkSearchPage extends QueryPage {
* This allows for dependency injection even though we don't control object creation.
*/
private function initServices() {
+ global $wgLanguageCode;
if ( !$this->linkRenderer ) {
- $lang = $this->getContext()->getLanguage();
+ $lang = Language::factory( $wgLanguageCode );
$titleFormatter = new MediaWikiTitleCodec( $lang, GenderCache::singleton() );
$this->linkRenderer = new MediaWikiPageLinkRenderer( $titleFormatter );
}
@@ -88,7 +91,7 @@ class LinkSearchPage extends QueryPage {
$request = $this->getRequest();
$target = $request->getVal( 'target', $par );
- $namespace = $request->getIntorNull( 'namespace', null );
+ $namespace = $request->getIntOrNull( 'namespace', null );
$protocols_list = array();
foreach ( $this->getConfig()->get( 'UrlProtocols' ) as $prot ) {
@@ -162,7 +165,7 @@ class LinkSearchPage extends QueryPage {
'namespace' => $namespace,
'protocol' => $protocol ) );
parent::execute( $par );
- if ( $this->mMungedQuery === false ) {
+ if ( $this->mungedQuery === false ) {
$out->addWikiMsg( 'linksearch-error' );
}
}
@@ -221,13 +224,13 @@ class LinkSearchPage extends QueryPage {
$dbr = wfGetDB( DB_SLAVE );
// strip everything past first wildcard, so that
// index-based-only lookup would be done
- list( $this->mMungedQuery, $clause ) = self::mungeQuery( $this->mQuery, $this->mProt );
- if ( $this->mMungedQuery === false ) {
+ list( $this->mungedQuery, $clause ) = self::mungeQuery( $this->mQuery, $this->mProt );
+ if ( $this->mungedQuery === false ) {
// Invalid query; return no results
return array( 'tables' => 'page', 'fields' => 'page_id', 'conds' => '0=1' );
}
- $stripped = LinkFilter::keepOneWildcard( $this->mMungedQuery );
+ $stripped = LinkFilter::keepOneWildcard( $this->mungedQuery );
$like = $dbr->buildLike( $stripped );
$retval = array(
'tables' => array( 'page', 'externallinks' ),
@@ -252,6 +255,25 @@ class LinkSearchPage extends QueryPage {
}
/**
+ * Pre-fill the link cache
+ *
+ * @param IDatabase $db
+ * @param ResultWrapper $res
+ */
+ function preprocessResults( $db, $res ) {
+ if ( $res->numRows() > 0 ) {
+ $linkBatch = new LinkBatch();
+
+ foreach ( $res as $row ) {
+ $linkBatch->add( $row->namespace, $row->title );
+ }
+
+ $res->seek( 0 );
+ $linkBatch->execute();
+ }
+ }
+
+ /**
* @param Skin $skin
* @param object $result Result row
* @return string
@@ -267,24 +289,6 @@ class LinkSearchPage extends QueryPage {
}
/**
- * Override to check query validity.
- *
- * @param mixed $offset Numerical offset or false for no offset
- * @param mixed $limit Numerical limit or false for no limit
- */
- function doQuery( $offset = false, $limit = false ) {
- list( $this->mMungedQuery, ) = LinkSearchPage::mungeQuery( $this->mQuery, $this->mProt );
- if ( $this->mMungedQuery === false ) {
- $this->getOutput()->addWikiMsg( 'linksearch-error' );
- } else {
- // For debugging
- // Generates invalid xhtml with patterns that contain --
- //$this->getOutput()->addHTML( "\n<!-- " . htmlspecialchars( $this->mMungedQuery ) . " -->\n" );
- parent::doQuery( $offset, $limit );
- }
- }
-
- /**
* Override to squash the ORDER BY.
* We do a truncated index search, so the optimizer won't trust
* it as good enough for optimizing sort. The implicit ordering
diff --git a/includes/specials/SpecialListDuplicatedFiles.php b/includes/specials/SpecialListDuplicatedFiles.php
index 26672706..1e3dff6f 100644
--- a/includes/specials/SpecialListDuplicatedFiles.php
+++ b/includes/specials/SpecialListDuplicatedFiles.php
@@ -71,7 +71,7 @@ class ListDuplicatedFilesPage extends QueryPage {
/**
* Pre-fill the link cache
*
- * @param DatabaseBase $db
+ * @param IDatabase $db
* @param ResultWrapper $res
*/
function preprocessResults( $db, $res ) {
diff --git a/includes/specials/SpecialListfiles.php b/includes/specials/SpecialListfiles.php
index 04a83c8f..d4b45fb3 100644
--- a/includes/specials/SpecialListfiles.php
+++ b/includes/specials/SpecialListfiles.php
@@ -87,7 +87,7 @@ class ImageListPager extends TablePager {
$this->mIncluding = $including;
$this->mShowAll = $showAll;
- if ( $userName ) {
+ if ( $userName !== null && $userName !== '' ) {
$nt = Title::newFromText( $userName, NS_USER );
if ( !is_null( $nt ) ) {
$this->mUserName = $nt->getText();
@@ -203,7 +203,9 @@ class ImageListPager extends TablePager {
} else {
return false;
}
- } elseif ( $this->getConfig()->get( 'MiserMode' ) && $this->mShowAll /* && mUserName === null */ ) {
+ } elseif ( $this->getConfig()->get( 'MiserMode' )
+ && $this->mShowAll /* && mUserName === null */
+ ) {
// no oi_timestamp index, so only alphabetical sorting in this case.
if ( $field === 'img_name' ) {
return true;
@@ -300,6 +302,7 @@ class ImageListPager extends TablePager {
* @param int $limit
* @param bool $asc
* @return array
+ * @throws MWException
*/
function reallyDoQuery( $offset, $limit, $asc ) {
$prevTableName = $this->mTableName;
@@ -422,7 +425,7 @@ class ImageListPager extends TablePager {
function formatValue( $field, $value ) {
switch ( $field ) {
case 'thumb':
- $opt = array( 'time' => $this->mCurrentRow->img_timestamp );
+ $opt = array( 'time' => wfTimestamp( TS_MW, $this->mCurrentRow->img_timestamp ) );
$file = RepoGroup::singleton()->getLocalRepo()->findFile( $value, $opt );
// If statement for paranoia
if ( $file ) {
@@ -519,6 +522,7 @@ class ImageListPager extends TablePager {
);
}
+ $this->getOutput()->addModules( 'mediawiki.userSuggest' );
$fields['user'] = array(
'type' => 'text',
'name' => 'user',
@@ -527,6 +531,7 @@ class ImageListPager extends TablePager {
'default' => $this->mUserName,
'size' => '40',
'maxlength' => '255',
+ 'cssclass' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
);
$fields['ilshowall'] = array(
@@ -541,6 +546,7 @@ class ImageListPager extends TablePager {
unset( $query['title'] );
unset( $query['limit'] );
unset( $query['ilsearch'] );
+ unset( $query['ilshowall'] );
unset( $query['user'] );
$form = new HTMLForm( $fields, $this->getContext() );
diff --git a/includes/specials/SpecialListgrouprights.php b/includes/specials/SpecialListgrouprights.php
index 5bae28f0..828a93b9 100644
--- a/includes/specials/SpecialListgrouprights.php
+++ b/includes/specials/SpecialListgrouprights.php
@@ -86,13 +86,14 @@ class SpecialListGroupRights extends SpecialPage {
$grouppageLocalized = !$msg->isBlank() ?
$msg->text() :
MWNamespace::getCanonicalName( NS_PROJECT ) . ':' . $groupname;
+ $grouppageLocalizedTitle = Title::newFromText( $grouppageLocalized );
- if ( $group == '*' ) {
- // Do not make a link for the generic * group
+ if ( $group == '*' || !$grouppageLocalizedTitle ) {
+ // Do not make a link for the generic * group or group with invalid group page
$grouppage = htmlspecialchars( $groupnameLocalized );
} else {
$grouppage = Linker::link(
- Title::newFromText( $grouppageLocalized ),
+ $grouppageLocalizedTitle,
htmlspecialchars( $groupnameLocalized )
);
}
@@ -235,20 +236,18 @@ class SpecialListGroupRights extends SpecialPage {
foreach ( $permissions as $permission => $granted ) {
//show as granted only if it isn't revoked to prevent duplicate display of permissions
if ( $granted && ( !isset( $revoke[$permission] ) || !$revoke[$permission] ) ) {
- $description = $this->msg( 'listgrouprights-right-display',
+ $r[] = $this->msg( 'listgrouprights-right-display',
User::getRightDescription( $permission ),
'<span class="mw-listgrouprights-right-name">' . $permission . '</span>'
)->parse();
- $r[] = $description;
}
}
foreach ( $revoke as $permission => $revoked ) {
if ( $revoked ) {
- $description = $this->msg( 'listgrouprights-right-revoked',
+ $r[] = $this->msg( 'listgrouprights-right-revoked',
User::getRightDescription( $permission ),
'<span class="mw-listgrouprights-right-name">' . $permission . '</span>'
)->parse();
- $r[] = $description;
}
}
@@ -257,51 +256,28 @@ class SpecialListGroupRights extends SpecialPage {
$lang = $this->getLanguage();
$allGroups = User::getAllGroups();
- if ( $add === true ) {
- $r[] = $this->msg( 'listgrouprights-addgroup-all' )->escaped();
- } elseif ( is_array( $add ) ) {
- $add = array_intersect( array_values( array_unique( $add ) ), $allGroups );
- if ( count( $add ) ) {
- $r[] = $this->msg( 'listgrouprights-addgroup',
- $lang->listToText( array_map( array( 'User', 'makeGroupLinkWiki' ), $add ) ),
- count( $add )
- )->parse();
- }
- }
-
- if ( $remove === true ) {
- $r[] = $this->msg( 'listgrouprights-removegroup-all' )->escaped();
- } elseif ( is_array( $remove ) ) {
- $remove = array_intersect( array_values( array_unique( $remove ) ), $allGroups );
- if ( count( $remove ) ) {
- $r[] = $this->msg( 'listgrouprights-removegroup',
- $lang->listToText( array_map( array( 'User', 'makeGroupLinkWiki' ), $remove ) ),
- count( $remove )
- )->parse();
- }
- }
-
- if ( $addSelf === true ) {
- $r[] = $this->msg( 'listgrouprights-addgroup-self-all' )->escaped();
- } elseif ( is_array( $addSelf ) ) {
- $addSelf = array_intersect( array_values( array_unique( $addSelf ) ), $allGroups );
- if ( count( $addSelf ) ) {
- $r[] = $this->msg( 'listgrouprights-addgroup-self',
- $lang->listToText( array_map( array( 'User', 'makeGroupLinkWiki' ), $addSelf ) ),
- count( $addSelf )
- )->parse();
- }
- }
+ $changeGroups = array(
+ 'addgroup' => $add,
+ 'removegroup' => $remove,
+ 'addgroup-self' => $addSelf,
+ 'removegroup-self' => $removeSelf
+ );
- if ( $removeSelf === true ) {
- $r[] = $this->msg( 'listgrouprights-removegroup-self-all' )->parse();
- } elseif ( is_array( $removeSelf ) ) {
- $removeSelf = array_intersect( array_values( array_unique( $removeSelf ) ), $allGroups );
- if ( count( $removeSelf ) ) {
- $r[] = $this->msg( 'listgrouprights-removegroup-self',
- $lang->listToText( array_map( array( 'User', 'makeGroupLinkWiki' ), $removeSelf ) ),
- count( $removeSelf )
- )->parse();
+ foreach ( $changeGroups as $messageKey => $changeGroup ) {
+ if ( $changeGroup === true ) {
+ // For grep: listgrouprights-addgroup-all, listgrouprights-removegroup-all,
+ // listgrouprights-addgroup-self-all, listgrouprights-removegroup-self-all
+ $r[] = $this->msg( 'listgrouprights-' . $messageKey . '-all' )->escaped();
+ } elseif ( is_array( $changeGroup ) ) {
+ $changeGroup = array_intersect( array_values( array_unique( $changeGroup ) ), $allGroups );
+ if ( count( $changeGroup ) ) {
+ // For grep: listgrouprights-addgroup, listgrouprights-removegroup,
+ // listgrouprights-addgroup-self, listgrouprights-removegroup-self
+ $r[] = $this->msg( 'listgrouprights-' . $messageKey,
+ $lang->listToText( array_map( array( 'User', 'makeGroupLinkWiki' ), $changeGroup ) ),
+ count( $changeGroup )
+ )->parse();
+ }
}
}
diff --git a/includes/specials/SpecialListredirects.php b/includes/specials/SpecialListredirects.php
index de05be41..2df48347 100644
--- a/includes/specials/SpecialListredirects.php
+++ b/includes/specials/SpecialListredirects.php
@@ -72,7 +72,7 @@ class ListredirectsPage extends QueryPage {
/**
* Cache page existence for performance
*
- * @param DatabaseBase $db
+ * @param IDatabase $db
* @param ResultWrapper $res
*/
function preprocessResults( $db, $res ) {
diff --git a/includes/specials/SpecialListusers.php b/includes/specials/SpecialListusers.php
index dad9074d..56c4eb50 100644
--- a/includes/specials/SpecialListusers.php
+++ b/includes/specials/SpecialListusers.php
@@ -35,6 +35,11 @@
class UsersPager extends AlphabeticPager {
/**
+ * @var array A array with user ids as key and a array of groups as value
+ */
+ protected $userGroupCache;
+
+ /**
* @param IContextSource $context
* @param array $par (Default null)
* @param bool $including Whether this page is being transcluded in
@@ -132,8 +137,6 @@ class UsersPager extends AlphabeticPager {
'user_name' => $this->creationSort ? 'MAX(user_name)' : 'user_name',
'user_id' => $this->creationSort ? 'user_id' : 'MAX(user_id)',
'edits' => 'MAX(user_editcount)',
- 'numgroups' => 'COUNT(ug_group)',
- 'singlegroup' => 'MAX(ug_group)', // the usergroup if there is only one
'creation' => 'MIN(user_registration)',
'ipb_deleted' => 'MAX(ipb_deleted)' // block/hide status
),
@@ -150,7 +153,7 @@ class UsersPager extends AlphabeticPager {
'conds' => $conds
);
- wfRunHooks( 'SpecialListusersQueryInfo', array( $this, &$query ) );
+ Hooks::run( 'SpecialListusersQueryInfo', array( $this, &$query ) );
return $query;
}
@@ -176,7 +179,7 @@ class UsersPager extends AlphabeticPager {
$lang = $this->getLanguage();
$groups = '';
- $groups_list = self::getGroups( $row->user_id );
+ $groups_list = self::getGroups( intval( $row->user_id ), $this->userGroupCache );
if ( !$this->including && count( $groups_list ) > 0 ) {
$list = array();
@@ -211,18 +214,45 @@ class UsersPager extends AlphabeticPager {
' ' . $this->msg( 'listusers-blocked', $userName )->escaped() :
'';
- wfRunHooks( 'SpecialListusersFormatRow', array( &$item, $row ) );
+ Hooks::run( 'SpecialListusersFormatRow', array( &$item, $row ) );
return Html::rawElement( 'li', array(), "{$item}{$edits}{$created}{$blocked}" );
}
function doBatchLookups() {
$batch = new LinkBatch();
+ $userIds = array();
# Give some pointers to make user links
foreach ( $this->mResult as $row ) {
$batch->add( NS_USER, $row->user_name );
$batch->add( NS_USER_TALK, $row->user_name );
+ $userIds[] = $row->user_id;
+ }
+
+ // Lookup groups for all the users
+ $dbr = wfGetDB( DB_SLAVE );
+ $groupRes = $dbr->select(
+ 'user_groups',
+ array( 'ug_user', 'ug_group' ),
+ array( 'ug_user' => $userIds ),
+ __METHOD__
+ );
+ $cache = array();
+ $groups = array();
+ foreach ( $groupRes as $row ) {
+ $cache[intval( $row->ug_user )][] = $row->ug_group;
+ $groups[$row->ug_group] = true;
}
+ $this->userGroupCache = $cache;
+
+ // Add page of groups to link batch
+ foreach ( $groups as $group => $unused ) {
+ $groupPage = User::getGroupPage( $group );
+ if ( $groupPage ) {
+ $batch->addObj( $groupPage );
+ }
+ }
+
$batch->execute();
$this->mResult->rewind();
}
@@ -284,12 +314,12 @@ class UsersPager extends AlphabeticPager {
);
$out .= '<br />';
- wfRunHooks( 'SpecialListusersHeaderForm', array( $this, &$out ) );
+ Hooks::run( 'SpecialListusersHeaderForm', array( $this, &$out ) );
# Submit button and form bottom
$out .= Html::hidden( 'limit', $this->mLimit );
$out .= Xml::submitButton( $this->msg( 'allpagessubmit' )->text() );
- wfRunHooks( 'SpecialListusersHeader', array( $this, &$out ) );
+ Hooks::run( 'SpecialListusersHeader', array( $this, &$out ) );
$out .= Xml::closeElement( 'fieldset' ) .
Xml::closeElement( 'form' );
@@ -322,7 +352,7 @@ class UsersPager extends AlphabeticPager {
if ( $this->requestedUser != '' ) {
$query['username'] = $this->requestedUser;
}
- wfRunHooks( 'SpecialListusersDefaultQuery', array( $this, &$query ) );
+ Hooks::run( 'SpecialListusersDefaultQuery', array( $this, &$query ) );
return $query;
}
@@ -331,11 +361,17 @@ class UsersPager extends AlphabeticPager {
* Get a list of groups the specified user belongs to
*
* @param int $uid User id
+ * @param array|null $cache
* @return array
*/
- protected static function getGroups( $uid ) {
- $user = User::newFromId( $uid );
- $groups = array_diff( $user->getEffectiveGroups(), User::getImplicitGroups() );
+ protected static function getGroups( $uid, $cache = null ) {
+ if ( $cache === null ) {
+ $user = User::newFromId( $uid );
+ $effectiveGroups = $user->getEffectiveGroups();
+ } else {
+ $effectiveGroups = isset( $cache[$uid] ) ? $cache[$uid] : array();
+ }
+ $groups = array_diff( $effectiveGroups, User::getImplicitGroups() );
return $groups;
}
@@ -350,7 +386,7 @@ class UsersPager extends AlphabeticPager {
protected static function buildGroupLink( $group, $username ) {
return User::makeGroupLinkHtml(
$group,
- htmlspecialchars( User::getGroupMember( $group, $username ) )
+ User::getGroupMember( $group, $username )
);
}
}
@@ -397,15 +433,12 @@ class SpecialListUsers extends IncludableSpecialPage {
}
/**
- * Return an array of subpages beginning with $search that this special page will accept.
+ * Return an array of subpages that this special page will accept.
*
- * @param string $search Prefix to search for
- * @param int $limit Maximum number of results to return
- * @return string[] Matching subpages
+ * @return string[] subpages
*/
- public function prefixSearchSubpages( $search, $limit = 10 ) {
- $subpages = User::getAllGroups();
- return self::prefixSearchArray( $search, $limit, $subpages );
+ public function getSubpagesForPrefixSearch() {
+ return User::getAllGroups();
}
protected function getGroupName() {
diff --git a/includes/specials/SpecialLog.php b/includes/specials/SpecialLog.php
index dc33801d..e44ce5f5 100644
--- a/includes/specials/SpecialLog.php
+++ b/includes/specials/SpecialLog.php
@@ -29,17 +29,6 @@
* @ingroup SpecialPage
*/
class SpecialLog extends SpecialPage {
- /**
- * List log type for which the target is a user
- * Thus if the given target is in NS_MAIN we can alter it to be an NS_USER
- * Title user instead.
- */
- private $typeOnUser = array(
- 'block',
- 'newusers',
- 'rights',
- );
-
public function __construct() {
parent::__construct( 'Log' );
}
@@ -47,6 +36,7 @@ class SpecialLog extends SpecialPage {
public function execute( $par ) {
$this->setHeaders();
$this->outputHeader();
+ $this->getOutput()->addModules( 'mediawiki.userSuggest' );
$opts = new FormOptions;
$opts->add( 'type', '' );
@@ -94,13 +84,18 @@ class SpecialLog extends SpecialPage {
} elseif ( $offender && IP::isIPAddress( $offender->getName() ) ) {
$qc = array( 'ls_field' => 'target_author_ip', 'ls_value' => $offender->getName() );
}
+ } else {
+ // Allow extensions to add relations to their search types
+ Hooks::run(
+ 'SpecialLogAddLogSearchRelations',
+ array( $opts->getValue( 'type' ), $this->getRequest(), &$qc )
+ );
}
# Some log types are only for a 'User:' title but we might have been given
# only the username instead of the full title 'User:username'. This part try
# to lookup for a user by that name and eventually fix user input. See bug 1697.
- wfRunHooks( 'GetLogTypesOnUser', array( &$this->typeOnUser ) );
- if ( in_array( $opts->getValue( 'type' ), $this->typeOnUser ) ) {
+ if ( in_array( $opts->getValue( 'type' ), self::getLogTypesOnUser() ) ) {
# ok we have a type of log which expect a user title.
$target = Title::newFromText( $opts->getValue( 'page' ) );
if ( $target && $target->getNamespace() === NS_MAIN ) {
@@ -115,17 +110,38 @@ class SpecialLog extends SpecialPage {
}
/**
- * Return an array of subpages beginning with $search that this special page will accept.
+ * List log type for which the target is a user
+ * Thus if the given target is in NS_MAIN we can alter it to be an NS_USER
+ * Title user instead.
*
- * @param string $search Prefix to search for
- * @param int $limit Maximum number of results to return
- * @return string[] Matching subpages
+ * @since 1.25
+ * @return array
*/
- public function prefixSearchSubpages( $search, $limit = 10 ) {
+ public static function getLogTypesOnUser() {
+ static $types = null;
+ if ( $types !== null ) {
+ return $types;
+ }
+ $types = array(
+ 'block',
+ 'newusers',
+ 'rights',
+ );
+
+ Hooks::run( 'GetLogTypesOnUser', array( &$types ) );
+ return $types;
+ }
+
+ /**
+ * Return an array of subpages that this special page will accept.
+ *
+ * @return string[] subpages
+ */
+ public function getSubpagesForPrefixSearch() {
$subpages = $this->getConfig()->get( 'LogTypes' );
$subpages[] = 'all';
sort( $subpages );
- return self::prefixSearchArray( $search, $limit, $subpages );
+ return $subpages;
}
private function parseParams( FormOptions $opts, $par ) {
@@ -149,7 +165,7 @@ class SpecialLog extends SpecialPage {
$loglist = new LogEventsList(
$this->getContext(),
null,
- LogEventsList::USE_REVDEL_CHECKBOXES
+ LogEventsList::USE_CHECKBOXES
);
$pager = new LogPager(
$loglist,
@@ -187,7 +203,7 @@ class SpecialLog extends SpecialPage {
if ( $logBody ) {
$this->getOutput()->addHTML(
$pager->getNavigationBar() .
- $this->getRevisionButton(
+ $this->getActionButtons(
$loglist->beginLogEventsList() .
$logBody .
$loglist->endLogEventsList()
@@ -199,30 +215,50 @@ class SpecialLog extends SpecialPage {
}
}
- private function getRevisionButton( $formcontents ) {
- # If the user doesn't have the ability to delete log entries,
- # don't bother showing them the button.
- if ( !$this->getUser()->isAllowedAll( 'deletedhistory', 'deletelogentry' ) ) {
+ private function getActionButtons( $formcontents ) {
+ $user = $this->getUser();
+ $canRevDelete = $user->isAllowedAll( 'deletedhistory', 'deletelogentry' );
+ $showTagEditUI = ChangeTags::showTagEditingUI( $user );
+ # If the user doesn't have the ability to delete log entries nor edit tags,
+ # don't bother showing them the button(s).
+ if ( !$canRevDelete && !$showTagEditUI ) {
return $formcontents;
}
- # Show button to hide log entries
+ # Show button to hide log entries and/or edit change tags
$s = Html::openElement(
'form',
array( 'action' => wfScript(), 'id' => 'mw-log-deleterevision-submit' )
) . "\n";
- $s .= Html::hidden( 'title', SpecialPage::getTitleFor( 'Revisiondelete' ) ) . "\n";
- $s .= Html::hidden( 'target', SpecialPage::getTitleFor( 'Log' ) ) . "\n";
+ $s .= Html::hidden( 'action', 'historysubmit' ) . "\n";
$s .= Html::hidden( 'type', 'logging' ) . "\n";
- $button = Html::element(
- 'button',
- array(
- 'type' => 'submit',
- 'class' => "deleterevision-log-submit mw-log-deleterevision-button"
- ),
- $this->msg( 'showhideselectedlogentries' )->text()
- ) . "\n";
- $s .= $button . $formcontents . $button;
+
+ $buttons = '';
+ if ( $canRevDelete ) {
+ $buttons .= Html::element(
+ 'button',
+ array(
+ 'type' => 'submit',
+ 'name' => 'revisiondelete',
+ 'value' => '1',
+ 'class' => "deleterevision-log-submit mw-log-deleterevision-button"
+ ),
+ $this->msg( 'showhideselectedlogentries' )->text()
+ ) . "\n";
+ }
+ if ( $showTagEditUI ) {
+ $buttons .= Html::element(
+ 'button',
+ array(
+ 'type' => 'submit',
+ 'name' => 'editchangetags',
+ 'value' => '1',
+ 'class' => "editchangetags-log-submit mw-log-editchangetags-button"
+ ),
+ $this->msg( 'log-edit-tags' )->text()
+ ) . "\n";
+ }
+ $s .= $buttons . $formcontents . $buttons;
$s .= Html::closeElement( 'form' );
return $s;
@@ -235,8 +271,9 @@ class SpecialLog extends SpecialPage {
*/
protected function addHeader( $type ) {
$page = new LogPage( $type );
- $this->getOutput()->setPageTitle( $page->getName()->text() );
- $this->getOutput()->addHTML( $page->getDescription()->parseAsBlock() );
+ $this->getOutput()->setPageTitle( $page->getName() );
+ $this->getOutput()->addHTML( $page->getDescription()
+ ->setContext( $this->getContext() )->parseAsBlock() );
}
protected function getGroupName() {
diff --git a/includes/specials/SpecialLonelypages.php b/includes/specials/SpecialLonelypages.php
index f533234f..c072491b 100644
--- a/includes/specials/SpecialLonelypages.php
+++ b/includes/specials/SpecialLonelypages.php
@@ -72,7 +72,7 @@ class LonelyPagesPage extends PageQueryPage {
);
// Allow extensions to modify the query
- wfRunHooks( 'LonelyPagesQuery', array( &$tables, &$conds, &$joinConds ) );
+ Hooks::run( 'LonelyPagesQuery', array( &$tables, &$conds, &$joinConds ) );
return array(
'tables' => $tables,
diff --git a/includes/specials/SpecialMediaStatistics.php b/includes/specials/SpecialMediaStatistics.php
index 681c332f..b62de5d2 100644
--- a/includes/specials/SpecialMediaStatistics.php
+++ b/includes/specials/SpecialMediaStatistics.php
@@ -73,6 +73,10 @@ class MediaStatisticsPage extends QueryPage {
'namespace' => NS_MEDIA, /* needs to be something */
'value' => '1'
),
+ 'conds' => array(
+ // WMF has a random null row in the db
+ 'img_media_type IS NOT NULL'
+ ),
'options' => array(
'GROUP BY' => array(
'img_media_type',
@@ -99,7 +103,7 @@ class MediaStatisticsPage extends QueryPage {
*
* @param $out OutputPage
* @param $skin Skin (deprecated presumably)
- * @param $dbr DatabaseBase
+ * @param $dbr IDatabase
* @param $res ResultWrapper Results from query
* @param $num integer Number of results
* @param $offset integer Paging offset (Should always be 0 in our case)
@@ -153,7 +157,8 @@ class MediaStatisticsPage extends QueryPage {
);
$row .= Html::rawElement(
'td',
- array(),
+ // Make sure js sorts it in numeric order
+ array( 'data-sort-value' => $count ),
$this->msg( 'mediastatistics-nfiles' )
->numParams( $count )
/** @todo Check to be sure this really should have number formatting */
@@ -185,6 +190,9 @@ class MediaStatisticsPage extends QueryPage {
if ( $decimal == 0 ) {
return '0';
}
+ if ( $decimal >= 100 ) {
+ return '100';
+ }
$percent = sprintf( "%." . max( 0, 2 - floor( log10( $decimal ) ) ) . "f", $decimal );
// Then remove any trailing 0's
return preg_replace( '/\.?0*$/', '', $percent );
@@ -302,6 +310,8 @@ class MediaStatisticsPage extends QueryPage {
*
* @param $skin Skin
* @param $result stdObject Result row
+ * @return bool|string|void
+ * @throws MWException
*/
public function formatResult( $skin, $result ) {
throw new MWException( "unimplemented" );
@@ -310,15 +320,15 @@ class MediaStatisticsPage extends QueryPage {
/**
* Initialize total values so we can figure out percentages later.
*
- * @param $dbr DatabaseBase
+ * @param $dbr IDatabase
* @param $res ResultWrapper
*/
public function preprocessResults( $dbr, $res ) {
$this->totalCount = $this->totalBytes = 0;
foreach ( $res as $row ) {
- list( , , $count, $bytes ) = $this->splitFakeTitle( $row->title );
- $this->totalCount += $count;
- $this->totalBytes += $bytes;
+ $mediaStats = $this->splitFakeTitle( $row->title );
+ $this->totalCount += isset( $mediaStats[2] ) ? $mediaStats[2] : 0;
+ $this->totalBytes += isset( $mediaStats[3] ) ? $mediaStats[3] : 0;
}
$res->seek( 0 );
}
diff --git a/includes/specials/SpecialMergeHistory.php b/includes/specials/SpecialMergeHistory.php
index 43f5a1ba..1f0b6d45 100644
--- a/includes/specials/SpecialMergeHistory.php
+++ b/includes/specials/SpecialMergeHistory.php
@@ -159,9 +159,10 @@ class SpecialMergeHistory extends SpecialPage {
}
function showMergeForm() {
- $this->getOutput()->addWikiMsg( 'mergehistory-header' );
+ $out = $this->getOutput();
+ $out->addWikiMsg( 'mergehistory-header' );
- $this->getOutput()->addHTML(
+ $out->addHTML(
Xml::openElement( 'form', array(
'method' => 'get',
'action' => wfScript() ) ) .
@@ -185,6 +186,8 @@ class SpecialMergeHistory extends SpecialPage {
'</fieldset>' .
'</form>'
);
+
+ $this->addHelpLink( 'Help:Merge history' );
}
private function showHistory() {
@@ -469,18 +472,23 @@ class SpecialMergeHistory extends SpecialPage {
return false;
}
# Update our logs
- $log = new LogPage( 'merge' );
- $log->addEntry(
- 'merge', $targetTitle, $this->mComment,
- array( $destTitle->getPrefixedText(), $timestampLimit ), $this->getUser()
- );
+ $logEntry = new ManualLogEntry( 'merge', 'merge' );
+ $logEntry->setPerformer( $this->getUser() );
+ $logEntry->setComment( $this->mComment );
+ $logEntry->setTarget( $targetTitle );
+ $logEntry->setParameters( array(
+ '4::dest' => $destTitle->getPrefixedText(),
+ '5::mergepoint' => $timestampLimit
+ ) );
+ $logId = $logEntry->insert();
+ $logEntry->publish( $logId );
# @todo message should use redirect=no
$this->getOutput()->addWikiText( $this->msg( 'mergehistory-success',
$targetTitle->getPrefixedText(), $destTitle->getPrefixedText() )->numParams(
$count )->text() );
- wfRunHooks( 'ArticleMergeComplete', array( $targetTitle, $destTitle ) );
+ Hooks::run( 'ArticleMergeComplete', array( $targetTitle, $destTitle ) );
return true;
}
@@ -516,7 +524,6 @@ class MergeHistoryPager extends ReverseChronologicalPager {
}
function getStartBody() {
- wfProfileIn( __METHOD__ );
# Do a link batch query
$this->mResult->seek( 0 );
$batch = new LinkBatch();
@@ -539,8 +546,6 @@ class MergeHistoryPager extends ReverseChronologicalPager {
$batch->execute();
$this->mResult->seek( 0 );
- wfProfileOut( __METHOD__ );
-
return '';
}
diff --git a/includes/specials/SpecialMostcategories.php b/includes/specials/SpecialMostcategories.php
index 9b67f343..c70bbdba 100644
--- a/includes/specials/SpecialMostcategories.php
+++ b/includes/specials/SpecialMostcategories.php
@@ -65,7 +65,7 @@ class MostcategoriesPage extends QueryPage {
}
/**
- * @param DatabaseBase $db
+ * @param IDatabase $db
* @param ResultWrapper $res
*/
function preprocessResults( $db, $res ) {
diff --git a/includes/specials/SpecialMostimages.php b/includes/specials/SpecialMostimages.php
index 98d8da3a..36669641 100644
--- a/includes/specials/SpecialMostimages.php
+++ b/includes/specials/SpecialMostimages.php
@@ -25,7 +25,7 @@
*/
/**
- * A special page page that list most used images
+ * A special page that lists most used images
*
* @ingroup SpecialPage
*/
diff --git a/includes/specials/SpecialMostinterwikis.php b/includes/specials/SpecialMostinterwikis.php
index 30ccbe5a..ab3d9c91 100644
--- a/includes/specials/SpecialMostinterwikis.php
+++ b/includes/specials/SpecialMostinterwikis.php
@@ -71,7 +71,7 @@ class MostinterwikisPage extends QueryPage {
/**
* Pre-fill the link cache
*
- * @param DatabaseBase $db
+ * @param IDatabase $db
* @param ResultWrapper $res
*/
function preprocessResults( $db, $res ) {
diff --git a/includes/specials/SpecialMostlinked.php b/includes/specials/SpecialMostlinked.php
index 99f0ecf5..ae0b0708 100644
--- a/includes/specials/SpecialMostlinked.php
+++ b/includes/specials/SpecialMostlinked.php
@@ -74,7 +74,7 @@ class MostlinkedPage extends QueryPage {
/**
* Pre-fill the link cache
*
- * @param DatabaseBase $db
+ * @param IDatabase $db
* @param ResultWrapper $res
*/
function preprocessResults( $db, $res ) {
diff --git a/includes/specials/SpecialMostlinkedcategories.php b/includes/specials/SpecialMostlinkedcategories.php
index f61a1158..cc718e06 100644
--- a/includes/specials/SpecialMostlinkedcategories.php
+++ b/includes/specials/SpecialMostlinkedcategories.php
@@ -55,7 +55,7 @@ class MostlinkedCategoriesPage extends QueryPage {
/**
* Fetch user page links and cache their existence
*
- * @param DatabaseBase $db
+ * @param IDatabase $db
* @param ResultWrapper $res
*/
function preprocessResults( $db, $res ) {
diff --git a/includes/specials/SpecialMostlinkedtemplates.php b/includes/specials/SpecialMostlinkedtemplates.php
index 8e6a596d..a924525d 100644
--- a/includes/specials/SpecialMostlinkedtemplates.php
+++ b/includes/specials/SpecialMostlinkedtemplates.php
@@ -75,7 +75,7 @@ class MostlinkedTemplatesPage extends QueryPage {
/**
* Pre-cache page existence to speed up link generation
*
- * @param DatabaseBase $db
+ * @param IDatabase $db
* @param ResultWrapper $res
*/
public function preprocessResults( $db, $res ) {
diff --git a/includes/specials/SpecialMovepage.php b/includes/specials/SpecialMovepage.php
index ec9593f7..ae1fefea 100644
--- a/includes/specials/SpecialMovepage.php
+++ b/includes/specials/SpecialMovepage.php
@@ -140,6 +140,7 @@ class MovePageForm extends UnlistedSpecialPage {
$out = $this->getOutput();
$out->setPageTitle( $this->msg( 'move-page', $this->oldTitle->getPrefixedText() ) );
$out->addModules( 'mediawiki.special.movePage' );
+ $this->addHelpLink( 'Help:Moving a page' );
$newTitle = $this->newTitle;
@@ -165,17 +166,7 @@ class MovePageForm extends UnlistedSpecialPage {
$out->addWikiMsg( 'delete_and_move_text', $newTitle->getPrefixedText() );
$movepagebtn = $this->msg( 'delete_and_move' )->text();
$submitVar = 'wpDeleteAndMove';
- $confirm = "
- <tr>
- <td></td>
- <td class='mw-input'>" .
- Xml::checkLabel(
- $this->msg( 'delete_and_move_confirm' )->text(),
- 'wpConfirm',
- 'wpConfirm'
- ) .
- "</td>
- </tr>";
+ $confirm = true;
$err = array();
} else {
if ( $this->oldTitle->getNamespace() == NS_USER && !$this->oldTitle->isSubpage() ) {
@@ -310,12 +301,15 @@ class MovePageForm extends UnlistedSpecialPage {
'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( 'fieldset' ) .
+ Xml::element( 'legend', null, $this->msg( 'move-page-legend' )->text() ) .
+ Xml::openElement( 'table', array( 'id' => 'mw-movepage-table' ) )
+ );
+
+ $out->addHTML(
+ "<tr>
<td class='mw-label'>" .
- $this->msg( 'movearticle' )->escaped() .
+ $this->msg( 'movearticle' )->escaped() .
"</td>
<td class='mw-input'>
<strong>{$oldTitleLink}</strong>
@@ -323,32 +317,32 @@ class MovePageForm extends UnlistedSpecialPage {
</tr>
<tr>
<td class='mw-label'>" .
- Xml::label( $this->msg( 'newtitle' )->text(), 'wpNewTitleMain' ) .
+ Xml::label( $this->msg( 'newtitle' )->text(), 'wpNewTitleMain' ) .
"</td>
<td class='mw-input'>" .
- Html::namespaceSelector(
- array(
- 'selected' => $newTitle->getNamespace(),
- 'exclude' => $immovableNamespaces
- ),
- array( 'name' => 'wpNewTitleNs', 'id' => 'wpNewTitleNs' )
- ) .
- Xml::input(
- 'wpNewTitleMain',
- 60,
- $wgContLang->recodeForEdit( $newTitle->getText() ),
- array(
- 'type' => 'text',
- 'id' => 'wpNewTitleMain',
- 'maxlength' => 255
- )
- ) .
- Html::hidden( 'wpOldTitle', $this->oldTitle->getPrefixedText() ) .
+ Html::namespaceSelector(
+ array(
+ 'selected' => $newTitle->getNamespace(),
+ 'exclude' => $immovableNamespaces
+ ),
+ array( 'name' => 'wpNewTitleNs', 'id' => 'wpNewTitleNs' )
+ ) .
+ Xml::input(
+ 'wpNewTitleMain',
+ 60,
+ $wgContLang->recodeForEdit( $newTitle->getText() ),
+ array(
+ 'type' => 'text',
+ 'id' => 'wpNewTitleMain',
+ 'maxlength' => 255
+ )
+ ) .
+ Html::hidden( 'wpOldTitle', $this->oldTitle->getPrefixedText() ) .
"</td>
</tr>
<tr>
<td class='mw-label'>" .
- Xml::label( $this->msg( 'movereason' )->text(), 'wpReason' ) .
+ Xml::label( $this->msg( 'movereason' )->text(), 'wpReason' ) .
"</td>
<td class='mw-input'>" .
Xml::input( 'wpReason', 60, $this->reason, array(
@@ -365,12 +359,12 @@ class MovePageForm extends UnlistedSpecialPage {
<tr>
<td></td>
<td class='mw-input'>" .
- Xml::checkLabel(
- $this->msg( 'movetalk' )->text(),
- 'wpMovetalk',
- 'wpMovetalk',
- $this->moveTalk
- ) .
+ Xml::checkLabel(
+ $this->msg( 'movetalk' )->text(),
+ 'wpMovetalk',
+ 'wpMovetalk',
+ $this->moveTalk
+ ) .
"</td>
</tr>"
);
@@ -389,14 +383,14 @@ class MovePageForm extends UnlistedSpecialPage {
$out->addHTML( "
<tr>
<td></td>
- <td class='mw-input' >" .
- Xml::checkLabel(
- $this->msg( 'move-leave-redirect' )->text(),
- 'wpLeaveRedirect',
- 'wpLeaveRedirect',
- $isChecked,
- $options
- ) .
+ <td class='mw-input'>" .
+ Xml::checkLabel(
+ $this->msg( 'move-leave-redirect' )->text(),
+ 'wpLeaveRedirect',
+ 'wpLeaveRedirect',
+ $isChecked,
+ $options
+ ) .
"</td>
</tr>"
);
@@ -406,13 +400,13 @@ class MovePageForm extends UnlistedSpecialPage {
$out->addHTML( "
<tr>
<td></td>
- <td class='mw-input' >" .
- Xml::checkLabel(
- $this->msg( 'fix-double-redirects' )->text(),
- 'wpFixRedirects',
- 'wpFixRedirects',
- $this->fixRedirects
- ) .
+ <td class='mw-input'>" .
+ Xml::checkLabel(
+ $this->msg( 'fix-double-redirects' )->text(),
+ 'wpFixRedirects',
+ 'wpFixRedirects',
+ $this->fixRedirects
+ ) .
"</td>
</tr>"
);
@@ -423,21 +417,23 @@ class MovePageForm extends UnlistedSpecialPage {
$out->addHTML( "
<tr>
<td></td>
- <td class=\"mw-input\">" .
- Xml::check(
- '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 ),
- array( 'id' => 'wpMovesubpages' )
- ) . '&#160;' .
- Xml::tags( 'label', array( 'for' => 'wpMovesubpages' ),
- $this->msg(
- ( $this->oldTitle->hasSubpages()
- ? 'move-subpages'
- : 'move-talk-subpages' )
- )->numParams( $maximumMovedPages )->params( $maximumMovedPages )->parse()
- ) .
+ <td class='mw-input'>" .
+ Xml::check(
+ '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 ),
+ array( 'id' => 'wpMovesubpages' )
+ ) . '&#160;' .
+ Xml::tags(
+ 'label',
+ array( 'for' => 'wpMovesubpages' ),
+ $this->msg(
+ ( $this->oldTitle->hasSubpages()
+ ? 'move-subpages'
+ : 'move-talk-subpages' )
+ )->numParams( $maximumMovedPages )->params( $maximumMovedPages )->parse()
+ ) .
"</td>
</tr>"
);
@@ -448,32 +444,50 @@ class MovePageForm extends UnlistedSpecialPage {
# Don't allow watching if user is not logged in
if ( $user->isLoggedIn() ) {
$out->addHTML( "
- <tr>
- <td></td>
- <td class='mw-input'>" .
- Xml::checkLabel(
- $this->msg( 'move-watch' )->text(),
- 'wpWatch',
- 'watch',
- $watchChecked
- ) .
- "</td>
- </tr>" );
+ <tr>
+ <td></td>
+ <td class='mw-input'>" .
+ Xml::checkLabel(
+ $this->msg( 'move-watch' )->text(),
+ 'wpWatch',
+ 'watch',
+ $watchChecked
+ ) .
+ "</td>
+ </tr>"
+ );
+ }
+
+ if ( $confirm ) {
+ $out->addHTML( "
+ <tr>
+ <td></td>
+ <td class='mw-input'>" .
+ Xml::checkLabel(
+ $this->msg( 'delete_and_move_confirm' )->text(),
+ 'wpConfirm',
+ 'wpConfirm'
+ ) .
+ "</td>
+ </tr>"
+ );
}
$out->addHTML( "
- {$confirm}
<tr>
- <td>&#160;</td>
+ <td></td>
<td class='mw-submit'>" .
- Xml::submitButton( $movepagebtn, array( 'name' => $submitVar ) ) .
+ Xml::submitButton( $movepagebtn, array( 'name' => $submitVar ) ) .
"</td>
- </tr>" .
- Xml::closeElement( 'table' ) .
- Html::hidden( 'wpEditToken', $user->getEditToken() ) .
- Xml::closeElement( 'fieldset' ) .
- Xml::closeElement( 'form' ) .
- "\n"
+ </tr>"
+ );
+
+ $out->addHTML(
+ Xml::closeElement( 'table' ) .
+ Html::hidden( 'wpEditToken', $user->getEditToken() ) .
+ Xml::closeElement( 'fieldset' ) .
+ Xml::closeElement( 'form' ) .
+ "\n"
);
$this->showLogFragment( $this->oldTitle );
@@ -523,8 +537,9 @@ class MovePageForm extends UnlistedSpecialPage {
// Delete an associated image if there is
if ( $nt->getNamespace() == NS_FILE ) {
$file = wfLocalFile( $nt );
+ $file->load( File::READ_LATEST );
if ( $file->exists() ) {
- $file->delete( $reason, false );
+ $file->delete( $reason, false, $user );
}
}
@@ -549,10 +564,22 @@ class MovePageForm extends UnlistedSpecialPage {
}
# Do the actual move.
- $error = $ot->moveTo( $nt, true, $this->reason, $createRedirect );
- if ( $error !== true ) {
- $this->showForm( $error );
+ $mp = new MovePage( $ot, $nt );
+ $valid = $mp->isValidMove();
+ if ( !$valid->isOK() ) {
+ $this->showForm( $valid->getErrorsArray() );
+ return;
+ }
+
+ $permStatus = $mp->checkPermissions( $user, $this->reason );
+ if ( !$permStatus->isOK() ) {
+ $this->showForm( $permStatus->getErrorsArray() );
+ return;
+ }
+ $status = $mp->move( $user, $this->reason, $createRedirect );
+ if ( !$status->isOK() ) {
+ $this->showForm( $status->getErrorsArray() );
return;
}
@@ -592,7 +619,7 @@ class MovePageForm extends UnlistedSpecialPage {
$newLink )->params( $oldText, $newText )->parseAsBlock() );
$out->addWikiMsg( $msgName );
- wfRunHooks( 'SpecialMovepageAfterMove', array( &$this, &$ot, &$nt ) );
+ Hooks::run( '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
@@ -673,7 +700,10 @@ class MovePageForm extends UnlistedSpecialPage {
$oldSubpage->getDBkey()
);
- if ( $oldSubpage->isTalkPage() ) {
+ if ( $oldSubpage->isSubpage() && ( $ot->isTalkPage() xor $nt->isTalkPage() ) ) {
+ // Moving a subpage from a subject namespace to a talk namespace or vice-versa
+ $newNs = $nt->getNamespace();
+ } elseif ( $oldSubpage->isTalkPage() ) {
$newNs = $nt->getTalkPage()->getNamespace();
} else {
$newNs = $nt->getSubjectPage()->getNamespace();
diff --git a/includes/specials/SpecialMyLanguage.php b/includes/specials/SpecialMyLanguage.php
index 71b18930..6cea1581 100644
--- a/includes/specials/SpecialMyLanguage.php
+++ b/includes/specials/SpecialMyLanguage.php
@@ -80,6 +80,11 @@ class SpecialMyLanguage extends RedirectSpecialArticle {
return null;
}
+ if ( $base->isRedirect() ) {
+ $page = new WikiPage( $base );
+ $base = $page->getRedirectTarget();
+ }
+
$uiCode = $this->getLanguage()->getCode();
$proposed = $base->getSubpage( $uiCode );
if ( $uiCode !== $this->getConfig()->get( 'LanguageCode' ) && $proposed && $proposed->exists() ) {
diff --git a/includes/specials/SpecialNewimages.php b/includes/specials/SpecialNewimages.php
index 546c1914..00c8e050 100644
--- a/includes/specials/SpecialNewimages.php
+++ b/includes/specials/SpecialNewimages.php
@@ -30,6 +30,9 @@ class SpecialNewFiles extends IncludableSpecialPage {
$this->setHeaders();
$this->outputHeader();
+ $out = $this->getOutput();
+ $this->addHelpLink( 'Help:New images' );
+
$pager = new NewFilesPager( $this->getContext(), $par );
if ( !$this->including() ) {
@@ -39,9 +42,9 @@ class SpecialNewFiles extends IncludableSpecialPage {
$form->displayForm( '' );
}
- $this->getOutput()->addHTML( $pager->getBody() );
+ $out->addHTML( $pager->getBody() );
if ( !$this->including() ) {
- $this->getOutput()->addHTML( $pager->getNavigationBar() );
+ $out->addHTML( $pager->getNavigationBar() );
}
}
@@ -59,7 +62,7 @@ class SpecialNewFiles extends IncludableSpecialPage {
if ( !$message->isDisabled() ) {
$this->getOutput()->addWikiText(
Html::rawElement( 'p',
- array( 'lang' => $wgContLang->getCode(), 'dir' => $wgContLang->getDir() ),
+ array( 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ),
"\n" . $message->plain() . "\n"
),
/* $lineStart */ false,
@@ -141,7 +144,7 @@ class NewFilesPager extends ReverseChronologicalPager {
$mode = $this->getRequest()->getVal( 'gallerymode', null );
try {
$this->gallery = ImageGalleryBase::factory( $mode, $this->getContext() );
- } catch ( MWException $e ) {
+ } catch ( Exception $e ) {
// User specified something invalid, fallback to default.
$this->gallery = ImageGalleryBase::factory( false, $this->getContext() );
}
@@ -201,7 +204,10 @@ class NewFilesPager extends ReverseChronologicalPager {
$context = new DerivativeContext( $this->getContext() );
$context->setTitle( $this->getTitle() ); // Remove subpage
$form = new HTMLForm( $fields, $context );
+
$form->setSubmitTextMsg( 'ilsubmit' );
+ $form->setSubmitProgressive();
+
$form->setMethod( 'get' );
$form->setWrapperLegendMsg( 'newimages-legend' );
diff --git a/includes/specials/SpecialNewpages.php b/includes/specials/SpecialNewpages.php
index 0b70bb7e..899c7368 100644
--- a/includes/specials/SpecialNewpages.php
+++ b/includes/specials/SpecialNewpages.php
@@ -56,7 +56,7 @@ class SpecialNewpages extends IncludableSpecialPage {
$opts->add( 'invert', false );
$this->customFilters = array();
- wfRunHooks( 'SpecialNewPagesFilters', array( $this, &$this->customFilters ) );
+ Hooks::run( 'SpecialNewPagesFilters', array( $this, &$this->customFilters ) );
foreach ( $this->customFilters as $key => $params ) {
$opts->add( $key, $params['default'] );
}
@@ -127,6 +127,8 @@ class SpecialNewpages extends IncludableSpecialPage {
$this->showNavigation = !$this->including(); // Maybe changed in setup
$this->setup( $par );
+ $this->addHelpLink( 'Help:New pages' );
+
if ( !$this->including() ) {
// Settings
$this->form();
@@ -198,6 +200,9 @@ class SpecialNewpages extends IncludableSpecialPage {
}
protected function form() {
+ $out = $this->getOutput();
+ $out->addModules( 'mediawiki.userSuggest' );
+
// Consume values
$this->opts->consumeValue( 'offset' ); // don't carry offset, DWIW
$namespace = $this->opts->consumeValue( 'namespace' );
@@ -216,72 +221,62 @@ class SpecialNewpages extends IncludableSpecialPage {
}
$hidden = implode( "\n", $hidden );
- $tagFilter = ChangeTags::buildTagFilterSelector( $tagFilterVal );
- if ( $tagFilter ) {
- list( $tagFilterLabel, $tagFilterSelector ) = $tagFilter;
- }
+ $form = array(
+ 'namespace' => array(
+ 'type' => 'namespaceselect',
+ 'name' => 'namespace',
+ 'label-message' => 'namespace',
+ 'default' => $namespace,
+ ),
+ 'nsinvert' => array(
+ 'type' => 'check',
+ 'name' => 'invert',
+ 'label-message' => 'invert',
+ 'default' => $nsinvert,
+ 'tooltip' => $this->msg( 'tooltip-invert' )->text(),
+ ),
+ 'tagFilter' => array(
+ 'type' => 'tagfilter',
+ 'name' => 'tagfilter',
+ 'label-raw' => $this->msg( 'tag-filter' )->parse(),
+ 'default' => $tagFilterVal,
+ ),
+ 'username' => array(
+ 'type' => 'text',
+ 'name' => 'username',
+ 'label-message' => 'newpages-username',
+ 'default' => $userText,
+ 'id' => 'mw-np-username',
+ 'size' => 30,
+ 'cssclass' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
+ ),
+ );
+
+ $htmlForm = new HTMLForm( $form, $this->getContext() );
- $form = Xml::openElement( 'form', array( 'action' => wfScript() ) ) .
- Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) .
- Xml::fieldset( $this->msg( 'newpages' )->text() ) .
- Xml::openElement( 'table', array( 'id' => 'mw-newpages-table' ) ) .
- '<tr>
- <td class="mw-label">' .
- Xml::label( $this->msg( 'namespace' )->text(), 'namespace' ) .
- '</td>
- <td class="mw-input">' .
- Html::namespaceSelector(
- array(
- 'selected' => $namespace,
- 'all' => 'all',
- ), array(
- 'name' => 'namespace',
- 'id' => 'namespace',
- 'class' => 'namespaceselector',
- )
- ) . '&#160;' .
- Xml::checkLabel(
- $this->msg( 'invert' )->text(),
- 'invert',
- 'nsinvert',
- $nsinvert,
- array( 'title' => $this->msg( 'tooltip-invert' )->text() )
+ $htmlForm->setSubmitText( $this->msg( 'allpagessubmit' )->text() );
+ $htmlForm->setSubmitProgressive();
+ // The form should be visible on each request (inclusive requests with submitted forms), so
+ // return always false here.
+ $htmlForm->setSubmitCallback(
+ function () {
+ return false;
+ }
+ );
+ $htmlForm->setMethod( 'get' );
+
+ $out->addHtml( Xml::fieldset( $this->msg( 'newpages' )->text() ) );
+
+ $htmlForm->show();
+
+ $out->addHtml(
+ Html::rawElement(
+ 'div',
+ null,
+ $this->filterLinks()
) .
- '</td>
- </tr>' . ( $tagFilter ? (
- '<tr>
- <td class="mw-label">' .
- $tagFilterLabel .
- '</td>
- <td class="mw-input">' .
- $tagFilterSelector .
- '</td>
- </tr>' ) : '' ) .
- '<tr>
- <td class="mw-label">' .
- Xml::label( $this->msg( 'newpages-username' )->text(), 'mw-np-username' ) .
- '</td>
- <td class="mw-input">' .
- Xml::input( 'username', 30, $userText, array( 'id' => 'mw-np-username' ) ) .
- '</td>
- </tr>' .
- '<tr> <td></td>
- <td class="mw-submit">' .
- Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) .
- '</td>
- </tr>' .
- '<tr>
- <td></td>
- <td class="mw-input">' .
- $this->filterLinks() .
- '</td>
- </tr>' .
- Xml::closeElement( 'table' ) .
- Xml::closeElement( 'fieldset' ) .
- $hidden .
- Xml::closeElement( 'form' );
-
- $this->getOutput()->addHTML( $form );
+ Xml::closeElement( 'fieldset' )
+ );
}
/**
@@ -340,12 +335,12 @@ class SpecialNewpages extends IncludableSpecialPage {
$hist = Html::rawElement( 'span', array( 'class' => 'mw-newpages-history' ),
$this->msg( 'parentheses' )->rawParams( $histLink )->escaped() );
- $length = Html::element(
+ $length = Html::rawElement(
'span',
array( 'class' => 'mw-newpages-length' ),
- $this->msg( 'brackets' )->params( $this->msg( 'nbytes' )
- ->numParams( $result->length )->text()
- )
+ $this->msg( 'brackets' )->rawParams(
+ $this->msg( 'nbytes' )->numParams( $result->length )->escaped()
+ )->escaped()
);
$ulink = Linker::revUserTools( $rev );
@@ -555,7 +550,7 @@ class NewPagesPager extends ReverseChronologicalPager {
);
$join_conds = array( 'page' => array( 'INNER JOIN', 'page_id=rc_cur_id' ) );
- wfRunHooks( 'SpecialNewpagesConditions',
+ Hooks::run( 'SpecialNewpagesConditions',
array( &$this, $this->opts, &$conds, &$tables, &$fields, &$join_conds ) );
$options = array();
diff --git a/includes/specials/SpecialPageLanguage.php b/includes/specials/SpecialPageLanguage.php
index 2acf23cd..79b2444e 100644
--- a/includes/specials/SpecialPageLanguage.php
+++ b/includes/specials/SpecialPageLanguage.php
@@ -90,10 +90,12 @@ class SpecialPageLanguage extends FormSpecialPage {
return $this->showLogFragment( $this->par );
}
+ protected function getDisplayFormat() {
+ return 'vform';
+ }
+
public function alterForm( HTMLForm $form ) {
- $form->setDisplayFormat( 'vform' );
- $form->setWrapperLegend( false );
- wfRunHooks( 'LanguageSelector', array( $this->getOutput(), 'mw-languageselector' ) );
+ Hooks::run( 'LanguageSelector', array( $this->getOutput(), 'mw-languageselector' ) );
}
/**
diff --git a/includes/specials/SpecialPagesWithProp.php b/includes/specials/SpecialPagesWithProp.php
index f5b19cc6..670a3973 100644
--- a/includes/specials/SpecialPagesWithProp.php
+++ b/includes/specials/SpecialPagesWithProp.php
@@ -83,11 +83,13 @@ class SpecialPagesWithProp extends QueryPage {
*
* @param string $search Prefix to search for
* @param int $limit Maximum number of results to return
+ * @param int $offset Number of pages to skip
* @return string[] Matching subpages
*/
- public function prefixSearchSubpages( $search, $limit = 10 ) {
- $subpages = array_keys( $this->getExistingPropNames() );
- return self::prefixSearchArray( $search, $limit, $subpages );
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ $subpages = array_keys( $this->queryExistingProps( $limit, $offset ) );
+ // We've already limited and offsetted, set to N and 0 respectively.
+ return self::prefixSearchArray( $search, count( $subpages ), $subpages, 0 );
}
/**
@@ -154,23 +156,38 @@ class SpecialPagesWithProp extends QueryPage {
public function getExistingPropNames() {
if ( $this->existingPropNames === null ) {
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select(
- 'page_props',
- 'pp_propname',
- '',
- __METHOD__,
- array( 'DISTINCT', 'ORDER BY' => 'pp_propname' )
- );
- $propnames = array();
- foreach ( $res as $row ) {
- $propnames[$row->pp_propname] = $row->pp_propname;
- }
- $this->existingPropNames = $propnames;
+ $this->existingPropNames = $this->queryExistingProps();
}
return $this->existingPropNames;
}
+ protected function queryExistingProps( $limit = null, $offset = 0 ) {
+ $opts = array(
+ 'DISTINCT', 'ORDER BY' => 'pp_propname'
+ );
+ if ( $limit ) {
+ $opts['LIMIT'] = $limit;
+ }
+ if ( $offset ) {
+ $opts['OFFSET'] = $offset;
+ }
+
+ $res = wfGetDB( DB_SLAVE )->select(
+ 'page_props',
+ 'pp_propname',
+ '',
+ __METHOD__,
+ $opts
+ );
+
+ $propnames = array();
+ foreach ( $res as $row ) {
+ $propnames[$row->pp_propname] = $row->pp_propname;
+ }
+
+ return $propnames;
+ }
+
protected function getGroupName() {
return 'pages';
}
diff --git a/includes/specials/SpecialPasswordReset.php b/includes/specials/SpecialPasswordReset.php
index 3061c85b..a2dc2add 100644
--- a/includes/specials/SpecialPasswordReset.php
+++ b/includes/specials/SpecialPasswordReset.php
@@ -103,16 +103,13 @@ class SpecialPasswordReset extends FormSpecialPage {
return $a;
}
+ protected function getDisplayFormat() {
+ return 'vform';
+ }
+
public function alterForm( HTMLForm $form ) {
$resetRoutes = $this->getConfig()->get( 'PasswordResetRoutes' );
- $form->setDisplayFormat( 'vform' );
- // Turn the old-school line around the form off.
- // XXX This wouldn't be necessary here if we could set the format of
- // the HTMLForm to 'vform' at its creation, but there's no way to do so
- // from a FormSpecialPage class.
- $form->setWrapperLegend( false );
-
$form->addHiddenFields( $this->getRequest()->getValues( 'returnto', 'returntoquery' ) );
$i = 0;
@@ -195,7 +192,7 @@ class SpecialPasswordReset extends FormSpecialPage {
// Check for hooks (captcha etc), and allow them to modify the users list
$error = array();
- if ( !wfRunHooks( 'SpecialPasswordResetOnSubmit', array( &$users, $data, &$error ) ) ) {
+ if ( !Hooks::run( 'SpecialPasswordResetOnSubmit', array( &$users, $data, &$error ) ) ) {
return array( $error );
}
@@ -246,7 +243,7 @@ class SpecialPasswordReset extends FormSpecialPage {
return array( 'badipaddress' );
}
$caller = $this->getUser();
- wfRunHooks( 'User::mailPasswordInternal', array( &$caller, &$ip, &$firstUser ) );
+ Hooks::run( 'User::mailPasswordInternal', array( &$caller, &$ip, &$firstUser ) );
$username = $caller->getName();
$msg = IP::isValid( $username )
? 'passwordreset-emailtext-ip'
diff --git a/includes/specials/SpecialPopularpages.php b/includes/specials/SpecialPopularpages.php
deleted file mode 100644
index 2a80f651..00000000
--- a/includes/specials/SpecialPopularpages.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-/**
- * Implements Special:PopularPages
- *
- * 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
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A special page that list most viewed pages
- *
- * @ingroup SpecialPage
- */
-class PopularPagesPage extends QueryPage {
- function __construct( $name = 'Popularpages' ) {
- parent::__construct( $name );
- }
-
- function isExpensive() {
- # page_counter is not indexed
- return true;
- }
-
- function isSyndicated() {
- return false;
- }
-
- function getQueryInfo() {
- return array(
- 'tables' => array( 'page' ),
- 'fields' => array(
- 'namespace' => 'page_namespace',
- 'title' => 'page_title',
- 'value' => 'page_counter' ),
- 'conds' => array(
- 'page_is_redirect' => 0,
- 'page_namespace' => MWNamespace::getContentNamespaces()
- )
- );
- }
-
- /**
- * @param Skin $skin
- * @param object $result Result row
- * @return string
- */
- function formatResult( $skin, $result ) {
- global $wgContLang;
-
- $title = Title::makeTitleSafe( $result->namespace, $result->title );
- if ( !$title ) {
- return Html::element(
- 'span',
- array( 'class' => 'mw-invalidtitle' ),
- Linker::getInvalidTitleDescription(
- $this->getContext(),
- $result->namespace,
- $result->title )
- );
- }
-
- $link = Linker::linkKnown(
- $title,
- htmlspecialchars( $wgContLang->convert( $title->getPrefixedText() ) )
- );
- $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 cea00fa6..7371da74 100644
--- a/includes/specials/SpecialPreferences.php
+++ b/includes/specials/SpecialPreferences.php
@@ -55,6 +55,8 @@ class SpecialPreferences extends SpecialPage {
);
}
+ $this->addHelpLink( 'Help:Preferences' );
+
$htmlForm = Preferences::getFormObject( $this->getUser(), $this->getContext() );
$htmlForm->setSubmitCallback( array( 'Preferences', 'tryUISubmit' ) );
diff --git a/includes/specials/SpecialPrefixindex.php b/includes/specials/SpecialPrefixindex.php
index 2e67e2b5..5a67d924 100644
--- a/includes/specials/SpecialPrefixindex.php
+++ b/includes/specials/SpecialPrefixindex.php
@@ -102,7 +102,10 @@ class SpecialPrefixindex extends SpecialAllPages {
*/
protected function namespacePrefixForm( $namespace = NS_MAIN, $from = '' ) {
$out = Xml::openElement( 'div', array( 'class' => 'namespaceoptions' ) );
- $out .= Xml::openElement( 'form', array( 'method' => 'get', 'action' => $this->getConfig()->get( 'Script' ) ) );
+ $out .= Xml::openElement(
+ 'form',
+ array( 'method' => 'get', 'action' => $this->getConfig()->get( 'Script' ) )
+ );
$out .= Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() );
$out .= Xml::openElement( 'fieldset' );
$out .= Xml::element( 'legend', null, $this->msg( 'allpages' )->text() );
diff --git a/includes/specials/SpecialProtectedpages.php b/includes/specials/SpecialProtectedpages.php
index 0ba73857..00e56c1c 100644
--- a/includes/specials/SpecialProtectedpages.php
+++ b/includes/specials/SpecialProtectedpages.php
@@ -39,11 +39,6 @@ class SpecialProtectedpages extends SpecialPage {
$this->outputHeader();
$this->getOutput()->addModuleStyles( 'mediawiki.special' );
- // Purge expired entries on one in every 10 queries
- if ( !mt_rand( 0, 10 ) ) {
- Title::purgeExpiredRestrictions();
- }
-
$request = $this->getRequest();
$type = $request->getVal( $this->IdType );
$level = $request->getVal( $this->IdLevel );
@@ -353,7 +348,7 @@ class ProtectedPagesPager extends TablePager {
/**
* @param string $field
* @param string $value
- * @return string
+ * @return string HTML
* @throws MWException
*/
function formatValue( $field, $value ) {
@@ -372,7 +367,8 @@ class ProtectedPagesPager extends TablePager {
$this->msg( 'protectedpages-unknown-timestamp' )->escaped()
);
} else {
- $formatted = $this->getLanguage()->userTimeAndDate( $value, $this->getUser() );
+ $formatted = htmlspecialchars( $this->getLanguage()->userTimeAndDate(
+ $value, $this->getUser() ) );
}
break;
@@ -402,7 +398,8 @@ class ProtectedPagesPager extends TablePager {
break;
case 'pr_expiry':
- $formatted = $this->getLanguage()->formatExpiry( $value, /* User preference timezone */true );
+ $formatted = htmlspecialchars( $this->getLanguage()->formatExpiry(
+ $value, /* User preference timezone */true ) );
$title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
if ( $this->getUser()->isAllowed( 'protect' ) && $title ) {
$changeProtection = Linker::linkKnown(
@@ -454,7 +451,7 @@ class ProtectedPagesPager extends TablePager {
// Messages: restriction-level-sysop, restriction-level-autoconfirmed
$params[] = $this->msg( 'restriction-level-' . $row->pr_level )->escaped();
if ( $row->pr_cascade ) {
- $params[] = $this->msg( 'protect-summary-cascade' )->text();
+ $params[] = $this->msg( 'protect-summary-cascade' )->escaped();
}
$formatted = $this->getLanguage()->commaList( $params );
break;
@@ -493,7 +490,7 @@ class ProtectedPagesPager extends TablePager {
function getQueryInfo() {
$conds = $this->mConds;
$conds[] = 'pr_expiry > ' . $this->mDb->addQuotes( $this->mDb->timestamp() ) .
- 'OR pr_expiry IS NULL';
+ ' OR pr_expiry IS NULL';
$conds[] = 'page_id=pr_page';
$conds[] = 'pr_type=' . $this->mDb->addQuotes( $this->type );
diff --git a/includes/specials/SpecialProtectedtitles.php b/includes/specials/SpecialProtectedtitles.php
index a40da87d..dd9198cb 100644
--- a/includes/specials/SpecialProtectedtitles.php
+++ b/includes/specials/SpecialProtectedtitles.php
@@ -38,11 +38,6 @@ class SpecialProtectedtitles extends SpecialPage {
$this->setHeaders();
$this->outputHeader();
- // Purge expired entries on one in every 10 queries
- if ( !mt_rand( 0, 10 ) ) {
- Title::purgeExpiredRestrictions();
- }
-
$request = $this->getRequest();
$type = $request->getVal( $this->IdType );
$level = $request->getVal( $this->IdLevel );
@@ -72,7 +67,6 @@ class SpecialProtectedtitles extends SpecialPage {
* @return string
*/
function formatRow( $row ) {
- wfProfileIn( __METHOD__ );
static $infinity = null;
@@ -82,7 +76,6 @@ class SpecialProtectedtitles extends SpecialPage {
$title = Title::makeTitleSafe( $row->pt_namespace, $row->pt_title );
if ( !$title ) {
- wfProfileOut( __METHOD__ );
return Html::rawElement(
'li',
@@ -119,8 +112,6 @@ class SpecialProtectedtitles extends SpecialPage {
)->escaped();
}
- wfProfileOut( __METHOD__ );
-
// @todo i18n: This should use a comma separator instead of a hard coded comma, right?
return '<li>' . $lang->specialList( $link, implode( $description_items, ', ' ) ) . "</li>\n";
}
@@ -227,7 +218,6 @@ class ProtectedTitlesPager extends AlphabeticPager {
}
function getStartBody() {
- wfProfileIn( __METHOD__ );
# Do a link batch query
$this->mResult->seek( 0 );
$lb = new LinkBatch;
@@ -237,7 +227,6 @@ class ProtectedTitlesPager extends AlphabeticPager {
}
$lb->execute();
- wfProfileOut( __METHOD__ );
return '';
}
@@ -258,7 +247,8 @@ class ProtectedTitlesPager extends AlphabeticPager {
*/
function getQueryInfo() {
$conds = $this->mConds;
- $conds[] = 'pt_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() );
+ $conds[] = 'pt_expiry > ' . $this->mDb->addQuotes( $this->mDb->timestamp() ) .
+ ' OR pt_expiry IS NULL';
if ( $this->level ) {
$conds['pt_create_perm'] = $this->level;
}
diff --git a/includes/specials/SpecialRandomInCategory.php b/includes/specials/SpecialRandomInCategory.php
index 570ab3bf..b5c9e19a 100644
--- a/includes/specials/SpecialRandomInCategory.php
+++ b/includes/specials/SpecialRandomInCategory.php
@@ -68,6 +68,8 @@ class SpecialRandomInCategory extends FormSpecialPage {
}
protected function getFormFields() {
+ $this->addHelpLink( 'Help:RandomInCategory' );
+
$form = array(
'category' => array(
'type' => 'text',
@@ -117,7 +119,7 @@ class SpecialRandomInCategory extends FormSpecialPage {
return Status::newFatal( $msg );
} elseif ( !$this->category ) {
- return; // no data sent
+ return false; // no data sent
}
$title = $this->getRandomTitle();
@@ -179,12 +181,12 @@ class SpecialRandomInCategory extends FormSpecialPage {
* @param float $rand Random number between 0 and 1
* @param int $offset Extra offset to fudge randomness
* @param bool $up True to get the result above the random number, false for below
- *
+ * @return array Query information.
+ * @throws MWException
* @note The $up parameter is supposed to counteract what would happen if there
* was a large gap in the distribution of cl_timestamp values. This way instead
* of things to the right of the gap being favoured, both sides of the gap
* are favoured.
- * @return array Query information.
*/
protected function getQueryInfo( $rand, $offset, $up ) {
$op = $up ? '>=' : '<=';
@@ -230,7 +232,7 @@ class SpecialRandomInCategory extends FormSpecialPage {
if ( !$this->minTimestamp || !$this->maxTimestamp ) {
try {
list( $this->minTimestamp, $this->maxTimestamp ) = $this->getMinAndMaxForCat( $this->category );
- } catch ( MWException $e ) {
+ } catch ( Exception $e ) {
// Possibly no entries in category.
return false;
}
diff --git a/includes/specials/SpecialRandompage.php b/includes/specials/SpecialRandompage.php
index 6d8f59b5..73a88b9e 100644
--- a/includes/specials/SpecialRandompage.php
+++ b/includes/specials/SpecialRandompage.php
@@ -106,7 +106,7 @@ class RandomPage extends SpecialPage {
$randstr = wfRandom();
$title = null;
- if ( !wfRunHooks(
+ if ( !Hooks::run(
'SpecialRandomGetRandomTitle',
array( &$randstr, &$this->isRedir, &$this->namespaces, &$this->extra, &$title )
) ) {
diff --git a/includes/specials/SpecialRecentchanges.php b/includes/specials/SpecialRecentchanges.php
index e6d8f1c3..64b0ecae 100644
--- a/includes/specials/SpecialRecentchanges.php
+++ b/includes/specials/SpecialRecentchanges.php
@@ -95,7 +95,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
protected function getCustomFilters() {
if ( $this->customFilters === null ) {
$this->customFilters = parent::getCustomFilters();
- wfRunHooks( 'SpecialRecentChangesFilters', array( $this, &$this->customFilters ), '1.23' );
+ Hooks::run( 'SpecialRecentChangesFilters', array( $this, &$this->customFilters ), '1.23' );
}
return $this->customFilters;
@@ -252,9 +252,11 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
return $rows;
}
- protected function runMainQueryHook( &$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts ) {
+ protected function runMainQueryHook( &$tables, &$fields, &$conds,
+ &$query_options, &$join_conds, $opts
+ ) {
return parent::runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds, $opts )
- && wfRunHooks(
+ && Hooks::run(
'SpecialRecentChangesQuery',
array( &$conds, &$tables, &$join_conds, $opts, &$query_options, &$fields ),
'1.23'
@@ -311,7 +313,9 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
$rc = RecentChange::newFromRow( $obj );
$rc->counter = $counter++;
# Check if the page has been updated since the last visit
- if ( $this->getConfig()->get( 'ShowUpdatedMarker' ) && !empty( $obj->wl_notificationtimestamp ) ) {
+ if ( $this->getConfig()->get( 'ShowUpdatedMarker' )
+ && !empty( $obj->wl_notificationtimestamp )
+ ) {
$rc->notificationtimestamp = ( $obj->rc_timestamp >= $obj->wl_notificationtimestamp );
} else {
$rc->notificationtimestamp = false; // Default
@@ -440,11 +444,11 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
$message = $this->msg( 'recentchangestext' )->inContentLanguage();
if ( !$message->isDisabled() ) {
$this->getOutput()->addWikiText(
- Html::rawElement( 'p',
- array( 'lang' => $wgContLang->getCode(), 'dir' => $wgContLang->getDir() ),
+ Html::rawElement( 'div',
+ array( 'lang' => $wgContLang->getHtmlCode(), 'dir' => $wgContLang->getDir() ),
"\n" . $message->plain() . "\n"
),
- /* $lineStart */ false,
+ /* $lineStart */ true,
/* $interface */ false
);
}
@@ -475,7 +479,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
// Don't fire the hook for subclasses. (Or should we?)
if ( $this->getName() === 'Recentchanges' ) {
- wfRunHooks( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) );
+ Hooks::run( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) );
}
return $extraOpts;
@@ -732,7 +736,8 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
$link = $this->makeOptionsLink( $linkMessage->text(),
array( $key => 1 - $options[$key] ), $nondefaults );
- $links[] = "<span class=\"$msg rcshowhideoption\">" . $this->msg( $msg )->rawParams( $link )->escaped() . '</span>';
+ $links[] = "<span class=\"$msg rcshowhideoption\">"
+ . $this->msg( $msg )->rawParams( $link )->escaped() . '</span>';
}
// show from this onward link
diff --git a/includes/specials/SpecialRedirect.php b/includes/specials/SpecialRedirect.php
index 2022d748..72d21ebe 100644
--- a/includes/specials/SpecialRedirect.php
+++ b/includes/specials/SpecialRedirect.php
@@ -2,7 +2,6 @@
/**
* Implements Special:Redirect
*
- * @section LICENSE
* 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
@@ -263,6 +262,20 @@ class SpecialRedirect extends FormSpecialPage {
$form->setMethod( 'get' );
}
+ /**
+ * Return an array of subpages that this special page will accept.
+ *
+ * @return string[] subpages
+ */
+ protected function getSubpagesForPrefixSearch() {
+ return array(
+ "file",
+ "page",
+ "revision",
+ "user",
+ );
+ }
+
protected function getGroupName() {
return 'redirects';
}
diff --git a/includes/specials/SpecialResetTokens.php b/includes/specials/SpecialResetTokens.php
index 4add7421..ba2b9a5b 100644
--- a/includes/specials/SpecialResetTokens.php
+++ b/includes/specials/SpecialResetTokens.php
@@ -44,7 +44,7 @@ class SpecialResetTokens extends FormSpecialPage {
$tokens = array(
array( 'preference' => 'watchlisttoken', 'label-message' => 'resettokens-watchlist-token' ),
);
- wfRunHooks( 'SpecialResetTokensTokens', array( &$tokens ) );
+ Hooks::run( 'SpecialResetTokensTokens', array( &$tokens ) );
$hiddenPrefs = $this->getConfig()->get( 'HiddenPrefs' );
$tokens = array_filter( $tokens, function ( $tok ) use ( $hiddenPrefs ) {
diff --git a/includes/specials/SpecialRevisiondelete.php b/includes/specials/SpecialRevisiondelete.php
index 7eea71da..9e2ca277 100644
--- a/includes/specials/SpecialRevisiondelete.php
+++ b/includes/specials/SpecialRevisiondelete.php
@@ -132,18 +132,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
// $this->ids = array_map( 'intval', $this->ids );
$this->ids = array_unique( array_filter( $this->ids ) );
- if ( $request->getVal( 'action' ) == 'historysubmit'
- || $request->getVal( 'action' ) == 'revisiondelete'
- ) {
- // For show/hide form submission from history page
- // Since we are access through index.php?title=XXX&action=historysubmit
- // getFullTitle() will contain the target title and not our title
- $this->targetObj = $this->getFullTitle();
- $this->typeName = 'revision';
- } else {
- $this->typeName = $request->getVal( 'type' );
- $this->targetObj = Title::newFromText( $request->getText( 'target' ) );
- }
+ $this->typeName = $request->getVal( 'type' );
+ $this->targetObj = Title::newFromText( $request->getText( 'target' ) );
# For reviewing deleted files...
$this->archiveName = $request->getVal( 'file' );
@@ -293,6 +283,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
* Show a deleted file version requested by the visitor.
* @todo Mostly copied from Special:Undelete. Refactor.
* @param string $archiveName
+ * @throws MWException
+ * @throws PermissionsError
*/
protected function tryShowFile( $archiveName ) {
$repo = RepoGroup::singleton()->getLocalRepo();
@@ -372,10 +364,12 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
$userAllowed = true;
// Messages: revdelete-selected-text, revdelete-selected-file, logdelete-selected
- $this->getOutput()->wrapWikiMsg( "<strong>$1</strong>", array( $this->typeLabels['selected'],
+ $out = $this->getOutput();
+ $out->wrapWikiMsg( "<strong>$1</strong>", array( $this->typeLabels['selected'],
$this->getLanguage()->formatNum( count( $this->ids ) ), $this->targetObj->getPrefixedText() ) );
- $this->getOutput()->addHTML( "<ul>" );
+ $this->addHelpLink( 'Help:RevisionDelete' );
+ $out->addHTML( "<ul>" );
$numRevisions = 0;
// Live revisions...
@@ -393,14 +387,14 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
}
$numRevisions++;
- $this->getOutput()->addHTML( $item->getHTML() );
+ $out->addHTML( $item->getHTML() );
}
if ( !$numRevisions ) {
throw new ErrorPageError( 'revdelete-nooldid-title', 'revdelete-nooldid-text' );
}
- $this->getOutput()->addHTML( "</ul>" );
+ $out->addHTML( "</ul>" );
// Explanation text
$this->addUsageText();
@@ -411,7 +405,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
// Show form if the user can submit
if ( $this->mIsAllowed ) {
- $out = Xml::openElement( 'form', array( 'method' => 'post',
+ $form = Xml::openElement( 'form', array( 'method' => 'post',
'action' => $this->getPageTitle()->getLocalURL( array( 'action' => 'submit' ) ),
'id' => 'mw-revdel-form-revisions' ) ) .
Xml::fieldset( $this->msg( 'revdelete-legend' )->text() ) .
@@ -463,12 +457,12 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
array(),
array( 'action' => 'edit' )
);
- $out .= Xml::tags( 'p', array( 'class' => 'mw-revdel-editreasons' ), $link ) . "\n";
+ $form .= Xml::tags( 'p', array( 'class' => 'mw-revdel-editreasons' ), $link ) . "\n";
}
} else {
- $out = '';
+ $form = '';
}
- $this->getOutput()->addHTML( $out );
+ $out->addHTML( $form );
}
/**
diff --git a/includes/specials/SpecialRunJobs.php b/includes/specials/SpecialRunJobs.php
index d4a06eb5..8cf93670 100644
--- a/includes/specials/SpecialRunJobs.php
+++ b/includes/specials/SpecialRunJobs.php
@@ -22,6 +22,8 @@
* @author Aaron Schulz
*/
+use MediaWiki\Logger\LoggerFactory;
+
/**
* Special page designed for running background tasks (internal use only)
*
@@ -61,10 +63,11 @@ class SpecialRunJobs extends UnlistedSpecialPage {
$squery = $params;
unset( $squery['signature'] );
- $cSig = self::getQuerySignature( $squery, $this->getConfig()->get( 'SecretKey' ) ); // correct signature
- $rSig = $params['signature']; // provided signature
+ $correctSignature = self::getQuerySignature( $squery, $this->getConfig()->get( 'SecretKey' ) );
+ $providedSignature = $params['signature'];
- $verified = is_string( $rSig ) && hash_equals( $cSig, $rSig );
+ $verified = is_string( $providedSignature )
+ && hash_equals( $correctSignature, $providedSignature );
if ( !$verified || $params['sigexpiry'] < time() ) {
header( "HTTP/1.0 400 Bad Request" );
print 'Invalid or stale signature provided';
@@ -88,7 +91,7 @@ class SpecialRunJobs extends UnlistedSpecialPage {
// Do all of the specified tasks...
if ( in_array( 'jobs', explode( '|', $params['tasks'] ) ) ) {
- $runner = new JobRunner();
+ $runner = new JobRunner( LoggerFactory::getInstance( 'runJobs' ) );
$response = $runner->run( array(
'type' => $params['type'],
'maxJobs' => $params['maxjobs'] ? $params['maxjobs'] : 1,
diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php
index 88ab7d82..608d62e6 100644
--- a/includes/specials/SpecialSearch.php
+++ b/includes/specials/SpecialSearch.php
@@ -63,7 +63,7 @@ class SpecialSearch extends SpecialPage {
/**
* @var string
*/
- protected $didYouMeanHtml, $fulltext;
+ protected $fulltext;
const NAMESPACES_CURRENT = 'sense';
@@ -165,7 +165,6 @@ class SpecialSearch extends SpecialPage {
}
}
- $this->didYouMeanHtml = ''; # html of did you mean... link
$this->fulltext = $request->getVal( 'fulltext' );
$this->profile = $profile;
}
@@ -196,7 +195,7 @@ class SpecialSearch extends SpecialPage {
# No match, generate an edit URL
$title = Title::newFromText( $term );
if ( !is_null( $title ) ) {
- wfRunHooks( 'SpecialSearchNogomatch', array( &$title ) );
+ Hooks::run( 'SpecialSearchNogomatch', array( &$title ) );
}
$this->showResults( $term );
}
@@ -207,14 +206,14 @@ class SpecialSearch extends SpecialPage {
public function showResults( $term ) {
global $wgContLang;
- $profile = new ProfileSection( __METHOD__ );
$search = $this->getSearchEngine();
$search->setLimitOffset( $this->limit, $this->offset );
$search->setNamespaces( $this->namespaces );
$search->prefix = $this->mPrefix;
$term = $search->transformSearchTerm( $term );
+ $didYouMeanHtml = '';
- wfRunHooks( 'SpecialSearchSetupEngine', array( $this, $this->profile, $search ) );
+ Hooks::run( 'SpecialSearchSetupEngine', array( $this, $this->profile, $search ) );
$this->setupPage( $term );
@@ -289,11 +288,14 @@ class SpecialSearch extends SpecialPage {
$stParams
);
- $this->didYouMeanHtml = '<div class="searchdidyoumean">'
- . $this->msg( 'search-suggest' )->rawParams( $suggestLink )->text() . '</div>';
+ # html of did you mean... search suggestion link
+ $didYouMeanHtml =
+ Xml::openElement( 'div', array( 'class' => 'searchdidyoumean' ) ) .
+ $this->msg( 'search-suggest' )->rawParams( $suggestLink )->text() .
+ Xml::closeElement( 'div' );
}
- if ( !wfRunHooks( 'SpecialSearchResultsPrepend', array( $this, $out, $term ) ) ) {
+ if ( !Hooks::run( 'SpecialSearchResultsPrepend', array( $this, $out, $term ) ) ) {
# Hook requested termination
return;
}
@@ -303,7 +305,7 @@ class SpecialSearch extends SpecialPage {
Xml::openElement(
'form',
array(
- 'id' => ( $this->profile === 'advanced' ? 'powersearch' : 'search' ),
+ 'id' => ( $this->isPowerSearch() ? 'powersearch' : 'search' ),
'method' => 'get',
'action' => wfScript(),
)
@@ -330,8 +332,10 @@ class SpecialSearch extends SpecialPage {
Xml::openElement( 'div', array( 'id' => 'mw-search-top-table' ) ) .
$this->shortDialog( $term, $num, $totalRes ) .
Xml::closeElement( 'div' ) .
- $this->formHeader( $term ) .
- Xml::closeElement( 'form' )
+ $this->searchProfileTabs( $term ) .
+ $this->searchOptions( $term ) .
+ Xml::closeElement( 'form' ) .
+ $didYouMeanHtml
);
$filePrefix = $wgContLang->getFormattedNsText( NS_FILE ) . ':';
@@ -360,7 +364,7 @@ class SpecialSearch extends SpecialPage {
);
}
}
- wfRunHooks( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) );
+ Hooks::run( 'SpecialSearchResults', array( $term, &$titleMatches, &$textMatches ) );
$out->parserOptions()->setEditSection( false );
if ( $titleMatches ) {
@@ -426,21 +430,24 @@ class SpecialSearch extends SpecialPage {
return;
}
+ $messageName = 'searchmenu-new-nocreate';
$linkClass = 'mw-search-createlink';
- if ( $title->isKnown() ) {
- $messageName = 'searchmenu-exists';
- $linkClass = 'mw-search-exists';
- } elseif ( $title->quickUserCan( 'create', $this->getUser() ) ) {
- $messageName = 'searchmenu-new';
- } else {
- $messageName = 'searchmenu-new-nocreate';
+
+ if ( !$title->isExternal() ) {
+ if ( $title->isKnown() ) {
+ $messageName = 'searchmenu-exists';
+ $linkClass = 'mw-search-exists';
+ } elseif ( $title->quickUserCan( 'create', $this->getUser() ) ) {
+ $messageName = 'searchmenu-new';
+ }
}
+
$params = array(
$messageName,
wfEscapeWikiText( $title->getPrefixedText() ),
Message::numParam( $num )
);
- wfRunHooks( 'SpecialSearchCreateLink', array( $title, &$params ) );
+ Hooks::run( 'SpecialSearchCreateLink', array( $title, &$params ) );
// Extensions using the hook might still return an empty $messageName
if ( $messageName ) {
@@ -455,8 +462,6 @@ class SpecialSearch extends SpecialPage {
* @param string $term
*/
protected function setupPage( $term ) {
- # Should advanced UI be used?
- $this->searchAdvanced = ( $this->profile === 'advanced' );
$out = $this->getOutput();
if ( strval( $term ) !== '' ) {
$out->setPageTitle( $this->msg( 'searchresults' ) );
@@ -470,6 +475,15 @@ class SpecialSearch extends SpecialPage {
}
/**
+ * Return true if current search is a power (advanced) search
+ *
+ * @return bool
+ */
+ protected function isPowerSearch() {
+ return $this->profile === 'advanced';
+ }
+
+ /**
* Extract "power search" namespace settings from the request object,
* returning a list of index numbers to search.
*
@@ -494,7 +508,7 @@ class SpecialSearch extends SpecialPage {
*/
protected function powerSearchOptions() {
$opt = array();
- if ( $this->profile !== 'advanced' ) {
+ if ( !$this->isPowerSearch() ) {
$opt['profile'] = $this->profile;
} else {
foreach ( $this->namespaces as $n ) {
@@ -519,7 +533,7 @@ class SpecialSearch extends SpecialPage {
$request->getVal( 'nsRemember' ),
'searchnamespace',
$request
- )
+ ) && !wfReadOnly()
) {
// Reset namespace preferences: namespaces are not searched
// when they're not mentioned in the URL parameters.
@@ -549,7 +563,6 @@ class SpecialSearch extends SpecialPage {
protected function showMatches( &$matches ) {
global $wgContLang;
- $profile = new ProfileSection( __METHOD__ );
$terms = $wgContLang->convertForSearchResult( $matches->termMatches() );
$out = "<ul class='mw-search-results'>\n";
@@ -575,7 +588,6 @@ class SpecialSearch extends SpecialPage {
* @return string
*/
protected function showHit( $result, $terms ) {
- $profile = new ProfileSection( __METHOD__ );
if ( $result->isBrokenTitle() ) {
return '';
@@ -583,7 +595,7 @@ class SpecialSearch extends SpecialPage {
$title = $result->getTitle();
- $titleSnippet = $result->getTitleSnippet( $terms );
+ $titleSnippet = $result->getTitleSnippet();
if ( $titleSnippet == '' ) {
$titleSnippet = null;
@@ -591,7 +603,7 @@ class SpecialSearch extends SpecialPage {
$link_t = clone $title;
- wfRunHooks( 'ShowSearchHitTitle',
+ Hooks::run( 'ShowSearchHitTitle',
array( &$link_t, &$titleSnippet, $result, $terms, $this ) );
$link = Linker::linkKnown(
@@ -615,11 +627,12 @@ class SpecialSearch extends SpecialPage {
// format redirects / relevant sections
$redirectTitle = $result->getRedirectTitle();
- $redirectText = $result->getRedirectSnippet( $terms );
+ $redirectText = $result->getRedirectSnippet();
$sectionTitle = $result->getSectionTitle();
- $sectionText = $result->getSectionSnippet( $terms );
- $redirect = '';
+ $sectionText = $result->getSectionSnippet();
+ $categorySnippet = $result->getCategorySnippet();
+ $redirect = '';
if ( !is_null( $redirectTitle ) ) {
if ( $redirectText == '' ) {
$redirectText = null;
@@ -632,7 +645,6 @@ class SpecialSearch extends SpecialPage {
}
$section = '';
-
if ( !is_null( $sectionTitle ) ) {
if ( $sectionText == '' ) {
$sectionText = null;
@@ -644,6 +656,13 @@ class SpecialSearch extends SpecialPage {
"</span>";
}
+ $category = '';
+ if ( $categorySnippet ) {
+ $category = "<span class='searchalttitle'>" .
+ $this->msg( 'search-category' )->rawParams( $categorySnippet )->text() .
+ "</span>";
+ }
+
// format text extract
$extract = "<div class='searchresult'>" . $result->getTextSnippet( $terms ) . "</div>";
@@ -688,7 +707,7 @@ class SpecialSearch extends SpecialPage {
$thumb->toHtml( array( 'desc-link' => true ) ) .
'</td>' .
'<td style="vertical-align: top;">' .
- "{$link} {$redirect} {$section} {$fileMatch}" .
+ "{$link} {$redirect} {$category} {$section} {$fileMatch}" .
$extract .
"<div class='mw-search-result-data'>{$desc} - {$date}</div>" .
'</td>' .
@@ -702,14 +721,14 @@ class SpecialSearch extends SpecialPage {
$html = null;
$score = '';
- if ( wfRunHooks( 'ShowSearchHit', array(
+ if ( Hooks::run( '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} {$fileMatch}</div> {$extract}\n" .
+ "{$link} {$redirect} {$category} {$section} {$fileMatch}</div> {$extract}\n" .
"<div class='mw-search-result-data'>{$size} - {$date}</div>" .
"</li>\n";
}
@@ -727,7 +746,6 @@ class SpecialSearch extends SpecialPage {
*/
protected function showInterwiki( $matches, $query ) {
global $wgContLang;
- $profile = new ProfileSection( __METHOD__ );
$out = "<div id='mw-search-interwiki'><div id='mw-search-interwiki-caption'>" .
$this->msg( 'search-interwiki-caption' )->text() . "</div>\n";
@@ -778,7 +796,6 @@ class SpecialSearch extends SpecialPage {
* @return string
*/
protected function showInterwikiHit( $result, $lastInterwiki, $query, $customCaptions ) {
- $profile = new ProfileSection( __METHOD__ );
if ( $result->isBrokenTitle() ) {
return '';
@@ -885,10 +902,7 @@ class SpecialSearch extends SpecialPage {
// be arranged nicely while still accommodating different screen widths
$namespaceTables = '';
for ( $i = 0; $i < $numRows; $i += 4 ) {
- $namespaceTables .= Xml::openElement(
- 'table',
- array( 'cellpadding' => 0, 'cellspacing' => 0 )
- );
+ $namespaceTables .= Xml::openElement( 'table' );
for ( $j = $i; $j < $i + 4 && $j < $numRows; $j++ ) {
$namespaceTables .= Xml::tags( 'tr', null, $rows[$j] );
@@ -899,7 +913,7 @@ class SpecialSearch extends SpecialPage {
$showSections = array( 'namespaceTables' => $namespaceTables );
- wfRunHooks( 'SpecialSearchPowerBox', array( &$showSections, $term, $opts ) );
+ Hooks::run( 'SpecialSearchPowerBox', array( &$showSections, $term, $opts ) );
$hidden = '';
foreach ( $opts as $key => $value ) {
@@ -911,7 +925,7 @@ class SpecialSearch extends SpecialPage {
$user = $this->getUser();
if ( $user->isLoggedIn() ) {
$remember .= Xml::checkLabel(
- wfMessage( 'powersearch-remember' )->text(),
+ $this->msg( 'powersearch-remember' )->text(),
'nsRemember',
'mw-search-powersearch-remember',
false,
@@ -970,7 +984,7 @@ class SpecialSearch extends SpecialPage {
)
);
- wfRunHooks( 'SpecialSearchProfiles', array( &$profiles ) );
+ Hooks::run( 'SpecialSearchProfiles', array( &$profiles ) );
foreach ( $profiles as &$data ) {
if ( !is_array( $data['namespaces'] ) ) {
@@ -986,8 +1000,8 @@ class SpecialSearch extends SpecialPage {
* @param string $term
* @return string
*/
- protected function formHeader( $term ) {
- $out = Xml::openElement( 'div', array( 'class' => 'mw-search-formheader' ) );
+ protected function searchProfileTabs( $term ) {
+ $out = Xml::openElement( 'div', array( 'class' => 'mw-search-profile-tabs' ) );
$bareterm = $term;
if ( $this->startsWithImage( $term ) ) {
@@ -1028,15 +1042,23 @@ class SpecialSearch extends SpecialPage {
$out .= Xml::element( 'div', array( 'style' => 'clear:both' ), '', false );
$out .= Xml::closeElement( 'div' );
- // Hidden stuff
+ return $out;
+ }
+
+ /**
+ * @param string $term Search term
+ * @return string
+ */
+ protected function searchOptions( $term ) {
+ $out = '';
$opts = array();
$opts['profile'] = $this->profile;
- if ( $this->profile === 'advanced' ) {
+ if ( $this->isPowerSearch() ) {
$out .= $this->powerSearchBox( $term, $opts );
} else {
$form = '';
- wfRunHooks( 'SpecialSearchProfileForm', array( $this, &$form, $this->profile, $term, $opts ) );
+ Hooks::run( 'SpecialSearchProfileForm', array( $this, &$form, $this->profile, $term, $opts ) );
$out .= $form;
}
@@ -1054,15 +1076,16 @@ class SpecialSearch extends SpecialPage {
$out .= Html::hidden( 'profile', $this->profile ) . "\n";
// Term box
$out .= Html::input( 'search', $term, 'search', array(
- 'id' => $this->profile === 'advanced' ? 'powerSearchText' : 'searchText',
+ 'id' => $this->isPowerSearch() ? 'powerSearchText' : 'searchText',
'size' => '50',
- 'autofocus',
+ 'autofocus' => trim( $term ) === '',
'class' => 'mw-ui-input mw-ui-input-inline',
) ) . "\n";
$out .= Html::hidden( 'fulltext', 'Search' ) . "\n";
- $out .= Xml::submitButton(
+ $out .= Html::submitButton(
$this->msg( 'searchbutton' )->text(),
- array( 'class' => array( 'mw-ui-button', 'mw-ui-progressive' ) )
+ array( 'class' => 'mw-ui-button mw-ui-progressive' ),
+ array( 'mw-ui-progressive' )
) . "\n";
// Results-info
@@ -1075,7 +1098,7 @@ class SpecialSearch extends SpecialPage {
Xml::element( 'div', array( 'style' => 'clear:both' ), '', false );
}
- return $out . $this->didYouMeanHtml;
+ return $out;
}
/**
diff --git a/includes/specials/SpecialShortpages.php b/includes/specials/SpecialShortpages.php
index 782d9a17..7ec69e06 100644
--- a/includes/specials/SpecialShortpages.php
+++ b/includes/specials/SpecialShortpages.php
@@ -58,7 +58,7 @@ class ShortPagesPage extends QueryPage {
}
/**
- * @param DatabaseBase $db
+ * @param IDatabase $db
* @param ResultWrapper $res
*/
function preprocessResults( $db, $res ) {
diff --git a/includes/specials/SpecialSpecialpages.php b/includes/specials/SpecialSpecialpages.php
index eff06f46..eaa90072 100644
--- a/includes/specials/SpecialSpecialpages.php
+++ b/includes/specials/SpecialSpecialpages.php
@@ -45,6 +45,7 @@ class SpecialSpecialpages extends UnlistedSpecialPage {
return;
}
+ $this->addHelpLink( 'Help:Special pages' );
$this->outputPageList( $groups );
}
diff --git a/includes/specials/SpecialStatistics.php b/includes/specials/SpecialStatistics.php
index f0e360e8..c35de241 100644
--- a/includes/specials/SpecialStatistics.php
+++ b/includes/specials/SpecialStatistics.php
@@ -28,7 +28,7 @@
* @ingroup SpecialPage
*/
class SpecialStatistics extends SpecialPage {
- private $views, $edits, $good, $images, $total, $users,
+ private $edits, $good, $images, $total, $users,
$activeUsers = 0;
public function __construct() {
@@ -38,13 +38,11 @@ class SpecialStatistics extends SpecialPage {
public function execute( $par ) {
global $wgMemc;
- $disableCounters = $this->getConfig()->get( 'DisableCounters' );
$miserMode = $this->getConfig()->get( 'MiserMode' );
$this->setHeaders();
$this->getOutput()->addModuleStyles( 'mediawiki.special' );
- $this->views = SiteStats::views();
$this->edits = SiteStats::edits();
$this->good = SiteStats::articles();
$this->images = SiteStats::images();
@@ -53,12 +51,6 @@ class SpecialStatistics extends SpecialPage {
$this->activeUsers = SiteStats::activeUsers();
$this->hook = '';
- # Staticic - views
- $viewsStats = '';
- if ( !$disableCounters ) {
- $viewsStats = $this->getViewsStats();
- }
-
# Set active user count
if ( !$miserMode ) {
$key = wfMemcKey( 'sitestats', 'activeusers-updated' );
@@ -83,16 +75,10 @@ class SpecialStatistics extends SpecialPage {
# Statistic - usergroups
$text .= $this->getGroupStats();
- $text .= $viewsStats;
-
- # Statistic - popular pages
- if ( !$disableCounters && !$miserMode ) {
- $text .= $this->getMostViewedPages();
- }
# Statistic - other
$extraStats = array();
- if ( wfRunHooks( 'SpecialStatsAddExtra', array( &$extraStats ) ) ) {
+ if ( Hooks::run( 'SpecialStatsAddExtra', array( &$extraStats, $this->getContext() ) ) ) {
$text .= $this->getOtherStats( $extraStats );
}
@@ -213,10 +199,16 @@ class SpecialStatistics extends SpecialPage {
$grouppageLocalized = $msg->text();
}
$linkTarget = Title::newFromText( $grouppageLocalized );
- $grouppage = Linker::link(
- $linkTarget,
- htmlspecialchars( $groupnameLocalized )
- );
+
+ if ( $linkTarget ) {
+ $grouppage = Linker::link(
+ $linkTarget,
+ htmlspecialchars( $groupnameLocalized )
+ );
+ } else {
+ $grouppage = htmlspecialchars( $groupnameLocalized );
+ }
+
$grouplink = Linker::linkKnown(
SpecialPage::getTitleFor( 'Listusers' ),
$this->msg( 'listgrouprights-members' )->escaped(),
@@ -237,63 +229,6 @@ class SpecialStatistics extends SpecialPage {
return $text;
}
- private function getViewsStats() {
- return Xml::openElement( 'tr' ) .
- Xml::tags( 'th', array( 'colspan' => '2' ), $this->msg( 'statistics-header-views' )->parse() ) .
- Xml::closeElement( 'tr' ) .
- $this->formatRow( $this->msg( 'statistics-views-total' )->parse(),
- $this->getLanguage()->formatNum( $this->views ),
- array( 'class' => 'mw-statistics-views-total' ), 'statistics-views-total-desc' ) .
- $this->formatRow( $this->msg( 'statistics-views-peredit' )->parse(),
- $this->getLanguage()->formatNum( sprintf( '%.2f', $this->edits ?
- $this->views / $this->edits : 0 ) ),
- array( 'class' => 'mw-statistics-views-peredit' ) );
- }
-
- private function getMostViewedPages() {
- $text = '';
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select(
- 'page',
- array(
- 'page_namespace',
- 'page_title',
- 'page_counter',
- ),
- array(
- 'page_is_redirect' => 0,
- 'page_counter > 0',
- ),
- __METHOD__,
- array(
- 'ORDER BY' => 'page_counter DESC',
- 'LIMIT' => 10,
- )
- );
-
- if ( $res->numRows() > 0 ) {
- $text .= Xml::openElement( 'tr' );
- $text .= Xml::tags(
- 'th',
- array( 'colspan' => '2' ),
- $this->msg( 'statistics-mostpopular' )->parse()
- );
- $text .= Xml::closeElement( 'tr' );
-
- foreach ( $res as $row ) {
- $title = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
-
- if ( $title instanceof Title ) {
- $text .= $this->formatRow( Linker::link( $title ),
- $this->getLanguage()->formatNum( $row->page_counter ) );
- }
- }
- $res->free();
- }
-
- return $text;
- }
-
/**
* Conversion of external statistics into an internal representation
* Following a ([<header-message>][<item-message>] = number) pattern
@@ -315,12 +250,17 @@ class SpecialStatistics extends SpecialPage {
// Collect all items that belong to the same header
foreach ( $items as $key => $value ) {
- $name = $this->msg( $key )->parse();
- $number = htmlspecialchars( $value );
+ if ( is_array( $value ) ) {
+ $name = $value['name'];
+ $number = $value['number'];
+ } else {
+ $name = $this->msg( $key )->parse();
+ $number = $value;
+ }
$return .= $this->formatRow(
$name,
- $this->getLanguage()->formatNum( $number ),
+ $this->getLanguage()->formatNum( htmlspecialchars( $number ) ),
array( 'class' => 'mw-statistics-hook', 'id' => 'mw-' . $key )
);
}
diff --git a/includes/specials/SpecialTags.php b/includes/specials/SpecialTags.php
index b7627285..0b8147e1 100644
--- a/includes/specials/SpecialTags.php
+++ b/includes/specials/SpecialTags.php
@@ -31,6 +31,10 @@ class SpecialTags extends SpecialPage {
* @var array List of defined tags
*/
public $definedTags;
+ /**
+ * @var array List of active tags
+ */
+ public $activeTags;
function __construct() {
parent::__construct( 'Tags' );
@@ -40,33 +44,114 @@ class SpecialTags extends SpecialPage {
$this->setHeaders();
$this->outputHeader();
+ $request = $this->getRequest();
+ switch ( $par ) {
+ case 'delete':
+ $this->showDeleteTagForm( $request->getVal( 'tag' ) );
+ break;
+ case 'activate':
+ $this->showActivateDeactivateForm( $request->getVal( 'tag' ), true );
+ break;
+ case 'deactivate':
+ $this->showActivateDeactivateForm( $request->getVal( 'tag' ), false );
+ break;
+ case 'create':
+ // fall through, thanks to HTMLForm's logic
+ default:
+ $this->showTagList();
+ break;
+ }
+ }
+
+ function showTagList() {
$out = $this->getOutput();
$out->setPageTitle( $this->msg( 'tags-title' ) );
$out->wrapWikiMsg( "<div class='mw-tags-intro'>\n$1\n</div>", 'tags-intro' );
+ $user = $this->getUser();
+
+ // Show form to create a tag
+ if ( $user->isAllowed( 'managechangetags' ) ) {
+ $fields = array(
+ 'Tag' => array(
+ 'type' => 'text',
+ 'label' => $this->msg( 'tags-create-tag-name' )->plain(),
+ 'required' => true,
+ ),
+ 'Reason' => array(
+ 'type' => 'text',
+ 'label' => $this->msg( 'tags-create-reason' )->plain(),
+ 'size' => 50,
+ ),
+ 'IgnoreWarnings' => array(
+ 'type' => 'hidden',
+ ),
+ );
+
+ $form = new HTMLForm( $fields, $this->getContext() );
+ $form->setAction( $this->getPageTitle( 'create' )->getLocalURL() );
+ $form->setWrapperLegendMsg( 'tags-create-heading' );
+ $form->setHeaderText( $this->msg( 'tags-create-explanation' )->plain() );
+ $form->setSubmitCallback( array( $this, 'processCreateTagForm' ) );
+ $form->setSubmitTextMsg( 'tags-create-submit' );
+ $form->show();
+
+ // If processCreateTagForm generated a redirect, there's no point
+ // continuing with this, as the user is just going to end up getting sent
+ // somewhere else. Additionally, if we keep going here, we end up
+ // populating the memcache of tag data (see ChangeTags::listDefinedTags)
+ // with out-of-date data from the slave, because the slave hasn't caught
+ // up to the fact that a new tag has been created as part of an implicit,
+ // as yet uncommitted transaction on master.
+ if ( $out->getRedirect() !== '' ) {
+ return;
+ }
+ }
+
+ // Whether to show the "Actions" column in the tag list
+ // If any actions added in the future require other user rights, add those
+ // rights here
+ $showActions = $user->isAllowed( 'managechangetags' );
+
// Write the headers
+ $tagUsageStatistics = ChangeTags::tagUsageStatistics();
+
+ // Show header only if there exists atleast one tag
+ if ( !$tagUsageStatistics ) {
+ return;
+ }
$html = Xml::tags( 'tr', null, Xml::tags( 'th', null, $this->msg( 'tags-tag' )->parse() ) .
Xml::tags( 'th', null, $this->msg( 'tags-display-header' )->parse() ) .
Xml::tags( 'th', null, $this->msg( 'tags-description-header' )->parse() ) .
+ Xml::tags( 'th', null, $this->msg( 'tags-source-header' )->parse() ) .
Xml::tags( 'th', null, $this->msg( 'tags-active-header' )->parse() ) .
- Xml::tags( 'th', null, $this->msg( 'tags-hitcount-header' )->parse() )
+ Xml::tags( 'th', null, $this->msg( 'tags-hitcount-header' )->parse() ) .
+ ( $showActions ?
+ Xml::tags( 'th', array( 'class' => 'unsortable' ),
+ $this->msg( 'tags-actions-header' )->parse() ) :
+ '' )
);
// Used in #doTagRow()
- $this->definedTags = array_fill_keys( ChangeTags::listDefinedTags(), true );
+ $this->explicitlyDefinedTags = array_fill_keys(
+ ChangeTags::listExplicitlyDefinedTags(), true );
+ $this->extensionDefinedTags = array_fill_keys(
+ ChangeTags::listExtensionDefinedTags(), true );
+ $this->extensionActivatedTags = array_fill_keys(
+ ChangeTags::listExtensionActivatedTags(), true );
- foreach ( ChangeTags::tagUsageStatistics() as $tag => $hitcount ) {
- $html .= $this->doTagRow( $tag, $hitcount );
+ foreach ( $tagUsageStatistics as $tag => $hitcount ) {
+ $html .= $this->doTagRow( $tag, $hitcount, $showActions );
}
$out->addHTML( Xml::tags(
'table',
- array( 'class' => 'wikitable sortable mw-tags-table' ),
+ array( 'class' => 'mw-datatable sortable mw-tags-table' ),
$html
) );
}
- function doTagRow( $tag, $hitcount ) {
+ function doTagRow( $tag, $hitcount, $showActions ) {
$user = $this->getUser();
$newRow = '';
$newRow .= Xml::tags( 'td', null, Xml::element( 'code', null, $tag ) );
@@ -94,9 +179,23 @@ class SpecialTags extends SpecialPage {
}
$newRow .= Xml::tags( 'td', null, $desc );
- $active = isset( $this->definedTags[$tag] ) ? 'tags-active-yes' : 'tags-active-no';
- $active = $this->msg( $active )->escaped();
- $newRow .= Xml::tags( 'td', null, $active );
+ $sourceMsgs = array();
+ $isExtension = isset( $this->extensionDefinedTags[$tag] );
+ $isExplicit = isset( $this->explicitlyDefinedTags[$tag] );
+ if ( $isExtension ) {
+ $sourceMsgs[] = $this->msg( 'tags-source-extension' )->escaped();
+ }
+ if ( $isExplicit ) {
+ $sourceMsgs[] = $this->msg( 'tags-source-manual' )->escaped();
+ }
+ if ( !$sourceMsgs ) {
+ $sourceMsgs[] = $this->msg( 'tags-source-none' )->escaped();
+ }
+ $newRow .= Xml::tags( 'td', null, implode( Xml::element( 'br' ), $sourceMsgs ) );
+
+ $isActive = $isExplicit || isset( $this->extensionActivatedTags[$tag] );
+ $activeMsg = ( $isActive ? 'tags-active-yes' : 'tags-active-no' );
+ $newRow .= Xml::tags( 'td', null, $this->msg( $activeMsg )->escaped() );
$hitcountLabel = $this->msg( 'tags-hitcount' )->numParams( $hitcount )->escaped();
$hitcountLink = Linker::link(
@@ -109,9 +208,228 @@ class SpecialTags extends SpecialPage {
// add raw $hitcount for sorting, because tags-hitcount contains numbers and letters
$newRow .= Xml::tags( 'td', array( 'data-sort-value' => $hitcount ), $hitcountLink );
+ // actions
+ $actionLinks = array();
+ if ( $showActions ) {
+ // delete
+ if ( ChangeTags::canDeleteTag( $tag, $user )->isOK() ) {
+ $actionLinks[] = Linker::linkKnown( $this->getPageTitle( 'delete' ),
+ $this->msg( 'tags-delete' )->escaped(),
+ array(),
+ array( 'tag' => $tag ) );
+ }
+
+ // activate
+ if ( ChangeTags::canActivateTag( $tag, $user )->isOK() ) {
+ $actionLinks[] = Linker::linkKnown( $this->getPageTitle( 'activate' ),
+ $this->msg( 'tags-activate' )->escaped(),
+ array(),
+ array( 'tag' => $tag ) );
+ }
+
+ // deactivate
+ if ( ChangeTags::canDeactivateTag( $tag, $user )->isOK() ) {
+ $actionLinks[] = Linker::linkKnown( $this->getPageTitle( 'deactivate' ),
+ $this->msg( 'tags-deactivate' )->escaped(),
+ array(),
+ array( 'tag' => $tag ) );
+ }
+
+ $newRow .= Xml::tags( 'td', null, $this->getLanguage()->pipeList( $actionLinks ) );
+ }
+
return Xml::tags( 'tr', null, $newRow ) . "\n";
}
+ public function processCreateTagForm( array $data, HTMLForm $form ) {
+ $context = $form->getContext();
+ $out = $context->getOutput();
+
+ $tag = trim( strval( $data['Tag'] ) );
+ $ignoreWarnings = isset( $data['IgnoreWarnings'] ) && $data['IgnoreWarnings'] === '1';
+ $status = ChangeTags::createTagWithChecks( $tag, $data['Reason'],
+ $context->getUser(), $ignoreWarnings );
+
+ if ( $status->isGood() ) {
+ $out->redirect( $this->getPageTitle()->getLocalURL() );
+ return true;
+ } elseif ( $status->isOK() ) {
+ // we have some warnings, so we show a confirmation form
+ $fields = array(
+ 'Tag' => array(
+ 'type' => 'hidden',
+ 'default' => $data['Tag'],
+ ),
+ 'Reason' => array(
+ 'type' => 'hidden',
+ 'default' => $data['Reason'],
+ ),
+ 'IgnoreWarnings' => array(
+ 'type' => 'hidden',
+ 'default' => '1',
+ ),
+ );
+
+ // fool HTMLForm into thinking the form hasn't been submitted yet. Otherwise
+ // we get into an infinite loop!
+ $context->getRequest()->unsetVal( 'wpEditToken' );
+
+ $headerText = $this->msg( 'tags-create-warnings-above', $tag,
+ count( $status->getWarningsArray() ) )->parseAsBlock() .
+ $out->parse( $status->getWikitext() ) .
+ $this->msg( 'tags-create-warnings-below' )->parseAsBlock();
+
+ $subform = new HTMLForm( $fields, $this->getContext() );
+ $subform->setAction( $this->getPageTitle( 'create' )->getLocalURL() );
+ $subform->setWrapperLegendMsg( 'tags-create-heading' );
+ $subform->setHeaderText( $headerText );
+ $subform->setSubmitCallback( array( $this, 'processCreateTagForm' ) );
+ $subform->setSubmitTextMsg( 'htmlform-yes' );
+ $subform->show();
+
+ $out->addBacklinkSubtitle( $this->getPageTitle() );
+ return true;
+ } else {
+ $out->addWikiText( "<div class=\"error\">\n" . $status->getWikitext() .
+ "\n</div>" );
+ return false;
+ }
+ }
+
+ protected function showDeleteTagForm( $tag ) {
+ $user = $this->getUser();
+ if ( !$user->isAllowed( 'managechangetags' ) ) {
+ throw new PermissionsError( 'managechangetags' );
+ }
+
+ $out = $this->getOutput();
+ $out->setPageTitle( $this->msg( 'tags-delete-title' ) );
+ $out->addBacklinkSubtitle( $this->getPageTitle() );
+
+ // is the tag actually able to be deleted?
+ $canDeleteResult = ChangeTags::canDeleteTag( $tag, $user );
+ if ( !$canDeleteResult->isGood() ) {
+ $out->addWikiText( "<div class=\"error\">\n" . $canDeleteResult->getWikiText() .
+ "\n</div>" );
+ if ( !$canDeleteResult->isOK() ) {
+ return;
+ }
+ }
+
+ $preText = $this->msg( 'tags-delete-explanation-initial', $tag )->parseAsBlock();
+ $tagUsage = ChangeTags::tagUsageStatistics();
+ if ( $tagUsage[$tag] > 0 ) {
+ $preText .= $this->msg( 'tags-delete-explanation-in-use', $tag,
+ $tagUsage[$tag] )->parseAsBlock();
+ }
+ $preText .= $this->msg( 'tags-delete-explanation-warning', $tag )->parseAsBlock();
+
+ // see if the tag is in use
+ $this->extensionActivatedTags = array_fill_keys(
+ ChangeTags::listExtensionActivatedTags(), true );
+ if ( isset( $this->extensionActivatedTags[$tag] ) ) {
+ $preText .= $this->msg( 'tags-delete-explanation-active', $tag )->parseAsBlock();
+ }
+
+ $fields = array();
+ $fields['Reason'] = array(
+ 'type' => 'text',
+ 'label' => $this->msg( 'tags-delete-reason' )->plain(),
+ 'size' => 50,
+ );
+ $fields['HiddenTag'] = array(
+ 'type' => 'hidden',
+ 'name' => 'tag',
+ 'default' => $tag,
+ 'required' => true,
+ );
+
+ $form = new HTMLForm( $fields, $this->getContext() );
+ $form->setAction( $this->getPageTitle( 'delete' )->getLocalURL() );
+ $form->tagAction = 'delete'; // custom property on HTMLForm object
+ $form->setSubmitCallback( array( $this, 'processTagForm' ) );
+ $form->setSubmitTextMsg( 'tags-delete-submit' );
+ $form->setSubmitDestructive(); // nasty!
+ $form->addPreText( $preText );
+ $form->show();
+ }
+
+ protected function showActivateDeactivateForm( $tag, $activate ) {
+ $actionStr = $activate ? 'activate' : 'deactivate';
+
+ $user = $this->getUser();
+ if ( !$user->isAllowed( 'managechangetags' ) ) {
+ throw new PermissionsError( 'managechangetags' );
+ }
+
+ $out = $this->getOutput();
+ // tags-activate-title, tags-deactivate-title
+ $out->setPageTitle( $this->msg( "tags-$actionStr-title" ) );
+ $out->addBacklinkSubtitle( $this->getPageTitle() );
+
+ // is it possible to do this?
+ $func = $activate ? 'canActivateTag' : 'canDeactivateTag';
+ $result = ChangeTags::$func( $tag, $user );
+ if ( !$result->isGood() ) {
+ $out->wrapWikiMsg( "<div class=\"error\">\n$1" . $result->getWikiText() .
+ "\n</div>" );
+ if ( !$result->isOK() ) {
+ return;
+ }
+ }
+
+ // tags-activate-question, tags-deactivate-question
+ $preText = $this->msg( "tags-$actionStr-question", $tag )->parseAsBlock();
+
+ $fields = array();
+ // tags-activate-reason, tags-deactivate-reason
+ $fields['Reason'] = array(
+ 'type' => 'text',
+ 'label' => $this->msg( "tags-$actionStr-reason" )->plain(),
+ 'size' => 50,
+ );
+ $fields['HiddenTag'] = array(
+ 'type' => 'hidden',
+ 'name' => 'tag',
+ 'default' => $tag,
+ 'required' => true,
+ );
+
+ $form = new HTMLForm( $fields, $this->getContext() );
+ $form->setAction( $this->getPageTitle( $actionStr )->getLocalURL() );
+ $form->tagAction = $actionStr;
+ $form->setSubmitCallback( array( $this, 'processTagForm' ) );
+ // tags-activate-submit, tags-deactivate-submit
+ $form->setSubmitTextMsg( "tags-$actionStr-submit" );
+ $form->addPreText( $preText );
+ $form->show();
+ }
+
+ public function processTagForm( array $data, HTMLForm $form ) {
+ $context = $form->getContext();
+ $out = $context->getOutput();
+
+ $tag = $data['HiddenTag'];
+ $status = call_user_func( array( 'ChangeTags', "{$form->tagAction}TagWithChecks" ),
+ $tag, $data['Reason'], $context->getUser(), true );
+
+ if ( $status->isGood() ) {
+ $out->redirect( $this->getPageTitle()->getLocalURL() );
+ return true;
+ } elseif ( $status->isOK() && $form->tagAction === 'delete' ) {
+ // deletion succeeded, but hooks raised a warning
+ $out->addWikiText( $this->msg( 'tags-delete-warnings-after-delete', $tag,
+ count( $status->getWarningsArray() ) )->text() . "\n" .
+ $status->getWikitext() );
+ $out->addReturnTo( $this->getPageTitle() );
+ return true;
+ } else {
+ $out->addWikiText( "<div class=\"error\">\n" . $status->getWikitext() .
+ "\n</div>" );
+ return false;
+ }
+ }
+
protected function getGroupName() {
return 'changes';
}
diff --git a/includes/specials/SpecialTrackingCategories.php b/includes/specials/SpecialTrackingCategories.php
index 552031f1..d219c99d 100644
--- a/includes/specials/SpecialTrackingCategories.php
+++ b/includes/specials/SpecialTrackingCategories.php
@@ -36,6 +36,24 @@ class SpecialTrackingCategories extends SpecialPage {
parent::__construct( 'TrackingCategories' );
}
+ /**
+ * Tracking categories that exist in core
+ *
+ * @var array
+ */
+ private static $coreTrackingCategories = array(
+ 'index-category',
+ 'noindex-category',
+ 'duplicate-args-category',
+ 'expensive-parserfunction-category',
+ 'post-expand-template-argument-category',
+ 'post-expand-template-inclusion-category',
+ 'hidden-category-category',
+ 'broken-file-category',
+ 'node-count-exceeded-category',
+ 'expansion-depth-exceeded-category',
+ );
+
function execute( $par ) {
$this->setHeaders();
$this->outputHeader();
@@ -56,23 +74,88 @@ class SpecialTrackingCategories extends SpecialPage {
</tr></thead>"
);
- foreach ( $this->getConfig()->get( 'TrackingCategories' ) as $catMsg ) {
+ $trackingCategories = $this->prepareTrackingCategoriesData();
+
+ $batch = new LinkBatch();
+ foreach ( $trackingCategories as $catMsg => $data ) {
+ $batch->addObj( $data['msg'] );
+ foreach ( $data['cats'] as $catTitle ) {
+ $batch->addObj( $catTitle );
+ }
+ }
+ $batch->execute();
+
+ foreach ( $trackingCategories as $catMsg => $data ) {
+ $allMsgs = array();
+ $catDesc = $catMsg . '-desc';
+
+ $catMsgTitleText = Linker::link(
+ $data['msg'],
+ htmlspecialchars( $catMsg )
+ );
+
+ foreach ( $data['cats'] as $catTitle ) {
+ $catTitleText = Linker::link(
+ $catTitle,
+ htmlspecialchars( $catTitle->getText() )
+ );
+ $allMsgs[] = $catTitleText;
+ }
+
+ # Extra message, when no category was found
+ if ( !count( $allMsgs ) ) {
+ $allMsgs[] = $this->msg( 'trackingcategories-disabled' )->parse();
+ }
+
+ /*
+ * Show category description if it exists as a system message
+ * as category-name-desc
+ */
+ $descMsg = $this->msg( $catDesc );
+ if ( $descMsg->isBlank() ) {
+ $descMsg = $this->msg( 'trackingcategories-nodesc' );
+ }
+
+ $this->getOutput()->addHTML(
+ Html::openElement( 'tr' ) .
+ Html::openElement( 'td', array( 'class' => 'mw-trackingcategories-name' ) ) .
+ $this->getLanguage()->commaList( array_unique( $allMsgs ) ) .
+ Html::closeElement( 'td' ) .
+ Html::openElement( 'td', array( 'class' => 'mw-trackingcategories-msg' ) ) .
+ $catMsgTitleText .
+ Html::closeElement( 'td' ) .
+ Html::openElement( 'td', array( 'class' => 'mw-trackingcategories-desc' ) ) .
+ $descMsg->parse() .
+ Html::closeElement( 'td' ) .
+ Html::closeElement( 'tr' )
+ );
+ }
+ $this->getOutput()->addHTML( Html::closeElement( 'table' ) );
+ }
+
+ /**
+ * Read the global and extract title objects from the corresponding messages
+ * @return array Array( 'msg' => Title, 'cats' => Title[] )
+ */
+ private function prepareTrackingCategoriesData() {
+ $categories = array_merge(
+ self::$coreTrackingCategories,
+ ExtensionRegistry::getInstance()->getAttribute( 'TrackingCategories' ),
+ $this->getConfig()->get( 'TrackingCategories' ) // deprecated
+ );
+ $trackingCategories = array();
+ foreach ( $categories as $catMsg ) {
/*
* Check if the tracking category varies by namespace
* Otherwise only pages in the current namespace will be displayed
* If it does vary, show pages considering all namespaces
*/
$msgObj = $this->msg( $catMsg )->inContentLanguage();
- $allMsgs = array();
- $catDesc = $catMsg . '-desc';
+ $allCats = array();
$catMsgTitle = Title::makeTitleSafe( NS_MEDIAWIKI, $catMsg );
if ( !$catMsgTitle ) {
continue;
}
- $catMsgTitleText = Linker::link(
- $catMsgTitle,
- htmlspecialchars( $catMsg )
- );
// Match things like {{NAMESPACE}} and {{NAMESPACENUMBER}}.
// False positives are ok, this is just an efficiency shortcut
@@ -88,11 +171,7 @@ class SpecialTrackingCategories extends SpecialPage {
if ( $catName !== '-' ) {
$catTitle = Title::makeTitleSafe( NS_CATEGORY, $catName );
if ( $catTitle ) {
- $catTitleText = Linker::link(
- $catTitle,
- htmlspecialchars( $catName )
- );
- $allMsgs[] = $catTitleText;
+ $allCats[] = $catTitle;
}
}
}
@@ -102,44 +181,17 @@ class SpecialTrackingCategories extends SpecialPage {
if ( $catName !== '-' ) {
$catTitle = Title::makeTitleSafe( NS_CATEGORY, $catName );
if ( $catTitle ) {
- $catTitleText = Linker::link(
- $catTitle,
- htmlspecialchars( $catName )
- );
- $allMsgs[] = $catTitleText;
+ $allCats[] = $catTitle;
}
}
}
-
- # Extra message, when no category was found
- if ( !count( $allMsgs ) ) {
- $allMsgs[] = $this->msg( 'trackingcategories-disabled' )->parse();
- }
-
- /*
- * Show category description if it exists as a system message
- * as category-name-desc
- */
- $descMsg = $this->msg( $catDesc );
- if ( $descMsg->isBlank() ) {
- $descMsg = $this->msg( 'trackingcategories-nodesc' );
- }
-
- $this->getOutput()->addHTML(
- Html::openElement( 'tr' ) .
- Html::openElement( 'td', array( 'class' => 'mw-trackingcategories-name' ) ) .
- $this->getLanguage()->commaList( array_unique( $allMsgs ) ) .
- Html::closeElement( 'td' ) .
- Html::openElement( 'td', array( 'class' => 'mw-trackingcategories-msg' ) ) .
- $catMsgTitleText .
- Html::closeElement( 'td' ) .
- Html::openElement( 'td', array( 'class' => 'mw-trackingcategories-desc' ) ) .
- $descMsg->parse() .
- Html::closeElement( 'td' ) .
- Html::closeElement( 'tr' )
+ $trackingCategories[$catMsg] = array(
+ 'cats' => $allCats,
+ 'msg' => $catMsgTitle,
);
}
- $this->getOutput()->addHTML( Html::closeElement( 'table' ) );
+
+ return $trackingCategories;
}
protected function getGroupName() {
diff --git a/includes/specials/SpecialUnblock.php b/includes/specials/SpecialUnblock.php
index 244b8894..f81f1c30 100644
--- a/includes/specials/SpecialUnblock.php
+++ b/includes/specials/SpecialUnblock.php
@@ -53,7 +53,7 @@ class SpecialUnblock extends SpecialPage {
$out = $this->getOutput();
$out->setPageTitle( $this->msg( 'unblockip' ) );
- $out->addModules( 'mediawiki.special' );
+ $out->addModules( array( 'mediawiki.special', 'mediawiki.userSuggest' ) );
$form = new HTMLForm( $this->getFields(), $this->getContext() );
$form->setWrapperLegendMsg( 'unblockip' );
@@ -88,6 +88,7 @@ class SpecialUnblock extends SpecialPage {
'autofocus' => true,
'size' => '45',
'required' => true,
+ 'cssclass' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
),
'Name' => array(
'type' => 'info',
@@ -226,8 +227,12 @@ class SpecialUnblock extends SpecialPage {
}
# Make log entry
- $log = new LogPage( 'block' );
- $log->addEntry( 'unblock', $page, $data['Reason'], array(), $performer );
+ $logEntry = new ManualLogEntry( 'block', 'unblock' );
+ $logEntry->setTarget( $page );
+ $logEntry->setComment( $data['Reason'] );
+ $logEntry->setPerformer( $performer );
+ $logId = $logEntry->insert();
+ $logEntry->publish( $logId );
return true;
}
diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php
index c3e871b8..f2362a18 100644
--- a/includes/specials/SpecialUndelete.php
+++ b/includes/specials/SpecialUndelete.php
@@ -94,7 +94,7 @@ class PageArchive {
}
/**
- * @param DatabaseBase $dbr
+ * @param IDatabase $dbr
* @param string|array $condition
* @return bool|ResultWrapper
*/
@@ -370,6 +370,7 @@ class PageArchive {
if ( $restoreFiles && $this->title->getNamespace() == NS_FILE ) {
$img = wfLocalFile( $this->title );
+ $img->load( File::READ_LATEST );
$this->fileStatus = $img->restore( $fileVersions, $unsuppress );
if ( !$this->fileStatus->isOK() ) {
return false;
@@ -421,7 +422,7 @@ class PageArchive {
$logEntry->setTarget( $this->title );
$logEntry->setComment( $reason );
- wfRunHooks( 'ArticleUndeleteLogEntry', array( $this, &$logEntry, $user ) );
+ Hooks::run( 'ArticleUndeleteLogEntry', array( $this, &$logEntry, $user ) );
$logid = $logEntry->insert();
$logEntry->publish( $logid );
@@ -550,7 +551,7 @@ class PageArchive {
'title' => $article->getTitle(), // used to derive default content model
)
);
- $user = User::newFromName( $revision->getRawUserText(), false );
+ $user = User::newFromName( $revision->getUserText( Revision::RAW ), false );
$content = $revision->getContent( Revision::RAW );
//NOTE: article ID may not be known yet. prepareSave() should not modify the database.
@@ -605,7 +606,7 @@ class PageArchive {
$revision->insertOn( $dbw );
$restored++;
- wfRunHooks( 'ArticleRevisionUndeleted', array( &$this->title, $revision, $row->ar_page_id ) );
+ Hooks::run( 'ArticleRevisionUndeleted', array( &$this->title, $revision, $row->ar_page_id ) );
}
# Now that it's safely stored, take it out of the archive
$dbw->delete( 'archive',
@@ -623,7 +624,7 @@ class PageArchive {
$wasnew = $article->updateIfNewerOn( $dbw, $revision, $previousRevId );
if ( $created || $wasnew ) {
// Update site stats, link tables, etc
- $user = User::newFromName( $revision->getRawUserText(), false );
+ $user = User::newFromName( $revision->getUserText( Revision::RAW ), false );
$article->doEditUpdates(
$revision,
$user,
@@ -631,7 +632,7 @@ class PageArchive {
);
}
- wfRunHooks( 'ArticleUndelete', array( &$this->title, $created, $comment, $oldPageId ) );
+ Hooks::run( 'ArticleUndelete', array( &$this->title, $created, $comment, $oldPageId ) );
if ( $this->title->getNamespace() == NS_FILE ) {
$update = new HTMLCacheUpdate( $this->title, 'imagelinks' );
@@ -790,6 +791,7 @@ class SpecialUndelete extends SpecialPage {
return;
}
+ $this->addHelpLink( 'Help:Undelete' );
if ( $this->mAllowed ) {
$out->setPageTitle( $this->msg( 'undeletepage' ) );
} else {
@@ -839,7 +841,7 @@ class SpecialUndelete extends SpecialPage {
'prefix',
20,
$this->mSearchPrefix,
- array( 'id' => 'prefix', 'autofocus' => true )
+ array( 'id' => 'prefix', 'autofocus' => '' )
) . ' ' .
Xml::submitButton( $this->msg( 'undelete-search-submit' )->text() ) .
Xml::closeElement( 'fieldset' ) .
@@ -908,7 +910,7 @@ class SpecialUndelete extends SpecialPage {
}
$archive = new PageArchive( $this->mTargetObj, $this->getConfig() );
- if ( !wfRunHooks( 'UndeleteForm::showRevision', array( &$archive, $this->mTargetObj ) ) ) {
+ if ( !Hooks::run( 'UndeleteForm::showRevision', array( &$archive, $this->mTargetObj ) ) ) {
return;
}
$rev = $archive->getRevision( $timestamp );
@@ -992,7 +994,7 @@ class SpecialUndelete extends SpecialPage {
$out->addHTML( $this->msg( 'undelete-revision' )->rawParams( $link )->params(
$time )->rawParams( $userLink )->params( $d, $t )->parse() . '</div>' );
- if ( !wfRunHooks( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) ) ) {
+ if ( !Hooks::run( 'UndeleteShowRevision', array( $this->mTargetObj, $rev ) ) ) {
return;
}
@@ -1215,7 +1217,7 @@ class SpecialUndelete extends SpecialPage {
);
$archive = new PageArchive( $this->mTargetObj, $this->getConfig() );
- wfRunHooks( 'UndeleteForm::showHistory', array( &$archive, $this->mTargetObj ) );
+ Hooks::run( 'UndeleteForm::showHistory', array( &$archive, $this->mTargetObj ) );
/*
$text = $archive->getLastRevisionText();
if( is_null( $text ) ) {
@@ -1312,7 +1314,7 @@ class SpecialUndelete extends SpecialPage {
'wpComment',
50,
$this->mComment,
- array( 'id' => 'wpComment', 'autofocus' => true )
+ array( 'id' => 'wpComment', 'autofocus' => '' )
) .
"</td>
</tr>
@@ -1628,7 +1630,9 @@ class SpecialUndelete extends SpecialPage {
}
function undelete() {
- if ( $this->getConfig()->get( 'UploadMaintenance' ) && $this->mTargetObj->getNamespace() == NS_FILE ) {
+ if ( $this->getConfig()->get( 'UploadMaintenance' )
+ && $this->mTargetObj->getNamespace() == NS_FILE
+ ) {
throw new ErrorPageError( 'undelete-error', 'filedelete-maintenance' );
}
@@ -1638,7 +1642,7 @@ class SpecialUndelete extends SpecialPage {
$out = $this->getOutput();
$archive = new PageArchive( $this->mTargetObj, $this->getConfig() );
- wfRunHooks( 'UndeleteForm::undelete', array( &$archive, $this->mTargetObj ) );
+ Hooks::run( 'UndeleteForm::undelete', array( &$archive, $this->mTargetObj ) );
$ok = $archive->undelete(
$this->mTargetTimestamp,
$this->mComment,
@@ -1649,7 +1653,7 @@ class SpecialUndelete extends SpecialPage {
if ( is_array( $ok ) ) {
if ( $ok[1] ) { // Undeleted file count
- wfRunHooks( 'FileUndeleteComplete', array(
+ Hooks::run( 'FileUndeleteComplete', array(
$this->mTargetObj, $this->mFileVersions,
$this->getUser(), $this->mComment ) );
}
diff --git a/includes/specials/SpecialUpload.php b/includes/specials/SpecialUpload.php
index 55d09dd6..640562e4 100644
--- a/includes/specials/SpecialUpload.php
+++ b/includes/specials/SpecialUpload.php
@@ -143,6 +143,13 @@ class SpecialUpload extends SpecialPage {
/**
* Special page entry point
* @param string $par
+ * @throws ErrorPageError
+ * @throws Exception
+ * @throws FatalError
+ * @throws MWException
+ * @throws PermissionsError
+ * @throws ReadOnlyError
+ * @throws UserBlockedError
*/
public function execute( $par ) {
$this->setHeaders();
@@ -153,6 +160,8 @@ class SpecialUpload extends SpecialPage {
throw new ErrorPageError( 'uploaddisabled', 'uploaddisabledtext' );
}
+ $this->addHelpLink( 'Help:Managing files' );
+
# Check permissions
$user = $this->getUser();
$permissionRequired = UploadBase::isAllowed( $user );
@@ -186,7 +195,7 @@ class SpecialUpload extends SpecialPage {
$this->processUpload();
} else {
# Backwards compatibility hook
- if ( !wfRunHooks( 'UploadForm:initial', array( &$this ) ) ) {
+ if ( !Hooks::run( 'UploadForm:initial', array( &$this ) ) ) {
wfDebug( "Hook 'UploadForm:initial' broke output of the upload form\n" );
return;
@@ -414,7 +423,7 @@ class SpecialUpload extends SpecialPage {
return;
}
- if ( !wfRunHooks( 'UploadForm:BeforeProcessing', array( &$this ) ) ) {
+ if ( !Hooks::run( 'UploadForm:BeforeProcessing', array( &$this ) ) ) {
wfDebug( "Hook 'UploadForm:BeforeProcessing' broke processing the file.\n" );
// This code path is deprecated. If you want to break upload processing
// do so by hooking into the appropriate hooks in UploadBase::verifyUpload
@@ -454,7 +463,7 @@ class SpecialUpload extends SpecialPage {
// Get the page text if this is not a reupload
if ( !$this->mForReUpload ) {
$pageText = self::getInitialPageText( $this->mComment, $this->mLicense,
- $this->mCopyrightStatus, $this->mCopyrightSource );
+ $this->mCopyrightStatus, $this->mCopyrightSource, $this->getConfig() );
} else {
$pageText = false;
}
@@ -474,7 +483,7 @@ class SpecialUpload extends SpecialPage {
// Success, redirect to description page
$this->mUploadSuccessful = true;
- wfRunHooks( 'SpecialUploadComplete', array( &$this ) );
+ Hooks::run( 'SpecialUploadComplete', array( &$this ) );
$this->getOutput()->redirect( $this->mLocalFile->getTitle()->getFullURL() );
}
@@ -484,28 +493,32 @@ class SpecialUpload extends SpecialPage {
* @param string $license
* @param string $copyStatus
* @param string $source
+ * @param Config $config Configuration object to load data from
* @return string
- * @todo Use Config obj instead of globals
*/
public static function getInitialPageText( $comment = '', $license = '',
- $copyStatus = '', $source = ''
+ $copyStatus = '', $source = '', Config $config = null
) {
- global $wgUseCopyrightUpload, $wgForceUIMsgAsContentMsg;
+ if ( $config === null ) {
+ wfDebug( __METHOD__ . ' called without a Config instance passed to it' );
+ $config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+ }
$msg = array();
+ $forceUIMsgAsContentMsg = (array)$config->get( 'ForceUIMsgAsContentMsg' );
/* These messages are transcluded into the actual text of the description page.
* Thus, forcing them as content messages makes the upload to produce an int: template
* instead of hardcoding it there in the uploader language.
*/
foreach ( array( 'license-header', 'filedesc', 'filestatus', 'filesource' ) as $msgName ) {
- if ( in_array( $msgName, (array)$wgForceUIMsgAsContentMsg ) ) {
+ if ( in_array( $msgName, $forceUIMsgAsContentMsg ) ) {
$msg[$msgName] = "{{int:$msgName}}";
} else {
$msg[$msgName] = wfMessage( $msgName )->inContentLanguage()->text();
}
}
- if ( $wgUseCopyrightUpload ) {
+ if ( $config->get( 'UseCopyrightUpload' ) ) {
$licensetxt = '';
if ( $license != '' ) {
$licensetxt = '== ' . $msg['license-header'] . " ==\n" . '{{' . $license . '}}' . "\n";
@@ -731,8 +744,8 @@ class SpecialUpload extends SpecialPage {
}
return '<li>' .
- wfMessage( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() .
- $gallery->toHtml() . "</li>\n";
+ $this->msg( 'file-exists-duplicate' )->numParams( count( $dupes ) )->parse() .
+ $gallery->toHTML() . "</li>\n";
}
protected function getGroupName() {
@@ -746,7 +759,7 @@ class SpecialUpload extends SpecialPage {
*
* @todo What about non-BitmapHandler handled files?
*/
- static public function rotationEnabled() {
+ public static function rotationEnabled() {
$bitmapHandler = new BitmapHandler();
return $bitmapHandler->autoRotateEnabled();
}
@@ -795,7 +808,7 @@ class UploadForm extends HTMLForm {
+ $this->getDescriptionSection()
+ $this->getOptionsSection();
- wfRunHooks( 'UploadFormInitDescriptor', array( &$descriptor ) );
+ Hooks::run( 'UploadFormInitDescriptor', array( &$descriptor ) );
parent::__construct( $descriptor, $context, 'upload' );
# Add a link to edit MediaWik:Licenses
@@ -872,6 +885,17 @@ class UploadForm extends HTMLForm {
);
}
+ $help = $this->msg( 'upload-maxfilesize',
+ $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] )
+ )->parse();
+
+ // If the user can also upload by URL, there are 2 different file size limits.
+ // This extra message helps stress which limit corresponds to what.
+ if ( $canUploadByUrl ) {
+ $help .= $this->msg( 'word-separator' )->escaped();
+ $help .= $this->msg( 'upload_source_file' )->parse();
+ }
+
$descriptor['UploadFile'] = array(
'class' => 'UploadSourceField',
'section' => 'source',
@@ -881,11 +905,7 @@ class UploadForm extends HTMLForm {
'label-message' => 'sourcefilename',
'upload-type' => 'File',
'radio' => &$radio,
- 'help' => $this->msg( 'upload-maxfilesize',
- $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['file'] )
- )->parse() .
- $this->msg( 'word-separator' )->escaped() .
- $this->msg( 'upload_source_file' )->escaped(),
+ 'help' => $help,
'checked' => $selectedSourceType == 'file',
);
@@ -903,11 +923,11 @@ class UploadForm extends HTMLForm {
$this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] )
)->parse() .
$this->msg( 'word-separator' )->escaped() .
- $this->msg( 'upload_source_url' )->escaped(),
+ $this->msg( 'upload_source_url' )->parse(),
'checked' => $selectedSourceType == 'url',
);
}
- wfRunHooks( 'UploadFormSourceDescriptors', array( &$descriptor, &$radio, $selectedSourceType ) );
+ Hooks::run( 'UploadFormSourceDescriptors', array( &$descriptor, &$radio, $selectedSourceType ) );
$descriptor['Extensions'] = array(
'type' => 'info',
@@ -930,35 +950,31 @@ class UploadForm extends HTMLForm {
$config = $this->getConfig();
if ( $config->get( 'CheckFileExtensions' ) ) {
+ $fileExtensions = array_unique( $config->get( 'FileExtensions' ) );
if ( $config->get( 'StrictFileExtensions' ) ) {
# Everything not permitted is banned
$extensionsList =
'<div id="mw-upload-permitted">' .
- $this->msg(
- 'upload-permitted',
- $this->getContext()->getLanguage()->commaList(
- array_unique( $config->get( 'FileExtensions' ) )
- )
- )->parseAsBlock() .
+ $this->msg( 'upload-permitted' )
+ ->params( $this->getLanguage()->commaList( $fileExtensions ) )
+ ->numParams( count( $fileExtensions ) )
+ ->parseAsBlock() .
"</div>\n";
} else {
# We have to list both preferred and prohibited
+ $fileBlacklist = array_unique( $config->get( 'FileBlacklist' ) );
$extensionsList =
'<div id="mw-upload-preferred">' .
- $this->msg(
- 'upload-preferred',
- $this->getContext()->getLanguage()->commaList(
- array_unique( $config->get( 'FileExtensions' ) )
- )
- )->parseAsBlock() .
+ $this->msg( 'upload-preferred' )
+ ->params( $this->getLanguage()->commaList( $fileExtensions ) )
+ ->numParams( count( $fileExtensions ) )
+ ->parseAsBlock() .
"</div>\n" .
'<div id="mw-upload-prohibited">' .
- $this->msg(
- 'upload-prohibited',
- $this->getContext()->getLanguage()->commaList(
- array_unique( $config->get( 'FileBlacklist' ) )
- )
- )->parseAsBlock() .
+ $this->msg( 'upload-prohibited' )
+ ->params( $this->getLanguage()->commaList( $fileBlacklist ) )
+ ->numParams( count( $fileBlacklist ) )
+ ->parseAsBlock() .
"</div>\n";
}
} else {
@@ -978,10 +994,10 @@ class UploadForm extends HTMLForm {
protected function getDescriptionSection() {
$config = $this->getConfig();
if ( $this->mSessionKey ) {
- $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash();
+ $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash( $this->getUser() );
try {
$file = $stash->getFile( $this->mSessionKey );
- } catch ( MWException $e ) {
+ } catch ( Exception $e ) {
$file = null;
}
if ( $file ) {
@@ -1139,9 +1155,12 @@ class UploadForm extends HTMLForm {
// the wpDestFile textbox
$this->mDestFile === '',
'wgUploadSourceIds' => $this->mSourceIds,
+ 'wgCheckFileExtensions' => $config->get( 'CheckFileExtensions' ),
'wgStrictFileExtensions' => $config->get( 'StrictFileExtensions' ),
+ 'wgFileExtensions' => array_values( array_unique( $config->get( 'FileExtensions' ) ) ),
'wgCapitalizeUploads' => MWNamespace::isCapitalized( NS_FILE ),
'wgMaxUploadSize' => $this->mMaxUploadSize,
+ 'wgFileCanRotate' => SpecialUpload::rotationEnabled(),
);
$out = $this->getOutput();
diff --git a/includes/specials/SpecialUploadStash.php b/includes/specials/SpecialUploadStash.php
index ddb435d9..12e103e7 100644
--- a/includes/specials/SpecialUploadStash.php
+++ b/includes/specials/SpecialUploadStash.php
@@ -48,10 +48,6 @@ class SpecialUploadStash extends UnlistedSpecialPage {
public function __construct() {
parent::__construct( 'UploadStash', 'upload' );
- try {
- $this->stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash();
- } catch ( UploadStashNotAvailableException $e ) {
- }
}
/**
@@ -62,6 +58,7 @@ class SpecialUploadStash extends UnlistedSpecialPage {
* @return bool Success
*/
public function execute( $subPage ) {
+ $this->stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash( $this->getUser() );
$this->checkPermissions();
if ( $subPage === null || $subPage === '' ) {
@@ -250,9 +247,9 @@ class SpecialUploadStash extends UnlistedSpecialPage {
// make a curl call to the scaler to create a thumbnail
$httpOptions = array(
'method' => 'GET',
- 'timeout' => 'default'
+ 'timeout' => 5 // T90599 attempt to time out cleanly
);
- $req = MWHttpRequest::factory( $scalerThumbUrl, $httpOptions );
+ $req = MWHttpRequest::factory( $scalerThumbUrl, $httpOptions, __METHOD__ );
$status = $req->execute();
if ( !$status->isOK() ) {
$errors = $status->getErrorsArray();
@@ -331,11 +328,12 @@ class SpecialUploadStash extends UnlistedSpecialPage {
* This works, because there really is only one stash per logged-in user, despite appearances.
*
* @param array $formData
+ * @param HTMLForm $form
* @return Status
*/
- public static function tryClearStashedUploads( $formData ) {
+ public static function tryClearStashedUploads( $formData, $form ) {
if ( isset( $formData['Clear'] ) ) {
- $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash();
+ $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash( $form->getUser() );
wfDebug( 'stash has: ' . print_r( $stash->listFiles(), true ) . "\n" );
if ( !$stash->clear() ) {
diff --git a/includes/specials/SpecialUserlogin.php b/includes/specials/SpecialUserlogin.php
index 24b636b1..10edbcfb 100644
--- a/includes/specials/SpecialUserlogin.php
+++ b/includes/specials/SpecialUserlogin.php
@@ -105,9 +105,27 @@ class LoginForm extends SpecialPage {
* @param WebRequest $request
*/
public function __construct( $request = null ) {
+ global $wgUseMediaWikiUIEverywhere;
parent::__construct( 'Userlogin' );
$this->mOverrideRequest = $request;
+ // Override UseMediaWikiEverywhere to true, to force login and create form to use mw ui
+ $wgUseMediaWikiUIEverywhere = true;
+ }
+
+ /**
+ * Returns an array of all valid error messages.
+ *
+ * @return array
+ */
+ public static function getValidErrorMessages() {
+ static $messages = null;
+ if ( !$messages ) {
+ $messages = self::$validErrorMessages;
+ Hooks::run( 'LoginFormValidErrorMessages', array( &$messages ) );
+ }
+
+ return $messages;
}
/**
@@ -142,7 +160,8 @@ class LoginForm extends SpecialPage {
$this->mLoginattempt = $request->getCheck( 'wpLoginattempt' );
$this->mAction = $request->getVal( 'action' );
$this->mRemember = $request->getCheck( 'wpRemember' );
- $this->mFromHTTP = $request->getBool( 'fromhttp', false );
+ $this->mFromHTTP = $request->getBool( 'fromhttp', false )
+ || $request->getBool( 'wpFromhttp', false );
$this->mStickHTTPS = ( !$this->mFromHTTP && $request->getProtocol() === 'https' )
|| $request->getBool( 'wpForceHttps', false );
$this->mLanguage = $request->getText( 'uselang' );
@@ -172,13 +191,13 @@ class LoginForm extends SpecialPage {
// Only show valid error or warning messages.
if ( $entryError->exists()
- && in_array( $entryError->getKey(), self::$validErrorMessages )
+ && in_array( $entryError->getKey(), self::getValidErrorMessages() )
) {
$this->mEntryErrorType = 'error';
$this->mEntryError = $entryError->rawParams( $loginreqlink )->escaped();
} elseif ( $entryWarning->exists()
- && in_array( $entryWarning->getKey(), self::$validErrorMessages )
+ && in_array( $entryWarning->getKey(), self::getValidErrorMessages() )
) {
$this->mEntryErrorType = 'warning';
$this->mEntryError = $entryWarning->rawParams( $loginreqlink )->escaped();
@@ -333,7 +352,7 @@ class LoginForm extends SpecialPage {
$u->saveSettings();
$result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' );
- wfRunHooks( 'AddNewAccount', array( $u, true ) );
+ Hooks::run( 'AddNewAccount', array( $u, true ) );
$u->addNewUserLogEntry( 'byemail', $this->mReason );
$out = $this->getOutput();
@@ -408,7 +427,7 @@ class LoginForm extends SpecialPage {
// which is needed or the personal links will be
// wrong.
$this->getContext()->setUser( $u );
- wfRunHooks( 'AddNewAccount', array( $u, false ) );
+ Hooks::run( 'AddNewAccount', array( $u, false ) );
$u->addNewUserLogEntry( 'create' );
if ( $this->hasSessionCookie() ) {
$this->successfulCreation();
@@ -420,7 +439,7 @@ class LoginForm extends SpecialPage {
$out->setPageTitle( $this->msg( 'accountcreated' ) );
$out->addWikiMsg( 'accountcreatedtext', $u->getName() );
$out->addReturnTo( $this->getPageTitle() );
- wfRunHooks( 'AddNewAccount', array( $u, false ) );
+ Hooks::run( 'AddNewAccount', array( $u, false ) );
$u->addNewUserLogEntry( 'create2', $this->mReason );
}
@@ -509,20 +528,8 @@ class LoginForm extends SpecialPage {
return Status::newFatal( 'sorbs_create_account_reason' );
}
- // Normalize the name so that silly things don't cause "invalid username"
- // errors. User::newFromName does some rather strict checking, rejecting
- // e.g. leading/trailing/multiple spaces. But first we need to reject
- // usernames that would be treated as titles with a fragment part.
- if ( strpos( $this->mUsername, '#' ) !== false ) {
- return Status::newFatal( 'noname' );
- }
- $title = Title::makeTitleSafe( NS_USER, $this->mUsername );
- if ( !is_object( $title ) ) {
- return Status::newFatal( 'noname' );
- }
-
# Now create a dummy user ($u) and check if it is valid
- $u = User::newFromName( $title->getText(), 'creatable' );
+ $u = User::newFromName( $this->mUsername, 'creatable' );
if ( !is_object( $u ) ) {
return Status::newFatal( 'noname' );
} elseif ( 0 != $u->idForName() ) {
@@ -563,7 +570,7 @@ class LoginForm extends SpecialPage {
$abortError = '';
$abortStatus = null;
- if ( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError, &$abortStatus ) ) ) {
+ if ( !Hooks::run( 'AbortNewAccount', array( $u, &$abortError, &$abortStatus ) ) ) {
// Hook point to add extra creation throttles and blocks
wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
if ( $abortStatus === null ) {
@@ -583,7 +590,7 @@ class LoginForm extends SpecialPage {
}
// Hook point to check for exempt from account creation throttle
- if ( !wfRunHooks( 'ExemptFromAccountCreationThrottle', array( $ip ) ) ) {
+ if ( !Hooks::run( 'ExemptFromAccountCreationThrottle', array( $ip ) ) ) {
wfDebug( "LoginForm::exemptFromAccountCreationThrottle: a hook " .
"allowed account creation w/o throttle\n" );
} else {
@@ -706,7 +713,7 @@ class LoginForm extends SpecialPage {
// Give extensions a way to indicate the username has been updated,
// rather than telling the user the account doesn't exist.
- if ( !wfRunHooks( 'LoginUserMigrated', array( $u, &$msg ) ) ) {
+ if ( !Hooks::run( 'LoginUserMigrated', array( $u, &$msg ) ) ) {
$this->mAbortLoginErrorMsg = $msg;
return self::USER_MIGRATED;
}
@@ -730,7 +737,7 @@ class LoginForm extends SpecialPage {
// Give general extensions, such as a captcha, a chance to abort logins
$abort = self::ABORTED;
$msg = null;
- if ( !wfRunHooks( 'AbortLogin', array( $u, $this->mPassword, &$abort, &$msg ) ) ) {
+ if ( !Hooks::run( 'AbortLogin', array( $u, $this->mPassword, &$abort, &$msg ) ) ) {
$this->mAbortLoginErrorMsg = $msg;
return $abort;
@@ -755,7 +762,7 @@ class LoginForm extends SpecialPage {
// As a side-effect, we can authenticate the user's e-mail ad-
// dress if it's not already done, since the temporary password
// was sent via e-mail.
- if ( !$u->isEmailConfirmed() ) {
+ if ( !$u->isEmailConfirmed() && !wfReadOnly() ) {
$u->confirmEmail();
$u->saveSettings();
}
@@ -791,12 +798,12 @@ class LoginForm extends SpecialPage {
if ( $isAutoCreated ) {
// Must be run after $wgUser is set, for correct new user log
- wfRunHooks( 'AuthPluginAutoCreate', array( $u ) );
+ Hooks::run( 'AuthPluginAutoCreate', array( $u ) );
}
$retval = self::SUCCESS;
}
- wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) );
+ Hooks::run( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) );
return $retval;
}
@@ -877,7 +884,7 @@ class LoginForm extends SpecialPage {
}
$abortError = '';
- if ( !wfRunHooks( 'AbortAutoAccount', array( $user, &$abortError ) ) ) {
+ if ( !Hooks::run( 'AbortAutoAccount', array( $user, &$abortError ) ) ) {
// Hook point to add extra creation throttles and blocks
wfDebug( "LoginForm::attemptAutoCreate: a hook blocked creation: $abortError\n" );
$this->mAbortLoginErrorMsg = $abortError;
@@ -906,7 +913,7 @@ class LoginForm extends SpecialPage {
case self::SUCCESS:
# We've verified now, update the real record
$user = $this->getUser();
- $user->invalidateCache();
+ $user->touch();
if ( $user->requiresHTTPS() ) {
$this->mStickHTTPS = true;
@@ -1030,7 +1037,7 @@ class LoginForm extends SpecialPage {
*/
protected function resetLoginForm( Message $msg ) {
// Allow hooks to explain this password reset in more detail
- wfRunHooks( 'LoginPasswordResetMessage', array( &$msg, $this->mUsername ) );
+ Hooks::run( 'LoginPasswordResetMessage', array( &$msg, $this->mUsername ) );
$reset = new SpecialChangePassword();
$derivative = new DerivativeContext( $this->getContext() );
$derivative->setTitle( $reset->getPageTitle() );
@@ -1063,7 +1070,7 @@ class LoginForm extends SpecialPage {
}
$currentUser = $this->getUser();
- wfRunHooks( 'User::mailPasswordInternal', array( &$currentUser, &$ip, &$u ) );
+ Hooks::run( 'User::mailPasswordInternal', array( &$currentUser, &$ip, &$u ) );
$np = $u->randomPassword();
$u->setNewpassword( $np, $throttle );
@@ -1094,7 +1101,7 @@ class LoginForm extends SpecialPage {
# Run any hooks; display injected HTML if any, else redirect
$currentUser = $this->getUser();
$injected_html = '';
- wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) );
+ Hooks::run( 'UserLoginComplete', array( &$currentUser, &$injected_html ) );
if ( $injected_html !== '' ) {
$this->displaySuccessfulAction( 'success', $this->msg( 'loginsuccesstitle' ),
@@ -1116,14 +1123,14 @@ class LoginForm extends SpecialPage {
$injected_html = '';
$welcome_creation_msg = 'welcomecreation-msg';
- wfRunHooks( 'UserLoginComplete', array( &$currentUser, &$injected_html ) );
+ Hooks::run( 'UserLoginComplete', array( &$currentUser, &$injected_html ) );
/**
* Let any extensions change what message is shown.
* @see https://www.mediawiki.org/wiki/Manual:Hooks/BeforeWelcomeCreation
* @since 1.18
*/
- wfRunHooks( 'BeforeWelcomeCreation', array( &$welcome_creation_msg, &$injected_html ) );
+ Hooks::run( 'BeforeWelcomeCreation', array( &$welcome_creation_msg, &$injected_html ) );
$this->displaySuccessfulAction(
'signup',
@@ -1233,7 +1240,7 @@ class LoginForm extends SpecialPage {
}
// Allow modification of redirect behavior
- wfRunHooks( 'PostLoginRedirect', array( &$returnTo, &$returnToQuery, &$type ) );
+ Hooks::run( 'PostLoginRedirect', array( &$returnTo, &$returnToQuery, &$type ) );
$returnToTitle = Title::newFromText( $returnTo );
if ( !$returnToTitle ) {
@@ -1262,6 +1269,12 @@ class LoginForm extends SpecialPage {
/**
* @param string $msg
* @param string $msgtype
+ * @throws ErrorPageError
+ * @throws Exception
+ * @throws FatalError
+ * @throws MWException
+ * @throws PermissionsError
+ * @throws ReadOnlyError
* @private
*/
function mainLoginForm( $msg, $msgtype = 'error' ) {
@@ -1325,7 +1338,7 @@ class LoginForm extends SpecialPage {
'mediawiki.special.userlogin.signup.styles'
) );
- $template = new UsercreateTemplate();
+ $template = new UsercreateTemplate( $this->getConfig() );
// Must match number of benefits defined in messages
$template->set( 'benefitCount', 3 );
@@ -1338,7 +1351,7 @@ class LoginForm extends SpecialPage {
'mediawiki.special.userlogin.login.styles'
) );
- $template = new UserloginTemplate();
+ $template = new UserloginTemplate( $this->getConfig() );
$q = 'action=submitlogin&type=login';
$linkq = 'type=signup';
@@ -1420,28 +1433,26 @@ class LoginForm extends SpecialPage {
}
$template->set( 'secureLoginUrl', $this->mSecureLoginUrl );
- // Use loginend-https for HTTPS requests if it's not blank, loginend otherwise
- // Ditto for signupend. New forms use neither.
+ // Use signupend-https for HTTPS requests if it's not blank, signupend otherwise
$usingHTTPS = $this->mRequest->getProtocol() == 'https';
- $loginendHTTPS = $this->msg( 'loginend-https' );
$signupendHTTPS = $this->msg( 'signupend-https' );
- if ( $usingHTTPS && !$loginendHTTPS->isBlank() ) {
- $template->set( 'loginend', $loginendHTTPS->parse() );
- } else {
- $template->set( 'loginend', $this->msg( 'loginend' )->parse() );
- }
if ( $usingHTTPS && !$signupendHTTPS->isBlank() ) {
$template->set( 'signupend', $signupendHTTPS->parse() );
} else {
$template->set( 'signupend', $this->msg( 'signupend' )->parse() );
}
+ // If using HTTPS coming from HTTP, then the 'fromhttp' parameter must be preserved
+ if ( $usingHTTPS ) {
+ $template->set( 'fromhttp', $this->mFromHTTP );
+ }
+
// Give authentication and captcha plugins a chance to modify the form
$wgAuth->modifyUITemplate( $template, $this->mType );
if ( $this->mType == 'signup' ) {
- wfRunHooks( 'UserCreateForm', array( &$template ) );
+ Hooks::run( 'UserCreateForm', array( &$template ) );
} else {
- wfRunHooks( 'UserLoginForm', array( &$template ) );
+ Hooks::run( 'UserLoginForm', array( &$template ) );
}
$out->disallowUserJs(); // just in case...
diff --git a/includes/specials/SpecialUserlogout.php b/includes/specials/SpecialUserlogout.php
index d65ac852..080dc119 100644
--- a/includes/specials/SpecialUserlogout.php
+++ b/includes/specials/SpecialUserlogout.php
@@ -56,7 +56,7 @@ class SpecialUserlogout extends UnlistedSpecialPage {
// Hook.
$injected_html = '';
- wfRunHooks( 'UserLogoutComplete', array( &$user, &$injected_html, $oldName ) );
+ Hooks::run( 'UserLogoutComplete', array( &$user, &$injected_html, $oldName ) );
$out->addHTML( $injected_html );
$out->returnToMain();
diff --git a/includes/specials/SpecialUserrights.php b/includes/specials/SpecialUserrights.php
index cefdad07..758e3c05 100644
--- a/includes/specials/SpecialUserrights.php
+++ b/includes/specials/SpecialUserrights.php
@@ -106,7 +106,7 @@ class UserrightsPage extends SpecialPage {
}
}
- if ( User::getCanonicalName( $this->mTarget ) == $user->getName() ) {
+ if ( User::getCanonicalName( $this->mTarget ) === $user->getName() ) {
$this->isself = true;
}
@@ -135,6 +135,7 @@ class UserrightsPage extends SpecialPage {
$out = $this->getOutput();
$out->addModuleStyles( 'mediawiki.special' );
+ $this->addHelpLink( 'Help:Assigning permissions' );
// show the general form
if ( count( $available['add'] ) || count( $available['remove'] ) ) {
@@ -218,7 +219,7 @@ class UserrightsPage extends SpecialPage {
/**
* Save user groups changes in the database.
*
- * @param User $user
+ * @param User|UserRightsProxy $user
* @param array $add Array of groups to add
* @param array $remove Array of groups to remove
* @param string $reason Reason for group change
@@ -228,7 +229,7 @@ class UserrightsPage extends SpecialPage {
global $wgAuth;
// Validate input set...
- $isself = ( $user->getName() == $this->getUser()->getName() );
+ $isself = $user->getName() == $this->getUser()->getName();
$groups = $user->getGroups();
$changeable = $this->changeableGroups();
$addable = array_merge( $changeable['add'], $isself ? $changeable['add-self'] : array() );
@@ -244,18 +245,22 @@ class UserrightsPage extends SpecialPage {
$oldGroups = $user->getGroups();
$newGroups = $oldGroups;
- // remove then add groups
+ // Remove then add groups
if ( $remove ) {
- $newGroups = array_diff( $newGroups, $remove );
- foreach ( $remove as $group ) {
- $user->removeGroup( $group );
+ foreach ( $remove as $index => $group ) {
+ if ( !$user->removeGroup( $group ) ) {
+ unset($remove[$index]);
+ }
}
+ $newGroups = array_diff( $newGroups, $remove );
}
if ( $add ) {
- $newGroups = array_merge( $newGroups, $add );
- foreach ( $add as $group ) {
- $user->addGroup( $group );
+ foreach ( $add as $index => $group ) {
+ if ( !$user->addGroup( $group ) ) {
+ unset($add[$index]);
+ }
}
+ $newGroups = array_merge( $newGroups, $add );
}
$newGroups = array_unique( $newGroups );
@@ -267,7 +272,7 @@ class UserrightsPage extends SpecialPage {
wfDebug( 'oldGroups: ' . print_r( $oldGroups, true ) . "\n" );
wfDebug( 'newGroups: ' . print_r( $newGroups, true ) . "\n" );
- wfRunHooks( 'UserRights', array( &$user, $add, $remove ) );
+ Hooks::run( 'UserRights', array( &$user, $add, $remove ) );
if ( $newGroups != $oldGroups ) {
$this->addLogEntry( $user, $oldGroups, $newGroups, $reason );
@@ -415,6 +420,8 @@ class UserrightsPage extends SpecialPage {
* Output a form to allow searching for a user
*/
function switchForm() {
+ $this->getOutput()->addModules( 'mediawiki.userSuggest' );
+
$this->getOutput()->addHTML(
Html::openElement(
'form',
@@ -433,7 +440,10 @@ class UserrightsPage extends SpecialPage {
'username',
30,
str_replace( '_', ' ', $this->mTarget ),
- array( 'autofocus' => true )
+ array(
+ 'autofocus' => '',
+ 'class' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
+ )
) . ' ' .
Xml::submitButton( $this->msg( 'editusergroup' )->text() ) .
Html::closeElement( 'fieldset' ) .
@@ -488,25 +498,32 @@ class UserrightsPage extends SpecialPage {
}
$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();
+ $displayedList = $this->msg( 'userrights-groupsmember-type' )
+ ->rawParams(
+ $language->listToText( $list ),
+ $language->listToText( $membersList )
+ )->escaped();
+ $displayedAutolist = $this->msg( 'userrights-groupsmember-type' )
+ ->rawParams(
+ $language->listToText( $autoList ),
+ $language->listToText( $autoMembersList )
+ )->escaped();
$grouplist = '';
$count = count( $list );
if ( $count > 0 ) {
- $grouplist = $this->msg( 'userrights-groupsmember', $count, $user->getName() )->parse();
+ $grouplist = $this->msg( 'userrights-groupsmember' )
+ ->numParams( $count )
+ ->params( $user->getName() )
+ ->parse();
$grouplist = '<p>' . $grouplist . ' ' . $displayedList . "</p>\n";
}
$count = count( $autoList );
if ( $count > 0 ) {
- $autogrouplistintro = $this->msg( 'userrights-groupsmember-auto', $count, $user->getName() )
+ $autogrouplistintro = $this->msg( 'userrights-groupsmember-auto' )
+ ->numParams( $count )
+ ->params( $user->getName() )
->parse();
$grouplist .= '<p>' . $autogrouplistintro . ' ' . $displayedAutolist . "</p>\n";
}
@@ -664,9 +681,9 @@ class UserrightsPage extends SpecialPage {
$member = User::getGroupMember( $group, $user->getName() );
if ( $checkbox['irreversible'] ) {
- $text = $this->msg( 'userrights-irreversible-marker', $member )->escaped();
+ $text = $this->msg( 'userrights-irreversible-marker', $member )->text();
} else {
- $text = htmlspecialchars( $member );
+ $text = $member;
}
$checkboxHtml = Xml::checkLabel( $text, "wpGroup-" . $group,
"wpGroup-" . $group, $checkbox['set'], $attr );
diff --git a/includes/specials/SpecialVersion.php b/includes/specials/SpecialVersion.php
index cb3fc118..c1a95939 100644
--- a/includes/specials/SpecialVersion.php
+++ b/includes/specials/SpecialVersion.php
@@ -109,12 +109,7 @@ class SpecialVersion extends SpecialPage {
$file = $this->getExtLicenseFileName( dirname( $extNode['path'] ) );
if ( $file ) {
$wikiText = file_get_contents( $file );
- if ( !isset( $extNode['license-name'] ) ) {
- // If the developer did not explicitly set license-name they probably
- // are unaware that we're now sucking this file in and thus it's probably
- // not wikitext friendly.
- $wikiText = "<pre>$wikiText</pre>";
- }
+ $wikiText = "<pre>$wikiText</pre>";
}
}
@@ -132,6 +127,7 @@ class SpecialVersion extends SpecialPage {
$out->addHtml(
$this->getSkinCredits() .
$this->getExtensionCredits() .
+ $this->getExternalLibraries() .
$this->getParserTags() .
$this->getParserFunctionHooks()
);
@@ -191,8 +187,8 @@ 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', 'Daniel Kinzler', 'Jeroen De Dauw', $othersLink,
- $translatorsLink
+ 'Timo Tijhof', 'Daniel Kinzler', 'Jeroen De Dauw', 'Brad Jorsch',
+ $othersLink, $translatorsLink
);
return wfMessage( 'version-poweredby-credits', MWTimestamp::getLocalInstance()->format( 'Y' ),
@@ -220,7 +216,7 @@ class SpecialVersion extends SpecialPage {
$software[$dbr->getSoftwareLink()] = $dbr->getServerInfo();
// Allow a hook to add/remove items.
- wfRunHooks( 'SoftwareInfo', array( &$software ) );
+ Hooks::run( 'SoftwareInfo', array( &$software ) );
$out = Xml::element(
'h2',
@@ -251,7 +247,6 @@ class SpecialVersion extends SpecialPage {
*/
public static function getVersion( $flags = '' ) {
global $wgVersion, $IP;
- wfProfileIn( __METHOD__ );
$gitInfo = self::getGitHeadSha1( $IP );
$svnInfo = self::getSvnInfo( $IP );
@@ -275,8 +270,6 @@ class SpecialVersion extends SpecialPage {
)->text();
}
- wfProfileOut( __METHOD__ );
-
return $version;
}
@@ -290,7 +283,6 @@ class SpecialVersion extends SpecialPage {
*/
public static function getVersionLinked() {
global $wgVersion;
- wfProfileIn( __METHOD__ );
$gitVersion = self::getVersionLinkedGit();
if ( $gitVersion ) {
@@ -304,8 +296,6 @@ class SpecialVersion extends SpecialPage {
}
}
- wfProfileOut( __METHOD__ );
-
return $v;
}
@@ -341,7 +331,7 @@ class SpecialVersion extends SpecialPage {
private static function getwgVersionLinked() {
global $wgVersion;
$versionUrl = "";
- if ( wfRunHooks( 'SpecialVersionVersionUrl', array( $wgVersion, &$versionUrl ) ) ) {
+ if ( Hooks::run( 'SpecialVersionVersionUrl', array( $wgVersion, &$versionUrl ) ) ) {
$versionParts = array();
preg_match( "/^(\d+\.\d+)/", $wgVersion, $versionParts );
$versionUrl = "https://www.mediawiki.org/wiki/MediaWiki_{$versionParts[1]}";
@@ -402,7 +392,7 @@ class SpecialVersion extends SpecialPage {
'other' => wfMessage( 'version-other' )->text(),
);
- wfRunHooks( 'ExtensionTypes', array( &self::$extensionTypes ) );
+ Hooks::run( 'ExtensionTypes', array( &self::$extensionTypes ) );
}
return self::$extensionTypes;
@@ -504,6 +494,57 @@ class SpecialVersion extends SpecialPage {
}
/**
+ * Generate an HTML table for external libraries that are installed
+ *
+ * @return string
+ */
+ protected function getExternalLibraries() {
+ global $IP;
+ $path = "$IP/composer.lock";
+ if ( !file_exists( $path ) ) {
+ // Maybe they're using mediawiki/vendor?
+ $path = "$IP/vendor/composer.lock";
+ if ( !file_exists( $path ) ) {
+ return '';
+ }
+ }
+
+ $lock = new ComposerLock( $path );
+ $out = Html::element(
+ 'h2',
+ array( 'id' => 'mw-version-libraries' ),
+ $this->msg( 'version-libraries' )->text()
+ );
+ $out .= Html::openElement(
+ 'table',
+ array( 'class' => 'wikitable plainlinks', 'id' => 'sv-libraries' )
+ );
+ $out .= Html::openElement( 'tr' )
+ . Html::element( 'th', array(), $this->msg( 'version-libraries-library' )->text() )
+ . Html::element( 'th', array(), $this->msg( 'version-libraries-version' )->text() )
+ . Html::closeElement( 'tr' );
+
+ foreach ( $lock->getInstalledDependencies() as $name => $info ) {
+ if ( strpos( $info['type'], 'mediawiki-' ) === 0 ) {
+ // Skip any extensions or skins since they'll be listed
+ // in their proper section
+ continue;
+ }
+ $out .= Html::openElement( 'tr' )
+ . Html::rawElement(
+ 'td',
+ array(),
+ Linker::makeExternalLink( "https://packagist.org/packages/$name", $name )
+ )
+ . Html::element( 'td', array(), $info['version'] )
+ . Html::closeElement( 'tr' );
+ }
+ $out .= Html::closeElement( 'table' );
+
+ return $out;
+ }
+
+ /**
* Obtains a list of installed parser tags and the associated H2 header
*
* @return string HTML output
@@ -516,7 +557,7 @@ class SpecialVersion extends SpecialPage {
if ( count( $tags ) ) {
$out = Html::rawElement(
'h2',
- array( 'class' => 'mw-headline' ),
+ array( 'class' => 'mw-headline plainlinks' ),
Linker::makeExternalLink(
'//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Tag_extensions',
$this->msg( 'version-parser-extensiontags' )->parse(),
@@ -525,8 +566,17 @@ class SpecialVersion extends SpecialPage {
);
array_walk( $tags, function ( &$value ) {
- $value = '&lt;' . htmlspecialchars( $value ) . '&gt;';
+ // Bidirectional isolation improves readability in RTL wikis
+ $value = Html::element(
+ 'bdi',
+ // Prevent < and > from slipping to another line
+ array(
+ 'style' => 'white-space: nowrap;',
+ ),
+ "<$value>"
+ );
} );
+
$out .= $this->listToText( $tags );
} else {
$out = '';
@@ -545,11 +595,15 @@ class SpecialVersion extends SpecialPage {
$fhooks = $wgParser->getFunctionHooks();
if ( count( $fhooks ) ) {
- $out = Html::rawElement( 'h2', array( 'class' => 'mw-headline' ), Linker::makeExternalLink(
- '//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Parser_functions',
- $this->msg( 'version-parser-function-hooks' )->parse(),
- false /* msg()->parse() already escapes */
- ) );
+ $out = Html::rawElement(
+ 'h2',
+ array( 'class' => 'mw-headline plainlinks' ),
+ Linker::makeExternalLink(
+ '//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Parser_functions',
+ $this->msg( 'version-parser-function-hooks' )->parse(),
+ false /* msg()->parse() already escapes */
+ )
+ );
$out .= $this->listToText( $fhooks );
} else {
@@ -680,7 +734,7 @@ class SpecialVersion extends SpecialPage {
list( $vcsVersion, $vcsLink, $vcsDate ) = $cache->get( $memcKey );
if ( !$vcsVersion ) {
- wfDebug( "Getting VCS info for extension $extensionName" );
+ wfDebug( "Getting VCS info for extension {$extension['name']}" );
$gitInfo = new GitInfo( $extensionPath );
$vcsVersion = $gitInfo->getHeadSHA1();
if ( $vcsVersion !== false ) {
@@ -696,7 +750,7 @@ class SpecialVersion extends SpecialPage {
}
$cache->set( $memcKey, array( $vcsVersion, $vcsLink, $vcsDate ), 60 * 60 * 24 );
} else {
- wfDebug( "Pulled VCS info for extension $extensionName from cache" );
+ wfDebug( "Pulled VCS info for extension {$extension['name']} from cache" );
}
}
@@ -739,18 +793,23 @@ class SpecialVersion extends SpecialPage {
// ... and license information; if a license file exists we
// will link to it
$licenseLink = '';
- if ( isset( $extension['license-name'] ) ) {
- $licenseLink = Linker::link(
- $this->getPageTitle( 'License/' . $extensionName ),
- $out->parseInline( $extension['license-name'] ),
- array( 'class' => 'mw-version-ext-license' )
- );
- } elseif ( $this->getExtLicenseFileName( $extensionPath ) ) {
- $licenseLink = Linker::link(
- $this->getPageTitle( 'License/' . $extensionName ),
- $this->msg( 'version-ext-license' ),
- array( 'class' => 'mw-version-ext-license' )
- );
+ if ( isset( $extension['name'] ) ) {
+ $licenseName = null;
+ if ( isset( $extension['license-name'] ) ) {
+ $licenseName = $out->parseInline( $extension['license-name'] );
+ } elseif ( $this->getExtLicenseFileName( $extensionPath ) ) {
+ $licenseName = $this->msg( 'version-ext-license' );
+ }
+ if ( $licenseName !== null ) {
+ $licenseLink = Linker::link(
+ $this->getPageTitle( 'License/' . $extension['name'] ),
+ $licenseName,
+ array(
+ 'class' => 'mw-version-ext-license',
+ 'dir' => 'auto',
+ )
+ );
+ }
}
// ... and generate the description; which can be a parameterized l10n message
@@ -778,12 +837,12 @@ class SpecialVersion extends SpecialPage {
// ... now get the authors for this extension
$authors = isset( $extension['author'] ) ? $extension['author'] : array();
- $authors = $this->listAuthors( $authors, $extensionName, $extensionPath );
+ $authors = $this->listAuthors( $authors, $extension['name'], $extensionPath );
// Finally! Create the table
$html = Html::openElement( 'tr', array(
'class' => 'mw-version-ext',
- 'id' => "mw-version-ext-{$extensionName}"
+ 'id' => "mw-version-ext-{$extension['name']}"
)
);
@@ -793,7 +852,7 @@ class SpecialVersion extends SpecialPage {
$html .= Html::rawElement( 'td', array( 'class' => 'mw-version-ext-description' ), $description );
$html .= Html::rawElement( 'td', array( 'class' => 'mw-version-ext-authors' ), $authors );
- $html .= Html::closeElement( 'td' );
+ $html .= Html::closeElement( 'tr' );
return $html;
}
@@ -916,10 +975,10 @@ class SpecialVersion extends SpecialPage {
if ( $this->getExtAuthorsFileName( $extDir ) ) {
$text = Linker::link(
$this->getPageTitle( "Credits/$extName" ),
- $this->msg( 'version-poweredby-others' )->text()
+ $this->msg( 'version-poweredby-others' )->escaped()
);
} else {
- $text = $this->msg( 'version-poweredby-others' )->text();
+ $text = $this->msg( 'version-poweredby-others' )->escaped();
}
$list[] = $text;
} elseif ( substr( $item, -5 ) == ' ...]' ) {
@@ -935,7 +994,7 @@ class SpecialVersion extends SpecialPage {
if ( !$hasOthers && $this->getExtAuthorsFileName( $extDir ) ) {
$list[] = $text = Linker::link(
$this->getPageTitle( "Credits/$extName" ),
- $this->msg( 'version-poweredby-others' )->text()
+ $this->msg( 'version-poweredby-others' )->escaped()
);
}
@@ -1024,7 +1083,7 @@ class SpecialVersion extends SpecialPage {
* Convert an array or object to a string for display.
*
* @param mixed $list Will convert an array to string if given and return
- * the paramater unaltered otherwise
+ * the parameter unaltered otherwise
*
* @return mixed
*/
@@ -1188,7 +1247,7 @@ class SpecialVersion extends SpecialPage {
$language = $this->getLanguage();
$thAttribures = array(
'dir' => $language->getDir(),
- 'lang' => $language->getCode()
+ 'lang' => $language->getHtmlCode()
);
$out = Html::element(
'h2',
diff --git a/includes/specials/SpecialWantedcategories.php b/includes/specials/SpecialWantedcategories.php
index b8c0bb28..7ddafae4 100644
--- a/includes/specials/SpecialWantedcategories.php
+++ b/includes/specials/SpecialWantedcategories.php
@@ -109,6 +109,7 @@ class WantedCategoriesPage extends WantedQueryPage {
$currentValue = isset( $this->currentCategoryCounts[$result->title] )
? $this->currentCategoryCounts[$result->title]
: 0;
+ $cachedValue = intval( $result->value ); // T76910
// If the category has been created or emptied since the list was refreshed, strike it
if ( $nt->isKnown() || $currentValue === 0 ) {
@@ -116,11 +117,11 @@ class WantedCategoriesPage extends WantedQueryPage {
}
// Show the current number of category entries if it changed
- if ( $currentValue !== $result->value ) {
+ if ( $currentValue !== $cachedValue ) {
$nlinks = $this->msg( 'nmemberschanged' )
- ->numParams( $result->value, $currentValue )->escaped();
+ ->numParams( $cachedValue, $currentValue )->escaped();
} else {
- $nlinks = $this->msg( 'nmembers' )->numParams( $result->value )->escaped();
+ $nlinks = $this->msg( 'nmembers' )->numParams( $cachedValue )->escaped();
}
}
diff --git a/includes/specials/SpecialWantedfiles.php b/includes/specials/SpecialWantedfiles.php
index 937a503c..8a1a6c6c 100644
--- a/includes/specials/SpecialWantedfiles.php
+++ b/includes/specials/SpecialWantedfiles.php
@@ -99,10 +99,10 @@ class WantedFilesPage extends WantedQueryPage {
* Use wfFindFile so we still think file namespace pages without
* files are missing, but valid file redirects and foreign files are ok.
*
- * @return boolean
+ * @return bool
*/
protected function existenceCheck( Title $title ) {
- return (bool) wfFindFile( $title );
+ return (bool)wfFindFile( $title );
}
function getQueryInfo() {
diff --git a/includes/specials/SpecialWantedpages.php b/includes/specials/SpecialWantedpages.php
index 38f1808f..dd4eb0a8 100644
--- a/includes/specials/SpecialWantedpages.php
+++ b/includes/specials/SpecialWantedpages.php
@@ -72,7 +72,10 @@ class WantedPagesPage extends WantedQueryPage {
"pg2.page_namespace != '" . NS_MEDIAWIKI . "'"
),
'options' => array(
- 'HAVING' => "COUNT(*) > $count",
+ 'HAVING' => array(
+ "COUNT(*) > $count",
+ "COUNT(*) > SUM(pg2.page_is_redirect)"
+ ),
'GROUP BY' => array( 'pl_namespace', 'pl_title' )
),
'join_conds' => array(
@@ -86,7 +89,7 @@ class WantedPagesPage extends WantedQueryPage {
)
);
// Replacement for the WantedPages::getSQL hook
- wfRunHooks( 'WantedPages::getQueryInfo', array( &$this, &$query ) );
+ Hooks::run( 'WantedPages::getQueryInfo', array( &$this, &$query ) );
return $query;
}
diff --git a/includes/specials/SpecialWatchlist.php b/includes/specials/SpecialWatchlist.php
index 8f2f86b9..df9d3639 100644
--- a/includes/specials/SpecialWatchlist.php
+++ b/includes/specials/SpecialWatchlist.php
@@ -79,22 +79,16 @@ class SpecialWatchlist extends ChangesListSpecialPage {
}
/**
- * Return an array of subpages beginning with $search that this special page will accept.
+ * Return an array of subpages that this special page will accept.
*
- * @param string $search Prefix to search for
- * @param int $limit Maximum number of results to return
- * @return string[] Matching subpages
+ * @see also SpecialEditWatchlist::getSubpagesForPrefixSearch
+ * @return string[] subpages
*/
- public function prefixSearchSubpages( $search, $limit = 10 ) {
- // See also SpecialEditWatchlist::prefixSearchSubpages
- return self::prefixSearchArray(
- $search,
- $limit,
- array(
- 'clear',
- 'edit',
- 'raw',
- )
+ public function getSubpagesForPrefixSearch() {
+ return array(
+ 'clear',
+ 'edit',
+ 'raw',
);
}
@@ -129,7 +123,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
protected function getCustomFilters() {
if ( $this->customFilters === null ) {
$this->customFilters = parent::getCustomFilters();
- wfRunHooks( 'SpecialWatchlistFilters', array( $this, &$this->customFilters ), '1.23' );
+ Hooks::run( 'SpecialWatchlistFilters', array( $this, &$this->customFilters ), '1.23' );
}
return $this->customFilters;
@@ -207,7 +201,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
} else {
# Top log Ids for a page are not stored
$nonRevisionTypes = array( RC_LOG );
- wfRunHooks( 'SpecialWatchlistGetNonRevisionTypes', array( &$nonRevisionTypes ) );
+ Hooks::run( 'SpecialWatchlistGetNonRevisionTypes', array( &$nonRevisionTypes ) );
if ( $nonRevisionTypes ) {
$conds[] = $dbr->makeList(
array(
@@ -288,9 +282,11 @@ class SpecialWatchlist extends ChangesListSpecialPage {
);
}
- protected function runMainQueryHook( &$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts ) {
+ protected function runMainQueryHook( &$tables, &$fields, &$conds, &$query_options,
+ &$join_conds, $opts
+ ) {
return parent::runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds, $opts )
- && wfRunHooks(
+ && Hooks::run(
'SpecialWatchlistQuery',
array( &$conds, &$tables, &$join_conds, &$fields, $opts ),
'1.23'
@@ -298,9 +294,9 @@ class SpecialWatchlist extends ChangesListSpecialPage {
}
/**
- * Return a DatabaseBase object for reading
+ * Return a IDatabase object for reading
*
- * @return DatabaseBase
+ * @return IDatabase
*/
protected function getDB() {
return wfGetDB( DB_SLAVE, 'watchlist' );
@@ -367,7 +363,9 @@ class SpecialWatchlist extends ChangesListSpecialPage {
$updated = false;
}
- if ( $this->getConfig()->get( 'RCShowWatchingUsers' ) && $user->getOption( 'shownumberswatching' ) ) {
+ if ( $this->getConfig()->get( 'RCShowWatchingUsers' )
+ && $user->getOption( 'shownumberswatching' )
+ ) {
$rc->numberofWatchingusers = $dbr->selectField( 'watchlist',
'COUNT(*)',
array(
@@ -430,7 +428,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
$filters[$key] = $params['msg'];
}
// Disable some if needed
- if ( !$user->useNPPatrol() ) {
+ if ( !$user->useRCPatrol() ) {
unset( $filters['hidepatrolled'] );
}
@@ -503,7 +501,9 @@ class SpecialWatchlist extends ChangesListSpecialPage {
$form .= $this->msg( 'nowatchlist' )->parse() . "\n";
} else {
$form .= $this->msg( 'watchlist-details' )->numParams( $numItems )->parse() . "\n";
- if ( $this->getConfig()->get( 'EnotifWatchlist' ) && $user->getOption( 'enotifwatchlistpages' ) ) {
+ if ( $this->getConfig()->get( 'EnotifWatchlist' )
+ && $user->getOption( 'enotifwatchlistpages' )
+ ) {
$form .= $this->msg( 'wlheader-enotif' )->parse() . "\n";
}
if ( $showUpdatedMarker ) {
@@ -562,12 +562,10 @@ class SpecialWatchlist extends ChangesListSpecialPage {
protected function daysLink( $d, $options = array() ) {
$options['days'] = $d;
- $message = $d ? $this->getLanguage()->formatNum( $d )
- : $this->msg( 'watchlistall2' )->escaped();
return Linker::linkKnown(
$this->getPageTitle(),
- $message,
+ $this->getLanguage()->formatNum( $d ),
array(),
$options
);
@@ -581,8 +579,11 @@ class SpecialWatchlist extends ChangesListSpecialPage {
* @return string
*/
protected function cutoffLinks( $days, $options = array() ) {
+ global $wgRCMaxAge;
+ $watchlistMaxDays = ceil( $wgRCMaxAge / ( 3600 * 24 ) );
+
$hours = array( 1, 2, 6, 12 );
- $days = array( 1, 3, 7 );
+ $days = array( 1, 3, 7, $watchlistMaxDays );
$i = 0;
foreach ( $hours as $h ) {
$hours[$i++] = $this->hoursLink( $h, $options );
@@ -594,14 +595,13 @@ class SpecialWatchlist extends ChangesListSpecialPage {
return $this->msg( 'wlshowlast' )->rawParams(
$this->getLanguage()->pipeList( $hours ),
- $this->getLanguage()->pipeList( $days ),
- $this->daysLink( 0, $options ) )->parse();
+ $this->getLanguage()->pipeList( $days ) )->parse();
}
/**
* Count the number of items on a user's watchlist
*
- * @param DatabaseBase $dbr A database connection
+ * @param IDatabase $dbr A database connection
* @return int
*/
protected function countItems( $dbr ) {
diff --git a/includes/specials/SpecialWhatlinkshere.php b/includes/specials/SpecialWhatlinkshere.php
index 7dc6da1f..0b3175a6 100644
--- a/includes/specials/SpecialWhatlinkshere.php
+++ b/includes/specials/SpecialWhatlinkshere.php
@@ -58,6 +58,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
$opts->add( 'hidetrans', false );
$opts->add( 'hidelinks', false );
$opts->add( 'hideimages', false );
+ $opts->add( 'invert', false );
$opts->fetchValuesFromRequest( $this->getRequest() );
$opts->validateIntBounds( 'limit', 0, 5000 );
@@ -72,7 +73,9 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
$this->target = Title::newFromURL( $opts->getValue( 'target' ) );
if ( !$this->target ) {
- $out->addHTML( $this->whatlinkshereForm() );
+ if ( !$this->including() ) {
+ $out->addHTML( $this->whatlinkshereForm() );
+ }
return;
}
@@ -125,15 +128,17 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
$useLinkNamespaceDBFields = $this->getConfig()->get( 'UseLinkNamespaceDBFields' );
$namespace = $this->opts->getValue( 'namespace' );
+ $invert = $this->opts->getValue( 'invert' );
+ $nsComparison = ( $invert ? '!= ' : '= ' ) . $dbr->addQuotes( $namespace );
if ( is_int( $namespace ) ) {
if ( $useLinkNamespaceDBFields ) {
- $conds['pagelinks']['pl_from_namespace'] = $namespace;
- $conds['templatelinks']['tl_from_namespace'] = $namespace;
- $conds['imagelinks']['il_from_namespace'] = $namespace;
+ $conds['pagelinks'][] = "pl_from_namespace $nsComparison";
+ $conds['templatelinks'][] = "tl_from_namespace $nsComparison";
+ $conds['imagelinks'][] = "il_from_namespace $nsComparison";
} else {
- $conds['pagelinks']['page_namespace'] = $namespace;
- $conds['templatelinks']['page_namespace'] = $namespace;
- $conds['imagelinks']['page_namespace'] = $namespace;
+ $conds['pagelinks'][] = "page_namespace $nsComparison";
+ $conds['templatelinks'][] = "page_namespace $nsComparison";
+ $conds['imagelinks'][] = "page_namespace $nsComparison";
}
}
@@ -149,7 +154,9 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
$conds['pagelinks'][] = 'rd_from is NOT NULL';
}
- $queryFunc = function ( $dbr, $table, $fromCol ) use ( $conds, $target, $limit, $useLinkNamespaceDBFields ) {
+ $queryFunc = function ( $dbr, $table, $fromCol ) use (
+ $conds, $target, $limit, $useLinkNamespaceDBFields
+ ) {
// Read an extra row as an at-end check
$queryLimit = $limit + 1;
$on = array(
@@ -174,7 +181,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
);
return $dbr->select(
array( 'page', 'temp_backlink_range' => "($subQuery)" ),
- array( 'page_id', 'page_namespace', 'page_title', 'rd_from' ),
+ array( 'page_id', 'page_namespace', 'page_title', 'rd_from', 'page_is_redirect' ),
array(),
__CLASS__ . '::showIndirectLinks',
array( 'ORDER BY' => 'page_id', 'LIMIT' => $queryLimit ),
@@ -275,7 +282,11 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
if ( $row->rd_from && $level < 2 ) {
$out->addHTML( $this->listItem( $row, $nt, $target, true ) );
- $this->showIndirectLinks( $level + 1, $nt, $this->getConfig()->get( 'MaxRedirectLinksRetrieved' ) );
+ $this->showIndirectLinks(
+ $level + 1,
+ $nt,
+ $this->getConfig()->get( 'MaxRedirectLinksRetrieved' )
+ );
$out->addHTML( Xml::closeElement( 'li' ) );
} else {
$out->addHTML( $this->listItem( $row, $nt, $target ) );
@@ -318,7 +329,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
$link = Linker::linkKnown(
$nt,
null,
- array(),
+ $row->page_is_redirect ? array( 'class' => 'mw-redirect' ) : array(),
$query
);
@@ -335,7 +346,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
$props[] = $msgcache['isimage'];
}
- wfRunHooks( 'WhatLinksHereProps', array( $row, $nt, $target, &$props ) );
+ Hooks::run( 'WhatLinksHereProps', array( $row, $nt, $target, &$props ) );
if ( count( $props ) ) {
$propsText = $this->msg( 'parentheses' )
@@ -419,6 +430,7 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
$target = $this->target ? $this->target->getPrefixedText() : '';
$namespace = $this->opts->consumeValue( 'namespace' );
+ $nsinvert = $this->opts->consumeValue( 'invert' );
# Build up the form
$f = Xml::openElement( 'form', array( 'action' => wfScript() ) );
@@ -431,9 +443,9 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
$f .= Xml::fieldset( $this->msg( 'whatlinkshere' )->text() );
- # Target input
+ # Target input (.mw-searchInput enables suggestions)
$f .= Xml::inputLabel( $this->msg( 'whatlinkshere-page' )->text(), 'target',
- 'mw-whatlinkshere-target', 40, $target );
+ 'mw-whatlinkshere-target', 40, $target, array( 'class' => 'mw-searchInput' ) );
$f .= ' ';
@@ -450,6 +462,15 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
)
);
+ $f .= '&#160;' .
+ Xml::checkLabel(
+ $this->msg( 'invert' )->text(),
+ 'invert',
+ 'nsinvert',
+ $nsinvert,
+ array( 'title' => $this->msg( 'tooltip-whatlinkshere-invert' )->text() )
+ );
+
$f .= ' ';
# Submit
@@ -496,6 +517,24 @@ class SpecialWhatLinksHere extends IncludableSpecialPage {
);
}
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ if ( $search === '' ) {
+ return array();
+ }
+ // Autocomplete subpage the same as a normal search
+ $prefixSearcher = new StringPrefixSearch;
+ $result = $prefixSearcher->search( $search, $limit, array(), $offset );
+ return $result;
+ }
+
protected function getGroupName() {
return 'pagetools';
}