Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.86% covered (success)
98.86%
174 / 176
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImportPreviewPage
98.86% covered (success)
98.86%
174 / 176
83.33% covered (warning)
83.33%
5 / 6
13
0.00% covered (danger)
0.00%
0 / 1
 getHtml
98.35% covered (success)
98.35%
119 / 121
0.00% covered (danger)
0.00%
0 / 1
3
 buildShowChangesButtonHtml
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 buildEditSummaryHtml
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
5
 buildImportIdentityFormSnippet
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 buildCategoriesSnippet
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 wasEdited
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace FileImporter\Html;
4
5use ExtensionRegistry;
6use FileImporter\Data\ImportPlan;
7use FileImporter\Services\CategoryExtractor;
8use MediaWiki\EditPage\EditPage;
9use MediaWiki\Html\Html;
10use MediaWiki\Linker\Linker;
11use MediaWiki\Logger\LoggerFactory;
12use MediaWiki\MediaWikiServices;
13use OOUI\ButtonInputWidget;
14use OOUI\ButtonWidget;
15use OOUI\TextInputWidget;
16
17/**
18 * Page displaying the preview of the import before it has happened.
19 *
20 * @license GPL-2.0-or-later
21 * @author Addshore
22 */
23class ImportPreviewPage extends SpecialPageHtmlFragment {
24
25    public const ACTION_BUTTON = 'action';
26    public const ACTION_EDIT_TITLE = 'edittitle';
27    public const ACTION_EDIT_INFO = 'editinfo';
28    public const ACTION_SUBMIT = 'submit';
29    public const ACTION_VIEW_DIFF = 'viewdiff';
30
31    /**
32     * @return string
33     */
34    public function getHtml( ImportPlan $importPlan ) {
35        // TODO: Inject
36        if ( ExtensionRegistry::getInstance()->isLoaded( 'CentralAuth' ) ) {
37            $config = $this->getContext()->getConfig();
38            $sourceEditingEnabled = $config->get( 'FileImporterSourceWikiTemplating' );
39            $sourceDeletionEnabled = $config->get( 'FileImporterSourceWikiDeletion' );
40        } else {
41            $sourceEditingEnabled = false;
42            $sourceDeletionEnabled = false;
43        }
44
45        $text = $importPlan->getFileInfoText();
46        $title = $importPlan->getTitle();
47
48        $details = $importPlan->getDetails();
49        $textRevisionsCount = count( $details->getTextRevisions()->toArray() );
50        $fileRevisionsCount = count( $details->getFileRevisions()->toArray() );
51        $categoriesSnippet = $this->buildCategoriesSnippet( $importPlan );
52
53        return Html::openElement(
54            'form',
55            [
56                'action' => $this->getPageTitle()->getLocalURL(),
57                'method' => 'POST',
58            ]
59        ) .
60        Html::hidden( 'wpUnicodeCheck', EditPage::UNICODE_CHECK ) .
61        ( new HelpBanner( $this ) )->getHtml() .
62        $this->msg( 'fileimporter-previewnote' )->parseAsBlock() .
63        Html::rawElement(
64            'div',
65            [ 'class' => 'mw-importfile-header' ],
66            Html::element(
67                'h2',
68                [],
69                $title->getText()
70            ) .
71            new ButtonInputWidget(
72                [
73                    'classes' => [ 'mw-importfile-rightAlign' ],
74                    'label' => $this->msg( 'fileimporter-edittitle' )->plain(),
75                    'type' => 'submit',
76                    'name' => self::ACTION_BUTTON,
77                    'value' => self::ACTION_EDIT_TITLE
78                ]
79            )
80        ) .
81        Linker::makeExternalImage( $details->getImageDisplayUrl(), $title->getPrefixedText() ) .
82        Html::rawElement(
83            'div',
84            [ 'class' => 'mw-importfile-header' ],
85            Html::element(
86                'h2',
87                [],
88                $this->msg( 'fileimporter-heading-fileinfo' )->plain()
89            ) .
90            new ButtonInputWidget(
91                [
92                    'classes' => [ 'mw-importfile-rightAlign' ],
93                    'label' => $this->msg( 'fileimporter-editinfo' )->plain(),
94                    'type' => 'submit',
95                    'name' => self::ACTION_BUTTON,
96                    'value' => self::ACTION_EDIT_INFO
97                ]
98            )
99        ) .
100        Html::rawElement(
101            'div',
102            [ 'class' => 'mw-importfile-parsedContent' ],
103            ( new TextRevisionSnippet( $this ) )->getHtml(
104                $details->getTextRevisions()->getLatest(),
105                $text
106            )
107        ) .
108        $categoriesSnippet .
109        Html::element( 'h2', [], $this->msg( 'fileimporter-heading-filehistory' )->plain() ) .
110        $this->msg(
111            'fileimporter-filerevisions',
112            [
113                $fileRevisionsCount,
114                $fileRevisionsCount,
115            ]
116        )->parseAsBlock() .
117        ( new SourceWikiCleanupSnippet(
118            $sourceEditingEnabled,
119            $sourceDeletionEnabled,
120            LoggerFactory::getInstance( 'FileImporter' )
121        ) )->getHtml( $importPlan, $this->getUser() ) .
122        Html::openElement( 'div', [ 'class' => 'mw-importfile-importOptions' ] ) .
123        $this->buildEditSummaryHtml( $importPlan ) .
124        $this->msg(
125            'fileimporter-textrevisions',
126            [
127                $textRevisionsCount,
128                $textRevisionsCount,
129            ]
130        )->parseAsBlock() .
131        Html::element(
132            'input',
133            [
134                'type' => 'hidden',
135                'name' => 'token',
136                'value' => $this->getUser()->getEditToken()
137            ]
138        ) .
139        new ButtonInputWidget(
140            [
141                'classes' => [ 'mw-importfile-import-submit' ],
142                'label' => $this->msg( 'fileimporter-import' )->plain(),
143                'type' => 'submit',
144                'flags' => [ 'primary', 'progressive' ],
145                'name' => self::ACTION_BUTTON,
146                'value' => self::ACTION_SUBMIT,
147                'infusable' => true
148            ]
149        ) .
150        ( $this->wasEdited( $importPlan ) ? $this->buildShowChangesButtonHtml() : '' ) .
151        new ButtonWidget(
152            [
153                'classes' => [ 'mw-importfile-import-cancel' ],
154                'label' => $this->msg( 'fileimporter-cancel' )->plain(),
155                'href' => $importPlan->getRequest()->getUrl()->getUrl()
156            ]
157        ) .
158        Html::element( 'span', [], $this->msg( 'fileimporter-import-wait' )->plain() ) .
159        $this->buildImportIdentityFormSnippet( $importPlan ) .
160        // End of mw-importfile-importOptions
161        Html::closeElement( 'div' ) .
162        Html::closeElement( 'form' );
163    }
164
165    private function buildShowChangesButtonHtml() {
166        return new ButtonInputWidget(
167            [
168                'classes' => [ 'mw-importfile-import-diff' ],
169                'label' => $this->msg( 'fileimporter-viewdiff' )->plain(),
170                'name' => self::ACTION_BUTTON,
171                'value' => self::ACTION_VIEW_DIFF,
172                'type' => 'submit',
173            ]
174        );
175    }
176
177    /**
178     * @return string
179     */
180    private function buildEditSummaryHtml( ImportPlan $importPlan ) {
181        $summary = $importPlan->getRequest()->getIntendedSummary();
182        if ( $summary === null ) {
183            $replacements = $importPlan->getNumberOfTemplateReplacements();
184            $summary = $replacements > 0
185                ? $this->msg(
186                    'fileimporter-auto-replacements-summary',
187                    $replacements
188                )->inContentLanguage()->text()
189                : null;
190        }
191        if ( $summary === null
192            && !$this->wasEdited( $importPlan )
193        ) {
194            return '';
195        }
196
197        return Html::element(
198            'p',
199            [],
200            $this->msg( 'fileimporter-editsummary' )->plain()
201        ) .
202        new TextInputWidget(
203            [
204                'name' => 'intendedRevisionSummary',
205                'classes' => [ 'mw-importfile-import-summary' ],
206                'value' => $summary,
207                'infusable' => true
208            ]
209        );
210    }
211
212    /**
213     * @return string
214     */
215    private function buildImportIdentityFormSnippet( ImportPlan $importPlan ) {
216        return ( new ImportIdentityFormSnippet( [
217            'clientUrl' => $importPlan->getRequest()->getUrl(),
218            'intendedFileName' => $importPlan->getFileName(),
219            'intendedWikitext' => $importPlan->getFileInfoText(),
220            'actionStats' => json_encode( $importPlan->getActionStats() ),
221            'validationWarnings' => json_encode( $importPlan->getValidationWarnings() ),
222            'importDetailsHash' => $importPlan->getDetails()->getOriginalHash(),
223        ] ) )->getHtml();
224    }
225
226    /**
227     * @return string HTML snippet for a box showing the categories, or empty string if there are
228     * no categories.
229     */
230    private function buildCategoriesSnippet( ImportPlan $importPlan ) {
231        /** @var CategoryExtractor $categoryExtractor */
232        $categoryExtractor = MediaWikiServices::getInstance()
233            ->getService( 'FileImporterCategoryExtractor' );
234        [ $visibleCategories, $hiddenCategories ] = $categoryExtractor->getCategoriesGrouped(
235            $importPlan->getFileInfoText(),
236            $importPlan->getTitle(),
237            $this->getUser()
238        );
239        return ( new CategoriesSnippet(
240            $visibleCategories,
241            $hiddenCategories
242        ) )->getHtml();
243    }
244
245    /**
246     * @return bool
247     */
248    private function wasEdited( ImportPlan $importPlan ) {
249        return $importPlan->wasFileInfoTextChanged() ||
250            $importPlan->getNumberOfTemplateReplacements() > 0;
251    }
252
253}