* hook play with them, *then* expand it all at once.
*/
function makeExternalLink( $url, $text, $escape = true, $linktype = '', $attribs = array() ) {
- $attribsText = $this->getExternalLinkAttributes( 'external ' . $linktype );
+ if ( isset( $attribs[ 'class' ] ) ) $class = $attribs[ 'class' ]; # yet another hack :(
+ else $class = 'external ' . $linktype;
+
+ $attribsText = $this->getExternalLinkAttributes( $class );
$url = htmlspecialchars( $url );
if( $escape ) {
$text = htmlspecialchars( $text );
*/
static function validateAttributes( $attribs, $whitelist ) {
$whitelist = array_flip( $whitelist );
+ $hrefExp = '/^(' . wfUrlProtocols() . ')[^\s]+$/';
+
$out = array();
foreach( $attribs as $attribute => $value ) {
if( !isset( $whitelist[$attribute] ) ) {
}
}
+ # NOTE: even though elements using href/src are not allowed directly, supply
+ # validation code that can be used by tag hook handlers, etc
+ if ( $attribute === 'href' || $attribute === 'src' ) {
+ if ( !preg_match( $hrefExp, $value ) ) {
+ continue; //drop any href or src attributes not using an allowed protocol.
+ //NOTE: this also drops all relative URLs
+ }
+ }
+
// If this attribute was previously set, override it.
// Output should only have one attribute of each name.
$out[$attribute] = $value;
'td' => array_merge( $common, $tablecell, $tablealign ),
'th' => array_merge( $common, $tablecell, $tablealign ),
+ # 12.2 # NOTE: <a> is not allowed directly, but the attrib whitelist is used from the Parser object
+ 'a' => array_merge( $common, array( 'href', 'rel', 'rev' ) ), # rel/rev esp. for RDFa
+
# 13.2
# Not usually allowed, but may be used for extension-style hooks
# such as <math> when it is rasterized
$this->mFunctionHooks = array();
$this->mFunctionTagHooks = array();
$this->mFunctionSynonyms = array( 0 => array(), 1 => array() );
- $this->mDefaultStripList = $this->mStripList = array( 'nowiki', 'gallery' );
+ $this->mDefaultStripList = $this->mStripList = array( 'nowiki', 'gallery', 'a' );
$this->mUrlProtocols = wfUrlProtocols();
$this->mExtLinkBracketedRegex = '/\[(\b(' . wfUrlProtocols() . ')'.
'[^][<>"\\x00-\\x20\\x7F]+) *([^\]\\x0a\\x0d]*?)\]/S';
case 'gallery':
$output = $this->renderImageGallery( $content, $attributes );
break;
+ case 'a':
+ $output = $this->renderHyperlink( $content, $attributes, $frame );
+ break;
case 'math':
if ( $this->mOptions->getUseTeX() ) {
$output = $wgContLang->armourMath(
'</pre>';
}
+ /**
+ * Tag hook handler for 'a'. Renders a HTML <a> tag, allowing most attributes, filtering href against
+ * allowed protocols and spam blacklist.
+ **/
+ function renderHyperlink( $text, $params, $frame = false ) {
+ foreach ( $params as $name => $value ) {
+ $params[ $name ] = $this->replaceVariables( $value, $frame );
+ }
+
+ $whitelist = Sanitizer::attributeWhitelist( 'a' );
+ $params = Sanitizer::validateAttributes( $params, $whitelist );
+
+ $content = $this->recursiveTagParse( trim( $text ), $frame );
+
+ if ( isset( $params[ 'href' ] ) ) {
+ $href = $params[ 'href' ];
+ $this->mOutput->addExternalLink( $href );
+ unset( $params[ 'href' ] );
+ } else {
+ # Non-link <a> tag
+ return Xml::openElement( 'a', $params ) . $content . Xml::closeElement( 'a' );
+ }
+
+ $sk = $this->mOptions->getSkin();
+ $html = $sk->makeExternalLink( $href, $content, false, '', $params );
+
+ return $html;
+ }
+
/**
* Renders an image gallery from a text with one line per image.
* text labels may be given by using |-style alternative text. E.g.