Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
74.19% covered (warning)
74.19%
69 / 93
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
CargoRecreateData
78.41% covered (warning)
78.41%
69 / 88
33.33% covered (danger)
33.33%
1 / 3
39.06
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 execute
84.21% covered (warning)
84.21%
16 / 19
0.00% covered (danger)
0.00%
0 / 1
10.39
 recreateAllDataForTable
74.60% covered (warning)
74.60%
47 / 63
0.00% covered (danger)
0.00%
0 / 1
24.91
1<?php
2
3/**
4 * This script creates, or recreates, one or more Cargo database tables,
5 * based on the relevant data contained in the wiki.
6 *
7 * Usage:
8 *  php cargoRecreateData.php [--table tableName] [--quiet] etc.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 * http://www.gnu.org/copyleft/gpl.html
24 *
25 * @author Yaron Koren
26 * @ingroup Maintenance
27 */
28
29if ( getenv( 'MW_INSTALL_PATH' ) ) {
30    require_once getenv( 'MW_INSTALL_PATH' ) . '/maintenance/Maintenance.php';
31} else {
32    require_once __DIR__ . '/../../../maintenance/Maintenance.php';
33}
34
35use MediaWiki\MediaWikiServices;
36use MediaWiki\Title\Title;
37
38$maintClass = CargoRecreateData::class;
39
40class CargoRecreateData extends Maintenance {
41    /** @var array */
42    private $templatesThatDeclareTables;
43    /** @var array */
44    private $templatesThatAttachToTables;
45
46    public function __construct() {
47        parent::__construct();
48
49        $this->requireExtension( 'Cargo' );
50        $this->addDescription( "Recreate the data for one or more Cargo database tables." );
51        $this->addOption( 'table', 'The Cargo table to recreate', false, true );
52        $this->addOption( 'replacement', 'Put all new data into a replacement table, to be switched in later' );
53        $this->addOption( 'create-missing-tables-only', 'Only create the tables, and their metadata, that do not already exist, without storing any data in them' );
54    }
55
56    public function execute() {
57        $quiet = $this->getOption( 'quiet' );
58        $this->templatesThatDeclareTables = CargoUtils::getAllPageProps( 'CargoTableName' );
59        $this->templatesThatAttachToTables = CargoUtils::getAllPageProps( 'CargoAttachedTable' );
60
61        $tableName = $this->getOption( 'table' );
62        $createReplacement = $this->hasOption( 'replacement' );
63        $createMissingTablesOnly = $this->hasOption( 'create-missing-tables-only' );
64
65        if ( $createMissingTablesOnly && $createReplacement ) {
66            $this->fatalError( 'The --create-missing-tables-only and --replacement options cannot be used together.' );
67        }
68
69        if ( $tableName == null ) {
70            if ( $createMissingTablesOnly ) {
71                // The cargo_tables table holds only tables that already exist.
72                $tableNames = array_keys( $this->templatesThatDeclareTables );
73            } else {
74                $tableNames = CargoUtils::getTables();
75            }
76            foreach ( $tableNames as $i => $tableName ) {
77                if ( $tableName == '_pageData' || $tableName == '_fileData' ) {
78                    // This is handled in a separate script.
79                    continue;
80                }
81                if ( $i > 0 && !$quiet ) {
82                    print "\n";
83                }
84                $this->recreateAllDataForTable( $tableName, $createReplacement, $createMissingTablesOnly );
85            }
86        } else {
87            $this->recreateAllDataForTable( $tableName, $createReplacement, $createMissingTablesOnly );
88        }
89    }
90
91    private function recreateAllDataForTable( $tableName, $createReplacement, $createMissingTablesOnly ) {
92        global $wgTitle;
93
94        $quiet = $this->getOption( 'quiet' );
95
96        // Quick retrieval and check before we do anything else.
97        $templatePageID = CargoUtils::getTemplateIDForDBTable( $tableName );
98        if ( $templatePageID == null ) {
99            if ( !$quiet ) {
100                print "Table \"$tableName\" is not declared in any template; skipping.\n";
101            }
102            return;
103        }
104
105        if ( $createMissingTablesOnly && CargoUtils::tableFullyExists( $tableName ) ) {
106            if ( !$quiet ) {
107                print "Table \"$tableName\" already exists; skipping.\n";
108            }
109            return;
110        }
111
112        if ( !$quiet ) {
113            if ( $createMissingTablesOnly ) {
114                print "Creating Cargo table $tableName...\n";
115            } else {
116                print "Recreating data for Cargo table $tableName in 5 seconds... hit [Ctrl]-C to escape.\n";
117                if ( $createReplacement ) {
118                    print "(Data will be placed in a separate, replacement table.)\n";
119                }
120                // No point waiting if the user doesn't know about it
121                // anyway, right?
122                sleep( 5 );
123                print "Deleting and recreating table...\n";
124            }
125        }
126
127        try {
128            $user = User::newSystemUser( 'Maintenance script', [ 'steal' => true ] );
129            CargoUtils::recreateDBTablesForTemplate(
130                $templatePageID,
131                $createReplacement,
132                $user,
133                $tableName
134            );
135        } catch ( MWException $e ) {
136            print "Error: " . $e->getMessage() . "\n";
137            return;
138        }
139
140        if ( $createMissingTablesOnly ) {
141            return;
142        }
143
144        // These arrays are poorly named. @TODO - fix names
145        if ( array_key_exists( $tableName, $this->templatesThatDeclareTables ) ) {
146            $templatesThatDeclareThisTable = $this->templatesThatDeclareTables[$tableName];
147        } else {
148            // This shouldn't happen, given that we already did
149            // a check.
150            print "Table \"$tableName\" is not declared in any template.\n";
151            return;
152        }
153
154        if ( array_key_exists( $tableName, $this->templatesThatAttachToTables ) ) {
155            $templatesThatAttachToThisTable = $this->templatesThatAttachToTables[$tableName];
156        } else {
157            $templatesThatAttachToThisTable = [];
158        }
159        $templatesForThisTable = array_merge( $templatesThatDeclareThisTable, $templatesThatAttachToThisTable );
160        $wikiPageFactory = MediaWikiServices::getInstance()->getWikiPageFactory();
161
162        foreach ( $templatesForThisTable as $templatePageID ) {
163            $templateTitle = Title::newFromID( $templatePageID );
164            if ( $templateTitle == null ) {
165                // It is possible that the Template to which the table is associated, is now deleted by the user
166                print "Template (Template Page ID = $templatePageID) does not exist, cannot recreate data corresponding to this template\n";
167                continue;
168            }
169            if ( !$quiet ) {
170                print "Handling template that adds to this table: " . $templateTitle->getText() . "\n";
171            }
172
173            $offset = 0;
174            do {
175                $titlesWithThisTemplate = $templateTitle->getTemplateLinksTo( [
176                    'LIMIT' => 500, 'OFFSET' => $offset ] );
177                if ( !$quiet ) {
178                    print "Saving data for pages " . ( $offset + 1 ) . " to " . ( $offset + count( $titlesWithThisTemplate ) ) . " that call this template...\n";
179                }
180
181                foreach ( $titlesWithThisTemplate as $title ) {
182                    // This may be helpful.
183                    $wgTitle = $title;
184                    // All we need to do here is set some global variables based
185                    // on the parameters of this job, then parse the page -
186                    // the #cargo_store function will take care of the rest.
187                    CargoStore::$settings['origin'] = 'template';
188                    CargoStore::$settings['dbTableName'] = $tableName;
189                    $wikiPage = $wikiPageFactory->newFromID( $title->getArticleID() );
190                    if ( $wikiPage == null ) {
191                        continue;
192                    }
193                    $content = $wikiPage->getContent();
194                    $contentText = CargoUtils::getContentText( $content );
195                    CargoUtils::parsePageForStorage( $title, $contentText );
196                }
197                $offset += 500;
198            } while ( count( $titlesWithThisTemplate ) == 500 );
199        }
200    }
201
202}
203
204require_once RUN_MAINTENANCE_IF_MAIN;