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