diff --git a/autoload.php b/autoload.php
index d46d2cedcbd..58fcff3768e 100644
--- a/autoload.php
+++ b/autoload.php
@@ -1397,6 +1397,7 @@ $wgAutoloadLocalClasses = [
'TempFSFile' => __DIR__ . '/includes/filebackend/TempFSFile.php',
'TempFileRepo' => __DIR__ . '/includes/filerepo/FileRepo.php',
'TemplateParser' => __DIR__ . '/includes/TemplateParser.php',
+ 'TemplatesOnThisPageFormatter' => __DIR__ . '/includes/TemplatesOnThisPageFormatter.php',
'TestFileOpPerformance' => __DIR__ . '/maintenance/fileOpPerfTest.php',
'TextContent' => __DIR__ . '/includes/content/TextContent.php',
'TextContentHandler' => __DIR__ . '/includes/content/TextContentHandler.php',
diff --git a/includes/DummyLinker.php b/includes/DummyLinker.php
index ba24799818b..89a735e73e8 100644
--- a/includes/DummyLinker.php
+++ b/includes/DummyLinker.php
@@ -453,12 +453,17 @@ class DummyLinker {
);
}
+ /**
+ * @deprecated since 1.28, use TemplatesOnThisPageFormatter directly
+ */
public function formatTemplates(
$templates,
$preview = false,
$section = false,
$more = null
) {
+ wfDeprecated( __METHOD__, '1.28' );
+
return Linker::formatTemplates(
$templates,
$preview,
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 140cd7236a5..9d329e8ceba 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -21,6 +21,7 @@
*/
use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
/**
* The edit page/HTML interface (split from Article)
@@ -747,8 +748,7 @@ class EditPage {
$this->showTextbox( $text, 'wpTextbox1', [ 'readonly' ] );
$wgOut->addHTML( $this->editFormTextAfterContent );
- $wgOut->addHTML( Html::rawElement( 'div', [ 'class' => 'templatesUsed' ],
- Linker::formatTemplates( $this->getTemplates() ) ) );
+ $wgOut->addHTML( $this->makeTemplatesOnThisPageList( $this->getTemplates() ) );
$wgOut->addModules( 'mediawiki.action.edit.collapsibleFooter' );
@@ -2752,8 +2752,7 @@ class EditPage {
$wgOut->addHTML( $this->editFormTextAfterTools . "\n" );
- $wgOut->addHTML( Html::rawElement( 'div', [ 'class' => 'templatesUsed' ],
- Linker::formatTemplates( $this->getTemplates(), $this->preview, $this->section != '' ) ) );
+ $wgOut->addHTML( $this->makeTemplatesOnThisPageList( $this->getTemplates() ) );
$wgOut->addHTML( Html::rawElement( 'div', [ 'class' => 'hiddencats' ],
Linker::formatHiddenCategories( $this->page->getHiddenCategories() ) ) );
@@ -2802,6 +2801,32 @@ class EditPage {
}
+ /**
+ * Wrapper around TemplatesOnThisPageFormatter to make
+ * a "templates on this page" list.
+ *
+ * @param Title[] $templates
+ * @return string HTML
+ */
+ protected function makeTemplatesOnThisPageList( array $templates ) {
+ $templateListFormatter = new TemplatesOnThisPageFormatter(
+ $this->context, MediaWikiServices::getInstance()->getLinkRenderer()
+ );
+
+ // preview if preview, else section if section, else false
+ $type = false;
+ if ( $this->preview ) {
+ $type = 'preview';
+ } elseif ( $this->section != '' ) {
+ $type = 'section';
+ }
+
+ return Html::rawElement( 'div', [ 'class' => 'templatesUsed' ],
+ $templateListFormatter->format( $templates, $type )
+ );
+
+ }
+
/**
* Extract the section title from current section text, if any.
*
diff --git a/includes/Linker.php b/includes/Linker.php
index bcc348e7d64..eae49e7464f 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -1919,6 +1919,8 @@ class Linker {
}
/**
+ * @deprecated since 1.28, use TemplatesOnThisPageFormatter directly
+ *
* Returns HTML for the "templates used on this page" list.
*
* Make an HTML list of templates, and then add a "More..." link at
@@ -1937,87 +1939,24 @@ class Linker {
public static function formatTemplates( $templates, $preview = false,
$section = false, $more = null
) {
- global $wgLang;
+ wfDeprecated( __METHOD__, '1.28' );
- $outText = '';
- if ( count( $templates ) > 0 ) {
- # Do a batch existence check
- $batch = new LinkBatch;
- foreach ( $templates as $title ) {
- $batch->addObj( $title );
- }
- $batch->execute();
-
- # Construct the HTML
- $outText = '
';
- if ( $preview ) {
- $outText .= wfMessage( 'templatesusedpreview' )->numParams( count( $templates ) )
- ->parseAsBlock();
- } elseif ( $section ) {
- $outText .= wfMessage( 'templatesusedsection' )->numParams( count( $templates ) )
- ->parseAsBlock();
- } else {
- $outText .= wfMessage( 'templatesused' )->numParams( count( $templates ) )
- ->parseAsBlock();
- }
- $outText .= "
\n";
-
- usort( $templates, 'Title::compare' );
- foreach ( $templates as $titleObj ) {
- $protected = '';
- $restrictions = $titleObj->getRestrictions( 'edit' );
- if ( $restrictions ) {
- // Check backwards-compatible messages
- $msg = null;
- if ( $restrictions === [ 'sysop' ] ) {
- $msg = wfMessage( 'template-protected' );
- } elseif ( $restrictions === [ 'autoconfirmed' ] ) {
- $msg = wfMessage( 'template-semiprotected' );
- }
- if ( $msg && !$msg->isDisabled() ) {
- $protected = $msg->parse();
- } else {
- // Construct the message from restriction-level-*
- // e.g. restriction-level-sysop, restriction-level-autoconfirmed
- $msgs = [];
- foreach ( $restrictions as $r ) {
- $msgs[] = wfMessage( "restriction-level-$r" )->parse();
- }
- $protected = wfMessage( 'parentheses' )
- ->rawParams( $wgLang->commaList( $msgs ) )->escaped();
- }
- }
- if ( $titleObj->quickUserCan( 'edit' ) ) {
- $editLink = self::link(
- $titleObj,
- wfMessage( 'editlink' )->escaped(),
- [],
- [ 'action' => 'edit' ]
- );
- } else {
- $editLink = self::link(
- $titleObj,
- wfMessage( 'viewsourcelink' )->escaped(),
- [],
- [ 'action' => 'edit' ]
- );
- }
- $outText .= '- ' . self::link( $titleObj )
- . wfMessage( 'word-separator' )->escaped()
- . wfMessage( 'parentheses' )->rawParams( $editLink )->escaped()
- . wfMessage( 'word-separator' )->escaped()
- . $protected . '
';
- }
-
- if ( $more instanceof Title ) {
- $outText .= '- ' . self::link( $more, wfMessage( 'moredotdotdot' ) ) . '
';
- } elseif ( $more ) {
- $outText .= "- $more
";
- }
-
- $outText .= '
';
+ $type = false;
+ if ( $preview ) {
+ $type = 'preview';
+ } elseif ( $section ) {
+ $type = 'section';
}
- return $outText;
+
+ if ( $more instanceof Message ) {
+ $more = $more->toString();
+ }
+
+ $formatter = new TemplatesOnThisPageFormatter(
+ RequestContext::getMain(),
+ MediaWikiServices::getInstance()->getLinkRenderer()
+ );
+ return $formatter->format( $templates, $type, $more );
}
/**
diff --git a/includes/TemplatesOnThisPageFormatter.php b/includes/TemplatesOnThisPageFormatter.php
new file mode 100644
index 00000000000..c0ae374a05f
--- /dev/null
+++ b/includes/TemplatesOnThisPageFormatter.php
@@ -0,0 +1,183 @@
+context = $context;
+ $this->linkRenderer = $linkRenderer;
+ }
+
+ /**
+ * Make an HTML list of templates, and then add a "More..." link at
+ * the bottom. If $more is null, do not add a "More..." link. If $more
+ * is a LinkTarget, make a link to that title and use it. If $more is a string,
+ * directly paste it in as the link (escaping needs to be done manually).
+ *
+ * @param LinkTarget[] $templates
+ * @param string|bool $type 'preview' if a preview, 'section' if a section edit, false if neither
+ * @param LinkTarget|string|null $more An escaped link for "More..." of the templates
+ * @return string HTML output
+ */
+ public function format( array $templates, $type = false, $more = null ) {
+ if ( !$templates ) {
+ // No templates
+ return '';
+ }
+
+ # Do a batch existence check
+ $batch = new LinkBatch;
+ foreach ( $templates as $title ) {
+ $batch->addObj( $title );
+ }
+ $batch->execute();
+
+ # Construct the HTML
+ $outText = '';
+ $count = count( $templates );
+ if ( $type === 'preview' ) {
+ $outText .= $this->context->msg( 'templatesusedpreview' )->numParams( $count )
+ ->parseAsBlock();
+ } elseif ( $type === 'section' ) {
+ $outText .= $this->context->msg( 'templatesusedsection' )->numParams( $count )
+ ->parseAsBlock();
+ } else {
+ $outText .= $this->context->msg( 'templatesused' )->numParams( $count )
+ ->parseAsBlock();
+ }
+ $outText .= "
\n";
+
+ usort( $templates, 'Title::compare' );
+ foreach ( $templates as $template ) {
+ $outText .= $this->formatTemplate( $template );
+ }
+
+ if ( $more instanceof LinkTarget ) {
+ $outText .= Html::rawElement( 'li', $this->linkRenderer->makeLink(
+ $more, $this->context->msg( 'moredotdotdot' )->text() ) );
+ } elseif ( $more ) {
+ // Documented as should already be escaped
+ $outText .= Html::rawElement( 'li', $more );
+ }
+
+ $outText .= '
';
+ return $outText;
+ }
+
+ /**
+ * Builds an item for an individual template
+ *
+ * @param LinkTarget $target
+ * @return string
+ */
+ private function formatTemplate( LinkTarget $target ) {
+ // TODO Would be nice if we didn't have to use Title here
+ $titleObj = Title::newFromLinkTarget( $target );
+ $protected = $this->getRestrictionsText( $titleObj->getRestrictions( 'edit' ) );
+ $editLink = $this->buildEditLink( $titleObj );
+ return '' . $this->linkRenderer->makeLink( $target )
+ . $this->context->msg( 'word-separator' )->escaped()
+ . $this->context->msg( 'parentheses' )->rawParams( $editLink )->escaped()
+ . $this->context->msg( 'word-separator' )->escaped()
+ . $protected . '';
+ }
+
+ /**
+ * If the page is protected, get the relevant text
+ * for those restrictions
+ *
+ * @param array $restrictions
+ * @return string
+ */
+ private function getRestrictionsText( array $restrictions ) {
+ $protected = '';
+ if ( !$restrictions ) {
+ return $protected;
+ }
+
+ // Check backwards-compatible messages
+ $msg = null;
+ if ( $restrictions === [ 'sysop' ] ) {
+ $msg = $this->context->msg( 'template-protected' );
+ } elseif ( $restrictions === [ 'autoconfirmed' ] ) {
+ $msg = $this->context->msg( 'template-semiprotected' );
+ }
+ if ( $msg && !$msg->isDisabled() ) {
+ $protected = $msg->parse();
+ } else {
+ // Construct the message from restriction-level-*
+ // e.g. restriction-level-sysop, restriction-level-autoconfirmed
+ $msgs = [];
+ foreach ( $restrictions as $r ) {
+ $msgs[] = $this->context->msg( "restriction-level-$r" )->parse();
+ }
+ $protected = $this->context->msg( 'parentheses' )
+ ->rawParams( $this->context->getLanguage()->commaList( $msgs ) )->escaped();
+ }
+
+ return $protected;
+ }
+
+ /**
+ * Return a link to the edit page, with the text
+ * saying "view source" if the user can't edit the page
+ *
+ * @param Title $titleObj
+ * @return string
+ */
+ private function buildEditLink( Title $titleObj ) {
+ if ( $titleObj->quickUserCan( 'edit', $this->context->getUser() ) ) {
+ $linkMsg = 'editlink';
+ } else {
+ $linkMsg = 'viewsourcelink';
+ }
+
+ return $this->linkRenderer->makeLink(
+ $titleObj,
+ $this->context->msg( $linkMsg )->text(),
+ [],
+ [ 'action' => 'edit' ]
+ );
+ }
+
+}
diff --git a/includes/actions/InfoAction.php b/includes/actions/InfoAction.php
index abc7cb2f46e..4d80a1cd332 100644
--- a/includes/actions/InfoAction.php
+++ b/includes/actions/InfoAction.php
@@ -631,14 +631,15 @@ class InfoAction extends FormlessAction {
$more = null;
}
+ $templateListFormatter = new TemplatesOnThisPageFormatter(
+ $this->getContext(),
+ $linkRenderer
+ );
+
$pageInfo['header-properties'][] = [
$this->msg( 'pageinfo-templates' )
->numParams( $pageCounts['transclusion']['from'] ),
- Linker::formatTemplates(
- $transcludedTemplates,
- false,
- false,
- $more )
+ $templateListFormatter->format( $transcludedTemplates, false, $more )
];
}
@@ -654,14 +655,15 @@ class InfoAction extends FormlessAction {
$more = null;
}
+ $templateListFormatter = new TemplatesOnThisPageFormatter(
+ $this->getContext(),
+ $linkRenderer
+ );
+
$pageInfo['header-properties'][] = [
$this->msg( 'pageinfo-transclusions' )
->numParams( $pageCounts['transclusion']['to'] ),
- Linker::formatTemplates(
- $transcludedTargets,
- false,
- false,
- $more )
+ $templateListFormatter->format( $transcludedTargets, false, $more )
];
}
}