Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
37.50% covered (danger)
37.50%
18 / 48
50.00% covered (danger)
50.00%
7 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConversionStrategy
37.50% covered (danger)
37.50%
18 / 48
50.00% covered (danger)
50.00%
7 / 14
61.85
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 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 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        return !LqtDispatch::isLqtPage( $title );
89    }
90
91    public function createImportSource( Title $title ) {
92        return new ImportSource( $this->api, $title->getPrefixedText(), $this->talkpageUser );
93    }
94
95    /**
96     * Flow does not support viewing the history of the wikitext pages it takes
97     * over, so those need to be moved out the way. This method decides that
98     * destination. The archived revisions include the headers displayed with
99     * lqt and potentially any pre-lqt wikitext talk page content.
100     *
101     * @param Title $source
102     * @return Title
103     */
104    public function decideArchiveTitle( Title $source ) {
105        $archiveNameHelper = new ArchiveNameHelper();
106        return $archiveNameHelper->decideArchiveTitle( $source, [
107            '%s/LQT Archive %d',
108        ] );
109    }
110
111    /**
112     * Creates a new revision that ensures the LQT magic word is there and turning LQT off.
113     * It also adds a template about the move.
114     * effectively no longer be LQT pages.
115     *
116     * @param WikitextContent $content
117     * @param Title $title
118     * @return WikitextContent
119     */
120    public function createArchiveCleanupRevisionContent( WikitextContent $content, Title $title ) {
121        // cleanup existing text
122        $existing = $content->getText();
123        $existing = self::removeLqtMagicWord( $existing );
124        $existing = $this->removePrefixText( $existing );
125
126        // prefix the existing text with some additional info related to the conversion
127        $text = $this->getPrefixText( $content, $title ) . "\n\n";
128        $text .= self::getDisableLqtMagicWord() . "\n\n";
129        $text .= $existing;
130
131        return new WikitextContent( $text );
132    }
133
134    public function getPostprocessor() {
135        $group = new ProcessorGroup;
136        $group->add( new LqtRedirector( $this->urlGenerator, $this->talkpageUser ) );
137        $group->add( new LqtNotifications( $this->notificationController, $this->dbr ) );
138
139        return $group;
140    }
141
142    /**
143     * @inheritDoc
144     */
145    public function shouldConvert( Title $sourceTitle ) {
146        // The expensive part of this (user-override checking) is cached by LQT.
147        return LqtDispatch::isLqtPage( $sourceTitle );
148    }
149
150    /**
151     * Gets rid of any "This page is an archived page..." prefix that may have
152     * been added in an earlier conversion run.
153     *
154     * @param string $content
155     * @return string
156     */
157    protected function removePrefixText( $content ) {
158        $template = wfMessage( 'flow-importer-lqt-converted-archive-template' )->inContentLanguage()->plain();
159        $templateSearch = preg_quote( $template, '/' );
160        return preg_replace( '/{{' . $templateSearch . '}\\|[^\\}]+}/', '', $content );
161    }
162
163    /**
164     * Generates a "This page is an archived page..." text to add to the
165     * existing content.
166     *
167     * @param WikitextContent $content
168     * @param Title $title
169     * @return string
170     */
171    protected function getPrefixText( WikitextContent $content, Title $title ) {
172        $arguments = implode( '|', [
173            'from=' . $title->getPrefixedText(),
174            'date=' . MWTimestamp::getInstance()->timestamp->format( 'Y-m-d' ),
175        ] );
176
177        $template = wfMessage( 'flow-importer-lqt-converted-archive-template' )->inContentLanguage()->plain();
178
179        return "{{{$template}|$arguments}}\n\n" . self::getDisableLqtMagicWord() . "\n\n";
180    }
181
182    /**
183     * Remove the LQT magic word or its localized version
184     * @param string $content
185     * @return string
186     */
187    public static function removeLqtMagicWord( $content ) {
188        $magicWord = MediaWikiServices::getInstance()->getMagicWordFactory()->
189            get( 'useliquidthreads' );
190        $patterns = array_map(
191            // delete any status: enabled or disabled doesn't matter (we're
192            // adding disabled magic word anyway and having it twice is messy)
193            static function ( $word ) {
194                return '/{{\\s*#' . preg_quote( $word ) . ':\\s*[01]*\\s*}}/i';
195            },
196            [ 'useliquidthreads' ] + $magicWord->getSynonyms() );
197
198        return preg_replace( $patterns, '', $content );
199    }
200
201    /**
202     * @return string The localized magic word to disable LQT on a page
203     */
204    public static function getDisableLqtMagicWord() {
205        $wordObj = MediaWikiServices::getInstance()->getMagicWordFactory()->
206            get( 'useliquidthreads' );
207        $magicWord = strtolower( $wordObj->getSynonym( 0 ) );
208        return "{{#$magicWord:0}}";
209    }
210}