Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConvertAllLqtPages
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 3
110
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
42
 buildIterator
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace Flow\Maintenance;
4
5use AppendIterator;
6use Flow\Container;
7use Flow\Import\Converter;
8use Flow\Import\LiquidThreadsApi\ConversionStrategy;
9use Flow\Import\LiquidThreadsApi\LocalApiBackend;
10use Flow\Import\SourceStore\FileImportSourceStore;
11use Flow\Import\SourceStore\FlowRevisionsDb;
12use Flow\OccupationController;
13use Flow\Utils\NamespaceIterator;
14use Flow\Utils\PagesWithPropertyIterator;
15use MediaWiki\Maintenance\Maintenance;
16use MediaWiki\MediaWikiServices;
17use Psr\Log\AbstractLogger;
18use Psr\Log\LogLevel;
19use Wikimedia\Rdbms\IReadableDatabase;
20
21$IP = getenv( 'MW_INSTALL_PATH' );
22if ( $IP === false ) {
23    $IP = __DIR__ . '/../../..';
24}
25
26require_once "$IP/maintenance/Maintenance.php";
27
28/**
29 * Converts all LiquidThreads pages on a wiki to Flow. When using the logfile
30 * option this process is idempotent.It may be run many times and will only import
31 * one copy of each item.
32 */
33class ConvertAllLqtPages extends Maintenance {
34    public function __construct() {
35        parent::__construct();
36        $this->addDescription( "Converts LiquidThreads data to Flow data" );
37        $this->addOption( 'logfile', 'File to read and store associations between imported items ' .
38            'and their sources. This is required for the import to be idempotent.', false, true );
39        $this->addOption( 'force-recovery-conversion', 'If a previous logfile was lost, this ' .
40            'option can be set to attempt to map threads to topics that have already been ' .
41            'imported to prevent doubles.' );
42        $this->addOption( 'debug', 'Include debug information with progress report' );
43        $this->addOption( 'dryrun', 'Show what would be converted, but do not make any changes.' );
44        $this->addOption( 'ignoreflowreadonly', 'Ignore $wgFlowReadOnly if set, allowing boards to be created.' );
45        $this->addOption( 'convertempty', 'Convert pages even if they have no threads.' );
46        $this->addOption( 'insert-ignore', 'Ignore duplicate key insert errors.' );
47        $this->requireExtension( 'Flow' );
48    }
49
50    public function execute() {
51        global $wgFlowReadOnly;
52
53        if ( $wgFlowReadOnly ) {
54            if ( $this->getOption( 'ignoreflowreadonly', false ) ) {
55                // Make Flow writable for the duration of the script
56                $wgFlowReadOnly = false;
57            } else {
58                $this->error( 'Flow is in read-only mode. Use --ignoreflowreadonly to continue.' );
59                return;
60            }
61        }
62
63        $logfile = $this->getOption( 'logfile' );
64        if ( $logfile ) {
65            $sourceStore = new FileImportSourceStore( $logfile );
66        } elseif ( $this->getOption( 'force-recovery-conversion' ) ) {
67            // fallback: if we don't have a sourcestore to go on, at least look
68            // at DB to figure out what's already imported...
69            $dbr = Container::get( 'db.factory' )->getDB( DB_REPLICA );
70            $sourceStore = new FlowRevisionsDb( $dbr );
71        } else {
72            $this->error( 'Param logfile or force-recovery-conversion required!' );
73            $this->maybeHelp( true );
74            die( 1 );
75        }
76
77        $logger = new MaintenanceDebugLogger( $this );
78        if ( $this->getOption( 'debug' ) ) {
79            $logger->setMaximumLevel( LogLevel::DEBUG );
80        } else {
81            $logger->setMaximumLevel( LogLevel::INFO );
82        }
83
84        $importer = Container::get( 'importer' );
85        /** @var OccupationController $occupationController */
86        $occupationController = MediaWikiServices::getInstance()->getService( 'FlowTalkpageManager' );
87        $talkpageManagerUser = $occupationController->getTalkpageManager();
88
89        $dbw = $this->getPrimaryDB();
90        $strategy = new ConversionStrategy(
91            $dbw,
92            $sourceStore,
93            new LocalApiBackend( $talkpageManagerUser ),
94            Container::get( 'url_generator' ),
95            $talkpageManagerUser,
96            Container::get( 'controller.notification' )
97        );
98
99        $converter = new Converter(
100            $dbw,
101            $importer,
102            $logger,
103            $talkpageManagerUser,
104            $strategy
105        );
106
107        $titles = $this->buildIterator( $logger, $dbw );
108        $dryRun = $this->getOption( 'dryrun', false );
109        $convertEmpty = $this->getOption( 'convertempty', false );
110
111        $logger->info( "Starting full wiki LQT conversion of all LiquidThreads pages" );
112        $converter->convertAll( $titles, $dryRun, $convertEmpty );
113
114        $logger->info( "Finished conversion" );
115    }
116
117    /**
118     * @param AbstractLogger $logger
119     * @param IReadableDatabase $db
120     *
121     * @return AppendIterator
122     */
123    private function buildIterator( $logger, $db ) {
124        global $wgLqtTalkPages;
125
126        $iterator = new AppendIterator();
127
128        $logger->info( "Considering for conversion: pages with the 'use-liquid-threads' property" );
129        $withProperty = new PagesWithPropertyIterator( $db, 'use-liquid-threads' );
130        $iterator->append( $withProperty->getIterator() );
131
132        if ( $wgLqtTalkPages ) {
133            foreach ( $this->getServiceContainer()->getNamespaceInfo()->getTalkNamespaces() as $ns ) {
134                $logger->info( "Considering for conversion: pages in namespace $ns" );
135                $it = new NamespaceIterator( $db, $ns );
136                $iterator->append( $it->getIterator() );
137            }
138        }
139
140        return $iterator;
141    }
142}
143
144$maintClass = ConvertAllLqtPages::class;
145require_once RUN_MAINTENANCE_IF_MAIN;