MediaWiki REL1_36
LinkRenderer.php
Go to the documentation of this file.
1<?php
21namespace MediaWiki\Linker;
22
23use Html;
24use HtmlArmor;
25use LinkCache;
30use Sanitizer;
31use Title;
33use TitleValue;
34
42
48 private $forceArticlePath = false;
49
55 private $expandUrls = false;
56
60 private $stubThreshold = 0;
61
66
70 private $linkCache;
71
75 private $nsInfo;
76
79
81 private $hookRunner;
82
87
96 public function __construct(
102 ) {
103 $this->titleFormatter = $titleFormatter;
104 $this->linkCache = $linkCache;
105 $this->nsInfo = $nsInfo;
106 $this->specialPageFactory = $specialPageFactory;
107 $this->hookContainer = $hookContainer;
108 $this->hookRunner = new HookRunner( $hookContainer );
109 }
110
114 public function setForceArticlePath( $force ) {
115 $this->forceArticlePath = $force;
116 }
117
121 public function getForceArticlePath() {
123 }
124
128 public function setExpandURLs( $expand ) {
129 $this->expandUrls = $expand;
130 }
131
135 public function getExpandURLs() {
136 return $this->expandUrls;
137 }
138
142 public function setStubThreshold( $threshold ) {
143 $this->stubThreshold = $threshold;
144 }
145
149 public function getStubThreshold() {
151 }
152
160 public function makeLink(
161 LinkTarget $target, $text = null, array $extraAttribs = [], array $query = []
162 ) {
163 $title = Title::newFromLinkTarget( $target );
164 if ( $title->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( LinkTarget $target, &$text, &$extraAttribs, &$query, $isKnown ) {
172 $ret = null;
173 if ( !$this->hookRunner->onHtmlPageLinkRendererBegin(
174 $this, $target, $text, $extraAttribs, $query, $ret )
175 ) {
176 return $ret;
177 }
178 }
179
191 public function makePreloadedLink(
192 LinkTarget $target, $text = null, $classes = '', array $extraAttribs = [], array $query = []
193 ) {
194 // Run begin hook
195 $ret = $this->runBeginHook( $target, $text, $extraAttribs, $query, true );
196 if ( $ret !== null ) {
197 return $ret;
198 }
199 $target = $this->normalizeTarget( $target );
200 $url = $this->getLinkURL( $target, $query );
201 $attribs = [ 'class' => $classes ];
202 $prefixedText = $this->titleFormatter->getPrefixedText( $target );
203 if ( $prefixedText !== '' ) {
204 $attribs['title'] = $prefixedText;
205 }
206
207 $attribs = [
208 'href' => $url,
209 ] + $this->mergeAttribs( $attribs, $extraAttribs );
210
211 if ( $text === null ) {
212 $text = $this->getLinkText( $target );
213 }
214
215 return $this->buildAElement( $target, $text, $attribs, true );
216 }
217
225 public function makeKnownLink(
226 LinkTarget $target, $text = null, array $extraAttribs = [], array $query = []
227 ) {
228 $classes = [];
229 if ( $target->isExternal() ) {
230 $classes[] = 'extiw';
231 }
232 $colour = $this->getLinkClasses( $target );
233 if ( $colour !== '' ) {
234 $classes[] = $colour;
235 }
236
237 return $this->makePreloadedLink(
238 $target,
239 $text,
240 implode( ' ', $classes ),
241 $extraAttribs,
242 $query
243 );
244 }
245
254 public function makeBrokenLink(
255 LinkTarget $target, $text = null, array $extraAttribs = [], array $query = []
256 ) {
257 // Run legacy hook
258 $ret = $this->runBeginHook( $target, $text, $extraAttribs, $query, false );
259 if ( $ret !== null ) {
260 return $ret;
261 }
262
263 # We don't want to include fragments for broken links, because they
264 # generally make no sense.
265 if ( $target->hasFragment() ) {
266 $target = $target->createFragmentTarget( '' );
267 }
268 $target = $this->normalizeTarget( $target );
269
270 if ( !isset( $query['action'] ) && $target->getNamespace() !== NS_SPECIAL ) {
271 $query['action'] = 'edit';
272 $query['redlink'] = '1';
273 }
274
275 $url = $this->getLinkURL( $target, $query );
276 $attribs = [ 'class' => 'new' ];
277 $prefixedText = $this->titleFormatter->getPrefixedText( $target );
278 if ( $prefixedText !== '' ) {
279 // This ends up in parser cache!
280 $attribs['title'] = wfMessage( 'red-link-title', $prefixedText )
281 ->inContentLanguage()
282 ->text();
283 }
284
285 $attribs = [
286 'href' => $url,
287 ] + $this->mergeAttribs( $attribs, $extraAttribs );
288
289 if ( $text === null ) {
290 $text = $this->getLinkText( $target );
291 }
292
293 return $this->buildAElement( $target, $text, $attribs, false );
294 }
295
305 private function buildAElement( LinkTarget $target, $text, array $attribs, $isKnown ) {
306 $ret = null;
307 if ( !$this->hookRunner->onHtmlPageLinkRendererEnd(
308 $this, $target, $isKnown, $text, $attribs, $ret )
309 ) {
310 return $ret;
311 }
312
313 return Html::rawElement( 'a', $attribs, HtmlArmor::getHtml( $text ) );
314 }
315
320 private function getLinkText( LinkTarget $target ) {
321 $prefixedText = $this->titleFormatter->getPrefixedText( $target );
322 // If the target is just a fragment, with no title, we return the fragment
323 // text. Otherwise, we return the title text itself.
324 if ( $prefixedText === '' && $target->hasFragment() ) {
325 return $target->getFragment();
326 }
327
328 return $prefixedText;
329 }
330
331 private function getLinkURL( LinkTarget $target, array $query = [] ) {
332 // TODO: Use a LinkTargetResolver service instead of Title
333 $title = Title::newFromLinkTarget( $target );
334 if ( $this->forceArticlePath ) {
335 $realQuery = $query;
336 $query = [];
337 } else {
338 $realQuery = [];
339 }
340 $url = $title->getLinkURL( $query, false, $this->expandUrls );
341
342 if ( $this->forceArticlePath && $realQuery ) {
343 $url = wfAppendQuery( $url, $realQuery );
344 }
345
346 return $url;
347 }
348
357 public function normalizeTarget( LinkTarget $target ) {
358 if ( $target->getNamespace() === NS_SPECIAL && !$target->isExternal() ) {
359 list( $name, $subpage ) = $this->specialPageFactory->resolveAlias(
360 $target->getDBkey()
361 );
362 if ( $name ) {
363 return new TitleValue(
365 $this->specialPageFactory->getLocalNameFor( $name, $subpage ),
366 $target->getFragment()
367 );
368 }
369 }
370
371 return $target;
372 }
373
382 private function mergeAttribs( $defaults, $attribs ) {
383 if ( !$attribs ) {
384 return $defaults;
385 }
386 # Merge the custom attribs with the default ones, and iterate
387 # over that, deleting all "false" attributes.
388 $ret = [];
389 $merged = Sanitizer::mergeAttributes( $defaults, $attribs );
390 foreach ( $merged as $key => $val ) {
391 # A false value suppresses the attribute
392 if ( $val !== false ) {
393 $ret[$key] = $val;
394 }
395 }
396 return $ret;
397 }
398
405 public function getLinkClasses( LinkTarget $target ) {
406 // Make sure the target is in the cache
407 $id = $this->linkCache->addLinkObj( $target );
408 if ( $id == 0 ) {
409 // Doesn't exist
410 return '';
411 }
412
413 if ( $this->linkCache->getGoodLinkFieldObj( $target, 'redirect' ) ) {
414 # Page is a redirect
415 return 'mw-redirect';
416 } elseif (
417 $this->stubThreshold > 0 && $this->nsInfo->isContent( $target->getNamespace() ) &&
418 $this->linkCache->getGoodLinkFieldObj( $target, 'length' ) < $this->stubThreshold
419 ) {
420 # Page is a stub
421 return 'stub';
422 }
423
424 return '';
425 }
426}
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.
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:35
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(LinkTarget $target, $text=null, $classes='', array $extraAttribs=[], array $query=[])
If you have already looked up the proper CSS classes using LinkRenderer::getLinkClasses() or some oth...
getLinkURL(LinkTarget $target, array $query=[])
SpecialPageFactory $specialPageFactory
buildAElement(LinkTarget $target, $text, array $attribs, $isKnown)
Builds the final element.
runBeginHook(LinkTarget $target, &$text, &$extraAttribs, &$query, $isKnown)
makeBrokenLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
getLinkClasses(LinkTarget $target)
Return the CSS classes of a known link.
mergeAttribs( $defaults, $attribs)
Merges two sets of attributes.
getLinkText(LinkTarget $target)
makeKnownLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
makeLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
normalizeTarget(LinkTarget $target)
Normalizes the provided target.
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:34
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.
getFragment()
Get the link fragment (i.e.
getNamespace()
Get the namespace index.
getDBkey()
Get the main part with underscores.
isExternal()
Whether this LinkTarget has an interwiki component.
createFragmentTarget( $fragment)
Creates a new LinkTarget for a different fragment of the same page.
A title formatter service for MediaWiki.