From: Leo Koppelkamm
Date: Tue, 12 Apr 2011 21:27:24 +0000 (+0000)
Subject: (no commit message)
X-Git-Tag: 1.31.0-rc.0~30885
X-Git-Url: http://git.cyclocoop.org/%22%20.%20generer_url_ecrire%28%22suivi_revisions%22%29%20.%20%22?a=commitdiff_plain;h=109f7bcb9de9b9f82baa5a9a7454dc7448a05aec;p=lhc%2Fweb%2Fwiklou.git
---
diff --git a/includes/Sanitizer.php b/includes/Sanitizer.php
index 0fd593f295..fa961cf974 100644
--- a/includes/Sanitizer.php
+++ b/includes/Sanitizer.php
@@ -369,7 +369,7 @@ class Sanitizer {
'strike', 'strong', 'tt', 'var', 'div', 'center',
'blockquote', 'ol', 'ul', 'dl', 'table', 'caption', 'pre',
'ruby', 'rt' , 'rb' , 'rp', 'p', 'span', 'abbr', 'dfn',
- 'kbd', 'samp'
+ 'kbd', 'samp', 'thead', 'tbody', 'tfoot'
);
$htmlsingle = array(
'br', 'hr', 'li', 'dt', 'dd'
diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php
index b14e366e8c..7045690b15 100644
--- a/includes/parser/Parser.php
+++ b/includes/parser/Parser.php
@@ -824,189 +824,283 @@ class Parser {
$lines = StringUtils::explode( "\n", $text );
$out = '';
- $td_history = array(); # Is currently a td tag open?
- $last_tag_history = array(); # Save history of last lag activated (td, th or caption)
- $tr_history = array(); # Is currently a tr tag open?
- $tr_attributes = array(); # history of tr attributes
- $has_opened_tr = array(); # Did this table open a element?
- $indent_level = 0; # indent level of the table
+ $output =& $out;
foreach ( $lines as $outLine ) {
$line = trim( $outLine );
- if ( $line === '' ) { # empty line, go to next line
+ if ( $line == '') { //empty line, go to next line
$out .= $outLine."\n";
continue;
}
-
- $first_character = $line[0];
+ $first_chars = $line[0];
+ if ( strlen($line) > 1) {
+ $first_chars .= in_array($line[1], array('}', '+', '-')) ? $line[1] : '';
+ }
$matches = array();
if ( preg_match( '/^(:*)\{\|(.*)$/', $line , $matches ) ) {
- # First check if we are starting a new table
- $indent_level = strlen( $matches[1] );
+ $tables[] = array();
+ $table =& $this->last($tables);
+ $table[0] = array(); //first row
+ $current_row =& $table[0];
+
+ $table['indent'] = strlen( $matches[1] );
$attributes = $this->mStripState->unstripBoth( $matches[2] );
$attributes = Sanitizer::fixTagAttributes( $attributes , 'table' );
- $outLine = str_repeat( '- ' , $indent_level ) . "
";
- array_push( $td_history , false );
- array_push( $last_tag_history , '' );
- array_push( $tr_history , false );
- array_push( $tr_attributes , '' );
- array_push( $has_opened_tr , false );
- } elseif ( count( $td_history ) == 0 ) {
- # Don't do any of the following
- $out .= $outLine."\n";
- continue;
- } elseif ( substr( $line , 0 , 2 ) === '|}' ) {
- # We are ending a table
- $line = '
' . substr( $line , 2 );
- $last_tag = array_pop( $last_tag_history );
-
- if ( !array_pop( $has_opened_tr ) ) {
- $line = " |
{$line}";
+ if ( $attributes !== '' ) {
+ $table['attributes'] = $attributes;
}
+ } else if ( !isset($tables[0]) ) {
+ // we're outside the table
- if ( array_pop( $tr_history ) ) {
- $line = "
{$line}";
+ $out .= $outLine."\n";
+ } else if ( $first_chars === '|}' ) {
+ // trim the |} code from the line
+ $line = substr ( $line , 2 );
+
+ // Shorthand for last row
+ $last_row =& $this->last($table);
+
+ // a thead at the end becomes a tfoot, unless there is only one row
+ // Do this before deleting empty last lines to allow headers at the bottom of tables
+ if ( isset($last_row['type'] ) && $last_row['type'] == 'thead' && isset($table[1])) {
+ $last_row['type'] = 'tfoot';
+ for($i = 0; isset($last_row[$i]); $i++ ) {
+ $last_row[$i]['type'] = 'td';
+ }
}
- if ( array_pop( $td_history ) ) {
- $line = "{$last_tag}>{$line}";
+ // Delete empty last lines
+ if ( empty($last_row) ) {
+ $last_row = NULL;
}
- array_pop( $tr_attributes );
- $outLine = $line . str_repeat( '' , $indent_level );
- } elseif ( substr( $line , 0 , 2 ) === '|-' ) {
- # Now we have a table row
- $line = preg_replace( '#^\|-+#', '', $line );
+ $o = $this->printTableHtml( array_pop($tables) ) . $line;
- # Whats after the tag is now only attributes
- $attributes = $this->mStripState->unstripBoth( $line );
- $attributes = Sanitizer::fixTagAttributes( $attributes, 'tr' );
- array_pop( $tr_attributes );
- array_push( $tr_attributes, $attributes );
+ if ( count($tables) > 0 ) {
+ $table =& $this->last($tables);
+ $current_row =& $this->last($table);
+ $current_element =& $this->last($current_row);
- $line = '';
- $last_tag = array_pop( $last_tag_history );
- array_pop( $has_opened_tr );
- array_push( $has_opened_tr , true );
-
- if ( array_pop( $tr_history ) ) {
- $line = '';
+ $output =& $current_element['content'];
+ } else {
+ $output =& $out;
}
- if ( array_pop( $td_history ) ) {
- $line = "{$last_tag}>{$line}";
- }
+ $output .= $o;
- $outLine = $line;
- array_push( $tr_history , false );
- array_push( $td_history , false );
- array_push( $last_tag_history , '' );
- } elseif ( $first_character === '|' || $first_character === '!' || substr( $line , 0 , 2 ) === '|+' ) {
- # This might be cell elements, td, th or captions
- if ( substr( $line , 0 , 2 ) === '|+' ) {
- $first_character = '+';
- $line = substr( $line , 1 );
+ } else if ( $first_chars === '|-' ) {
+ // start a new row element
+ // but only when we haven't started one already
+ if( count($current_row) != 0 ) {
+ $table[] = array();
+ $current_row =& $this->last($table);
}
-
- $line = substr( $line , 1 );
-
- if ( $first_character === '!' ) {
- $line = str_replace( '!!' , '||' , $line );
+ // Get the attributes, there's nothing else useful in $line now
+ $line = substr ( $line , 2 );
+ $attributes = $this->mStripState->unstripBoth( $line );
+ $attributes = Sanitizer::fixTagAttributes( $attributes, 'tr' );
+ if( $attributes !== '') {
+ $current_row['attributes'] = $attributes;
+ }
+
+ } else if ( $first_chars === '|+' ) {
+ // a table caption
+ $line = substr ( $line , 2 );
+
+ $c = $this->getCellAttr($line , 'caption');
+ $table['caption'] = array();
+ $table['caption']['content'] = $c[0];
+ if(isset($c[1])) $table['caption']['attributes'] = $c[1];
+ unset($c);
+
+ $output =& $table['caption'];
+ } else if ( $first_chars === '|' || $first_chars === '!' || $first_chars === '!+' ) {
+ // Which kind of cells are we dealing with
+ $this_tag = 'td';
+ $line = substr ( $line , 1 );
+
+ if ( $first_chars === '!' || $first_chars === '!+' ) {
+ $line = str_replace ( '!!' , '||' , $line );
+ $this_tag = 'th';
}
- # Split up multiple cells on the same line.
- # FIXME : This can result in improper nesting of tags processed
- # by earlier parser steps, but should avoid splitting up eg
- # attribute values containing literal "||".
+ // Split up multiple cells on the same line.
$cells = StringUtils::explodeMarkup( '||' , $line );
+ $line = ''; // save memory
- $outLine = '';
+ // decide whether thead to tbody
+ if ( !array_key_exists('type', $current_row) ) {
+ $current_row['type'] = ( $first_chars === '!' ) ? 'thead' : 'tbody' ;
+ } else if( $first_chars === '|' ) {
+ $current_row['type'] = 'tbody';
+ }
- # Loop through each table cell
+ // Loop through each table cell
foreach ( $cells as $cell ) {
- $previous = '';
- if ( $first_character !== '+' ) {
- $tr_after = array_pop( $tr_attributes );
- if ( !array_pop( $tr_history ) ) {
- $previous = "\n";
- }
- array_push( $tr_history , true );
- array_push( $tr_attributes , '' );
- array_pop( $has_opened_tr );
- array_push( $has_opened_tr , true );
- }
+ // a new cell
+ $current_row[] = array();
+ $current_element =& $this->last($current_row);
- $last_tag = array_pop( $last_tag_history );
+ $current_element['type'] = $this_tag;
- if ( array_pop( $td_history ) ) {
- $previous = "{$last_tag}>\n{$previous}";
- }
+ $c = $this->getCellAttr($cell , $this_tag);
+ $current_element['content'] = $c[0];
+ if(isset($c[1])) $current_element['attributes'] = $c[1];
+ unset($c);
+ }
+ $output =& $current_element['content'];
+
+ } else {
+ $output .= $outLine."\n";
+ }
+ }
+
+ # Remove trailing line-ending (b/c)
+ if ( substr( $out, -1 ) === "\n" ) {
+ $out = substr( $out, 0, -1 );
+ }
+
+ #Close any unclosed tables
+ if (isset($tables) && count($tables) > 0 ) {
+ for ($i = 0; $i < count($tables); $i++) {
+ $out .= $this->printTableHtml( array_pop($tables) );
+ }
+ }
+
+ wfProfileOut( __METHOD__ );
- if ( $first_character === '|' ) {
- $last_tag = 'td';
- } elseif ( $first_character === '!' ) {
- $last_tag = 'th';
- } elseif ( $first_character === '+' ) {
- $last_tag = 'caption';
- } else {
- $last_tag = '';
- }
+ return $out;
+ }
- array_push( $last_tag_history , $last_tag );
- # A cell could contain both parameters and data
- $cell_data = explode( '|' , $cell , 2 );
+ /**
+ * Helper function for doTableStuff() separating the contents of cells from
+ * attributes. Particularly useful as there's a possible bug and this action
+ * is repeated twice.
+ *
+ * @private
+ */
+ function getCellAttr ($cell , $tag_name) {
+ $content = null;
+ $attributes = null;
- # 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}";
- } elseif ( count( $cell_data ) == 1 ) {
- $cell = "{$previous}<{$last_tag}>{$cell_data[0]}";
- } else {
- $attributes = $this->mStripState->unstripBoth( $cell_data[0] );
- $attributes = Sanitizer::fixTagAttributes( $attributes , $last_tag );
- $cell = "{$previous}<{$last_tag}{$attributes}>{$cell_data[1]}";
- }
+ $cell = trim ( $cell );
- $outLine .= $cell;
- array_push( $td_history , true );
- }
- }
- $out .= $outLine . "\n";
+ // A cell could contain both parameters and data
+ $cell_data = explode ( '|' , $cell , 2 );
+
+ // Bug 553: Note that a '|' inside an invalid link should not
+ // be mistaken as delimiting cell parameters
+ if ( strpos( $cell_data[0], '[[' ) !== false ) {
+ $content = trim ( $cell );
}
+ else if ( count ( $cell_data ) == 1 ) {
+ $content = trim ( $cell_data[0] );
+ }
+ else {
+ $attributes = $this->mStripState->unstripBoth( $cell_data[0] );
+ $attributes = Sanitizer::fixTagAttributes( $attributes , $tag_name );
- # Closing open td, tr && table
- while ( count( $td_history ) > 0 ) {
- if ( array_pop( $td_history ) ) {
- $out .= "\n";
- }
- if ( array_pop( $tr_history ) ) {
- $out .= "
\n";
+ $content = trim ( $cell_data[1] );
+ }
+ return array($content, $attributes);
+ }
+
+
+ /**
+ * Helper function for doTableStuff(). This converts the structured array into html.
+ *
+ * @private
+ */
+ function printTableHtml (&$t) {
+ $r = "\n";
+ $r .= str_repeat( '- ' , $t['indent'] );
+ $r .= '\n";
- }
+ $r .= "\n
\n
|
\n" ) {
- $out = '';
+ $r .= '' . $t[$i][$j]['type'] . '>';
+ unset($t[$i][$j]);
+ }
+ $r .= "\n";
+
+ if( ( !isset($t[$i+1]) && !$simple )|| ( isset($t[$i+1]) && ($t[$i]['type'] != $t[$i+1]['type'])) ) {
+ $r .= '' . $t[$i]['type'] . '>';
+ }
+ $last_section = $t[$i]['type'];
+ unset($t[$i]);
+ }
+ if ( $empty ) {
+ if ( isset($t['caption']) ) {
+ $r .= "\n |
";
+ } else {
+ return '';
+ }
}
+ $r .= "\n";
+ $r .= str_repeat( '
' , $t['indent'] );
- wfProfileOut( __METHOD__ );
+ return $r;
+ }
- return $out;
+ /**
+ * like end() but only works on the numeric array index and php's internal pointers
+ * returns a reference to the last element of an array much like "\$arr[-1]" in perl
+ * ignores associative elements and will create a 0 key will a NULL value if there were
+ * no numric elements and an array itself if not previously defined.
+ *
+ * @private
+ */
+ function &last (&$arr) {
+ for($i = count($arr); (!isset($arr[$i]) && $i > 0); $i--) { }
+ return $arr[$i];
}
/**
@@ -2239,7 +2333,7 @@ class Parser {
'mUniqPrefix.'-pre|<\\/li|<\\/ul|<\\/ol|<\\/?center)/iS', $t );
if ( $openmatch or $closematch ) {
$paragraphStack = false;
- #Â TODO bug 5718: paragraph closed
+ # TODO bug 5718: paragraph closed
$output .= $this->closeParagraph();
if ( $preOpenMatch and !$preCloseMatch ) {
$this->mInPre = true;
diff --git a/skins/common/wikibits.js b/skins/common/wikibits.js
index 3f9dc6752a..1db54f27fb 100644
--- a/skins/common/wikibits.js
+++ b/skins/common/wikibits.js
@@ -551,14 +551,7 @@ window.sortables_init = function() {
};
window.ts_makeSortable = function( table ) {
- var firstRow;
- if ( table.rows && table.rows.length > 0 ) {
- if ( table.tHead && table.tHead.rows.length > 0 ) {
- firstRow = table.tHead.rows[table.tHead.rows.length-1];
- } else {
- firstRow = table.rows[0];
- }
- }
+ var firstRow = table.rows[0];
if ( !firstRow ) {
return;
}
diff --git a/tests/parser/parserTests.txt b/tests/parser/parserTests.txt
index bce99f6768..6e45e0aa73 100644
--- a/tests/parser/parserTests.txt
+++ b/tests/parser/parserTests.txt
@@ -1237,8 +1237,9 @@ A table with nothing but a caption
|}
!! result
+caption
+ | |
+
!! end
@@ -1253,13 +1254,14 @@ Simple table
!! result
- 1 |
- 2
- |
+1 |
+2 |
+
- 3 |
- 4
- |
+3 |
+4 |
+
+
!! end
@@ -1288,49 +1290,46 @@ Multiplication table
|}
!! result
-Multiplication table
-
+Multiplication table
+
- × |
- 1 |
- 2 |
- 3
- |
+× |
+1 |
+2 |
+3 |
+
+
- 1
- |
- 1 |
- 2 |
- 3
- |
+1 |
+1 |
+2 |
+3 |
+
- 2
- |
- 2 |
- 4 |
- 6
- |
+2 |
+2 |
+4 |
+6 |
+
- 3
- |
- 3 |
- 6 |
- 9
- |
+3 |
+3 |
+6 |
+9 |
+
- 4
- |
- 4 |
- 8 |
- 12
- |
+4 |
+4 |
+8 |
+12 |
+
- 5
- |
- 5 |
- 10 |
- 15
- |
+5 |
+5 |
+10 |
+15 |
+
+
!! end
@@ -1348,17 +1347,15 @@ Table rowspan
!! result
- Cell 1, row 1
- |
- Cell 2, row 1 (and 2)
- |
- Cell 3, row 1
- |
+Cell 1, row 1 |
+Cell 2, row 1 (and 2) |
+Cell 3, row 1 |
+
- Cell 1, row 2
- |
- Cell 3, row 2
- |
+Cell 1, row 2 |
+Cell 3, row 2 |
+
+
!! end
@@ -1378,19 +1375,19 @@ Nested table
!! result
- α
- |
+α |
-nested
- |
+nested |
+
-table
- |
- |
-the original table again
- |
+table |
+
+
+the original table again |
+
+
!! end
@@ -1403,8 +1400,9 @@ Invalid attributes in table cell (bug 1830)
!! result
+broken |
+
+
!! end
@@ -1418,8 +1416,7 @@ Table security: embedded pipes (http://lists.wikimedia.org/mailman/htdig/wikitec
[ftp://%7Cx |
-]" onmouseover="alert(document.cookie)">test
- |
+]" onmouseover="alert(document.cookie)">test |
@@ -2685,8 +2682,9 @@ BUG 553: link with two variables in a piped link
!! result
+[[{{{1}}}|{{{2}}}]] |
+
+
!! end
@@ -2795,13 +2793,14 @@ foo {{table}}
- 1 |
- 2
- |
+1 |
+2 |
+
- 3 |
- 4
- |
+3 |
+4 |
+
+
!! end
@@ -2815,13 +2814,14 @@ foo
- 1 |
- 2
- |
+1 |
+2 |
+
- 3 |
- 4
- |
+3 |
+4 |
+
+
!! end
@@ -4370,8 +4370,9 @@ Table multiple attributes correction
!! result
+status |
+
+
!!end
@@ -4815,8 +4816,9 @@ Table attribute legitimate extension
!! result
+status |
+
+
!!end
@@ -4829,8 +4831,9 @@ Table attribute safety
!! result
+status |
+
+
!! end
@@ -5452,8 +5455,7 @@ Fuzz testing: Parser13
!! result
@@ -5501,8 +5503,7 @@ noxml
https:// |
|
|
-
-
+ | |
@@ -5517,10 +5518,8 @@ Fuzz testing: Parser21
!! result
@@ -5542,6 +5541,22 @@ http://===r:::https://b
!! end
# Known to produce bad XML for now
+
+# Note: the current result listed for this is not what the original one was,
+# but the original bug was JavaScript injection, which is fixed in any case.
+# It's not clear that the original result listed was any more correct than the
+# current one. Original result:
+#
+# {{{|
+# }}}} >
+#
+#
+# MOVE YOUR MOUSE CURSOR OVER THIS TEXT
+#
+#
+# |
+#
+#
!! test
Fuzz testing: Parser24
!! options
@@ -5556,15 +5571,14 @@ noxml
MOVE YOUR MOUSE CURSOR OVER THIS TEXT
|
!! result
-
-{{{|
+{{{|
}}}} >
-
-MOVE YOUR MOUSE CURSOR OVER THIS TEXT
+
MOVE YOUR MOUSE CURSOR OVER THIS TEXT
+
+
@@ -7735,13 +7749,13 @@ y
- 1 |
- 2
- |
+1 |
+2 |
+
- 3 |
- 4
- |
+3 |
+4 |
+
y
!! end