Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
36.00% covered (danger)
36.00%
18 / 50
50.00% covered (danger)
50.00%
7 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConversionStrategy
36.00% covered (danger)
36.00%
18 / 50
50.00% covered (danger)
50.00%
7 / 14
73.98
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
 getSourceStore
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMoveComment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCleanupComment
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isConversionFinished
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 createImportSource
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 decideArchiveTitle
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 createArchiveCleanupRevisionContent
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 getPostprocessor
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 shouldConvert
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 removePrefixText
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getPrefixText
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 removeLqtMagicWord
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getDisableLqtMagicWord
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Flow\Import\LiquidThreadsApi;
4
5use Flow\Import\ArchiveNameHelper;
6use Flow\Import\IConversionStrategy;
7use Flow\Import\Postprocessor\LqtNotifications;
8use Flow\Import\Postprocessor\LqtRedirector;
9use Flow\Import\Postprocessor\ProcessorGroup;
10use Flow\Import\SourceStore\SourceStoreInterface;
11use Flow\Notifications\Controller;
12use Flow\UrlGenerator;
13use LqtDispatch;
14use MediaWiki\Content\WikitextContent;
15use MediaWiki\MediaWikiServices;
16use MediaWiki\Title\Title;
17use MediaWiki\User\User;
18use MediaWiki\Utils\MWTimestamp;
19use Wikimedia\Rdbms\IReadableDatabase;
20
21/**
22 * Converts LiquidThreads pages on a wiki to Flow. This converter is idempotent
23 * when used with an appropriate SourceStoreInterface, and may be run many times
24 * without worry for duplicate imports.
25 *
26 * Pages with the LQT magic word will be moved to a subpage of their original location
27 * named 'LQT Archive N' with N increasing starting at 1 looking for the first empty page.
28 * On successful import of an entire page the LQT magic word will be stripped from the
29 * archive version of the page.
30 */
31class ConversionStrategy implements IConversionStrategy {
32
33    protected IReadableDatabase $dbr;
34    /**
35     * @var SourceStoreInterface
36     */
37    protected $sourceStore;
38
39    /**
40     * @var ApiBackend
41     */
42    public $api;
43
44    /**
45     * @var UrlGenerator
46     */
47    protected $urlGenerator;
48
49    /**
50     * @var User
51     */
52    protected $talkpageUser;
53
54    /**
55     * @var Controller
56     */
57    protected $notificationController;
58
59    public function __construct(
60        IReadableDatabase $dbr,
61        SourceStoreInterface $sourceStore,
62        ApiBackend $api,
63        UrlGenerator $urlGenerator,
64        User $talkpageUser,
65        Controller $notificationController
66    ) {
67        $this->dbr = $dbr;
68        $this->sourceStore = $sourceStore;
69        $this->api = $api;
70        $this->urlGenerator = $urlGenerator;
71        $this->talkpageUser = $talkpageUser;
72        $this->notificationController = $notificationController;
73    }
74
75    public function getSourceStore() {
76        return $this->sourceStore;
77    }
78
79    public function getMoveComment( Title $from, Title $to ) {
80        return "Conversion of LQT to Flow from: {$from->getPrefixedText()}";
81    }
82
83    public function getCleanupComment( Title $from, Title $to ) {
84        return "LQT to Flow conversion";
85    }
86
87    public function isConversionFinished( Title $title, ?Title $movedFrom = null ) {
88        if ( LqtDispatch::isLqtPage( $title ) ) {
89            return false;
90        } else {
91            return true;
92        }
93    }
94
95    public function createImportSource( Title $title ) {
96        return new ImportSource( $this->api, $title->getPrefixedText(), $this->talkpageUser );
97    }
98
99    /**
100     * Flow does not support viewing the history of the wikitext pages it takes
101     * over, so those need to be moved out the way. This method decides that
102     * destination. The archived revisions include the headers displayed with
103     * lqt and potentially any pre-lqt wikitext talk page content.
104     *
105     * @param Title $source
106     * @return Title
107     */
108    public function decideArchiveTitle( Title $source ) {
109        $archiveNameHelper = new ArchiveNameHelper();
110        return $archiveNameHelper->decideArchiveTitle( $source, [
111            '%s/LQT Archive %d',
112        ] );
113    }
114
115    /**
116     * Creates a new revision that ensures the LQT magic word is there and turning LQT off.
117     * It also adds a template about the move.
118     * effectively no longer be LQT pages.
119     *
120     * @param WikitextContent $content
121     * @param Title $title
122     * @return WikitextContent
123     */
124    public function createArchiveCleanupRevisionContent( WikitextContent $content, Title $title ) {
125        // cleanup existing text
126        $existing = $content->getText();
127        $existing = self::removeLqtMagicWord( $existing );
128        $existing = $this->removePrefixText( $existing );
129
130        // prefix the existing text with some additional info related to the conversion
131        $text = $this->getPrefixText( $content, $title ) . "\n\n";
132        $text .= self::getDisableLqtMagicWord() . "\n\n";
133        $text .= $existing;
134
135        return new WikitextContent( $text );
136    }
137
138    public function getPostprocessor() {
139        $group = new ProcessorGroup;
140        $group->add( new LqtRedirector( $this->urlGenerator, $this->talkpageUser ) );
141        $group->add( new LqtNotifications( $this->notificationController, $this->dbr ) );
142
143        return $group;
144    }
145
146    /**
147     * @inheritDoc
148     */
149    public function shouldConvert( Title $sourceTitle ) {
150        // The expensive part of this (user-override checking) is cached by LQT.
151        return LqtDispatch::isLqtPage( $sourceTitle );
152    }
153
154    /**
155     * Gets rid of any "This page is an archived page..." prefix that may have
156     * been added in an earlier conversion run.
157     *
158     * @param string $content
159     * @return string
160     */
161    protected function removePrefixText( $content ) {
162        $template = wfMessage( 'flow-importer-lqt-converted-archive-template' )->inContentLanguage()->plain();
163        $templateSearch = preg_quote( $template, '/' );
164        return preg_replace( '/{{' . $templateSearch . '}\\|[^\\}]+}/', '', $content );
165    }
166
167    /**
168     * Generates a "This page is an archived page..." text to add to the
169     * existing content.
170     *
171     * @param WikitextContent $content
172     * @param Title $title
173     * @return string
174     */
175    protected function getPrefixText( WikitextContent $content, Title $title ) {
176        $arguments = implode( '|', [
177            'from=' . $title->getPrefixedText(),
178            'date=' . MWTimestamp::getInstance()->timestamp->format( 'Y-m-d' ),
179        ] );
180
181        $template = wfMessage( 'flow-importer-lqt-converted-archive-template' )->inContentLanguage()->plain();
182
183        return "{{{$template}|$arguments}}\n\n" . self::getDisableLqtMagicWord() . "\n\n";
184    }
185
186    /**
187     * Remove the LQT magic word or its localized version
188     * @param string $content
189     * @return string
190     */
191    public static function removeLqtMagicWord( $content ) {
192        $magicWord = MediaWikiServices::getInstance()->getMagicWordFactory()->
193            get( 'useliquidthreads' );
194        $patterns = array_map(
195            // delete any status: enabled or disabled doesn't matter (we're
196            // adding disabled magic word anyway and having it twice is messy)
197            static function ( $word ) {
198                return '/{{\\s*#' . preg_quote( $word ) . ':\\s*[01]*\\s*}}/i';
199            },
200            [ 'useliquidthreads' ] + $magicWord->getSynonyms() );
201
202        return preg_replace( $patterns, '', $content );
203    }
204
205    /**
206     * @return string The localized magic word to disable LQT on a page
207     */
208    public static function getDisableLqtMagicWord() {
209        $wordObj = MediaWikiServices::getInstance()->getMagicWordFactory()->
210            get( 'useliquidthreads' );
211        $magicWord = strtolower( $wordObj->getSynonym( 0 ) );
212        return "{{#$magicWord:0}}";
213    }
214}