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 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialSwitchCargoTable
0.00% covered (danger)
0.00%
0 / 91
0.00% covered (danger)
0.00%
0 / 4
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 switchInTableReplacement
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
30
 execute
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2/**
3 * An interface to delete a "Cargo table", which can be one or more real
4 * database tables.
5 *
6 * @author Yaron Koren
7 * @ingroup Cargo
8 */
9
10use MediaWiki\Html\Html;
11
12class SpecialSwitchCargoTable extends UnlistedSpecialPage {
13
14    public function __construct() {
15        parent::__construct( 'SwitchCargoTable' );
16    }
17
18    public function doesWrites() {
19        return true;
20    }
21
22    /**
23     * The table being switched here is a Cargo table, not a DB table per
24     * se - a Cargo table corresponds to a main DB table, plus
25     * potentially one or more helper tables; all need to be switched.
26     * Also, records need to be removed, and modified, in the cargo_tables and
27     * cargo_pages tables.
28     */
29    public static function switchInTableReplacement(
30        $mainTable,
31        $fieldTables,
32        $fieldHelperTables,
33        User $user
34    ) {
35        $cdb = CargoUtils::getDB();
36        $origFieldTableNames = [];
37        $origFieldHelperTableNames = [];
38        try {
39            $cdb->begin( __METHOD__ );
40
41            // The helper tables' names come from the database,
42            // so they already contain '__NEXT' - remove that,
43            // instead of adding it, when getting table names.
44            foreach ( $fieldTables as $fieldTable ) {
45                $origFieldTable = str_replace( '__NEXT', '', $fieldTable );
46                $origFieldTableNames[] = $origFieldTable;
47                $cdb->dropTable( $origFieldTable, __METHOD__ );
48                $cdb->query( 'ALTER TABLE ' .
49                    $cdb->tableName( $fieldTable ) .
50                    ' RENAME TO ' .
51                    $cdb->tableName( $origFieldTable ), __METHOD__ );
52            }
53            if ( is_array( $fieldHelperTables ) ) {
54                foreach ( $fieldHelperTables as $fieldHelperTable ) {
55                    $origFieldHelperTable = str_replace( '__NEXT', '', $fieldHelperTable );
56                    $origFieldHelperTableNames[] = $origFieldHelperTable;
57                    $cdb->dropTable( $origFieldHelperTable, __METHOD__ );
58                    $cdb->query( 'ALTER TABLE ' .
59                        $cdb->tableName( $fieldHelperTable ) .
60                        ' RENAME TO ' .
61                        $cdb->tableName( $origFieldHelperTable ), __METHOD__ );
62                }
63            }
64
65            $cdb->dropTable( $mainTable, __METHOD__ );
66            $cdb->query( 'ALTER TABLE ' .
67                $cdb->tableName( $mainTable . '__NEXT' ) .
68                ' RENAME TO ' . $cdb->tableName( $mainTable ), __METHOD__ );
69
70            $cdb->commit( __METHOD__ );
71        } catch ( Exception $e ) {
72            throw new MWException( "Caught exception ($e) while trying to switch in replacement for Cargo table. "
73            . "Please make sure that your database user account has the DROP permission." );
74        }
75
76        $dbw = CargoUtils::getMainDBForWrite();
77        $dbw->delete( 'cargo_tables', [ 'main_table' => $mainTable ], __METHOD__ );
78        $dbw->delete( 'cargo_pages', [ 'table_name' => $mainTable ], __METHOD__ );
79        $dbw->update( 'cargo_tables', [ 'main_table' => $mainTable ], [ 'main_table' => $mainTable . '__NEXT' ], __METHOD__ );
80        $dbw->update(
81            'cargo_tables',
82            [
83                'field_tables' => serialize( $origFieldTableNames ),
84                'field_helper_tables' => serialize( $origFieldHelperTableNames )
85            ],
86            [ 'main_table' => $mainTable ],
87            __METHOD__
88        );
89        $dbw->update( 'cargo_pages', [ 'table_name' => $mainTable ], [ 'table_name' => $mainTable . '__NEXT' ], __METHOD__ );
90
91        CargoUtils::logTableAction( 'replacetable', $mainTable, $user );
92    }
93
94    public function execute( $subpage = false ) {
95        // Check permissions.
96        if ( !$this->getUser()->isAllowed( 'recreatecargodata' ) ) {
97            $this->displayRestrictionError();
98        }
99
100        $out = $this->getOutput();
101        $req = $this->getRequest();
102        $csrfTokenSet = $this->getContext()->getCsrfTokenSet();
103
104        $tableName = $subpage;
105        $out->enableOOUI();
106
107        $this->setHeaders();
108        if ( $tableName == '' ) {
109            CargoUtils::displayErrorMessage( $out, $this->msg( "cargo-notable" ) );
110            return true;
111        }
112
113        // Make sure that this table, and its replacement, both exist.
114        $dbr = CargoUtils::getMainDBForRead();
115        $res = $dbr->select( 'cargo_tables', [ 'main_table', 'field_tables', 'field_helper_tables' ],
116            [ 'main_table' => $tableName ], __METHOD__ );
117        if ( $res->numRows() == 0 ) {
118            CargoUtils::displayErrorMessage( $out, $this->msg( "cargo-unknowntable", $tableName ) );
119            return true;
120        }
121        $res = $dbr->select( 'cargo_tables', [ 'main_table', 'field_tables', 'field_helper_tables' ],
122            [ 'main_table' => $tableName . '__NEXT' ], __METHOD__ );
123        if ( $res->numRows() == 0 ) {
124            CargoUtils::displayErrorMessage( $out, $this->msg( "cargo-unknowntable", $tableName . "__NEXT" ) );
125            return true;
126        }
127
128        $ctPage = CargoUtils::getSpecialPage( 'CargoTables' );
129        $row = $res->fetchRow();
130        $fieldTables = unserialize( $row['field_tables'] );
131        $fieldHelperTables = unserialize( $row['field_helper_tables'] );
132
133        if ( $req->wasPosted() && $req->getCheck( 'switch' ) && $csrfTokenSet->matchToken( $req->getText( 'wpEditToken' ) ) ) {
134            self::switchInTableReplacement( $tableName, $fieldTables, $fieldHelperTables, $this->getUser() );
135            $text = Html::element( 'p', [], $this->msg( 'cargo-switchtables-success', $tableName )->parse() ) . "\n";
136            $tablesLink = CargoUtils::makeLink( $this->getLinkRenderer(),
137                $ctPage->getPageTitle(), $ctPage->getDescription()->escaped() );
138            $text .= Html::rawElement( 'p', [], $this->msg( 'returnto' )->rawParams( $tablesLink )->escaped() );
139            $out->addHTML( $text );
140            return true;
141        }
142
143        $ctURL = $ctPage->getPageTitle()->getLocalURL();
144        $tableLink = Html::element( 'a', [ 'href' => "$ctURL/$tableName", ], $tableName );
145
146        $text = Html::rawElement( 'p', [], $this->msg( 'cargo-switchtables-confirm' )->rawParams( $tableLink )->escaped() );
147        $out->addHTML( $text );
148
149        $htmlForm = HTMLForm::factory( 'ooui', [], $this->getContext() );
150        $htmlForm
151            ->setSubmitName( 'switch' )
152            ->setSubmitTextMsg( 'cargo-switchtables-switch' )
153            ->prepareForm()
154            ->displayForm( false );
155
156        return true;
157    }
158
159}