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
There are 8 comments on this page. [Show comments]
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki