summaryrefslogtreecommitdiff
path: root/includes/specials/SpecialContributions.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/specials/SpecialContributions.php')
-rw-r--r--includes/specials/SpecialContributions.php254
1 files changed, 167 insertions, 87 deletions
diff --git a/includes/specials/SpecialContributions.php b/includes/specials/SpecialContributions.php
index 1fe98190..32a887c4 100644
--- a/includes/specials/SpecialContributions.php
+++ b/includes/specials/SpecialContributions.php
@@ -26,8 +26,7 @@
*
* @ingroup SpecialPage
*/
-
-class SpecialContributions extends SpecialPage {
+class SpecialContributions extends IncludableSpecialPage {
protected $opts;
public function __construct() {
@@ -63,7 +62,9 @@ class SpecialContributions extends SpecialPage {
$this->opts['deletedOnly'] = $request->getBool( 'deletedOnly' );
if ( !strlen( $target ) ) {
- $out->addHTML( $this->getForm() );
+ if ( !$this->including() ) {
+ $out->addHTML( $this->getForm() );
+ }
return;
}
@@ -73,6 +74,7 @@ class SpecialContributions extends SpecialPage {
$this->opts['limit'] = $request->getInt( 'limit', $user->getOption( 'rclimit' ) );
$this->opts['target'] = $target;
$this->opts['topOnly'] = $request->getBool( 'topOnly' );
+ $this->opts['newOnly'] = $request->getBool( 'newOnly' );
$nt = Title::makeTitleSafe( NS_USER, $target );
if ( !$nt ) {
@@ -94,14 +96,14 @@ class SpecialContributions extends SpecialPage {
$out->setHTMLTitle( $this->msg(
'pagetitle',
$this->msg( 'contributions-title', $target )->plain()
- ) );
+ )->inContentLanguage() );
$this->getSkin()->setRelevantUser( $userObj );
} else {
$out->addSubtitle( $this->msg( 'sp-contributions-newbies-sub' ) );
$out->setHTMLTitle( $this->msg(
'pagetitle',
$this->msg( 'sp-contributions-newbies-title' )->plain()
- ) );
+ )->inContentLanguage() );
}
if ( ( $ns = $request->getVal( 'namespace', null ) ) !== null && $ns !== '' ) {
@@ -131,34 +133,40 @@ class SpecialContributions extends SpecialPage {
}
$feedType = $request->getVal( 'feed' );
+
+ $feedParams = array(
+ 'action' => 'feedcontributions',
+ 'user' => $target,
+ );
+ if ( $this->opts['topOnly'] ) {
+ $feedParams['toponly'] = true;
+ }
+ if ( $this->opts['newOnly'] ) {
+ $feedParams['newonly'] = true;
+ }
+ if ( $this->opts['deletedOnly'] ) {
+ $feedParams['deletedonly'] = true;
+ }
+ if ( $this->opts['tagfilter'] !== '' ) {
+ $feedParams['tagfilter'] = $this->opts['tagfilter'];
+ }
+ if ( $this->opts['namespace'] !== '' ) {
+ $feedParams['namespace'] = $this->opts['namespace'];
+ }
+ // Don't use year and month for the feed URL, but pass them on if
+ // we redirect to API (if $feedType is specified)
+ if ( $feedType && $this->opts['year'] !== null ) {
+ $feedParams['year'] = $this->opts['year'];
+ }
+ if ( $feedType && $this->opts['month'] !== null ) {
+ $feedParams['month'] = $this->opts['month'];
+ }
+
if ( $feedType ) {
- // Maintain some level of backwards compatability
+ // Maintain some level of backwards compatibility
// If people request feeds using the old parameters, redirect to API
- $apiParams = array(
- 'action' => 'feedcontributions',
- 'feedformat' => $feedType,
- 'user' => $target,
- );
- if ( $this->opts['topOnly'] ) {
- $apiParams['toponly'] = true;
- }
- if ( $this->opts['deletedOnly'] ) {
- $apiParams['deletedonly'] = true;
- }
- if ( $this->opts['tagfilter'] !== '' ) {
- $apiParams['tagfilter'] = $this->opts['tagfilter'];
- }
- if ( $this->opts['namespace'] !== '' ) {
- $apiParams['namespace'] = $this->opts['namespace'];
- }
- if ( $this->opts['year'] !== null ) {
- $apiParams['year'] = $this->opts['year'];
- }
- if ( $this->opts['month'] !== null ) {
- $apiParams['month'] = $this->opts['month'];
- }
-
- $url = wfAppendQuery( wfScript( 'api' ), $apiParams );
+ $feedParams['feedformat'] = $feedType;
+ $url = wfAppendQuery( wfScript( 'api' ), $feedParams );
$out->redirect( $url, '301' );
@@ -166,11 +174,12 @@ class SpecialContributions extends SpecialPage {
}
// Add RSS/atom links
- $this->addFeedLinks( array( 'action' => 'feedcontributions', 'user' => $target ) );
-
- if ( wfRunHooks( 'SpecialContributionsBeforeMainOutput', array( $id ) ) ) {
- $out->addHTML( $this->getForm() );
+ $this->addFeedLinks( $feedParams );
+ if ( wfRunHooks( 'SpecialContributionsBeforeMainOutput', array( $id, $userObj, $this ) ) ) {
+ if ( !$this->including() ) {
+ $out->addHTML( $this->getForm() );
+ }
$pager = new ContribsPager( $this->getContext(), array(
'target' => $target,
'contribs' => $this->opts['contribs'],
@@ -180,6 +189,7 @@ class SpecialContributions extends SpecialPage {
'month' => $this->opts['month'],
'deletedOnly' => $this->opts['deletedOnly'],
'topOnly' => $this->opts['topOnly'],
+ 'newOnly' => $this->opts['newOnly'],
'nsInvert' => $this->opts['nsInvert'],
'associated' => $this->opts['associated'],
) );
@@ -193,11 +203,13 @@ class SpecialContributions extends SpecialPage {
$out->showLagWarning( $lag );
}
- $out->addHTML(
- '<p>' . $pager->getNavigationBar() . '</p>' .
- $pager->getBody() .
- '<p>' . $pager->getNavigationBar() . '</p>'
- );
+ $output = $pager->getBody();
+ if ( !$this->including() ) {
+ $output = '<p>' . $pager->getNavigationBar() . '</p>' .
+ $output .
+ '<p>' . $pager->getNavigationBar() . '</p>';
+ }
+ $out->addHTML( $output );
}
$out->preventClickjacking( $pager->getPreventClickjacking() );
@@ -214,10 +226,12 @@ class SpecialContributions extends SpecialPage {
}
if ( $message ) {
- if ( !$this->msg( $message, $target )->isDisabled() ) {
- $out->wrapWikiMsg(
- "<div class='mw-contributions-footer'>\n$1\n</div>",
- array( $message, $target ) );
+ if ( !$this->including() ) {
+ if ( !$this->msg( $message, $target )->isDisabled() ) {
+ $out->wrapWikiMsg(
+ "<div class='mw-contributions-footer'>\n$1\n</div>",
+ array( $message, $target ) );
+ }
}
}
}
@@ -225,13 +239,26 @@ class SpecialContributions extends SpecialPage {
/**
* Generates the subheading with links
- * @param $userObj User object for the target
- * @return String: appropriately-escaped HTML to be output literally
+ * @param User $userObj User object for the target
+ * @return string Appropriately-escaped HTML to be output literally
* @todo FIXME: Almost the same as getSubTitle in SpecialDeletedContributions.php.
* Could be combined.
*/
protected function contributionsSub( $userObj ) {
if ( $userObj->isAnon() ) {
+ // Show a warning message that the user being searched for doesn't exists
+ if ( !User::isIP( $userObj->getName() ) ) {
+ $this->getOutput()->wrapWikiMsg(
+ "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
+ array(
+ 'contributions-userdoesnotexist',
+ wfEscapeWikiText( $userObj->getName() ),
+ )
+ );
+ if ( !$this->including() ) {
+ $this->getOutput()->setStatusCode( 404 );
+ }
+ }
$user = htmlspecialchars( $userObj->getName() );
} else {
$user = Linker::link( $userObj->getUserPage(), htmlspecialchars( $userObj->getName() ) );
@@ -246,25 +273,32 @@ class SpecialContributions extends SpecialPage {
// Show a note if the user is blocked and display the last block log entry.
// Do not expose the autoblocks, since that may lead to a leak of accounts' IPs,
// and also this will display a totally irrelevant log entry as a current block.
- if ( $userObj->isBlocked() && $userObj->getBlock()->getType() != Block::TYPE_AUTO ) {
- $out = $this->getOutput(); // showLogExtract() wants first parameter by reference
- LogEventsList::showLogExtract(
- $out,
- 'block',
- $nt,
- '',
- array(
- 'lim' => 1,
- 'showIfEmpty' => false,
- 'msgKey' => array(
- $userObj->isAnon() ?
- 'sp-contributions-blocked-notice-anon' :
- 'sp-contributions-blocked-notice',
- $userObj->getName() # Support GENDER in 'sp-contributions-blocked-notice'
- ),
- 'offset' => '' # don't use WebRequest parameter offset
- )
- );
+ if ( !$this->including() ) {
+ $block = Block::newFromTarget( $userObj, $userObj );
+ if ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
+ if ( $block->getType() == Block::TYPE_RANGE ) {
+ $nt = MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget();
+ }
+
+ $out = $this->getOutput(); // showLogExtract() wants first parameter by reference
+ LogEventsList::showLogExtract(
+ $out,
+ 'block',
+ $nt,
+ '',
+ array(
+ 'lim' => 1,
+ 'showIfEmpty' => false,
+ 'msgKey' => array(
+ $userObj->isAnon() ?
+ 'sp-contributions-blocked-notice-anon' :
+ 'sp-contributions-blocked-notice',
+ $userObj->getName() # Support GENDER in 'sp-contributions-blocked-notice'
+ ),
+ 'offset' => '' # don't use WebRequest parameter offset
+ )
+ );
+ }
}
}
@@ -273,9 +307,9 @@ class SpecialContributions extends SpecialPage {
/**
* Links to different places.
- * @param $userpage Title: Target user page
- * @param $talkpage Title: Talk page
- * @param $target User: Target user object
+ * @param Title $userpage Target user page
+ * @param Title $talkpage Talk page
+ * @param User $target Target user object
* @return array
*/
public function getUserLinks( Title $userpage, Title $talkpage, User $target ) {
@@ -311,6 +345,16 @@ class SpecialContributions extends SpecialPage {
array(),
array( 'page' => $userpage->getPrefixedText() )
);
+
+ # Suppression log link (bug 59120)
+ if ( $this->getUser()->isAllowed( 'suppressionlog' ) ) {
+ $tools[] = Linker::linkKnown(
+ SpecialPage::getTitleFor( 'Log', 'suppress' ),
+ $this->msg( 'sp-contributions-suppresslog' )->escaped(),
+ array(),
+ array( 'offender' => $username )
+ );
+ }
}
# Uploads
$tools[] = Linker::linkKnown(
@@ -349,12 +393,10 @@ class SpecialContributions extends SpecialPage {
/**
* Generates the namespace selector form with hidden attributes.
- * @return String: HTML fragment
+ * @return string HTML fragment
*/
protected function getForm() {
- global $wgScript;
-
- $this->opts['title'] = $this->getTitle()->getPrefixedText();
+ $this->opts['title'] = $this->getPageTitle()->getPrefixedText();
if ( !isset( $this->opts['target'] ) ) {
$this->opts['target'] = '';
} else {
@@ -397,11 +439,15 @@ class SpecialContributions extends SpecialPage {
$this->opts['topOnly'] = false;
}
+ if ( !isset( $this->opts['newOnly'] ) ) {
+ $this->opts['newOnly'] = false;
+ }
+
$form = Html::openElement(
'form',
array(
'method' => 'get',
- 'action' => $wgScript,
+ 'action' => wfScript(),
'class' => 'mw-contributions-form'
)
);
@@ -416,6 +462,7 @@ class SpecialContributions extends SpecialPage {
'year',
'month',
'topOnly',
+ 'newOnly',
'associated'
);
@@ -548,10 +595,21 @@ class SpecialContributions extends SpecialPage {
array( 'class' => 'mw-input' )
)
);
+ $checkLabelNewOnly = Html::rawElement(
+ 'span',
+ array( 'style' => 'white-space: nowrap' ),
+ Xml::checkLabel(
+ $this->msg( 'sp-contributions-newonly' )->text(),
+ 'newOnly',
+ 'mw-show-new-only',
+ $this->opts['newOnly'],
+ array( 'class' => 'mw-input' )
+ )
+ );
$extraOptions = Html::rawElement(
'td',
array( 'colspan' => 2 ),
- $deletedOnlyCheck . $checkLabelTopOnly
+ $deletedOnlyCheck . $checkLabelTopOnly . $checkLabelNewOnly
);
$dateSelectionAndSubmit = Xml::tags( 'td', array( 'colspan' => 2 ),
@@ -594,13 +652,16 @@ class SpecialContributions extends SpecialPage {
* @ingroup SpecialPage Pager
*/
class ContribsPager extends ReverseChronologicalPager {
- public $mDefaultDirection = true;
+ public $mDefaultDirection = IndexPager::DIR_DESCENDING;
public $messages;
public $target;
public $namespace = '';
public $mDb;
public $preventClickjacking = false;
+ /** @var DatabaseBase */
+ public $mDbSecondary;
+
/**
* @var array
*/
@@ -632,11 +693,16 @@ class ContribsPager extends ReverseChronologicalPager {
$this->deletedOnly = !empty( $options['deletedOnly'] );
$this->topOnly = !empty( $options['topOnly'] );
+ $this->newOnly = !empty( $options['newOnly'] );
$year = isset( $options['year'] ) ? $options['year'] : false;
$month = isset( $options['month'] ) ? $options['month'] : false;
$this->getDateCond( $year, $month );
+ // Most of this code will use the 'contributions' group DB, which can map to slaves
+ // with extra user based indexes or partioning by user. The additional metadata
+ // queries should use a regular slave since the lookup pattern is not all by user.
+ $this->mDbSecondary = wfGetDB( DB_SLAVE ); // any random slave
$this->mDb = wfGetDB( DB_SLAVE, 'contributions' );
}
@@ -651,9 +717,9 @@ class ContribsPager extends ReverseChronologicalPager {
* This method basically executes the exact same code as the parent class, though with
* a hook added, to allow extentions to add additional queries.
*
- * @param string $offset index offset, inclusive
- * @param $limit Integer: exact query limit
- * @param $descending Boolean: query direction, false for ascending, true for descending
+ * @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 ) {
@@ -725,7 +791,7 @@ class ContribsPager extends ReverseChronologicalPager {
// Paranoia: avoid brute force searches (bug 17342)
if ( !$user->isAllowed( 'deletedhistory' ) ) {
$conds[] = $this->mDb->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0';
- } elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
+ } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
$conds[] = $this->mDb->bitAnd( 'rev_deleted', Revision::SUPPRESSED_USER ) .
' != ' . Revision::SUPPRESSED_USER;
}
@@ -735,6 +801,11 @@ class ContribsPager extends ReverseChronologicalPager {
# Get the current user name for accounts
$join_cond['user'] = Revision::userJoinCond();
+ $options = array();
+ if ( $index ) {
+ $options['USE INDEX'] = array( 'revision' => $index );
+ }
+
$queryInfo = array(
'tables' => $tables,
'fields' => array_merge(
@@ -744,7 +815,7 @@ class ContribsPager extends ReverseChronologicalPager {
'page_latest', 'page_is_redirect', 'page_len' )
),
'conds' => $conds,
- 'options' => array( 'USE INDEX' => array( 'revision' => $index ) ),
+ 'options' => $options,
'join_conds' => $join_cond
);
@@ -766,10 +837,10 @@ class ContribsPager extends ReverseChronologicalPager {
$condition = array();
$join_conds = array();
$tables = array( 'revision', 'page', 'user' );
+ $index = false;
if ( $this->contribs == 'newbie' ) {
$max = $this->mDb->selectField( 'user', 'max(user_id)', false, __METHOD__ );
$condition[] = 'rev_user >' . (int)( $max - $max / 100 );
- $index = 'user_timestamp';
# ignore local groups with the bot right
# @todo FIXME: Global groups may have 'bot' rights
$groupsWithBotPermission = User::getGroupsWithPermission( 'bot' );
@@ -802,6 +873,10 @@ class ContribsPager extends ReverseChronologicalPager {
$condition[] = 'rev_id = page_latest';
}
+ if ( $this->newOnly ) {
+ $condition[] = 'rev_parent_id = 0';
+ }
+
return array( $tables, $index, $condition, $join_conds );
}
@@ -851,7 +926,7 @@ class ContribsPager extends ReverseChronologicalPager {
$batch->add( $row->page_namespace, $row->page_title );
}
}
- $this->mParentLens = Revision::getParentLengths( $this->getDatabase(), $revIds );
+ $this->mParentLens = Revision::getParentLengths( $this->mDbSecondary, $revIds );
$batch->execute();
$this->mResult->seek( 0 );
}
@@ -879,7 +954,7 @@ class ContribsPager extends ReverseChronologicalPager {
* was not written by the target user.
*
* @todo This would probably look a lot nicer in a table.
- * @param $row
+ * @param object $row
* @return string
*/
function formatRow( $row ) {
@@ -896,8 +971,12 @@ class ContribsPager extends ReverseChronologicalPager {
* to extensions to subscribe to the hook to parse the row.
*/
wfSuppressWarnings();
- $rev = new Revision( $row );
- $validRevision = (bool)$rev->getId();
+ try {
+ $rev = new Revision( $row );
+ $validRevision = (bool)$rev->getId();
+ } catch ( MWException $e ) {
+ $validRevision = false;
+ }
wfRestoreWarnings();
if ( $validRevision ) {
@@ -986,7 +1065,8 @@ class ContribsPager extends ReverseChronologicalPager {
# Show user names for /newbies as there may be different users.
# Note that we already excluded rows with hidden user names.
if ( $this->contribs == 'newbie' ) {
- $userlink = ' . . ' . $lang->getDirMark() . Linker::userLink( $rev->getUser(), $rev->getUserText() );
+ $userlink = ' . . ' . $lang->getDirMark()
+ . Linker::userLink( $rev->getUser(), $rev->getUserText() );
$userlink .= ' ' . $this->msg( 'parentheses' )->rawParams(
Linker::userTalkLink( $rev->getUser(), $rev->getUserText() ) )->escaped() . ' ';
} else {
@@ -1036,7 +1116,7 @@ class ContribsPager extends ReverseChronologicalPager {
wfRunHooks( 'ContributionsLineEnding', array( $this, &$ret, $row, &$classes ) );
if ( $classes === array() && $ret === '' ) {
- wfDebug( 'Dropping Special:Contribution row that could not be formatted' );
+ wfDebug( "Dropping Special:Contribution row that could not be formatted\n" );
$ret = "<!-- Could not format Special:Contribution row. -->\n";
} else {
$ret = Html::rawElement( 'li', array( 'class' => $classes ), $ret ) . "\n";