3 * Split off some of the internal bits from Skin.php. These functions are used
4 * for primarily page content: links, embedded images, table of contents. Links
5 * are also used in the skin. For the moment, Skin is a descendent class of
6 * Linker. In the future, it should probably be further split so that every
7 * other bit of the wiki doesn't have to go loading up Skin to get at it.
14 * Flags for userToolLinks()
16 const TOOL_LINKS_NOBLOCK
= 1;
18 function __construct() {}
23 function postParseLinkColour( $s = null ) {
24 wfDeprecated( __METHOD__
);
29 * Get the appropriate HTML attributes to add to the "a" element of an ex-
30 * ternal link, as created by [wikisyntax].
32 * @param string $title The (unescaped) title text for the link
33 * @param string $unused Unused
34 * @param string $class The contents of the class attribute; if an empty
35 * string is passed, which is the default value, defaults to 'external'.
37 function getExternalLinkAttributes( $title, $unused = null, $class='' ) {
38 return $this->getLinkAttributesInternal( $title, $class, 'external' );
42 * Get the appropriate HTML attributes to add to the "a" element of an in-
45 * @param string $title The title text for the link, URL-encoded (???) but
47 * @param string $unused Unused
48 * @param string $class The contents of the class attribute; if an empty
49 * string is passed, which is the default value, defaults to 'external'.
51 function getInterwikiLinkAttributes( $title, $unused = null, $class='' ) {
54 # FIXME: We have a whole bunch of handling here that doesn't happen in
55 # getExternalLinkAttributes, why?
56 $title = urldecode( $title );
57 $title = $wgContLang->checkTitleEncoding( $title );
58 $title = preg_replace( '/[\\x00-\\x1f]/', ' ', $title );
60 return $this->getLinkAttributesInternal( $title, $class, 'external' );
64 * Get the appropriate HTML attributes to add to the "a" element of an in-
67 * @param string $title The title text for the link, URL-encoded (???) but
69 * @param string $unused Unused
70 * @param string $class The contents of the class attribute, default none
72 function getInternalLinkAttributes( $title, $unused = null, $class='' ) {
73 $title = urldecode( $title );
74 $title = str_replace( '_', ' ', $title );
75 return $this->getLinkAttributesInternal( $title, $class );
79 * Get the appropriate HTML attributes to add to the "a" element of an in-
80 * ternal link, given the Title object for the page we want to link to.
82 * @param Title $nt The Title object
83 * @param string $unused Unused
84 * @param string $class The contents of the class attribute, default none
85 * @param mixed $title Optional (unescaped) string to use in the title
86 * attribute; if false, default to the name of the page we're linking to
88 function getInternalLinkAttributesObj( $nt, $unused = null, $class = '', $title = false ) {
89 if( $title === false ) {
90 $title = $nt->getPrefixedText();
92 return $this->getLinkAttributesInternal( $title, $class );
96 * Common code for getLinkAttributesX functions
98 private function getLinkAttributesInternal( $title, $class, $classDefault = false ) {
99 $title = htmlspecialchars( $title );
100 if( $class === '' and $classDefault !== false ) {
101 # FIXME: Parameter defaults the hard way! We should just have
102 # $class = 'external' or whatever as the default in the externally-
103 # exposed functions, not $class = ''.
104 $class = $classDefault;
106 $class = htmlspecialchars( $class );
108 if( $class !== '' ) {
109 $r .= " class=\"$class\"";
111 $r .= " title=\"$title\"";
116 * Return the CSS colour of a known link
119 * @param integer $threshold user defined threshold
120 * @return string CSS class
122 function getLinkColour( $t, $threshold ) {
124 if ( $t->isRedirect() ) {
126 $colour = 'mw-redirect';
127 } elseif ( $threshold > 0 && $t->getLength() < $threshold && MWNamespace
::isContent( $t->getNamespace() ) ) {
135 * This function returns an HTML link to the given target. It serves a few purposes:
136 * 1) If $target is a Title, the correct URL to link to will be figured out automatically.
137 * 2) It automatically adds the usual classes for various types of link targets: "new" for red links, "extern" for external links, etc.
138 * 3) It escapes all attribute values safely so there's no risk of XSS.
139 * 4) It provides a default tooltip if the target is a Title (the page name of the target).
141 * @param $target Title Can currently only be a Title, but this may change.
142 * @param $text string The HTML contents of the <a> element, i.e., the link text. This is raw HTML and will not be escaped. If null, defaults to the page name of the Title or Image, or the text of the URL if $target is a URL.
143 * @param $query array The query string to append to the URL you're linking to, in key => value array form. Useful mainly for Titles and Images. Query keys and values will be URL-encoded.
144 * @param $customAttribs array A key => value array of extra HTML attributes, such as title and class. (href is ignored.) Classes will be merged with the default classes, while other attributes will replace default attributes. All passed attribute values will be HTML-escaped. A false attribute value means to suppress that attribute.
145 * @param $options mixed String or array of strings:
146 * 'known': Page is known to exist, so don't check if it does.
147 * 'broken': Page is known not to exist, so don't check if it does.
148 * 'noclasses': Don't add any classes automatically (includes "new", "stub", "mw-redirect"). Only use the class attribute provided, if any.
149 * @return string HTML <a> attribute
151 public function link( $target, $text = null, $customAttribs = array(), $query = array(), $options = array() ) {
152 wfProfileIn( __METHOD__
);
153 if( !($target instanceof Title
) ) {
154 throw new MWException( 'Linker::link passed invalid target' );
156 $options = (array)$options;
158 # Normalize the Title if it's a special page
159 if( $target->getNamespace() == NS_SPECIAL
) {
160 list( $name, $subpage ) = SpecialPage
::resolveAliasWithSubpage( $target->getDBkey() );
162 $target = SpecialPage
::getTitleFor( $name, $subpage );
166 # If we don't know whether the page exists, let's find out.
167 if( !in_array( 'known', $options ) and !in_array( 'broken', $options ) ) {
168 if( $target->getNamespace() == NS_SPECIAL
) {
169 if( SpecialPage
::exists( $target->getDbKey() ) ) {
170 $options []= 'known';
172 $options []= 'broken';
174 } elseif( $target->isAlwaysKnown() or
175 ($target->getPrefixedText() == '' and $target->getFragment() != '')
176 or $target->exists() ) {
177 $options []= 'known';
180 $options []= 'broken';
184 # Note: we want the href attribute first, for prettiness.
185 $attribs = array( 'href' => $this->linkUrl( $target, $query, $options ) );
186 $attribs = array_merge(
188 $this->linkAttribs( $target, $customAttribs, $options )
190 if( is_null( $text ) ) {
191 $text = $this->linkText( $target, $options );
194 $ret = Xml
::element( 'a', $attribs, $text, false );
196 wfProfileOut( __METHOD__
);
200 private function linkUrl( $target, $query, $options ) {
201 # If it's a broken link, add the appropriate query pieces. This over-
202 # writes the default action!
203 if( in_array( 'broken', $options ) ) {
204 $query['action'] = 'edit';
205 $query['redlink'] = '1';
208 $queryString = array();
209 foreach( $query as $key => $val ) {
210 $queryString []= urlencode( $key ) . '=' . urlencode( $val );
212 $queryString = implode( '&', $queryString );
214 if( $target->isExternal() ) {
215 return $target->getFullURL( $queryString );
217 return $target->getLocalURL( $queryString );
220 private function linkAttribs( $target, $attribs, $options ) {
224 # First get a default title attribute.
225 if( in_array( 'known', $options ) ) {
226 $defaults['title'] = $target->getPrefixedText();
228 $defaults['title'] = wfMsg( 'red-link-title', $target->getPrefixedText() );
231 if( !in_array( 'noclasses', $options ) ) {
232 # Now build the classes. This is the bulk of what we're doing.
235 if( in_array( 'broken', $options ) ) {
239 # Note that redirects never count as stubs here.
240 if ( $target->isRedirect() ) {
241 $classes []= 'mw-redirect';
242 } elseif( $target->isContentPage() ) {
243 $threshold = $wgUser->getOption( 'stubthreshold' );
244 if( $threshold > 0 and $target->getLength() < $threshold ) {
248 if( $classes != array() ) {
249 $defaults['class'] = implode( ' ', $classes );
253 # Finally, merge the custom attribs with the default ones, and iterate
254 # over that, deleting all "false" attributes.
255 if( !empty( $attribs['class'] ) and !empty( $defaults['class'] ) ) {
256 $attribs['class'] .= ' '.$defaults['class'];
259 foreach( array_merge( $defaults, $attribs ) as $key => $val ) {
260 if( $key != 'href' and $val !== false ) {
267 private function linkText( $target, $options ) {
268 # If the target is just a fragment, with no title, we return the frag-
269 # ment text. Otherwise, we return the title text itself.
270 if( $target->getPrefixedText() === '' and $target->getFragment() !== '' ) {
271 return htmlspecialchars( $target->getFragment() );
273 return htmlspecialchars( $target->getPrefixedText() );
277 * This function is a shortcut to makeLinkObj(Title::newFromText($title),...). Do not call
278 * it if you already have a title object handy. See makeLinkObj for further documentation.
280 * @param $title String: the text of the title
281 * @param $text String: link text
282 * @param $query String: optional query part
283 * @param $trail String: optional trail. Alphabetic characters at the start of this string will
284 * be included in the link text. Other characters will be appended after
285 * the end of the link.
287 function makeLink( $title, $text = '', $query = '', $trail = '' ) {
288 wfProfileIn( __METHOD__
);
289 $nt = Title
::newFromText( $title );
290 if ( $nt instanceof Title
) {
291 $result = $this->makeLinkObj( $nt, $text, $query, $trail );
293 wfDebug( 'Invalid title passed to Linker::makeLink(): "'.$title."\"\n" );
294 $result = $text == "" ?
$title : $text;
297 wfProfileOut( __METHOD__
);
302 * This function is a shortcut to makeKnownLinkObj(Title::newFromText($title),...). Do not call
303 * it if you already have a title object handy. See makeKnownLinkObj for further documentation.
305 * @param $title String: the text of the title
306 * @param $text String: link text
307 * @param $query String: optional query part
308 * @param $trail String: optional trail. Alphabetic characters at the start of this string will
309 * be included in the link text. Other characters will be appended after
310 * the end of the link.
312 function makeKnownLink( $title, $text = '', $query = '', $trail = '', $prefix = '',$aprops = '') {
313 $nt = Title
::newFromText( $title );
314 if ( $nt instanceof Title
) {
315 return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix , $aprops );
317 wfDebug( 'Invalid title passed to Linker::makeKnownLink(): "'.$title."\"\n" );
318 return $text == '' ?
$title : $text;
323 * This function is a shortcut to makeBrokenLinkObj(Title::newFromText($title),...). Do not call
324 * it if you already have a title object handy. See makeBrokenLinkObj for further documentation.
326 * @param string $title The text of the title
327 * @param string $text Link text
328 * @param string $query Optional query part
329 * @param string $trail Optional trail. Alphabetic characters at the start of this string will
330 * be included in the link text. Other characters will be appended after
331 * the end of the link.
333 function makeBrokenLink( $title, $text = '', $query = '', $trail = '' ) {
334 $nt = Title
::newFromText( $title );
335 if ( $nt instanceof Title
) {
336 return $this->makeBrokenLinkObj( $nt, $text, $query, $trail );
338 wfDebug( 'Invalid title passed to Linker::makeBrokenLink(): "'.$title."\"\n" );
339 return $text == '' ?
$title : $text;
344 * @deprecated use makeColouredLinkObj
346 * This function is a shortcut to makeStubLinkObj(Title::newFromText($title),...). Do not call
347 * it if you already have a title object handy. See makeStubLinkObj for further documentation.
349 * @param $title String: the text of the title
350 * @param $text String: link text
351 * @param $query String: optional query part
352 * @param $trail String: optional trail. Alphabetic characters at the start of this string will
353 * be included in the link text. Other characters will be appended after
354 * the end of the link.
356 function makeStubLink( $title, $text = '', $query = '', $trail = '' ) {
357 $nt = Title
::newFromText( $title );
358 if ( $nt instanceof Title
) {
359 return $this->makeStubLinkObj( $nt, $text, $query, $trail );
361 wfDebug( 'Invalid title passed to Linker::makeStubLink(): "'.$title."\"\n" );
362 return $text == '' ?
$title : $text;
367 * Make a link for a title which may or may not be in the database. If you need to
368 * call this lots of times, pre-fill the link cache with a LinkBatch, otherwise each
369 * call to this will result in a DB query.
371 * @param $nt Title: the title object to make the link from, e.g. from
372 * Title::newFromText.
373 * @param $text String: link text
374 * @param $query String: optional query part
375 * @param $trail String: optional trail. Alphabetic characters at the start of this string will
376 * be included in the link text. Other characters will be appended after
377 * the end of the link.
378 * @param $prefix String: optional prefix. As trail, only before instead of after.
380 function makeLinkObj( Title
$nt, $text= '', $query = '', $trail = '', $prefix = '' ) {
382 wfProfileIn( __METHOD__
);
384 if ( $nt->isExternal() ) {
385 $u = $nt->getFullURL();
386 $link = $nt->getPrefixedURL();
387 if ( '' == $text ) { $text = $nt->getPrefixedText(); }
388 $style = $this->getInterwikiLinkAttributes( $link, $text, 'extiw' );
391 if ( '' != $trail ) {
393 if ( preg_match( '/^([a-z]+)(.*)$$/sD', $trail, $m ) ) {
398 $t = "<a href=\"{$u}\"{$style}>{$text}{$inside}</a>";
400 wfProfileOut( __METHOD__
);
402 } elseif ( $nt->isAlwaysKnown() ) {
403 # Image links, special page links and self-links with fragments are always known.
404 $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
406 wfProfileIn( __METHOD__
.'-immediate' );
408 # Handles links to special pages which do not exist in the database:
409 if( $nt->getNamespace() == NS_SPECIAL
) {
410 if( SpecialPage
::exists( $nt->getDBkey() ) ) {
411 $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix );
413 $retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix );
415 wfProfileOut( __METHOD__
.'-immediate' );
416 wfProfileOut( __METHOD__
);
420 # Work out link colour immediately
421 $aid = $nt->getArticleID() ;
423 $retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix );
426 if ( $nt->isContentPage() ) {
427 $threshold = $wgUser->getOption('stubthreshold');
428 $colour = $this->getLinkColour( $nt, $threshold );
430 $retVal = $this->makeColouredLinkObj( $nt, $colour, $text, $query, $trail, $prefix );
432 wfProfileOut( __METHOD__
.'-immediate' );
434 wfProfileOut( __METHOD__
);
439 * Make a link for a title which definitely exists. This is faster than makeLinkObj because
440 * it doesn't have to do a database query. It's also valid for interwiki titles and special
443 * @param $nt Title object of target page
444 * @param $text String: text to replace the title
445 * @param $query String: link target
446 * @param $trail String: text after link
447 * @param $prefix String: text before link text
448 * @param $aprops String: extra attributes to the a-element
449 * @param $style String: style to apply - if empty, use getInternalLinkAttributesObj instead
450 * @return the a-element
452 function makeKnownLinkObj( Title
$title, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) {
453 wfProfileIn( __METHOD__
);
455 $nt = $this->normaliseSpecialPage( $title );
457 $u = $nt->escapeLocalURL( $query );
458 if ( $nt->getFragment() != '' ) {
459 if( $nt->getPrefixedDbkey() == '' ) {
462 $text = htmlspecialchars( $nt->getFragment() );
465 $u .= $nt->getFragmentForURL();
468 $text = htmlspecialchars( $nt->getPrefixedText() );
470 if ( $style == '' ) {
471 $style = $this->getInternalLinkAttributesObj( $nt, $text );
474 if ( $aprops !== '' ) $aprops = " $aprops";
476 list( $inside, $trail ) = Linker
::splitTrail( $trail );
477 $r = "<a href=\"{$u}\"{$style}{$aprops}>{$prefix}{$text}{$inside}</a>{$trail}";
478 wfProfileOut( __METHOD__
);
483 * Make a red link to the edit page of a given title.
485 * @param $nt Title object of the target page
486 * @param $text String: Link text
487 * @param $query String: Optional query part
488 * @param $trail String: Optional trail. Alphabetic characters at the start of this string will
489 * be included in the link text. Other characters will be appended after
490 * the end of the link.
492 function makeBrokenLinkObj( Title
$title, $text = '', $query = '', $trail = '', $prefix = '' ) {
493 wfProfileIn( __METHOD__
);
495 $nt = $this->normaliseSpecialPage( $title );
497 if( $nt->getNamespace() == NS_SPECIAL
) {
499 } else if ( '' == $query ) {
500 $q = 'action=edit&redlink=1';
502 $q = 'action=edit&redlink=1&'.$query;
504 $u = $nt->escapeLocalURL( $q );
506 $titleText = $nt->getPrefixedText();
508 $text = htmlspecialchars( $titleText );
510 $titleAttr = wfMsg( 'red-link-title', $titleText );
511 $style = $this->getInternalLinkAttributesObj( $nt, $text, 'new', $titleAttr );
512 list( $inside, $trail ) = Linker
::splitTrail( $trail );
514 wfRunHooks( 'BrokenLink', array( &$this, $nt, $query, &$u, &$style, &$prefix, &$text, &$inside, &$trail ) );
515 $s = "<a href=\"{$u}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
517 wfProfileOut( __METHOD__
);
522 * @deprecated use makeColouredLinkObj
524 * Make a brown link to a short article.
526 * @param $nt Title object of the target page
527 * @param $text String: link text
528 * @param $query String: optional query part
529 * @param $trail String: optional trail. Alphabetic characters at the start of this string will
530 * be included in the link text. Other characters will be appended after
531 * the end of the link.
533 function makeStubLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
534 wfDeprecated( __METHOD__
);
535 return $this->makeColouredLinkObj( $nt, 'stub', $text, $query, $trail, $prefix );
539 * Make a coloured link.
541 * @param $nt Title object of the target page
542 * @param $colour Integer: colour of the link
543 * @param $text String: link text
544 * @param $query String: optional query part
545 * @param $trail String: optional trail. Alphabetic characters at the start of this string will
546 * be included in the link text. Other characters will be appended after
547 * the end of the link.
549 function makeColouredLinkObj( $nt, $colour, $text = '', $query = '', $trail = '', $prefix = '' ) {
551 $style = $this->getInternalLinkAttributesObj( $nt, $text, $colour );
553 return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix, '', $style );
557 * Generate either a normal exists-style link or a stub link, depending
558 * on the given page size.
560 * @param $size Integer
561 * @param $nt Title object.
562 * @param $text String
563 * @param $query String
564 * @param $trail String
565 * @param $prefix String
566 * @return string HTML of link
568 function makeSizeLinkObj( $size, $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
570 $threshold = intval( $wgUser->getOption( 'stubthreshold' ) );
571 $colour = ( $size < $threshold ) ?
'stub' : '';
572 return $this->makeColouredLinkObj( $nt, $colour, $text, $query, $trail, $prefix );
576 * Make appropriate markup for a link to the current article. This is currently rendered
577 * as the bold link text. The calling sequence is the same as the other make*LinkObj functions,
578 * despite $query not being used.
580 function makeSelfLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
582 $text = htmlspecialchars( $nt->getPrefixedText() );
584 list( $inside, $trail ) = Linker
::splitTrail( $trail );
585 return "<strong class=\"selflink\">{$prefix}{$text}{$inside}</strong>{$trail}";
588 function normaliseSpecialPage( Title
$title ) {
589 if ( $title->getNamespace() == NS_SPECIAL
) {
590 list( $name, $subpage ) = SpecialPage
::resolveAliasWithSubpage( $title->getDBkey() );
591 if ( !$name ) return $title;
592 return SpecialPage
::getTitleFor( $name, $subpage );
598 /** @todo document */
599 function fnamePart( $url ) {
600 $basename = strrchr( $url, '/' );
601 if ( false === $basename ) {
604 $basename = substr( $basename, 1 );
609 /** Obsolete alias */
610 function makeImage( $url, $alt = '' ) {
611 wfDeprecated( __METHOD__
);
612 return $this->makeExternalImage( $url, $alt );
615 /** @todo document */
616 function makeExternalImage( $url, $alt = '' ) {
618 $alt = $this->fnamePart( $url );
621 $success = wfRunHooks('LinkerMakeExternalImage', array( &$url, &$alt, &$img ) );
623 wfDebug("Hook LinkerMakeExternalImage changed the output of external image with url {$url} and alt text {$alt} to {$img}", true);
626 return Xml
::element( 'img',
633 * Creates the HTML source for images
634 * @deprecated use makeImageLink2
636 * @param object $title
637 * @param string $label label text
638 * @param string $alt alt text
639 * @param string $align horizontal alignment: none, left, center, right)
640 * @param array $handlerParams Parameters to be passed to the media handler
641 * @param boolean $framed shows image in original size in a frame
642 * @param boolean $thumb shows image as thumbnail in a frame
643 * @param string $manualthumb image name for the manual thumbnail
644 * @param string $valign vertical alignment: baseline, sub, super, top, text-top, middle, bottom, text-bottom
645 * @param string $time, timestamp of the file, set as false for current
648 function makeImageLinkObj( $title, $label, $alt, $align = '', $handlerParams = array(), $framed = false,
649 $thumb = false, $manualthumb = '', $valign = '', $time = false )
651 $frameParams = array( 'alt' => $alt, 'caption' => $label );
653 $frameParams['align'] = $align;
656 $frameParams['framed'] = true;
659 $frameParams['thumbnail'] = true;
661 if ( $manualthumb ) {
662 $frameParams['manualthumb'] = $manualthumb;
665 $frameParams['valign'] = $valign;
667 $file = wfFindFile( $title, $time );
668 return $this->makeImageLink2( $title, $file, $frameParams, $handlerParams, $time );
672 * Given parameters derived from [[Image:Foo|options...]], generate the
673 * HTML that that syntax inserts in the page.
675 * @param Title $title Title object
676 * @param File $file File object, or false if it doesn't exist
678 * @param array $frameParams Associative array of parameters external to the media handler.
679 * Boolean parameters are indicated by presence or absence, the value is arbitrary and
680 * will often be false.
681 * thumbnail If present, downscale and frame
682 * manualthumb Image name to use as a thumbnail, instead of automatic scaling
683 * framed Shows image in original size in a frame
684 * frameless Downscale but don't frame
685 * upright If present, tweak default sizes for portrait orientation
686 * upright_factor Fudge factor for "upright" tweak (default 0.75)
687 * border If present, show a border around the image
688 * align Horizontal alignment (left, right, center, none)
689 * valign Vertical alignment (baseline, sub, super, top, text-top, middle,
690 * bottom, text-bottom)
691 * alt Alternate text for image (i.e. alt attribute). Plain text.
692 * caption HTML for image caption.
694 * @param array $handlerParams Associative array of media handler parameters, to be passed
695 * to transform(). Typical keys are "width" and "page".
696 * @param string $time, timestamp of the file, set as false for current
697 * @param string $query, query params for desc url
698 * @return string HTML for an image, with links, wrappers, etc.
700 function makeImageLink2( Title
$title, $file, $frameParams = array(), $handlerParams = array(), $time = false, $query = "" ) {
702 if( !wfRunHooks( 'ImageBeforeProduceHTML', array( &$this, &$title,
703 &$file, &$frameParams, &$handlerParams, &$time, &$res ) ) ) {
707 global $wgContLang, $wgUser, $wgThumbLimits, $wgThumbUpright;
708 if ( $file && !$file->allowInlineDisplay() ) {
709 wfDebug( __METHOD__
.': '.$title->getPrefixedDBkey()." does not allow inline display\n" );
710 return $this->link( $title );
715 $hp =& $handlerParams;
717 // Clean up parameters
718 $page = isset( $hp['page'] ) ?
$hp['page'] : false;
719 if ( !isset( $fp['align'] ) ) $fp['align'] = '';
720 if ( !isset( $fp['alt'] ) ) $fp['alt'] = '';
722 $prefix = $postfix = '';
724 if ( 'center' == $fp['align'] )
726 $prefix = '<div class="center">';
728 $fp['align'] = 'none';
730 if ( $file && !isset( $hp['width'] ) ) {
731 $hp['width'] = $file->getWidth( $page );
733 if( isset( $fp['thumbnail'] ) ||
isset( $fp['framed'] ) ||
isset( $fp['frameless'] ) ||
!$hp['width'] ) {
734 $wopt = $wgUser->getOption( 'thumbsize' );
736 if( !isset( $wgThumbLimits[$wopt] ) ) {
737 $wopt = User
::getDefaultOption( 'thumbsize' );
740 // Reduce width for upright images when parameter 'upright' is used
741 if ( isset( $fp['upright'] ) && $fp['upright'] == 0 ) {
742 $fp['upright'] = $wgThumbUpright;
744 // Use width which is smaller: real image width or user preference width
745 // For caching health: If width scaled down due to upright parameter, round to full __0 pixel to avoid the creation of a lot of odd thumbs
746 $prefWidth = isset( $fp['upright'] ) ?
747 round( $wgThumbLimits[$wopt] * $fp['upright'], -1 ) :
748 $wgThumbLimits[$wopt];
749 if ( $hp['width'] <= 0 ||
$prefWidth < $hp['width'] ) {
750 $hp['width'] = $prefWidth;
755 if ( isset( $fp['thumbnail'] ) ||
isset( $fp['manualthumb'] ) ||
isset( $fp['framed'] ) ) {
757 # Create a thumbnail. Alignment depends on language
758 # writing direction, # right aligned for left-to-right-
759 # languages ("Western languages"), left-aligned
760 # for right-to-left-languages ("Semitic languages")
762 # If thumbnail width has not been provided, it is set
763 # to the default user option as specified in Language*.php
764 if ( $fp['align'] == '' ) {
765 $fp['align'] = $wgContLang->isRTL() ?
'left' : 'right';
767 return $prefix.$this->makeThumbLink2( $title, $file, $fp, $hp, $time, $query ).$postfix;
770 if ( $file && isset( $fp['frameless'] ) ) {
771 $srcWidth = $file->getWidth( $page );
772 # For "frameless" option: do not present an image bigger than the source (for bitmap-style images)
773 # This is the same behaviour as the "thumb" option does it already.
774 if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
775 $hp['width'] = $srcWidth;
779 if ( $file && $hp['width'] ) {
780 # Create a resized image, without the additional thumbnail features
781 $thumb = $file->transform( $hp );
787 $s = $this->makeBrokenImageLinkObj( $title, '', '', '', '', $time==true );
789 $s = $thumb->toHtml( array(
791 'desc-query' => $query,
793 'valign' => isset( $fp['valign'] ) ?
$fp['valign'] : false ,
794 'img-class' => isset( $fp['border'] ) ?
'thumbborder' : false ) );
796 if ( '' != $fp['align'] ) {
797 $s = "<div class=\"float{$fp['align']}\"><span>{$s}</span></div>";
799 return str_replace("\n", ' ',$prefix.$s.$postfix);
803 * Make HTML for a thumbnail including image, border and caption
804 * @param Title $title
805 * @param File $file File object or false if it doesn't exist
807 function makeThumbLinkObj( Title
$title, $file, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manualthumb = "" ) {
808 $frameParams = array(
813 if ( $framed ) $frameParams['framed'] = true;
814 if ( $manualthumb ) $frameParams['manualthumb'] = $manualthumb;
815 return $this->makeThumbLink2( $title, $file, $frameParams, $params );
818 function makeThumbLink2( Title
$title, $file, $frameParams = array(), $handlerParams = array(), $time = false, $query = "" ) {
819 global $wgStylePath, $wgContLang;
820 $exists = $file && $file->exists();
824 $hp =& $handlerParams;
826 $page = isset( $hp['page'] ) ?
$hp['page'] : false;
827 if ( !isset( $fp['align'] ) ) $fp['align'] = 'right';
828 if ( !isset( $fp['alt'] ) ) $fp['alt'] = '';
829 if ( !isset( $fp['caption'] ) ) $fp['caption'] = '';
831 if ( empty( $hp['width'] ) ) {
832 // Reduce width for upright images when parameter 'upright' is used
833 $hp['width'] = isset( $fp['upright'] ) ?
130 : 180;
838 $outerWidth = $hp['width'] +
2;
840 if ( isset( $fp['manualthumb'] ) ) {
841 # Use manually specified thumbnail
842 $manual_title = Title
::makeTitleSafe( NS_IMAGE
, $fp['manualthumb'] );
843 if( $manual_title ) {
844 $manual_img = wfFindFile( $manual_title );
846 $thumb = $manual_img->getUnscaledThumb();
851 } elseif ( isset( $fp['framed'] ) ) {
852 // Use image dimensions, don't scale
853 $thumb = $file->getUnscaledThumb( $page );
855 # Do not present an image bigger than the source, for bitmap-style images
856 # This is a hack to maintain compatibility with arbitrary pre-1.10 behaviour
857 $srcWidth = $file->getWidth( $page );
858 if ( $srcWidth && !$file->mustRender() && $hp['width'] > $srcWidth ) {
859 $hp['width'] = $srcWidth;
861 $thumb = $file->transform( $hp );
865 $outerWidth = $thumb->getWidth() +
2;
867 $outerWidth = $hp['width'] +
2;
872 $query = $query ?
'&page=' . urlencode( $page ) : 'page=' . urlencode( $page );
874 $url = $title->getLocalURL( $query );
876 $more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
878 $s = "<div class=\"thumb t{$fp['align']}\"><div class=\"thumbinner\" style=\"width:{$outerWidth}px;\">";
880 $s .= $this->makeBrokenImageLinkObj( $title, '', '', '', '', $time==true );
882 } elseif ( !$thumb ) {
883 $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) );
886 $s .= $thumb->toHtml( array(
888 'img-class' => 'thumbimage',
890 'desc-query' => $query ) );
891 if ( isset( $fp['framed'] ) ) {
894 $zoomicon = '<div class="magnify">'.
895 '<a href="'.$url.'" class="internal" title="'.$more.'">'.
896 '<img src="'.$wgStylePath.'/common/images/magnify-clip.png" ' .
897 'width="15" height="11" alt="" /></a></div>';
900 $s .= ' <div class="thumbcaption">'.$zoomicon.$fp['caption']."</div></div></div>";
901 return str_replace("\n", ' ', $s);
905 * Make a "broken" link to an image
907 * @param Title $title Image title
908 * @param string $text Link label
909 * @param string $query Query string
910 * @param string $trail Link trail
911 * @param string $prefix Link prefix
912 * @param bool $time, a file of a certain timestamp was requested
915 public function makeBrokenImageLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '', $time = false ) {
916 global $wgEnableUploads;
917 if( $title instanceof Title
) {
918 wfProfileIn( __METHOD__
);
919 $currentExists = $time ?
( wfFindFile( $title ) != false ) : false;
920 if( $wgEnableUploads && !$currentExists ) {
921 $upload = SpecialPage
::getTitleFor( 'Upload' );
923 $text = htmlspecialchars( $title->getPrefixedText() );
924 $redir = RepoGroup
::singleton()->getLocalRepo()->checkRedirect( $title );
926 return $this->makeKnownLinkObj( $title, $text, $query, $trail, $prefix );
928 $q = 'wpDestFile=' . $title->getPartialUrl();
931 list( $inside, $trail ) = self
::splitTrail( $trail );
932 $style = $this->getInternalLinkAttributesObj( $title, $text, 'new' );
933 wfProfileOut( __METHOD__
);
934 return '<a href="' . $upload->escapeLocalUrl( $q ) . '"'
935 . $style . '>' . $prefix . $text . $inside . '</a>' . $trail;
937 wfProfileOut( __METHOD__
);
938 return $this->makeKnownLinkObj( $title, $text, $query, $trail, $prefix );
941 return "<!-- ERROR -->{$prefix}{$text}{$trail}";
945 /** @deprecated use Linker::makeMediaLinkObj() */
946 function makeMediaLink( $name, $unused = '', $text = '', $time = false ) {
947 $nt = Title
::makeTitleSafe( NS_IMAGE
, $name );
948 return $this->makeMediaLinkObj( $nt, $text, $time );
952 * Create a direct link to a given uploaded file.
954 * @param $title Title object.
955 * @param $text String: pre-sanitized HTML
956 * @param $time string: time image was created
957 * @return string HTML
960 * @todo Handle invalid or missing images better.
962 function makeMediaLinkObj( $title, $text = '', $time = false ) {
963 if( is_null( $title ) ) {
964 ### HOTFIX. Instead of breaking, return empty string.
967 $img = wfFindFile( $title, $time );
969 $url = $img->getURL();
972 $upload = SpecialPage
::getTitleFor( 'Upload' );
973 $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $title->getDBkey() ) );
976 $alt = htmlspecialchars( $title->getText() );
980 $u = htmlspecialchars( $url );
981 return "<a href=\"{$u}\" class=\"$class\" title=\"{$alt}\">{$text}</a>";
985 /** @todo document */
986 function specialLink( $name, $key = '' ) {
989 if ( '' == $key ) { $key = strtolower( $name ); }
990 $pn = $wgContLang->ucfirst( $name );
991 return $this->makeKnownLink( $wgContLang->specialPage( $pn ),
995 /** @todo document */
996 function makeExternalLink( $url, $text, $escape = true, $linktype = '', $ns = null ) {
997 $style = $this->getExternalLinkAttributes( $url, $text, 'external ' . $linktype );
998 global $wgNoFollowLinks, $wgNoFollowNsExceptions;
999 if( $wgNoFollowLinks && !(isset($ns) && in_array($ns, $wgNoFollowNsExceptions)) ) {
1000 $style .= ' rel="nofollow"';
1002 $url = htmlspecialchars( $url );
1004 $text = htmlspecialchars( $text );
1007 $success = wfRunHooks('LinkerMakeExternalLink', array( &$url, &$text, &$link ) );
1009 wfDebug("Hook LinkerMakeExternalLink changed the output of link with url {$url} and text {$text} to {$link}", true);
1012 return '<a href="'.$url.'"'.$style.'>'.$text.'</a>';
1016 * Make user link (or user contributions for unregistered users)
1017 * @param $userId Integer: user id in database.
1018 * @param $userText String: user name in database
1019 * @return string HTML fragment
1022 function userLink( $userId, $userText ) {
1023 $encName = htmlspecialchars( $userText );
1024 if( $userId == 0 ) {
1025 $page = SpecialPage
::getTitleFor( 'Contributions', $userText );
1027 $page = Title
::makeTitle( NS_USER
, $userText );
1029 return $this->link( $page, $encName );
1033 * Generate standard user tool links (talk, contributions, block link, etc.)
1035 * @param int $userId User identifier
1036 * @param string $userText User name or IP address
1037 * @param bool $redContribsWhenNoEdits Should the contributions link be red if the user has no edits?
1038 * @param int $flags Customisation flags (e.g. self::TOOL_LINKS_NOBLOCK)
1039 * @param int $edits, user edit count (optional, for performance)
1042 public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits=null ) {
1043 global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
1044 $talkable = !( $wgDisableAnonTalk && 0 == $userId );
1045 $blockable = ( $wgSysopUserBans ||
0 == $userId ) && !$flags & self
::TOOL_LINKS_NOBLOCK
;
1049 $items[] = $this->userTalkLink( $userId, $userText );
1052 // check if the user has an edit
1054 if( $redContribsWhenNoEdits ) {
1055 $count = !is_null($edits) ?
$edits : User
::edits( $userId );
1057 $attribs['class'] = 'new';
1060 $contribsPage = SpecialPage
::getTitleFor( 'Contributions', $userText );
1062 $items[] = $this->link( $contribsPage, wfMsgHtml( 'contribslink' ), $attribs );
1064 if( $blockable && $wgUser->isAllowed( 'block' ) ) {
1065 $items[] = $this->blockLink( $userId, $userText );
1069 return ' (' . implode( ' | ', $items ) . ')';
1076 * Alias for userToolLinks( $userId, $userText, true );
1077 * @param int $userId User identifier
1078 * @param string $userText User name or IP address
1079 * @param int $edits, user edit count (optional, for performance)
1081 public function userToolLinksRedContribs( $userId, $userText, $edits=null ) {
1082 return $this->userToolLinks( $userId, $userText, true, 0, $edits );
1087 * @param $userId Integer: user id in database.
1088 * @param $userText String: user name in database.
1089 * @return string HTML fragment with user talk link
1092 function userTalkLink( $userId, $userText ) {
1093 $userTalkPage = Title
::makeTitle( NS_USER_TALK
, $userText );
1094 $userTalkLink = $this->link( $userTalkPage, wfMsgHtml( 'talkpagelinktext' ) );
1095 return $userTalkLink;
1099 * @param $userId Integer: userid
1100 * @param $userText String: user name in database.
1101 * @return string HTML fragment with block link
1104 function blockLink( $userId, $userText ) {
1105 $blockPage = SpecialPage
::getTitleFor( 'Blockip', $userText );
1106 $blockLink = $this->link( $blockPage, wfMsgHtml( 'blocklink' ) );
1111 * Generate a user link if the current user is allowed to view it
1112 * @param $rev Revision object.
1113 * @param $isPublic, bool, show only if all users can see it
1114 * @return string HTML
1116 function revUserLink( $rev, $isPublic = false ) {
1117 if( $rev->isDeleted( Revision
::DELETED_USER
) && $isPublic ) {
1118 $link = wfMsgHtml( 'rev-deleted-user' );
1119 } else if( $rev->userCan( Revision
::DELETED_USER
) ) {
1120 $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() );
1122 $link = wfMsgHtml( 'rev-deleted-user' );
1124 if( $rev->isDeleted( Revision
::DELETED_USER
) ) {
1125 return '<span class="history-deleted">' . $link . '</span>';
1131 * Generate a user tool link cluster if the current user is allowed to view it
1132 * @param $rev Revision object.
1133 * @param $isPublic, bool, show only if all users can see it
1134 * @return string HTML
1136 function revUserTools( $rev, $isPublic = false ) {
1137 if( $rev->isDeleted( Revision
::DELETED_USER
) && $isPublic ) {
1138 $link = wfMsgHtml( 'rev-deleted-user' );
1139 } else if( $rev->userCan( Revision
::DELETED_USER
) ) {
1140 $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ) .
1141 ' ' . $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() );
1143 $link = wfMsgHtml( 'rev-deleted-user' );
1145 if( $rev->isDeleted( Revision
::DELETED_USER
) ) {
1146 return ' <span class="history-deleted">' . $link . '</span>';
1152 * This function is called by all recent changes variants, by the page history,
1153 * and by the user contributions list. It is responsible for formatting edit
1154 * comments. It escapes any HTML in the comment, but adds some CSS to format
1155 * auto-generated comments (from section editing) and formats [[wikilinks]].
1157 * @author Erik Moeller <moeller@scireview.de>
1159 * Note: there's not always a title to pass to this function.
1160 * Since you can't set a default parameter for a reference, I've turned it
1161 * temporarily to a value pass. Should be adjusted further. --brion
1163 * @param string $comment
1164 * @param mixed $title Title object (to generate link to the section in autocomment) or null
1165 * @param bool $local Whether section links should refer to local page
1167 function formatComment($comment, $title = NULL, $local = false) {
1168 wfProfileIn( __METHOD__
);
1170 # Sanitize text a bit:
1171 $comment = str_replace( "\n", " ", $comment );
1172 $comment = htmlspecialchars( $comment );
1174 # Render autocomments and make links:
1175 $comment = $this->formatAutoComments( $comment, $title, $local );
1176 $comment = $this->formatLinksInComment( $comment );
1178 wfProfileOut( __METHOD__
);
1183 * The pattern for autogen comments is / * foo * /, which makes for
1185 * We look for all comments, match any text before and after the comment,
1186 * add a separator where needed and format the comment itself with CSS
1187 * Called by Linker::formatComment.
1189 * @param string $comment Comment text
1190 * @param object $title An optional title object used to links to sections
1191 * @return string $comment formatted comment
1193 * @todo Document the $local parameter.
1195 private function formatAutocomments( $comment, $title = NULL, $local = false ) {
1197 while (preg_match('!(.*)/\*\s*(.*?)\s*\*/(.*)!', $comment,$match)) {
1205 # Generate a valid anchor name from the section title.
1206 # Hackish, but should generally work - we strip wiki
1207 # syntax, including the magic [[: that is used to
1208 # "link rather than show" in case of images and
1209 # interlanguage links.
1210 $section = str_replace( '[[:', '', $section );
1211 $section = str_replace( '[[', '', $section );
1212 $section = str_replace( ']]', '', $section );
1214 $sectionTitle = Title
::newFromText( '#' . $section);
1216 $sectionTitle = wfClone( $title );
1217 $sectionTitle->mFragment
= $section;
1219 $link = $this->link( $sectionTitle, wfMsgForContent( 'sectionlink' ) );
1221 $auto = $link . $auto;
1223 # written summary $presep autocomment (summary /* section */)
1224 $auto = wfMsgExt( 'autocomment-prefix', array( 'escapenoentities', 'content' ) ) . $auto;
1227 # autocomment $postsep written summary (/* section */ summary)
1228 $auto .= wfMsgExt( 'colon-separator', array( 'escapenoentities', 'content' ) );
1230 $auto = '<span class="autocomment">' . $auto . '</span>';
1231 $comment = $pre . $auto . $post;
1238 * Formats wiki links and media links in text; all other wiki formatting
1241 * @fixme doesn't handle sub-links as in image thumb texts like the main parser
1242 * @param string $comment Text to format links in
1245 public function formatLinksInComment( $comment ) {
1246 return preg_replace_callback(
1247 '/\[\[:?(.*?)(\|(.*?))*\]\]([^[]*)/',
1248 array( $this, 'formatLinksInCommentCallback' ),
1252 protected function formatLinksInCommentCallback( $match ) {
1255 $medians = '(?:' . preg_quote( MWNamespace
::getCanonicalName( NS_MEDIA
), '/' ) . '|';
1256 $medians .= preg_quote( $wgContLang->getNsText( NS_MEDIA
), '/' ) . '):';
1258 $comment = $match[0];
1260 # fix up urlencoded title texts (copied from Parser::replaceInternalLinks)
1261 if( strpos( $match[1], '%' ) !== false ) {
1262 $match[1] = str_replace( array('<', '>'), array('<', '>'), urldecode($match[1]) );
1265 # Handle link renaming [[foo|text]] will show link as "text"
1266 if( "" != $match[3] ) {
1271 $submatch = array();
1272 if( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) {
1273 # Media link; trail not supported.
1274 $linkRegexp = '/\[\[(.*?)\]\]/';
1275 $thelink = $this->makeMediaLink( $submatch[1], "", $text );
1277 # Other kind of link
1278 if( preg_match( $wgContLang->linkTrail(), $match[4], $submatch ) ) {
1279 $trail = $submatch[1];
1283 $linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/';
1284 if (isset($match[1][0]) && $match[1][0] == ':')
1285 $match[1] = substr($match[1], 1);
1286 $thelink = $this->makeLink( $match[1], $text, "", $trail );
1288 $comment = preg_replace( $linkRegexp, StringUtils
::escapeRegexReplacement( $thelink ), $comment, 1 );
1294 * Wrap a comment in standard punctuation and formatting if
1295 * it's non-empty, otherwise return empty string.
1297 * @param string $comment
1298 * @param mixed $title Title object (to generate link to section in autocomment) or null
1299 * @param bool $local Whether section links should refer to local page
1303 function commentBlock( $comment, $title = NULL, $local = false ) {
1304 // '*' used to be the comment inserted by the software way back
1305 // in antiquity in case none was provided, here for backwards
1306 // compatability, acc. to brion -ævar
1307 if( $comment == '' ||
$comment == '*' ) {
1310 $formatted = $this->formatComment( $comment, $title, $local );
1311 return " <span class=\"comment\">($formatted)</span>";
1316 * Wrap and format the given revision's comment block, if the current
1317 * user is allowed to view it.
1319 * @param Revision $rev
1320 * @param bool $local Whether section links should refer to local page
1321 * @param $isPublic, show only if all users can see it
1322 * @return string HTML
1324 function revComment( Revision
$rev, $local = false, $isPublic = false ) {
1325 if( $rev->isDeleted( Revision
::DELETED_COMMENT
) && $isPublic ) {
1326 $block = " <span class=\"comment\">" . wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
1327 } else if( $rev->userCan( Revision
::DELETED_COMMENT
) ) {
1328 $block = $this->commentBlock( $rev->getRawComment(), $rev->getTitle(), $local );
1330 $block = " <span class=\"comment\">" . wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
1332 if( $rev->isDeleted( Revision
::DELETED_COMMENT
) ) {
1333 return " <span class=\"history-deleted\">$block</span>";
1338 public function formatRevisionSize( $size ) {
1340 $stxt = wfMsgExt( 'historyempty', 'parsemag' );
1343 $stxt = wfMsgExt( 'nbytes', 'parsemag', $wgLang->formatNum( $size ) );
1346 $stxt = htmlspecialchars( $stxt );
1347 return "<span class=\"history-size\">$stxt</span>";
1350 /** @todo document */
1351 function tocIndent() {
1355 /** @todo document */
1356 function tocUnindent($level) {
1357 return "</li>\n" . str_repeat( "</ul>\n</li>\n", $level>0 ?
$level : 0 );
1361 * parameter level defines if we are on an indentation level
1363 function tocLine( $anchor, $tocline, $tocnumber, $level ) {
1364 return "\n<li class=\"toclevel-$level\"><a href=\"#" .
1365 $anchor . '"><span class="tocnumber">' .
1366 $tocnumber . '</span> <span class="toctext">' .
1367 $tocline . '</span></a>';
1370 /** @todo document */
1371 function tocLineEnd() {
1375 /** @todo document */
1376 function tocList($toc) {
1377 global $wgJsMimeType;
1378 $title = wfMsgHtml('toc') ;
1380 '<table id="toc" class="toc" summary="' . $title .'"><tr><td>'
1381 . '<div id="toctitle"><h2>' . $title . "</h2></div>\n"
1383 # no trailing newline, script should not be wrapped in a
1385 . "</ul>\n</td></tr></table>"
1386 . '<script type="' . $wgJsMimeType . '">'
1387 . ' if (window.showTocToggle) {'
1388 . ' var tocShowText = "' . wfEscapeJsString( wfMsg('showtoc') ) . '";'
1389 . ' var tocHideText = "' . wfEscapeJsString( wfMsg('hidetoc') ) . '";'
1390 . ' showTocToggle();'
1396 * Used to generate section edit links that point to "other" pages
1397 * (sections that are really part of included pages).
1399 * @param $title Title string.
1400 * @param $section Integer: section number.
1402 public function editSectionLinkForOther( $title, $section ) {
1403 wfDeprecated( __METHOD__
);
1404 $title = Title
::newFromText( $title );
1405 return $this->doEditSectionLink( $title, $section );
1409 * @param $nt Title object.
1410 * @param $section Integer: section number.
1411 * @param $hint Link String: title, or default if omitted or empty
1413 public function editSectionLink( Title
$nt, $section, $hint = '' ) {
1414 wfDeprecated( __METHOD__
);
1415 if( $hint === '' ) {
1416 # No way to pass an actual empty $hint here! The new interface al-
1417 # lows this, so we have to do this for compatibility.
1420 return $this->doEditSectionLink( $nt, $section, $hint );
1424 * Create a section edit link. This supersedes editSectionLink() and
1425 * editSectionLinkForOther().
1427 * @param $nt Title The title being linked to (may not be the same as
1428 * $wgTitle, if the section is included from a template)
1429 * @param $section string The designation of the section being pointed to,
1430 * to be included in the link, like "§ion=$section"
1431 * @param $tooltip string The tooltip to use for the link: will be escaped
1432 * and wrapped in the 'editsectionhint' message
1433 * @return string HTML to use for edit link
1435 public function doEditSectionLink( Title
$nt, $section, $tooltip = null ) {
1437 if( !is_null( $tooltip ) ) {
1438 $attribs['title'] = wfMsg( 'editsectionhint', $tooltip );
1440 $url = $this->link( $nt, wfMsg('editsection'),
1442 array( 'action' => 'edit', 'section' => $section ),
1443 array( 'noclasses', 'known' )
1446 # Run the old hook. This takes up half of the function . . . hopefully
1447 # we can rid of it someday.
1450 $attribs = wfMsgHtml( 'editsectionhint', htmlspecialchars( $tooltip ) );
1451 $attribs = " title=\"$attribs\"";
1454 wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $attribs, $url, &$result ) );
1455 if( !is_null( $result ) ) {
1456 # For reverse compatibility, add the brackets *after* the hook is
1457 # run, and even add them to hook-provided text. (This is the main
1458 # reason that the EditSectionLink hook is deprecated in favor of
1459 # DoEditSectionLink: it can't change the brackets or the span.)
1460 $result = wfMsgHtml( 'editsection-brackets', $url );
1461 return "<span class=\"editsection\">$result</span>";
1464 # Add the brackets and the span, and *then* run the nice new hook, with
1465 # clean and non-redundant arguments.
1466 $result = wfMsgHtml( 'editsection-brackets', $url );
1467 $result = "<span class=\"editsection\">$result</span>";
1469 wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result ) );
1474 * Create a headline for content
1476 * @param int $level The level of the headline (1-6)
1477 * @param string $attribs Any attributes for the headline, starting with a space and ending with '>'
1478 * This *must* be at least '>' for no attribs
1479 * @param string $anchor The anchor to give the headline (the bit after the #)
1480 * @param string $text The text of the header
1481 * @param string $link HTML to add for the section edit link
1483 * @return string HTML headline
1485 public function makeHeadline( $level, $attribs, $anchor, $text, $link ) {
1486 return "<a name=\"$anchor\"></a><h$level$attribs$link <span class=\"mw-headline\">$text</span></h$level>";
1490 * Split a link trail, return the "inside" portion and the remainder of the trail
1491 * as a two-element array
1495 static function splitTrail( $trail ) {
1496 static $regex = false;
1497 if ( $regex === false ) {
1499 $regex = $wgContLang->linkTrail();
1502 if ( '' != $trail ) {
1504 if ( preg_match( $regex, $trail, $m ) ) {
1509 return array( $inside, $trail );
1513 * Generate a rollback link for a given revision. Currently it's the
1514 * caller's responsibility to ensure that the revision is the top one. If
1515 * it's not, of course, the user will get an error message.
1517 * If the calling page is called with the parameter &bot=1, all rollback
1518 * links also get that parameter. It causes the edit itself and the rollback
1519 * to be marked as "bot" edits. Bot edits are hidden by default from recent
1520 * changes, so this allows sysops to combat a busy vandal without bothering
1523 * @param Revision $rev
1525 function generateRollback( $rev ) {
1526 return '<span class="mw-rollback-link">['
1527 . $this->buildRollbackLink( $rev )
1532 * Build a raw rollback link, useful for collections of "tool" links
1534 * @param Revision $rev
1537 public function buildRollbackLink( $rev ) {
1538 global $wgRequest, $wgUser;
1539 $title = $rev->getTitle();
1540 $query = array( 'action' => 'rollback' );
1541 if( $wgRequest->getBool( 'bot' ) ) {
1542 $query['bot'] = '1';
1544 $query['token'] = $wgUser->editToken( array( $title->getPrefixedText(),
1545 $rev->getUserText() ) );
1546 return $this->link( $title, wfMsgHtml( 'rollbacklink' ), array(),
1551 * Returns HTML for the "templates used on this page" list.
1553 * @param array $templates Array of templates from Article::getUsedTemplate
1555 * @param bool $preview Whether this is for a preview
1556 * @param bool $section Whether this is for a section edit
1557 * @return string HTML output
1559 public function formatTemplates( $templates, $preview = false, $section = false) {
1561 wfProfileIn( __METHOD__
);
1563 $sk = $wgUser->getSkin();
1566 if ( count( $templates ) > 0 ) {
1567 # Do a batch existence check
1568 $batch = new LinkBatch
;
1569 foreach( $templates as $title ) {
1570 $batch->addObj( $title );
1574 # Construct the HTML
1575 $outText = '<div class="mw-templatesUsedExplanation">';
1577 $outText .= wfMsgExt( 'templatesusedpreview', array( 'parse' ) );
1578 } elseif ( $section ) {
1579 $outText .= wfMsgExt( 'templatesusedsection', array( 'parse' ) );
1581 $outText .= wfMsgExt( 'templatesused', array( 'parse' ) );
1583 $outText .= '</div><ul>';
1585 usort( $templates, array( 'Title', 'compare' ) );
1586 foreach ( $templates as $titleObj ) {
1587 $r = $titleObj->getRestrictions( 'edit' );
1588 if ( in_array( 'sysop', $r ) ) {
1589 $protected = wfMsgExt( 'template-protected', array( 'parseinline' ) );
1590 } elseif ( in_array( 'autoconfirmed', $r ) ) {
1591 $protected = wfMsgExt( 'template-semiprotected', array( 'parseinline' ) );
1595 $outText .= '<li>' . $sk->link( $titleObj ) . ' ' . $protected . '</li>';
1597 $outText .= '</ul>';
1599 wfProfileOut( __METHOD__
);
1604 * Returns HTML for the "hidden categories on this page" list.
1606 * @param array $hiddencats Array of hidden categories from Article::getHiddenCategories
1608 * @return string HTML output
1610 public function formatHiddenCategories( $hiddencats) {
1611 global $wgUser, $wgLang;
1612 wfProfileIn( __METHOD__
);
1614 $sk = $wgUser->getSkin();
1617 if ( count( $hiddencats ) > 0 ) {
1618 # Construct the HTML
1619 $outText = '<div class="mw-hiddenCategoriesExplanation">';
1620 $outText .= wfMsgExt( 'hiddencategories', array( 'parse' ), $wgLang->formatnum( count( $hiddencats ) ) );
1621 $outText .= '</div><ul>';
1623 foreach ( $hiddencats as $titleObj ) {
1624 $outText .= '<li>' . $sk->link( $titleObj, null, array(), array(), 'known' ) . '</li>'; # If it's hidden, it must exist - no need to check with a LinkBatch
1626 $outText .= '</ul>';
1628 wfProfileOut( __METHOD__
);
1633 * Format a size in bytes for output, using an appropriate
1634 * unit (B, KB, MB or GB) according to the magnitude in question
1636 * @param $size Size to format
1639 public function formatSize( $size ) {
1641 return htmlspecialchars( $wgLang->formatSize( $size ) );
1645 * Given the id of an interface element, constructs the appropriate title
1646 * and accesskey attributes from the system messages. (Note, this is usu-
1647 * ally the id but isn't always, because sometimes the accesskey needs to
1648 * go on a different element than the id, for reverse-compatibility, etc.)
1650 * @param string $name Id of the element, minus prefixes.
1651 * @return string title and accesskey attributes, ready to drop in an
1652 * element (e.g., ' title="This does something [x]" accesskey="x"').
1654 public function tooltipAndAccesskey($name) {
1655 $fname="Linker::tooltipAndAccesskey";
1656 wfProfileIn($fname);
1659 $tooltip = wfMsg('tooltip-'.$name);
1660 if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
1661 // Compatibility: formerly some tooltips had [alt-.] hardcoded
1662 $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
1663 $out .= ' title="'.htmlspecialchars($tooltip);
1665 $accesskey = wfMsg('accesskey-'.$name);
1666 if ($accesskey && $accesskey != '-' && !wfEmptyMsg('accesskey-'.$name, $accesskey)) {
1667 if ($out) $out .= " [$accesskey]\" accesskey=\"$accesskey\"";
1668 else $out .= " title=\"[$accesskey]\" accesskey=\"$accesskey\"";
1672 wfProfileOut($fname);
1677 * Given the id of an interface element, constructs the appropriate title
1678 * attribute from the system messages. (Note, this is usually the id but
1679 * isn't always, because sometimes the accesskey needs to go on a different
1680 * element than the id, for reverse-compatibility, etc.)
1682 * @param string $name Id of the element, minus prefixes.
1683 * @return string title attribute, ready to drop in an element
1684 * (e.g., ' title="This does something"').
1686 public function tooltip($name) {
1689 $tooltip = wfMsg('tooltip-'.$name);
1690 if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
1691 $out = ' title="'.htmlspecialchars($tooltip).'"';