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, "extern" for external links, etc.
+ * 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).
+ * 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.
- * @param $text string The HTML contents of the <a> element, i.e., the link text. This is raw HTML and will not be escaped. If null, defaults to the page name of the Title or Image, or the text of the URL if $target is a URL.
- * @param $query array The query string to append to the URL you're linking to, in key => value array form. Useful mainly for Titles and Images. Query keys and values will be URL-encoded.
- * @param $customAttribs array A key => value array of extra HTML attributes, such as title and class. (href is ignored.) Classes will be merged with the default classes, while other attributes will replace default attributes. All passed attribute values will be HTML-escaped. A false attribute value means to suppress that attribute.
+ * @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"). Only use the class attribute provided, if any.
+ * '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.
+ * 'forcearticlepath': Use the article path always, even with a querystring.
+ * Has compatibility issues on some setups, so avoid wherever possible.
* @return string HTML <a> attribute
*/
public function link( $target, $text = null, $customAttribs = array(), $query = array(), $options = array() ) {
wfProfileIn( __METHOD__ );
- if( !($target instanceof Title) ) {
- throw new MWException( 'Linker::link passed invalid target' );
+ if( !$target instanceof Title ) {
+ return "<!-- ERROR -->$text";
}
$options = (array)$options;
- # Normalize the Title if it's a special page
- if( $target->getNamespace() == NS_SPECIAL ) {
- list( $name, $subpage ) = SpecialPage::resolveAliasWithSubpage( $target->getDBkey() );
- if( $name ) {
- $target = SpecialPage::getTitleFor( $name, $subpage );
- }
+ $ret = null;
+ if( !wfRunHooks( 'LinkBegin', array( $this, $target, &$text,
+ &$customAttribs, &$query, &$options, &$ret ) ) ) {
+ wfProfileOut( __METHOD__ );
+ return $ret;
}
+ # Normalize the Title if it's a special page
+ $target = $this->normaliseSpecialPage( $target );
+
# If we don't know whether the page exists, let's find out.
+ wfProfileIn( __METHOD__ . '-checkPageExistence' );
if( !in_array( 'known', $options ) and !in_array( 'broken', $options ) ) {
- if( $target->getNamespace() == NS_SPECIAL ) {
- if( SpecialPage::exists( $target->getDbKey() ) ) {
- $options []= 'known';
- } else {
- $options []= 'broken';
- }
- } elseif( $target->isAlwaysKnown() or
- ($target->getPrefixedText() == '' and $target->getFragment() != '')
- or $target->exists() ) {
+ if( $target->isKnown() ) {
$options []= 'known';
} else {
- # Either it exists
$options []= 'broken';
}
}
+ wfProfileOut( __METHOD__ . '-checkPageExistence' );
+
+ $oldquery = array();
+ if( in_array( "forcearticlepath", $options ) && $query ){
+ $oldquery = $query;
+ $query = array();
+ }
# Note: we want the href attribute first, for prettiness.
$attribs = array( 'href' => $this->linkUrl( $target, $query, $options ) );
+ if( in_array( 'forcearticlepath', $options ) && $oldquery ){
+ $attribs['href'] = wfAppendQuery( $attribs['href'], wfArrayToCgi( $oldquery ) );
+ }
+
$attribs = array_merge(
$attribs,
$this->linkAttribs( $target, $customAttribs, $options )
);
if( is_null( $text ) ) {
- $text = $this->linkText( $target, $options );
+ $text = $this->linkText( $target );
}
- $ret = Xml::element( 'a', $attribs, $text, false );
+ $ret = null;
+ if( wfRunHooks( 'LinkEnd', array( $this, $target, $options, &$text, &$attribs, &$ret ) ) ) {
+ $ret = Xml::openElement( 'a', $attribs ) . $text . Xml::closeElement( 'a' );
+ }
wfProfileOut( __METHOD__ );
return $ret;
}
+ /**
+ * Identical to link(), except $options defaults to 'known'.
+ */
+ public function linkKnown( $target, $text = null, $customAttribs = array(), $query = array(), $options = 'known' ) {
+ return $this->link( $target, $text, $customAttribs, $query, $options );
+ }
+
private function linkUrl( $target, $query, $options ) {
- # If it's a broken link, add the appropriate query pieces. This over-
- # writes the default action!
- if( in_array( 'broken', $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';
}
-
- $queryString = array();
- foreach( $query as $key => $val ) {
- $queryString []= urlencode( $key ) . '=' . urlencode( $val );
- }
- $queryString = implode( '&', $queryString );
-
- if( $target->isExternal() ) {
- return $target->getFullURL( $queryString );
- }
- return $target->getLocalURL( $queryString );
+ $ret = $target->getLinkUrl( $query );
+ wfProfileOut( __METHOD__ );
+ return $ret;
}
private function linkAttribs( $target, $attribs, $options ) {
+ wfProfileIn( __METHOD__ );
global $wgUser;
$defaults = array();
- # First get a default title attribute.
- if( in_array( 'known', $options ) ) {
- $defaults['title'] = $target->getPrefixedText();
- } else {
- $defaults['title'] = wfMsg( 'red-link-title', $target->getPrefixedText() );
- }
-
if( !in_array( 'noclasses', $options ) ) {
- # Now build the classes. This is the bulk of what we're doing.
+ wfProfileIn( __METHOD__ . '-getClasses' );
+ # Now build the classes.
$classes = array();
if( in_array( 'broken', $options ) ) {
- $classes []= 'new';
+ $classes[] = 'new';
+ }
+
+ if( $target->isExternal() ) {
+ $classes[] = 'extiw';
}
# Note that redirects never count as stubs here.
if ( $target->isRedirect() ) {
- $classes []= 'mw-redirect';
+ $classes[] = 'mw-redirect';
} elseif( $target->isContentPage() ) {
+ # Check for stub.
$threshold = $wgUser->getOption( 'stubthreshold' );
- if( $threshold > 0 and $target->getLength() < $threshold ) {
- $classes []= 'stub';
+ 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( $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() );
}
# Finally, merge the custom attribs with the default ones, and iterate
# over that, deleting all "false" attributes.
- if( !empty( $attribs['class'] ) and !empty( $defaults['class'] ) ) {
- $attribs['class'] .= ' '.$defaults['class'];
- }
$ret = array();
- foreach( array_merge( $defaults, $attribs ) as $key => $val ) {
+ $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, $options ) {
+ 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() !== '' ) {
}
/**
+ * @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.
* the end of the link.
* @param $prefix String: optional prefix. As trail, only before instead of after.
*/
- function makeLinkObj( Title $nt, $text= '', $query = '', $trail = '', $prefix = '' ) {
+ function makeLinkObj( $nt, $text= '', $query = '', $trail = '', $prefix = '' ) {
global $wgUser;
wfProfileIn( __METHOD__ );
- if ( $nt->isExternal() ) {
- $u = $nt->getFullURL();
- $link = $nt->getPrefixedURL();
- if ( '' == $text ) { $text = $nt->getPrefixedText(); }
- $style = $this->getInterwikiLinkAttributes( $link, $text, 'extiw' );
-
- $inside = '';
- if ( '' != $trail ) {
- $m = array();
- if ( preg_match( '/^([a-z]+)(.*)$$/sD', $trail, $m ) ) {
- $inside = $m[1];
- $trail = $m[2];
- }
- }
- $t = "<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' );
+ $query = wfCgiToArray( $query );
+ list( $inside, $trail ) = Linker::splitTrail( $trail );
+ if( $text === '' ) {
+ $text = $this->linkText( $nt );
+ }
- # 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;
- }
+ $ret = $this->link( $nt, "$prefix$text$inside", array(), $query ) . $trail;
- # 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( Title $title, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) {
+ function makeKnownLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' ) {
wfProfileIn( __METHOD__ );
- $nt = $this->normaliseSpecialPage( $title );
-
- $u = $nt->escapeLocalURL( $query );
- if ( $nt->getFragment() != '' ) {
- if( $nt->getPrefixedDbkey() == '' ) {
- $u = '';
- if ( '' == $text ) {
- $text = htmlspecialchars( $nt->getFragment() );
- }
- }
- $u .= $nt->getFragmentForURL();
- }
if ( $text == '' ) {
- $text = htmlspecialchars( $nt->getPrefixedText() );
- }
- if ( $style == '' ) {
- $style = $this->getInternalLinkAttributesObj( $nt, $text );
+ $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( Title $title, $text = '', $query = '', $trail = '', $prefix = '' ) {
+ function makeBrokenLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '' ) {
wfProfileIn( __METHOD__ );
- $nt = $this->normaliseSpecialPage( $title );
-
- if( $nt->getNamespace() == NS_SPECIAL ) {
- $q = $query;
- } else if ( '' == $query ) {
- $q = 'action=edit&redlink=1';
- } else {
- $q = 'action=edit&redlink=1&'.$query;
- }
- $u = $nt->escapeLocalURL( $q );
-
- $titleText = $nt->getPrefixedText();
- if ( '' == $text ) {
- $text = htmlspecialchars( $titleText );
- }
- $titleAttr = wfMsg( 'red-link-title', $titleText );
- $style = $this->getInternalLinkAttributesObj( $nt, $text, 'new', $titleAttr );
list( $inside, $trail ) = Linker::splitTrail( $trail );
+ 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.
*
}
/**
+ * @deprecated Use link()
+ *
* Make a coloured link.
*
* @param $nt Title object of the target page
if ( $title->getNamespace() == NS_SPECIAL ) {
list( $name, $subpage ) = SpecialPage::resolveAliasWithSubpage( $title->getDBkey() );
if ( !$name ) return $title;
- return SpecialPage::getTitleFor( $name, $subpage );
+ $ret = SpecialPage::getTitleFor( $name, $subpage );
+ $ret->mFragment = $title->getFragment();
+ return $ret;
} else {
return $title;
}
$img = '';
$success = wfRunHooks('LinkerMakeExternalImage', array( &$url, &$alt, &$img ) );
if(!$success) {
- wfDebug("Hook LinkerMakeExternalImage changed the output of external image with url {$url} and alt text {$alt} to {$img}", true);
+ wfDebug("Hook LinkerMakeExternalImage changed the output of external image with url {$url} and alt text {$alt} to {$img}\n", true);
return $img;
}
return Xml::element( 'img',
* bottom, text-bottom)
* alt Alternate text for image (i.e. alt attribute). Plain text.
* caption HTML for image caption.
+ * link-url URL to link to
+ * link-title Title object to link to
+ * no-link Boolean, suppress description link
*
* @param array $handlerParams Associative array of media handler parameters, to be passed
* to transform(). Typical keys are "width" and "page".
$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'];
$prefix = $postfix = '';
- if ( 'center' == $fp['align'] )
- {
+ if ( 'center' == $fp['align'] ) {
$prefix = '<div class="center">';
$postfix = '</div>';
$fp['align'] = 'none';
}
if ( isset( $fp['thumbnail'] ) || isset( $fp['manualthumb'] ) || isset( $fp['framed'] ) ) {
-
# Create a thumbnail. Alignment depends on language
# writing direction, # right aligned for left-to-right-
# languages ("Western languages"), left-aligned
if ( !$thumb ) {
$s = $this->makeBrokenImageLinkObj( $title, '', '', '', '', $time==true );
} else {
- $s = $thumb->toHtml( array(
- 'desc-link' => true,
- 'desc-query' => $query,
+ $params = array(
'alt' => $fp['alt'],
+ 'title' => $fp['title'],
'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false ,
- 'img-class' => isset( $fp['border'] ) ? 'thumbborder' : false ) );
+ 'img-class' => isset( $fp['border'] ) ? 'thumbborder' : false );
+ if ( !empty( $fp['link-url'] ) ) {
+ $params['custom-url-link'] = $fp['link-url'];
+ } elseif ( !empty( $fp['link-title'] ) ) {
+ $params['custom-title-link'] = $fp['link-title'];
+ } elseif ( !empty( $fp['no-link'] ) ) {
+ // No link
+ } else {
+ $params['desc-link'] = true;
+ $params['desc-query'] = $query;
+ }
+
+ $s = $thumb->toHtml( $params );
}
if ( '' != $fp['align'] ) {
- $s = "<div class=\"float{$fp['align']}\"><span>{$s}</span></div>";
+ $s = "<div class=\"float{$fp['align']}\">{$s}</div>";
}
return str_replace("\n", ' ',$prefix.$s.$postfix);
}
$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['caption'] ) ) $fp['caption'] = '';
if ( empty( $hp['width'] ) ) {
} else {
if ( isset( $fp['manualthumb'] ) ) {
# Use manually specified thumbnail
- $manual_title = Title::makeTitleSafe( NS_IMAGE, $fp['manualthumb'] );
+ $manual_title = Title::makeTitleSafe( NS_FILE, $fp['manualthumb'] );
if( $manual_title ) {
$manual_img = wfFindFile( $manual_title );
if ( $manual_img ) {
}
}
- if( $page ) {
- $query = $query ? '&page=' . urlencode( $page ) : 'page=' . urlencode( $page );
- }
+ # ThumbnailImage::toHtml() already adds page= onto the end of DjVu URLs
+ # So we don't need to pass it here in $query. However, the URL for the
+ # zoom icon still needs it, so we make a unique query for it. See bug 14771
$url = $title->getLocalURL( $query );
+ if( $page ) {
+ $url = wfAppendQuery( $url, 'page=' . urlencode( $page ) );
+ }
$more = htmlspecialchars( wfMsg( 'thumbnail-more' ) );
} else {
$s .= $thumb->toHtml( array(
'alt' => $fp['alt'],
+ 'title' => $fp['title'],
'img-class' => 'thumbimage',
'desc-link' => true,
'desc-query' => $query ) );
/** @deprecated use Linker::makeMediaLinkObj() */
function makeMediaLink( $name, $unused = '', $text = '', $time = false ) {
- $nt = Title::makeTitleSafe( NS_IMAGE, $name );
+ $nt = Title::makeTitleSafe( NS_FILE, $name );
return $this->makeMediaLinkObj( $nt, $text, $time );
}
wfMsg( $key ) );
}
- /** @todo document */
- function makeExternalLink( $url, $text, $escape = true, $linktype = '', $ns = null ) {
- $style = $this->getExternalLinkAttributes( $url, $text, 'external ' . $linktype );
- global $wgNoFollowLinks, $wgNoFollowNsExceptions;
- if( $wgNoFollowLinks && !(isset($ns) && in_array($ns, $wgNoFollowNsExceptions)) ) {
- $style .= ' rel="nofollow"';
- }
+ /**
+ * Make an external link
+ * @param String $url URL to link to
+ * @param String $text text of link
+ * @param boolean $escape Do we escape the link text?
+ * @param String $linktype Type of external link. Gets added to the classes
+ * @param array $attribs Array of extra attributes to <a>
+ *
+ * @TODO! @FIXME! This is a really crappy implementation. $linktype and
+ * 'external' are mashed into the class attrib for the link (which is made
+ * into a string). Then, if we've got additional params in $attribs, we
+ * add to it. People using this might want to change the classes (or other
+ * default link attributes), but passing $attribsText is just messy. Would
+ * make a lot more sense to make put the classes into $attribs, let the
+ * hook play with them, *then* expand it all at once.
+ */
+ function makeExternalLink( $url, $text, $escape = true, $linktype = '', $attribs = array() ) {
+ $attribsText = $this->getExternalLinkAttributes( $url, $text, 'external ' . $linktype );
$url = htmlspecialchars( $url );
if( $escape ) {
$text = htmlspecialchars( $text );
}
$link = '';
- $success = wfRunHooks('LinkerMakeExternalLink', array( &$url, &$text, &$link ) );
+ $success = wfRunHooks('LinkerMakeExternalLink', array( &$url, &$text, &$link, &$attribs, $linktype ) );
if(!$success) {
- wfDebug("Hook LinkerMakeExternalLink changed the output of link with url {$url} and text {$text} to {$link}", true);
+ wfDebug("Hook LinkerMakeExternalLink changed the output of link with url {$url} and text {$text} to {$link}\n", true);
return $link;
}
- return '<a href="'.$url.'"'.$style.'>'.$text.'</a>';
+ if ( $attribs ) {
+ $attribsText .= Xml::expandAttributes( $attribs );
+ }
+ return '<a href="'.$url.'"'.$attribsText.'>'.$text.'</a>';
}
/**
* @private
*/
function userLink( $userId, $userText ) {
- $encName = htmlspecialchars( $userText );
if( $userId == 0 ) {
$page = SpecialPage::getTitleFor( 'Contributions', $userText );
} else {
$page = Title::makeTitle( NS_USER, $userText );
}
- return $this->link( $page, $encName );
+ return $this->link( $page, htmlspecialchars( $userText ), array( 'class' => 'mw-userlink' ) );
}
/**
* @return string
*/
public function userToolLinks( $userId, $userText, $redContribsWhenNoEdits = false, $flags = 0, $edits=null ) {
- global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans;
+ global $wgUser, $wgDisableAnonTalk, $wgSysopUserBans, $wgLang;
$talkable = !( $wgDisableAnonTalk && 0 == $userId );
$blockable = ( $wgSysopUserBans || 0 == $userId ) && !$flags & self::TOOL_LINKS_NOBLOCK;
}
if( $items ) {
- return ' (' . implode( ' | ', $items ) . ')';
+ return ' <span class="mw-usertoollinks">(' . $wgLang->pipeList( $items ) . ')</span>';
} else {
return '';
}
if( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
$link = wfMsgHtml( 'rev-deleted-user' );
} else if( $rev->userCan( Revision::DELETED_USER ) ) {
- $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() );
+ $link = $this->userLink( $rev->getUser( Revision::FOR_THIS_USER ),
+ $rev->getUserText( Revision::FOR_THIS_USER ) );
} else {
$link = wfMsgHtml( 'rev-deleted-user' );
}
if( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
$link = wfMsgHtml( 'rev-deleted-user' );
} else if( $rev->userCan( Revision::DELETED_USER ) ) {
- $link = $this->userLink( $rev->getRawUser(), $rev->getRawUserText() ) .
- ' ' . $this->userToolLinks( $rev->getRawUser(), $rev->getRawUserText() );
+ $userId = $rev->getUser( Revision::FOR_THIS_USER );
+ $userText = $rev->getUserText( Revision::FOR_THIS_USER );
+ $link = $this->userLink( $userId, $userText ) .
+ ' ' . $this->userToolLinks( $userId, $userText );
} else {
$link = wfMsgHtml( 'rev-deleted-user' );
}
# 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->link( $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;
}
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 );
+ $block = $this->commentBlock( $rev->getComment( Revision::FOR_THIS_USER ),
+ $rev->getTitle(), $local );
} else {
$block = " <span class=\"comment\">" . wfMsgHtml( 'rev-deleted-comment' ) . "</span>";
}
. "</ul>\n</td></tr></table>"
. '<script type="' . $wgJsMimeType . '">'
. ' if (window.showTocToggle) {'
- . ' var tocShowText = "' . wfEscapeJsString( wfMsg('showtoc') ) . '";'
- . ' var tocHideText = "' . wfEscapeJsString( wfMsg('hidetoc') ) . '";'
+ . ' var tocShowText = "' . Xml::escapeJsString( wfMsg('showtoc') ) . '";'
+ . ' var tocHideText = "' . Xml::escapeJsString( wfMsg('hidetoc') ) . '";'
. ' showTocToggle();'
. ' } '
. "</script>\n";
if( !is_null( $tooltip ) ) {
$attribs['title'] = wfMsg( 'editsectionhint', $tooltip );
}
- $url = $this->link( $nt, wfMsg('editsection'),
+ $link = $this->link( $nt, wfMsg('editsection'),
$attribs,
array( 'action' => 'edit', 'section' => $section ),
array( 'noclasses', 'known' )
$attribs = " title=\"$attribs\"";
}
$result = null;
- wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $attribs, $url, &$result ) );
+ wfRunHooks( 'EditSectionLink', array( &$this, $nt, $section, $attribs, $link, &$result ) );
if( !is_null( $result ) ) {
# For reverse compatibility, add the brackets *after* the hook is
# run, and even add them to hook-provided text. (This is the main
# reason that the EditSectionLink hook is deprecated in favor of
# DoEditSectionLink: it can't change the brackets or the span.)
- $result = wfMsgHtml( 'editsection-brackets', $url );
+ $result = wfMsgHtml( 'editsection-brackets', $result );
return "<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', $url );
+ $result = wfMsgHtml( 'editsection-brackets', $link );
$result = "<span class=\"editsection\">$result</span>";
wfRunHooks( 'DoEditSectionLink', array( $this, $nt, $section, $tooltip, &$result ) );
* @param string $anchor The anchor to give the headline (the bit after the #)
* @param string $text The text of the header
* @param string $link HTML to add for the section edit link
+ * @param mixed $legacyAnchor A second, optional anchor to give for
+ * backward compatibility (false to omit)
*
* @return string HTML headline
*/
- public function makeHeadline( $level, $attribs, $anchor, $text, $link ) {
- return "<a name=\"$anchor\"></a><h$level$attribs$link <span class=\"mw-headline\">$text</span></h$level>";
+ public function makeHeadline( $level, $attribs, $anchor, $text, $link, $legacyAnchor = false ) {
+ $ret = "<a name=\"$anchor\" id=\"$anchor\"></a>"
+ . "<h$level$attribs"
+ . $link
+ . " <span class=\"mw-headline\">$text</span>"
+ . "</h$level>";
+ if ( $legacyAnchor !== false ) {
+ $ret = "<a name=\"$legacyAnchor\" id=\"$legacyAnchor\"></a>$ret";
+ }
+ return $ret;
}
/**
public function buildRollbackLink( $rev ) {
global $wgRequest, $wgUser;
$title = $rev->getTitle();
- $query = array( 'action' => 'rollback' );
+ $query = array(
+ 'action' => 'rollback',
+ 'from' => $rev->getUserText()
+ );
if( $wgRequest->getBool( 'bot' ) ) {
$query['bot'] = '1';
+ $query['hidediff'] = '1'; // bug 15999
}
$query['token'] = $wgUser->editToken( array( $title->getPrefixedText(),
$rev->getUserText() ) );
- return $this->link( $title, wfMsgHtml( 'rollbacklink' ), array(),
- $query, 'known' );
+ return $this->link( $title, wfMsgHtml( 'rollbacklink' ),
+ array( 'title' => wfMsg( 'tooltip-rollback' ) ),
+ $query, array( 'known', 'noclasses' ) );
}
/**
* @param bool $section Whether this is for a section edit
* @return string HTML output
*/
- public function formatTemplates( $templates, $preview = false, $section = false) {
- global $wgUser;
+ public function formatTemplates( $templates, $preview = false, $section = false ) {
wfProfileIn( __METHOD__ );
- $sk = $wgUser->getSkin();
-
$outText = '';
if ( count( $templates ) > 0 ) {
# Do a batch existence check
} else {
$outText .= wfMsgExt( 'templatesused', array( 'parse' ) );
}
- $outText .= '</div><ul>';
+ $outText .= "</div><ul>\n";
usort( $templates, array( 'Title', 'compare' ) );
foreach ( $templates as $titleObj ) {
} else {
$protected = '';
}
- $outText .= '<li>' . $sk->link( $titleObj ) . ' ' . $protected . '</li>';
+ if( $titleObj->quickUserCan( 'edit' ) ) {
+ $editLink = $this->makeLinkObj( $titleObj, wfMsg('editlink'), 'action=edit' );
+ } else {
+ $editLink = $this->makeLinkObj( $titleObj, wfMsg('viewsourcelink'), 'action=edit' );
+ }
+ $outText .= '<li>' . $this->link( $titleObj ) . ' (' . $editLink . ') ' . $protected . '</li>';
}
$outText .= '</ul>';
}
* or similar
* @return string HTML output
*/
- public function formatHiddenCategories( $hiddencats) {
- global $wgUser, $wgLang;
+ public function formatHiddenCategories( $hiddencats ) {
+ global $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>';
+ $outText .= "</div><ul>\n";
foreach ( $hiddencats as $titleObj ) {
- $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 .= '<li>' . $this->link( $titleObj, null, array(), array(), 'known' ) . "</li>\n"; # If it's hidden, it must exist - no need to check with a LinkBatch
}
$outText .= '</ul>';
}
}
/**
- * Given the id of an interface element, constructs the appropriate title
- * and accesskey attributes from the system messages. (Note, this is usu-
- * ally the id but 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.
- * @return string title and accesskey attributes, ready to drop in an
- * element (e.g., ' title="This does something [x]" accesskey="x"').
+ * @deprecated Returns raw bits of HTML, use titleAttrib() and accesskey()
*/
- public function tooltipAndAccesskey($name) {
- $fname="Linker::tooltipAndAccesskey";
- wfProfileIn($fname);
- $out = '';
-
- $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);
+ public function tooltipAndAccesskey( $name ) {
+ # FIXME: If Sanitizer::expandAttributes() treated "false" as "output
+ # no attribute" instead of "output '' as value for attribute", this
+ # would be three lines.
+ $attribs = array(
+ 'title' => $this->titleAttrib( $name, 'withaccess' ),
+ 'accesskey' => $this->accesskey( $name )
+ );
+ if ( $attribs['title'] === false ) {
+ unset( $attribs['title'] );
}
- $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 .= '"';
+ if ( $attribs['accesskey'] === false ) {
+ unset( $attribs['accesskey'] );
}
- wfProfileOut($fname);
- return $out;
+ return Xml::expandAttributes( $attribs );
+ }
+
+ /** @deprecated Returns raw bits of HTML, use titleAttrib() */
+ public function tooltip( $name, $options = null ) {
+ # FIXME: If Sanitizer::expandAttributes() treated "false" as "output
+ # no attribute" instead of "output '' as value for attribute", this
+ # would be two lines.
+ $tooltip = $this->titleAttrib( $name, $options );
+ if ( $tooltip === false ) {
+ return '';
+ }
+ return Xml::expandAttributes( array(
+ 'title' => $this->titleAttrib( $name, $options )
+ ) );
}
/**
* 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.
- * @return string title attribute, ready to drop in an element
- * (e.g., ' title="This does something"').
+ * @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 Contents of the title attribute (which you must HTML-
+ * escape), or false for no title attribute
+ */
+ public function titleAttrib( $name, $options = null ) {
+ wfProfileIn( __METHOD__ );
+
+ $tooltip = wfMsg( "tooltip-$name" );
+ # Compatibility: formerly some tooltips had [alt-.] hardcoded
+ $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip );
+
+ # Message equal to '-' means suppress it.
+ if ( wfEmptyMsg( "tooltip-$name", $tooltip ) || $tooltip == '-' ) {
+ $tooltip = false;
+ }
+
+ if ( $options == 'withaccess' ) {
+ $accesskey = $this->accesskey( $name );
+ if( $accesskey !== false ) {
+ if ( $tooltip === false || $tooltip === '' ) {
+ $tooltip = "[$accesskey]";
+ } else {
+ $tooltip .= " [$accesskey]";
+ }
+ }
+ }
+
+ wfProfileOut( __METHOD__ );
+ return $tooltip;
+ }
+
+ /**
+ * Given the id of an interface element, constructs the appropriate
+ * accesskey attribute from the system messages. (Note, this is usually
+ * the id but 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.
+ * @return string Contents of the accesskey attribute (which you must HTML-
+ * escape), or false for no accesskey attribute
*/
- public function tooltip($name) {
- $out = '';
+ public function accesskey( $name ) {
+ wfProfileIn( __METHOD__ );
+
+ $accesskey = wfMsg( "accesskey-$name" );
- $tooltip = wfMsg('tooltip-'.$name);
- if (!wfEmptyMsg('tooltip-'.$name, $tooltip) && $tooltip != '-') {
- $out = ' title="'.htmlspecialchars($tooltip).'"';
+ # FIXME: Per standard MW behavior, a value of '-' means to suppress the
+ # attribute, but this is broken for accesskey: that might be a useful
+ # value.
+ if( $accesskey != '' && $accesskey != '-' && !wfEmptyMsg( "accesskey-$name", $accesskey ) ) {
+ wfProfileOut( __METHOD__ );
+ return $accesskey;
}
- return $out;
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ /**
+ * Creates a (show/hide) link for deleting revisions/log entries
+ *
+ * @param array $query Query parameters to be passed to link()
+ * @param bool $restricted Set to true to use a <strong> instead of a <span>
+ *
+ * @return string HTML <a> link to Special:Revisiondelete, wrapped in a
+ * span to allow for customization of appearance with CSS
+ */
+ public function revDeleteLink( $query = array(), $restricted = false ) {
+ $sp = SpecialPage::getTitleFor( 'Revisiondelete' );
+ $text = wfMsgHtml( 'rev-delundel' );
+ $tag = $restricted ? 'strong' : 'span';
+ $link = $this->link( $sp, $text, array(), $query, array( 'known', 'noclasses' ) );
+ return Xml::tags( $tag, array( 'class' => 'mw-revdelundel-link' ), "($link)" );
}
}