Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 91
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
FlagProtectToSemiProtect
0.00% covered (danger)
0.00%
0 / 85
0.00% covered (danger)
0.00%
0 / 3
462
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
 flagToSemiProtect
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 1
272
1<?php
2/**
3 * @ingroup Maintenance
4 */
5
6use MediaWiki\MediaWikiServices;
7
8if ( getenv( 'MW_INSTALL_PATH' ) ) {
9    $IP = getenv( 'MW_INSTALL_PATH' );
10} else {
11    $IP = __DIR__ . '/../../..';
12}
13
14require_once "$IP/maintenance/Maintenance.php";
15
16class FlagProtectToSemiProtect extends Maintenance {
17
18    public function __construct() {
19        parent::__construct();
20        $this->addDescription( 'Convert flag-protected pages to semi-protection.' );
21        $this->addOption( 'user', 'The name of the admin user to use as the "protector"', true, true );
22        $this->addOption( 'reason', 'The reason for the conversion', false, true );
23        $this->setBatchSize( 500 );
24        $this->requireExtension( 'FlaggedRevs' );
25    }
26
27    /**
28     * @inheritDoc
29     */
30    public function execute() {
31        global $wgFlaggedRevsProtection;
32
33        if ( !$wgFlaggedRevsProtection ) {
34            $this->output( "\$wgFlaggedRevsProtection not enabled.\n" );
35            return;
36        }
37
38        $user = User::newFromName( $this->getOption( 'user' ) );
39        if ( !$user || !$user->isRegistered() ) {
40            $this->fatalError( "Invalid user specified!" );
41        }
42        $reason = $this->getOption( 'reason',
43            "Converting flagged protection settings to edit protection settings." );
44
45        $this->output( "Protecter username: \"" . $user->getName() . "\"\n" );
46        $this->output( "Running in 5 seconds...Press ctrl-c to abort.\n" );
47        sleep( 5 );
48
49        $this->flagToSemiProtect( $user, $reason );
50    }
51
52    /**
53     * @param User $user
54     * @param string $reason
55     */
56    private function flagToSemiProtect( User $user, $reason ) {
57        global $wgFlaggedRevsNamespaces;
58
59        $this->output( "Semi-protecting all flag-protected pages...\n" );
60        if ( !$wgFlaggedRevsNamespaces ) {
61            $this->output( "\$wgFlaggedRevsNamespaces is empty.\n" );
62            return;
63        }
64
65        $db = wfGetDB( DB_PRIMARY );
66        $start = $db->selectField( 'flaggedpage_config', 'MIN(fpc_page_id)', false, __METHOD__ );
67        $end = $db->selectField( 'flaggedpage_config', 'MAX(fpc_page_id)', false, __METHOD__ );
68        if ( $start === null || $end === null ) {
69            $this->output( "...flaggedpage_config table seems to be empty.\n" );
70            return;
71        }
72        # Do remaining chunk
73        $end += $this->mBatchSize - 1;
74        $blockStart = (int)$start;
75        $blockEnd = (int)( $start + $this->mBatchSize - 1 );
76        $count = 0;
77
78        $services = MediaWikiServices::getInstance();
79        $lbFactory = $services->getDBLoadBalancerFactory();
80        $restrictionStore = $services->getRestrictionStore();
81        $wikiPageFactory = $services->getWikiPageFactory();
82
83        while ( $blockEnd <= $end ) {
84            $this->output( "...doing fpc_page_id from $blockStart to $blockEnd\n" );
85            $res = $db->select(
86                [ 'flaggedpage_config', 'page' ],
87                [ 'fpc_page_id', 'fpc_level', 'fpc_expiry' ],
88                [ "fpc_page_id BETWEEN $blockStart AND $blockEnd",
89                    'page_namespace' => $wgFlaggedRevsNamespaces,
90                    'page_id = fpc_page_id',
91                    'fpc_level != ' . $db->addQuotes( '' ) ],
92                __METHOD__
93            );
94            # Go through and protect each page...
95            foreach ( $res as $row ) {
96                $title = Title::newFromID( $row->fpc_page_id );
97                if ( $restrictionStore->isProtected( $title, 'edit' ) ) {
98                    continue; // page already has edit protection - skip it
99                }
100                # Flagged protection settings
101                $frLimit = trim( $row->fpc_level );
102                $frExpiry = ( $row->fpc_expiry === $db->getInfinity() )
103                    ? 'infinity'
104                    : wfTimestamp( TS_MW, $row->fpc_expiry );
105                # Build the new protection settings
106                $cascade = false;
107                $limit = [];
108                $expiry = [];
109                foreach ( $restrictionStore->listApplicableRestrictionTypes( $title ) as $type ) {
110                    # Get existing restrictions for this action
111                    $oldLimit = $restrictionStore->getRestrictions( $title, $type ); // array
112                    $oldExpiry = $restrictionStore->getRestrictionExpiry( $title, $type ); // MW_TS
113                    # Move or Edit rights - take highest of (flag,type) settings
114                    if ( $type == 'edit' || $type == 'move' ) {
115                        # Sysop flag-protect -> full protect
116                        if ( $frLimit == 'sysop' || in_array( 'sysop', $oldLimit ) ) {
117                            $newLimit = 'sysop';
118                        # Reviewer/autoconfirmed flag-protect -> semi-protect
119                        } else {
120                            $newLimit = 'autoconfirmed';
121                        }
122                        # Take highest expiry of (flag,type) settings
123                        $newExpiry = ( !$oldLimit || $frExpiry >= $oldExpiry )
124                            ? $frExpiry // note: 'infinity' > '99999999999999'
125                            : $oldExpiry;
126                    # Otherwise - maintain original limits
127                    } else {
128                        $newLimit = $oldLimit;
129                        $newExpiry = $oldExpiry;
130                    }
131                    $limit[$type] = $newLimit;
132                    $expiry[$type] = $newExpiry;
133                }
134
135                $db->begin( __METHOD__ );
136                $wikiPage = $wikiPageFactory->newFromTitle( $title );
137                $ok = $wikiPage->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user );
138                if ( $ok ) {
139                    $count++;
140                } else {
141                    $this->output( "Could not protect: " . $title->getPrefixedText() . "\n" );
142                }
143                $db->commit( __METHOD__ );
144            }
145            $blockStart += $this->mBatchSize - 1;
146            $blockEnd += $this->mBatchSize - 1;
147            $lbFactory->waitForReplication( [ 'ifWritesSince' => 5 ] );
148        }
149        $this->output( "Protection of all flag-protected pages complete ... {$count} pages\n" );
150    }
151}
152
153$maintClass = FlagProtectToSemiProtect::class;
154require_once RUN_MAINTENANCE_IF_MAIN;