36 private $linkRenderer;
38 private $linkBatchFactory;
48 private $namespaceInfo;
65 private const MAX_ID_SIZE = 7;
89 $this->linkRenderer = $linkRenderer;
90 $this->linkBatchFactory = $linkBatchFactory;
91 $this->linkCache = $linkCache;
92 $this->repoGroup = $repoGroup;
93 $this->userLang = $userLang;
94 $this->contLang = $contLang;
95 $this->titleParser = $titleParser;
96 $this->namespaceInfo = $namespaceInfo;
97 $this->hookRunner =
new HookRunner( $hookContainer );
112 $samePage =
false, $wikiId =
false, $enableSectionLinks =
true
114 return $this->preprocessInternal( $comment,
false, $selfLinkTarget,
115 $samePage, $wikiId, $enableSectionLinks );
129 $samePage =
false, $wikiId =
false, $enableSectionLinks =
true
131 return $this->preprocessInternal( $comment,
true, $selfLinkTarget,
132 $samePage, $wikiId, $enableSectionLinks );
143 $this->flushLinkBatches();
144 return preg_replace_callback(
145 '/\x1b([0-9]{' . self::MAX_ID_SIZE .
'})/',
147 $callback = $this->links[(int)$m[1]] ??
null;
151 return '<!-- MISSING -->';
167 private function preprocessInternal( $comment, $unsafe, $selfLinkTarget, $samePage, $wikiId,
172 $comment = strtr( $comment,
"\n\x1b",
" " );
177 if ( $enableSectionLinks ) {
178 $comment = $this->doSectionLinks( $comment, $selfLinkTarget, $samePage, $wikiId );
180 return $this->doWikiLinks( $comment, $selfLinkTarget, $samePage, $wikiId );
199 private function doSectionLinks(
201 $selfLinkTarget =
null,
211 $comment = preg_replace_callback(
217 '!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!',
218 function ( $match ) use ( &$append, $selfLinkTarget, $samePage, $wikiId ) {
220 $match += [
'',
'',
'',
'' ];
222 $pre = $match[1] !==
'';
224 $post = $match[3] !==
'';
227 $this->hookRunner->onFormatAutocomments(
228 $comment, $pre, $auto, $post,
232 if ( $comment !==
null ) {
236 if ( $selfLinkTarget ) {
238 # Remove links that a user may have manually put in the autosummary
239 # This could be improved by copying as much of Parser::stripSectionName as desired.
240 $section = str_replace( [
248 $sectionText = str_replace(
'[[',
'[[', $auto );
251 if ( $section !==
'' ) {
255 $sectionTitle = $selfLinkTarget->createFragmentTarget( $section );
257 $auto = $this->makeSectionLink(
259 $this->userLang->getArrow() . $this->userLang->getDirMark() . $sectionText,
265 # written summary $presep autocomment (summary )
266 $pre =
wfMessage(
'autocomment-prefix' )->inContentLanguage()->escaped();
269 # autocomment $postsep written summary ( summary)
270 $auto .=
wfMessage(
'colon-separator' )->inContentLanguage()->escaped();
273 $auto =
'<span dir="auto"><span class="autocomment">' . $auto .
'</span>';
274 $append .=
'</span>';
276 $comment = $pre . $auto;
281 return $comment . $append;
295 private function makeSectionLink(
296 LinkTarget $target, $text, $wikiId
298 if ( $wikiId !==
null && $wikiId !==
false && !$target->isExternal() ) {
302 $target->getNamespace() === 0
303 ? $target->getDBkey()
304 : $this->namespaceInfo->getCanonicalName( $target->getNamespace() ) .
305 ':' . $target->getDBkey(),
306 $target->getFragment()
312 return $this->linkRenderer->makePreloadedLink( $target,
new HtmlArmor( $text ),
'' );
333 private function doWikiLinks( $comment, $selfLinkTarget =
null, $samePage =
false, $wikiId =
false ) {
334 return preg_replace_callback(
337 \s*+ # ignore leading whitespace, the *+ quantifier disallows backtracking
338 :? # ignore optional leading colon
339 ([^[\]|]+) # 1. link target; page names cannot include [, ] or |
342 # Stop matching at ]] without relying on backtracking.
346 ([^[]*) # 3. link trail (the text up until the next link)
348 function ( $match ) use ( $selfLinkTarget, $samePage, $wikiId ) {
350 $medians .= preg_quote(
351 $this->namespaceInfo->getCanonicalName(
NS_MEDIA ),
'/' );
353 $medians .= preg_quote(
354 $this->contLang->getNsText(
NS_MEDIA ),
358 $comment = $match[0];
361 if ( strpos( $match[1],
'%' ) !==
false ) {
363 rawurldecode( $match[1] ),
364 [
'<' =>
'<',
'>' =>
'>' ]
369 if ( $match[2] !=
"" ) {
376 if ( preg_match(
'/^' . $medians .
'(.*)$/i', $match[1], $submatch ) ) {
378 $linkRegexp =
'/\[\[(.*?)\]\]/';
379 $linkTarget = $this->titleParser->makeTitleValueSafe(
NS_FILE, $submatch[1] );
381 $linkMarker = $this->addFileLink( $linkTarget, $text );
386 if ( isset( $match[1][0] ) && $match[1][0] ==
':' ) {
387 $match[1] = substr( $match[1], 1 );
390 if ( $match[1] !==
false && $match[1] !==
null && $match[1] !==
'' ) {
392 $this->contLang->linkTrail(),
396 $trail = $submatch[1];
400 $linkRegexp =
'/\[\[(.*?)\]\]' . preg_quote( $trail,
'/' ) .
'/';
407 $target = $this->titleParser->parseTitle( $linkTarget );
409 if ( $target->getText() ==
'' && !$target->isExternal()
410 && !$samePage && $selfLinkTarget
412 $target = $selfLinkTarget->createFragmentTarget( $target->getFragment() );
415 $linkMarker = $this->addPageLink( $target, $linkText . $inside, $wikiId );
416 $linkMarker .= $trail;
424 $comment = preg_replace(
446 private function addLinkMarker( $callback ) {
447 $nextId = count( $this->links );
448 if ( strlen( (
string)$nextId ) > self::MAX_ID_SIZE ) {
449 throw new \RuntimeException(
'Too many links in comment batch' );
451 $this->links[] = $callback;
452 return sprintf(
"\x1b%0" . self::MAX_ID_SIZE .
'd', $nextId );
464 private function addPageLink( LinkTarget $target, $text, $wikiId ) {
465 if ( $wikiId !==
null && $wikiId !==
false && !$target->isExternal() ) {
470 $target->getNamespace() === 0
471 ? $target->getDBkey()
472 : $this->namespaceInfo->getCanonicalName( $target->getNamespace() ) .
473 ':' . $target->getDBkey(),
474 $target->getFragment()
479 } elseif ( $this->linkCache->getGoodLinkID( $target ) ||
483 return $this->linkRenderer->makeKnownLink( $target,
new HtmlArmor( $text ) );
484 } elseif ( $this->linkCache->isBadLink( $target ) ) {
486 return $this->linkRenderer->makeBrokenLink( $target,
new HtmlArmor( $text ) );
490 if ( !$this->linkBatch ) {
491 $this->linkBatch = $this->linkBatchFactory->newLinkBatch();
492 $this->linkBatch->setCaller( __METHOD__ );
494 $this->linkBatch->addObj( $target );
495 return $this->addLinkMarker(
function () use ( $target, $text ) {
496 return $this->linkRenderer->makeLink( $target,
new HtmlArmor( $text ) );
507 private function addFileLink( LinkTarget $target, $html ) {
508 $this->fileBatch[] = [
511 return $this->addLinkMarker(
function () use ( $target, $html ) {
514 $this->files[$target->getDBkey()] ??
false,
523 private function flushLinkBatches() {
524 if ( $this->linkBatch ) {
525 $this->linkBatch->execute();
526 $this->linkBatch =
null;
528 if ( $this->fileBatch ) {
529 $this->files += $this->repoGroup->findFiles( $this->fileBatch );
530 $this->fileBatch = [];
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Implements some public methods and some protected utility functions which are required by multiple ch...
Marks HTML that shouldn't be escaped.
Base class for language-specific code.
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Cache for article titles (prefixed DB keys) and ids linked from one source.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
PHP Parser - Processes wiki markup (which uses a more user-friendly syntax, such as "[[link]]" for ma...
static guessSectionNameFromStrippedText( $text)
Like guessSectionNameFromWikiText(), but takes already-stripped text as input.
Prioritized list of file repositories.
static escapeHtmlAllowEntities( $html)
Given HTML input, escape with htmlspecialchars but un-escape entities.
A collection of static methods to play with strings.
static escapeRegexReplacement( $string)
Escape a string to make it suitable for inclusion in a preg_replace() replacement parameter.
Represents a page (or page fragment) title within MediaWiki.
A title parser service for MediaWiki.