====== tabtables plugin ====== ---- plugin ---- description: Lets you use tab-delimited tables in your wiki by styling them as normal DokuWiki tables author : Mike "Pomax" Kamermans email : pomax@nihongoresources.com type : Action lastupdate : 2010-10-04 compatible : !Hogfather, Lemming tags : tables downloadurl: http://projects.nihongoresources.com/downloadables/plugin-tabtables.tar.gz ---- ===== Download and Installation ===== Search and install the plugin using the [[plugin:extension|Extension Manager]]. Refer to [[:Plugins]] on how to install plugins manually. * [[http://projects.nihongoresources.com/downloadables/plugin-tabtables.tar.gz|tar.gz format (3k)]] ===== Details ===== This plugin will let you use standard tab delimited tables in your wiki instead of using DokuWiki tabling syntax. This kind of data will look like a table will be turned into: | This | kind | of | | data | will | look | | like | a | table | Table headers are supported, both as column headers only, or as row+column headers: This kind of data will look like a table will be turned into: ^ This ^ kind ^ of ^ | data | will | look | | like | a | table | and kind of data will look like a table will be turned into: | ^ kind ^ of ^ ^ data | will | look | ^ like | a | table | Consecutive tables are dealt with as expected, although consecutive single row tables must be separated with two newlines, rather than one, in order to prevent data from turning into header data. table1 row1 data1 table2 row2 data2 Will be turned into ^ table1 ^ row1 ^ data1 ^ | table2 | row2 | data2 | whereas table1 row1 data1 table2 row2 data2 will be turned into | table1 | row1 | data1 | | table2 | row2 | data2 | ===== This is an action plugin, not a syntax plugin ===== Despite the function of this plugin being syntactical, the job it has to do cannot be done with a regular expression, and so is implemented as an action plugin, instead. This means that the action is performed before the standard wiki syntax translation (including all syntax plugins) take effect. A consequence of this is that lines that start with tabs are translated to wikisymbols. Since processing is per line, the plugin will not convert anything that's in %%%%, %%%% and %%%% blocks **unless these blocks are opened and closed on the same line**. ===== Todo ===== Table pruning? (if a table block has a first X columns that are empty in all cells, prune; same for last columns?) Maybe, though, since I can't really think of when I would need this other than "when I've been sloppy as a content author". >How can I type a "tab" character while I'm editing the page in DokuWiki? If I press TAB on the keyboard, it jumps to the Edit Summary field below the edition area. Is there a better way than write the text in notepad and paste it here? One option is to prepare your table data in a text editor with tabs, the other is to copy a tab into your clipboard and instead of typing "tab" to get one, typing ctrl/cmd-v (or whatever key combination acts as "paste" on your OS). This plugin will covert tabs in %%%%, %%%%, and even in %%%% blocks, **provided these are opened and closed on the same line**. This is a consequence of running as action plugin. ===== Bugs ===== ==== PHP 7.0 compatibility ==== This plugin uses the //split// function that is not available in PHP7.0. To fix you must replace line 165 in action.php (that contains //split//) with: $cells = count(preg_split('/\t/',$original[0])); ==== 2009-10-02 ==== > After installing this plugin I saw the error "Cannot modify header information - headers already sent ..." after every save. Deactivating the plugin solved the issue. ( breg ) Can you indicate in combination with which plugins this happened, and which version you have installed? Because the 2009-09-29 version solved that problem by not writing any debug information to the html output unless the debug flag is manually set to true (by default it is set to false). ==== 2009-09-29 ==== The timing information echo statements caused tabtables to generate output before DokuWiki, causing default headers to be sent, which broke functionality with other components and plugins. The timing information is now not sent unless the $this->echo_timing variable (line 21) is set to 'true' manually. ===== Source ===== This plugin consists of a single action.php script: */ if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'action.php'); class action_plugin_tabtables extends DokuWiki_Action_Plugin { /** * Set this flag to true for timing information. Note that when this is turned on, * the plugin may generate output before dokuwiki's sent its own headers information, * which may cause problems for other plugins or even base configuration functionality. */ var $echo_timing = false; /** * offsets-at-character-position. recorded as tuples {charpos,offset}, where charpos is the * position in the MODIFIED data, not the position in the original data, and offset is the * CUMULATIVE offset at that position, not the offset relative to the previous location. */ var $offsets = array(); /** * During the run, contains the original wiki data. */ var $original; /** * During the run, contains the modified wiki data. */ var $wikified; /** * Required function, used by dokuwiki on the plugins configuration page. */ function getInfo() { return array( 'author' => 'Mike "Pomax" Kamermans', 'email' => 'pomax@nihongoresources.com', 'date' => '2010-10-04', 'name' => 'TabTables', 'desc' => 'Turns tab delimited table data into dokuwiki tables', 'url' => 'http://www.dokuwiki.org/plugin:tabtables'); } /** * Preprocesses the user's written data, by hooking into the text parser at the preprocessing point */ function register(Doku_Event_Handler $controller) { $controller->register_hook('PARSER_WIKITEXT_PREPROCESS', 'BEFORE', $this, '_tablify'); $controller->register_hook('PARSER_HANDLER_DONE','BEFORE', $this, '_fixsecedit'); } /** * Tablify - runs through the base text, and replaces tab delimited table data * with proper docuwiki table syntax */ function _tablify(&$event, $param) { $start = $this->microtime_float(); if($this->echo_timing) { echo "\n\n"; } $this->original = explode("\n",$event->data); $this->wikified = $this->original; // tabling administration $table_position = -1; $in_block = false; $_table_data = array(); $_empty_count = 0; // iterate through the wiki data, line by line $code_blocked = false; $file_blocked = false; $nowiki_blocked = false; for($l=0; $loriginal); $l++) { $line = $this->original[$l]; // blocking? if(strpos($line,"")!==false) { $code_blocked = false; } if(strpos($line,"</file>")!==false) { $file_blocked = false; } if(strpos($line,"</nowiki>")!==false) { $nowiki_blocked = false; } // if blocked, immediately continue on to the next line if($code_blocked || $file_blocked || $nowiki_blocked) { continue; } // aggregate tabling lines (a tabling line either contains tabs, or is either empty after trimming) if(strpos($line,"\t")!==false || ($in_block && $line=="")) { // set up table block if not aggregating yet if(!$in_block) { $in_block=true; $table_position=$l; $_table_data = array(); } // if empty line, is this the first or second consecutive empty line? // If the second, we need to finalise this table block if($line=="" && count($_table_data)>1) { $in_block = $this->_finalise($_table_data, $table_position); } // if we didn't just finalise, aggregate the data if($in_block) { $_table_data[]=$line; }} // last option: this was not a tabling line, but we have a filled table block that needs processing elseif($in_block) { $in_block = $this->_finalise($_table_data, $table_position); } } // In case the table was the last thing on the page, we still have a table block to process if($in_block) { $this->_finalise($_table_data, $table_position); } if($this->echo_timing) { echo "\n"; } $start = $this->microtime_float(); // then, some administration so that we can perform section start/end // marker correction after parsing is done (next event) $char_pos = 0; $text_offset = 0; for($l=0; $lwikified); $l++) { // record offsets at the start of this line $this->offsets[] = array('pos'=>$char_pos,'offset'=>$text_offset); // pos/offset for next line will be: $char_pos += strlen($this->wikified[$l]) + 1; // +1 for the missing newline $text_offset += strlen($this->wikified[$l]) - strlen($this->original[$l]); } if($this->echo_timing) { echo "\n"; } // and we're done... $event->data = implode("\n",$this->wikified); // but just for good measure, unset the original/wikified variables unset($this->original); unset($this->wikified); } /** * Gets the table data rewritten, then updates the modified container. returns false, so that * the iteration knows we're no longer in table data aggregation mode. */ function _finalise(&$_table_data, $table_position) { $wikified = $this->_replace($_table_data); for($r=0; $rwikified[$table_position + $r] = $wikified[$r]; } return false; } /** * this function does the actual syntax replacement */ function _replace($original) { $new_table_block = array(); // preprocess check: will this use row headers? ie, is the first table "cell" an empty tab? $_has_row_headers = (strpos($original[0],"\t")==0) ? true : false; // how many cells are we actually dealing with? $cells = count(split("\t",$original[0])); $empty_line = "| " . str_repeat("|",$cells); // replace the tabulation with wiki table syntax for($r=0; $r2 && $new_table_block[1]==$empty_line) { $new_table_block[0] = str_replace("|","^",$new_table_block[0]); // make sure to clear the header/content separator line for($r=1; $rmicrotime_float(); $calls = &$event->data->calls; $count = count($calls); if($this->echo_timing) { echo "\n"; } // iterate through the instruction list and set the file offset values // back to the values they would be if no tabling syntax ahd been added by this plugin for ($i=0; $i < $count; $i++) { if ($calls[$i][0] == 'section_edit') { $calls[$i][1][0] = $this->_convert($calls[$i][1][0]); $calls[$i][1][1] = $this->_convert($calls[$i][1][1]); $calls[$i][2] = $this->_convert($calls[$i][2]); }} if($this->echo_timing) { echo "\n\n"; } } /** * Convert modified raw wiki offset value ($pos) back to the unmodified value */ function _convert($pos) { // find the offset that applies to this character position $offset=0; foreach($this->offsets as $tuple) { if($pos>=$tuple['pos']) { $offset = $tuple['offset']; } else { break; }} // return offset-corrected position return $pos - $offset; } /** * debugging helper function - gives us the microsecond * timestamp (in actual microseconds, not seconds) */ function microtime_float() { list($usec, $sec) = explode(" ", microtime()); return 1000000*((float)$usec + (float)$sec); } } ?>