* @file
* @ingroup Parser
*/
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
/**
* @defgroup Parser Parser
*
* Must not consist of all title characters, or else it will change
* the behavior of <nowiki> in a link.
+ *
+ * Must have a character that needs escaping in attributes, otherwise
+ * someone could put a strip marker in an attribute, to get around
+ * escaping quote marks, and break out of the attribute. Thus we add
+ * `'".
*/
- const MARKER_SUFFIX = "-QINU\x7f";
- const MARKER_PREFIX = "\x7fUNIQ-";
+ const MARKER_SUFFIX = "-QINU`\"'\x7f";
+ const MARKER_PREFIX = "\x7f'\"`UNIQ-";
# Markers used for wrapping the table of contents
const TOC_START = '<mw:toc>';
/** @var SectionProfiler */
protected $mProfiler;
+ /**
+ * @var LinkRenderer
+ */
+ protected $mLinkRenderer;
+
/**
* @param array $conf
*/
return $this->mPreprocessor;
}
+ /**
+ * Get a LinkRenderer instance to make links with
+ *
+ * @since 1.28
+ * @return LinkRenderer
+ */
+ public function getLinkRenderer() {
+ if ( !$this->mLinkRenderer ) {
+ $this->mLinkRenderer = MediaWikiServices::getInstance()
+ ->getLinkRendererFactory()->create();
+ $this->mLinkRenderer->setStubThreshold(
+ $this->getOptions()->getStubThreshold()
+ );
+ }
+
+ return $this->mLinkRenderer;
+ }
+
/**
* Replaces all occurrences of HTML-style comments and the given tags
* in the text with a random marker and returns the next text. The output
substr( $m[0], 0, 20 ) . '"' );
}
$url = wfMessage( $urlmsg, $id )->inContentLanguage()->text();
- return Linker::makeExternalLink( $url, "{$keyword} {$id}", true, $cssClass );
+ return Linker::makeExternalLink( $url, "{$keyword} {$id}", true, $cssClass, [], $this->mTitle );
} elseif ( isset( $m[6] ) && $m[6] !== '' ) {
# ISBN
$isbn = $m[6];
$text = Linker::makeExternalLink( $url,
$this->getConverterLanguage()->markNoConversion( $url, true ),
true, 'free',
- $this->getExternalLinkAttribs( $url ) );
+ $this->getExternalLinkAttribs( $url ), $this->mTitle );
# Register it in the output object...
# Replace unnecessary URL escape codes with their equivalent characters
$pasteurized = self::normalizeLinkUrl( $url );
# Funny characters like รถ aren't valid in URLs anyway
# This was changed in August 2004
$s .= Linker::makeExternalLink( $url, $text, false, $linktype,
- $this->getExternalLinkAttribs( $url ) ) . $dtrail . $trail;
+ $this->getExternalLinkAttribs( $url ), $this->mTitle ) . $dtrail . $trail;
# Register link in the output object.
# Replace unnecessary URL escape codes with the referenced character
* (depending on configuration, namespace, and the URL's domain) and/or a
* target attribute (depending on configuration).
*
- * @param string|bool $url Optional URL, to extract the domain from for rel =>
+ * @param string $url URL to extract the domain from for rel =>
* nofollow if appropriate
* @return array Associative array of HTML attributes
*/
- public function getExternalLinkAttribs( $url = false ) {
+ public function getExternalLinkAttribs( $url ) {
$attribs = [];
- $attribs['rel'] = self::getExternalLinkRel( $url, $this->mTitle );
-
- if ( $this->mOptions->getExternalLinkTarget() ) {
- $attribs['target'] = $this->mOptions->getExternalLinkTarget();
+ $rel = self::getExternalLinkRel( $url, $this->mTitle );
+
+ $target = $this->mOptions->getExternalLinkTarget();
+ if ( $target ) {
+ $attribs['target'] = $target;
+ if ( !in_array( $target, [ '_self', '_parent', '_top' ] ) ) {
+ // T133507. New windows can navigate parent cross-origin.
+ // Including noreferrer due to lacking browser
+ // support of noopener. Eventually noreferrer should be removed.
+ if ( $rel !== '' ) {
+ $rel .= ' ';
+ }
+ $rel .= 'noreferrer noopener';
+ }
}
+ $attribs['rel'] = $rel;
return $attribs;
}
# batch file existence checks for NS_FILE and NS_MEDIA
if ( $iw == '' && $nt->isAlwaysKnown() ) {
$this->mOutput->addLink( $nt );
- $s .= $this->makeKnownLinkHolder( $nt, $text, [], $trail, $prefix );
+ $s .= $this->makeKnownLinkHolder( $nt, $text, $trail, $prefix );
} else {
# Links will be added to the output link list after checking
$s .= $holders->makeHolder( $nt, $text, [], $trail, $prefix );
*
* @param Title $nt
* @param string $text
- * @param array|string $query
* @param string $trail
* @param string $prefix
* @return string HTML-wikitext mix oh yuck
*/
- public function makeKnownLinkHolder( $nt, $text = '', $query = [], $trail = '', $prefix = '' ) {
+ protected function makeKnownLinkHolder( $nt, $text = '', $trail = '', $prefix = '' ) {
list( $inside, $trail ) = Linker::splitTrail( $trail );
- if ( is_string( $query ) ) {
- $query = wfCgiToArray( $query );
- }
if ( $text == '' ) {
$text = htmlspecialchars( $nt->getPrefixedText() );
}
- $link = Linker::linkKnown( $nt, "$prefix$text$inside", [], $query );
+ $link = $this->getLinkRenderer()->makeKnownLink(
+ $nt, new HtmlArmor( "$prefix$text$inside" )
+ );
return $this->armorLinks( $link ) . $trail;
}
break;
case 'revisionuser':
# Let the edit saving system know we should parse the page
- # *after* a revision ID has been assigned. This is for null edits.
- $this->mOutput->setFlag( 'vary-revision' );
- wfDebug( __METHOD__ . ": {{REVISIONUSER}} used, setting vary-revision...\n" );
+ # *after* a revision ID has been assigned for null edits.
+ $this->mOutput->setFlag( 'vary-user' );
+ wfDebug( __METHOD__ . ": {{REVISIONUSER}} used, setting vary-user...\n" );
$value = $this->getRevisionUser();
break;
case 'revisionsize':
- # Let the edit saving system know we should parse the page
- # *after* a revision ID has been assigned. This is for null edits.
- $this->mOutput->setFlag( 'vary-revision' );
- wfDebug( __METHOD__ . ": {{REVISIONSIZE}} used, setting vary-revision...\n" );
$value = $this->getRevisionSize();
break;
case 'namespace':
&& $this->mOptions->getAllowSpecialInclusion()
&& $this->ot['html']
) {
+ $specialPage = SpecialPageFactory::getPage( $title->getDBkey() );
// Pass the template arguments as URL parameters.
// "uselang" will have no effect since the Language object
// is forced to the one defined in ParserOptions.
$context = new RequestContext;
$context->setTitle( $title );
$context->setRequest( new FauxRequest( $pageArgs ) );
- $context->setUser( $this->getUser() );
+ if ( $specialPage && $specialPage->maxIncludeCacheTime() === 0 ) {
+ $context->setUser( $this->getUser() );
+ } else {
+ // If this page is cached, then we better not be per user.
+ $context->setUser( User::newFromName( '127.0.0.1', false ) );
+ }
$context->setLanguage( $this->mOptions->getUserLangObj() );
$ret = SpecialPageFactory::capturePath( $title, $context );
if ( $ret ) {
$this->mOutput->addOutputPageMetadata( $context->getOutput() );
$found = true;
$isHTML = true;
- $this->disableCache();
+ if ( $specialPage && $specialPage->maxIncludeCacheTime() !== false ) {
+ $this->mOutput->updateCacheExpiry( $specialPage->maxIncludeCacheTime() );
+ }
}
} elseif ( MWNamespace::isNonincludable( $title->getNamespace() ) ) {
$found = false; # access denied
# will change the size.
if ( $revObject ) {
$this->mRevisionSize = $revObject->getSize();
- } elseif ( $this->ot['wiki'] || $this->mOptions->getIsPreview() ) {
+ } else {
$this->mRevisionSize = $this->mInputSize;
}
}