MediaWiki REL1_37
WikitextContent.php
Go to the documentation of this file.
1<?php
30
38 private $redirectTargetAndText = null;
39
44
49
55 public function __construct( $text ) {
56 parent::__construct( $text, CONTENT_MODEL_WIKITEXT );
57 }
58
66 public function getSection( $sectionId ) {
67 $text = $this->getText();
68 $sect = MediaWikiServices::getInstance()->getParser()
69 ->getSection( $text, $sectionId, false );
70
71 if ( $sect === false ) {
72 return false;
73 } else {
74 return new static( $sect );
75 }
76 }
77
88 public function replaceSection( $sectionId, Content $with, $sectionTitle = '' ) {
89 $myModelId = $this->getModel();
90 $sectionModelId = $with->getModel();
91
92 if ( $sectionModelId != $myModelId ) {
93 throw new MWException( "Incompatible content model for section: " .
94 "document uses $myModelId but " .
95 "section uses $sectionModelId." );
96 }
98 '@phan-var self $with';
99
100 $oldtext = $this->getText();
101 $text = $with->getText();
102
103 if ( strval( $sectionId ) === '' ) {
104 return $with; # XXX: copy first?
105 }
106
107 if ( $sectionId === 'new' ) {
108 # Inserting a new section
109 $subject = strval( $sectionTitle ) !== '' ? wfMessage( 'newsectionheaderdefaultlevel' )
110 ->plaintextParams( $sectionTitle )->inContentLanguage()->text() . "\n\n" : '';
111 if ( Hooks::runner()->onPlaceNewSection( $this, $oldtext, $subject, $text ) ) {
112 $text = strlen( trim( $oldtext ) ) > 0
113 ? "{$oldtext}\n\n{$subject}{$text}"
114 : "{$subject}{$text}";
115 }
116 } else {
117 # Replacing an existing section; roll out the big guns
118 $text = MediaWikiServices::getInstance()->getParser()
119 ->replaceSection( $oldtext, $sectionId, $text );
120 }
121
122 $newContent = new static( $text );
123
124 return $newContent;
125 }
126
135 public function addSectionHeader( $header ) {
136 $text = wfMessage( 'newsectionheaderdefaultlevel' )
137 ->rawParams( $header )->inContentLanguage()->text();
138 $text .= "\n\n";
139 $text .= $this->getText();
140
141 return new static( $text );
142 }
143
153 protected function getRedirectTargetAndText() {
154 global $wgMaxRedirects;
155
156 if ( $this->redirectTargetAndText !== null ) {
158 }
159
160 if ( $wgMaxRedirects < 1 ) {
161 // redirects are disabled, so quit early
162 $this->redirectTargetAndText = [ null, $this->getText() ];
164 }
165
166 $redir = MediaWikiServices::getInstance()->getMagicWordFactory()->get( 'redirect' );
167 $text = ltrim( $this->getText() );
168 if ( $redir->matchStartAndRemove( $text ) ) {
169 // Extract the first link and see if it's usable
170 // Ensure that it really does come directly after #REDIRECT
171 // Some older redirects included a colon, so don't freak about that!
172 $m = [];
173 if ( preg_match( '!^\s*:?\s*\[{2}(.*?)(?:\|.*?)?\]{2}\s*!', $text, $m ) ) {
174 // Strip preceding colon used to "escape" categories, etc.
175 // and URL-decode links
176 if ( strpos( $m[1], '%' ) !== false ) {
177 // Match behavior of inline link parsing here;
178 $m[1] = rawurldecode( ltrim( $m[1], ':' ) );
179 }
180 $title = Title::newFromText( $m[1] );
181 // If the title is a redirect to bad special pages or is invalid, return null
182 if ( !$title instanceof Title || !$title->isValidRedirectTarget() ) {
183 $this->redirectTargetAndText = [ null, $this->getText() ];
185 }
186
187 $this->redirectTargetAndText = [ $title, substr( $text, strlen( $m[0] ) ) ];
189 }
190 }
191
192 $this->redirectTargetAndText = [ null, $this->getText() ];
194 }
195
203 public function getRedirectTarget() {
204 list( $title, ) = $this->getRedirectTargetAndText();
205
206 return $title;
207 }
208
221 public function updateRedirect( Title $target ) {
222 if ( !$this->isRedirect() ) {
223 return $this;
224 }
225
226 # Fix the text
227 # Remember that redirect pages can have categories, templates, etc.,
228 # so the regex has to be fairly general
229 $newText = preg_replace( '/ \[ \[ [^\]]* \] \] /x',
230 '[[' . $target->getFullText() . ']]',
231 $this->getText(), 1 );
232
233 return new static( $newText );
234 }
235
247 public function isCountable( $hasLinks = null, Title $title = null ) {
249
250 if ( $this->isRedirect() ) {
251 return false;
252 }
253
254 if ( $wgArticleCountMethod === 'link' ) {
255 if ( $hasLinks === null ) { # not known, find out
256 // @TODO: require an injected title
257 if ( !$title ) {
258 $context = RequestContext::getMain();
259 $title = $context->getTitle();
260 }
261
262 $po = $this->getParserOutput( $title, null, null, false );
263 $links = $po->getLinks();
264 $hasLinks = !empty( $links );
265 }
266
267 return $hasLinks;
268 }
269
270 return true;
271 }
272
277 public function getTextForSummary( $maxlength = 250 ) {
278 $truncatedtext = parent::getTextForSummary( $maxlength );
279
280 # clean up unfinished links
281 # XXX: make this optional? wasn't there in autosummary, but required for
282 # deletion summary.
283 $truncatedtext = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $truncatedtext );
284
285 return $truncatedtext;
286 }
287
300 protected function fillParserOutput( Title $title, $revId,
301 ParserOptions $options, $generateHtml, ParserOutput &$output
302 ) {
303 $stackTrace = ( new RuntimeException() )->getTraceAsString();
304 if ( $this->previousParseStackTrace ) {
305 // NOTE: there may be legitimate changes to re-parse the same WikiText content,
306 // e.g. if predicted revision ID for the REVISIONID magic word mismatched.
307 // But that should be rare.
308 $logger = LoggerFactory::getInstance( 'DuplicateParse' );
309 $logger->debug(
310 __METHOD__ . ': Possibly redundant parse!',
311 [
312 'title' => $title->getPrefixedDBkey(),
313 'rev' => $revId,
314 'options-hash' => $options->optionsHash(
315 ParserOptions::allCacheVaryingOptions(),
316 $title
317 ),
318 'trace' => $stackTrace,
319 'previous-trace' => $this->previousParseStackTrace,
320 ]
321 );
322 }
323 $this->previousParseStackTrace = $stackTrace;
324
325 list( $redir, $text ) = $this->getRedirectTargetAndText();
326 $output = MediaWikiServices::getInstance()->getParser()
327 ->parse( $text, $title, $options, true, true, $revId );
328
329 // Add redirect indicator at the top
330 if ( $redir ) {
331 // Make sure to include the redirect link in pagelinks
332 $output->addLink( $redir );
333 if ( $generateHtml ) {
334 $chain = $this->getRedirectChain();
335 $output->setText(
336 Article::getRedirectHeaderHtml( $title->getPageLanguage(), $chain, false ) .
337 $output->getRawText()
338 );
339 $output->addModuleStyles( 'mediawiki.action.view.redirectPage' );
340 }
341 }
342
343 // Pass along user-signature flag
344 if ( in_array( 'user-signature', $this->preSaveTransformFlags ) ) {
345 $output->setFlag( 'user-signature' );
346 }
347 }
348
352 protected function getHtml() {
353 // @phan-suppress-previous-line PhanPluginNeverReturnMethod
354 throw new MWException(
355 "getHtml() not implemented for wikitext. "
356 . "Use getParserOutput()->getText()."
357 );
358 }
359
369 public function matchMagicWord( MagicWord $word ) {
370 return $word->match( $this->getText() );
371 }
372
378 public function setPreSaveTransformFlags( array $flags ) {
379 $this->preSaveTransformFlags = $flags;
380 }
381}
$wgMaxRedirects
Max number of redirects to follow when resolving redirects.
$wgArticleCountMethod
Method used to determine if a page in a content namespace should be counted as a valid article.
const CONTENT_MODEL_WIKITEXT
Definition Defines.php:208
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
getParserOutput(Title $title, $revId=null, ParserOptions $options=null, $generateHtml=true)
Returns a ParserOutput object containing information derived from this content.
MediaWiki exception.
This class encapsulates "magic words" such as "#redirect", NOTOC, etc.
Definition MagicWord.php:60
match( $text)
Returns true if the text contains the word.
PSR-3 logger instance factory.
MediaWikiServices is the service locator for the application scope of MediaWiki.
Set options of the Parser.
optionsHash( $forOptions, $title=null)
Generate a hash string with the values set on these ParserOptions for the keys given in the array.
addLink(Title $title, $id=null)
Record a local or interwiki inline link for saving in future link tables.
getRawText()
Get the cacheable text with <mw:editsection> markers still in it.
addModuleStyles( $modules)
setFlag( $flag)
Attach a flag to the output so that it can be checked later to handle special cases.
Content object implementation for representing flat text.
getText()
Returns the text represented by this Content object, as a string.
Represents a title within MediaWiki.
Definition Title.php:48
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
Definition Title.php:1945
isValidRedirectTarget()
Check if this Title is a valid redirect target.
Definition Title.php:3896
Content object for wiki text pages.
updateRedirect(Title $target)
This implementation replaces the first link on the page with the given new target if this Content obj...
setPreSaveTransformFlags(array $flags)
Records flags set by preSaveTransform.
string[] $preSaveTransformFlags
flags set by PST
getRedirectTarget()
Implement redirect extraction for wikitext.
string null $previousParseStackTrace
Stack trace of the previous parse.
getTextForSummary( $maxlength=250)
fillParserOutput(Title $title, $revId, ParserOptions $options, $generateHtml, ParserOutput &$output)
Returns a ParserOutput object resulting from parsing the content's text using the global Parser servi...
getRedirectTargetAndText()
Extract the redirect target and the remaining text on the page.
addSectionHeader( $header)
Returns a new WikitextContent object with the given section heading prepended.
isCountable( $hasLinks=null, Title $title=null)
Returns true if this content is not a redirect, and this content's text is countable according to the...
getSection( $sectionId)
matchMagicWord(MagicWord $word)
This implementation calls $word->match() on the this TextContent object's text.
replaceSection( $sectionId, Content $with, $sectionTitle='')
Base interface for content objects.
Definition Content.php:35
getModel()
Returns the ID of the content model used by this Content object.
replaceSection( $sectionId, Content $with, $sectionTitle='')
Replaces a section of the content and returns a Content object with the section replaced.
$header