diff options
Diffstat (limited to 'includes/Linker.php')
-rw-r--r-- | includes/Linker.php | 464 |
1 files changed, 308 insertions, 156 deletions
diff --git a/includes/Linker.php b/includes/Linker.php index 4b092cf9..32c506a4 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -1,15 +1,12 @@ <?php /** - * Split off some of the internal bits from Skin.php. - * These functions are used for primarily page content: - * links, embedded images, table of contents. Links are - * also used in the skin. - * For the moment, Skin is a descendent class of Linker. - * In the future, it should probably be further split - * so that ever other bit of the wiki doesn't have to - * go loading up Skin to get at it. + * Split off some of the internal bits from Skin.php. These functions are used + * for primarily page content: links, embedded images, table of contents. Links + * are also used in the skin. For the moment, Skin is a descendent class of + * Linker. In the future, it should probably be further split so that every + * other bit of the wiki doesn't have to go loading up Skin to get at it. * - * @addtogroup Skins + * @ingroup Skins */ class Linker { @@ -23,72 +20,110 @@ class Linker { /** * @deprecated */ - function postParseLinkColour( $s = NULL ) { - return NULL; + function postParseLinkColour( $s = null ) { + return null; } - /** @todo document */ - function getExternalLinkAttributes( $link, $text, $class='' ) { - $link = htmlspecialchars( $link ); - - $r = ($class != '') ? " class=\"$class\"" : " class=\"external\""; - - $r .= " title=\"{$link}\""; - return $r; + /** + * 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 getInterwikiLinkAttributes( $link, $text, $class='' ) { + /** + * Get the appropriate HTML attributes to add to the "a" element of an in- + * terwiki link. + * + * @param string $title The title text for the link, URL-encoded (???) but + * not HTML-escaped + * @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 getInterwikiLinkAttributes( $title, $unused = null, $class='' ) { global $wgContLang; - $link = urldecode( $link ); - $link = $wgContLang->checkTitleEncoding( $link ); - $link = preg_replace( '/[\\x00-\\x1f]/', ' ', $link ); - $link = htmlspecialchars( $link ); + # FIXME: We have a whole bunch of handling here that doesn't happen in + # getExternalLinkAttributes, why? + $title = urldecode( $title ); + $title = $wgContLang->checkTitleEncoding( $title ); + $title = preg_replace( '/[\\x00-\\x1f]/', ' ', $title ); - $r = ($class != '') ? " class=\"$class\"" : " class=\"external\""; + return $this->getLinkAttributesInternal( $title, $class, 'external' ); + } - $r .= " title=\"{$link}\""; - return $r; + /** + * Get the appropriate HTML attributes to add to the "a" element of an in- + * ternal link. + * + * @param string $title The title text for the link, URL-encoded (???) but + * not HTML-escaped + * @param string $unused Unused + * @param string $class The contents of the class attribute, default none + */ + function getInternalLinkAttributes( $title, $unused = null, $class='' ) { + $title = urldecode( $title ); + $title = str_replace( '_', ' ', $title ); + return $this->getLinkAttributesInternal( $title, $class ); } - /** @todo document */ - function getInternalLinkAttributes( $link, $text, $class='' ) { - $link = urldecode( $link ); - $link = str_replace( '_', ' ', $link ); - $link = htmlspecialchars( $link ); - $r = ($class != '') ? ' class="' . htmlspecialchars( $class ) . '"' : ''; - $r .= " title=\"{$link}\""; - return $r; + /** + * Get the appropriate HTML attributes to add to the "a" element of an in- + * ternal link, given the Title object for the page we want to link to. + * + * @param Title $nt The Title object + * @param string $unused Unused + * @param string $class The contents of the class attribute, default none + * @param mixed $title Optional (unescaped) string to use in the title + * attribute; if false, default to the name of the page we're linking to + */ + function getInternalLinkAttributesObj( $nt, $unused = null, $class = '', $title = false ) { + if( $title === false ) { + $title = $nt->getPrefixedText(); + } + return $this->getLinkAttributesInternal( $title, $class ); } /** - * @param $nt Title object. - * @param $text String: FIXME - * @param $class String: CSS class of the link, default ''. + * Common code for getLinkAttributesX functions */ - function getInternalLinkAttributesObj( &$nt, $text, $class='' ) { - $r = ($class != '') ? ' class="' . htmlspecialchars( $class ) . '"' : ''; - $r .= ' title="' . $nt->getEscapedText() . '"'; + private function getLinkAttributesInternal( $title, $class, $classDefault = false ) { + $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 !== '' ) { + $r .= " class=\"$class\""; + } + $r .= " title=\"$title\""; return $r; } /** * Return the CSS colour of a known link * - * @param mixed $s + * @param Title $t * @param integer $threshold user defined threshold * @return string CSS class */ - function getLinkColour( $s, $threshold ) { - if( $s === false ) { - return ''; - } - + function getLinkColour( $t, $threshold ) { $colour = ''; - if ( !empty( $s->page_is_redirect ) ) { + if ( $t->isRedirect() ) { # Page is a redirect $colour = 'mw-redirect'; - } elseif ( $threshold > 0 && $s->page_len < $threshold && Namespace::isContent( $s->page_namespace ) ) { + } elseif ( $threshold > 0 && $t->getLength() < $threshold && MWNamespace::isContent( $t->getNamespace() ) ) { # Page is a stub $colour = 'stub'; } @@ -123,7 +158,7 @@ class Linker { /** * 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 @@ -144,7 +179,7 @@ class Linker { /** * 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 @@ -164,10 +199,10 @@ class Linker { /** * @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 @@ -189,7 +224,7 @@ class Linker { * 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 @@ -228,7 +263,7 @@ class Linker { wfProfileOut( __METHOD__ ); return $t; } elseif ( $nt->isAlwaysKnown() ) { - # Image links, special page links and self-links with fragements are always known. + # 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' ); @@ -252,15 +287,8 @@ class Linker { } else { $colour = ''; if ( $nt->isContentPage() ) { - # FIXME: This is stupid, we should combine this query with - # the Title::getArticleID() query above. $threshold = $wgUser->getOption('stubthreshold'); - $dbr = wfGetDB( DB_SLAVE ); - $s = $dbr->selectRow( - array( 'page' ), - array( 'page_len', 'page_is_redirect', 'page_namespace' ), - array( 'page_id' => $aid ), __METHOD__ ) ; - $colour = $this->getLinkColour( $s, $threshold ); + $colour = $this->getLinkColour( $nt, $threshold ); } $retVal = $this->makeColouredLinkObj( $nt, $colour, $text, $query, $trail, $prefix ); } @@ -284,15 +312,17 @@ class Linker { * @param $style String: style to apply - if empty, use getInternalLinkAttributesObj instead * @return the a-element */ - function makeKnownLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) { + function makeKnownLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) { wfProfileIn( __METHOD__ ); - if ( !$nt instanceof Title ) { + if ( !$title instanceof Title ) { # Fail gracefully wfProfileOut( __METHOD__ ); return "<!-- ERROR -->{$prefix}{$text}{$trail}"; } + $nt = $this->normaliseSpecialPage( $title ); + $u = $nt->escapeLocalURL( $query ); if ( $nt->getFragment() != '' ) { if( $nt->getPrefixedDbkey() == '' ) { @@ -320,7 +350,7 @@ class Linker { /** * 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 @@ -328,30 +358,35 @@ class Linker { * be included in the link text. Other characters will be appended after * the end of the link. */ - function makeBrokenLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) { + function makeBrokenLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '' ) { wfProfileIn( __METHOD__ ); - if ( !$nt instanceof Title ) { + if ( !$title instanceof Title ) { # Fail gracefully wfProfileOut( __METHOD__ ); return "<!-- ERROR -->{$prefix}{$text}{$trail}"; } + $nt = $this->normaliseSpecialPage( $title ); + if( $nt->getNamespace() == NS_SPECIAL ) { $q = $query; } else if ( '' == $query ) { - $q = 'action=edit'; + $q = 'action=edit&redlink=1'; } else { - $q = 'action=edit&'.$query; + $q = 'action=edit&redlink=1&'.$query; } $u = $nt->escapeLocalURL( $q ); + $titleText = $nt->getPrefixedText(); if ( '' == $text ) { - $text = htmlspecialchars( $nt->getPrefixedText() ); + $text = htmlspecialchars( $titleText ); } - $style = $this->getInternalLinkAttributesObj( $nt, $text, 'new' ); - + $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 = "<a href=\"{$u}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}"; wfProfileOut( __METHOD__ ); @@ -360,9 +395,9 @@ class Linker { /** * @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 @@ -376,7 +411,7 @@ class Linker { /** * Make a coloured link. - * + * * @param $nt Title object of the target page * @param $colour Integer: colour of the link * @param $text String: link text @@ -412,7 +447,7 @@ class Linker { return $this->makeColouredLinkObj( $nt, $colour, $text, $query, $trail, $prefix ); } - /** + /** * Make appropriate markup for a link to the current article. This is currently rendered * as the bold link text. The calling sequence is the same as the other make*LinkObj functions, * despite $query not being used. @@ -425,6 +460,16 @@ class Linker { return "<strong class=\"selflink\">{$prefix}{$text}{$inside}</strong>{$trail}"; } + function normaliseSpecialPage( Title $title ) { + if ( $title->getNamespace() == NS_SPECIAL ) { + list( $name, $subpage ) = SpecialPage::resolveAliasWithSubpage( $title->getDBkey() ); + if ( !$name ) return $title; + return SpecialPage::getTitleFor( $name, $subpage ); + } else { + return $title; + } + } + /** @todo document */ function fnamePart( $url ) { $basename = strrchr( $url, '/' ); @@ -433,7 +478,7 @@ class Linker { } else { $basename = substr( $basename, 1 ); } - return htmlspecialchars( $basename ); + return $basename; } /** Obsolete alias */ @@ -446,11 +491,19 @@ class Linker { if ( '' == $alt ) { $alt = $this->fnamePart( $url ); } - $s = '<img src="'.$url.'" alt="'.$alt.'" />'; - return $s; + $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); + return $img; + } + return Xml::element( 'img', + array( + 'src' => $url, + 'alt' => $alt ) ); } - /** + /** * Creates the HTML source for images * @deprecated use makeImageLink2 * @@ -490,12 +543,14 @@ class Linker { } /** - * Make an image link + * Given parameters derived from [[Image:Foo|options...]], generate the + * HTML that that syntax inserts in the page. + * * @param Title $title Title object * @param File $file File object, or false if it doesn't exist * * @param array $frameParams Associative array of parameters external to the media handler. - * Boolean parameters are indicated by presence or absence, the value is arbitrary and + * Boolean parameters are indicated by presence or absence, the value is arbitrary and * will often be false. * thumbnail If present, downscale and frame * manualthumb Image name to use as a thumbnail, instead of automatic scaling @@ -505,16 +560,24 @@ class Linker { * upright_factor Fudge factor for "upright" tweak (default 0.75) * border If present, show a border around the image * align Horizontal alignment (left, right, center, none) - * valign Vertical alignment (baseline, sub, super, top, text-top, middle, + * valign Vertical alignment (baseline, sub, super, top, text-top, middle, * bottom, text-bottom) * alt Alternate text for image (i.e. alt attribute). Plain text. * caption HTML for image caption. * - * @param array $handlerParams Associative array of media handler parameters, to be passed - * to transform(). Typical keys are "width" and "page". + * @param array $handlerParams Associative array of media handler parameters, to be passed + * to transform(). Typical keys are "width" and "page". * @param string $time, timestamp of the file, set as false for current + * @param string $query, query params for desc url + * @return string HTML for an image, with links, wrappers, etc. */ - function makeImageLink2( Title $title, $file, $frameParams = array(), $handlerParams = array(), $time = false ) { + function makeImageLink2( Title $title, $file, $frameParams = array(), $handlerParams = array(), $time = false, $query = "" ) { + $res = null; + if( !wfRunHooks( 'ImageBeforeProduceHTML', array( &$this, &$title, + &$file, &$frameParams, &$handlerParams, &$time, &$res ) ) ) { + return $res; + } + global $wgContLang, $wgUser, $wgThumbLimits, $wgThumbUpright; if ( $file && !$file->allowInlineDisplay() ) { wfDebug( __METHOD__.': '.$title->getPrefixedDBkey()." does not allow inline display\n" ); @@ -554,8 +617,8 @@ class Linker { } // Use width which is smaller: real image width or user preference width // 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 - $prefWidth = isset( $fp['upright'] ) ? - round( $wgThumbLimits[$wopt] * $fp['upright'], -1 ) : + $prefWidth = isset( $fp['upright'] ) ? + round( $wgThumbLimits[$wopt] * $fp['upright'], -1 ) : $wgThumbLimits[$wopt]; if ( $hp['width'] <= 0 || $prefWidth < $hp['width'] ) { $hp['width'] = $prefWidth; @@ -575,7 +638,7 @@ class Linker { if ( $fp['align'] == '' ) { $fp['align'] = $wgContLang->isRTL() ? 'left' : 'right'; } - return $prefix.$this->makeThumbLink2( $title, $file, $fp, $hp, $time ).$postfix; + return $prefix.$this->makeThumbLink2( $title, $file, $fp, $hp, $time, $query ).$postfix; } if ( $file && isset( $fp['frameless'] ) ) { @@ -599,6 +662,7 @@ class Linker { } else { $s = $thumb->toHtml( array( 'desc-link' => true, + 'desc-query' => $query, 'alt' => $fp['alt'], 'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false , 'img-class' => isset( $fp['border'] ) ? 'thumbborder' : false ) ); @@ -611,11 +675,11 @@ class Linker { /** * Make HTML for a thumbnail including image, border and caption - * @param Title $title + * @param Title $title * @param File $file File object or false if it doesn't exist */ function makeThumbLinkObj( Title $title, $file, $label = '', $alt, $align = 'right', $params = array(), $framed=false , $manualthumb = "" ) { - $frameParams = array( + $frameParams = array( 'alt' => $alt, 'caption' => $label, 'align' => $align @@ -625,7 +689,7 @@ class Linker { return $this->makeThumbLink2( $title, $file, $frameParams, $params ); } - function makeThumbLink2( Title $title, $file, $frameParams = array(), $handlerParams = array(), $time = false ) { + function makeThumbLink2( Title $title, $file, $frameParams = array(), $handlerParams = array(), $time = false, $query = "" ) { global $wgStylePath, $wgContLang; $exists = $file && $file->exists(); @@ -639,7 +703,7 @@ class Linker { if ( !isset( $fp['caption'] ) ) $fp['caption'] = ''; if ( empty( $hp['width'] ) ) { - // Reduce width for upright images when parameter 'upright' is used + // Reduce width for upright images when parameter 'upright' is used $hp['width'] = isset( $fp['upright'] ) ? 130 : 180; } $thumb = false; @@ -678,7 +742,9 @@ class Linker { } } - $query = $page ? 'page=' . urlencode( $page ) : ''; + if( $page ) { + $query = $query ? '&page=' . urlencode( $page ) : 'page=' . urlencode( $page ); + } $url = $title->getLocalURL( $query ); $more = htmlspecialchars( wfMsg( 'thumbnail-more' ) ); @@ -694,7 +760,8 @@ class Linker { $s .= $thumb->toHtml( array( 'alt' => $fp['alt'], 'img-class' => 'thumbimage', - 'desc-link' => true ) ); + 'desc-link' => true, + 'desc-query' => $query ) ); if ( isset( $fp['framed'] ) ) { $zoomicon=""; } else { @@ -729,9 +796,9 @@ class Linker { if( $text == '' ) $text = htmlspecialchars( $title->getPrefixedText() ); $redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title ); - if( $redir ) { + if( $redir ) { return $this->makeKnownLinkObj( $title, $text, $query, $trail, $prefix ); - } + } $q = 'wpDestFile=' . $title->getPartialUrl(); if( $query != '' ) $q .= '&' . $query; @@ -750,27 +817,28 @@ class Linker { } /** @deprecated use Linker::makeMediaLinkObj() */ - function makeMediaLink( $name, $unused = '', $text = '' ) { + function makeMediaLink( $name, $unused = '', $text = '', $time = false ) { $nt = Title::makeTitleSafe( NS_IMAGE, $name ); - return $this->makeMediaLinkObj( $nt, $text ); + return $this->makeMediaLinkObj( $nt, $text, $time ); } /** * Create a direct link to a given uploaded file. * * @param $title Title object. - * @param $text String: pre-sanitized HTML + * @param $text String: pre-sanitized HTML + * @param $time string: time image was created * @return string HTML * * @public * @todo Handle invalid or missing images better. */ - function makeMediaLinkObj( $title, $text = '' ) { + function makeMediaLinkObj( $title, $text = '', $time = false ) { if( is_null( $title ) ) { ### HOTFIX. Instead of breaking, return empty string. return $text; } else { - $img = wfFindFile( $title ); + $img = wfFindFile( $title, $time ); if( $img ) { $url = $img->getURL(); $class = 'internal'; @@ -809,6 +877,12 @@ class Linker { if( $escape ) { $text = htmlspecialchars( $text ); } + $link = ''; + $success = wfRunHooks('LinkerMakeExternalLink', array( &$url, &$text, &$link ) ); + if(!$success) { + wfDebug("Hook LinkerMakeExternalLink changed the output of link with url {$url} and text {$text} to {$link}", true); + return $link; + } return '<a href="'.$url.'"'.$style.'>'.$text.'</a>'; } @@ -838,9 +912,10 @@ class Linker { * @param string $userText User name or IP address * @param bool $redContribsWhenNoEdits Should the contributions link be red if the user has no edits? * @param int $flags Customisation flags (e.g. self::TOOL_LINKS_NOBLOCK) + * @param int $edits, user edit count (optional, for performance) * @return string */ - public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0 ) { + public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits=null ) { global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans; $talkable = !( $wgDisableAnonTalk && 0 == $userId ); $blockable = ( $wgSysopUserBans || 0 == $userId ) && !$flags & self::TOOL_LINKS_NOBLOCK; @@ -851,8 +926,9 @@ class Linker { } if( $userId ) { // check if the user has an edit - if( $redContribsWhenNoEdits && User::edits( $userId ) == 0 ) { - $style = " class='new'"; + if( $redContribsWhenNoEdits ) { + $count = !is_null($edits) ? $edits : User::edits( $userId ); + $style = ($count == 0) ? " class='new'" : ''; } else { $style = ''; } @@ -873,9 +949,12 @@ class Linker { /** * Alias for userToolLinks( $userId, $userText, true ); + * @param int $userId User identifier + * @param string $userText User name or IP address + * @param int $edits, user edit count (optional, for performance) */ - public function userToolLinksRedContribs( $userId, $userText ) { - return $this->userToolLinks( $userId, $userText, true ); + public function userToolLinksRedContribs( $userId, $userText, $edits=null ) { + return $this->userToolLinks( $userId, $userText, true, 0, $edits ); } @@ -903,14 +982,17 @@ class Linker { wfMsgHtml( 'blocklink' ) ); return $blockLink; } - + /** * Generate a user link if the current user is allowed to view it * @param $rev Revision object. + * @param $isPublic, bool, show only if all users can see it * @return string HTML */ - function revUserLink( $rev ) { - if( $rev->userCan( Revision::DELETED_USER ) ) { + function revUserLink( $rev, $isPublic = false ) { + 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() ); } else { $link = wfMsgHtml( 'rev-deleted-user' ); @@ -924,22 +1006,24 @@ class Linker { /** * Generate a user tool link cluster if the current user is allowed to view it * @param $rev Revision object. + * @param $isPublic, bool, show only if all users can see it * @return string HTML */ - function revUserTools( $rev ) { - if( $rev->userCan( Revision::DELETED_USER ) ) { + function revUserTools( $rev, $isPublic = false ) { + 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() ); + ' ' . $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() ); } else { $link = wfMsgHtml( 'rev-deleted-user' ); } if( $rev->isDeleted( Revision::DELETED_USER ) ) { - return '<span class="history-deleted">' . $link . '</span>'; + return ' <span class="history-deleted">' . $link . '</span>'; } return $link; } - + /** * This function is called by all recent changes variants, by the page history, * and by the user contributions list. It is responsible for formatting edit @@ -1012,10 +1096,12 @@ class Linker { } $auto = $link . $auto; if( $pre ) { - $auto = '- ' . $auto; # written summary $presep autocomment (summary /* section */) + # written summary $presep autocomment (summary /* section */) + $auto = wfMsgExt( 'autocomment-prefix', array( 'escapenoentities', 'content' ) ) . $auto; } if( $post ) { - $auto .= ': '; # autocomment $postsep written summary (/* section */ summary) + # autocomment $postsep written summary (/* section */ summary) + $auto .= wfMsgExt( 'colon-separator', array( 'escapenoentities', 'content' ) ); } $auto = '<span class="autocomment">' . $auto . '</span>'; $comment = $pre . $auto . $post; @@ -1038,15 +1124,20 @@ class Linker { array( $this, 'formatLinksInCommentCallback' ), $comment ); } - + protected function formatLinksInCommentCallback( $match ) { global $wgContLang; - $medians = '(?:' . preg_quote( Namespace::getCanonicalName( NS_MEDIA ), '/' ) . '|'; + $medians = '(?:' . preg_quote( MWNamespace::getCanonicalName( NS_MEDIA ), '/' ) . '|'; $medians .= preg_quote( $wgContLang->getNsText( NS_MEDIA ), '/' ) . '):'; - + $comment = $match[0]; + # fix up urlencoded title texts (copied from Parser::replaceInternalLinks) + if( strpos( $match[1], '%' ) !== false ) { + $match[1] = str_replace( array('<', '>'), array('<', '>'), urldecode($match[1]) ); + } + # Handle link renaming [[foo|text]] will show link as "text" if( "" != $match[3] ) { $text = $match[3]; @@ -1103,14 +1194,16 @@ class Linker { * * @param Revision $rev * @param bool $local Whether section links should refer to local page + * @param $isPublic, show only if all users can see it * @return string HTML */ - function revComment( Revision $rev, $local = false ) { - if( $rev->userCan( Revision::DELETED_COMMENT ) ) { + function revComment( Revision $rev, $local = false, $isPublic = false ) { + if( $rev->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) { + $block = " <span class=\"comment\">" . wfMsgHtml( 'rev-deleted-comment' ) . "</span>"; + } else if( $rev->userCan( Revision::DELETED_COMMENT ) ) { $block = $this->commentBlock( $rev->getRawComment(), $rev->getTitle(), $local ); } else { - $block = " <span class=\"comment\">" . - wfMsgHtml( 'rev-deleted-comment' ) . "</span>"; + $block = " <span class=\"comment\">" . wfMsgHtml( 'rev-deleted-comment' ) . "</span>"; } if( $rev->isDeleted( Revision::DELETED_COMMENT ) ) { return " <span class=\"history-deleted\">$block</span>"; @@ -1118,6 +1211,18 @@ class Linker { return $block; } + public function formatRevisionSize( $size ) { + if ( $size == 0 ) { + $stxt = wfMsgExt( 'historyempty', 'parsemag' ); + } else { + global $wgLang; + $stxt = wfMsgExt( 'nbytes', 'parsemag', $wgLang->formatNum( $size ) ); + $stxt = "($stxt)"; + } + $stxt = htmlspecialchars( $stxt ); + return "<span class=\"history-size\">$stxt</span>"; + } + /** @todo document */ function tocIndent() { return "\n<ul>"; @@ -1202,7 +1307,7 @@ class Linker { $editurl = '§ion='.$section; $url = $this->makeKnownLinkObj( $nt, - wfMsg('editsection'), + htmlspecialchars(wfMsg('editsection')), 'action=edit'.$editurl, '', '', '', $hint ); @@ -1214,13 +1319,13 @@ class Linker { } elseif( $hook == 'EditSectionLinkForOther' ) { wfRunHooks( 'EditSectionLinkForOther', array( &$this, $nt, $section, $url, &$result ) ); } - + // For reverse compatibility, add the brackets *after* the hook is run, // and even add them to hook-provided text. if( is_null( $result ) ) { - $result = wfMsg( 'editsection-brackets', $url ); + $result = wfMsgHtml( 'editsection-brackets', $url ); } else { - $result = wfMsg( 'editsection-brackets', $result ); + $result = wfMsgHtml( 'editsection-brackets', $result ); } return "<span class=\"editsection\">$result</span>"; } @@ -1282,7 +1387,7 @@ class Linker { . $this->buildRollbackLink( $rev ) . ']</span>'; } - + /** * Build a raw rollback link, useful for collections of "tool" links * @@ -1299,7 +1404,7 @@ class Linker { $title, wfMsgHtml( 'rollbacklink' ), 'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extra - ); + ); } /** @@ -1337,9 +1442,10 @@ class Linker { } $outText .= '</div><ul>'; + usort( $templates, array( 'Title', 'compare' ) ); foreach ( $templates as $titleObj ) { $r = $titleObj->getRestrictions( 'edit' ); - if ( in_array( 'sysop', $r ) ) { + if ( in_array( 'sysop', $r ) ) { $protected = wfMsgExt( 'template-protected', array( 'parseinline' ) ); } elseif ( in_array( 'autoconfirmed', $r ) ) { $protected = wfMsgExt( 'template-semiprotected', array( 'parseinline' ) ); @@ -1353,7 +1459,36 @@ class Linker { wfProfileOut( __METHOD__ ); return $outText; } - + + /** + * Returns HTML for the "hidden categories on this page" list. + * + * @param array $hiddencats Array of hidden categories from Article::getHiddenCategories + * or similar + * @return string HTML output + */ + public function formatHiddenCategories( $hiddencats) { + global $wgUser, $wgLang; + wfProfileIn( __METHOD__ ); + + $sk = $wgUser->getSkin(); + + $outText = ''; + if ( count( $hiddencats ) > 0 ) { + # Construct the HTML + $outText = '<div class="mw-hiddenCategoriesExplanation">'; + $outText .= wfMsgExt( 'hiddencategories', array( 'parse' ), $wgLang->formatnum( count( $hiddencats ) ) ); + $outText .= '</div><ul>'; + + foreach ( $hiddencats as $titleObj ) { + $outText .= '<li>' . $sk->makeKnownLinkObj( $titleObj ) . '</li>'; # If it's hidden, it must exist - no need to check with a LinkBatch + } + $outText .= '</ul>'; + } + wfProfileOut( __METHOD__ ); + return $outText; + } + /** * Format a size in bytes for output, using an appropriate * unit (B, KB, MB or GB) according to the magnitude in question @@ -1376,26 +1511,29 @@ class Linker { * @return string title and accesskey attributes, ready to drop in an * element (e.g., ' title="This does something [x]" accesskey="x"'). */ - public function tooltipAndAccesskey($name) { - $fname="Linker::tooltipAndAccesskey"; - wfProfileIn($fname); - $out = ''; + public function tooltipAndAccesskey( $name ) { + wfProfileIn( __METHOD__ ); + $attribs = array(); - $tooltip = wfMsg('tooltip-'.$name); - if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') { + $tooltip = wfMsg( "tooltip-$name" ); + if( !wfEmptyMsg( "tooltip-$name", $tooltip ) && $tooltip != '-' ) { // Compatibility: formerly some tooltips had [alt-.] hardcoded $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip ); - $out .= ' title="'.htmlspecialchars($tooltip); + $attribs['title'] = $tooltip; } - $accesskey = wfMsg('accesskey-'.$name); - if ($accesskey && $accesskey != '-' && !wfEmptyMsg('accesskey-'.$name, $accesskey)) { - if ($out) $out .= " [$accesskey]\" accesskey=\"$accesskey\""; - else $out .= " title=\"[$accesskey]\" accesskey=\"$accesskey\""; - } elseif ($out) { - $out .= '"'; + + $accesskey = wfMsg( "accesskey-$name" ); + if( $accesskey && $accesskey != '-' && + !wfEmptyMsg( "accesskey-$name", $accesskey ) ) { + if( isset( $attribs['title'] ) ) { + $attribs['title'] .= " [$accesskey]"; + } + $attribs['accesskey'] = $accesskey; } - wfProfileOut($fname); - return $out; + + $ret = Xml::expandAttributes( $attribs ); + wfProfileOut( __METHOD__ ); + return $ret; } /** @@ -1404,18 +1542,32 @@ class Linker { * isn't always, because sometimes the accesskey needs to go on a different * element than the id, for reverse-compatibility, etc.) * - * @param string $name Id of the element, minus prefixes. + * @param string $name Id of the element, minus prefixes. + * @param mixed $options null or the string 'withaccess' to add an access- + * key hint * @return string title attribute, ready to drop in an element * (e.g., ' title="This does something"'). */ - public function tooltip($name) { - $out = ''; + public function tooltip( $name, $options = null ) { + wfProfileIn( __METHOD__ ); + + $attribs = array(); + + $tooltip = wfMsg( "tooltip-$name" ); + if( !wfEmptyMsg( "tooltip-$name", $tooltip ) && $tooltip != '-' ) { + $attribs['title'] = $tooltip; + } - $tooltip = wfMsg('tooltip-'.$name); - if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') { - $out = ' title="'.htmlspecialchars($tooltip).'"'; + if( isset( $attribs['title'] ) && $options == 'withaccess' ) { + $accesskey = wfMsg( "accesskey-$name" ); + if( $accesskey && $accesskey != '-' && + !wfEmptyMsg( "accesskey-$name", $accesskey ) ) { + $attribs['title'] .= " [$accesskey]"; + } } - return $out; + $ret = Xml::expandAttributes( $attribs ); + wfProfileOut( __METHOD__ ); + return $ret; } } |