From: Daniel Kinzler Date: Fri, 18 Jun 2010 19:17:17 +0000 (+0000) Subject: let mode="grid" on a wiki table trigger output as div soup instead of a html table... X-Git-Tag: 1.31.0-rc.0~36474 X-Git-Url: https://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/banques/%7B%7B%20url_for%28%27admin_users%27%29%20%7D%7D?a=commitdiff_plain;h=4760201ac24c9cbcfd3263f212d27f2d4042eae5;p=lhc%2Fweb%2Fwiklou.git let mode="grid" on a wiki table trigger output as div soup instead of a html table. this should get rid of "layout tables" that confuse screen readers etc. this is very experimental - feel free to revert, i just want to have this diff in the repository, to play with... --- diff --git a/includes/Sanitizer.php b/includes/Sanitizer.php index b422c4e4bb..00837fdadf 100644 --- a/includes/Sanitizer.php +++ b/includes/Sanitizer.php @@ -793,6 +793,51 @@ class Sanitizer { } } + /** + * Take an associative array of attribute name/value pairs + * and generate a css style representing all the style-related + * attributes. If there already a style attribute in the array, + * it is also included in the value returned. + */ + static function styleFromAttributes( $attributes ) { + $styles = array(); + + foreach ( $attributes as $attribute => $value ) { + if ( $attribute == 'bgcolor' ) { + $styles[] = "background-color: $value"; + } else if ( $attribute == 'border' ) { + $styles[] = "border-width: $value"; + } else if ( $attribute == 'align' ) { + $styles[] = "text-align: $value"; + } else if ( $attribute == 'valign' ) { + $styles[] = "vertical-align: $value"; + } else if ( $attribute == 'width' ) { + if ( preg_match( '/\d+/', $value ) === false ) { + $value .= 'px'; + } + + $styles[] = "width: $value"; + } else if ( $attribute == 'height' ) { + if ( preg_match( '/\d+/', $value ) === false ) { + $value .= 'px'; + } + + $styles[] = "height: $value"; + } else if ( $attribute == 'nowrap' ) { + if ( $value ) { + $styles[] = "white-space: nowrap"; + } + } + } + + if ( isset( $attributes[ 'style' ] ) ) { + $styles[] = $attributes[ 'style' ]; + } + + if ( !$styles ) return ''; + else return implode( '; ', $styles ); + } + /** * Take a tag soup fragment listing an HTML element's attributes * and normalize it to well-formed XML, discarding unwanted attributes. @@ -810,24 +855,66 @@ class Sanitizer { * * @param $text String * @param $element String + * @param $defaults Array (optional) associative array of default attributes to splice in. + * class and style attributes are combined. Otherwise, values from + * $attributes take precedence over values from $defaults. * @return String */ - static function fixTagAttributes( $text, $element ) { + static function fixTagAttributes( $text, $element, $defaults = null ) { if( trim( $text ) == '' ) { return ''; } - $stripped = Sanitizer::validateTagAttributes( - Sanitizer::decodeTagAttributes( $text ), $element ); + $decoded = Sanitizer::decodeTagAttributes( $text ); + $stripped = Sanitizer::validateTagAttributes( $decoded, $element ); + $attribs = Sanitizer::collapseTagAttributes( $stripped, $defaults ); - $attribs = array(); - foreach( $stripped as $attribute => $value ) { + return $attribs; + } + + /** + * Take an associative array or attribute name/value pairs + * and collapses it to well-formed XML. + * Does not filter attributes. + * Output is safe for further wikitext processing, with escaping of + * values that could trigger problems. + * + * - Double-quotes all attribute values + * - Prepends space if there are attributes. + * + * @param $attributes Array is an associative array of attribute name/value pairs. + * Assumed to be sanitized already. + * @param $defaults Array (optional) associative array of default attributes to splice in. + * class and style attributes are combined. Otherwise, values from + * $attributes take precedence over values from $defaults. + * @return String + */ + static function collapseTagAttributes( $attributes, $defaults = null ) { + if ( $defaults ) { + foreach( $defaults as $attribute => $value ) { + if ( isset( $attributes[ $attribute ] ) ) { + if ( $attribute == 'class' ) { + $value .= ' '. $attributes[ $attribute ]; + } else if ( $attribute == 'style' ) { + $value .= '; ' . $attributes[ $attribute ]; + } else { + continue; + } + } + + $attributes[ $attribute ] = $value; + } + } + + $chunks = array(); + + foreach( $attributes as $attribute => $value ) { $encAttribute = htmlspecialchars( $attribute ); $encValue = Sanitizer::safeEncodeAttribute( $value ); - $attribs[] = "$encAttribute=\"$encValue\""; + $chunks[] = "$encAttribute=\"$encValue\""; } - return count( $attribs ) ? ' ' . implode( ' ', $attribs ) : ''; + return count( $chunks ) ? ' ' . implode( ' ', $chunks ) : ''; } /** diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php index 00eaa6e5d4..4605baa8cc 100644 --- a/includes/parser/Parser.php +++ b/includes/parser/Parser.php @@ -795,6 +795,18 @@ class Parser { $has_opened_tr = array(); # Did this table open a element? $indent_level = 0; # indent level of the table + $table_tag = 'table'; + $tr_tag = 'tr'; + $th_tag = 'th'; + $td_tag = 'td'; + $caption_tag = 'caption'; + + $extra_table_attribs = null; + $extra_tr_attribs = null; + $extra_td_attribs = null; + + $convert_style = false; + foreach ( $lines as $outLine ) { $line = trim( $outLine ); @@ -811,9 +823,31 @@ class Parser { $indent_level = strlen( $matches[1] ); $attributes = $this->mStripState->unstripBoth( $matches[2] ); - $attributes = Sanitizer::fixTagAttributes( $attributes , 'table' ); - $outLine = str_repeat( '
' , $indent_level ) . ""; + $attr = Sanitizer::decodeTagAttributes( $attributes ); + + $mode = @$attr['mode']; + if ( !$mode ) $mode = 'data'; + + if ( $mode == 'grid' || $mode == 'layout' ) { + $table_tag = 'div'; + $tr_tag = 'div'; + $th_tag = 'div'; + $td_tag = 'div'; + $caption_tag = 'div'; + + $extra_table_attribs = array( 'class' => 'grid-table', 'style' => 'display:table;' ); + $extra_tr_attribs = array( 'class' => 'grid-row', 'style' => 'display:table-row;' ); + $extra_td_attribs = array( 'class' => 'grid-cell', 'style' => 'display:table-cell;' ); + + $convert_style = true; + } + + if ($convert_style) $attr['style'] = Sanitizer::styleFromAttributes( $attr ); + $attr = Sanitizer::validateTagAttributes( $attr, $table_tag ); + $attributes = Sanitizer::collapseTagAttributes( $attr, $extra_table_attribs ); + + $outLine = str_repeat( '
' , $indent_level ) . "<$table_tag{$attributes}>"; array_push( $td_history , false ); array_push( $last_tag_history , '' ); array_push( $tr_history , false ); @@ -825,15 +859,15 @@ class Parser { continue; } elseif ( substr( $line , 0 , 2 ) === '|}' ) { # We are ending a table - $line = '' . substr( $line , 2 ); + $line = "" . substr( $line , 2 ); $last_tag = array_pop( $last_tag_history ); if ( !array_pop( $has_opened_tr ) ) { - $line = "{$line}"; + $line = "<$tr_tag><$td_tag>{$line}"; } if ( array_pop( $tr_history ) ) { - $line = "{$line}"; + $line = "{$line}"; } if ( array_pop( $td_history ) ) { @@ -847,7 +881,12 @@ class Parser { # Whats after the tag is now only attributes $attributes = $this->mStripState->unstripBoth( $line ); - $attributes = Sanitizer::fixTagAttributes( $attributes, 'tr' ); + + $attr = Sanitizer::decodeTagAttributes( $attributes ); + if ($convert_style) $attr['style'] = Sanitizer::styleFromAttributes( $attr ); + $attr = Sanitizer::validateTagAttributes( $attr, $tr_tag ); + $attributes = Sanitizer::collapseTagAttributes( $attr, $extra_tr_attribs ); + array_pop( $tr_attributes ); array_push( $tr_attributes, $attributes ); @@ -857,7 +896,7 @@ class Parser { array_push( $has_opened_tr , true ); if ( array_pop( $tr_history ) ) { - $line = ''; + $line = ""; } if ( array_pop( $td_history ) ) { @@ -895,7 +934,7 @@ class Parser { if ( $first_character !== '+' ) { $tr_after = array_pop( $tr_attributes ); if ( !array_pop( $tr_history ) ) { - $previous = "\n"; + $previous = "<$tr_tag{$tr_after}>\n"; } array_push( $tr_history , true ); array_push( $tr_attributes , '' ); @@ -910,11 +949,11 @@ class Parser { } if ( $first_character === '|' ) { - $last_tag = 'td'; + $last_tag = $td_tag; } elseif ( $first_character === '!' ) { - $last_tag = 'th'; + $last_tag = $th_tag; } elseif ( $first_character === '+' ) { - $last_tag = 'caption'; + $last_tag = $caption_tag; } else { $last_tag = ''; } @@ -924,15 +963,24 @@ class Parser { # A cell could contain both parameters and data $cell_data = explode( '|' , $cell , 2 ); + $attributes = ''; + # Bug 553: Note that a '|' inside an invalid link should not # be mistaken as delimiting cell parameters if ( strpos( $cell_data[0], '[[' ) !== false ) { - $cell = "{$previous}<{$last_tag}>{$cell}"; + if ($extra_td_attribs) $attributes = Sanitizer::collapseTagAttributes( $extra_td_attribs ); + $cell = "{$previous}<{$last_tag}{$attributes}>{$cell}"; } elseif ( count( $cell_data ) == 1 ) { - $cell = "{$previous}<{$last_tag}>{$cell_data[0]}"; + if ($extra_td_attribs) $attributes = Sanitizer::collapseTagAttributes( $extra_td_attribs ); + $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[0]}"; } else { $attributes = $this->mStripState->unstripBoth( $cell_data[0] ); - $attributes = Sanitizer::fixTagAttributes( $attributes , $last_tag ); + + $attr = Sanitizer::decodeTagAttributes( $attributes ); + if ($convert_style) $attr['style'] = Sanitizer::styleFromAttributes( $attr ); + $attr = Sanitizer::validateTagAttributes( $attr, $last_tag ); + $attributes = Sanitizer::collapseTagAttributes( $attr, $extra_td_attribs ); + $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}"; } @@ -946,16 +994,16 @@ class Parser { # Closing open td, tr && table while ( count( $td_history ) > 0 ) { if ( array_pop( $td_history ) ) { - $out .= "\n"; + $out .= "\n"; } if ( array_pop( $tr_history ) ) { - $out .= "\n"; + $out .= "\n"; } if ( !array_pop( $has_opened_tr ) ) { - $out .= "\n" ; + $out .= "<$tr_tag><$td_tag>\n" ; } - $out .= "\n"; + $out .= "\n"; } # Remove trailing line-ending (b/c) @@ -964,7 +1012,7 @@ class Parser { } # special case: don't return empty table - if ( $out === "\n\n
" ) { + if ( $out === "<$table_tag>\n<$tr_tag><$td_tag>\n" ) { $out = ''; }