Add a maintenance script to delete a change tag

Based on Ib8b96b8460db3832e9297a07922ee1d9d1af5ccb by Legoktm.

Bug: T75181
Change-Id: I0e61dfbc72fe27efa9c1deabc928e7c9af66e1bc
This commit is contained in:
Max Semenik 2019-10-14 00:36:51 -07:00
parent 5a811a1ea2
commit 38a9f0a42a
3 changed files with 86 additions and 2 deletions

View file

@ -388,6 +388,7 @@ $wgAutoloadLocalClasses = [
'DeleteOrphanedRevisions' => __DIR__ . '/maintenance/deleteOrphanedRevisions.php',
'DeletePageJob' => __DIR__ . '/includes/jobqueue/jobs/DeletePageJob.php',
'DeleteSelfExternals' => __DIR__ . '/maintenance/deleteSelfExternals.php',
'DeleteTag' => __DIR__ . '/maintenance/deleteTag.php',
'DeletedContribsPager' => __DIR__ . '/includes/specials/pagers/DeletedContribsPager.php',
'DependencyWrapper' => __DIR__ . '/includes/cache/dependency/DependencyWrapper.php',
'DeprecatedGlobal' => __DIR__ . '/includes/DeprecatedGlobal.php',

View file

@ -1301,10 +1301,11 @@ class ChangeTags {
* @param string $tag Tag that you are interested in deleting
* @param User|null $user User whose permission you wish to check, or null if
* you don't care (e.g. maintenance scripts)
* @param bool $ignoreCounts If true, no check for tag usage will be preformed
* @return Status
* @since 1.25
*/
public static function canDeleteTag( $tag, User $user = null ) {
public static function canDeleteTag( $tag, User $user = null, bool $ignoreCounts = false ) {
$tagUsage = self::tagUsageStatistics();
if ( !is_null( $user ) ) {
@ -1321,7 +1322,7 @@ class ChangeTags {
return Status::newFatal( 'tags-delete-not-found', $tag );
}
if ( isset( $tagUsage[$tag] ) && $tagUsage[$tag] > self::MAX_DELETE_USES ) {
if ( !$ignoreCounts && isset( $tagUsage[$tag] ) && $tagUsage[$tag] > self::MAX_DELETE_USES ) {
return Status::newFatal( 'tags-delete-too-many-uses', $tag, self::MAX_DELETE_USES );
}

82
maintenance/deleteTag.php Normal file
View file

@ -0,0 +1,82 @@
<?php
/**
* Remove a revision tag from edits and log entries it was applied to.
* @see bug T75181
*/
use MediaWiki\MediaWikiServices;
use MediaWiki\Storage\NameTableAccessException;
require_once __DIR__ . '/Maintenance.php';
class DeleteTag extends Maintenance {
public function __construct() {
parent::__construct();
$this->addDescription( 'Deletes a change tag' );
$this->addArg( 'tag name', 'Name of the tag to delete' );
$this->setBatchSize( 500 );
}
public function execute() {
$dbw = $this->getDB( DB_MASTER );
$services = MediaWikiServices::getInstance();
$defStore = $services->getChangeTagDefStore();
$lbFactory = $services->getDBLoadBalancerFactory();
$options = [ 'domain' => $lbFactory->getLocalDomainID() ];
$tag = $this->getArg( 0 );
try {
$tagId = $defStore->getId( $tag );
} catch ( NameTableAccessException $ex ) {
$this->fatalError( "Tag '$tag' not found" );
// To make analyzers happy
return;
}
$status = ChangeTags::canDeleteTag( $tag, null, true );
if ( !$status->isOK() ) {
$message = $status->getHTML( false, false, 'en' );
$this->fatalError( Sanitizer::stripAllTags( $message ) );
}
$this->output( "Deleting tag '$tag'...\n" );
// Make the tag imposssible to add by users while we're deleting it and drop the
// usage counter to zero
$dbw->update(
'change_tag_def',
[
'ctd_user_defined' => 0,
'ctd_count' => 0,
],
[ 'ctd_id' => $tagId ],
__METHOD__
);
ChangeTags::purgeTagCacheAll();
// Iterate over change_tag, deleting rows in batches
$count = 0;
do {
$ids = $dbw->selectFieldValues(
'change_tag',
'ct_id',
[ 'ct_tag_id' => $tagId ],
__METHOD__,
[ 'LIMIT' => $this->getBatchSize() ]
);
$dbw->delete( 'change_tag', [ 'ct_id' => $ids ], __METHOD__ );
$count += $dbw->affectedRows();
$this->output( "$count\n" );
$lbFactory->waitForReplication( $options );
} while ( $dbw->affectedRows() === $this->getBatchSize() );
$this->output( "The tag has been removed from $count revisions, deleting the tag itself...\n" );
ChangeTags::deleteTagEverywhere( $tag );
$this->output( "Done.\n" );
}
}
$maintClass = DeleteTag::class;
require_once RUN_MAINTENANCE_IF_MAIN;