Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 85
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
CleanCNTranslateMetadata
0.00% covered (danger)
0.00%
0 / 85
0.00% covered (danger)
0.00%
0 / 5
90
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 cleanDuplicates
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
12
 populateIDs
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
6
 deleteOrphans
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3use MediaWiki\Maintenance\Maintenance;
4
5// @codeCoverageIgnoreStart
6$IP = getenv( 'MW_INSTALL_PATH' );
7if ( $IP === false ) {
8    $IP = __DIR__ . '/../../..';
9}
10require_once "$IP/maintenance/Maintenance.php";
11// @codeCoverageIgnoreEnd
12
13/**
14 * Cleans up the Revision Tag table which is where CentralNotice stores
15 * metadata required for the Translate extension.
16 *
17 * So far this class:
18 * * Removes duplicate revision entries (there should be only one per banner)
19 * * Associates entries with a banner by name
20 * * Removes entries that have no banner object
21 */
22class CleanCNTranslateMetadata extends Maintenance {
23    /** @var string|null */
24    private $ttag;
25
26    public function __construct() {
27        parent::__construct();
28        $this->requireExtension( 'CentralNotice' );
29    }
30
31    public function execute() {
32        $this->ttag = Banner::TRANSLATE_BANNER_TAG;
33
34        $this->cleanDuplicates();
35        $this->populateIDs();
36        $this->deleteOrphans();
37
38        ChoiceDataProvider::invalidateCache();
39    }
40
41    /**
42     * Remove duplicated revtags
43     */
44    private function cleanDuplicates() {
45        $this->output( "Cleaning duplicates\n" );
46
47        $db = CNDatabase::getPrimaryDb();
48
49        $res = $db->newSelectQueryBuilder()
50            ->select( [
51                'rt_page',
52                'maxrev' => 'MAX(rt_revision)',
53                'count' => 'COUNT(*)'
54            ] )
55            ->from( 'revtag' )
56            ->where( [ 'rt_type' => $this->ttag ] )
57            ->groupBy( 'rt_page' )
58            ->caller( __METHOD__ )
59            ->fetchResultSet();
60
61        foreach ( $res as $row ) {
62            if ( (int)$row->count === 1 ) {
63                continue;
64            }
65
66            $maxRev = (int)$row->maxrev;
67            $db->newDeleteQueryBuilder()
68                ->deleteFrom( 'revtag' )
69                ->where( [
70                    'rt_type' => $this->ttag,
71                    'rt_page' => $row->rt_page,
72                    $db->expr( 'rt_revision', '!=', $maxRev ),
73                ] )
74                ->caller( __METHOD__ )
75                ->execute();
76            $numRows = $db->affectedRows();
77            $this->output(
78                " -- Deleted {$numRows} rows for banner with page id {$row->rt_page}\n"
79            );
80        }
81    }
82
83    /**
84     * Attach a banner ID with a orphan metadata line
85     */
86    private function populateIDs() {
87        $this->output( "Associating metadata with banner ids\n" );
88
89        $db = CNDatabase::getPrimaryDb();
90
91        $res = $db->newSelectQueryBuilder()
92            ->select( [ 'rt_page', 'rt_revision', 'page_title', 'tmp_id' ] )
93            ->from( 'revtag' )
94            ->join( 'page', null, 'rt_page=page_id' )
95            ->join( 'cn_templates', null, [
96                # Length of "centralnotice-template-"
97                'tmp_name=substr(page_title, 24)'
98            ] )
99            ->where( [
100                'rt_type' => $this->ttag,
101                'rt_value' => null,
102            ] )
103            ->caller( __METHOD__ )
104            ->fetchResultSet();
105
106        foreach ( $res as $row ) {
107            $this->output( " -- Associating banner id {$row->tmp_id} " .
108                "with revtag with page id {$row->rt_page}\n" );
109            $db->newUpdateQueryBuilder()
110                ->update( 'revtag' )
111                ->set( [ 'rt_value' => $row->tmp_id ] )
112                ->where( [
113                    'rt_type' => $this->ttag,
114                    'rt_page' => $row->rt_page,
115                    'rt_value' => null,
116                ] )
117                ->caller( __METHOD__ )
118                ->execute();
119        }
120    }
121
122    /**
123     * Delete rows that have no banner ID associated with them
124     */
125    private function deleteOrphans() {
126        $db = CNDatabase::getPrimaryDb();
127        $this->output( "Preparing to delete orphaned rows\n" );
128
129        $res = $db->newSelectQueryBuilder()
130            ->select( [ 'rt_page', 'rt_revision' ] )
131            ->from( 'revtag' )
132            ->where( [ 'rt_type' => $this->ttag, 'rt_value' => null ] )
133            ->caller( __METHOD__ )
134            ->fetchResultSet();
135
136        foreach ( $res as $row ) {
137            $this->output( " -- Deleting orphan row {$row->rt_page}:{$row->rt_revision}\n" );
138            $db->newDeleteQueryBuilder()
139                ->deleteFrom( 'revtag' )
140                ->where( [
141                    'rt_type' => $this->ttag,
142                    'rt_page' => $row->rt_page,
143                    'rt_revision' => $row->rt_revision,
144                    // Just in case something updated it
145                    'rt_value' => null,
146                ] )
147                ->caller( __METHOD__ )
148                ->execute();
149        }
150    }
151}
152
153// @codeCoverageIgnoreStart
154$maintClass = CleanCNTranslateMetadata::class;
155require_once RUN_MAINTENANCE_IF_MAIN;
156// @codeCoverageIgnoreEnd