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