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