Table of Contents

Extended Table Syntax 2 Plugin

exttab2 plugin by disorder Chang
Another Mediawiki style tables inside DokuWiki

Last updated on 2007-11-06. Provides Syntax.
Compatible with DokuWiki 2007-06-26b.

Similar to exttab1, truetable.

Tagged with mediawiki, tables.

The following security issue was reported for this plugin:

XSS vulnerability allows arbitrary JavaScript insertion. Author informed on 2008-02-07.

It is not recommended to use this plugin until this issue was fixed. Plugin authors should read the plugin security guidelines.

About

I like the 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

Usage

Unlike exttab1 plugin, you don't need to put any markup to enclose the syntax, just draw table as 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 row1)
* |+ 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 table2)

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>
file or code
goes 
here
</file>
|}

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

Sources

 
<?php
//ini_set("display_errors", "On"); // for debuging
/**
 * exttab2-Plugin: Parses extended tables (like MediaWiki) 
 *
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     disorde chang <disorder.chang@gmail.com>
 * @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("(?<!\|)\n",'plugin_exttab2'); // not work
    $this->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."</".$tagstr.">". $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 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.

1) caption appears in other place will be echo out directly
2) no need for nested table