MediaWiki  master
LinkRenderer.php
Go to the documentation of this file.
1 <?php
21 namespace MediaWiki\Linker;
22 
23 use Html;
24 use HtmlArmor;
25 use LinkCache;
29 use NamespaceInfo;
30 use Sanitizer;
31 use Title;
32 use TitleFormatter;
33 use TitleValue;
34 
41 class LinkRenderer {
42 
48  private $forceArticlePath = false;
49 
55  private $expandUrls = false;
56 
60  private $stubThreshold = 0;
61 
65  private $titleFormatter;
66 
70  private $linkCache;
71 
75  private $nsInfo;
76 
78  private $hookContainer;
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() {
150  return $this->stubThreshold;
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(
364  NS_SPECIAL,
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 }
MediaWiki\Linker\LinkRenderer\$titleFormatter
TitleFormatter $titleFormatter
Definition: LinkRenderer.php:65
LinkCache
Cache for article titles (prefixed DB keys) and ids linked from one source.
Definition: LinkCache.php:35
MediaWiki\Linker\LinkRenderer\$hookRunner
HookRunner $hookRunner
Definition: LinkRenderer.php:81
MediaWiki\Linker\LinkRenderer\$stubThreshold
int $stubThreshold
Definition: LinkRenderer.php:60
HtmlArmor
Marks HTML that shouldn't be escaped.
Definition: HtmlArmor.php:30
MediaWiki\Linker\LinkRenderer\getLinkText
getLinkText(LinkTarget $target)
Definition: LinkRenderer.php:320
MediaWiki\Linker\LinkRenderer\mergeAttribs
mergeAttribs( $defaults, $attribs)
Merges two sets of attributes.
Definition: LinkRenderer.php:382
MediaWiki\Linker\LinkRenderer\normalizeTarget
normalizeTarget(LinkTarget $target)
Normalizes the provided target.
Definition: LinkRenderer.php:357
MediaWiki\Linker\LinkRenderer\$linkCache
LinkCache $linkCache
Definition: LinkRenderer.php:70
MediaWiki\Linker\LinkRenderer\$nsInfo
NamespaceInfo $nsInfo
Definition: LinkRenderer.php:75
MediaWiki\Linker\LinkRenderer\__construct
__construct(TitleFormatter $titleFormatter, LinkCache $linkCache, NamespaceInfo $nsInfo, SpecialPageFactory $specialPageFactory, HookContainer $hookContainer)
Definition: LinkRenderer.php:96
MediaWiki\Linker\LinkTarget\createFragmentTarget
createFragmentTarget( $fragment)
Creates a new LinkTarget for a different fragment of the same page.
MediaWiki\Linker\LinkRenderer
Class that generates HTML links for pages.
Definition: LinkRenderer.php:41
MediaWiki\SpecialPage\SpecialPageFactory
Factory for handling the special page list and generating SpecialPage objects.
Definition: SpecialPageFactory.php:61
MediaWiki\Linker\LinkRenderer\buildAElement
buildAElement(LinkTarget $target, $text, array $attribs, $isKnown)
Builds the final element.
Definition: LinkRenderer.php:305
Sanitizer\mergeAttributes
static mergeAttributes( $a, $b)
Merge two sets of HTML attributes.
Definition: Sanitizer.php:541
MediaWiki\Linker\LinkRenderer\setStubThreshold
setStubThreshold( $threshold)
Definition: LinkRenderer.php:142
MediaWiki\Linker\LinkRenderer\getLinkClasses
getLinkClasses(LinkTarget $target)
Return the CSS classes of a known link.
Definition: LinkRenderer.php:405
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1220
MediaWiki\Linker\LinkRenderer\$expandUrls
string bool int $expandUrls
A PROTO_* constant or false.
Definition: LinkRenderer.php:55
MediaWiki\Linker\LinkTarget\isExternal
isExternal()
Whether this LinkTarget has an interwiki component.
MediaWiki\Linker\LinkRenderer\getForceArticlePath
getForceArticlePath()
Definition: LinkRenderer.php:121
wfAppendQuery
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
Definition: GlobalFunctions.php:438
MediaWiki\Linker\LinkRenderer\getExpandURLs
getExpandURLs()
Definition: LinkRenderer.php:135
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:58
MediaWiki\Linker\LinkTarget\getNamespace
getNamespace()
Get the namespace index.
HtmlArmor\getHtml
static getHtml( $input)
Provide a string or HtmlArmor object and get safe HTML back.
Definition: HtmlArmor.php:54
MediaWiki\Linker
MediaWiki\Linker\LinkRenderer\$forceArticlePath
bool $forceArticlePath
Whether to force the pretty article path.
Definition: LinkRenderer.php:48
$title
$title
Definition: testCompression.php:38
MediaWiki\Linker\LinkRenderer\setExpandURLs
setExpandURLs( $expand)
Definition: LinkRenderer.php:128
MediaWiki\Linker\LinkRenderer\makePreloadedLink
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...
Definition: LinkRenderer.php:191
MediaWiki\Linker\LinkRenderer\setForceArticlePath
setForceArticlePath( $force)
Definition: LinkRenderer.php:114
MediaWiki\Linker\LinkRenderer\runBeginHook
runBeginHook(LinkTarget $target, &$text, &$extraAttribs, &$query, $isKnown)
Definition: LinkRenderer.php:171
MediaWiki\Linker\LinkRenderer\$specialPageFactory
SpecialPageFactory $specialPageFactory
Definition: LinkRenderer.php:86
MediaWiki\Linker\LinkTarget\getDBkey
getDBkey()
Get the main part with underscores.
MediaWiki\Linker\LinkTarget\getFragment
getFragment()
Get the link fragment (i.e.
Title\newFromLinkTarget
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Definition: Title.php:280
MediaWiki\Linker\LinkRenderer\$hookContainer
HookContainer $hookContainer
Definition: LinkRenderer.php:78
Title
Represents a title within MediaWiki.
Definition: Title.php:41
MediaWiki\Linker\LinkRenderer\makeKnownLink
makeKnownLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:225
TitleFormatter
A title formatter service for MediaWiki.
Definition: TitleFormatter.php:34
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:212
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
MediaWiki\Linker\LinkRenderer\getLinkURL
getLinkURL(LinkTarget $target, array $query=[])
Definition: LinkRenderer.php:331
NamespaceInfo
This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of them ba...
Definition: NamespaceInfo.php:35
MediaWiki\Linker\LinkRenderer\getStubThreshold
getStubThreshold()
Definition: LinkRenderer.php:149
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:567
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
Sanitizer
HTML sanitizer for MediaWiki.
Definition: Sanitizer.php:34
MediaWiki\Linker\LinkRenderer\makeLink
makeLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:160
MediaWiki\Linker\LinkTarget\hasFragment
hasFragment()
Whether the link target has a fragment.
MediaWiki\Linker\LinkRenderer\makeBrokenLink
makeBrokenLink(LinkTarget $target, $text=null, array $extraAttribs=[], array $query=[])
Definition: LinkRenderer.php:254
Html
This class is a collection of static functions that serve two purposes:
Definition: Html.php:49
TitleValue
Represents a page (or page fragment) title within MediaWiki.
Definition: TitleValue.php:39