Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 102
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
UpdateFRTracking
0.00% covered (danger)
0.00%
0 / 96
0.00% covered (danger)
0.00%
0 / 3
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 updateFlaggedPages
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 1
182
1<?php
2/**
3 * @ingroup Maintenance
4 */
5
6use MediaWiki\Maintenance\Maintenance;
7use MediaWiki\Title\Title;
8use Wikimedia\Rdbms\IDBAccessObject;
9use Wikimedia\Rdbms\SelectQueryBuilder;
10
11if ( getenv( 'MW_INSTALL_PATH' ) ) {
12    $IP = getenv( 'MW_INSTALL_PATH' );
13} else {
14    $IP = __DIR__ . '/../../..';
15}
16
17require_once "$IP/maintenance/Maintenance.php";
18
19class UpdateFRTracking extends Maintenance {
20
21    public function __construct() {
22        parent::__construct();
23        $this->addDescription( "Correct the page data in the flaggedrevs tracking tables. " );
24        $this->addOption( 'startpage', 'Page ID to start on', false, true );
25        $this->requireExtension( 'FlaggedRevs' );
26    }
27
28    /**
29     * @inheritDoc
30     */
31    public function execute() {
32        $startPage = $this->getOption( 'startpage' );
33        $this->updateFlaggedPages( $startPage );
34    }
35
36    /**
37     * @param int|null $start Page ID
38     */
39    private function updateFlaggedPages( $start = null ) {
40        $this->output( "Populating and correcting flaggedpages/flaggedpage_config columns\n" );
41
42        $BATCH_SIZE = 300;
43
44        $db = $this->getPrimaryDB();
45        $revisionStore = $this->getServiceContainer()->getRevisionStore();
46
47        if ( $start === null ) {
48            $start = $db->newSelectQueryBuilder()
49                ->select( 'MIN(page_id)' )
50                ->from( 'page' )
51                ->caller( __METHOD__ )
52                ->fetchField();
53        }
54        $end = $db->newSelectQueryBuilder()
55            ->select( 'MAX(page_id)' )
56            ->from( 'page' )
57            ->caller( __METHOD__ )
58            ->fetchField();
59        if ( $start === null || $end === null ) {
60            $this->output( "...flaggedpages table seems to be empty.\n" );
61            return;
62        }
63        # Do remaining chunk
64        $end += $BATCH_SIZE - 1;
65        $blockStart = (int)$start;
66        $blockEnd = (int)$start + $BATCH_SIZE - 1;
67        $count = 0;
68        $deleted = 0;
69        $fixed = 0;
70        while ( $blockEnd <= $end ) {
71            $this->output( "...doing page_id from $blockStart to $blockEnd\n" );
72
73            $this->beginTransaction( $db, __METHOD__ );
74            $res = $db->newSelectQueryBuilder()
75                ->select( [ 'page_id', 'page_namespace', 'page_title', 'page_latest' ] )
76                ->from( 'page' )
77                ->where( $db->expr( 'page_id', '>=', $blockStart )->and( 'page_id', '<=', $blockEnd ) )
78                ->caller( __METHOD__ )
79                ->fetchResultSet();
80            # Go through and update the de-normalized references...
81            foreach ( $res as $row ) {
82                $title = Title::newFromRow( $row );
83                $article = FlaggableWikiPage::newInstance( $title );
84                $oldFrev = FlaggedRevision::newFromStable( $title, IDBAccessObject::READ_LATEST );
85                $frev = FlaggedRevision::determineStable( $title );
86                # Update fp_stable, fp_quality, and fp_reviewed
87                if ( $frev ) {
88                    $article->updateStableVersion( $frev, $row->page_latest );
89                    $changed = ( !$oldFrev || $oldFrev->getRevId() != $frev->getRevId() );
90                # Somethings broke? Delete the row...
91                } else {
92                    $changed = (bool)$oldFrev;
93                    $deleted += (int)$changed;
94                }
95                # Get the latest revision
96                $queryInfo = $revisionStore->getQueryInfo();
97                $revRow = $db->newSelectQueryBuilder()
98                    ->queryInfo( $queryInfo )
99                    ->where( [ 'rev_page' => $row->page_id ] )
100                    ->orderBy( 'rev_timestamp', SelectQueryBuilder::SORT_DESC )
101                    ->caller( __METHOD__ )
102                    ->fetchRow();
103                # Correct page_latest if needed (import/files made plenty of bad rows)
104                if ( $revRow ) {
105                    $latestRevId = $article->getLatest();
106                    if ( $latestRevId ) {
107                        // If not found (false), cast to 0 so that the
108                        // page is updated, just to be on the safe side,
109                        // even though it should always be found
110                        $latestTimestamp = (int)$revisionStore->getTimestampFromId(
111                            $latestRevId,
112                            IDBAccessObject::READ_LATEST
113                        );
114                    } else {
115                        $latestTimestamp = 0;
116                    }
117                    if ( $revRow->rev_timestamp > $latestTimestamp ) {
118                        // Most recent revision, based on timestamp, is
119                        // newer than the page_latest
120                        // update page_latest accordingly
121                        $revRecord = $revisionStore->newRevisionFromRow(
122                            $revRow,
123                            IDBAccessObject::READ_LATEST,
124                            $title
125                        );
126                        if ( $article->updateRevisionOn(
127                            $db,
128                            $revRecord,
129                            $latestRevId
130                        ) ) {
131                            $fixed++;
132                        }
133                    }
134                }
135                if ( $changed ) {
136                    # Lazily rebuild dependencies on next parse (we invalidate below)
137                    FlaggedRevs::clearStableOnlyDeps( $title->getArticleID() );
138                    $title->invalidateCache();
139                }
140                $count++;
141            }
142            # Remove manual config settings that simply restate the site defaults
143            $db->newDeleteQueryBuilder()
144                ->deleteFrom( 'flaggedpage_config' )
145                ->where( [
146                    $db->expr( 'fpc_page_id', '>=', $blockStart ),
147                    $db->expr( 'fpc_page_id', '<=', $blockEnd ),
148                    'fpc_override'  => intval( FlaggedRevs::isStableShownByDefault() ),
149                    'fpc_level'     => ''
150                ] )
151                ->caller( __METHOD__ )
152                ->execute();
153            $deleted += $db->affectedRows();
154            $this->commitTransaction( $db, __METHOD__ );
155
156            $blockStart += $BATCH_SIZE;
157            $blockEnd += $BATCH_SIZE;
158        }
159        $this->output( "flaggedpage columns update complete ..." .
160            " {$count} rows [{$fixed} fixed] [{$deleted} deleted]\n" );
161    }
162}
163
164$maintClass = UpdateFRTracking::class;
165require_once RUN_MAINTENANCE_IF_MAIN;