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