1<?php
2/**
3 * Plugin itemtable: Renders tables in DokuWiki format by using itemlists instead of the Wiki syntax (very helpful for big tables with a lot of text)
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Olaf Trieschmann <trieschmann@otri.de>
7 *
8 * Thanks to Stephen C's plugin "dbtables", which was used as a starting point!
9 */
10
11// must be run within DokuWiki
12if(!defined('DOKU_INC')) die();
13
14if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
15require_once DOKU_PLUGIN.'syntax.php';
16require_once DOKU_INC.'inc/parser/parser.php';
17require_once DOKU_INC . 'inc/parser/xhtml.php';
18
19/**
20 * All DokuWiki plugins to extend the parser/rendering mechanism
21 * need to inherit from this class
22 */
23class syntax_plugin_itemtable extends DokuWiki_Syntax_Plugin {
24
25    // $options is used for rendering options
26    public $options=array();
27
28    function getInfo() {
29        return array('author' => 'Olaf Trieschmann',
30                     'email'  => 'develop@otri.de',
31                     'date'   => '2010-11-06',
32                     'name'   => 'Item Table',
33                     'desc'   => 'Renders tables in DokuWiki format by using itemlists instead of the Wiki syntax',
34                     'url'    => 'https://github.com/otriesch/itemtable/raw/master/itemtable.zip');
35    }
36    function getType() { return 'substition'; }
37    function getSort() { return 32; }
38
39    function connectTo($mode) {
40        $this->Lexer->addEntryPattern('<itemtable *[^>]*>',$mode,'plugin_itemtable');
41    }
42
43    function postConnect() {
44        $this->Lexer->addExitPattern('</itemtable>','plugin_itemtable');
45    }
46
47    /**
48     * Handle the match
49     */
50    function handle($match, $state, $pos, &$handler){
51        switch ($state) {
52            case DOKU_LEXER_ENTER :
53              return array($state, substr($match, 10, -1) );
54              break;
55            case DOKU_LEXER_MATCHED :
56              return array($state,$match);
57              break;
58            case DOKU_LEXER_UNMATCHED :
59              return array($state, $match);
60              break;
61            case DOKU_LEXER_EXIT :
62              return array($state, '');
63              break;
64        }
65        return array();
66    }
67
68    function render_tables($match,$mode,$data) {
69      // $match is the full text we're to consider
70      $raw=explode("\n",$match);
71
72//    $TableData.=$this->options["test"];
73//    foreach($this->options as $option) {
74//      $TableData.=$option." ";
75//    }
76//    $TableData.="\n\n\n";
77
78      // Yes, so draw the heading
79      if (trim($this->options["header"])!=""){
80      	// Draw the Dokuwiki table heading
81      	$TableData.="^".$this->options["header"].substr("^^^^^^^^^^",0,$this->options["cols"]+1)."\n";
82      } else {
83			$TableData.="";
84      }
85
86      // Draw the descriptors of each field
87      $TableData.="^ ";
88      for($ColPos=0;$ColPos<$this->options["cols"];$ColPos++)
89              $TableData.="^".$this->options["__col"][$ColPos]." ";
90      $TableData.="^\n";
91
92      for($ColPos=0;$ColPos<$this->options["cols"];$ColPos++) {
93  	     $RowElements["__col"][$ColPos]=" ";
94      }
95		$RowCount=0;
96		$CellActive=0;
97
98      // Run through each line and decide how to render the text
99      foreach($raw as $rawline) {
100        //In case we have to read a multiline input for one cell
101        if ($CellActive) {
102			 if (strstr($rawline,$this->options["cell_off"])) {
103				$RowElements["__col"][$CellActive-1].=" ".substr($rawline,0,strpos($rawline,$this->options["cell_off"]));
104				$CellActive=0;
105          } else {
106            $RowElements["__col"][$CellActive-1].=" ".$rawline;
107          }
108        } else {
109	        $CurrentLine=trim($rawline);
110	        if ($CurrentLine!=""){
111	          // Is this row the name of a row?
112	          if (substr($rawline,0,1)==$this->options["thead"]) {
113	            if ($RowCount!=0) {
114				     // Go through each entity and output it
115				     for($ColPos=0;$ColPos<$this->options["cols"];$ColPos++) {
116				  	    $TableData.="|".$RowElements["__col"][$ColPos]."  ";
117				     }
118	              // SHIP IT!
119	              $TableData.="|\n";
120	            }
121	            // Remember the current row name
122	      		$TableData.="|".substr($rawline,1)."  ";
123	      		for($ColPos=0;$ColPos<$this->options["cols"];$ColPos++) {
124	  	     		  $RowElements["__col"][$ColPos]=" ";
125	  	     		}
126					$RowCount++;
127	          } else {
128	            // Split the fields up.
129	            $RowInfo=explode($this->options["fdelim"],$rawline);
130	            if (count($RowInfo)>=2) {
131	      		  for($ColPos=0;$ColPos<$this->options["cols"];$ColPos++) {
132	           		 if ($RowInfo[0]==$this->options["__col"][$ColPos]) {
133							$r=substr($rawline,strlen($RowInfo[0])+1);
134							if (strstr($r,$this->options["cell_on"])) {
135							  $r=substr(strstr($r,$this->options["cell_on"]),strlen($this->options["cell_on"]));
136							  if (strstr($r,$this->options["cell_off"])) {
137							    $r=substr($r,0,strpos($r,$this->options["cell_off"]));
138							  } else {
139							    $CellActive=$ColPos+1;
140							  }
141							}
142	           		   $RowElements["__col"][$ColPos]=$r;
143	           		 }
144					  }
145	            }
146	          }
147	        }
148	     }
149	   }
150      // Go through each entity and output it
151   	for($ColPos=0;$ColPos<$this->options["cols"];$ColPos++) {
152  		  $TableData.="|".$RowElements["__col"][$ColPos]."  ";
153      }
154      // SHIP IT!
155      $TableData.="|\n";
156      // Start the HTML table rendering
157      $res="</p><table";
158      if ($this->options["twidth"]!="")
159        $res.=" width='".$this->options["twidth"]."'>";
160      else
161        $res.=">";
162
163      // Prepare the table information
164      // The option to not render from Dokuwiki to HTML is available
165      if ($this->options["norender"]=="")
166        $td="<td class='dbtables-td_0'>".p_render($mode,p_get_instructions($TableData),$data)."</td>";
167      else
168        $td="<td><pre>".$TableData."</pre></td>";
169
170      // Draw the table row
171      $res.="\n<tr class='dbtables-tr_0' valign='top'>\n";
172      // Write out the table data
173      $res.=$td."\n";
174      $CurTablePos=$CurTablePos+1;
175      // Close off the HTML-Table
176      $res.="</tr></table><p>";
177      return $res;
178    }
179
180    function render($mode, &$renderer, $data) {
181      // This will only render in xhtml
182      if($mode == 'xhtml'){
183         list($state, $match) = $data;
184          switch ($state) {
185              // This happens when we first find the <itemtable>
186              case DOKU_LEXER_ENTER :
187                $parmsexp=explode(';',$match);
188                // Set the relevant default values
189                $this->options["fdelim"]="="; // The character used to delimit what goes between fields
190                $this->options["header"]=""; //
191                $this->options["__col"]=array();
192                $this->options["cell_on"]="<tablecell>";
193                $this->options["cell_off"]="</tablecell>";
194                $this->options["thead"]="_";  // The character used to indicate the table name
195                // $this->options["twidth"]  // Default HTML table width in HTML specifications (IE: 95% - 960px)
196                // $this->options["norender"] -> Assign a value to NOT render from Dokuwiki to HTML
197
198                // Prepare each option
199//          $this->options["test"]="";
200                $this->options["cols"]=0;
201                foreach($parmsexp as $pexp) {
202                  $p=explode("=",$pexp);
203                  $p[0]=trim($p[0]);
204                  if (substr($p[0],0,1)=="c") {
205                    $pp=explode(",",$p[1]);
206//			$this->options["test"].=" p[0]=".$p[0]." p[1]=".$p[1]." pp[0]=".$pp[0]." pp[1]=".$pp[1]."\\ \n";
207						  $this->options["__col"]=array_merge ($this->options["__col"],$pp);
208                    foreach($pp as $ppexp) {
209                      $this->options["cols"]++;
210						  }
211                  } else {
212                    $this->options[$p[0]]=$p[1];
213                  }
214                }
215                break;
216              // This happens each line between <dbtables> and </dbtables>
217              case DOKU_LEXER_UNMATCHED :
218                // Send to the rendering function
219                $renderer->doc.=$this->render_tables($match,$mode,$data);
220                //$renderer->doc .= $renderer->_xmlEntities($match);
221                break;
222          }
223          return true;
224      }
225      return false;
226    }
227}