Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
PurgeReviewablePages
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 4
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 listReviewablePages
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 1
90
 purgeReviewablePages
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2/**
3 * @ingroup Maintenance
4 */
5
6use MediaWiki\Cache\HTMLFileCache;
7use MediaWiki\MainConfigNames;
8use MediaWiki\Maintenance\Maintenance;
9use MediaWiki\Title\Title;
10
11// @codeCoverageIgnoreStart
12if ( getenv( 'MW_INSTALL_PATH' ) ) {
13    $IP = getenv( 'MW_INSTALL_PATH' );
14} else {
15    $IP = __DIR__ . '/../../..';
16}
17
18require_once "$IP/maintenance/Maintenance.php";
19// @codeCoverageIgnoreEnd
20
21class PurgeReviewablePages extends Maintenance {
22
23    public function __construct() {
24        parent::__construct();
25        $this->addDescription( "Use to purge CDN/file cache for all reviewable pages" );
26        $this->addOption( 'makelist',
27            "Build the list of reviewable pages to pagesToPurge.list" );
28        $this->addOption( 'purgelist',
29            "Purge the list of pages in pagesToPurge.list" );
30        $this->setBatchSize( 1000 );
31        $this->requireExtension( 'FlaggedRevs' );
32    }
33
34    /**
35     * @inheritDoc
36     */
37    public function execute() {
38        $fileName = "pagesToPurge.list";
39        // Build the list file...
40        if ( $this->getOption( 'makelist' ) ) {
41            $fileHandle = fopen( $fileName, 'w+' );
42            if ( !$fileHandle ) {
43                $this->fatalError( "Can't open file to create purge list." );
44            }
45            $this->listReviewablePages( $fileHandle );
46            fclose( $fileHandle );
47        // Purge pages on the list file...
48        } elseif ( $this->getOption( 'purgelist' ) ) {
49            $fileHandle = fopen( $fileName, 'r' );
50            if ( !$fileHandle ) {
51                $this->fatalError( "Can't open file to read purge list." );
52            }
53            $this->purgeReviewablePages( $fileHandle );
54            fclose( $fileHandle );
55        } else {
56            $this->fatalError( "No purge list action specified." );
57        }
58    }
59
60    /**
61     * @param resource $fileHandle
62     */
63    private function listReviewablePages( $fileHandle ) {
64        $this->output( "Building list of all reviewable pages to purge ...\n" );
65        $config = $this->getConfig();
66        $reviewNamespaces = $config->get( 'FlaggedRevsNamespaces' );
67        if ( !$config->get( MainConfigNames::UseCdn ) && !$config->get( MainConfigNames::UseFileCache ) ) {
68            $this->output( "CDN/file cache not enabled ... nothing to purge.\n" );
69            return;
70        } elseif ( !$reviewNamespaces ) {
71            $this->output( "There are no reviewable namespaces ... nothing to purge.\n" );
72            return;
73        }
74
75        $db = $this->getPrimaryDB();
76
77        $start = $db->newSelectQueryBuilder()
78            ->select( 'MIN(page_id)' )
79            ->from( 'page' )
80            ->caller( __METHOD__ )
81            ->fetchField();
82        $end = $db->newSelectQueryBuilder()
83            ->select( 'MAX(page_id)' )
84            ->from( 'page' )
85            ->caller( __METHOD__ )
86            ->fetchField();
87        if ( $start === null || $end === null ) {
88            $this->output( "... page table seems to be empty.\n" );
89            return;
90        }
91        # Do remaining chunk
92        $end += $this->mBatchSize - 1;
93        $blockStart = (int)$start;
94        $blockEnd = (int)( $start + $this->mBatchSize - 1 );
95
96        $count = 0;
97        while ( $blockEnd <= $end ) {
98            $this->output( "... doing page_id from $blockStart to $blockEnd\n" );
99            $res = $db->newSelectQueryBuilder()
100                ->select( '*' )
101                ->from( 'page' )
102                ->where( [
103                    $db->expr( 'page_id', '>=', $blockEnd ),
104                    $db->expr( 'page_id', '<=', $blockEnd ),
105                    'page_namespace' => $reviewNamespaces,
106                ] )
107                ->caller( __METHOD__ )
108                ->fetchResultSet();
109            # Go through and append each purgeable page...
110            foreach ( $res as $row ) {
111                $title = Title::newFromRow( $row );
112                $fa = FlaggableWikiPage::getTitleInstance( $title );
113                if ( $fa->isReviewable() ) {
114                    # Need to purge this page - add to list
115                    fwrite( $fileHandle, $title->getPrefixedDBkey() . "\n" );
116                    $count++;
117                }
118            }
119            $blockStart += $this->mBatchSize - 1;
120            $blockEnd += $this->mBatchSize - 1;
121            $this->waitForReplication();
122        }
123        $this->output( "List of reviewable pages to purge complete ... {$count} pages\n" );
124    }
125
126    /**
127     * @param resource $fileHandle
128     */
129    private function purgeReviewablePages( $fileHandle ) {
130        $this->output( "Purging CDN cache for list of pages to purge ...\n" );
131        $config = $this->getConfig();
132        if ( !$config->get( MainConfigNames::UseCdn ) && !$config->get( MainConfigNames::UseFileCache ) ) {
133            $this->output( "CDN/file cache not enabled ... nothing to purge.\n" );
134            return;
135        }
136
137        $services = $this->getServiceContainer();
138        $htmlCache = $services->getHTMLCacheUpdater();
139
140        $count = 0;
141        while ( !feof( $fileHandle ) ) {
142            $dbKey = trim( fgets( $fileHandle ) );
143            if ( $dbKey == '' ) {
144                continue; // last line?
145            }
146            $title = Title::newFromDBkey( $dbKey );
147            if ( $title ) {
148                // send PURGE
149                $htmlCache->purgeTitleUrls( $title, $htmlCache::PURGE_INTENT_TXROUND_REFLECTED );
150                // purge poor-mans's CDN
151                HTMLFileCache::clearFileCache( $title );
152                $this->output( "... $dbKey\n" );
153
154                $count++;
155                if ( ( $count % $this->mBatchSize ) == 0 ) {
156                    $this->waitForReplication();
157                }
158            } else {
159                $this->output( "Invalid title - cannot purge: $dbKey\n" );
160            }
161        }
162        $this->output( "CDN/file cache purge of page list complete ... {$count} pages\n" );
163    }
164}
165
166// @codeCoverageIgnoreStart
167$maintClass = PurgeReviewablePages::class;
168require_once RUN_MAINTENANCE_IF_MAIN;
169// @codeCoverageIgnoreEnd