MediaWiki REL1_35
WikitextContent.php
Go to the documentation of this file.
1<?php
30
38 private $redirectTargetAndText = null;
39
44 private $hadSignature = false;
45
50
56 public function __construct( $text ) {
57 parent::__construct( $text, CONTENT_MODEL_WIKITEXT );
58 }
59
67 public function getSection( $sectionId ) {
68 $text = $this->getText();
69 $sect = MediaWikiServices::getInstance()->getParser()
70 ->getSection( $text, $sectionId, false );
71
72 if ( $sect === false ) {
73 return false;
74 } else {
75 return new static( $sect );
76 }
77 }
78
89 public function replaceSection( $sectionId, Content $with, $sectionTitle = '' ) {
90 $myModelId = $this->getModel();
91 $sectionModelId = $with->getModel();
92
93 if ( $sectionModelId != $myModelId ) {
94 throw new MWException( "Incompatible content model for section: " .
95 "document uses $myModelId but " .
96 "section uses $sectionModelId." );
97 }
99 '@phan-var self $with';
100
101 $oldtext = $this->getText();
102 $text = $with->getText();
103
104 if ( strval( $sectionId ) === '' ) {
105 return $with; # XXX: copy first?
106 }
107
108 if ( $sectionId === 'new' ) {
109 # Inserting a new section
110 $subject = strval( $sectionTitle ) !== '' ? wfMessage( 'newsectionheaderdefaultlevel' )
111 ->plaintextParams( $sectionTitle )->inContentLanguage()->text() . "\n\n" : '';
112 if ( Hooks::runner()->onPlaceNewSection( $this, $oldtext, $subject, $text ) ) {
113 $text = strlen( trim( $oldtext ) ) > 0
114 ? "{$oldtext}\n\n{$subject}{$text}"
115 : "{$subject}{$text}";
116 }
117 } else {
118 # Replacing an existing section; roll out the big guns
119 $text = MediaWikiServices::getInstance()->getParser()
120 ->replaceSection( $oldtext, $sectionId, $text );
121 }
122
123 $newContent = new static( $text );
124
125 return $newContent;
126 }
127
136 public function addSectionHeader( $header ) {
137 $text = wfMessage( 'newsectionheaderdefaultlevel' )
138 ->rawParams( $header )->inContentLanguage()->text();
139 $text .= "\n\n";
140 $text .= $this->getText();
141
142 return new static( $text );
143 }
144
155 public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
156 $text = $this->getText();
157
158 $parser = MediaWikiServices::getInstance()->getParser();
159 $pst = $parser->preSaveTransform( $text, $title, $user, $popts );
160
161 if ( $text === $pst ) {
162 return $this;
163 }
164
165 $ret = new static( $pst );
166
167 if ( $parser->getOutput()->getFlag( 'user-signature' ) ) {
168 $ret->hadSignature = true;
169 }
170
171 return $ret;
172 }
173
184 public function preloadTransform( Title $title, ParserOptions $popts, $params = [] ) {
185 $text = $this->getText();
186 $plt = MediaWikiServices::getInstance()->getParser()
187 ->getPreloadText( $text, $title, $popts, $params );
188
189 return new static( $plt );
190 }
191
201 protected function getRedirectTargetAndText() {
202 global $wgMaxRedirects;
203
204 if ( $this->redirectTargetAndText !== null ) {
206 }
207
208 if ( $wgMaxRedirects < 1 ) {
209 // redirects are disabled, so quit early
210 $this->redirectTargetAndText = [ null, $this->getText() ];
212 }
213
214 $redir = MediaWikiServices::getInstance()->getMagicWordFactory()->get( 'redirect' );
215 $text = ltrim( $this->getText() );
216 if ( $redir->matchStartAndRemove( $text ) ) {
217 // Extract the first link and see if it's usable
218 // Ensure that it really does come directly after #REDIRECT
219 // Some older redirects included a colon, so don't freak about that!
220 $m = [];
221 if ( preg_match( '!^\s*:?\s*\[{2}(.*?)(?:\|.*?)?\]{2}\s*!', $text, $m ) ) {
222 // Strip preceding colon used to "escape" categories, etc.
223 // and URL-decode links
224 if ( strpos( $m[1], '%' ) !== false ) {
225 // Match behavior of inline link parsing here;
226 $m[1] = rawurldecode( ltrim( $m[1], ':' ) );
227 }
228 $title = Title::newFromText( $m[1] );
229 // If the title is a redirect to bad special pages or is invalid, return null
230 if ( !$title instanceof Title || !$title->isValidRedirectTarget() ) {
231 $this->redirectTargetAndText = [ null, $this->getText() ];
233 }
234
235 $this->redirectTargetAndText = [ $title, substr( $text, strlen( $m[0] ) ) ];
237 }
238 }
239
240 $this->redirectTargetAndText = [ null, $this->getText() ];
242 }
243
251 public function getRedirectTarget() {
252 list( $title, ) = $this->getRedirectTargetAndText();
253
254 return $title;
255 }
256
269 public function updateRedirect( Title $target ) {
270 if ( !$this->isRedirect() ) {
271 return $this;
272 }
273
274 # Fix the text
275 # Remember that redirect pages can have categories, templates, etc.,
276 # so the regex has to be fairly general
277 $newText = preg_replace( '/ \[ \[ [^\]]* \] \] /x',
278 '[[' . $target->getFullText() . ']]',
279 $this->getText(), 1 );
280
281 return new static( $newText );
282 }
283
295 public function isCountable( $hasLinks = null, Title $title = null ) {
297
298 if ( $this->isRedirect() ) {
299 return false;
300 }
301
302 if ( $wgArticleCountMethod === 'link' ) {
303 if ( $hasLinks === null ) { # not known, find out
304 if ( !$title ) {
305 $context = RequestContext::getMain();
306 $title = $context->getTitle();
307 }
308
309 $po = $this->getParserOutput( $title, null, null, false );
310 $links = $po->getLinks();
311 $hasLinks = !empty( $links );
312 }
313
314 return $hasLinks;
315 }
316
317 return true;
318 }
319
324 public function getTextForSummary( $maxlength = 250 ) {
325 $truncatedtext = parent::getTextForSummary( $maxlength );
326
327 # clean up unfinished links
328 # XXX: make this optional? wasn't there in autosummary, but required for
329 # deletion summary.
330 $truncatedtext = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $truncatedtext );
331
332 return $truncatedtext;
333 }
334
347 protected function fillParserOutput( Title $title, $revId,
348 ParserOptions $options, $generateHtml, ParserOutput &$output
349 ) {
350 $stackTrace = ( new RuntimeException() )->getTraceAsString();
351 if ( $this->previousParseStackTrace ) {
352 // NOTE: there may be legitimate changes to re-parse the same WikiText content,
353 // e.g. if predicted revision ID for the REVISIONID magic word mismatched.
354 // But that should be rare.
355 $logger = LoggerFactory::getInstance( 'DuplicateParse' );
356 $logger->debug(
357 __METHOD__ . ': Possibly redundant parse!',
358 [
359 'title' => $title->getPrefixedDBkey(),
360 'rev' => $revId,
361 'options-hash' => $options->optionsHash(
362 ParserOptions::allCacheVaryingOptions(),
363 $title
364 ),
365 'trace' => $stackTrace,
366 'previous-trace' => $this->previousParseStackTrace,
367 ]
368 );
369 }
370 $this->previousParseStackTrace = $stackTrace;
371
372 list( $redir, $text ) = $this->getRedirectTargetAndText();
373 $output = MediaWikiServices::getInstance()->getParser()
374 ->parse( $text, $title, $options, true, true, $revId );
375
376 // Add redirect indicator at the top
377 if ( $redir ) {
378 // Make sure to include the redirect link in pagelinks
379 $output->addLink( $redir );
380 if ( $generateHtml ) {
381 $chain = $this->getRedirectChain();
382 $output->setText(
383 Article::getRedirectHeaderHtml( $title->getPageLanguage(), $chain, false ) .
384 $output->getRawText()
385 );
386 $output->addModuleStyles( 'mediawiki.action.view.redirectPage' );
387 }
388 }
389
390 // Pass along user-signature flag
391 if ( $this->hadSignature ) {
392 $output->setFlag( 'user-signature' );
393 }
394 }
395
399 protected function getHtml() {
400 throw new MWException(
401 "getHtml() not implemented for wikitext. "
402 . "Use getParserOutput()->getText()."
403 );
404 }
405
415 public function matchMagicWord( MagicWord $word ) {
416 return $word->match( $this->getText() );
417 }
418
419}
$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.
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:42
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
Definition Title.php:1883
isValidRedirectTarget()
Check if this Title is a valid redirect target.
Definition Title.php:4343
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:60
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...
getRedirectTarget()
Implement redirect extraction for wikitext.
string null $previousParseStackTrace
Stack trace of the previous parse.
getTextForSummary( $maxlength=250)
preloadTransform(Title $title, ParserOptions $popts, $params=[])
Returns a Content object with preload transformations applied (or this object if no transformations a...
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.
__construct( $text)
Stable to call.
bool $hadSignature
Tracks if the parser set the user-signature flag when creating this content, which would make it expi...
preSaveTransform(Title $title, User $user, ParserOptions $popts)
Returns a Content object with pre-save transformations applied using Parser::preSaveTransform().
replaceSection( $sectionId, Content $with, $sectionTitle='')
const CONTENT_MODEL_WIKITEXT
Definition Defines.php:225
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