$wgRCWatchCategoryMembership = false;
/**
- * Use RC Patrolling to check for vandalism
+ * Use RC Patrolling to check for vandalism (from recent changes and watchlists)
+ * New pages and new files are included.
*/
$wgUseRCPatrol = true;
*/
$wgUseNPPatrol = true;
+/**
+ * Use file patrolling to check new files on Special:Newfiles
+ *
+ * @since 1.27
+ */
+$wgUseFilePatrol = true;
+
/**
* Log autopatrol actions to the log table
*/
if ( $rc instanceof RecentChange ) {
$isPatrolled = $rc->mAttribs['rc_patrolled'];
$rcType = $rc->mAttribs['rc_type'];
+ $rcLogType = $rc->mAttribs['rc_log_type'];
} else {
$isPatrolled = $rc->rc_patrolled;
$rcType = $rc->rc_type;
+ $rcLogType = $rc->rc_log_type;
}
if ( !$isPatrolled ) {
if ( $user->useNPPatrol() && $rcType == RC_NEW ) {
return true;
}
+ if ( $user->useFilePatrol() && $rcLogType == 'upload' ) {
+ return true;
+ }
}
return false;
* @return array Array of permissions errors, see Title::getUserPermissionsErrors()
*/
public function doMarkPatrolled( User $user, $auto = false ) {
- global $wgUseRCPatrol, $wgUseNPPatrol;
+ global $wgUseRCPatrol, $wgUseNPPatrol, $wgUseFilePatrol;
$errors = array();
- // If recentchanges patrol is disabled, only new pages
- // can be patrolled
- if ( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute( 'rc_type' ) != RC_NEW ) ) {
+ // If recentchanges patrol is disabled, only new pages or new file versions
+ // can be patrolled, provided the appropriate config variable is set
+ if ( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute( 'rc_type' ) != RC_NEW ) &&
+ ( !$wgUseFilePatrol || !( $this->getAttribute( 'rc_type' ) == RC_LOG &&
+ $this->getAttribute( 'rc_log_type' ) == 'upload' ) ) ) {
$errors[] = array( 'rcpatroldisabled' );
}
// Automatic patrol needs "autopatrol", ordinary patrol needs "patrol"
}
$descTitle = $this->getTitle();
+ $descId = $descTitle->getArticleID();
$wikiPage = new WikiFilePage( $descTitle );
$wikiPage->setFile( $this );
$nullRevision = Revision::newNullRevision(
$dbw,
- $descTitle->getArticleID(),
+ $descId,
$editSummary,
false,
$user
array( $wikiPage, $nullRevision, $nullRevision->getParentId(), $user )
);
$wikiPage->updateRevisionOn( $dbw, $nullRevision );
+ // Associate null revision id
+ $logEntry->setAssociatedRevId( $nullRevision->getId() );
}
$newPageContent = null;
# b) They won't cause rollback of the log publish/update above
$that = $this;
$dbw->onTransactionIdle( function () use (
- $that, $reupload, $wikiPage, $newPageContent, $comment, $user, $logEntry, $logId
+ $that, $reupload, $wikiPage, $newPageContent, $comment, $user, $logEntry, $logId, $descId
) {
# Update memcache after the commit
$that->invalidateCache();
$user
);
+ if ( isset( $status->value['revision'] ) ) {
+ // Associate new page revision id
+ $logEntry->setAssociatedRevId( $status->value['revision']->getId() );
+ }
// This relies on the resetArticleID() call in WikiPage::insertOn(),
// which is triggered on $descTitle by doEditContent() above.
if ( isset( $status->value['revision'] ) ) {
# Existing file page: invalidate description page cache
$wikiPage->getTitle()->invalidateCache();
$wikiPage->getTitle()->purgeSquid();
+ # Allow the new file version to be patrolled from the page footer
+ Article::purgePatrolFooterCache( $descId );
}
# Now that the page exists, make an RC entry.
* @return bool
*/
public function showPatrolFooter() {
- global $wgUseNPPatrol, $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI;
+ global $wgUseNPPatrol, $wgUseRCPatrol, $wgUseFilePatrol, $wgEnableAPI, $wgEnableWriteAPI;
$outputPage = $this->getContext()->getOutput();
$user = $this->getContext()->getUser();
$rc = false;
if ( !$this->getTitle()->quickUserCan( 'patrol', $user )
- || !( $wgUseRCPatrol || $wgUseNPPatrol )
+ || !( $wgUseRCPatrol || $wgUseNPPatrol || $wgUseFilePatrol )
) {
// Patrolling is disabled or the user isn't allowed to
return false;
__METHOD__
);
+ $cantPatrolNewPage = false;
+ $cantPatrolFile = false;
+
if ( $oldestRevisionTimestamp
&& RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
) {
),
__METHOD__
);
+ if ( $rc ) {
+ // Use generic patrol message for new pages
+ $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
+ }
+ } else {
+ $cantPatrolNewPage = true;
+ }
+
+ // Allow patrolling of latest file upload
+ if ( !$rc && $wgUseFilePatrol && $this->getTitle()->getNamespace() === NS_FILE ) {
+ // Retrieve timestamp of most recent upload
+ $newestUploadTimestamp = $dbr->selectField(
+ 'image',
+ 'MAX( img_timestamp )',
+ array( 'img_name' => $this->getTitle()->getDBkey() ),
+ __METHOD__
+ );
+ if ( $newestUploadTimestamp
+ && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
+ ) {
+ // 6h tolerance because the RC might not be cleaned out regularly
+ $rc = RecentChange::newFromConds(
+ array(
+ 'rc_type' => RC_LOG,
+ 'rc_log_type' => 'upload',
+ 'rc_timestamp' => $newestUploadTimestamp,
+ 'rc_namespace' => NS_FILE,
+ 'rc_cur_id' => $this->getTitle()->getArticleID(),
+ 'rc_patrolled' => 0
+ ),
+ __METHOD__,
+ array( 'USE INDEX' => 'rc_timestamp' )
+ );
+ if ( $rc ) {
+ // Use patrol message specific to files
+ $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
+ }
+ } else {
+ $cantPatrolFile = true;
+ }
} else {
+ $cantPatrolFile = true;
+ }
+
+ if ( $cantPatrolFile && $cantPatrolNewPage ) {
// Cache the information we gathered above in case we can't patrol
// Don't cache in case we can patrol as this could change
$cache->set( $key, '1' );
$link = Linker::linkKnown(
$this->getTitle(),
- wfMessage( 'markaspatrolledtext' )->escaped(),
+ $markPatrolledMsg->escaped(),
array(),
array(
'action' => 'markpatrolled',
return true;
}
+ /**
+ * Purge the cache used to check if it is worth showing the patrol footer
+ * For example, it is done during re-uploads when file patrol is used.
+ * @param int $articleID ID of the article to purge
+ * @since 1.27
+ */
+ public static function purgePatrolFooterCache( $articleID ) {
+ $cache = ObjectCache::getMainWANInstance();
+ $cache->touchCheckKey( wfMemcKey( 'unpatrollable-page', $articleID ) );
+ }
+
/**
* Show the error text for a missing article. For articles in the MediaWiki
* namespace, show the default message text. To be called from Article::view().
*/
protected $gallery;
+ /**
+ * @var bool
+ */
+ protected $showBots;
+
+ /**
+ * @var bool
+ */
+ protected $hidePatrolled;
+
function __construct( IContextSource $context, $par = null ) {
$this->like = $context->getRequest()->getText( 'like' );
- $this->showbots = $context->getRequest()->getBool( 'showbots', 0 );
+ $this->showBots = $context->getRequest()->getBool( 'showbots', 0 );
+ $this->hidePatrolled = $context->getRequest()->getBool( 'hidepatrolled', 0 );
if ( is_numeric( $par ) ) {
$this->setLimit( $par );
}
$conds = $jconds = array();
$tables = array( 'image' );
- if ( !$this->showbots ) {
+ if ( !$this->showBots ) {
$groupsWithBotPermission = User::getGroupsWithPermission( 'bot' );
if ( count( $groupsWithBotPermission ) ) {
}
}
+ if ( $this->hidePatrolled ) {
+ $tables[] = 'recentchanges';
+ $conds['rc_type'] = RC_LOG;
+ $conds['rc_log_type'] = 'upload';
+ $conds['rc_patrolled'] = 0;
+ $jconds['recentchanges'] = array(
+ 'INNER JOIN',
+ array(
+ 'rc_title = img_name',
+ 'rc_user = img_user',
+ 'rc_timestamp = img_timestamp'
+ )
+ );
+ }
+
if ( !$this->getConfig()->get( 'MiserMode' ) && $this->like !== null ) {
$dbr = wfGetDB( DB_SLAVE );
$likeObj = Title::newFromText( $this->like );
'label-message' => 'newimages-showbots',
'name' => 'showbots',
),
+ 'hidepatrolled' => array(
+ 'type' => 'check',
+ 'label-message' => 'newimages-hidepatrolled',
+ 'name' => 'hidepatrolled',
+ ),
'limit' => array(
'type' => 'hidden',
'default' => $this->mLimit,
unset( $fields['like'] );
}
+ if ( !$this->getUser()->useFilePatrol() ) {
+ unset( $fields['hidepatrolled'] );
+ }
+
$context = new DerivativeContext( $this->getContext() );
$context->setTitle( $this->getTitle() ); // Remove subpage
$form = new HTMLForm( $fields, $context );
);
}
+ /**
+ * Check whether to enable new files patrol features for this user
+ * @return bool True or false
+ */
+ public function useFilePatrol() {
+ global $wgUseRCPatrol, $wgUseFilePatrol;
+ return (
+ ( $wgUseRCPatrol || $wgUseFilePatrol )
+ && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
+ );
+ }
+
/**
* Get the WebRequest object to use with this object
*
"markaspatrolleddiff": "Mark as patrolled",
"markaspatrolledlink": "[$1]",
"markaspatrolledtext": "Mark this page as patrolled",
+ "markaspatrolledtext-file": "Mark this file version as patrolled",
"markedaspatrolled": "Marked as patrolled",
"markedaspatrolledtext": "The selected revision of [[:$1]] has been marked as patrolled.",
"rcpatroldisabled": "Recent changes patrol disabled",
"newimages-legend": "Filter",
"newimages-label": "Filename (or a part of it):",
"newimages-showbots": "Show uploads by bots",
+ "newimages-hidepatrolled": "Hide patrolled uploads",
"noimages": "Nothing to see.",
"ilsubmit": "Search",
"bydate": "by date",
"markaspatrolleddiff": "{{doc-actionlink}}\nSee also:\n* {{msg-mw|Markaspatrolledtext}}\n{{Identical|Mark as patrolled}}",
"markaspatrolledlink": "{{notranslate}}\nParameters:\n* $1 - link which has text {{msg-mw|Markaspatrolledtext}}",
"markaspatrolledtext": "{{doc-actionlink}}\nSee also:\n* {{msg-mw|Markaspatrolleddiff}}",
+ "markaspatrolledtext-file": "Same as markaspatrolledtext, but for files (new versions included) instead of pages.",
"markedaspatrolled": "Used as title of the message {{msg-mw|Markedaspatrolledtext}}, when marking a change as patrolled.\n{{Related|Markedaspatrolled}}",
"markedaspatrolledtext": "Used when marking a change as patrolled.\n\nThe title for this message is {{msg-mw|Markedaspatrolled}}.\n\nParameters:\n* $1 - page title\n{{Related|Markedaspatrolled}}",
"rcpatroldisabled": "Used as title of the error message {{msg-mw|Rcpatroldisabledtext}}, when marking a change as patrolled.\n{{Related|Markedaspatrolled}}",
"newimages-legend": "Caption of the fieldset for the filter on [[Special:NewImages]]\n\n{{Identical|Filter}}",
"newimages-label": "Caption of the filter editbox on [[Special:NewImages]]",
"newimages-showbots": "Used as label for a checkbox. When checked, [[Special:NewImages]] will also display uploads by users in the bots group.",
+ "newimages-hidepatrolled": "Used as label for a checkbox. When checked, [[Special:NewImages]] will not display patrolled uploads.",
"noimages": "This is shown on the special page [[Special:NewImages]], when there aren't any recently uploaded files.",
"ilsubmit": "Used as label for input box in the MIMESearch form on [[Special:MIMESearch]].\n\nSee also:\n* {{msg-mw|Mimesearch|page title}}\n* {{msg-mw|Mimetype|label for input box}}\n{{Identical|Search}}",
"bydate": "{{Identical|Date}}",