Table Action


See also:
This is the development page for the Table action.
 

History

The original Table action available in Wikka was from IanAndolina's nontroppo wiki (archived).
Note: Searching on the web reveals that Ian is not the original author. Credit goes to Michael Abendroth (IbuKi) for being the original author of IbukiTableAction @ WakkaWiki.

The original Table action provides a nice way to create a simple HTML table.

This Table action has a number of drawbacks, however:
  1. It is awkward to use for anything but the most simple tables - mainly because it's an action, which is different from the common approach to table markup in many Wikis: Most WikiEngines that support tables provide a table markup instead of a "plugin"-like solution like this.
  1. Cell contents can't have Wikka markup (if they do, the markup is shown as raw text instead of formatted).
  1. The action does not actually produce data table markup (which it should).

Preliminary solution

There is an ongoing discussion about providing true table markup in Wikka:

Clearly, we need a better solution than the current action, preferably (some kind of) true table markup syntax.
However, it would be unwise to release a "preliminary" markup, as it could easily lead to confusion among our user base (as well as needless conversions down the line), and extra work for the developers.
Instead, we should first determine what we want to be able to do in terms of table markup, and then look if we can find a simpler preliminary solution (as a subset of that markup) that still enables us to get there. And any preliminary solution should at least address the problem of ease-of-use and generating data table markup.

So, until we have come to a clear conclusion about "Wikka table markup", we won't have a preliminary Wikka table markup either. Still, there is a clear (and sometimes pressing) need for an easier way to produce HTML tables from Wikka code. Therefore I've investigated whether it was possible to extend the current Table action to provide more flexibility and address the major drawbacks at least partially. I was glad to find it wasn't actually all that hard.

I'm presenting here a rewrite of the Table action and a minor patch to the main wikka.php file (which could benefit other actions as well). It's far from a perfect solution for producing tables - but it's a lot more flexible and easier to use than the current table action. So I'm hoping this can tide us over until we embark on true table markup.

Patch for ./libs/Wakka.class.php
Two very small changes in ./libs/Wakka.class.php are going to provide us with the framework that enables the new Table action to be much easier to use. Essentially, they make it possible to:
The second option, while very important for easy maintainability of Table action content (particularly, the cell contents), should be approached with caution however: currently (most? all?) actions are not written to take advantage of this. While the new Table action is written specifically to make use of this, other actions may actually fail if multi-line parameters are passed to them. Current action code won't fail, but all action code will need to be investigated (and maybe patched) if the new capability causes problems.

In ./libs/Wakka.class.php, find the Action function (line 981 in 1.1.6.3). Then replace this line:
            preg_match("/^([A-Za-z0-9]*)\s+(.*)$/", $action, $matches);
by this:
            preg_match("/^([A-Za-z0-9]*)\s+(.*)$/s", $action, $matches);
and replace this line:
                preg_match_all("/([A-Za-z0-9]*)=\"(.*)\"/U", $vars_temp, $matches);
by this:
                preg_match_all("/([A-Za-z0-9]*)=\"(.*)\"/sU", $vars_temp, $matches);

In both cases, all we do is add an 's' after the final '/' of the regular expression, which will make it match across multiple lines.

The first change allows us to "recognize" an action that spans multiple lines (so you can put each parameter on a new line, if desired); the second change allows parameter content to span multiple lines (a parameter value must still be enclosed in double quotes to be recognized, though). Careful with the latter one: not all actions may be able to handle such multi-line parameter values!!

New Table action
Starting with the current Table action, refactoring, refactoring, refactoring .... many of you won't be surprised that I ended up with a complete rewrite of the table action. It provides a lot more flexibility but is completely backwards-compatible with the current table action. Still, if you want to test only, I'd advise you to store the code under the name of table2.php rather than table.php (as indicated below): that way you can test without influencing the operation of any current table actions on your Wikka site.

Replace ./actions/table.php with the following code (or save it as table2.php for testing):
<?php
/**
 * Turns a simple list of cell contents into a (data) table.
 *
 * Wikka markup within cells is interpreted but HTML markup for cell contents is not possible.
 *
 * There is only one required paramater: cells; if this is missing an error message will be
 * displayed, along with the original action markup.
 *
 * It is possible to define cell contents across multiple lines; surrounding whitespace is trimmed
 * before generating output, embedded whitespace (including newlines) is retained.
 *
 * Cell contents must be separated by a delimiter character; by default this is a semicolon, but if
 * this would conflict with cell contents, a delimiter can be defined with the delimiter (or delim)
 * parameter. Superfluous delimiters at the start and end of the cells parameter are discarded,
 * so it's possible to write each cell row on a separate line, and end each cell with the delimiter:
 * this makes it relatively easy to maintain table contents.
 *
 * A caption can be defined, as well as a summary.
 * There is limited support for table headers (th cells): if cell content is embedded in sets of
 * 2 to 5 '=' characters (as in heading markup) these will be discarded and a header cell (th) will
 * be generated instead of a data cell (td). Row groups (thead, tfoot, tbody) are NOT supported.
 * The result is valid (data) table markup although accessibility will be limited.
 *
 * @package     Actions
 * @subpackage  Formatters
 * @name        Table
 *
 * @author      {@link http://wikka.jsnx.com/JavaWoman JavaWoman}
 *              (rewrite of {@link http://wikka.jsnx.com/JsnX JsnX}'s original)
 * @copyright   Copyright © 2005, Marjolein Katsma
 * @license     http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
 * @since       Wikka 1.0.0     (original)
 * @since       Wikka 1.1.6.x   (rewrite)
 *
 * @input       char    $delimiter  optional: delimiter that will be used to separate cells; must be a single character. default: semicolon (;)
 * @input       char    $delim      optional: synonym of $delimiter
 * @input       string  $caption    optional: table caption
 * @input       integer $columns    optional: number of columns the table is to have. default: 1
 * @input       integer $cols       optional: synonym of $columns
 * @input       integer $border     optional: borderwidth of table. default: 1
 * @input       integer $cellpadding    optional: cellpadding for the table. default: 3
 * @input       integer $cellspacing    optional: cellspacing for the table. default: 1
 * @input       string  $style      optional: in-line style for the table. default: none
 * @input       string  $class      optional: alternative for style: use the class to style with the stylesheet. default: none
 * @input       string  $summary    optional: summary of what the table contains. default: none
 * @input       string  $cells      required: content of the table with cells separated by the (defined) delimiter
 * @output      XHTML table, optionally with caption and header cells, and with markup within cells
 */


// Constants
define('EMPTY_CELL', '###');
define('METACHARS', '-+*,|[]{}/\\');    # meta characters used in REs must be escaped if used as delimiter

define('MISSING_PARAM', 'Error: required parameter %s is missing'); # i18n

// The following defaults can be overridden with a parameter
$lDelim = ';';      # default delimiter
$lCaption = '';
$lCols = 1;
$lBorder = 1;
$lPadding = 3;
$lSpacing = 1;
$lStyle = '';       # styling for table tag
$lClass = '';
$lSummary = '';

if (is_array($vars))
{
    // check presence of required parameters
    if (!isset($vars['cells']))
    {
        $out  = '<em class="error">';
        $out .= '{{table '.$vars['wikka_vars'].'}}';
        $out .= sprintf(' '.MISSING_PARAM, '<tt>cells</tt>');
        $out .= '</em>';
    }
    // all OK, let's go ahead
    else
    {
        // get delimiter first (escape chars that are special chars in REs)
        if (isset($vars['delimiter']))
        {
            if (1 == strlen($vars['delimiter'])) $lDelim = addcslashes($vars['delimiter'],METACHARS);
        }
        elseif (isset($vars['delim']))
        {
            if (1 == strlen($vars['delim'])) $lDelim = addcslashes($vars['delim'],METACHARS);
        }
        // process other parameters
        foreach ($vars as $param => $value)
        {
            // sanitize input
            $value = trim($value);
            while ($value != strip_tags($value)) $value = strip_tags($value);
            // parse input
            switch ($param)
            {
                case 'caption':
                    $lCaption = $value;
                    break;
                case 'columns':
                case 'cols':
                    if (preg_match('/[0-9]+/',$value)) $lCols = $value;
                    break;
                case 'border':
                    if (preg_match('/[0-9]+/',$value)) $lBorder = $value;
                    break;
                case 'cellpadding':
                    if (preg_match('/[0-9]+/',$value)) $lPadding = $value;
                    break;
                case 'cellspacing':
                    if (preg_match('/[0-9]+/',$value)) $lSpacing = $value;
                    break;
                case 'style':
                    $lStyle = $value;
                    break;
                case 'class':
                    $lClass = $value;
                    break;
                case 'summary':
                    $lSummary = $value;
                    break;
                case 'cells':
                    // get rid of any surrounding delimiters
                    if (preg_match('/(.*?)['.$lDelim.']+$/s',$value,$matches)) $value = $matches[1];
                    if (preg_match('/^['.$lDelim.']+(.*)/s',$value,$matches)) $value = $matches[1];
                    // split contents into array
                    $cells = split($lDelim, $value);
                    break;
            }
        }

        // start table
        $out = '<table cellpadding="'.$lPadding.'" cellspacing="'.$lSpacing.'" border="'.$lBorder.'"';
        if ('' != $lStyle) $out .= ' style="'.$lStyle.'"';
        if ('' != $lClass) $out .= ' class="'.$lClass.'"';
        if ('' != $lSummary) $out .= ' summary="'.$lSummary.'"';
        $out .= ">\n";
        // optional caption
        if ('' != $lCaption) $out .= '<caption>'.$lCaption.'</caption>'."\n";

        $iCell = 0;
        foreach ($cells as $content)
        {
            // discard surrounding whitespace
            $content = trim($content);
            // start row
            if (($iCell % $lCols) == 0) $out .= "<tr>\n";
            // process cells
            if ($content == EMPTY_CELL || $content == '')
            {
                $out .= "   <td>&nbsp;</td>\n";
            }
            elseif (preg_match('/={2,5}(.*?)={2,5}/',$content,$matches))
            {
                $out .= '   <th>'.trim($this->Format($matches[1]))."</th>\n";
            }
            else
            {
                $out .= '   <td>'.trim($this->Format($content))."</td>\n";
            }
            // end row
            if ((++$iCell % $lCols) == 0) $out .= "</tr>\n";
        }
        // end table
        $out .= "</table>\n";
    }
}
else
{
    $out  = '<em class="error">';
    $out .= '{{table}}';
    $out .= sprintf(' '.MISSING_PARAM, '<tt>cells</tt>');
    $out .= '</em>';
}
echo $out;
?>


Comments?

See the TableActionInfo page for documentation and examples [later]. Please try out the various new options, and see if this helps with producing tables (as a preliminary solution).

Comments and suggestions are welcome, as always. But please take into account that this is definitely not intended as a complete solution, only to tide us over until we get actual table markup.


A workaround for better table headers management


I modified the table action adding the header parameter so a user can specify if he wants a normal table or a table with the first row rendered as th or a table with the first row and the first cell of every row rendered as th.

{{table columns="3" cellpadding="1" cells="BIG;GREEN;FROGS;yes;yes;no;no;no;###" header="0"}}
{{table columns="3" cellpadding="1" cells="BIG;GREEN;FROGS;yes;yes;no;no;no;###" header="1"}}
{{table columns="3" cellpadding="1" cells="BIG;GREEN;FROGS;yes;yes;no;no;no;###" header="2"}}


Go to my wiki to see it in action.

This is the modified table action:

<?php

//$vars = array('columns' => '3', 'cellpadding' => '1', 'cells' => '**BIG**;**GREEN**;**FROGS**;yes;yes;no;no;yes;yes');

// Init:
$delimiter=';';
$empty_cell='###';
$row=1;
$cellpadding=1;
$cellspacing=1;
$border=1;
$columns=1;
$header = 0; // this is the new parameter. the default value is 0 so if no parameter is defined there will be no table header
$row_cont = 1; // this is a row counter
// $style='border-spacing: 2px;border:2px outset #876;width:auto;margin:0 auto;';
$style='';

if (is_array($vars))
{
    foreach ($vars as $param => $value)
    {
        if ($param == 'style') {$style=$value;}  
        if ($param == 'columns') {$columns=$value;}
        if ($param == 'header') {$header=$value;}
        if ($param == 'cellpadding')
        {
            $cellpadding=$value;
            $border=$value;
        }            
        if ($param == 'cells') $cells = split($delimiter, $value);
    }
   
    $cached_output = "<table cellpadding='".$cellpadding."' cellspacing='".$cellspacing."' border='".$border."' style='".$style."'>\n";
    foreach ($cells as $cell_item)
    {
        if ($row == 1) $cached_output .= "   <tr>\n";
        if ($cell_item==$empty_cell) $cell_item='<br />';
       
        // If we are not in the first row and header is set to 2 and if this is the first cell, then we render it as th
        if ($header == 2 && $row_cont > 1 && $row == 1 ) $cached_output .= "       <th> ".$cell_item."</th>\n";
        // If header is set to 1 or 2 and we are in the first row, then we rendere this row as a table header
        else if (($header == 1 || $header == 2) && $row_cont == 1) $cached_output .= "       <th>".$cell_item."</th>\n";
        // Else we rendere normal table cells
        else $cached_output .= "       <td>".$cell_item."</td>\n";
       
        $row ++;
        if ($row > $columns)
        {
            $row = "1";
            $row_cont ++; // Let's count rows number
            $cached_output .= "   </tr>\n";
        }
    }
    $cached_output .= "</table>";
    echo $this->ReturnSafeHTML($cached_output);
}

?>

--
AlessandroMelandri


CategoryDevelopmentActions
Comments
Comment by DarTar
2005-01-07 18:36:53
Layout note: if you close a float formatter like >> on the same line as a list item, it won't be recognized, so all the page will be included in the float.
This looks like a (minor) bug related to the known issues with the list formatter.
Comment by JavaWoman
2005-01-07 19:51:23
Well, looking at the formatter code, I've already concluded that quite likely tags aren't always properly closed (and there are other problems with recognizing markup, obviously). But a real solution would require a complete overhaul of the formatter code ... I'd prefer to postpone that a little. :)
Comment by JeroenJansen
2005-02-16 14:21:08
Have you omitted the ReturnSafeHTML($out); on purpose at the end?
Comment by JavaWoman
2005-02-17 07:57:03
@Jeroen,
- First, all parameters have any tags stripped with strip_tags
- Numeric parameters are accepted only if they really are numeric
- Cell contents are fed through the Formatter
The idea was to sanitize *input* rather than generated output (where safeHTML might strip things that are not user in put but actually generated by the action).
I suspect it isn't quite complete though... will review again.
Comment by HenkDaalder
2006-05-04 16:30:03
I noticed a difference in the handling of wikinames
if you enter a new wikiname (with the proper capitals) on a page and click that link lateron. The new page is opened in the editor, with the pagename with all lower case characters.
The new link to the new page is still with capaitals in it, but the capitals are lost in the adres when the new page is edited for the first time.
Comment by WazoO
2006-05-05 02:05:17
Version 1.1.6.1 installed .... although much beat upon, much modified, added to, etc. ... the CamelCase stuff works just fine on my install .... the page Title, the URL, both fire up with the page 'name' just as found on the 'generated' link ....
Not sure what this has to do with TableAction though .....
Comment by LightMan
2007-11-07 09:57:58
The instructions above reference changes to the function Action in /wikka.php, however the Action function has been moved to ./libs/Wakka.class.php. Once the above edits were made to the Action function in this new location, the new syntax seems to work as documented.
Comment by NilsLindenberg
2007-11-07 13:10:41
I have updated the links to the right place. LightMan, don't be afriad to change them yourself, it's a wiki.
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki