Enable users to watch category membership changes #2
This is part of a chain that reverts:
e412ff5ecc.
NOTE:
- The feature is disabled by default
- User settings default to hiding changes
- T109707 Touching a file on wikisource adds and
removes it from a category... Even when page
has no changes.... WTF? See linked issue,
marked as stalled with a possible way forward
for this patch.
@see https://gerrit.wikimedia.org/r/#/c/235467/
Changes since version 1:
- T109604 - Page names in comment are no longer
url encoded / have _'s
- T109638 & T110338 - Reserved username now used
when we can't determine a username for the change
(we could perhaps set the user and id to be blank
in the RC table, but who knows what this might do)
- T109688 - History links are now disabled in RC....
(could be fine for the introduction and worked
on more in the future)
- Categorization changes are now always patrolled
- Touching on T109672 in this change emails will never
be sent regarding categorization changes. (this
can of course be changed in a followup)
- Added $wgRCWatchCategoryMembership defaulting to true
for enabling / disabling the feature
- T109700 - for cases when no revision was retrieved
for a category change set the bot flag to true.
This means all changes caused by parser functions
& Lua will be marked as bot, as will changes that
cant find their revision due to slave lag..
Bug: T9148
Bug: T109604
Bug: T109638
Bug: T109688
Bug: T109700
Bug: T110338
Bug: T110340
Change-Id: I51c2c1254de862f24a26ef9dbbf027c6c83e9063
This commit is contained in:
parent
7b19f2e37d
commit
d40cd42b9f
25 changed files with 427 additions and 57 deletions
|
|
@ -4539,6 +4539,7 @@ $wgDefaultUserOptions = array(
|
|||
'gender' => 'unknown',
|
||||
'hideminor' => 0,
|
||||
'hidepatrolled' => 0,
|
||||
'hidecategorization' => 1,
|
||||
'imagesize' => 2,
|
||||
'math' => 1,
|
||||
'minordefault' => 0,
|
||||
|
|
@ -4570,6 +4571,7 @@ $wgDefaultUserOptions = array(
|
|||
'watchlisthideminor' => 0,
|
||||
'watchlisthideown' => 0,
|
||||
'watchlisthidepatrolled' => 0,
|
||||
'watchlisthidecategorization' => 1,
|
||||
'watchmoves' => 0,
|
||||
'watchrollback' => 0,
|
||||
'wllimit' => 250,
|
||||
|
|
@ -6169,6 +6171,12 @@ $wgRCEngines = array(
|
|||
'udp' => 'UDPRCFeedEngine',
|
||||
);
|
||||
|
||||
/**
|
||||
* Treat category membership changes as a RecentChange
|
||||
* @since 1.27
|
||||
*/
|
||||
$wgRCWatchCategoryMembership = false;
|
||||
|
||||
/**
|
||||
* Use RC Patrolling to check for vandalism
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -892,6 +892,14 @@ class Preferences {
|
|||
'section' => 'rc/advancedrc',
|
||||
);
|
||||
|
||||
if ( $config->get( 'RCWatchCategoryMembership' ) ) {
|
||||
$defaultPreferences['hidecategorization'] = array(
|
||||
'type' => 'toggle',
|
||||
'label-message' => 'tog-hidecategorization',
|
||||
'section' => 'rc/advancedrc',
|
||||
);
|
||||
}
|
||||
|
||||
if ( $user->useRCPatrol() ) {
|
||||
$defaultPreferences['hidepatrolled'] = array(
|
||||
'type' => 'toggle',
|
||||
|
|
@ -999,6 +1007,14 @@ class Preferences {
|
|||
'label-message' => 'tog-watchlisthideliu',
|
||||
);
|
||||
|
||||
if ( $config->get( 'RCWatchCategoryMembership' ) ) {
|
||||
$defaultPreferences['watchlisthidecategorization'] = array(
|
||||
'type' => 'toggle',
|
||||
'section' => 'watchlist/advancedwatchlist',
|
||||
'label-message' => 'tog-watchlisthidecategorization',
|
||||
);
|
||||
}
|
||||
|
||||
if ( $user->useRCPatrol() ) {
|
||||
$defaultPreferences['watchlisthidepatrolled'] = array(
|
||||
'type' => 'toggle',
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ class ApiFeedRecentChanges extends ApiBase {
|
|||
'hideliu' => false,
|
||||
'hidepatrolled' => false,
|
||||
'hidemyself' => false,
|
||||
'hidecategorization' => false,
|
||||
|
||||
'tagfilter' => array(
|
||||
ApiBase::PARAM_TYPE => 'string',
|
||||
|
|
|
|||
|
|
@ -678,14 +678,9 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
|
|||
ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
|
||||
),
|
||||
'type' => array(
|
||||
ApiBase::PARAM_DFLT => 'edit|new|log',
|
||||
ApiBase::PARAM_DFLT => 'edit|new|log|categorize',
|
||||
ApiBase::PARAM_ISMULTI => true,
|
||||
ApiBase::PARAM_TYPE => array(
|
||||
'edit',
|
||||
'external',
|
||||
'new',
|
||||
'log'
|
||||
)
|
||||
ApiBase::PARAM_TYPE => RecentChange::getChangeTypes()
|
||||
),
|
||||
'toponly' => false,
|
||||
'continue' => array(
|
||||
|
|
|
|||
|
|
@ -483,14 +483,10 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
|
|||
)
|
||||
),
|
||||
'type' => array(
|
||||
ApiBase::PARAM_DFLT => 'edit|new|log',
|
||||
ApiBase::PARAM_DFLT => 'edit|new|log|categorize',
|
||||
ApiBase::PARAM_ISMULTI => true,
|
||||
ApiBase::PARAM_TYPE => array(
|
||||
'edit',
|
||||
'external',
|
||||
'new',
|
||||
'log',
|
||||
)
|
||||
ApiBase::PARAM_HELP_MSG_PER_VALUE => array(),
|
||||
ApiBase::PARAM_TYPE => RecentChange::getChangeTypes()
|
||||
),
|
||||
'owner' => array(
|
||||
ApiBase::PARAM_TYPE => 'user'
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@
|
|||
"apihelp-feedrecentchanges-param-hideliu": "Hide changes made by registered users.",
|
||||
"apihelp-feedrecentchanges-param-hidepatrolled": "Hide patrolled changes.",
|
||||
"apihelp-feedrecentchanges-param-hidemyself": "Hide changes made by the current user.",
|
||||
"apihelp-feedrecentchanges-param-hidecategorization": "Hide category membership changes.",
|
||||
"apihelp-feedrecentchanges-param-tagfilter": "Filter by tag.",
|
||||
"apihelp-feedrecentchanges-param-target": "Show only changes on pages linked from this page.",
|
||||
"apihelp-feedrecentchanges-param-showlinkedto": "Show changes on pages linked to the selected page instead.",
|
||||
|
|
@ -1211,7 +1212,12 @@
|
|||
"apihelp-query+watchlist-paramvalue-prop-notificationtimestamp": "Adds timestamp of when the user was last notified about the edit.",
|
||||
"apihelp-query+watchlist-paramvalue-prop-loginfo": "Adds log information where appropriate.",
|
||||
"apihelp-query+watchlist-param-show": "Show only items that meet these criteria. For example, to see only minor edits done by logged-in users, set $1show=minor|!anon.",
|
||||
"apihelp-query+watchlist-param-type": "Which types of changes to show:\n;edit:Regular page edits.\n;external:External changes.\n;new:Page creations.\n;log:Log entries.",
|
||||
"apihelp-query+watchlist-param-type": "Which types of changes to show:",
|
||||
"apihelp-query+watchlist-paramvalue-type-edit": "Regular page edits.",
|
||||
"apihelp-query+watchlist-paramvalue-type-external": "External changes.",
|
||||
"apihelp-query+watchlist-paramvalue-type-new": "Page creations.",
|
||||
"apihelp-query+watchlist-paramvalue-type-log": "Log entries.",
|
||||
"apihelp-query+watchlist-paramvalue-type-categorize": "Category membership changes.",
|
||||
"apihelp-query+watchlist-param-owner": "Used along with $1token to access a different user's watchlist.",
|
||||
"apihelp-query+watchlist-param-token": "A security token (available in the user's [[Special:Preferences#mw-prefsection-watchlist|preferences]]) to allow access to another user's watchlist.",
|
||||
"apihelp-query+watchlist-example-simple": "List the top revision for recently changed pages on the current user's watchlist.",
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@
|
|||
"apihelp-feedrecentchanges-param-hideliu": "{{doc-apihelp-param|feedrecentchanges|hideliu}}",
|
||||
"apihelp-feedrecentchanges-param-hidepatrolled": "{{doc-apihelp-param|feedrecentchanges|hidepatrolled}}",
|
||||
"apihelp-feedrecentchanges-param-hidemyself": "{{doc-apihelp-param|feedrecentchanges|hidemyself}}",
|
||||
"apihelp-feedrecentchanges-param-hidecategorization": "{{doc-apihelp-param|feedrecentchanges|hidecategorization}}",
|
||||
"apihelp-feedrecentchanges-param-tagfilter": "{{doc-apihelp-param|feedrecentchanges|tagfilter}}",
|
||||
"apihelp-feedrecentchanges-param-target": "{{doc-apihelp-param|feedrecentchanges|target}}",
|
||||
"apihelp-feedrecentchanges-param-showlinkedto": "{{doc-apihelp-param|feedrecentchanges|showlinkedto}}",
|
||||
|
|
@ -1131,6 +1132,11 @@
|
|||
"apihelp-query+watchlist-paramvalue-prop-loginfo": "{{doc-apihelp-paramvalue|query+watchlist|prop|loginfo}}",
|
||||
"apihelp-query+watchlist-param-show": "{{doc-apihelp-param|query+watchlist|show}}",
|
||||
"apihelp-query+watchlist-param-type": "{{doc-apihelp-param|query+watchlist|type}}",
|
||||
"apihelp-query+watchlist-paramvalue-type-edit": "{{doc-apihelp-paramvalue|query+watchlist|type|edit}}",
|
||||
"apihelp-query+watchlist-paramvalue-type-external": "{{doc-apihelp-paramvalue|query+watchlist|type|external}}",
|
||||
"apihelp-query+watchlist-paramvalue-type-new": "{{doc-apihelp-paramvalue|query+watchlist|type|new}}",
|
||||
"apihelp-query+watchlist-paramvalue-type-log": "{{doc-apihelp-paramvalue|query+watchlist|type|log}}",
|
||||
"apihelp-query+watchlist-paramvalue-type-categorize": "{{doc-apihelp-paramvalue|query+watchlist|type|categorize}}",
|
||||
"apihelp-query+watchlist-param-owner": "{{doc-apihelp-param|query+watchlist|owner}}",
|
||||
"apihelp-query+watchlist-param-token": "{{doc-apihelp-param|query+watchlist|token}}",
|
||||
"apihelp-query+watchlist-example-simple": "{{doc-apihelp-example|query+watchlist}}",
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ class CategoryMembershipChange {
|
|||
|
||||
/**
|
||||
* @var int
|
||||
* Number of pages this WikiPage is embedded by; set by CategoryMembershipChange::setRecursive()
|
||||
* Number of pages this WikiPage is embedded by
|
||||
* Set by CategoryMembershipChange::checkTemplateLinks()
|
||||
*/
|
||||
private $numTemplateLinks = 0;
|
||||
|
||||
|
|
@ -239,7 +240,7 @@ class CategoryMembershipChange {
|
|||
* @param int $type may be CategoryMembershipChange::CATEGORY_ADDITION
|
||||
* or CategoryMembershipChange::CATEGORY_REMOVAL
|
||||
* @param array $params
|
||||
* - prefixedUrl: result of Title::->getPrefixedURL()
|
||||
* - prefixedText: result of Title::->getPrefixedText()
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -305,7 +305,11 @@ class ChangesList extends ContextSource {
|
|||
*/
|
||||
public function insertDiffHist( &$s, &$rc, $unpatrolled ) {
|
||||
# Diff link
|
||||
if ( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
|
||||
if (
|
||||
$rc->mAttribs['rc_type'] == RC_NEW ||
|
||||
$rc->mAttribs['rc_type'] == RC_LOG ||
|
||||
$rc->mAttribs['rc_type'] == RC_CATEGORIZE
|
||||
) {
|
||||
$diffLink = $this->message['diff'];
|
||||
} elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
|
||||
$diffLink = $this->message['diff'];
|
||||
|
|
@ -323,17 +327,22 @@ class ChangesList extends ContextSource {
|
|||
$query
|
||||
);
|
||||
}
|
||||
$diffhist = $diffLink . $this->message['pipe-separator'];
|
||||
# History link
|
||||
$diffhist .= Linker::linkKnown(
|
||||
$rc->getTitle(),
|
||||
$this->message['hist'],
|
||||
array(),
|
||||
array(
|
||||
'curid' => $rc->mAttribs['rc_cur_id'],
|
||||
'action' => 'history'
|
||||
)
|
||||
);
|
||||
if ( $rc->mAttribs['rc_type'] == RC_CATEGORIZE ) {
|
||||
$diffhist = $diffLink . $this->message['pipe-separator'] . $this->message['hist'];
|
||||
} else {
|
||||
$diffhist = $diffLink . $this->message['pipe-separator'];
|
||||
# History link
|
||||
$diffhist .= Linker::linkKnown(
|
||||
$rc->getTitle(),
|
||||
$this->message['hist'],
|
||||
array(),
|
||||
array(
|
||||
'curid' => $rc->mAttribs['rc_cur_id'],
|
||||
'action' => 'history'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// @todo FIXME: Hard coded ". .". Is there a message for this? Should there be?
|
||||
$s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() .
|
||||
' <span class="mw-changeslist-separator">. .</span> ';
|
||||
|
|
@ -630,4 +639,19 @@ class ChangesList extends ContextSource {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a revision is linked to this change; this may not be the case
|
||||
* when the categorization wasn't done by an edit but a conditional parser function
|
||||
*
|
||||
* @since 1.27
|
||||
*
|
||||
* @param RecentChange|RCCacheEntry $rcObj
|
||||
* @return bool
|
||||
*/
|
||||
protected function isCategorizationWithoutRevision( $rcObj ) {
|
||||
return intval( $rcObj->getAttribute( 'rc_type' ) ) === RC_CATEGORIZE
|
||||
&& intval( $rcObj->getAttribute( 'rc_this_oldid' ) ) === 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -405,6 +405,8 @@ class EnhancedChangesList extends ChangesList {
|
|||
|
||||
if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) {
|
||||
$data['logEntry'] = $this->insertLogEntry( $rcObj );
|
||||
} elseif ( $this->isCategorizationWithoutRevision( $rcObj ) ) {
|
||||
$data['comment'] = $this->insertComment( $rcObj );
|
||||
} else {
|
||||
# User links
|
||||
$data['userLink'] = $rcObj->userlink;
|
||||
|
|
@ -497,7 +499,7 @@ class EnhancedChangesList extends ChangesList {
|
|||
/** @var $block0 RecentChange */
|
||||
$block0 = $block[0];
|
||||
$last = $block[count( $block ) - 1];
|
||||
if ( !$allLogs ) {
|
||||
if ( !$allLogs && $rcObj->mAttribs['rc_type'] != RC_CATEGORIZE ) {
|
||||
if ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
|
||||
$links['total-changes'] = $nchanges[$n];
|
||||
} elseif ( $isnew ) {
|
||||
|
|
@ -529,7 +531,7 @@ class EnhancedChangesList extends ChangesList {
|
|||
}
|
||||
|
||||
# History
|
||||
if ( $allLogs ) {
|
||||
if ( $allLogs || $rcObj->mAttribs['rc_type'] == RC_CATEGORIZE ) {
|
||||
// don't show history link for logs
|
||||
} elseif ( $namehidden || !$block0->getTitle()->exists() ) {
|
||||
$links['history'] = $this->message['enhancedrc-history'];
|
||||
|
|
@ -605,15 +607,9 @@ class EnhancedChangesList extends ChangesList {
|
|||
}
|
||||
|
||||
# Diff and hist links
|
||||
if ( $type != RC_LOG ) {
|
||||
if ( $type == RC_LOG && $type != RC_CATEGORIZE ) {
|
||||
$query['action'] = 'history';
|
||||
$data['historyLink'] = ' ' . $this->msg( 'parentheses' )
|
||||
->rawParams( $rcObj->difflink . $this->message['pipe-separator'] . Linker::linkKnown(
|
||||
$rcObj->getTitle(),
|
||||
$this->message['hist'],
|
||||
array(),
|
||||
$query
|
||||
) )->escaped();
|
||||
$data['historyLink'] = $this->getDiffHistLinks( $rcObj, $query );
|
||||
}
|
||||
$data['separatorAfterLinks'] = ' <span class="mw-changeslist-separator">. .</span> ';
|
||||
|
||||
|
|
@ -628,10 +624,15 @@ class EnhancedChangesList extends ChangesList {
|
|||
|
||||
if ( $type == RC_LOG ) {
|
||||
$data['logEntry'] = $this->insertLogEntry( $rcObj );
|
||||
} elseif ( $this->isCategorizationWithoutRevision( $rcObj ) ) {
|
||||
$data['comment'] = $this->insertComment( $rcObj );
|
||||
} else {
|
||||
$data['userLink'] = $rcObj->userlink;
|
||||
$data['userTalkLink'] = $rcObj->usertalklink;
|
||||
$data['comment'] = $this->insertComment( $rcObj );
|
||||
if ( $type == RC_CATEGORIZE ) {
|
||||
$data['historyLink'] = $this->getDiffHistLinks( $rcObj, $query );
|
||||
}
|
||||
$data['rollback'] = $this->getRollback( $rcObj );
|
||||
}
|
||||
|
||||
|
|
@ -672,6 +673,33 @@ class EnhancedChangesList extends ChangesList {
|
|||
return $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns value to be used in 'historyLink' element of $data param in
|
||||
* EnhancedChangesListModifyBlockLineData hook.
|
||||
*
|
||||
* @since 1.27
|
||||
*
|
||||
* @param RCCacheEntry $rc
|
||||
* @param array $query array of key/value pairs to append as a query string
|
||||
* @return string HTML
|
||||
*/
|
||||
public function getDiffHistLinks( RCCacheEntry $rc, array $query ) {
|
||||
$pageTitle = $rc->getTitle();
|
||||
if ( $rc->getAttribute( 'rc_type' ) == RC_CATEGORIZE ) {
|
||||
// For categorizations we must swap the category title with the page title!
|
||||
$pageTitle = Title::newFromID( $rc->getAttribute( 'rc_cur_id' ) );
|
||||
}
|
||||
|
||||
$retVal = ' ' . $this->msg( 'parentheses' )
|
||||
->rawParams( $rc->difflink . $this->message['pipe-separator'] . Linker::linkKnown(
|
||||
$pageTitle,
|
||||
$this->message['hist'],
|
||||
array(),
|
||||
$query
|
||||
) )->escaped();
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* If enhanced RC is in use, this function takes the previously cached
|
||||
* RC lines, arranges them, and outputs the HTML
|
||||
|
|
|
|||
|
|
@ -87,7 +87,6 @@ class OldChangesList extends ChangesList {
|
|||
// Regular entries
|
||||
} else {
|
||||
$unpatrolled = $this->showAsUnpatrolled( $rc );
|
||||
|
||||
$this->insertDiffHist( $html, $rc, $unpatrolled );
|
||||
# M, N, b and ! (minor, new, bot and unpatrolled)
|
||||
$html .= $this->recentChangesFlags(
|
||||
|
|
@ -113,6 +112,8 @@ class OldChangesList extends ChangesList {
|
|||
|
||||
if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
|
||||
$html .= $this->insertLogEntry( $rc );
|
||||
} elseif ( $this->isCategorizationWithoutRevision( $rc ) ) {
|
||||
$html .= $this->insertComment( $rc );
|
||||
} else {
|
||||
# User tool links
|
||||
$this->insertUserRelatedLinks( $html, $rc );
|
||||
|
|
|
|||
|
|
@ -209,6 +209,15 @@ class RCCacheEntryFactory {
|
|||
$diffLink = $diffMessage;
|
||||
} elseif ( in_array( $cacheEntry->mAttribs['rc_type'], $logTypes ) ) {
|
||||
$diffLink = $diffMessage;
|
||||
} elseif ( $cacheEntry->getAttribute( 'rc_type' ) == RC_CATEGORIZE ) {
|
||||
$rcCurId = $cacheEntry->getAttribute( 'rc_cur_id' );
|
||||
$pageTitle = Title::newFromID( $rcCurId );
|
||||
if ( $pageTitle === null ) {
|
||||
wfDebugLog( 'RCCacheEntryFactory', 'Could not get Title for rc_cur_id: ' . $rcCurId );
|
||||
return $diffMessage;
|
||||
}
|
||||
$diffUrl = htmlspecialchars( $pageTitle->getLinkURL( $queryParams ) );
|
||||
$diffLink = "<a href=\"$diffUrl\" tabindex=\"$counter\">$diffMessage</a>";
|
||||
} else {
|
||||
$diffUrl = htmlspecialchars( $cacheEntry->getTitle()->getLinkURL( $queryParams ) );
|
||||
$diffLink = "<a href=\"$diffUrl\" tabindex=\"$counter\">$diffMessage</a>";
|
||||
|
|
|
|||
|
|
@ -324,15 +324,21 @@ class RecentChange {
|
|||
$editor = $this->getPerformer();
|
||||
$title = $this->getTitle();
|
||||
|
||||
if ( Hooks::run( 'AbortEmailNotification', array( $editor, $title, $this ) ) ) {
|
||||
# @todo FIXME: This would be better as an extension hook
|
||||
$enotif = new EmailNotification();
|
||||
$enotif->notifyOnPageChange( $editor, $title,
|
||||
$this->mAttribs['rc_timestamp'],
|
||||
$this->mAttribs['rc_comment'],
|
||||
$this->mAttribs['rc_minor'],
|
||||
$this->mAttribs['rc_last_oldid'],
|
||||
$this->mExtra['pageStatus'] );
|
||||
// Never send an RC notification email about categorization changes
|
||||
if ( $this->mAttribs['rc_type'] != RC_CATEGORIZE ) {
|
||||
if ( Hooks::run( 'AbortEmailNotification', array( $editor, $title, $this ) ) ) {
|
||||
# @todo FIXME: This would be better as an extension hook
|
||||
$enotif = new EmailNotification();
|
||||
$enotif->notifyOnPageChange(
|
||||
$editor,
|
||||
$title,
|
||||
$this->mAttribs['rc_timestamp'],
|
||||
$this->mAttribs['rc_comment'],
|
||||
$this->mAttribs['rc_minor'],
|
||||
$this->mAttribs['rc_last_oldid'],
|
||||
$this->mExtra['pageStatus']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
|
|||
/** @var bool Whether to queue jobs for recursive updates */
|
||||
public $mRecursive;
|
||||
|
||||
/** @var bool Whether this job was triggered by a recursive update job */
|
||||
private $mTriggeredRecursive;
|
||||
|
||||
/** @var Revision Revision for which this update has been triggered */
|
||||
private $mRevision;
|
||||
|
||||
/**
|
||||
* @var null|array Added links if calculated.
|
||||
*/
|
||||
|
|
@ -147,6 +153,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
|
|||
}
|
||||
|
||||
protected function doIncrementalUpdate() {
|
||||
global $wgRCWatchCategoryMembership;
|
||||
|
||||
# Page links
|
||||
$existing = $this->getExistingLinks();
|
||||
|
|
@ -199,6 +206,14 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
|
|||
$this->invalidateCategories( $categoryUpdates );
|
||||
$this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
|
||||
|
||||
# Category membership changes
|
||||
if (
|
||||
$wgRCWatchCategoryMembership &&
|
||||
!$this->mTriggeredRecursive && ( $categoryInserts || $categoryDeletes )
|
||||
) {
|
||||
$this->triggerCategoryChanges( $categoryInserts, $categoryDeletes );
|
||||
}
|
||||
|
||||
# Page properties
|
||||
$existing = $this->getExistingProperties();
|
||||
|
||||
|
|
@ -222,6 +237,24 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
|
|||
|
||||
}
|
||||
|
||||
private function triggerCategoryChanges( $categoryInserts, $categoryDeletes ) {
|
||||
$catMembChange = new CategoryMembershipChange( $this->mTitle, $this->mRevision );
|
||||
|
||||
if ( $this->mRecursive ) {
|
||||
$catMembChange->checkTemplateLinks();
|
||||
}
|
||||
|
||||
foreach ( $categoryInserts as $categoryName => $value ) {
|
||||
$categoryTitle = Title::newFromText( $categoryName, NS_CATEGORY );
|
||||
$catMembChange->triggerCategoryAddedNotification( $categoryTitle );
|
||||
}
|
||||
|
||||
foreach ( $categoryDeletes as $categoryName => $value ) {
|
||||
$categoryTitle = Title::newFromText( $categoryName, NS_CATEGORY );
|
||||
$catMembChange->triggerCategoryRemovedNotification( $categoryTitle );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue recursive jobs for this page
|
||||
*
|
||||
|
|
@ -863,6 +896,26 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
|
|||
return $this->mImages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this object as being triggered by a recursive LinksUpdate
|
||||
*
|
||||
* @since 1.27
|
||||
*/
|
||||
public function setTriggeredRecursive() {
|
||||
$this->mTriggeredRecursive = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the revision corresponding to this LinksUpdate
|
||||
*
|
||||
* @since 1.27
|
||||
*
|
||||
* @param Revision $revision
|
||||
*/
|
||||
public function setRevision( Revision $revision ) {
|
||||
$this->mRevision = $revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate any necessary link lists related to page property changes
|
||||
* @param array $changed
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ class RefreshLinksJob extends Job {
|
|||
} else {
|
||||
$extraParams['masterPos'] = false;
|
||||
}
|
||||
$extraParams['triggeredRecursive'] = true;
|
||||
// Convert this into no more than $wgUpdateRowsPerJob RefreshLinks per-title
|
||||
// jobs and possibly a recursive RefreshLinks job for the rest of the backlinks
|
||||
$jobs = BacklinkJobUtils::partitionBacklinkJob(
|
||||
|
|
@ -197,6 +198,12 @@ class RefreshLinksJob extends Job {
|
|||
}
|
||||
|
||||
$updates = $content->getSecondaryDataUpdates( $title, null, false, $parserOutput );
|
||||
foreach ( $updates as $key => $update ) {
|
||||
if ( $update instanceof LinksUpdate && isset( $this->params['triggeredRecursive'] ) ) {
|
||||
$update->setTriggeredRecursive();
|
||||
}
|
||||
}
|
||||
|
||||
DataUpdate::runUpdates( $updates );
|
||||
|
||||
InfoAction::invalidateCache( $title );
|
||||
|
|
|
|||
|
|
@ -2182,6 +2182,9 @@ class WikiPage implements Page, IDBAccessObject {
|
|||
$updates = $content->getSecondaryDataUpdates(
|
||||
$this->getTitle(), null, $recursive, $editInfo->output );
|
||||
foreach ( $updates as $update ) {
|
||||
if ( $update instanceof LinksUpdate ) {
|
||||
$update->setRevision( $revision );
|
||||
}
|
||||
DeferredUpdates::addUpdate( $update );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ abstract class ChangesListSpecialPage extends SpecialPage {
|
|||
* @return FormOptions
|
||||
*/
|
||||
public function getDefaultOptions() {
|
||||
$config = $this->getConfig();
|
||||
$opts = new FormOptions();
|
||||
|
||||
$opts->add( 'hideminor', false );
|
||||
|
|
@ -145,6 +146,10 @@ abstract class ChangesListSpecialPage extends SpecialPage {
|
|||
$opts->add( 'hidepatrolled', false );
|
||||
$opts->add( 'hidemyself', false );
|
||||
|
||||
if ( $config->get( 'RCWatchCategoryMembership' ) ) {
|
||||
$opts->add( 'hidecategorization', false );
|
||||
}
|
||||
|
||||
$opts->add( 'namespace', '', FormOptions::INTNULL );
|
||||
$opts->add( 'invert', false );
|
||||
$opts->add( 'associated', false );
|
||||
|
|
@ -249,6 +254,9 @@ abstract class ChangesListSpecialPage extends SpecialPage {
|
|||
$conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() );
|
||||
}
|
||||
}
|
||||
if ( $opts['hidecategorization'] === true ) {
|
||||
$conds[] = 'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE );
|
||||
}
|
||||
|
||||
// Namespace filtering
|
||||
if ( $opts['namespace'] !== '' ) {
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
|
|||
public function getDefaultOptions() {
|
||||
$opts = parent::getDefaultOptions();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
|
||||
$opts->add( 'days', $user->getIntOption( 'rcdays' ) );
|
||||
$opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
|
||||
|
|
@ -84,6 +85,10 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
|
|||
$opts->add( 'hidepatrolled', $user->getBoolOption( 'hidepatrolled' ) );
|
||||
$opts->add( 'hidemyself', false );
|
||||
|
||||
if ( $config->get( 'RCWatchCategoryMembership' ) ) {
|
||||
$opts->add( 'hidecategorization', $user->getBoolOption( 'hidecategorization' ) );
|
||||
}
|
||||
|
||||
$opts->add( 'categories', '' );
|
||||
$opts->add( 'categories_any', false );
|
||||
$opts->add( 'tagfilter', '' );
|
||||
|
|
@ -138,6 +143,9 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
|
|||
if ( 'hidemyself' === $bit ) {
|
||||
$opts['hidemyself'] = true;
|
||||
}
|
||||
if ( 'hidecategorization' === $bit ) {
|
||||
$opts['hidecategorization'] = true;
|
||||
}
|
||||
|
||||
if ( is_numeric( $bit ) ) {
|
||||
$opts['limit'] = $bit;
|
||||
|
|
@ -677,6 +685,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
|
|||
|
||||
$lang = $this->getLanguage();
|
||||
$user = $this->getUser();
|
||||
$config = $this->getConfig();
|
||||
if ( $options['from'] ) {
|
||||
$note .= $this->msg( 'rcnotefrom' )
|
||||
->numParams( $options['limit'] )
|
||||
|
|
@ -690,12 +699,12 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
|
|||
}
|
||||
|
||||
# Sort data for display and make sure it's unique after we've added user data.
|
||||
$linkLimits = $this->getConfig()->get( 'RCLinkLimits' );
|
||||
$linkLimits = $config->get( 'RCLinkLimits' );
|
||||
$linkLimits[] = $options['limit'];
|
||||
sort( $linkLimits );
|
||||
$linkLimits = array_unique( $linkLimits );
|
||||
|
||||
$linkDays = $this->getConfig()->get( 'RCLinkDays' );
|
||||
$linkDays = $config->get( 'RCLinkDays' );
|
||||
$linkDays[] = $options['days'];
|
||||
sort( $linkDays );
|
||||
$linkDays = array_unique( $linkDays );
|
||||
|
|
@ -726,6 +735,10 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
|
|||
'hidemyself' => 'rcshowhidemine'
|
||||
);
|
||||
|
||||
if ( $config->get( 'RCWatchCategoryMembership' ) ) {
|
||||
$filters['hidecategorization'] = 'rcshowhidecategorization';
|
||||
}
|
||||
|
||||
$showhide = array( 'show', 'hide' );
|
||||
|
||||
foreach ( $this->getCustomFilters() as $key => $params ) {
|
||||
|
|
@ -741,7 +754,8 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
|
|||
// The following messages are used here:
|
||||
// rcshowhideminor-show, rcshowhideminor-hide, rcshowhidebots-show, rcshowhidebots-hide,
|
||||
// rcshowhideanons-show, rcshowhideanons-hide, rcshowhideliu-show, rcshowhideliu-hide,
|
||||
// rcshowhidepatr-show, rcshowhidepatr-hide, rcshowhidemine-show, rcshowhidemine-hide.
|
||||
// rcshowhidepatr-show, rcshowhidepatr-hide, rcshowhidemine-show, rcshowhidemine-hide,
|
||||
// rcshowhidecategorization-show, rcshowhidecategorization-hide.
|
||||
$linkMessage = $this->msg( $msg . '-' . $showhide[1 - $options[$key]] );
|
||||
// Extensions can define additional filters, but don't need to define the corresponding
|
||||
// messages. If they don't exist, just fall back to 'show' and 'hide'.
|
||||
|
|
|
|||
|
|
@ -111,6 +111,10 @@ class SpecialWatchlist extends ChangesListSpecialPage {
|
|||
$opts->add( 'hidepatrolled', $user->getBoolOption( 'watchlisthidepatrolled' ) );
|
||||
$opts->add( 'hidemyself', $user->getBoolOption( 'watchlisthideown' ) );
|
||||
|
||||
if ( $this->getConfig()->get( 'RCWatchCategoryMembership' ) ) {
|
||||
$opts->add( 'hidecategorization', $user->getBoolOption( 'watchlisthidecategorization' ) );
|
||||
}
|
||||
|
||||
$opts->add( 'extended', $user->getBoolOption( 'extendwatchlist' ) );
|
||||
|
||||
return $opts;
|
||||
|
|
@ -425,6 +429,11 @@ class SpecialWatchlist extends ChangesListSpecialPage {
|
|||
'hidemyself' => 'rcshowhidemine',
|
||||
'hidepatrolled' => 'rcshowhidepatr'
|
||||
);
|
||||
|
||||
if ( $this->getConfig()->get( 'RCWatchCategoryMembership' ) ) {
|
||||
$filters['hidecategorization'] = 'rcshowhidecategorization';
|
||||
}
|
||||
|
||||
foreach ( $this->getCustomFilters() as $key => $params ) {
|
||||
$filters[$key] = $params['msg'];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
"tog-hideminor": "Hide minor edits from recent changes",
|
||||
"tog-hidepatrolled": "Hide patrolled edits from recent changes",
|
||||
"tog-newpageshidepatrolled": "Hide patrolled pages from new page list",
|
||||
"tog-hidecategorization": "Hide categorization of pages",
|
||||
"tog-extendwatchlist": "Expand watchlist to show all changes, not just the most recent",
|
||||
"tog-usenewrc": "Group changes by page in recent changes and watchlist",
|
||||
"tog-numberheadings": "Auto-number headings",
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
"tog-watchlisthideliu": "Hide edits by logged in users from the watchlist",
|
||||
"tog-watchlisthideanons": "Hide edits by anonymous users from the watchlist",
|
||||
"tog-watchlisthidepatrolled": "Hide patrolled edits from the watchlist",
|
||||
"tog-watchlisthidecategorization": "Hide categorization of pages",
|
||||
"tog-ccmeonemails": "Send me copies of emails I send to other users",
|
||||
"tog-diffonly": "Do not show page content below diffs",
|
||||
"tog-showhiddencats": "Show hidden categories",
|
||||
|
|
@ -1263,6 +1265,9 @@
|
|||
"rcshowhidemine": "$1 my edits",
|
||||
"rcshowhidemine-show": "Show",
|
||||
"rcshowhidemine-hide": "Hide",
|
||||
"rcshowhidecategorization": "$1 page categorization",
|
||||
"rcshowhidecategorization-show": "Show",
|
||||
"rcshowhidecategorization-hide": "Hide",
|
||||
"rclinks": "Show last $1 changes in last $2 days<br />$3",
|
||||
"diff": "diff",
|
||||
"hist": "hist",
|
||||
|
|
@ -2613,6 +2618,7 @@
|
|||
"spam_blanking": "All revisions contained links to $1, blanking",
|
||||
"spam_deleting": "All revisions contained links to $1, deleting",
|
||||
"simpleantispam-label": "Anti-spam check.\nDo <strong>not</strong> fill this in!",
|
||||
"autochange-username": "MediaWiki automatic change",
|
||||
"pageinfo-header": "-",
|
||||
"pageinfo-title": "Information for \"$1\"",
|
||||
"pageinfo-not-current": "Sorry, it's impossible to provide this information for old revisions.",
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@
|
|||
"tog-hideminor": "[[Special:Preferences]], tab 'Recent changes'. Offers user to hide minor edits in recent changes or not. {{Gender}}\n\n{{Related|Preferences-watchlistrc-toggle}}",
|
||||
"tog-hidepatrolled": "Option in Recent changes tab of [[Special:Preferences]] (if [[mw:Manual:$wgUseRCPatrol|$wgUseRCPatrol]] is enabled). {{Gender}}\n\n{{Related|Preferences-watchlistrc-toggle}}",
|
||||
"tog-newpageshidepatrolled": "Toggle in [[Special:Preferences]], section \"Recent changes\" (if [[mw:Manual:$wgUseRCPatrol|$wgUseRCPatrol]] is enabled). {{Gender}}",
|
||||
"tog-hidecategorization": "Option in \"Recent changes\" tab of [[Special:Preferences]]. Offers user to hide/show categorization of pages. Appears next to messages such as {{msg-mw|tog-hideminor}}.",
|
||||
"tog-extendwatchlist": "[[Special:Preferences]], tab 'Watchlist'. Offers user to show all applicable changes in watchlist (by default only the last change to a page on the watchlist is shown). {{Gender}}",
|
||||
"tog-usenewrc": "{{Gender}}\nUsed as label for the checkbox in [[Special:Preferences]], tab \"Recent changes\".\n\nOffers user to use alternative representation of [[Special:RecentChanges]] and watchlist.",
|
||||
"tog-numberheadings": "[[Special:Preferences]], tab 'Misc'. Offers numbered headings on content pages to user. {{Gender}}",
|
||||
|
|
@ -209,6 +210,7 @@
|
|||
"tog-watchlisthideliu": "Option in tab 'Watchlist' of [[Special:Preferences]]. {{Gender}}\n\n{{Related|Preferences-watchlistrc-toggle}}",
|
||||
"tog-watchlisthideanons": "Option in tab 'Watchlist' of [[Special:Preferences]]. {{Gender}}\n\n{{Related|Preferences-watchlistrc-toggle}}",
|
||||
"tog-watchlisthidepatrolled": "Option in Watchlist tab of [[Special:Preferences]]. {{Gender}}\n\n{{Related|Preferences-watchlistrc-toggle}}",
|
||||
"tog-watchlisthidecategorization": "Option in Watchlist tab of [[Special:Preferences]]. Offers user to hide/show categorization of pages. Appears next to checkboxes with labels such as {{msg-mw|tog-watchlisthideminor}}.",
|
||||
"tog-ccmeonemails": "Option in [[Special:Preferences]] > {{int:prefs-personal}} > {{int:email}}. {{Gender}}",
|
||||
"tog-diffonly": "Toggle option used in [[Special:Preferences]]. {{Gender}}",
|
||||
"tog-showhiddencats": "Toggle option used in [[Special:Preferences]]. {{Gender}}",
|
||||
|
|
@ -1436,6 +1438,9 @@
|
|||
"rcshowhidemine": "Option text in [[Special:RecentChanges]]. Parameters:\n* $1 - the \"show/hide\" command, with the text taken from either {{msg-mw|rcshowhidemine-show}} or {{msg-mw|rcshowhidemine-hide}}",
|
||||
"rcshowhidemine-show": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidemine}}.\n\nSee also:\n* {{msg-mw|rcshowhidemine-hide}}\n{{Identical|show}}",
|
||||
"rcshowhidemine-hide": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidemine}}.\n\nSee also:\n* {{msg-mw|rcshowhidemine-show}}\n{{Identical|hide}}",
|
||||
"rcshowhidecategorization": "Option text in [[Special:RecentChanges]]. Parameters:\n* $1 - the \"show/hide\" command, with the text taken from either {{msg-mw|rcshowhidecategorization-show}} or {{msg-mw|rcshowhidecategorization-hide}}",
|
||||
"rcshowhidecategorization-show": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidecategorization}}.\n\nSee also:\n* {{msg-mw|rcshowhidecategorization-hide}}\n{{Identical|show}}",
|
||||
"rcshowhidecategorization-hide": "{{doc-actionlink}}\nOption text in [[Special:RecentChanges]] in conjunction with {{msg-mw|rcshowhidecategorization}}.\n\nSee also:\n* {{msg-mw|rcshowhidecategorization-show}}\n{{Identical|hide}}",
|
||||
"rclinks": "Used on [[Special:RecentChanges]].\n* $1 - a list of different choices with number of pages to be shown.<br /> Example: \"''50{{int:pipe-separator}}100{{int:pipe-separator}}250{{int:pipe-separator}}500\".\n* $2 - a list of clickable links with a number of days for which recent changes are to be displayed.<br /> Example: \"''1{{int:pipe-separator}}3{{int:pipe-separator}}7{{int:pipe-separator}}14{{int:pipe-separator}}30''\".\n* $3 - a block of text that consists of other messages.<br /> Example: \"''Hide minor edits{{int:pipe-separator}}Show bots{{int:pipe-separator}}Hide anonymous users{{int:pipe-separator}}Hide logged-in users{{int:pipe-separator}}Hide patrolled edits{{int:pipe-separator}}Hide my edits''\"\nList elements are separated by {{msg-mw|Pipe-separator}} each. Each list element is, or contains, a link.",
|
||||
"diff": "Short form of \"differences\". Used on [[Special:RecentChanges]], [[Special:Watchlist]], ...\n{{Identical|Diff}}",
|
||||
"hist": "Short form of \"history\". Used on [[Special:RecentChanges]], [[Special:Watchlist]], ...",
|
||||
|
|
@ -2786,6 +2791,7 @@
|
|||
"spam_blanking": "Edit summary for spam cleanup script.\n\nUsed when a page is blanked (made to have no content, but still exist) because the script could not find an appropriate revision to set the page to.\n\nParameters:\n* $1 - a spammed domain name",
|
||||
"spam_deleting": "Edit summary for spam cleanup script.\n\nUsed when a page is deleted because all revisions contained a particular link.\n\nParameters:\n* $1 - a spammed domain name",
|
||||
"simpleantispam-label": "Used as label for the input box in \"Edit\" page.\n\nThe label and the input box are always hidden.",
|
||||
"autochange-username": "Used as bot / unknown username.",
|
||||
"pageinfo-header": "{{ignored}}Custom text for the top of the info page (action=info).",
|
||||
"pageinfo-title": "Page title for action=info. Parameters:\n* $1 is the page name",
|
||||
"pageinfo-not-current": "Error message displayed when information for an old revision is requested. Example: [{{fullurl:Project:News|oldid=4266597&action=info}}]",
|
||||
|
|
|
|||
|
|
@ -74,6 +74,20 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase {
|
|||
$this->assertEquals( '', $html );
|
||||
}
|
||||
|
||||
public function testCategorizationLineFormatting() {
|
||||
$html = $this->createCategorizationLine(
|
||||
$this->getCategorizationChange( '20150629191735', 0, 0 )
|
||||
);
|
||||
$this->assertNotContains( '(diff | hist)', strip_tags( $html ) );
|
||||
}
|
||||
|
||||
public function testCategorizationLineFormattingWithRevision() {
|
||||
$html = $this->createCategorizationLine(
|
||||
$this->getCategorizationChange( '20150629191735', 1025, 1024 )
|
||||
);
|
||||
$this->assertContains( '(diff | hist)', strip_tags( $html ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo more tests for actual formatting, this is more of a smoke test
|
||||
*/
|
||||
|
|
@ -115,6 +129,24 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase {
|
|||
return $recentChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecentChange
|
||||
*/
|
||||
private function getCategorizationChange( $timestamp, $thisId, $lastId ) {
|
||||
$wikiPage = new WikiPage( Title::newFromText( 'Testpage' ) );
|
||||
$wikiPage->doEditContent( new WikitextContent( 'Some random text' ), 'page created' );
|
||||
|
||||
$wikiPage = new WikiPage( Title::newFromText( 'Category:Foo' ) );
|
||||
$wikiPage->doEditContent( new WikitextContent( 'Some random text' ), 'category page created' );
|
||||
|
||||
$user = $this->getTestUser();
|
||||
$recentChange = $this->testRecentChangesHelper->makeCategorizationRecentChange(
|
||||
$user, 'Category:Foo', $wikiPage->getId(), $thisId, $lastId, $timestamp
|
||||
);
|
||||
|
||||
return $recentChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
|
|
@ -128,4 +160,15 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase {
|
|||
return $user;
|
||||
}
|
||||
|
||||
private function createCategorizationLine( $recentChange ) {
|
||||
$enhancedChangesList = $this->newEnhancedChangesList();
|
||||
$cacheEntry = $this->testRecentChangesHelper->getCacheEntry( $recentChange );
|
||||
|
||||
$reflection = new \ReflectionClass( get_class( $enhancedChangesList ) );
|
||||
$method = $reflection->getMethod( 'recentChangesBlockLine' );
|
||||
$method->setAccessible( true );
|
||||
|
||||
return $method->invokeArgs( $enhancedChangesList, array( $cacheEntry ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,36 @@ class TestRecentChangesHelper {
|
|||
return $change;
|
||||
}
|
||||
|
||||
public function getCacheEntry( $recentChange ) {
|
||||
$rcCacheFactory = new RCCacheEntryFactory(
|
||||
new RequestContext(),
|
||||
array( 'diff' => 'diff', 'cur' => 'cur', 'last' => 'last' )
|
||||
);
|
||||
return $rcCacheFactory->newFromRecentChange( $recentChange, false );
|
||||
}
|
||||
|
||||
public function makeCategorizationRecentChange(
|
||||
User $user, $titleText, $curid, $thisid, $lastid, $timestamp
|
||||
) {
|
||||
|
||||
$attribs = array_merge(
|
||||
$this->getDefaultAttributes( $titleText, $timestamp ),
|
||||
array(
|
||||
'rc_type' => RC_CATEGORIZE,
|
||||
'rc_user' => $user->getId(),
|
||||
'rc_user_text' => $user->getName(),
|
||||
'rc_this_oldid' => $thisid,
|
||||
'rc_last_oldid' => $lastid,
|
||||
'rc_cur_id' => $curid,
|
||||
'rc_comment' => '[[:Testpage]] added to category',
|
||||
'rc_old_len' => 0,
|
||||
'rc_new_len' => 0,
|
||||
)
|
||||
);
|
||||
|
||||
return $this->makeRecentChange( $attribs, 0, 0 );
|
||||
}
|
||||
|
||||
private function getDefaultAttributes( $titleText, $timestamp ) {
|
||||
return array(
|
||||
'rc_id' => 545,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ class LinksUpdateTest extends MediaWikiTestCase {
|
|||
'externallinks',
|
||||
'imagelinks',
|
||||
'templatelinks',
|
||||
'iwlinks'
|
||||
'iwlinks',
|
||||
'recentchanges',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
@ -39,6 +40,13 @@ class LinksUpdateTest extends MediaWikiTestCase {
|
|||
'iw_wikiid' => 'linksupdatetest',
|
||||
)
|
||||
);
|
||||
$this->setMwGlobals( 'wgRCWatchCategoryMembership', true );
|
||||
}
|
||||
|
||||
public function addDBData() {
|
||||
$this->insertPage( 'Testing' );
|
||||
$this->insertPage( 'Some_other_page' );
|
||||
$this->insertPage( 'Template:TestingTemplate' );
|
||||
}
|
||||
|
||||
protected function makeTitleAndParserOutput( $name, $id ) {
|
||||
|
|
@ -133,6 +141,61 @@ class LinksUpdateTest extends MediaWikiTestCase {
|
|||
) );
|
||||
}
|
||||
|
||||
public function testOnAddingAndRemovingCategory_recentChangesRowIsAdded() {
|
||||
$this->setMwGlobals( 'wgCategoryCollation', 'uppercase' );
|
||||
|
||||
$title = Title::newFromText( 'Testing' );
|
||||
$wikiPage = new WikiPage( $title );
|
||||
$wikiPage->doEditContent( new WikitextContent( '[[Category:Foo]]' ), 'added category' );
|
||||
|
||||
$this->assertRecentChangeByCategorization(
|
||||
$title,
|
||||
$wikiPage->getParserOutput( new ParserOptions() ),
|
||||
Title::newFromText( 'Category:Foo' ),
|
||||
array( array( 'Foo', '[[:Testing]] added to category' ) )
|
||||
);
|
||||
|
||||
$wikiPage->doEditContent( new WikitextContent( '[[Category:Bar]]' ), 'added category' );
|
||||
$this->assertRecentChangeByCategorization(
|
||||
$title,
|
||||
$wikiPage->getParserOutput( new ParserOptions() ),
|
||||
Title::newFromText( 'Category:Foo' ),
|
||||
array(
|
||||
array( 'Foo', '[[:Testing]] added to category' ),
|
||||
array( 'Foo', '[[:Testing]] removed from category' ),
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertRecentChangeByCategorization(
|
||||
$title,
|
||||
$wikiPage->getParserOutput( new ParserOptions() ),
|
||||
Title::newFromText( 'Category:Bar' ),
|
||||
array(
|
||||
array( 'Bar', '[[:Testing]] added to category' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testOnAddingAndRemovingCategoryToTemplates_embeddingPagesAreIgnored() {
|
||||
$this->setMwGlobals( 'wgCategoryCollation', 'uppercase' );
|
||||
|
||||
$templateTitle = Title::newFromText( 'Template:TestingTemplate' );
|
||||
$templatePage = new WikiPage( $templateTitle );
|
||||
|
||||
$wikiPage = new WikiPage( Title::newFromText( 'Testing' ) );
|
||||
$wikiPage->doEditContent( new WikitextContent( '{{TestingTemplate}}' ), 'added template' );
|
||||
$otherWikiPage = new WikiPage( Title::newFromText( 'Some_other_page' ) );
|
||||
$otherWikiPage->doEditContent( new WikitextContent( '{{TestingTemplate}}' ), 'added template' );
|
||||
$templatePage->doEditContent( new WikitextContent( '[[Category:Foo]]' ), 'added category' );
|
||||
|
||||
$this->assertRecentChangeByCategorization(
|
||||
$templateTitle,
|
||||
$templatePage->getParserOutput( new ParserOptions() ),
|
||||
Title::newFromText( 'Foo' ),
|
||||
array( array( 'Foo', '[[:Template:TestingTemplate]] and 2 pages added to category' ) )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ParserOutput::addInterwikiLink
|
||||
*/
|
||||
|
|
@ -263,4 +326,26 @@ class LinksUpdateTest extends MediaWikiTestCase {
|
|||
$this->assertSelect( $table, $fields, $condition, $expectedRows );
|
||||
return $update;
|
||||
}
|
||||
|
||||
protected function assertRecentChangeByCategorization(
|
||||
Title $pageTitle, ParserOutput $parserOutput, Title $categoryTitle, $expectedRows
|
||||
) {
|
||||
$update = new LinksUpdate( $pageTitle, $parserOutput );
|
||||
$revision = Revision::newFromTitle( $pageTitle );
|
||||
$update->setRevision( $revision );
|
||||
$update->beginTransaction();
|
||||
$update->doUpdate();
|
||||
$update->commitTransaction();
|
||||
|
||||
$this->assertSelect(
|
||||
'recentchanges',
|
||||
'rc_title, rc_comment',
|
||||
array(
|
||||
'rc_type' => RC_CATEGORIZE,
|
||||
'rc_namespace' => NS_CATEGORY,
|
||||
'rc_title' => $categoryTitle->getDBkey()
|
||||
),
|
||||
$expectedRows
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@
|
|||
*/
|
||||
class SpecialRecentchangesTest extends MediaWikiTestCase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->setMwGlobals( 'wgRCWatchCategoryMembership', true );
|
||||
}
|
||||
|
||||
/**
|
||||
* @var SpecialRecentChanges
|
||||
*/
|
||||
|
|
@ -50,7 +55,8 @@ class SpecialRecentchangesTest extends MediaWikiTestCase {
|
|||
$this->assertConditions(
|
||||
array( # expected
|
||||
'rc_bot' => 0,
|
||||
0 => "rc_namespace = '0'",
|
||||
0 => "rc_type != '6'",
|
||||
1 => "rc_namespace = '0'",
|
||||
),
|
||||
array(
|
||||
'namespace' => NS_MAIN,
|
||||
|
|
@ -63,7 +69,8 @@ class SpecialRecentchangesTest extends MediaWikiTestCase {
|
|||
$this->assertConditions(
|
||||
array( # expected
|
||||
'rc_bot' => 0,
|
||||
0 => sprintf( "rc_namespace != '%s'", NS_MAIN ),
|
||||
0 => "rc_type != '6'",
|
||||
1 => sprintf( "rc_namespace != '%s'", NS_MAIN ),
|
||||
),
|
||||
array(
|
||||
'namespace' => NS_MAIN,
|
||||
|
|
@ -81,7 +88,8 @@ class SpecialRecentchangesTest extends MediaWikiTestCase {
|
|||
$this->assertConditions(
|
||||
array( # expected
|
||||
'rc_bot' => 0,
|
||||
0 => sprintf( "(rc_namespace = '%s' OR rc_namespace = '%s')", $ns1, $ns2 ),
|
||||
0 => "rc_type != '6'",
|
||||
1 => sprintf( "(rc_namespace = '%s' OR rc_namespace = '%s')", $ns1, $ns2 ),
|
||||
),
|
||||
array(
|
||||
'namespace' => $ns1,
|
||||
|
|
@ -99,7 +107,8 @@ class SpecialRecentchangesTest extends MediaWikiTestCase {
|
|||
$this->assertConditions(
|
||||
array( # expected
|
||||
'rc_bot' => 0,
|
||||
0 => sprintf( "(rc_namespace != '%s' AND rc_namespace != '%s')", $ns1, $ns2 ),
|
||||
0 => "rc_type != '6'",
|
||||
1 => sprintf( "(rc_namespace != '%s' AND rc_namespace != '%s')", $ns1, $ns2 ),
|
||||
),
|
||||
array(
|
||||
'namespace' => $ns1,
|
||||
|
|
|
|||
Loading…
Reference in a new issue