From 222b01f5169f1c7e69762e0e8904c24f78f71882 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Wed, 28 Jul 2010 11:52:48 +0200 Subject: update to MediaWiki 1.16.0 --- includes/Linker.php | 1135 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 684 insertions(+), 451 deletions(-) (limited to 'includes/Linker.php') diff --git a/includes/Linker.php b/includes/Linker.php index b739244b..fe193011 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,7 @@ class Linker { if ( $t->isRedirect() ) { # Page is a redirect $colour = 'mw-redirect'; - } elseif ( $threshold > 0 && + } elseif ( $threshold > 0 && $t->exists() && $t->getLength() < $threshold && MWNamespace::isContent( $t->getNamespace() ) ) { # Page is a stub @@ -220,13 +206,23 @@ class Linker { $ret = null; if( wfRunHooks( 'LinkEnd', array( $this, $target, $options, &$text, &$attribs, &$ret ) ) ) { - $ret = Xml::openElement( 'a', $attribs ) . $text . Xml::closeElement( 'a' ); + $ret = Html::rawElement( 'a', $attribs, $text ); } wfProfileOut( __METHOD__ ); return $ret; } + /** + * Identical to link(), except $options defaults to 'known'. + */ + public function linkKnown( $target, $text = null, $customAttribs = array(), $query = array(), $options = array('known','noclasses') ) { + return $this->link( $target, $text, $customAttribs, $query, $options ); + } + + /** + * Returns the Url used to link to a Title + */ private function linkUrl( $target, $query, $options ) { wfProfileIn( __METHOD__ ); # We don't want to include fragments for broken links, because they @@ -249,6 +245,9 @@ class Linker { return $ret; } + /** + * Returns the array of attributes used when linking to the Title $target + */ private function linkAttribs( $target, $attribs, $options ) { wfProfileIn( __METHOD__ ); global $wgUser; @@ -268,7 +267,7 @@ 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. @@ -284,7 +283,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() ); @@ -305,6 +307,9 @@ class Linker { return $ret; } + /** + * Default text of the links to the Title $target + */ private function linkText( $target ) { # We might be passed a non-Title by make*LinkObj(). Fail gracefully. if( !$target instanceof Title ) { @@ -319,236 +324,6 @@ class Linker { return htmlspecialchars( $target->getPrefixedText() ); } - /** - * @deprecated Use link() - * - * 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; - } - - /** - * @deprecated Use link() - * - * 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; - } - } - - /** - * @deprecated Use link() - * - * 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 link() - * - * 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 = '' ) { - wfDeprecated( __METHOD__ ); - $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; - } - } - - /** - * @deprecated Use link() - * - * 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( $nt, $text= '', $query = '', $trail = '', $prefix = '' ) { - global $wgUser; - wfProfileIn( __METHOD__ ); - - $query = wfCgiToArray( $query ); - list( $inside, $trail ) = Linker::splitTrail( $trail ); - if( $text === '' ) { - $text = $this->linkText( $nt ); - } - - $ret = $this->link( $nt, "$prefix$text$inside", array(), $query ) . $trail; - - wfProfileOut( __METHOD__ ); - return $ret; - } - - /** - * @deprecated Use link() - * - * 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, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) { - wfProfileIn( __METHOD__ ); - - if ( $text == '' ) { - $text = $this->linkText( $title ); - } - $attribs = Sanitizer::mergeAttributes( - Sanitizer::decodeTagAttributes( $aprops ), - Sanitizer::decodeTagAttributes( $style ) - ); - $query = wfCgiToArray( $query ); - list( $inside, $trail ) = Linker::splitTrail( $trail ); - - $ret = $this->link( $title, "$prefix$text$inside", $attribs, $query, - array( 'known', 'noclasses' ) ) . $trail; - - wfProfileOut( __METHOD__ ); - return $ret; - } - - /** - * @deprecated Use link() - * - * 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, $text = '', $query = '', $trail = '', $prefix = '' ) { - wfProfileIn( __METHOD__ ); - - list( $inside, $trail ) = Linker::splitTrail( $trail ); - if( $text === '' ) { - $text = $this->linkText( $title ); - } - $nt = $this->normaliseSpecialPage( $title ); - - $ret = $this->link( $title, "$prefix$text$inside", array(), - wfCgiToArray( $query ), 'broken' ) . $trail; - - wfProfileOut( __METHOD__ ); - return $ret; - } - - /** - * @deprecated Use link() - * - * 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 ); - } - - /** - * @deprecated Use link() - * - * 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. @@ -565,6 +340,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 ); } @@ -574,7 +350,7 @@ class Linker { * despite $query not being used. */ function makeSelfLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) { - if ( '' == $text ) { + if ( $text == '' ) { $text = htmlspecialchars( $nt->getPrefixedText() ); } list( $inside, $trail ) = Linker::splitTrail( $trail ); @@ -593,7 +369,10 @@ class Linker { } } - /** @todo document */ + /** + * Returns the filename part of an url. + * Used as alternative text for external images. + */ function fnamePart( $url ) { $basename = strrchr( $url, '/' ); if ( false === $basename ) { @@ -604,15 +383,12 @@ class Linker { return $basename; } - /** Obsolete alias */ - function makeImage( $url, $alt = '' ) { - wfDeprecated( __METHOD__ ); - return $this->makeExternalImage( $url, $alt ); - } - - /** @todo document */ + /** + * Return the code for images which were added via external links, + * via Parser::maybeMakeExternalImage(). + */ function makeExternalImage( $url, $alt = '' ) { - if ( '' == $alt ) { + if ( $alt == '' ) { $alt = $this->fnamePart( $url ); } $img = ''; @@ -621,51 +397,12 @@ class Linker { 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', + return Html::element( 'img', array( 'src' => $url, '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. @@ -719,8 +456,7 @@ class Linker { $page = isset( $hp['page'] ) ? $hp['page'] : false; if ( !isset( $fp['align'] ) ) $fp['align'] = ''; if ( !isset( $fp['alt'] ) ) $fp['alt'] = ''; - # Backward compatibility, title used to always be equal to alt text - if ( !isset( $fp['title'] ) ) $fp['title'] = $fp['alt']; + if ( !isset( $fp['title'] ) ) $fp['title'] = ''; $prefix = $postfix = ''; @@ -763,7 +499,7 @@ class Linker { # If thumbnail width has not been provided, it is set # to the default user option as specified in Language*.php if ( $fp['align'] == '' ) { - $fp['align'] = $wgContLang->isRTL() ? 'left' : 'right'; + $fp['align'] = $wgContLang->alignEnd(); } return $prefix.$this->makeThumbLink2( $title, $file, $fp, $hp, $time, $query ).$postfix; } @@ -785,7 +521,7 @@ class Linker { } if ( !$thumb ) { - $s = $this->makeBrokenImageLinkObj( $title, '', '', '', '', $time==true ); + $s = $this->makeBrokenImageLinkObj( $title, $fp['title'], '', '', '', $time==true ); } else { $params = array( 'alt' => $fp['alt'], @@ -805,7 +541,7 @@ class Linker { $s = $thumb->toHtml( $params ); } - if ( '' != $fp['align'] ) { + if ( $fp['align'] != '' ) { $s = "
{$s}
"; } return str_replace("\n", ' ',$prefix.$s.$postfix); @@ -838,8 +574,7 @@ class Linker { $page = isset( $hp['page'] ) ? $hp['page'] : false; if ( !isset( $fp['align'] ) ) $fp['align'] = 'right'; if ( !isset( $fp['alt'] ) ) $fp['alt'] = ''; - # Backward compatibility, title used to always be equal to alt text - if ( !isset( $fp['title'] ) ) $fp['title'] = $fp['alt']; + if ( !isset( $fp['title'] ) ) $fp['title'] = ''; if ( !isset( $fp['caption'] ) ) $fp['caption'] = ''; if ( empty( $hp['width'] ) ) { @@ -886,7 +621,7 @@ class Linker { # 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 ) { + if( $page ) { $url = wfAppendQuery( $url, 'page=' . urlencode( $page ) ); } @@ -894,7 +629,7 @@ class Linker { $s = "
"; if( !$exists ) { - $s .= $this->makeBrokenImageLinkObj( $title, '', '', '', '', $time==true ); + $s .= $this->makeBrokenImageLinkObj( $title, $fp['title'], '', '', '', $time==true ); $zoomicon = ''; } elseif ( !$thumb ) { $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) ); @@ -931,26 +666,31 @@ class Linker { * @return string */ public function makeBrokenImageLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '', $time = false ) { - global $wgEnableUploads; + global $wgEnableUploads, $wgUploadNavigationUrl; if( $title instanceof Title ) { wfProfileIn( __METHOD__ ); $currentExists = $time ? ( wfFindFile( $title ) != false ) : false; - if( $wgEnableUploads && !$currentExists ) { - $upload = SpecialPage::getTitleFor( 'Upload' ); + if( ( $wgUploadNavigationUrl || $wgEnableUploads ) && !$currentExists ) { if( $text == '' ) $text = htmlspecialchars( $title->getPrefixedText() ); + $redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title ); if( $redir ) { + wfProfileOut( __METHOD__ ); return $this->makeKnownLinkObj( $title, $text, $query, $trail, $prefix ); } - $q = 'wpDestFile=' . $title->getPartialUrl(); - if( $query != '' ) - $q .= '&' . $query; + + $href = $this->getUploadUrl( $title, $query ); + + list( $inside, $trail ) = self::splitTrail( $trail ); - $style = $this->getInternalLinkAttributesObj( $title, $text, 'new' ); + wfProfileOut( __METHOD__ ); - return '' . $prefix . $text . $inside . '' . $trail; + return Html::element( 'a', array( + 'href' => $href, + 'class' => 'new', + 'title' => $title->getPrefixedText() + ), $prefix . $text . $inside ) . $trail; } else { wfProfileOut( __METHOD__ ); return $this->makeKnownLinkObj( $title, $text, $query, $trail, $prefix ); @@ -959,11 +699,26 @@ class Linker { return "{$prefix}{$text}{$trail}"; } } - - /** @deprecated use Linker::makeMediaLinkObj() */ - function makeMediaLink( $name, $unused = '', $text = '', $time = false ) { - $nt = Title::makeTitleSafe( NS_FILE, $name ); - return $this->makeMediaLinkObj( $nt, $text, $time ); + + /** + * Get the URL to upload a certain file + * + * @param $destFile Title Title of the file to upload + * @param $query string Urlencoded query string to prepend + * @return string Urlencoded URL + */ + protected function getUploadUrl( $destFile, $query = '' ) { + global $wgUploadNavigationUrl; + $q = 'wpDestFile=' . $destFile->getPartialUrl(); + if( $query != '' ) + $q .= '&' . $query; + + if( $wgUploadNavigationUrl ) { + return wfAppendQuery( $wgUploadNavigationUrl, $q ); + } else { + $upload = SpecialPage::getTitleFor( 'Upload' ); + return $upload->getLocalUrl( $q ); + } } /** @@ -982,13 +737,12 @@ class Linker { ### HOTFIX. Instead of breaking, return empty string. return $text; } else { - $img = wfFindFile( $title, $time ); + $img = wfFindFile( $title, array( 'time' => $time ) ); if( $img ) { $url = $img->getURL(); $class = 'internal'; } else { - $upload = SpecialPage::getTitleFor( 'Upload' ); - $url = $upload->getLocalUrl( 'wpDestFile=' . urlencode( $title->getDBkey() ) ); + $url = $this->getUploadUrl( $title ); $class = 'new'; } $alt = htmlspecialchars( $title->getText() ); @@ -1000,11 +754,15 @@ class Linker { } } - /** @todo document */ + /** + * Make a link to a special page given its name and, optionally, + * a message key from the link text. + * Usage example: $skin->specialLink( 'recentchanges' ) + */ function specialLink( $name, $key = '' ) { global $wgContLang; - if ( '' == $key ) { $key = strtolower( $name ); } + if ( $key == '' ) { $key = strtolower( $name ); } $pn = $wgContLang->ucfirst( $name ); return $this->makeKnownLink( $wgContLang->specialPage( $pn ), wfMsg( $key ) ); @@ -1017,17 +775,20 @@ class Linker { * @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 + * + * @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 + * 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. + * 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( $url, $text, 'external ' . $linktype ); + if ( isset( $attribs[ 'class' ] ) ) $class = $attribs[ 'class' ]; # yet another hack :( + else $class = 'external ' . $linktype; + + $attribsText = $this->getExternalLinkAttributes( $class ); $url = htmlspecialchars( $url ); if( $escape ) { $text = htmlspecialchars( $text ); @@ -1039,7 +800,7 @@ class Linker { return $link; } if ( $attribs ) { - $attribsText .= Xml::expandAttributes( $attribs ); + $attribsText .= Html::expandAttributes( $attribs ); } return ''.$text.''; } @@ -1148,7 +909,7 @@ 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->getUser( Revision::FOR_THIS_USER ), + $link = $this->userLink( $rev->getUser( Revision::FOR_THIS_USER ), $rev->getUserText( Revision::FOR_THIS_USER ) ); } else { $link = wfMsgHtml( 'rev-deleted-user' ); @@ -1170,7 +931,7 @@ class Linker { $link = wfMsgHtml( 'rev-deleted-user' ); } else if( $rev->userCan( Revision::DELETED_USER ) ) { $userId = $rev->getUser( Revision::FOR_THIS_USER ); - $userText = $rev->getUserText( Revision::FOR_THIS_USER ); + $userText = $rev->getUserText( Revision::FOR_THIS_USER ); $link = $this->userLink( $userId, $userText ) . ' ' . $this->userToolLinks( $userId, $userText ); } else { @@ -1198,7 +959,7 @@ class Linker { * @param mixed $title Title object (to generate link to the section in autocomment) or null * @param bool $local Whether section links should refer to local page */ - function formatComment($comment, $title = NULL, $local = false) { + function formatComment($comment, $title = null, $local = false) { wfProfileIn( __METHOD__ ); # Sanitize text a bit: @@ -1207,8 +968,8 @@ class Linker { $comment = Sanitizer::escapeHtmlAllowEntities( $comment ); # Render autocomments and make links: - $comment = $this->formatAutoComments( $comment, $title, $local ); - $comment = $this->formatLinksInComment( $comment ); + $comment = $this->formatAutocomments( $comment, $title, $local ); + $comment = $this->formatLinksInComment( $comment, $title, $local ); wfProfileOut( __METHOD__ ); return $comment; @@ -1239,16 +1000,16 @@ class Linker { 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 ) { + + $pre = $match[1]; + $auto = $match[2]; + $post = $match[3]; + $link = ''; + if ( $title ) { $section = $auto; # Generate a valid anchor name from the section title. @@ -1262,12 +1023,12 @@ class Linker { if ( $local ) { $sectionTitle = Title::newFromText( '#' . $section ); } else { - $sectionTitle = Title::makeTitleSafe( $title->getNamespace(), + $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 = ''; @@ -1291,15 +1052,20 @@ class Linker { * Formats wiki links and media links in text; all other wiki formatting * is ignored * - * @fixme doesn't handle sub-links as in image thumb texts like the main parser + * @todo Fixme: doesn't handle sub-links as in image thumb texts like the main parser * @param string $comment Text to format links in * @return string */ - public function formatLinksInComment( $comment ) { - return preg_replace_callback( + public function formatLinksInComment( $comment, $title = null, $local = false ) { + $this->commentContextTitle = $title; + $this->commentLocal = $local; + $html = preg_replace_callback( '/\[\[:?(.*?)(\|(.*?))*\]\]([^[]*)/', array( $this, 'formatLinksInCommentCallback' ), $comment ); + unset( $this->commentContextTitle ); + unset( $this->commentLocal ); + return $html; } protected function formatLinksInCommentCallback( $match ) { @@ -1316,16 +1082,18 @@ class Linker { } # Handle link renaming [[foo|text]] will show link as "text" - if( "" != $match[3] ) { + if( $match[3] != "" ) { $text = $match[3]; } else { $text = $match[1]; } $submatch = array(); + $thelink = null; 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 ) ) { @@ -1336,13 +1104,105 @@ 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 ); + + $linkText = $text; + $linkTarget = Linker::normalizeSubpageLink( $this->commentContextTitle, + $match[1], $linkText ); + + $target = Title::newFromText( $linkTarget ); + if( $target ) { + if( $target->getText() == '' && !$this->commentLocal && $this->commentContextTitle ) { + $newTarget = clone( $this->commentContextTitle ); + $newTarget->setFragment( '#' . $target->getFragment() ); + $target = $newTarget; + } + $thelink = $this->link( + $target, + $linkText . $inside + ) . $trail; + } + } + if( $thelink ) { + // If the link is still valid, go ahead and replace it in! + $comment = preg_replace( $linkRegexp, StringUtils::escapeRegexReplacement( $thelink ), $comment, 1 ); } - $comment = preg_replace( $linkRegexp, StringUtils::escapeRegexReplacement( $thelink ), $comment, 1 ); return $comment; } + static function normalizeSubpageLink( $contextTitle, $target, &$text ) { + # Valid link forms: + # Foobar -- normal + # :Foobar -- override special treatment of prefix (images, language links) + # /Foobar -- convert to CurrentPage/Foobar + # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial / from text + # ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage + # ../Foobar -- convert to CurrentPage/Foobar, from CurrentPage/CurrentSubPage + + wfProfileIn( __METHOD__ ); + $ret = $target; # default return value is no change + + # Some namespaces don't allow subpages, + # so only perform processing if subpages are allowed + if( $contextTitle && MWNamespace::hasSubpages( $contextTitle->getNamespace() ) ) { + $hash = strpos( $target, '#' ); + if( $hash !== false ) { + $suffix = substr( $target, $hash ); + $target = substr( $target, 0, $hash ); + } else { + $suffix = ''; + } + # bug 7425 + $target = trim( $target ); + # Look at the first character + if( $target != '' && $target{0} === '/' ) { + # / at end means we don't want the slash to be shown + $m = array(); + $trailingSlashes = preg_match_all( '%(/+)$%', $target, $m ); + if( $trailingSlashes ) { + $noslash = $target = substr( $target, 1, -strlen($m[0][0]) ); + } else { + $noslash = substr( $target, 1 ); + } + + $ret = $contextTitle->getPrefixedText(). '/' . trim($noslash) . $suffix; + if( $text === '' ) { + $text = $target . $suffix; + } # this might be changed for ugliness reasons + } else { + # check for .. subpage backlinks + $dotdotcount = 0; + $nodotdot = $target; + while( strncmp( $nodotdot, "../", 3 ) == 0 ) { + ++$dotdotcount; + $nodotdot = substr( $nodotdot, 3 ); + } + if($dotdotcount > 0) { + $exploded = explode( '/', $contextTitle->GetPrefixedText() ); + if( count( $exploded ) > $dotdotcount ) { # not allowed to go below top level page + $ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) ); + # / at the end means don't show full path + if( substr( $nodotdot, -1, 1 ) === '/' ) { + $nodotdot = substr( $nodotdot, 0, -1 ); + if( $text === '' ) { + $text = $nodotdot . $suffix; + } + } + $nodotdot = trim( $nodotdot ); + if( $nodotdot != '' ) { + $ret .= '/' . $nodotdot; + } + $ret .= $suffix; + } + } + } + } + + wfProfileOut( __METHOD__ ); + return $ret; + } + /** * Wrap a comment in standard punctuation and formatting if * it's non-empty, otherwise return empty string. @@ -1353,7 +1213,7 @@ class Linker { * * @return string */ - function commentBlock( $comment, $title = NULL, $local = false ) { + function commentBlock( $comment, $title = null, $local = false ) { // '*' used to be the comment inserted by the software way back // in antiquity in case none was provided, here for backwards // compatability, acc. to brion -ævar @@ -1375,6 +1235,7 @@ 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 ) ) { @@ -1401,12 +1262,16 @@ class Linker { return "$stxt"; } - /** @todo document */ + /** + * Add another level to the Table of Contents + */ function tocIndent() { return "\n
    "; } - /** @todo document */ + /** + * Finish one or more sublevels on the Table of Contents + */ function tocUnindent($level) { return "\n" . str_repeat( "
\n\n", $level>0 ? $level : 0 ); } @@ -1414,64 +1279,73 @@ class Linker { /** * parameter level defines if we are on an indentation level */ - function tocLine( $anchor, $tocline, $tocnumber, $level ) { - return "\n
  • ' . $tocnumber . ' ' . $tocline . ''; } - /** @todo document */ + /** + * End a Table Of Contents line. + * tocUnindent() will be used instead if we're ending a line below + * the new level. + */ function tocLineEnd() { return "
  • \n"; } - /** @todo document */ + /** + * Wraps the TOC in a table and provides the hide/collapse javascript. + * @param string $toc html of the Table Of Contents + * @return string Full html of the TOC + */ function tocList($toc) { - global $wgJsMimeType; $title = wfMsgHtml('toc') ; return - '
    ' + '
    ' . '

    ' . $title . "

    \n" . $toc # no trailing newline, script should not be wrapped in a # paragraph . "\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 ); + . Html::inlineScript( + 'if (window.showTocToggle) {' + . ' var tocShowText = "' . Xml::escapeJsString( wfMsg('showtoc') ) . '";' + . ' var tocHideText = "' . Xml::escapeJsString( wfMsg('hidetoc') ) . '";' + . ' showTocToggle();' + . ' } ' ) + . "\n"; } /** - * @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 + * Currently unused. + * @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 ); } /** @@ -1487,6 +1361,8 @@ class Linker { * @return string HTML to use for edit link */ public function doEditSectionLink( Title $nt, $section, $tooltip = null ) { + // HTML generated here should probably have userlangattributes + // added to it for LTR text on RTL pages $attribs = array(); if( !is_null( $tooltip ) ) { $attribs['title'] = wfMsg( 'editsectionhint', $tooltip ); @@ -1539,13 +1415,12 @@ class Linker { * @return string HTML headline */ public function makeHeadline( $level, $attribs, $anchor, $text, $link, $legacyAnchor = false ) { - $ret = "" - . "$text" + . " $text" . ""; if ( $legacyAnchor !== false ) { - $ret = "$ret"; + $ret = "$ret"; } return $ret; } @@ -1563,7 +1438,7 @@ class Linker { $regex = $wgContLang->linkTrail(); } $inside = ''; - if ( '' != $trail ) { + if ( $trail != '' ) { $m = array(); if ( preg_match( $regex, $trail, $m ) ) { $inside = $m[1]; @@ -1640,11 +1515,11 @@ class Linker { # Construct the HTML $outText = '
    '; if ( $preview ) { - $outText .= wfMsgExt( 'templatesusedpreview', array( 'parse' ) ); + $outText .= wfMsgExt( 'templatesusedpreview', array( 'parse' ), count( $templates ) ); } elseif ( $section ) { - $outText .= wfMsgExt( 'templatesusedsection', array( 'parse' ) ); + $outText .= wfMsgExt( 'templatesusedsection', array( 'parse' ), count( $templates ) ); } else { - $outText .= wfMsgExt( 'templatesused', array( 'parse' ) ); + $outText .= wfMsgExt( 'templatesused', array( 'parse' ), count( $templates ) ); } $outText .= "