MediaWiki  master
WikitextContent.php
Go to the documentation of this file.
1 <?php
30 
38  private $redirectTargetAndText = null;
39 
44  private $hadSignature = false;
45 
49  private $previousParseStackTrace = null;
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 = $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 ) {
296  global $wgArticleCountMethod;
297 
298  if ( $this->isRedirect() ) {
299  return false;
300  }
301 
302  if ( $wgArticleCountMethod === 'link' ) {
303  if ( $hasLinks === null ) { # not known, find out
304  // @TODO: require an injected title
305  if ( !$title ) {
306  $context = RequestContext::getMain();
307  $title = $context->getTitle();
308  }
309 
310  $po = $this->getParserOutput( $title, null, null, false );
311  $links = $po->getLinks();
312  $hasLinks = !empty( $links );
313  }
314 
315  return $hasLinks;
316  }
317 
318  return true;
319  }
320 
325  public function getTextForSummary( $maxlength = 250 ) {
326  $truncatedtext = parent::getTextForSummary( $maxlength );
327 
328  # clean up unfinished links
329  # XXX: make this optional? wasn't there in autosummary, but required for
330  # deletion summary.
331  $truncatedtext = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $truncatedtext );
332 
333  return $truncatedtext;
334  }
335 
348  protected function fillParserOutput( Title $title, $revId,
349  ParserOptions $options, $generateHtml, ParserOutput &$output
350  ) {
351  $stackTrace = ( new RuntimeException() )->getTraceAsString();
352  if ( $this->previousParseStackTrace ) {
353  // NOTE: there may be legitimate changes to re-parse the same WikiText content,
354  // e.g. if predicted revision ID for the REVISIONID magic word mismatched.
355  // But that should be rare.
356  $logger = LoggerFactory::getInstance( 'DuplicateParse' );
357  $logger->debug(
358  __METHOD__ . ': Possibly redundant parse!',
359  [
360  'title' => $title->getPrefixedDBkey(),
361  'rev' => $revId,
362  'options-hash' => $options->optionsHash(
364  $title
365  ),
366  'trace' => $stackTrace,
367  'previous-trace' => $this->previousParseStackTrace,
368  ]
369  );
370  }
371  $this->previousParseStackTrace = $stackTrace;
372 
373  list( $redir, $text ) = $this->getRedirectTargetAndText();
374  $output = MediaWikiServices::getInstance()->getParser()
375  ->parse( $text, $title, $options, true, true, $revId );
376 
377  // Add redirect indicator at the top
378  if ( $redir ) {
379  // Make sure to include the redirect link in pagelinks
380  $output->addLink( $redir );
381  if ( $generateHtml ) {
382  $chain = $this->getRedirectChain();
383  $output->setText(
384  Article::getRedirectHeaderHtml( $title->getPageLanguage(), $chain, false ) .
385  $output->getRawText()
386  );
387  $output->addModuleStyles( 'mediawiki.action.view.redirectPage' );
388  }
389  }
390 
391  // Pass along user-signature flag
392  if ( $this->hadSignature ) {
393  $output->setFlag( 'user-signature' );
394  }
395  }
396 
400  protected function getHtml() {
401  throw new MWException(
402  "getHtml() not implemented for wikitext. "
403  . "Use getParserOutput()->getText()."
404  );
405  }
406 
416  public function matchMagicWord( MagicWord $word ) {
417  return $word->match( $this->getText() );
418  }
419 
420 }
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:44
WikitextContent\$redirectTargetAndText
$redirectTargetAndText
Definition: WikitextContent.php:38
Title\newFromText
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:361
ParserOutput
Definition: ParserOutput.php:31
AbstractContent\isRedirect
isRedirect()
Definition: AbstractContent.php:379
WikitextContent\getRedirectTarget
getRedirectTarget()
Implement redirect extraction for wikitext.
Definition: WikitextContent.php:251
WikitextContent\$previousParseStackTrace
string null $previousParseStackTrace
Stack trace of the previous parse.
Definition: WikitextContent.php:49
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:166
AbstractContent\getParserOutput
getParserOutput(Title $title, $revId=null, ParserOptions $options=null, $generateHtml=true)
Returns a ParserOutput object containing information derived from this content.
Definition: AbstractContent.php:574
WikitextContent\updateRedirect
updateRedirect(Title $target)
This implementation replaces the first link on the page with the given new target if this Content obj...
Definition: WikitextContent.php:269
ParserOutput\setFlag
setFlag( $flag)
Attach a flag to the output so that it can be checked later to handle special cases.
Definition: ParserOutput.php:1068
ParserOutput\addLink
addLink(Title $title, $id=null)
Record a local or interwiki inline link for saving in future link tables.
Definition: ParserOutput.php:849
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1230
AbstractContent\getRedirectChain
getRedirectChain()
Definition: AbstractContent.php:312
WikitextContent\getRedirectTargetAndText
getRedirectTargetAndText()
Extract the redirect target and the remaining text on the page.
Definition: WikitextContent.php:201
TextContent\getText
getText()
Returns the text represented by this Content object, as a string.
Definition: TextContent.php:153
WikitextContent\$hadSignature
bool $hadSignature
Tracks if the parser set the user-signature flag when creating this content, which would make it expi...
Definition: WikitextContent.php:44
MWException
MediaWiki exception.
Definition: MWException.php:29
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
Title\getFullText
getFullText()
Get the prefixed title with spaces, plus any fragment (part beginning with '#')
Definition: Title.php:1908
TextContent\copy
copy()
Definition: TextContent.php:74
$title
$title
Definition: testCompression.php:38
WikitextContent\isCountable
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...
Definition: WikitextContent.php:295
ParserOutput\addModuleStyles
addModuleStyles( $modules)
Definition: ParserOutput.php:952
WikitextContent\__construct
__construct( $text)
Stable to call.
Definition: WikitextContent.php:56
ParserOutput\setText
setText( $text)
Definition: ParserOutput.php:724
MagicWord
This class encapsulates "magic words" such as "#redirect", NOTOC, etc.
Definition: MagicWord.php:60
WikitextContent
Content object for wiki text pages.
Definition: WikitextContent.php:37
WikitextContent\preSaveTransform
preSaveTransform(Title $title, User $user, ParserOptions $popts)
Returns a Content object with pre-save transformations applied using Parser::preSaveTransform().
Definition: WikitextContent.php:155
WikitextContent\getSection
getSection( $sectionId)
Definition: WikitextContent.php:67
WikitextContent\fillParserOutput
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...
Definition: WikitextContent.php:348
$header
$header
Definition: updateCredits.php:37
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:172
WikitextContent\matchMagicWord
matchMagicWord(MagicWord $word)
This implementation calls $word->match() on the this TextContent object's text.
Definition: WikitextContent.php:416
WikitextContent\preloadTransform
preloadTransform(Title $title, ParserOptions $popts, $params=[])
Returns a Content object with preload transformations applied (or this object if no transformations a...
Definition: WikitextContent.php:184
CONTENT_MODEL_WIKITEXT
const CONTENT_MODEL_WIKITEXT
Definition: Defines.php:218
WikitextContent\getTextForSummary
getTextForSummary( $maxlength=250)
Definition: WikitextContent.php:325
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:454
TextContent
Content object implementation for representing flat text.
Definition: TextContent.php:39
Content
Base interface for content objects.
Definition: Content.php:35
AbstractContent\getModel
getModel()
Definition: AbstractContent.php:67
Title
Represents a title within MediaWiki.
Definition: Title.php:46
WikitextContent\addSectionHeader
addSectionHeader( $header)
Returns a new WikitextContent object with the given section heading prepended.
Definition: WikitextContent.php:136
ParserOptions\allCacheVaryingOptions
static allCacheVaryingOptions()
Return all option keys that vary the options hash.
Definition: ParserOptions.php:1365
ParserOutput\getRawText
getRawText()
Get the cacheable text with <mw:editsection> markers still in it.
Definition: ParserOutput.php:316
Content\getModel
getModel()
Returns the ID of the content model used by this Content object.
WikitextContent\replaceSection
replaceSection( $sectionId, Content $with, $sectionTitle='')
Definition: WikitextContent.php:89
$wgMaxRedirects
$wgMaxRedirects
Max number of redirects to follow when resolving redirects.
Definition: DefaultSettings.php:4514
$wgArticleCountMethod
$wgArticleCountMethod
Method used to determine if a page in a content namespace should be counted as a valid article.
Definition: DefaultSettings.php:4787
WikitextContent\getHtml
getHtml()
Definition: WikitextContent.php:400
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:56
MagicWord\match
match( $text)
Returns true if the text contains the word.
Definition: MagicWord.php:246
ParserOptions\optionsHash
optionsHash( $forOptions, $title=null)
Generate a hash string with the values set on these ParserOptions for the keys given in the array.
Definition: ParserOptions.php:1407
Article\getRedirectHeaderHtml
static getRedirectHeaderHtml(Language $lang, $target, $forceKnown=false)
Return the HTML for the top of a redirect page.
Definition: Article.php:1769