====== Extended Table Syntax 2 Plugin ====== ---- plugin ---- description: Another Mediawiki style tables inside DokuWiki author : disorder Chang email : disorder.chang@gmail.com type : Syntax lastupdate : 2007-11-06 compatible : 2007-06-26b depends : conflicts : similar : exttab1 tags : Mediawiki, tables securityissue: XSS vulnerability allows arbitrary JavaScript insertion. Author informed on 2008-02-07. ---- ===== About ===== I like the [[plugin:exttab1|exttab1]] plugin, which can handle Wikimedia way of table syntax. But exttab1 does not parse some dokuwiki syntax correctly, eg. footnote, image link, etc. So I decided to write a new one. This plugin is still in its early stages, please feel free to modify the code. ===== Installation ===== * create the folder //lib/plugins/exttab2 // * and copy the source code to //lib/plugins/exttab2/syntax.php// ===== Usage ===== Unlike exttab1 plugin, you don't need to put any markup to enclose the syntax, just draw table as [[http://www.mediawiki.org/wiki/Help:Tables|mediawiki]] do. ==== markup summary ==== %%*%% means that the markup must be on a new line |* |%%{|%% | start table | |* |%%{| para %% | start table with parameters| |* |%%|+ caption%% | table caption; only one per table and between table start and first row((caption appears in other place will be echo out directly)) | |* |%%|+ para | caption %% | table caption with parameters | |* |%%|-%% | table row | |* |%%|- para%% | table row with parameters | |* |%%! header%% | table header cell | |* |%%! para | header%% | table header cell with parameters | | |%%!! header %% | consecutive table headers | | |%%!! para | header %% | consecutive table headers with parameters | |* |%%| cell%% | table data cell; Cell content may follow on same line or on following lines | |* |%%| para | cell%% | table data cell with parameters | | |%%|| cell %% | consecutive table data cell | | |%%|| para | cell %% | consecutive table data cell with parameters | |* |%%|}%% | end table; please add an additional empty line to end the whole table((no need for nested table)) | please see [[http://meta.wikimedia.org/wiki/Help:Table]] for more detail. ====Examples==== ===Simple table=== {| |+ caption ! header 1 ! header 2 |- | cell A | cell B |} {| |+ caption ! header 1 !! header 2 |- | cell A || cell B |} === Table with parameter and wiki markups (formatting, linking, etc.)=== {| border="1" style="width:300px" |+ style="color:red"| //caption// |- style="background:green;height:50px" ! style="color:white" |header 1 ! header 2 !! style="color:yellow" | header 3 |- | style="text-align:right" | **bold**, //italic//, ((footnote)) | [[link|a link]] || style="color:yellow" | {{pdf.pdf}} |- | text with \\ new line OK\\ [[http://www.google.com|google]] good\\ | colspan="2" | but ====Headline==== not working! |} === Table with complex wiki markups (listblock, hr, preformatted, code, etc.) === {| border="1" |- | * add an additional * blank line * after the last list | as many lines as you like but don't add any empty line |- | add an additional blank line after the preformatted text | hr fine ---- |- | > quoting must >> add a blank line too | file or code goes here |} === Nested tables === {| border="1" | you | can use {| border="2" style="background:#CCC;" | nested |- | table |} now | . |} ===== Limits ===== I use the source of [[http://meta.wikimedia.org/wiki/Help:Table]] to test this plugin. It works fine, except: ====Column headings with double bar==== The following syntax will render heading 2 as normal cell ! Column heading 1 || Column heading 2 =====Revision History===== * 2006-11-06 0.2.0 support nested table and many more... * 2006-10-04 0.1.0 Initial release =====Sources===== * @date 2007-10-04 */ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'syntax.php'); /** * All DokuWiki plugins to extend the parser/rendering mechanism * need to inherit from this class */ class syntax_plugin_exttab2 extends DokuWiki_Syntax_Plugin { var $stack = array(); function syntax_plugin_exttab2(){ define("EXTTAB2_TABLE", 0); define("EXTTAB2_CAPTION", 1); define("EXTTAB2_TR", 2); define("EXTTAB2_TD", 3); define("EXTTAB2_TH", 4); $this->tagsmap = array( EXTTAB2_TABLE=> array("table", "", "\n" ), EXTTAB2_CAPTION=> array("caption", "\t", "\n" ), EXTTAB2_TR=> array("tr", "\t", "\n" ), EXTTAB2_TD=> array("td", "\t"."\t", "\n" ), EXTTAB2_TH=> array("th", "\t"."\t", "\n" ), /* // DOKU constant not work when preview EXTTAB2_TABLE=> array("table", "", DOKU_LF ), EXTTAB2_CAPTION=> array("caption", DOKU_TAB, DOKU_LF ), EXTTAB2_TR=> array("tr", DOKU_TAB, DOKU_LF ), EXTTAB2_TD=> array("td", DOKU_TAB.DOKU_TAB, DOKU_LF ), EXTTAB2_TH=> array("th", DOKU_TAB.DOKU_TAB, DOKU_LF ), */ ); } function getInfo(){ return array( 'author' => 'Disorder Chang', 'email' => 'disorder.chang@gmail.com', 'date' => '2007-11-06', 'name' => 'exttab2 Plugin', 'desc' => 'parses MediaWiki-like tables', 'url' => 'http://www.dokuwiki.org/plugin:exttab2', ); } function getType(){ return 'container'; } function getPType(){ return 'block'; } function getAllowedTypes() { return array('container', 'formatting', 'substition', 'disabled', 'protected'); } function getSort(){ return 50; } function connectTo($mode) { $this->Lexer->addEntryPattern('\n\{\|[^\n]*',$mode,'plugin_exttab2'); } function postConnect() { $para = "[^\|\n\[\{\!]+"; // parametes // caption: |+ params | caption $this->Lexer->addPattern("\n\|\+(?:$para\|(?!\|))?",'plugin_exttab2'); // row: |- params $this->Lexer->addPattern('\n\|\-[^\n]*','plugin_exttab2'); // table open $this->Lexer->addPattern('\n\{\|[^\n]*','plugin_exttab2'); // table close $this->Lexer->addPattern('\n\|\}','plugin_exttab2'); // header $this->Lexer->addPattern("(?:\n|\!)\!(?:$para\|(?!\|))?",'plugin_exttab2'); //cell $this->Lexer->addPattern("(?:\n|\|)\|(?:$para\|(?!\|))?",'plugin_exttab2'); //end // $this->Lexer->addExitPattern('\n','plugin_exttab2'); // $this->Lexer->addExitPattern("(?Lexer->addExitPattern("\n(?=\n)",'plugin_exttab2'); } /** * Handle the match */ function handle($match, $state, $pos, &$handler){ if($state == DOKU_LEXER_EXIT) { return array($state, "end"); } else if($state == DOKU_LEXER_UNMATCHED){ return array($state, "", $match); } else{ $para = "[^\|\n]+"; // parametes if(preg_match ( '/\{\|([^\n]*)/', $match, $m)){ // table open $func = "table_open"; $params = $m[1]; return array($state, $func, $params); } else if($match == "\n|}"){ // table close $func = "table_close"; $params = ""; return array($state, $func, $params); } else if(preg_match ("/^\n\|\+(?:(?:($para)\|)?)$/", $match, $m)){ // caption $func = "caption"; $params = $m[1]; return array($state, $func, $params); } else if(preg_match ( '/\|-([^\n]*)/', $match, $m)){ // row $func = "row"; $params = $m[1]; return array($state, $func, $params); } else if(preg_match("/^(?:\n|\!)\!(?:(?:([^\|\n\!]+)\|)?)$/", $match, $m)){ // header $func = "header"; $params = $m[1]; return array($state, $func, $params); } else if(preg_match("/^(?:\n|\|)\|(?:(?:($para)\|)?)$/", $match, $m)){ // cell $func = "cell"; $params = $m[1]; return array($state, $func, $params); } else{ die("what? ".$match); // for debugging } } } /** * Create output */ function render($mode, &$renderer, $data) { if($mode == 'xhtml'){ list($state, $func, $params) = $data; switch ($state) { case DOKU_LEXER_UNMATCHED : $r = $renderer->_xmlEntities($params); $renderer->doc .= $r; break; case DOKU_LEXER_ENTER : case DOKU_LEXER_MATCHED: $r = $this->$func($params); $renderer->doc .= $r; break; case DOKU_LEXER_EXIT : $r = $this->$func($params); $renderer->doc .= $r; break; } return true; } return false; } function _attrString($attr="", $before=" "){ if(is_null($attr) || trim($attr)=="") $attr = ""; else $attr = $before.trim($attr); return $attr; } var $tagsmap = array(); function _starttag($tag, $params=NULL, $before="", $after=""){ $tagstr = $this->tagsmap[$tag][0]; $before = $this->tagsmap[$tag][1].$before; $after = $this->tagsmap[$tag][2].$after; $r = $before."<".$tagstr.$this->_attrString($params).">". $after; return $r; } function _endtag($tag, $before="", $after=""){ $tagstr = $this->tagsmap[$tag][0]; $before = $this->tagsmap[$tag][1].$before; $after = $this->tagsmap[$tag][2].$after; $r = $before."". $after; return $r; } function table_open($params=NULL){ $r .= $this->_closetags(EXTTAB2_TABLE); $r .= $this->_starttag(EXTTAB2_TABLE, $params); $this->stack[] = EXTTAB2_TABLE; return $r; } function table_close($params=NULL){ $t = end($this->stack); switch($t){ case EXTTAB2_TABLE: array_push($this->stack, EXTTAB2_TR, EXTTAB2_TD); $r .= $this->_starttag(EXTTAB2_TR, $params); $r .= $this->_starttag(EXTTAB2_TD, $params); break; case EXTTAB2_CAPTION: $r .= $this->_endtag(EXTTAB2_CAPTION); array_pop($this->stack); array_push($this->stack, EXTTAB2_TR, EXTTAB2_TD); $r .= $this->_starttag(EXTTAB2_TR, $params); $r .= $this->_starttag(EXTTAB2_TD, $params); break; case EXTTAB2_TR: array_push($this->stack, EXTTAB2_TD); $r = $this->_starttag(EXTTAB2_TD, $params); break; case EXTTAB2_TD: case EXTTAB2_TH: break; } while(($t = end($this->stack)) != EXTTAB2_TABLE){ $r .= $this->_endtag($t); array_pop($this->stack); } array_pop($this->stack); $r .= $this->_endtag(EXTTAB2_TABLE); return $r; } function end($params=NULL){ while(!empty($this->stack)){ $r .= $this->table_close(); } return $r; } function caption($params=NULL){ if(($r = $this->_closetags(EXTTAB2_CAPTION)) === FALSE){ return ""; } $r .= $this->_starttag(EXTTAB2_CAPTION, $params); $this->stack[] = EXTTAB2_CAPTION; return $r; } function row($params=NULL){ $r .= $this->_closetags(EXTTAB2_TR); $r .= $this->_starttag(EXTTAB2_TR, $params); $this->stack[] = EXTTAB2_TR; return $r; } function header($params=NULL){ $r .= $this->_closetags(EXTTAB2_TH); $r .= $this->_starttag(EXTTAB2_TH, $params); $this->stack[] = EXTTAB2_TH; return $r; } function cell($params=NULL){ $r .= $this->_closetags(EXTTAB2_TD); $r .= $this->_starttag(EXTTAB2_TD, $params); $this->stack[] = EXTTAB2_TD; return $r; } function _closetags($tag){ $r = ""; switch($tag){ case EXTTAB2_TD: case EXTTAB2_TH: $t = end($this->stack); switch($t){ case EXTTAB2_TABLE: array_push($this->stack, EXTTAB2_TR); $r .= $this->_starttag(EXTTAB2_TR, $params); break; case EXTTAB2_CAPTION: $r .= $this->_endtag(EXTTAB2_CAPTION); array_pop($this->stack); array_push($this->stack, EXTTAB2_TR); $r .= $this->_starttag(EXTTAB2_TR, $params); break; case EXTTAB2_TR: break; case EXTTAB2_TD: case EXTTAB2_TH: $r .= $this->_endtag($t); array_pop($this->stack); break; } break; case EXTTAB2_TR: $t = end($this->stack); switch($t){ case EXTTAB2_TABLE: break; case EXTTAB2_CAPTION: $r .= $this->_endtag(EXTTAB2_CAPTION); array_pop($this->stack); break; case EXTTAB2_TR: $r .= $this->_starttag(EXTTAB2_TD); $r .= $this->_endtag(EXTTAB2_TD); $r .= $this->_endtag(EXTTAB2_TR); array_pop($this->stack); break; case EXTTAB2_TD: case EXTTAB2_TH: $r .= $this->_endtag($t); $r .= $this->_endtag(EXTTAB2_TR); array_pop($this->stack); array_pop($this->stack); break; } break; case EXTTAB2_TABLE: $t = end($this->stack); if($t === FALSE) break; switch($t){ case EXTTAB2_TABLE: array_push($this->stack, EXTTAB2_TR, EXTTAB2_TD); $r .= $this->_starttag(EXTTAB2_TR, $params); $r .= $this->_starttag(EXTTAB2_TD, $params); break; case EXTTAB2_CAPTION: $r .= $this->_endtag(EXTTAB2_CAPTION); array_pop($this->stack); array_push($this->stack, EXTTAB2_TR, EXTTAB2_TD); $r .= $this->_starttag(EXTTAB2_TR, $params); $r .= $this->_starttag(EXTTAB2_TD, $params); break; case EXTTAB2_TR: array_push($this->stack, EXTTAB2_TD); $r = $this->_starttag(EXTTAB2_TD, $params); break; case EXTTAB2_TD: case EXTTAB2_TH: break; } break; case EXTTAB2_CAPTION: $t = end($this->stack); if($t==EXTTAB2_TABLE){ } else{ return false ; // ignore this, or should echo error? } break; } return $r; } } ?> ===== Discussion ===== Yup this is an awesome plug in I love it. One question. I tried using the [[http://meta.wikimedia.org/wiki/Help:Sorting |sorting feature]] but was unable to. Any hopes of getting this to work? --lenehey [Nov. 27, 2007] Another question. Any hopes of doing Ettab3 where Ettab3 is Ettab2 + sorting table and a finished plugin รก non-programmer can use ?. Philip Hettel [feb. 17, 2008] Found a bug which prevents you from coloring a cell anything brighter than #999999. For some reason if you include a letter such as "#AA88BB" in the hex-color, it won't parse.