Formatter: CSV

Compatible with: R6.1
Current version: 0.3 (beta)
Credits: http://docs.wikkawiki.org/FormatterCSV

This formatter converts inline CSV data into a table.

1. Features

  • supports both comma (default) and semicolon as separator
  • rows of the table alternate background color
  • column alignment: \left\, /right/, |center| or default
  • table headers/footers using wiki style ==heading markers==
  • add comments in between data by starting a line with (#); blank lines are ignored
  • cells can be surrounded in double quotes ("); text in quotes is preserved e.g., whitespace, no splitting on the separator
  • it is possible to escape a separator with a backslash (\), that should appear in text
  • wiki links in cell text are linked to their internal wiki pages

2. Usage

Call via (csv [delim=[comma|semicolon])


%%(csv delim="semicolon") source%%


formatter/highlight/csv.php

<?php

/*
    converts inline csv data into a table

    %%(csv
        [delim="comma|semicolon"]
        )
    content
    %%

    https://wackowiki.org/doc/Dev/PatchesHacks/CSV
*/

$options['delim']    ??= 'comma';

$delim    = match($options['delim']) {
    'semicolon'    => ';',
    default        => ',',
};

$blanks        = 0;
$csv_lines    = preg_split('/[\n]/', $text);

// asserts what precedes the ; is not a backslash \\\\, doesn't account for \\; (escaped backslash semicolon)
// https://stackoverflow.com/questions/40479546/how-to-split-on-white-spaces-not-between-quotes

// split on delimiter but not between quotes
$regex_split    = '/(?<!\\\\)' . $delim . '(?=(?:[^\"]*([\"])[^\"]*\\1)*[^\"]*$)/';
$regex_escaped    = '/\\\\' . $delim . '/';

$tpl->enter('r_');

foreach ($csv_lines as $i => $line)
{
    if (preg_match('/^#|^\s*$/', $line))
    {
        $blanks++;
        continue;
    }

    $tpl->commit = true; // alternation hack

    $tpl->enter('c_');

    foreach (preg_split($regex_split, $line) as $r => $field)
    {
        $tpl->commit = true; // alternation hack

        if ($i == $blanks)
        {
            $class[$r] = ''; // 'padding: 1px 10px 1px 10px; ';
        }

        if (preg_match('/^\"?\s*==(.*)==\s*\"?$/', $field, $header))
        {
            $title[$r] = $header[1];

            if (preg_match('/([\/\\\\|])(.*)\\1$/', $title[$r], $align))
            {
                $class[$r] .= match($align[1]) {
                    '/'        => 't-right',
                    '\\'    => 't-left',
                    '|'        => 't-center',
                };

                $title[$r] = $align[2];
            }

            $tpl->h_class    = $class[$r];
            $tpl->h_title    = $title[$r];
            continue;
        }

        // if a cell is blank, echo NBSP
        if (preg_match('/^\s*$/', $field))
        {
            $tpl->t_class    = $class[$r];
            $tpl->t_cell    = "\u{00A0}";
            continue;
        }
        // extract the cell out of it's quotes
        else if (preg_match('/^\s*(\"?)(.*?)\\1\s*$/', $field, $matches))
        {
            if ($matches[1] == '"')
            {
                #$style[$r]    = 'white-space: pre; ' . $style[$r];
                $cell        = $matches[2];
            }
            else
            {
                $cell        = preg_replace($regex_escaped, $delim, $matches[2]);
            }

            // [[wiki link]]
            if (preg_match_all('/\[\[([[:alnum:]]+)\]\]/', $cell, $all_links))
            {
                $linked = $cell;

                foreach ($all_links[1] as $camel_link)
                {
                    $linked = preg_replace('/\[\[' . $camel_link . '\]\]/', $this->link($camel_link), $linked);
                }
            }
            // [[url label]]
            else if (preg_match_all('/\[\[(.*?)\]\]/', $cell, $all_links))
            {
                $linked = $cell;

                foreach ($all_links[1] as $url_link)
                {
                    if(preg_match('/^(\S+)(\s+(.+))?$/us', $url_link, $matches))
                    {
                        $url    = $matches[1] ?? '';
                        $text    = $matches[3] ?? '';
                        $linked    = preg_replace('#\[\[' . $matches[0] . '\]\]#', $this->link($url, '', $text), $linked);
                    }
                }
            }
            else
            {
                $linked = Ut::html($cell);
            }

            $tpl->t_class    = $class[$r];
            $tpl->t_cell    = $linked;
            continue;
        }

        $tpl->t_class    = $class[$r];
        $tpl->t_cell    = 'ERROR!';
    }

    $tpl->leave();    // c_
}

$tpl->leave();    // r_

formatter/highlight/template/csv.tpl

[ === main === ]
<ignore>
	<!--notypo-->
	<table class="usertable">
		<tbody>
		[= r _ =
			[ ' commit | void ' ]
			<tr>
			[= c _ =
				[ ' commit | void ' ]
				[= h _ =
					<th class="[ ' class ' ]">[ ' title | e ' ]</th>
				=]
				[= t _ =
					<td class="[ ' class ' ]">[ ' cell ' ]</td>
				=]
			=]
			</tr>
		=]
		</tbody>
	</table>
	<!--/notypo-->
</ignore>	

2.1. local usage of repository

Symlink the formatter from community folder in the src/formatter/highlight folder and the corresponding template in the src/formatter/highlight/template folder in your repo.

ln -s ../../../community/formatter/highlight/csv.php csv.php
ln -s ../../../../community/formatter/highlight/template/csv.tpl csv.tpl	

3. Example

%%(csv delim="semicolon")
; ==/First Name/==; ==\Last Name\==; ==|Address|==; == Age ==
==Norwegian==; Sigurd; Nordmo; [[Viggo]],\, Hansteens allé 119\; 1524 MOSS; 38
==Swede==; Chanelle; Blomqvist; Överhogdal 95\; 282 02 HÖRJA; 61
==German==; Leah; Ackermann; "Landhausstraße 73; 15702 Königs Wusterhausen"; 25
# Comments are possible. Yes, the following person is a Hobbit!
==Hobbit==; Celendine; "Gam gee"; ; 216
%%


  First Name Last Name Address Age
Norwegian Sigurd Nordmo Viggo?,\, Hansteens allé 119; 1524 MOSS 38
Swede Chanelle Blomqvist Överhogdal 95; 282 02 HÖRJA 61
German Leah Ackermann Landhausstraße 73; 15702 Königs Wusterhausen 25
Hobbit Celendine Gam gee   216

4. Changelog

0.1 Backported from WikkaWiki
0.2 Simplified & updated to work with PHP 8 (Null coalescing assignment operator, Match expression)
0.3 Added template

5. To Do

  • fix link parsing
  • add additional $options['parameter'] and CSS class styles
  • modify the formatter for your purpose