Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
50.00% covered (danger)
50.00%
32 / 64
36.36% covered (danger)
36.36%
4 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
TextContentHandler
50.79% covered (warning)
50.79%
32 / 63
36.36% covered (danger)
36.36%
4 / 11
62.01
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 serializeContent
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 merge3
83.33% covered (warning)
83.33%
15 / 18
0.00% covered (danger)
0.00%
0 / 1
5.12
 getContentClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 unserializeContent
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 makeEmptyContent
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 supportsDirectEditing
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFieldsForSearchIndex
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getDataForSearchIndex
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 preSaveTransform
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 fillParserOutput
39.13% covered (danger)
39.13%
9 / 23
0.00% covered (danger)
0.00%
0 / 1
7.61
1<?php
2/**
3 * Base content handler class for flat text contents.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @since 1.21
21 *
22 * @file
23 * @ingroup Content
24 */
25
26namespace MediaWiki\Content;
27
28use Content;
29use ContentHandler;
30use MediaWiki\Content\Renderer\ContentParseParams;
31use MediaWiki\Content\Transform\PreSaveTransformParams;
32use MediaWiki\MainConfigNames;
33use MediaWiki\MediaWikiServices;
34use MediaWiki\Parser\ParserOutput;
35use MediaWiki\Revision\RevisionRecord;
36use ReflectionMethod;
37use SearchEngine;
38use SearchIndexField;
39use WikiPage;
40
41/**
42 * Base content handler implementation for flat text contents.
43 *
44 * @ingroup Content
45 */
46class TextContentHandler extends ContentHandler {
47
48    public function __construct( $modelId = CONTENT_MODEL_TEXT, $formats = [ CONTENT_FORMAT_TEXT ] ) {
49        parent::__construct( $modelId, $formats );
50    }
51
52    /**
53     * Returns the content's text as-is.
54     *
55     * @param Content $content
56     * @param string|null $format The serialization format to check
57     *
58     * @return mixed
59     */
60    public function serializeContent( Content $content, $format = null ) {
61        $this->checkFormat( $format );
62
63        // @phan-suppress-next-line PhanUndeclaredMethod
64        return $content->getText();
65    }
66
67    /**
68     * Attempts to merge differences between three versions. Returns a new
69     * Content object for a clean merge and false for failure or a conflict.
70     *
71     * All three Content objects passed as parameters must have the same
72     * content model.
73     *
74     * This text-based implementation uses wfMerge().
75     *
76     * @param Content $oldContent The page's previous content.
77     * @param Content $myContent One of the page's conflicting contents.
78     * @param Content $yourContent One of the page's conflicting contents.
79     *
80     * @return Content|false
81     */
82    public function merge3( Content $oldContent, Content $myContent, Content $yourContent ) {
83        // Nothing to do when the unsaved edit is already identical to the latest revision
84        if ( $myContent->equals( $yourContent ) ) {
85            return $yourContent;
86        }
87        // Impossible to have a conflict when the user just edited the latest revision. This can
88        // happen e.g. when $wgDiff3 is badly configured.
89        if ( $oldContent->equals( $yourContent ) ) {
90            return $myContent;
91        }
92
93        $this->checkModelID( $oldContent->getModel() );
94        $this->checkModelID( $myContent->getModel() );
95        $this->checkModelID( $yourContent->getModel() );
96
97        $format = $this->getDefaultFormat();
98
99        $old = $this->serializeContent( $oldContent, $format );
100        $mine = $this->serializeContent( $myContent, $format );
101        $yours = $this->serializeContent( $yourContent, $format );
102
103        $ok = wfMerge( $old, $mine, $yours, $result );
104
105        if ( !$ok ) {
106            return false;
107        }
108
109        if ( !$result ) {
110            return $this->makeEmptyContent();
111        }
112
113        $mergedContent = $this->unserializeContent( $result, $format );
114
115        return $mergedContent;
116    }
117
118    /**
119     * Returns the name of the associated Content class, to
120     * be used when creating new objects. Override expected
121     * by subclasses.
122     *
123     * @since 1.24
124     *
125     * @return class-string<TextContent>
126     */
127    protected function getContentClass() {
128        return TextContent::class;
129    }
130
131    /**
132     * Unserializes a Content object of the type supported by this ContentHandler.
133     *
134     * @since 1.21
135     *
136     * @param string $text Serialized form of the content
137     * @param string|null $format The format used for serialization
138     *
139     * @return Content The TextContent object wrapping $text
140     */
141    public function unserializeContent( $text, $format = null ) {
142        $this->checkFormat( $format );
143
144        $class = $this->getContentClass();
145        return new $class( $text );
146    }
147
148    /**
149     * Creates an empty TextContent object.
150     *
151     * @since 1.21
152     *
153     * @return Content A new TextContent object with empty text.
154     */
155    public function makeEmptyContent() {
156        $class = $this->getContentClass();
157        return new $class( '' );
158    }
159
160    /**
161     * @see ContentHandler::supportsDirectEditing
162     *
163     * @return bool Should return true for TextContent and derivatives.
164     */
165    public function supportsDirectEditing() {
166        return true;
167    }
168
169    public function getFieldsForSearchIndex( SearchEngine $engine ) {
170        $fields = parent::getFieldsForSearchIndex( $engine );
171        $fields['language'] =
172            $engine->makeSearchFieldMapping( 'language', SearchIndexField::INDEX_TYPE_KEYWORD );
173
174        return $fields;
175    }
176
177    public function getDataForSearchIndex(
178        WikiPage $page,
179        ParserOutput $output,
180        SearchEngine $engine,
181        ?RevisionRecord $revision = null
182    ) {
183        $fields = parent::getDataForSearchIndex( $page, $output, $engine, $revision );
184        $fields['language'] =
185            $this->getPageLanguage( $page->getTitle(), $page->getContent() )->getCode();
186        return $fields;
187    }
188
189    public function preSaveTransform(
190        Content $content,
191        PreSaveTransformParams $pstParams
192    ): Content {
193        '@phan-var TextContent $content';
194
195        $text = $content->getText();
196
197        $pst = TextContent::normalizeLineEndings( $text );
198
199        $contentClass = $this->getContentClass();
200        return ( $text === $pst ) ? $content : new $contentClass( $pst, $content->getModel() );
201    }
202
203    /**
204     * Fills the provided ParserOutput object with information derived from the content.
205     * Unless $generateHtml was false, this includes an HTML representation of the content
206     * provided by getHtml().
207     *
208     * For content models listed in $wgTextModelsToParse, this method will call the MediaWiki
209     * wikitext parser on the text to extract any (wikitext) links, magic words, etc.,
210     * but note that the Table of Contents will *not* be generated
211     * (feature added by T307691, but should be refactored: T313455).
212     *
213     * Subclasses may override this to provide custom content processing.
214     * For custom HTML generation alone, it is sufficient to override getHtml().
215     *
216     * @stable to override
217     *
218     * @since 1.38
219     * @param Content $content
220     * @param ContentParseParams $cpoParams
221     * @param ParserOutput &$output The output object to fill (reference).
222     */
223    protected function fillParserOutput(
224        Content $content,
225        ContentParseParams $cpoParams,
226        ParserOutput &$output
227    ) {
228        $textModelsToParse = MediaWikiServices::getInstance()->getMainConfig()->get(
229            MainConfigNames::TextModelsToParse );
230        '@phan-var TextContent $content';
231        if ( in_array( $content->getModel(), $textModelsToParse ) ) {
232            // parse just to get links etc into the database, HTML is replaced below.
233            $output = MediaWikiServices::getInstance()->getParserFactory()->getInstance()
234                ->parse(
235                    $content->getText(),
236                    $cpoParams->getPage(),
237                    $cpoParams->getParserOptions(),
238                    true,
239                    true,
240                    $cpoParams->getRevId()
241                );
242        }
243
244        if ( $cpoParams->getGenerateHtml() ) {
245            // Temporary changes as getHtml() is deprecated, we are working on removing usage of it.
246            if ( method_exists( $content, 'getHtml' ) ) {
247                $method = new ReflectionMethod( $content, 'getHtml' );
248                $method->setAccessible( true );
249                $html = $method->invoke( $content );
250                $html = "<pre>$html</pre>";
251            } else {
252                // Return an HTML representation of the content
253                $html = htmlspecialchars( $content->getText(), ENT_COMPAT );
254                $html = "<pre>$html</pre>";
255            }
256        } else {
257            $html = null;
258        }
259
260        $output->clearWrapperDivClass();
261        $output->setRawText( $html );
262    }
263}
264/** @deprecated class alias since 1.43 */
265class_alias( TextContentHandler::class, 'TextContentHandler' );