<?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.
*
* @ingroup Skins
*/
/**
* @deprecated
*/
- function postParseLinkColour( $s = NULL ) {
- return NULL;
+ function postParseLinkColour( $s = null ) {
+ wfDeprecated( __METHOD__ );
+ 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 );
-
- $r = ($class != '') ? " class=\"$class\"" : " class=\"external\"";
+ # 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 .= " title=\"{$link}\"";
- return $r;
+ return $this->getLinkAttributesInternal( $title, $class, 'external' );
}
- /** @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.
+ *
+ * @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 );
}
/**
- * @param $nt Title object.
- * @param $text String: FIXME
- * @param $class String: CSS class of the link, default ''.
+ * 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, $text, $class = '', $titleAttr = false ) {
- $r = ($class != '') ? ' class="' . htmlspecialchars( $class ) . '"' : '';
- if ( $titleAttr === false ) {
- $r .= ' title="' . $nt->getEscapedText() . '"';
- } else {
- $r .= ' title="' . htmlspecialchars( $titleAttr ) . '"';
+ function getInternalLinkAttributesObj( $nt, $unused = null, $class = '', $title = false ) {
+ if( $title === false ) {
+ $title = $nt->getPrefixedText();
}
+ return $this->getLinkAttributesInternal( $title, $class );
+ }
+
+ /**
+ * Common code for getLinkAttributesX functions
+ */
+ 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;
}
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';
}
}
/**
+ * This function returns an HTML link to the given target. It serves a few
+ * purposes:
+ * 1) If $target is a Title, the correct URL to link to will be figured
+ * out automatically.
+ * 2) It automatically adds the usual classes for various types of link
+ * targets: "new" for red links, "stub" for short articles, etc.
+ * 3) It escapes all attribute values safely so there's no risk of XSS.
+ * 4) It provides a default tooltip if the target is a Title (the page
+ * name of the target).
+ * link() replaces the old functions in the makeLink() family.
+ *
+ * @param $target Title Can currently only be a Title, but this may
+ * change to support Images, literal URLs, etc.
+ * @param $text string The HTML contents of the <a> element, i.e.,
+ * the link text. This is raw HTML and will not be escaped. If null,
+ * defaults to the prefixed text of the Title; or if the Title is just a
+ * fragment, the contents of the fragment.
+ * @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.
+ * 'noclasses': Don't add any classes automatically (includes "new",
+ * "stub", "mw-redirect", "extiw"). Only use the class attribute
+ * provided, if any, so you get a simple blue link with no funny i-
+ * cons.
+ * @return string HTML <a> attribute
+ */
+ public function link( $target, $text = null, $customAttribs = array(), $query = array(), $options = array() ) {
+ wfProfileIn( __METHOD__ );
+ $ret = null;
+ if( !wfRunHooks( 'LinkBegin', array( $this, $target, &$text,
+ &$customAttribs, &$query, &$options, &$ret ) ) ) {
+ wfProfileOut( __METHOD__ );
+ return $ret;
+ }
+
+ if( !$target instanceof Title ) {
+ return "<!-- ERROR -->$text";
+ }
+ $options = (array)$options;
+
+ # 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() ) {
+ $options []= 'known';
+ } else {
+ $options []= 'broken';
+ }
+ }
+ wfProfileOut( __METHOD__ . '-checkPageExistence' );
+
+ # Note: we want the href attribute first, for prettiness.
+ $attribs = array( 'href' => $this->linkUrl( $target, $query, $options ) );
+ $attribs = array_merge(
+ $attribs,
+ $this->linkAttribs( $target, $customAttribs, $options )
+ );
+ if( is_null( $text ) ) {
+ $text = $this->linkText( $target );
+ }
+
+ $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;
+ }
+
+ 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).
+ if( in_array( 'broken', $options ) and empty( $query['action'] )
+ and $target->getNamespace() != NS_SPECIAL ) {
+ $query['action'] = 'edit';
+ $query['redlink'] = '1';
+ }
+ $ret = $target->getLinkUrl( $query );
+ wfProfileOut( __METHOD__ );
+ return $ret;
+ }
+
+ private function linkAttribs( $target, $attribs, $options ) {
+ wfProfileIn( __METHOD__ );
+ global $wgUser;
+ $defaults = array();
+
+ if( !in_array( 'noclasses', $options ) ) {
+ wfProfileIn( __METHOD__ . '-getClasses' );
+ # Now build the classes.
+ $classes = array();
+
+ if( in_array( 'broken', $options ) ) {
+ $classes[] = 'new';
+ }
+
+ if( $target->isExternal() ) {
+ $classes[] = 'extiw';
+ }
+
+ # Note that redirects never count as stubs here.
+ if ( $target->isRedirect() ) {
+ $classes[] = 'mw-redirect';
+ } elseif( $target->isContentPage() ) {
+ # Check for stub.
+ $threshold = $wgUser->getOption( 'stubthreshold' );
+ if( $threshold > 0 and $target->exists() and $target->getLength() < $threshold ) {
+ $classes[] = 'stub';
+ }
+ }
+ if( $classes != array() ) {
+ $defaults['class'] = implode( ' ', $classes );
+ }
+ wfProfileOut( __METHOD__ . '-getClasses' );
+ }
+
+ # Get a default title attribute.
+ if( in_array( 'known', $options ) ) {
+ $defaults['title'] = $target->getPrefixedText();
+ } else {
+ $defaults['title'] = wfMsg( 'red-link-title', $target->getPrefixedText() );
+ }
+
+ # Finally, merge the custom attribs with the default ones, and iterate
+ # over that, deleting all "false" attributes.
+ $ret = array();
+ $merged = Sanitizer::mergeAttributes( $defaults, $attribs );
+ foreach( $merged as $key => $val ) {
+ # A false value suppresses the attribute, and we don't want the
+ # href attribute to be overridden.
+ if( $key != 'href' and $val !== false ) {
+ $ret[$key] = $val;
+ }
+ }
+ wfProfileOut( __METHOD__ );
+ return $ret;
+ }
+
+ 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() !== '' ) {
+ return htmlspecialchars( $target->getFragment() );
+ }
+ 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.
*
}
/**
+ * @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.
*
}
/**
+ * @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.
*
}
/**
- * @deprecated use makeColouredLinkObj
+ * @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.
* 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 );
}
/**
+ * @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.
global $wgUser;
wfProfileIn( __METHOD__ );
- if ( !$nt instanceof Title ) {
- # Fail gracefully
- wfProfileOut( __METHOD__ );
- return "<!-- ERROR -->{$prefix}{$text}{$trail}";
+ $query = wfCgiToArray( $query );
+ list( $inside, $trail ) = Linker::splitTrail( $trail );
+ if( $text === '' ) {
+ $text = $this->linkText( $nt );
}
- 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 = "<a href=\"{$u}\"{$style}>{$text}{$inside}</a>";
-
- 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' );
+ $ret = $this->link( $nt, "$prefix$text$inside", array(), $query ) . $trail;
- # 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;
+ 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 $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 ) {
- # Fail gracefully
- wfProfileOut( __METHOD__ );
- return "<!-- ERROR -->{$prefix}{$text}{$trail}";
- }
-
- $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 );
+ $text = $this->linkText( $title );
}
+ $attribs = Sanitizer::mergeAttributes(
+ Sanitizer::decodeTagAttributes( $aprops ),
+ Sanitizer::decodeTagAttributes( $style )
+ );
+ $query = wfCgiToArray( $query );
+ list( $inside, $trail ) = Linker::splitTrail( $trail );
- if ( $aprops !== '' ) $aprops = ' ' . $aprops;
+ $ret = $this->link( $title, "$prefix$text$inside", $attribs, $query,
+ array( 'known', 'noclasses' ) ) . $trail;
- list( $inside, $trail ) = Linker::splitTrail( $trail );
- $r = "<a href=\"{$u}\"{$style}{$aprops}>{$prefix}{$text}{$inside}</a>{$trail}";
wfProfileOut( __METHOD__ );
- return $r;
+ 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
* 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 ) {
- # Fail gracefully
- wfProfileOut( __METHOD__ );
- return "<!-- ERROR -->{$prefix}{$text}{$trail}";
- }
-
- 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 );
+ if( $text === '' ) {
+ $text = $this->linkText( $title );
+ }
+ $nt = $this->normaliseSpecialPage( $title );
- wfRunHooks( 'BrokenLink', array( &$this, $nt, $query, &$u, &$style, &$prefix, &$text, &$inside, &$trail ) );
- $s = "<a href=\"{$u}\"{$style}>{$prefix}{$text}{$inside}</a>{$trail}";
+ $ret = $this->link( $title, "$prefix$text$inside", array(),
+ wfCgiToArray( $query ), 'broken' ) . $trail;
wfProfileOut( __METHOD__ );
- return $s;
+ return $ret;
}
/**
- * @deprecated use makeColouredLinkObj
+ * @deprecated Use link()
*
* Make a brown link to a short article.
*
* 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
* the end of the link.
*/
function makeColouredLinkObj( $nt, $colour, $text = '', $query = '', $trail = '', $prefix = '' ) {
-
if($colour != ''){
$style = $this->getInternalLinkAttributesObj( $nt, $text, $colour );
} else $style = '';
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;
+ $ret = SpecialPage::getTitleFor( $name, $subpage );
+ $ret->mFragment = $title->getFragment();
+ return $ret;
+ } else {
+ return $title;
+ }
+ }
+
/** @todo document */
function fnamePart( $url ) {
$basename = strrchr( $url, '/' );
} else {
$basename = substr( $basename, 1 );
}
- return htmlspecialchars( $basename );
+ return $basename;
}
/** Obsolete alias */
function makeImage( $url, $alt = '' ) {
+ wfDeprecated( __METHOD__ );
return $this->makeExternalImage( $url, $alt );
}
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 ) );
}
/**
global $wgContLang, $wgUser, $wgThumbLimits, $wgThumbUpright;
if ( $file && !$file->allowInlineDisplay() ) {
wfDebug( __METHOD__.': '.$title->getPrefixedDBkey()." does not allow inline display\n" );
- return $this->makeKnownLinkObj( $title );
+ return $this->link( $title );
}
// Shortcuts
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>';
}
* @private
*/
function userLink( $userId, $userText ) {
- $encName = htmlspecialchars( $userText );
if( $userId == 0 ) {
- $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
- return $this->makeKnownLinkObj( $contribsPage,
- $encName);
+ $page = SpecialPage::getTitleFor( 'Contributions', $userText );
} else {
- $userPage = Title::makeTitle( NS_USER, $userText );
- return $this->makeLinkObj( $userPage, $encName );
+ $page = Title::makeTitle( NS_USER, $userText );
}
+ return $this->link( $page, htmlspecialchars( $userText ) );
}
/**
}
if( $userId ) {
// check if the user has an edit
+ $attribs = array();
if( $redContribsWhenNoEdits ) {
$count = !is_null($edits) ? $edits : User::edits( $userId );
- $style = ($count == 0) ? " class='new'" : '';
- } else {
- $style = '';
+ if( $count == 0 ) {
+ $attribs['class'] = 'new';
+ }
}
$contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText );
- $items[] = $this->makeKnownLinkObj( $contribsPage, wfMsgHtml( 'contribslink' ), '', '', '', '', $style );
+ $items[] = $this->link( $contribsPage, wfMsgHtml( 'contribslink' ), $attribs );
}
if( $blockable && $wgUser->isAllowed( 'block' ) ) {
$items[] = $this->blockLink( $userId, $userText );
*/
function userTalkLink( $userId, $userText ) {
$userTalkPage = Title::makeTitle( NS_USER_TALK, $userText );
- $userTalkLink = $this->makeLinkObj( $userTalkPage, wfMsgHtml( 'talkpagelinktext' ) );
+ $userTalkLink = $this->link( $userTalkPage, wfMsgHtml( 'talkpagelinktext' ) );
return $userTalkLink;
}
*/
function blockLink( $userId, $userText ) {
$blockPage = SpecialPage::getTitleFor( 'Blockip', $userText );
- $blockLink = $this->makeKnownLinkObj( $blockPage,
- wfMsgHtml( 'blocklink' ) );
+ $blockLink = $this->link( $blockPage, wfMsgHtml( 'blocklink' ) );
return $blockLink;
}
# 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 );
*
* @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 = wfClone( $title );
- $sectionTitle->mFragment = $section;
- }
- $link = $this->makeKnownLinkObj( $sectionTitle, wfMsgForContent( 'sectionlink' ) );
- }
- $auto = $link . $auto;
- if( $pre ) {
- # written summary $presep autocomment (summary /* section */)
- $auto = wfMsgExt( 'autocomment-prefix', array( 'escapenoentities', 'content' ) ) . $auto;
+ private function formatAutocomments( $comment, $title = null, $local = false ) {
+ // 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( $post ) {
- # autocomment $postsep written summary (/* section */ summary)
- $auto .= wfMsgExt( 'colon-separator', array( 'escapenoentities', 'content' ) );
+ if ( $sectionTitle ) {
+ $link = $this->link( $sectionTitle,
+ wfMsgForContent( 'sectionlink' ), array(), array(),
+ 'noclasses' );
+ } else {
+ $link = '';
}
- $auto = '<span class="autocomment">' . $auto . '</span>';
- $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 = '<span class="autocomment">' . $auto . '</span>';
+ $comment = $pre . $auto . $post;
return $comment;
}
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>";
* @param $section Integer: section number.
*/
public function editSectionLinkForOther( $title, $section ) {
+ wfDeprecated( __METHOD__ );
$title = Title::newFromText( $title );
- return $this->doEditSectionLink( $title, $section, '', 'EditSectionLinkForOther' );
+ return $this->doEditSectionLink( $title, $section );
}
/**
* @param $section Integer: section number.
* @param $hint Link String: title, or default if omitted or empty
*/
- public function editSectionLink( Title $nt, $section, $hint='' ) {
- if( $hint != '' ) {
- $hint = wfMsgHtml( 'editsectionhint', htmlspecialchars( $hint ) );
- $hint = " title=\"$hint\"";
- }
- return $this->doEditSectionLink( $nt, $section, $hint, 'EditSectionLink' );
+ 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 );
}
/**
- * Implement editSectionLink and editSectionLinkForOther.
+ * Create a section edit link. This supersedes editSectionLink() and
+ * editSectionLinkForOther().
*
- * @param $nt Title object
- * @param $section Integer, section number
- * @param $hint String, for HTML title attribute
- * @param $hook String, name of hook to run
- * @return String, HTML to use for edit link
+ * @param $nt Title The title being linked to (may not be the same as
+ * $wgTitle, if the section is included from a template)
+ * @param $section string The designation of the section being pointed to,
+ * to be included in the link, like "§ion=$section"
+ * @param $tooltip string The tooltip to use for the link: will be escaped
+ * and wrapped in the 'editsectionhint' message
+ * @return string HTML to use for edit link
*/
- protected function doEditSectionLink( Title $nt, $section, $hint, $hook ) {
- global $wgContLang;
- $editurl = '§ion='.$section;
- $url = $this->makeKnownLinkObj(
- $nt,
- wfMsg('editsection'),
- 'action=edit'.$editurl,
- '', '', '', $hint
+ public function doEditSectionLink( Title $nt, $section, $tooltip = null ) {
+ $attribs = array();
+ if( !is_null( $tooltip ) ) {
+ $attribs['title'] = wfMsg( 'editsectionhint', $tooltip );
+ }
+ $link = $this->link( $nt, wfMsg('editsection'),
+ $attribs,
+ array( 'action' => 'edit', 'section' => $section ),
+ array( 'noclasses', 'known' )
);
- $result = null;
- // The two hooks have slightly different interfaces . . .
- if( $hook == 'EditSectionLink' ) {
- wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $hint, $url, &$result ) );
- } elseif( $hook == 'EditSectionLinkForOther' ) {
- wfRunHooks( 'EditSectionLinkForOther', array( &$this, $nt, $section, $url, &$result ) );
+ # Run the old hook. This takes up half of the function . . . hopefully
+ # we can rid of it someday.
+ $attribs = '';
+ if( $tooltip ) {
+ $attribs = wfMsgHtml( 'editsectionhint', htmlspecialchars( $tooltip ) );
+ $attribs = " title=\"$attribs\"";
}
-
- // 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 );
- } else {
- $result = wfMsg( 'editsection-brackets', $result );
- }
- return "<span class=\"editsection\">$result</span>";
+ $result = null;
+ 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', $result );
+ return "<span class=\"editsection\">$result</span>";
+ }
+
+ # Add the brackets and the span, and *then* run the nice new hook, with
+ # clean and non-redundant arguments.
+ $result = wfMsgHtml( 'editsection-brackets', $link );
+ $result = "<span class=\"editsection\">$result</span>";
+
+ wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result ) );
+ return $result;
}
/**
public function buildRollbackLink( $rev ) {
global $wgRequest, $wgUser;
$title = $rev->getTitle();
- $extra = $wgRequest->getBool( 'bot' ) ? '&bot=1' : '';
- $extra .= '&token=' . urlencode( $wgUser->editToken( array( $title->getPrefixedText(),
- $rev->getUserText() ) ) );
- return $this->makeKnownLinkObj(
- $title,
- wfMsgHtml( 'rollbacklink' ),
- 'action=rollback&from=' . urlencode( $rev->getUserText() ) . $extra
+ $query = array(
+ 'action' => 'rollback',
+ 'from' => $rev->getUserText()
);
+ if( $wgRequest->getBool( 'bot' ) ) {
+ $query['bot'] = '1';
+ }
+ $query['token'] = $wgUser->editToken( array( $title->getPrefixedText(),
+ $rev->getUserText() ) );
+ return $this->link( $title, wfMsgHtml( 'rollbacklink' ),
+ array( 'title' => wfMsg( 'tooltip-rollback' ) ),
+ $query, array( 'known', 'noclasses' ) );
}
/**
} else {
$protected = '';
}
- $outText .= '<li>' . $sk->makeLinkObj( $titleObj ) . ' ' . $protected . '</li>';
+ $outText .= '<li>' . $sk->link( $titleObj ) . ' ' . $protected . '</li>';
}
$outText .= '</ul>';
}
$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 .= '<li>' . $sk->link( $titleObj, null, array(), array(), 'known' ) . '</li>'; # If it's hidden, it must exist - no need to check with a LinkBatch
}
$outText .= '</ul>';
}
* @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;
}
/**
* 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__ );
- $tooltip = wfMsg('tooltip-'.$name);
- if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
- $out = ' title="'.htmlspecialchars($tooltip).'"';
+ $attribs = array();
+
+ $tooltip = wfMsg( "tooltip-$name" );
+ if( !wfEmptyMsg( "tooltip-$name", $tooltip ) && $tooltip != '-' ) {
+ $attribs['title'] = $tooltip;
}
- return $out;
+ if( isset( $attribs['title'] ) && $options == 'withaccess' ) {
+ $accesskey = wfMsg( "accesskey-$name" );
+ if( $accesskey && $accesskey != '-' &&
+ !wfEmptyMsg( "accesskey-$name", $accesskey ) ) {
+ $attribs['title'] .= " [$accesskey]";
+ }
+ }
+
+ $ret = Xml::expandAttributes( $attribs );
+ wfProfileOut( __METHOD__ );
+ return $ret;
}
}