X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=includes%2FHtml.php;h=48cef6f6dd8a2238a64bf47fac111432aca0e0c0;hb=6d95f7f860ea897761d0ca4ba70f753ccaf8b69f;hp=ccd17876203a0b8ad624ccae185ea2feef343fcd;hpb=e347b2bab58e45c9b5ecd654eeb65c24558328c7;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Html.php b/includes/Html.php index ccd1787620..48cef6f6dd 100644 --- a/includes/Html.php +++ b/includes/Html.php @@ -1,21 +1,27 @@ 'http://www.mediawiki.org/' ). See expandAttributes() for + * @param $element string The element's name, e.g., 'a' + * @param $attribs array Associative array of attributes, e.g., array( + * 'href' => 'http://www.mediawiki.org/' ). See expandAttributes() for * further documentation. * @param $contents string The raw HTML contents of the element: *not* * escaped! @@ -125,6 +138,12 @@ class Html { /** * Identical to rawElement(), but HTML-escapes $contents (like * Xml::element()). + * + * @param $element string + * @param $attribs array + * @param $contents string + * + * @return string */ public static function element( $element, $attribs = array(), $contents = '' ) { return self::rawElement( $element, $attribs, strtr( $contents, array( @@ -138,6 +157,11 @@ class Html { /** * Identical to rawElement(), but has no third parameter and omits the end * tag (and the self-closing '/' in XML mode for empty elements). + * + * @param $element string + * @param $attribs array + * + * @return string */ public static function openElement( $element, $attribs = array() ) { global $wgHtml5, $wgWellFormedXml; @@ -154,49 +178,41 @@ class Html { return ''; } - # Remove HTML5-only attributes if we aren't doing HTML5 - if ( !$wgHtml5 ) { - if ( $element == 'input' ) { - # Whitelist of valid XHTML1 types - $validTypes = array( - 'hidden', - 'text', - 'password', - 'checkbox', - 'radio', - 'file', - 'submit', - 'image', - 'reset', - 'button', - ); - if ( isset( $attribs['type'] ) - && !in_array( $attribs['type'], $validTypes ) ) { - # Fall back to type=text, the default - unset( $attribs['type'] ); - } - } - if ( $element == 'textarea' && isset( $attribs['maxlength'] ) ) { - unset( $attribs['maxlength'] ); - } - # Here we're blacklisting some HTML5-only attributes... - $html5attribs = array( - 'autocomplete', - 'autofocus', - 'max', - 'min', - 'multiple', - 'pattern', - 'placeholder', - 'required', - 'step', - 'spellcheck', + # Remove HTML5-only attributes if we aren't doing HTML5, and disable + # form validation regardless (see bug 23769 and the more detailed + # comment in expandAttributes()) + if ( $element == 'input' ) { + # Whitelist of types that don't cause validation. All except + # 'search' are valid in XHTML1. + $validTypes = array( + 'hidden', + 'text', + 'password', + 'checkbox', + 'radio', + 'file', + 'submit', + 'image', + 'reset', + 'button', + 'search', ); - foreach ( $html5attribs as $badAttr ) { - unset( $attribs[$badAttr] ); + + if ( isset( $attribs['type'] ) + && !in_array( $attribs['type'], $validTypes ) ) { + unset( $attribs['type'] ); + } + + if ( isset( $attribs['type'] ) && $attribs['type'] == 'search' + && !$wgHtml5 ) { + unset( $attribs['type'] ); } } + if ( !$wgHtml5 && $element == 'textarea' && isset( $attribs['maxlength'] ) ) { + unset( $attribs['maxlength'] ); + } + return "<$element" . self::expandAttributes( self::dropDefaults( $element, $attribs ) ) . '>'; } @@ -205,6 +221,7 @@ class Html { * Returns "", except if $wgWellFormedXml is off, in which case * it returns the empty string when that's guaranteed to be safe. * + * @since 1.17 * @param $element string Name of the element, e.g., 'a' * @return string A closing tag, if required */ @@ -341,6 +358,26 @@ class Html { * For instance, it will omit quotation marks if $wgWellFormedXml is false, * and will treat boolean attributes specially. * + * Attributes that should contain space-separated lists (such as 'class') array + * values are allowed as well, which will automagically be normalized + * and converted to a space-separated string. In addition to a numerical + * array, the attribute value may also be an associative array. See the + * example below for how that works. + * @example Numerical array + * + * Html::element( 'em', array( + * 'class' => array( 'foo', 'bar' ) + * ) ); + * // gives '' + * + * @example Associative array + * + * Html::element( 'em', array( + * 'class' => array( 'foo', 'bar', 'foo' => false, 'quux' => true ) + * ) ); + * // gives '' + * + * * @param $attribs array Associative array of attributes, e.g., array( * 'href' => 'http://www.mediawiki.org/' ). Values will be HTML-escaped. * A value of false means to omit the attribute. For boolean attributes, @@ -355,7 +392,7 @@ class Html { $ret = ''; $attribs = (array)$attribs; foreach ( $attribs as $key => $value ) { - if ( $value === false ) { + if ( $value === false || is_null( $value ) ) { continue; } @@ -370,6 +407,22 @@ class Html { # and we'd like consistency and better compression anyway. $key = strtolower( $key ); + # Here we're blacklisting some HTML5-only attributes... + if ( !$wgHtml5 && in_array( $key, array( + 'autocomplete', + 'autofocus', + 'max', + 'min', + 'multiple', + 'pattern', + 'placeholder', + 'required', + 'step', + 'spellcheck', + ) ) ) { + continue; + } + # Bug 23769: Blacklist all form validation attributes for now. Current # (June 2010) WebKit has no UI, so the form just refuses to submit # without telling the user why, which is much worse than failing @@ -380,6 +433,55 @@ class Html { continue; } + // http://www.w3.org/TR/html401/index/attributes.html ("space-separated") + // http://www.w3.org/TR/html5/index.html#attributes-1 ("space-separated") + $spaceSeparatedListAttributes = array( + 'class', // html4, html5 + 'accesskey', // as of html5, multiple space-separated values allowed + // html4-spec doesn't document rel= as space-separated + // but has been used like that and is now documented as such + // in the html5-spec. + 'rel', + ); + + # Specific features for attributes that allow a list of space-separated values + if ( in_array( $key, $spaceSeparatedListAttributes ) ) { + // Apply some normalization and remove duplicates + + // Convert into correct array. Array can contain space-seperated + // values. Implode/explode to get those into the main array as well. + if ( is_array( $value ) ) { + // If input wasn't an array, we can skip this step + + $newValue = array(); + foreach ( $value as $k => $v ) { + if ( is_string( $v ) ) { + // String values should be normal `array( 'foo' )` + // Just append them + if ( !isset( $value[$v] ) ) { + // As a special case don't set 'foo' if a + // separate 'foo' => true/false exists in the array + // keys should be authoritive + $newValue[] = $v; + } + } elseif ( $v ) { + // If the value is truthy but not a string this is likely + // an array( 'foo' => true ), falsy values don't add strings + $newValue[] = $k; + } + } + $value = implode( ' ', $newValue ); + } + $value = explode( ' ', $value ); + + // Normalize spacing by fixing up cases where people used + // more than 1 space and/or a trailing/leading space + $value = array_diff( $value, array( '', ' ' ) ); + + // Remove duplicates and create the string + $value = implode( ' ', array_unique( $value ) ); + } + # See the "Attributes" section in the HTML syntax part of HTML5, # 9.1.2.3 as of 2009-08-10. Most attributes can have quotation # marks omitted, but not all. (Although a literal " is not @@ -413,7 +515,8 @@ class Html { # Apparently we need to entity-encode \n, \r, \t, although the # spec doesn't mention that. Since we're doing strtr() anyway, # and we don't need <> escaped here, we may as well not call - # htmlspecialchars(). FIXME: verify that we actually need to + # htmlspecialchars(). + # @todo FIXME: Verify that we actually need to # escape \n\r\t here, and explain why, exactly. # # We could call Sanitizer::encodeAttribute() for this, but we @@ -428,10 +531,11 @@ class Html { ); if ( $wgWellFormedXml ) { # This is allowed per spec: - # But reportedly it breaks some XML tools? FIXME: is this - # really true? + # But reportedly it breaks some XML tools? + # @todo FIXME: Is this really true? $map['<'] = '<'; } + $ret .= " $key=$quote" . strtr( $value, $map ) . $quote; } } @@ -450,12 +554,15 @@ class Html { global $wgHtml5, $wgJsMimeType, $wgWellFormedXml; $attrs = array(); + if ( !$wgHtml5 ) { $attrs['type'] = $wgJsMimeType; } + if ( $wgWellFormedXml && preg_match( '/[<&]/', $contents ) ) { $contents = "/**/"; } + return self::rawElement( 'script', $attrs, $contents ); } @@ -470,9 +577,11 @@ class Html { global $wgHtml5, $wgJsMimeType; $attrs = array( 'src' => $url ); + if ( !$wgHtml5 ) { $attrs['type'] = $wgJsMimeType; } + return self::element( 'script', $attrs ); } @@ -491,6 +600,7 @@ class Html { if ( $wgWellFormedXml && preg_match( '/[<&]/', $contents ) ) { $contents = "/**/"; } + return self::rawElement( 'style', array( 'type' => 'text/css', 'media' => $media, @@ -535,8 +645,7 @@ class Html { } /** - * Convenience function to produce an input element with type=hidden, like - * Xml::hidden. + * Convenience function to produce an input element with type=hidden * * @param $name string name attribute * @param $value string value attribute @@ -563,13 +672,19 @@ class Html { */ public static function textarea( $name, $value = '', $attribs = array() ) { global $wgHtml5; + $attribs['name'] = $name; + if ( !$wgHtml5 ) { - if ( !isset( $attribs['cols'] ) ) + if ( !isset( $attribs['cols'] ) ) { $attribs['cols'] = ""; - if ( !isset( $attribs['rows'] ) ) + } + + if ( !isset( $attribs['rows'] ) ) { $attribs['rows'] = ""; + } } + return self::element( 'textarea', $attribs, $value ); } @@ -584,38 +699,39 @@ class Html { public static function htmlHeader( $attribs = array() ) { $ret = ''; - global $wgMimeType, $wgOutputEncoding; + global $wgMimeType; + if ( self::isXmlMimeType( $wgMimeType ) ) { - $ret .= "\n"; + $ret .= "\n"; } - global $wgHtml5, $wgHtml5Version, $wgWellFormedXml, $wgDocType, $wgDTD; + global $wgHtml5, $wgHtml5Version, $wgDocType, $wgDTD; global $wgXhtmlNamespaces, $wgXhtmlDefaultNamespace; + if ( $wgHtml5 ) { - if ( $wgWellFormedXml ) { - # Unknown elements and attributes are okay in XML, but unknown - # named entities are well-formedness errors and will break XML - # parsers. Thus we need a doctype that gives us appropriate - # entity definitions. The HTML5 spec permits four legacy - # doctypes as obsolete but conforming, so let's pick one of - # those, although it makes our pages look like XHTML1 Strict. - # Isn't compatibility great? - $ret .= "\n"; - } else { - # Much saner. - $ret .= "\n"; - } + $ret .= "\n"; + if ( $wgHtml5Version ) { $attribs['version'] = $wgHtml5Version; } } else { $ret .= "\n"; $attribs['xmlns'] = $wgXhtmlDefaultNamespace; + foreach ( $wgXhtmlNamespaces as $tag => $ns ) { $attribs["xmlns:$tag"] = $ns; } } - return $ret . Html::openElement( 'html', $attribs ) . "\n"; + + $html = Html::openElement( 'html', $attribs ); + + if ( $html ) { + $html .= "\n"; + } + + $ret .= $html; + + return $ret; } /** @@ -626,12 +742,53 @@ class Html { */ public static function isXmlMimeType( $mimetype ) { switch ( $mimetype ) { - case 'text/xml': - case 'application/xhtml+xml': - case 'application/xml': - return true; - default: - return false; + case 'text/xml': + case 'application/xhtml+xml': + case 'application/xml': + return true; + default: + return false; + } + } + + /** + * Get HTML for an info box with an icon. + * + * @param $text String: wikitext, get this with wfMsgNoTrans() + * @param $icon String: icon name, file in skins/common/images + * @param $alt String: alternate text for the icon + * @param $class String: additional class name to add to the wrapper div + * @param $useStylePath + * + * @return string + */ + static function infoBox( $text, $icon, $alt, $class = false, $useStylePath = true ) { + global $wgStylePath; + + if ( $useStylePath ) { + $icon = $wgStylePath.'/common/images/'.$icon; } + + $s = Html::openElement( 'div', array( 'class' => "mw-infobox $class") ); + + $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-left' ) ). + Html::element( 'img', + array( + 'src' => $icon, + 'alt' => $alt, + ) + ). + Html::closeElement( 'div' ); + + $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-right' ) ). + $text. + Html::closeElement( 'div' ); + $s .= Html::element( 'div', array( 'style' => 'clear: left;' ), ' ' ); + + $s .= Html::closeElement( 'div' ); + + $s .= Html::element( 'div', array( 'style' => 'clear: left;' ), ' ' ); + + return $s; } }