X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FLinker.php;h=93ac43def2408f3b7b14b2a6dc842bce87d48a76;hb=7a7cf1a2f9468e0ac27dcd078da4a53e10cbe017;hp=b913ebf10cfff4901df6daa21e6d62a535d3d565;hpb=bd600419c4e0cf18694347c3f19e1df10e5c5d31;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Linker.php b/includes/Linker.php index b913ebf10c..93ac43def2 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -17,25 +17,15 @@ class Linker { function __construct() {} - /** - * @deprecated - */ - function postParseLinkColour( $s = null ) { - wfDeprecated( __METHOD__ ); - return null; - } - /** * Get the appropriate HTML attributes to add to the "a" element of an ex- * ternal link, as created by [wikisyntax]. * - * @param string $title The (unescaped) title text for the link - * @param string $unused Unused * @param string $class The contents of the class attribute; if an empty * string is passed, which is the default value, defaults to 'external'. */ - function getExternalLinkAttributes( $title, $unused = null, $class='' ) { - return $this->getLinkAttributesInternal( $title, $class, 'external' ); + function getExternalLinkAttributes( $class = 'external' ) { + return $this->getLinkAttributesInternal( '', $class ); } /** @@ -48,7 +38,7 @@ class Linker { * @param string $class The contents of the class attribute; if an empty * string is passed, which is the default value, defaults to 'external'. */ - function getInterwikiLinkAttributes( $title, $unused = null, $class='' ) { + function getInterwikiLinkAttributes( $title, $unused = null, $class = 'external' ) { global $wgContLang; # FIXME: We have a whole bunch of handling here that doesn't happen in @@ -57,7 +47,7 @@ class Linker { $title = $wgContLang->checkTitleEncoding( $title ); $title = preg_replace( '/[\\x00-\\x1f]/', ' ', $title ); - return $this->getLinkAttributesInternal( $title, $class, 'external' ); + return $this->getLinkAttributesInternal( $title, $class ); } /** @@ -95,20 +85,16 @@ class Linker { /** * Common code for getLinkAttributesX functions */ - private function getLinkAttributesInternal( $title, $class, $classDefault = false ) { + private function getLinkAttributesInternal( $title, $class ) { $title = htmlspecialchars( $title ); - if( $class === '' and $classDefault !== false ) { - # FIXME: Parameter defaults the hard way! We should just have - # $class = 'external' or whatever as the default in the externally- - # exposed functions, not $class = ''. - $class = $classDefault; - } $class = htmlspecialchars( $class ); $r = ''; - if( $class !== '' ) { + if ( $class != '' ) { $r .= " class=\"$class\""; } - $r .= " title=\"$title\""; + if ( $title != '') { + $r .= " title=\"$title\""; + } return $r; } @@ -124,7 +110,9 @@ class Linker { if ( $t->isRedirect() ) { # Page is a redirect $colour = 'mw-redirect'; - } elseif ( $threshold > 0 && $t->getLength() < $threshold && MWNamespace::isContent( $t->getNamespace() ) ) { + } elseif ( $threshold > 0 && + $t->exists() && $t->getLength() < $threshold && + MWNamespace::isContent( $t->getNamespace() ) ) { # Page is a stub $colour = 'stub'; } @@ -149,14 +137,14 @@ class Linker { * the link text. This is raw HTML and will not be escaped. If null, * defaults to the prefixed text of the Title; or if the Title is just a * fragment, the contents of the fragment. - * @param $query array The query string to append to the URL - * you're linking to, in key => value array form. Query keys and values - * will be URL-encoded. * @param $customAttribs array A key => value array of extra HTML attri- * butes, 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. + * @param $query array The query string to append to the URL + * you're linking to, in key => value array form. Query keys and values + * will be URL-encoded. * @param $options mixed String or array of strings: * 'known': Page is known to exist, so don't check if it does. * 'broken': Page is known not to exist, so don't check if it does. @@ -164,30 +152,31 @@ class Linker { * "stub", "mw-redirect", "extiw"). Only use the class attribute * provided, if any, so you get a simple blue link with no funny i- * cons. + * 'forcearticlepath': Use the article path always, even with a querystring. + * Has compatibility issues on some setups, so avoid wherever possible. * @return string HTML attribute */ public function link( $target, $text = null, $customAttribs = array(), $query = array(), $options = array() ) { wfProfileIn( __METHOD__ ); if( !$target instanceof Title ) { - throw new MWException( 'Linker::link passed invalid target' ); + return "$text"; } $options = (array)$options; + $ret = null; + if( !wfRunHooks( 'LinkBegin', array( $this, $target, &$text, + &$customAttribs, &$query, &$options, &$ret ) ) ) { + wfProfileOut( __METHOD__ ); + return $ret; + } + # Normalize the Title if it's a special page $target = $this->normaliseSpecialPage( $target ); # If we don't know whether the page exists, let's find out. wfProfileIn( __METHOD__ . '-checkPageExistence' ); if( !in_array( 'known', $options ) and !in_array( 'broken', $options ) ) { - if( $target->getNamespace() == NS_SPECIAL ) { - if( SpecialPage::exists( $target->getDbKey() ) ) { - $options []= 'known'; - } else { - $options []= 'broken'; - } - } elseif( $target->isAlwaysKnown() or - ($target->getPrefixedText() == '' and $target->getFragment() != '') - or $target->exists() ) { + if( $target->isKnown() ) { $options []= 'known'; } else { $options []= 'broken'; @@ -195,8 +184,18 @@ class Linker { } wfProfileOut( __METHOD__ . '-checkPageExistence' ); + $oldquery = array(); + if( in_array( "forcearticlepath", $options ) && $query ){ + $oldquery = $query; + $query = array(); + } + # Note: we want the href attribute first, for prettiness. $attribs = array( 'href' => $this->linkUrl( $target, $query, $options ) ); + if( in_array( 'forcearticlepath', $options ) && $oldquery ){ + $attribs['href'] = wfAppendQuery( $attribs['href'], wfArrayToCgi( $oldquery ) ); + } + $attribs = array_merge( $attribs, $this->linkAttribs( $target, $customAttribs, $options ) @@ -205,16 +204,31 @@ class Linker { $text = $this->linkText( $target ); } - $ret = Xml::openElement( 'a', $attribs ) - . $text - . Xml::closeElement( 'a' ); + $ret = null; + if( wfRunHooks( 'LinkEnd', array( $this, $target, $options, &$text, &$attribs, &$ret ) ) ) { + $ret = Xml::openElement( 'a', $attribs ) . $text . Xml::closeElement( 'a' ); + } wfProfileOut( __METHOD__ ); return $ret; } + /** + * Identical to link(), except $options defaults to 'known'. + */ + public function linkKnown( $target, $text = null, $customAttribs = array(), $query = array(), $options = 'known' ) { + return $this->link( $target, $text, $customAttribs, $query, $options ); + } + private function linkUrl( $target, $query, $options ) { wfProfileIn( __METHOD__ ); + # We don't want to include fragments for broken links, because they + # generally make no sense. + if( in_array( 'broken', $options ) and $target->mFragment !== '' ) { + $target = clone $target; + $target->mFragment = ''; + } + # If it's a broken link, add the appropriate query pieces, unless # there's already an action specified, or unless 'edit' makes no sense # (i.e., for a nonexistent special page). @@ -235,7 +249,7 @@ class Linker { if( !in_array( 'noclasses', $options ) ) { wfProfileIn( __METHOD__ . '-getClasses' ); - # Build the classes. + # Now build the classes. $classes = array(); if( in_array( 'broken', $options ) ) { @@ -247,12 +261,12 @@ class Linker { } # Note that redirects never count as stubs here. - if ( $target->isRedirect() ) { + if ( !in_array( 'broken', $options ) && $target->isRedirect() ) { $classes[] = 'mw-redirect'; } elseif( $target->isContentPage() ) { # Check for stub. $threshold = $wgUser->getOption( 'stubthreshold' ); - if( $threshold > 0 and $target->getLength() < $threshold ) { + if( $threshold > 0 and $target->exists() and $target->getLength() < $threshold ) { $classes[] = 'stub'; } } @@ -263,7 +277,10 @@ class Linker { } # Get a default title attribute. - if( in_array( 'known', $options ) ) { + if( $target->getPrefixedText() == '' ) { + # A link like [[#Foo]]. This used to mean an empty title + # attribute, but that's silly. Just don't output a title. + } elseif( in_array( 'known', $options ) ) { $defaults['title'] = $target->getPrefixedText(); } else { $defaults['title'] = wfMsg( 'red-link-title', $target->getPrefixedText() ); @@ -285,6 +302,11 @@ class Linker { } private function linkText( $target ) { + # We might be passed a non-Title by make*LinkObj(). Fail gracefully. + if( !$target instanceof Title ) { + return ''; + } + # If the target is just a fragment, with no title, we return the frag- # ment text. Otherwise, we return the title text itself. if( $target->getPrefixedText() === '' and $target->getFragment() !== '' ) { @@ -293,286 +315,6 @@ class Linker { return htmlspecialchars( $target->getPrefixedText() ); } - /** - * This function is a shortcut to makeLinkObj(Title::newFromText($title),...). Do not call - * it if you already have a title object handy. See makeLinkObj for further documentation. - * - * @param $title String: the text of the title - * @param $text String: link text - * @param $query String: optional query part - * @param $trail String: optional trail. Alphabetic characters at the start of this string will - * be included in the link text. Other characters will be appended after - * the end of the link. - */ - function makeLink( $title, $text = '', $query = '', $trail = '' ) { - wfProfileIn( __METHOD__ ); - $nt = Title::newFromText( $title ); - if ( $nt instanceof Title ) { - $result = $this->makeLinkObj( $nt, $text, $query, $trail ); - } else { - wfDebug( 'Invalid title passed to Linker::makeLink(): "'.$title."\"\n" ); - $result = $text == "" ? $title : $text; - } - - wfProfileOut( __METHOD__ ); - return $result; - } - - /** - * This function is a shortcut to makeKnownLinkObj(Title::newFromText($title),...). Do not call - * it if you already have a title object handy. See makeKnownLinkObj for further documentation. - * - * @param $title String: the text of the title - * @param $text String: link text - * @param $query String: optional query part - * @param $trail String: optional trail. Alphabetic characters at the start of this string will - * be included in the link text. Other characters will be appended after - * the end of the link. - */ - function makeKnownLink( $title, $text = '', $query = '', $trail = '', $prefix = '',$aprops = '') { - $nt = Title::newFromText( $title ); - if ( $nt instanceof Title ) { - return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix , $aprops ); - } else { - wfDebug( 'Invalid title passed to Linker::makeKnownLink(): "'.$title."\"\n" ); - return $text == '' ? $title : $text; - } - } - - /** - * This function is a shortcut to makeBrokenLinkObj(Title::newFromText($title),...). Do not call - * it if you already have a title object handy. See makeBrokenLinkObj for further documentation. - * - * @param string $title The text of the title - * @param string $text Link text - * @param string $query Optional query part - * @param string $trail Optional trail. Alphabetic characters at the start of this string will - * be included in the link text. Other characters will be appended after - * the end of the link. - */ - function makeBrokenLink( $title, $text = '', $query = '', $trail = '' ) { - $nt = Title::newFromText( $title ); - if ( $nt instanceof Title ) { - return $this->makeBrokenLinkObj( $nt, $text, $query, $trail ); - } else { - wfDebug( 'Invalid title passed to Linker::makeBrokenLink(): "'.$title."\"\n" ); - return $text == '' ? $title : $text; - } - } - - /** - * @deprecated use makeColouredLinkObj - * - * This function is a shortcut to makeStubLinkObj(Title::newFromText($title),...). Do not call - * it if you already have a title object handy. See makeStubLinkObj for further documentation. - * - * @param $title String: the text of the title - * @param $text String: link text - * @param $query String: optional query part - * @param $trail String: optional trail. Alphabetic characters at the start of this string will - * be included in the link text. Other characters will be appended after - * the end of the link. - */ - function makeStubLink( $title, $text = '', $query = '', $trail = '' ) { - $nt = Title::newFromText( $title ); - if ( $nt instanceof Title ) { - return $this->makeStubLinkObj( $nt, $text, $query, $trail ); - } else { - wfDebug( 'Invalid title passed to Linker::makeStubLink(): "'.$title."\"\n" ); - return $text == '' ? $title : $text; - } - } - - /** - * Make a link for a title which may or may not be in the database. If you need to - * call this lots of times, pre-fill the link cache with a LinkBatch, otherwise each - * call to this will result in a DB query. - * - * @param $nt Title: the title object to make the link from, e.g. from - * Title::newFromText. - * @param $text String: link text - * @param $query String: optional query part - * @param $trail String: optional trail. Alphabetic characters at the start of this string will - * be included in the link text. Other characters will be appended after - * the end of the link. - * @param $prefix String: optional prefix. As trail, only before instead of after. - */ - function makeLinkObj( Title $nt, $text= '', $query = '', $trail = '', $prefix = '' ) { - global $wgUser; - wfProfileIn( __METHOD__ ); - - if ( $nt->isExternal() ) { - $u = $nt->getFullURL(); - $link = $nt->getPrefixedURL(); - if ( '' == $text ) { $text = $nt->getPrefixedText(); } - $style = $this->getInterwikiLinkAttributes( $link, $text, 'extiw' ); - - $inside = ''; - if ( '' != $trail ) { - $m = array(); - if ( preg_match( '/^([a-z]+)(.*)$$/sD', $trail, $m ) ) { - $inside = $m[1]; - $trail = $m[2]; - } - } - $t = "{$text}{$inside}"; - - wfProfileOut( __METHOD__ ); - return $t; - } elseif ( $nt->isAlwaysKnown() ) { - # Image links, special page links and self-links with fragments are always known. - $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix ); - } else { - wfProfileIn( __METHOD__.'-immediate' ); - - # Handles links to special pages which do not exist in the database: - if( $nt->getNamespace() == NS_SPECIAL ) { - if( SpecialPage::exists( $nt->getDBkey() ) ) { - $retVal = $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix ); - } else { - $retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix ); - } - wfProfileOut( __METHOD__.'-immediate' ); - wfProfileOut( __METHOD__ ); - return $retVal; - } - - # Work out link colour immediately - $aid = $nt->getArticleID() ; - if ( 0 == $aid ) { - $retVal = $this->makeBrokenLinkObj( $nt, $text, $query, $trail, $prefix ); - } else { - $colour = ''; - if ( $nt->isContentPage() ) { - $threshold = $wgUser->getOption('stubthreshold'); - $colour = $this->getLinkColour( $nt, $threshold ); - } - $retVal = $this->makeColouredLinkObj( $nt, $colour, $text, $query, $trail, $prefix ); - } - wfProfileOut( __METHOD__.'-immediate' ); - } - wfProfileOut( __METHOD__ ); - return $retVal; - } - - /** - * Make a link for a title which definitely exists. This is faster than makeLinkObj because - * it doesn't have to do a database query. It's also valid for interwiki titles and special - * pages. - * - * @param $nt Title object of target page - * @param $text String: text to replace the title - * @param $query String: link target - * @param $trail String: text after link - * @param $prefix String: text before link text - * @param $aprops String: extra attributes to the a-element - * @param $style String: style to apply - if empty, use getInternalLinkAttributesObj instead - * @return the a-element - */ - function makeKnownLinkObj( Title $title, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) { - wfProfileIn( __METHOD__ ); - - $nt = $this->normaliseSpecialPage( $title ); - - $u = $nt->escapeLocalURL( $query ); - if ( $nt->getFragment() != '' ) { - if( $nt->getPrefixedDbkey() == '' ) { - $u = ''; - if ( '' == $text ) { - $text = htmlspecialchars( $nt->getFragment() ); - } - } - $u .= $nt->getFragmentForURL(); - } - if ( $text == '' ) { - $text = htmlspecialchars( $nt->getPrefixedText() ); - } - if ( $style == '' ) { - $style = $this->getInternalLinkAttributesObj( $nt, $text ); - } - - if ( $aprops !== '' ) $aprops = " $aprops"; - - list( $inside, $trail ) = Linker::splitTrail( $trail ); - $r = "{$prefix}{$text}{$inside}{$trail}"; - wfProfileOut( __METHOD__ ); - return $r; - } - - /** - * Make a red link to the edit page of a given title. - * - * @param $nt Title object of the target page - * @param $text String: Link text - * @param $query String: Optional query part - * @param $trail String: Optional trail. Alphabetic characters at the start of this string will - * be included in the link text. Other characters will be appended after - * the end of the link. - */ - function makeBrokenLinkObj( Title $title, $text = '', $query = '', $trail = '', $prefix = '' ) { - wfProfileIn( __METHOD__ ); - - $nt = $this->normaliseSpecialPage( $title ); - - if( $nt->getNamespace() == NS_SPECIAL ) { - $q = $query; - } else if ( '' == $query ) { - $q = 'action=edit&redlink=1'; - } else { - $q = 'action=edit&redlink=1&'.$query; - } - $u = $nt->escapeLocalURL( $q ); - - $titleText = $nt->getPrefixedText(); - if ( '' == $text ) { - $text = htmlspecialchars( $titleText ); - } - $titleAttr = wfMsg( 'red-link-title', $titleText ); - $style = $this->getInternalLinkAttributesObj( $nt, $text, 'new', $titleAttr ); - list( $inside, $trail ) = Linker::splitTrail( $trail ); - - wfRunHooks( 'BrokenLink', array( &$this, $nt, $query, &$u, &$style, &$prefix, &$text, &$inside, &$trail ) ); - $s = "{$prefix}{$text}{$inside}{$trail}"; - - wfProfileOut( __METHOD__ ); - return $s; - } - - /** - * @deprecated use makeColouredLinkObj - * - * Make a brown link to a short article. - * - * @param $nt Title object of the target page - * @param $text String: link text - * @param $query String: optional query part - * @param $trail String: optional trail. Alphabetic characters at the start of this string will - * be included in the link text. Other characters will be appended after - * the end of the link. - */ - function makeStubLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) { - wfDeprecated( __METHOD__ ); - return $this->makeColouredLinkObj( $nt, 'stub', $text, $query, $trail, $prefix ); - } - - /** - * Make a coloured link. - * - * @param $nt Title object of the target page - * @param $colour Integer: colour of the link - * @param $text String: link text - * @param $query String: optional query part - * @param $trail String: optional trail. Alphabetic characters at the start of this string will - * be included in the link text. Other characters will be appended after - * the end of the link. - */ - function makeColouredLinkObj( $nt, $colour, $text = '', $query = '', $trail = '', $prefix = '' ) { - if($colour != ''){ - $style = $this->getInternalLinkAttributesObj( $nt, $text, $colour ); - } else $style = ''; - return $this->makeKnownLinkObj( $nt, $text, $query, $trail, $prefix, '', $style ); - } - /** * Generate either a normal exists-style link or a stub link, depending * on the given page size. @@ -589,6 +331,7 @@ class Linker { global $wgUser; $threshold = intval( $wgUser->getOption( 'stubthreshold' ) ); $colour = ( $size < $threshold ) ? 'stub' : ''; + // FIXME: replace deprecated makeColouredLinkObj by link() return $this->makeColouredLinkObj( $nt, $colour, $text, $query, $trail, $prefix ); } @@ -628,12 +371,6 @@ class Linker { return $basename; } - /** Obsolete alias */ - function makeImage( $url, $alt = '' ) { - wfDeprecated( __METHOD__ ); - return $this->makeExternalImage( $url, $alt ); - } - /** @todo document */ function makeExternalImage( $url, $alt = '' ) { if ( '' == $alt ) { @@ -642,7 +379,7 @@ class Linker { $img = ''; $success = wfRunHooks('LinkerMakeExternalImage', array( &$url, &$alt, &$img ) ); if(!$success) { - wfDebug("Hook LinkerMakeExternalImage changed the output of external image with url {$url} and alt text {$alt} to {$img}", true); + wfDebug("Hook LinkerMakeExternalImage changed the output of external image with url {$url} and alt text {$alt} to {$img}\n", true); return $img; } return Xml::element( 'img', @@ -651,45 +388,6 @@ class Linker { 'alt' => $alt ) ); } - /** - * Creates the HTML source for images - * @deprecated use makeImageLink2 - * - * @param object $title - * @param string $label label text - * @param string $alt alt text - * @param string $align horizontal alignment: none, left, center, right) - * @param array $handlerParams Parameters to be passed to the media handler - * @param boolean $framed shows image in original size in a frame - * @param boolean $thumb shows image as thumbnail in a frame - * @param string $manualthumb image name for the manual thumbnail - * @param string $valign vertical alignment: baseline, sub, super, top, text-top, middle, bottom, text-bottom - * @param string $time, timestamp of the file, set as false for current - * @return string - */ - function makeImageLinkObj( $title, $label, $alt, $align = '', $handlerParams = array(), $framed = false, - $thumb = false, $manualthumb = '', $valign = '', $time = false ) - { - $frameParams = array( 'alt' => $alt, 'caption' => $label ); - if ( $align ) { - $frameParams['align'] = $align; - } - if ( $framed ) { - $frameParams['framed'] = true; - } - if ( $thumb ) { - $frameParams['thumbnail'] = true; - } - if ( $manualthumb ) { - $frameParams['manualthumb'] = $manualthumb; - } - if ( $valign ) { - $frameParams['valign'] = $valign; - } - $file = wfFindFile( $title, $time ); - return $this->makeImageLink2( $title, $file, $frameParams, $handlerParams, $time ); - } - /** * Given parameters derived from [[Image:Foo|options...]], generate the * HTML that that syntax inserts in the page. @@ -712,6 +410,9 @@ class Linker { * bottom, text-bottom) * alt Alternate text for image (i.e. alt attribute). Plain text. * caption HTML for image caption. + * link-url URL to link to + * link-title Title object to link to + * no-link Boolean, suppress description link * * @param array $handlerParams Associative array of media handler parameters, to be passed * to transform(). Typical keys are "width" and "page". @@ -740,11 +441,11 @@ class Linker { $page = isset( $hp['page'] ) ? $hp['page'] : false; if ( !isset( $fp['align'] ) ) $fp['align'] = ''; if ( !isset( $fp['alt'] ) ) $fp['alt'] = ''; + if ( !isset( $fp['title'] ) ) $fp['title'] = ''; $prefix = $postfix = ''; - if ( 'center' == $fp['align'] ) - { + if ( 'center' == $fp['align'] ) { $prefix = '
'; $postfix = '
'; $fp['align'] = 'none'; @@ -775,7 +476,6 @@ class Linker { } if ( isset( $fp['thumbnail'] ) || isset( $fp['manualthumb'] ) || isset( $fp['framed'] ) ) { - # Create a thumbnail. Alignment depends on language # writing direction, # right aligned for left-to-right- # languages ("Western languages"), left-aligned @@ -808,15 +508,26 @@ class Linker { if ( !$thumb ) { $s = $this->makeBrokenImageLinkObj( $title, '', '', '', '', $time==true ); } else { - $s = $thumb->toHtml( array( - 'desc-link' => true, - 'desc-query' => $query, + $params = array( 'alt' => $fp['alt'], + 'title' => $fp['title'], 'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false , - 'img-class' => isset( $fp['border'] ) ? 'thumbborder' : false ) ); + 'img-class' => isset( $fp['border'] ) ? 'thumbborder' : false ); + if ( !empty( $fp['link-url'] ) ) { + $params['custom-url-link'] = $fp['link-url']; + } elseif ( !empty( $fp['link-title'] ) ) { + $params['custom-title-link'] = $fp['link-title']; + } elseif ( !empty( $fp['no-link'] ) ) { + // No link + } else { + $params['desc-link'] = true; + $params['desc-query'] = $query; + } + + $s = $thumb->toHtml( $params ); } if ( '' != $fp['align'] ) { - $s = "
{$s}
"; + $s = "
{$s}
"; } return str_replace("\n", ' ',$prefix.$s.$postfix); } @@ -848,6 +559,7 @@ class Linker { $page = isset( $hp['page'] ) ? $hp['page'] : false; if ( !isset( $fp['align'] ) ) $fp['align'] = 'right'; if ( !isset( $fp['alt'] ) ) $fp['alt'] = ''; + if ( !isset( $fp['title'] ) ) $fp['title'] = ''; if ( !isset( $fp['caption'] ) ) $fp['caption'] = ''; if ( empty( $hp['width'] ) ) { @@ -861,7 +573,7 @@ class Linker { } else { if ( isset( $fp['manualthumb'] ) ) { # Use manually specified thumbnail - $manual_title = Title::makeTitleSafe( NS_IMAGE, $fp['manualthumb'] ); + $manual_title = Title::makeTitleSafe( NS_FILE, $fp['manualthumb'] ); if( $manual_title ) { $manual_img = wfFindFile( $manual_title ); if ( $manual_img ) { @@ -890,10 +602,13 @@ class Linker { } } - if( $page ) { - $query = $query ? '&page=' . urlencode( $page ) : 'page=' . urlencode( $page ); - } + # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs + # So we don't need to pass it here in $query. However, the URL for the + # zoom icon still needs it, so we make a unique query for it. See bug 14771 $url = $title->getLocalURL( $query ); + if( $page ) { + $url = wfAppendQuery( $url, 'page=' . urlencode( $page ) ); + } $more = htmlspecialchars( wfMsg( 'thumbnail-more' ) ); @@ -907,6 +622,7 @@ class Linker { } else { $s .= $thumb->toHtml( array( 'alt' => $fp['alt'], + 'title' => $fp['title'], 'img-class' => 'thumbimage', 'desc-link' => true, 'desc-query' => $query ) ); @@ -964,12 +680,6 @@ class Linker { } } - /** @deprecated use Linker::makeMediaLinkObj() */ - function makeMediaLink( $name, $unused = '', $text = '', $time = false ) { - $nt = Title::makeTitleSafe( NS_IMAGE, $name ); - return $this->makeMediaLinkObj( $nt, $text, $time ); - } - /** * Create a direct link to a given uploaded file. * @@ -1014,24 +724,38 @@ class Linker { wfMsg( $key ) ); } - /** @todo document */ - function makeExternalLink( $url, $text, $escape = true, $linktype = '', $ns = null ) { - $style = $this->getExternalLinkAttributes( $url, $text, 'external ' . $linktype ); - global $wgNoFollowLinks, $wgNoFollowNsExceptions; - if( $wgNoFollowLinks && !(isset($ns) && in_array($ns, $wgNoFollowNsExceptions)) ) { - $style .= ' rel="nofollow"'; - } + /** + * Make an external link + * @param String $url URL to link to + * @param String $text text of link + * @param boolean $escape Do we escape the link text? + * @param String $linktype Type of external link. Gets added to the classes + * @param array $attribs Array of extra attributes to + * + * @TODO! @FIXME! This is a really crappy implementation. $linktype and + * 'external' are mashed into the class attrib for the link (which is made + * into a string). Then, if we've got additional params in $attribs, we + * add to it. People using this might want to change the classes (or other + * default link attributes), but passing $attribsText is just messy. Would + * make a lot more sense to make put the classes into $attribs, let the + * hook play with them, *then* expand it all at once. + */ + function makeExternalLink( $url, $text, $escape = true, $linktype = '', $attribs = array() ) { + $attribsText = $this->getExternalLinkAttributes( 'external ' . $linktype ); $url = htmlspecialchars( $url ); if( $escape ) { $text = htmlspecialchars( $text ); } $link = ''; - $success = wfRunHooks('LinkerMakeExternalLink', array( &$url, &$text, &$link ) ); + $success = wfRunHooks('LinkerMakeExternalLink', array( &$url, &$text, &$link, &$attribs, $linktype ) ); if(!$success) { - wfDebug("Hook LinkerMakeExternalLink changed the output of link with url {$url} and text {$text} to {$link}", true); + wfDebug("Hook LinkerMakeExternalLink changed the output of link with url {$url} and text {$text} to {$link}\n", true); return $link; } - return ''.$text.''; + if ( $attribs ) { + $attribsText .= Xml::expandAttributes( $attribs ); + } + return ''.$text.''; } /** @@ -1047,7 +771,7 @@ class Linker { } else { $page = Title::makeTitle( NS_USER, $userText ); } - return $this->link( $page, htmlspecialchars( $userText ) ); + return $this->link( $page, htmlspecialchars( $userText ), array( 'class' => 'mw-userlink' ) ); } /** @@ -1061,7 +785,7 @@ class Linker { * @return string */ public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits=null ) { - global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans; + global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans, $wgLang; $talkable = !( $wgDisableAnonTalk && 0 == $userId ); $blockable = ( $wgSysopUserBans || 0 == $userId ) && !$flags & self::TOOL_LINKS_NOBLOCK; @@ -1087,7 +811,7 @@ class Linker { } if( $items ) { - return ' (' . implode( ' | ', $items ) . ')'; + return ' (' . $wgLang->pipeList( $items ) . ')'; } else { return ''; } @@ -1138,7 +862,8 @@ class Linker { if( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) { $link = wfMsgHtml( 'rev-deleted-user' ); } else if( $rev->userCan( Revision::DELETED_USER ) ) { - $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ); + $link = $this->userLink( $rev->getUser( Revision::FOR_THIS_USER ), + $rev->getUserText( Revision::FOR_THIS_USER ) ); } else { $link = wfMsgHtml( 'rev-deleted-user' ); } @@ -1158,8 +883,10 @@ class Linker { if( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) { $link = wfMsgHtml( 'rev-deleted-user' ); } else if( $rev->userCan( Revision::DELETED_USER ) ) { - $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ) . - ' ' . $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() ); + $userId = $rev->getUser( Revision::FOR_THIS_USER ); + $userText = $rev->getUserText( Revision::FOR_THIS_USER ); + $link = $this->userLink( $userId, $userText ) . + ' ' . $this->userToolLinks( $userId, $userText ); } else { $link = wfMsgHtml( 'rev-deleted-user' ); } @@ -1190,7 +917,8 @@ class Linker { # Sanitize text a bit: $comment = str_replace( "\n", " ", $comment ); - $comment = htmlspecialchars( $comment ); + # Allow HTML entities (for bug 13815) + $comment = Sanitizer::escapeHtmlAllowEntities( $comment ); # Render autocomments and make links: $comment = $this->formatAutoComments( $comment, $title, $local ); @@ -1214,46 +942,62 @@ class Linker { * @todo Document the $local parameter. */ private function formatAutocomments( $comment, $title = null, $local = false ) { - $match = array(); - while (preg_match('!(.*)/\*\s*(.*?)\s*\*/(.*)!', $comment,$match)) { - $pre=$match[1]; - $auto=$match[2]; - $post=$match[3]; - $link=''; - if( $title ) { - $section = $auto; - - # Generate a valid anchor name from the section title. - # Hackish, but should generally work - we strip wiki - # syntax, including the magic [[: that is used to - # "link rather than show" in case of images and - # interlanguage links. - $section = str_replace( '[[:', '', $section ); - $section = str_replace( '[[', '', $section ); - $section = str_replace( ']]', '', $section ); - if ( $local ) { - $sectionTitle = Title::newFromText( '#' . $section ); - } else { - $sectionTitle = clone( $title ); - $sectionTitle->mFragment = $section; - } + // Bah! + $this->autocommentTitle = $title; + $this->autocommentLocal = $local; + $comment = preg_replace_callback( + '!(.*)/\*\s*(.*?)\s*\*/(.*)!', + array( $this, 'formatAutocommentsCallback' ), + $comment ); + unset( $this->autocommentTitle ); + unset( $this->autocommentLocal ); + return $comment; + } + + private function formatAutocommentsCallback( $match ) { + $title = $this->autocommentTitle; + $local = $this->autocommentLocal; + + $pre=$match[1]; + $auto=$match[2]; + $post=$match[3]; + $link=''; + if( $title ) { + $section = $auto; + + # Generate a valid anchor name from the section title. + # Hackish, but should generally work - we strip wiki + # syntax, including the magic [[: that is used to + # "link rather than show" in case of images and + # interlanguage links. + $section = str_replace( '[[:', '', $section ); + $section = str_replace( '[[', '', $section ); + $section = str_replace( ']]', '', $section ); + if ( $local ) { + $sectionTitle = Title::newFromText( '#' . $section ); + } else { + $sectionTitle = Title::makeTitleSafe( $title->getNamespace(), + $title->getDBkey(), $section ); + } + if ( $sectionTitle ) { $link = $this->link( $sectionTitle, - wfMsgForContent( 'sectionlink' ), array(), array(), + htmlspecialchars( wfMsgForContent( 'sectionlink' ) ), array(), array(), 'noclasses' ); + } else { + $link = ''; } - $auto = $link . $auto; - if( $pre ) { - # written summary $presep autocomment (summary /* section */) - $auto = wfMsgExt( 'autocomment-prefix', array( 'escapenoentities', 'content' ) ) . $auto; - } - if( $post ) { - # autocomment $postsep written summary (/* section */ summary) - $auto .= wfMsgExt( 'colon-separator', array( 'escapenoentities', 'content' ) ); - } - $auto = '' . $auto . ''; - $comment = $pre . $auto . $post; } - + $auto = "$link$auto"; + if( $pre ) { + # written summary $presep autocomment (summary /* section */) + $auto = wfMsgExt( 'autocomment-prefix', array( 'escapenoentities', 'content' ) ) . $auto; + } + if( $post ) { + # autocomment $postsep written summary (/* section */ summary) + $auto .= wfMsgExt( 'colon-separator', array( 'escapenoentities', 'content' ) ); + } + $auto = '' . $auto . ''; + $comment = $pre . $auto . $post; return $comment; } @@ -1295,7 +1039,8 @@ class Linker { if( preg_match( '/^' . $medians . '(.*)$/i', $match[1], $submatch ) ) { # Media link; trail not supported. $linkRegexp = '/\[\[(.*?)\]\]/'; - $thelink = $this->makeMediaLink( $submatch[1], "", $text ); + $title = Title::makeTitleSafe( NS_FILE, $submatch[1] ); + $thelink = $this->makeMediaLinkObj( $title, $text ); } else { # Other kind of link if( preg_match( $wgContLang->linkTrail(), $match[4], $submatch ) ) { @@ -1306,7 +1051,11 @@ class Linker { $linkRegexp = '/\[\[(.*?)\]\]' . preg_quote( $trail, '/' ) . '/'; if (isset($match[1][0]) && $match[1][0] == ':') $match[1] = substr($match[1], 1); - $thelink = $this->makeLink( $match[1], $text, "", $trail ); + list( $inside, $trail ) = Linker::splitTrail( $trail ); + $thelink = $this->link( + Title::newFromText( $match[1] ), + $text . $inside + ) . $trail; } $comment = preg_replace( $linkRegexp, StringUtils::escapeRegexReplacement( $thelink ), $comment, 1 ); @@ -1345,10 +1094,12 @@ class Linker { * @return string HTML */ function revComment( Revision $rev, $local = false, $isPublic = false ) { + if( $rev->getRawComment() == "" ) return ""; if( $rev->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) { $block = " " . wfMsgHtml( 'rev-deleted-comment' ) . ""; } else if( $rev->userCan( Revision::DELETED_COMMENT ) ) { - $block = $this->commentBlock( $rev->getRawComment(), $rev->getTitle(), $local ); + $block = $this->commentBlock( $rev->getComment( Revision::FOR_THIS_USER ), + $rev->getTitle(), $local ); } else { $block = " " . wfMsgHtml( 'rev-deleted-comment' ) . ""; } @@ -1383,8 +1134,11 @@ class Linker { /** * parameter level defines if we are on an indentation level */ - function tocLine( $anchor, $tocline, $tocnumber, $level ) { - return "\n
  • ' . $tocnumber . ' ' . $tocline . ''; @@ -1408,39 +1162,37 @@ class Linker { . "\n" . '\n"; } - - /** - * Used to generate section edit links that point to "other" pages - * (sections that are really part of included pages). - * - * @param $title Title string. - * @param $section Integer: section number. - */ - public function editSectionLinkForOther( $title, $section ) { - wfDeprecated( __METHOD__ ); - $title = Title::newFromText( $title ); - return $this->doEditSectionLink( $title, $section ); - } - + /** - * @param $nt Title object. - * @param $section Integer: section number. - * @param $hint Link String: title, or default if omitted or empty + * Generate a table of contents from a section tree + * @param $tree Return value of ParserOutput::getSections() + * @return string HTML */ - public function editSectionLink( Title $nt, $section, $hint = '' ) { - wfDeprecated( __METHOD__ ); - if( $hint === '' ) { - # No way to pass an actual empty $hint here! The new interface al- - # lows this, so we have to do this for compatibility. - $hint = null; - } - return $this->doEditSectionLink( $nt, $section, $hint ); + public function generateTOC( $tree ) { + $toc = ''; + $lastLevel = 0; + foreach ( $tree as $section ) { + if ( $section['toclevel'] > $lastLevel ) + $toc .= $this->tocIndent(); + else if ( $section['toclevel'] < $lastLevel ) + $toc .= $this->tocUnindent( + $lastLevel - $section['toclevel'] ); + else + $toc .= $this->tocLineEnd(); + + $toc .= $this->tocLine( $section['anchor'], + $section['line'], $section['number'], + $section['toclevel'], $section['index'] ); + $lastLevel = $section['toclevel']; + } + $toc .= $this->tocLineEnd(); + return $this->tocList( $toc ); } /** @@ -1460,7 +1212,7 @@ class Linker { if( !is_null( $tooltip ) ) { $attribs['title'] = wfMsg( 'editsectionhint', $tooltip ); } - $url = $this->link( $nt, wfMsg('editsection'), + $link = $this->link( $nt, wfMsg('editsection'), $attribs, array( 'action' => 'edit', 'section' => $section ), array( 'noclasses', 'known' ) @@ -1474,19 +1226,19 @@ class Linker { $attribs = " title=\"$attribs\""; } $result = null; - wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $attribs, $url, &$result ) ); + wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $attribs, $link, &$result ) ); if( !is_null( $result ) ) { # For reverse compatibility, add the brackets *after* the hook is # run, and even add them to hook-provided text. (This is the main # reason that the EditSectionLink hook is deprecated in favor of # DoEditSectionLink: it can't change the brackets or the span.) - $result = wfMsgHtml( 'editsection-brackets', $url ); + $result = wfMsgHtml( 'editsection-brackets', $result ); return "$result"; } # Add the brackets and the span, and *then* run the nice new hook, with # clean and non-redundant arguments. - $result = wfMsgHtml( 'editsection-brackets', $url ); + $result = wfMsgHtml( 'editsection-brackets', $link ); $result = "$result"; wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result ) ); @@ -1502,11 +1254,20 @@ class Linker { * @param string $anchor The anchor to give the headline (the bit after the #) * @param string $text The text of the header * @param string $link HTML to add for the section edit link + * @param mixed $legacyAnchor A second, optional anchor to give for + * backward compatibility (false to omit) * * @return string HTML headline */ - public function makeHeadline( $level, $attribs, $anchor, $text, $link ) { - return "$text"; + public function makeHeadline( $level, $attribs, $anchor, $text, $link, $legacyAnchor = false ) { + $ret = "$text" + . ""; + if ( $legacyAnchor !== false ) { + $ret = "$ret"; + } + return $ret; } /** @@ -1566,10 +1327,12 @@ class Linker { ); if( $wgRequest->getBool( 'bot' ) ) { $query['bot'] = '1'; + $query['hidediff'] = '1'; // bug 15999 } $query['token'] = $wgUser->editToken( array( $title->getPrefixedText(), $rev->getUserText() ) ); - return $this->link( $title, wfMsgHtml( 'rollbacklink' ), array(), + return $this->link( $title, wfMsgHtml( 'rollbacklink' ), + array( 'title' => wfMsg( 'tooltip-rollback' ) ), $query, array( 'known', 'noclasses' ) ); } @@ -1582,12 +1345,9 @@ class Linker { * @param bool $section Whether this is for a section edit * @return string HTML output */ - public function formatTemplates( $templates, $preview = false, $section = false) { - global $wgUser; + public function formatTemplates( $templates, $preview = false, $section = false ) { wfProfileIn( __METHOD__ ); - $sk = $wgUser->getSkin(); - $outText = ''; if ( count( $templates ) > 0 ) { # Do a batch existence check @@ -1606,7 +1366,7 @@ class Linker { } else { $outText .= wfMsgExt( 'templatesused', array( 'parse' ) ); } - $outText .= '