MediaWiki REL1_37
LinkRenderer.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Linker;
22
23use Html;
24use HtmlArmor;
25use LinkCache;
31use Sanitizer;
32use Title;
34use TitleValue;
35use Wikimedia\Assert\Assert;
36
44
50 private $forceArticlePath = false;
51
57 private $expandUrls = false;
58
63
67 private $linkCache;
68
72 private $nsInfo;
73
76
78 private $hookRunner;
79
84
93 public function __construct(
99 ) {
100 $this->titleFormatter = $titleFormatter;
101 $this->linkCache = $linkCache;
102 $this->nsInfo = $nsInfo;
103 $this->specialPageFactory = $specialPageFactory;
104 $this->hookContainer = $hookContainer;
105 $this->hookRunner = new HookRunner( $hookContainer );
106 }
107
111 public function setForceArticlePath( $force ) {
112 $this->forceArticlePath = $force;
113 }
114
118 public function getForceArticlePath() {
120 }
121
125 public function setExpandURLs( $expand ) {
126 $this->expandUrls = $expand;
127 }
128
132 public function getExpandURLs() {
133 return $this->expandUrls;
134 }
135
140 public function setStubThreshold( $threshold ) {
141 wfDeprecated( __METHOD__, '1.37' );
142 }
143
148 public function getStubThreshold() {
149 wfDeprecated( __METHOD__, '1.37' );
150 return 0;
151 }
152
160 public function makeLink(
161 $target, $text = null, array $extraAttribs = [], array $query = []
162 ) {
163 Assert::parameterType( [ LinkTarget::class, PageReference::class ], $target, '$target' );
164 if ( $this->castToTitle( $target )->isKnown() ) {
165 return $this->makeKnownLink( $target, $text, $extraAttribs, $query );
166 } else {
167 return $this->makeBrokenLink( $target, $text, $extraAttribs, $query );
168 }
169 }
170
171 private function runBeginHook( $target, &$text, &$extraAttribs, &$query, $isKnown ) {
172 $ret = null;
173 if ( !$this->hookRunner->onHtmlPageLinkRendererBegin(
174 $this, $this->castToTitle( $target ), $text, $extraAttribs, $query, $ret )
175 ) {
176 return $ret;
177 }
178 }
179
191 public function makePreloadedLink(
192 $target, $text = null, $classes = '', array $extraAttribs = [], array $query = []
193 ) {
194 Assert::parameterType( [ LinkTarget::class, PageReference::class ], $target, '$target' );
195
196 // Run begin hook
197 $ret = $this->runBeginHook( $target, $text, $extraAttribs, $query, true );
198 if ( $ret !== null ) {
199 return $ret;
200 }
201 $target = $this->normalizeTarget( $target );
202 $url = $this->getLinkURL( $target, $query );
203 $attribs = [ 'class' => $classes ];
204 $prefixedText = $this->titleFormatter->getPrefixedText( $target );
205 if ( $prefixedText !== '' ) {
206 $attribs['title'] = $prefixedText;
207 }
208
209 $attribs = [
210 'href' => $url,
211 ] + $this->mergeAttribs( $attribs, $extraAttribs );
212
213 if ( $text === null ) {
214 $text = $this->getLinkText( $target );
215 }
216
217 return $this->buildAElement( $target, $text, $attribs, true );
218 }
219
227 public function makeKnownLink(
228 $target, $text = null, array $extraAttribs = [], array $query = []
229 ) {
230 Assert::parameterType( [ LinkTarget::class, PageReference::class ], $target, '$target' );
231 if ( $target instanceof LinkTarget ) {
232 $isExternal = $target->isExternal();
233 } else {
234 // $target instanceof PageReference
235 // treat all PageReferences as local for now
236 $isExternal = false;
237 }
238 $classes = [];
239 if ( $isExternal ) {
240 $classes[] = 'extiw';
241 }
242 $colour = $this->getLinkClasses( $target );
243 if ( $colour !== '' ) {
244 $classes[] = $colour;
245 }
246
247 return $this->makePreloadedLink(
248 $target,
249 $text,
250 implode( ' ', $classes ),
251 $extraAttribs,
252 $query
253 );
254 }
255
264 public function makeBrokenLink(
265 $target, $text = null, array $extraAttribs = [], array $query = []
266 ) {
267 Assert::parameterType( [ LinkTarget::class, PageReference::class ], $target, '$target' );
268 // Run legacy hook
269 $ret = $this->runBeginHook( $target, $text, $extraAttribs, $query, false );
270 if ( $ret !== null ) {
271 return $ret;
272 }
273
274 if ( $target instanceof LinkTarget ) {
275 # We don't want to include fragments for broken links, because they
276 # generally make no sense.
277 if ( $target->hasFragment() ) {
278 $target = $target->createFragmentTarget( '' );
279 }
280 }
281 $target = $this->normalizeTarget( $target );
282
283 if ( !isset( $query['action'] ) && $target->getNamespace() !== NS_SPECIAL ) {
284 $query['action'] = 'edit';
285 $query['redlink'] = '1';
286 }
287
288 $url = $this->getLinkURL( $target, $query );
289 $attribs = [ 'class' => 'new' ];
290 $prefixedText = $this->titleFormatter->getPrefixedText( $target );
291 if ( $prefixedText !== '' ) {
292 // This ends up in parser cache!
293 $attribs['title'] = wfMessage( 'red-link-title', $prefixedText )
294 ->inContentLanguage()
295 ->text();
296 }
297
298 $attribs = [
299 'href' => $url,
300 ] + $this->mergeAttribs( $attribs, $extraAttribs );
301
302 if ( $text === null ) {
303 $text = $this->getLinkText( $target );
304 }
305
306 return $this->buildAElement( $target, $text, $attribs, false );
307 }
308
318 private function buildAElement( $target, $text, array $attribs, $isKnown ) {
319 $ret = null;
320 if ( !$this->hookRunner->onHtmlPageLinkRendererEnd(
321 $this, $this->castToLinkTarget( $target ), $isKnown, $text, $attribs, $ret )
322 ) {
323 return $ret;
324 }
325
326 return Html::rawElement( 'a', $attribs, HtmlArmor::getHtml( $text ) );
327 }
328
333 private function getLinkText( $target ) {
334 $prefixedText = $this->titleFormatter->getPrefixedText( $target );
335 // If the target is just a fragment, with no title, we return the fragment
336 // text. Otherwise, we return the title text itself.
337 if ( $prefixedText === '' && $target instanceof LinkTarget && $target->hasFragment() ) {
338 return $target->getFragment();
339 }
340
341 return $prefixedText;
342 }
343
349 private function getLinkURL( $target, $query = [] ) {
350 if ( $this->forceArticlePath ) {
351 $realQuery = $query;
352 $query = [];
353 } else {
354 $realQuery = [];
355 }
356 $url = $this->castToTitle( $target )->getLinkURL( $query, false, $this->expandUrls );
357
358 if ( $this->forceArticlePath && $realQuery ) {
359 $url = wfAppendQuery( $url, $realQuery );
360 }
361
362 return $url;
363 }
364
373 public function normalizeTarget( $target ) {
374 $target = $this->castToLinkTarget( $target );
375 if ( $target->getNamespace() === NS_SPECIAL && !$target->isExternal() ) {
376 list( $name, $subpage ) = $this->specialPageFactory->resolveAlias(
377 $target->getDBkey()
378 );
379 if ( $name ) {
380 return new TitleValue(
382 $this->specialPageFactory->getLocalNameFor( $name, $subpage ),
383 $target->getFragment()
384 );
385 }
386 }
387
388 return $target;
389 }
390
399 private function mergeAttribs( $defaults, $attribs ) {
400 if ( !$attribs ) {
401 return $defaults;
402 }
403 # Merge the custom attribs with the default ones, and iterate
404 # over that, deleting all "false" attributes.
405 $ret = [];
406 $merged = Sanitizer::mergeAttributes( $defaults, $attribs );
407 foreach ( $merged as $key => $val ) {
408 # A false value suppresses the attribute
409 if ( $val !== false ) {
410 $ret[$key] = $val;
411 }
412 }
413 return $ret;
414 }
415
422 public function getLinkClasses( $target ) {
423 Assert::parameterType( [ LinkTarget::class, PageReference::class ], $target, '$target' );
424 $target = $this->castToLinkTarget( $target );
425 // Don't call LinkCache if the target is "non-proper"
426 if ( $target->isExternal() || $target->getText() === '' || $target->getNamespace() < 0 ) {
427 return '';
428 }
429 // Make sure the target is in the cache
430 $id = $this->linkCache->addLinkObj( $target );
431 if ( $id == 0 ) {
432 // Doesn't exist
433 return '';
434 }
435
436 if ( $this->linkCache->getGoodLinkFieldObj( $target, 'redirect' ) ) {
437 # Page is a redirect
438 return 'mw-redirect';
439 }
440
441 return '';
442 }
443
448 private function castToTitle( $target ): Title {
449 if ( $target instanceof LinkTarget ) {
450 return Title::newFromLinkTarget( $target );
451 }
452 // $target instanceof PageReference
453 return Title::castFromPageReference( $target );
454 }
455
460 private function castToLinkTarget( $target ): LinkTarget {
461 if ( $target instanceof PageReference ) {
462 return Title::castFromPageReference( $target );
463 }
464 // $target instanceof LinkTarget
465 return $target;
466 }
467}
const NS_SPECIAL
Definition Defines.php:53
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
if(ini_get('mbstring.func_overload')) if(!defined('MW_ENTRY_POINT'))
Pre-config setup: Before loading LocalSettings.php.
Definition Setup.php:88
Marks HTML that shouldn't be escaped.
Definition HtmlArmor.php:30
This class is a collection of static functions that serve two purposes:
Definition Html.php:49
Cache for article titles (prefixed DB keys) and ids linked from one source.
Definition LinkCache.php:41
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Class that generates HTML links for pages.
__construct(TitleFormatter $titleFormatter, LinkCache $linkCache, NamespaceInfo $nsInfo, SpecialPageFactory $specialPageFactory, HookContainer $hookContainer)
bool $forceArticlePath
Whether to force the pretty article path.
makePreloadedLink( $target, $text=null, $classes='', array $extraAttribs=[], array $query=[])
If you have already looked up the proper CSS classes using LinkRenderer::getLinkClasses() or some oth...
normalizeTarget( $target)
Normalizes the provided target.
SpecialPageFactory $specialPageFactory
makeKnownLink( $target, $text=null, array $extraAttribs=[], array $query=[])
buildAElement( $target, $text, array $attribs, $isKnown)
Builds the final element.
mergeAttribs( $defaults, $attribs)
Merges two sets of attributes.
getLinkClasses( $target)
Return the CSS classes of a known link.
makeBrokenLink( $target, $text=null, array $extraAttribs=[], array $query=[])
getLinkURL( $target, $query=[])
runBeginHook( $target, &$text, &$extraAttribs, &$query, $isKnown)
makeLink( $target, $text=null, array $extraAttribs=[], array $query=[])
string bool int $expandUrls
A PROTO_* constant or false.
Factory for handling the special page list and generating SpecialPage objects.
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:35
Represents a page (or page fragment) title within MediaWiki.
Represents a title within MediaWiki.
Definition Title.php:48
hasFragment()
Whether the link target has a fragment.
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
A title formatter service for MediaWiki.