Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 72
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
TemplatesOnThisPageFormatter
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 5
380
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 format
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
56
 formatTemplate
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 getRestrictionsText
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
56
 buildEditLink
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\EditPage;
22
23use MediaWiki\Cache\LinkBatchFactory;
24use MediaWiki\Context\IContextSource;
25use MediaWiki\Html\Html;
26use MediaWiki\Linker\LinkRenderer;
27use MediaWiki\Page\PageIdentity;
28use MediaWiki\Page\PageReference;
29use MediaWiki\Permissions\RestrictionStore;
30use MediaWiki\Title\Title;
31
32/**
33 * Handles formatting for the "templates used on this page"
34 * lists. Formerly known as Linker::formatTemplates()
35 *
36 * @since 1.28
37 */
38class TemplatesOnThisPageFormatter {
39
40    /**
41     * @var IContextSource
42     */
43    private $context;
44
45    /**
46     * @var LinkRenderer
47     */
48    private $linkRenderer;
49
50    /**
51     * @var LinkBatchFactory
52     */
53    private $linkBatchFactory;
54
55    /**
56     * @var RestrictionStore
57     */
58    private $restrictionStore;
59
60    /**
61     * @param IContextSource $context
62     * @param LinkRenderer $linkRenderer
63     * @param LinkBatchFactory $linkBatchFactory
64     * @param RestrictionStore $restrictionStore
65     */
66    public function __construct(
67        IContextSource $context,
68        LinkRenderer $linkRenderer,
69        LinkBatchFactory $linkBatchFactory,
70        RestrictionStore $restrictionStore
71    ) {
72        $this->context = $context;
73        $this->linkRenderer = $linkRenderer;
74        $this->linkBatchFactory = $linkBatchFactory;
75        $this->restrictionStore = $restrictionStore;
76    }
77
78    /**
79     * Make an HTML list of templates, and then add a "More..." link at
80     * the bottom. If $more is null, do not add a "More..." link. If $more
81     * is a PageReference, make a link to that page and use it. If $more is a string,
82     * directly paste it in as the link (escaping needs to be done manually).
83     *
84     * @param PageIdentity[] $templates
85     * @param string|false $type 'preview' if a preview, 'section' if a section edit, false if neither
86     * @param PageReference|string|null $more An escaped link for "More..." of the templates
87     * @return string HTML output
88     */
89    public function format( array $templates, $type = false, $more = null ) {
90        if ( !$templates ) {
91            // No templates
92            return '';
93        }
94
95        # Do a batch existence check
96        $batch = $this->linkBatchFactory->newLinkBatch( $templates );
97        $batch->setCaller( __METHOD__ );
98        $batch->execute();
99
100        # Construct the HTML
101        $outText = Html::openElement( 'div', [ 'class' => 'mw-templatesUsedExplanation' ] );
102        $count = count( $templates );
103        if ( $type === 'preview' ) {
104            $outText .= $this->context->msg( 'templatesusedpreview' )->numParams( $count )
105                ->parseAsBlock();
106        } elseif ( $type === 'section' ) {
107            $outText .= $this->context->msg( 'templatesusedsection' )->numParams( $count )
108                ->parseAsBlock();
109        } else {
110            $outText .= $this->context->msg( 'templatesused' )->numParams( $count )
111                ->parseAsBlock();
112        }
113        $outText .= Html::closeElement( 'div' ) . Html::openElement( 'ul' ) . "\n";
114
115        usort( $templates, [ Title::class, 'compare' ] );
116        foreach ( $templates as $template ) {
117            $outText .= $this->formatTemplate( $template );
118        }
119
120        if ( $more instanceof PageReference ) {
121            $outText .= Html::rawElement( 'li', [],
122                $this->linkRenderer->makeLink(
123                    $more,
124                    $this->context->msg( 'moredotdotdot' )->text()
125                )
126            );
127        } elseif ( $more ) {
128            // Documented as should already be escaped
129            $outText .= Html::rawElement( 'li', [], $more );
130        }
131
132        $outText .= Html::closeElement( 'ul' );
133        return $outText;
134    }
135
136    /**
137     * Builds a list item for an individual template
138     *
139     * The output of this is repeated for live-preview in resources/src/mediawiki.page.preview.js
140     *
141     * @param PageIdentity $target
142     * @return string
143     */
144    private function formatTemplate( PageIdentity $target ) {
145        if ( !$target->canExist() ) {
146            return Html::rawElement( 'li', [], $this->linkRenderer->makeLink( $target ) );
147        }
148
149        $protected = $this->getRestrictionsText(
150            $this->restrictionStore->getRestrictions( $target, 'edit' )
151        );
152        $editLink = $this->buildEditLink( $target );
153        return Html::rawElement( 'li', [], $this->linkRenderer->makeLink( $target )
154            . $this->context->msg( 'word-separator' )->escaped()
155            . $this->context->msg( 'parentheses' )->rawParams( $editLink )->escaped()
156            . $this->context->msg( 'word-separator' )->escaped()
157            . $protected
158        );
159    }
160
161    /**
162     * If the page is protected, get the relevant text
163     * for those restrictions
164     *
165     * @param array $restrictions
166     * @return string HTML
167     */
168    private function getRestrictionsText( array $restrictions ) {
169        $protected = '';
170        if ( !$restrictions ) {
171            return $protected;
172        }
173
174        // Check backwards-compatible messages
175        $msg = null;
176        if ( $restrictions === [ 'sysop' ] ) {
177            $msg = $this->context->msg( 'template-protected' );
178        } elseif ( $restrictions === [ 'autoconfirmed' ] ) {
179            $msg = $this->context->msg( 'template-semiprotected' );
180        }
181        if ( $msg && !$msg->isDisabled() ) {
182            $protected = $msg->parse();
183        } else {
184            // Construct the message from restriction-level-*
185            // e.g. restriction-level-sysop, restriction-level-autoconfirmed
186            $msgs = [];
187            foreach ( $restrictions as $r ) {
188                $msgs[] = $this->context->msg( "restriction-level-$r" )->parse();
189            }
190            $protected = $this->context->msg( 'parentheses' )
191                ->rawParams( $this->context->getLanguage()->commaList( $msgs ) )->escaped();
192        }
193
194        return $protected;
195    }
196
197    /**
198     * Return a link to the edit page, with the text
199     * saying "view source" if the user can't edit the page
200     *
201     * @param PageIdentity $page
202     * @return string HTML
203     */
204    private function buildEditLink( PageIdentity $page ) {
205        if ( $this->context->getAuthority()->probablyCan( 'edit', $page ) ) {
206            $linkMsg = 'editlink';
207        } else {
208            $linkMsg = 'viewsourcelink';
209        }
210
211        return $this->linkRenderer->makeLink(
212            $page,
213            $this->context->msg( $linkMsg )->text(),
214            [],
215            [ 'action' => 'edit' ]
216        );
217    }
218
219}
220
221/** @deprecated class alias since 1.40 */
222class_alias( TemplatesOnThisPageFormatter::class, 'TemplatesOnThisPageFormatter' );