You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

488 lines
18 KiB

<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
function wikiplugin_fancytable_info()
{
$tsOn = Table_Check::isEnabled();
if ($tsOn === true) {
$ts = new Table_Plugin();
$ts->createParams();
$tsparams = $ts->params;
unset($tsparams['server']);
} else {
$tsparams = [];
}
$params = array_merge(
[
'head' => [
'required' => false,
'name' => tra('Heading Row'),
'description' => tr('Header rows of the table. Use %0 to separate multiple rows.', '<code>>></code>'),
'default' => '',
'since' => '1'
],
'headclass' => [
'required' => false,
'name' => tra('Heading CSS Class'),
'description' => tra('CSS class to apply to the heading row.'),
'default' => '',
'since' => '1'
],
'headaligns' => [
'required' => false,
'name' => tra('Header Horizontal Alignment'),
'description' => tr(
'Horizontal alignments for header cells, separated by %0. Choices: %1',
'<code>|</code>',
'<code>left</code>, <code>right</code>, <code>center</code>, <code>justify</code>'
),
'default' => '',
'since' => '4.1',
'filter' => 'text',
],
'headvaligns' => [
'required' => false,
'name' => tra('Header Vertical Alignment'),
'description' => tr(
'Vertical alignments for header cells, separated by %0". Choices: %1',
'<code>|</code>',
'<code>top</code>, <code>middle</code>, <code>bottom</code>, <code>baseline</code>'
),
'default' => '',
'since' => '4.1',
'filter' => 'text',
],
'colwidths' => [
'required' => false,
'name' => tra('Column Widths'),
'description' => tr('Column widths followed by "px" for pixels or "%" for percentages. Each column
separated by %0.', '<code>|</code>'),
'default' => '',
'since' => '4.1'
],
'colaligns' => [
'required' => false,
'name' => tra('Cell Horizontal Align'),
'description' => tr(
'Table body column horizontal alignments, separated by %0. Choices: %1',
'<code>|</code>',
'<code>left</code>, <code>right</code>, <code>center</code>, <code>justify</code>'
),
'default' => '',
'since' => '4.1',
'filter' => 'text',
],
'colvaligns' => [
'required' => false,
'name' => tra('Cell Vertical Align'),
'description' => tr(
'Table body column vertical alignments, separated by %0. Choices: %1',
'<code>|</code>',
'<code>top</code>, <code>middle</code>, <code>bottom</code>, <code>baseline</code>'
),
'default' => '',
'since' => '4.1',
'filter' => 'text',
],
],
$tsparams
);
return [
'name' => tra('Fancy Table'),
'documentation' => 'PluginFancyTable',
'description' => tra('Create a formatted table that can be filtered and sorted'),
'prefs' => ['wikiplugin_fancytable'],
'body' => tr(
'Rows separated by %0 in the header; for the table body, one row per line. Cells separated by %1 (since Tiki4) or %2 in both cases.',
'<code>>></code>',
'<code>|</code>',
'<code>~|~</code>'
),
'iconname' => 'table',
'introduced' => 1,
'tags' => [ 'basic' ],
'params' => $params,
];
}
function wikiplugin_fancytable($data, $params)
{
global $prefs;
$tagremove = [];
$pluginremove = [];
static $iFancytable = 0;
++$iFancytable;
extract($params, EXTR_SKIP);
if (empty($sortable)) {
$sortable = 'n';
}
$msg = '';
if ((isset($sortable) && $sortable != 'n')) {
if (Table_Check::isEnabled()) {
$ts = new Table_Plugin();
$ts->setSettings(
'wpfancytable' . $iFancytable,
'n',
$sortable,
isset($sortList) ? $sortList : null,
isset($tsortcolumns) ? $tsortcolumns : null,
isset($tsfilters) ? $tsfilters : null,
isset($tsfilteroptions) ? $tsfilteroptions : null,
isset($tspaginate) ? $tspaginate : null,
isset($tscolselect) ? $tscolselect : null,
null,
null,
isset($tstotals) ? $tstotals : null,
isset($tstotalformat) ? $tstotalformat : null,
isset($tstotaloptions) ? $tstotaloptions : null
);
if (is_array($ts->settings)) {
$ts->settings['resizable'] = true;
Table_Factory::build('plugin', $ts->settings);
$sort = true;
} else {
$sort = false;
}
} else {
$sort = false;
}
if ($sort === false) {
if ($prefs['feature_jquery_tablesorter'] === 'n') {
$msg = '<em>' . tra('The jQuery Sortable Tables feature must be activated for the sort feature to work.')
. '</em>';
} elseif ($prefs['javascript_enabled'] !== 'y') {
$msg = '<em>' . tra('JavaScript must be enabled for the sort feature to work.') . '</em>';
} else {
$msg = '<em>' . tra('Unable to load the jQuery Sortable Tables feature.') . '</em>';
}
}
} else {
$sort = false;
}
//Start the table
$style = $sort === true ? ' style="visibility:hidden"' : '';
$wret = '<div class="table-responsive"><div id="wpfancytable' . $iFancytable . '-div"' . $style
. ' class="ts-wrapperdiv">' . "\r\t";
$wret .= '<table class="table table-striped table-hover normal" id="wpfancytable' . $iFancytable . '">' . "\r\t";
//Header
if (isset($head)) {
//set header class
if (! empty($headclass)) {
$tdhdr = "\r\t\t\t" . '<th class="' . $headclass . '"';
} else {
$tdhdr = "\r\t\t\t<th";
}
//replace tiki tags, plugins and other enclosing characters with hash strings before creating table so that any
//pipes (| or ~|~) inside aren't mistaken for cell dividers
preprocess_section($head, $tagremove, $pluginremove);
if ($sort) {
$type = 'hs';
} else {
$type = 'h';
}
//now create header table rows
$headrows = process_section(
$head,
$type,
'>>',
$tdhdr,
'</th>',
isset($colwidths) ? $colwidths : '',
isset($headaligns) ? $headaligns : '',
isset($headvaligns) ? $headvaligns : ''
);
//restore original tags and plugin syntax
$headhtml = $headrows['html'];
postprocess_section($headhtml, $tagremove, $pluginremove);
$wret .= '<thead>' . $headhtml . "\r\t" . '</thead>' . "\r\t";
}
//Body
//replace tiki tags, plugins and other enclosing characters with hash strings before creating table so that any
//pipes (| or ~|~) inside aren't mistaken for cell dividers
preprocess_section($data, $tagremove, $pluginremove);
if ($sort) {
$type = 'bs'; //sortable body rows - do not assign odd/even class to these since jquery will do it
} else {
$type = 'b'; //plain body rows
}
//now create table body rows
$bodyrows = process_section(
$data,
$type,
"\n",
"\r\t\t\t" . '<td',
'</td>',
isset($colwidths) ? $colwidths : '',
isset($colaligns) ? $colaligns : '',
isset($colvaligns) ? $colvaligns : ''
);
//restore original tags and plugin syntax
$bodyhtml = $bodyrows['html'];
postprocess_section($bodyhtml, $tagremove, $pluginremove);
//end the tbody
$wret .= '<tbody>' . $bodyhtml . "\r\t" . '</tbody>';
if (isset($ts->settings)) {
$footer = Table_Totals::getTotalsHtml($ts->settings, $bodyrows['cols']);
if ($footer) {
$wret .= $footer;
}
}
$wret .= "\r" . '</table></div></div>' . "\r" . $msg;
return $wret;
}
/**
* To keep pipes (| or ~|~) inside other Tiki tags or plugins, or other "enclosing" characters from being mistaken as
* cell dividers, replace them with hash strings until after the table is created
*
* @param string $data Header or body text with tags and plugins removed
* @param array $tagremove Key => value pairs of Hash => data for tags or enclosing characters
* @param array $pluginremove Key => value pairs of Hash => data for plugins
*/
function preprocess_section(&$data, &$tagremove, &$pluginremove)
{
$parserlib = TikiLib::lib('parser');
//first replace plugins with hash strings since they may contain pipe characters
$parserlib->plugins_remove($data, $pluginremove);
//then replace tags or other enclosing charcters that could enclose a pipe (| or ~|~) character with a hash string
$tikilib = TikiLib::lib('tiki');
$tags = [
[
'start' => '\(\(', // (( ))
'end' => '\)\)',
],
[
'start' => '\[', // [ ]
'end' => '\]',
],
[
'start' => '~np~', // ~np~ ~/np~
'end' => '~\/np~',
],
[
'start' => '~tc~', // ~tc~ ~/tc~
'end' => '~\/tc~',
],
[
'start' => '~hc~', // ~hc~ ~/hc~
'end' => '~\/hc~',
],
[
'start' => '\^', // ^ ^
'end' => '\^',
],
[
'start' => '__', // __ __
'end' => '__',
],
[
'start' => '\:\:', // :: ::
'end' => '\:\:',
],
[
'start' => '\:\:\:', // ::: :::
'end' => '\:\:\:',
],
[
'start' => '\'\'', // '' ''
'end' => '\'\'',
],
[
'start' => '-\+', // -+ +-
'end' => '\+-',
],
[
'start' => '-=', // -= =-
'end' => '=-',
],
[
'start' => '===', // === ===
'end' => '===',
],
[
'start' => '--', // -- --
'end' => '--',
],
[
'start' => '\(', // ( )
'end' => '\)',
],
[
'start' => '\"', // " "
'end' => '\"',
],
];
$count = count($tags) - 1;
$pattern = '/(';
foreach ($tags as $key => $tag) {
$pattern .= $tag['start'] . '(?:(?!' . $tag['end'] . ').)*' . $tag['end'];
if ($key < $count) {
$pattern .= '|';
}
}
$pattern .= ')/';
preg_match_all($pattern, $data, $matches);
foreach ($matches[0] as $match) {
$tagremove['key'][] = '§' . md5($tikilib->genPass()) . '§';
$tagremove['data'][] = $match;
$data = isset($tagremove['data']) ? str_replace($tagremove['data'], $tagremove['key'], $data) : $data;
}
}
/**
* Process header or body string to convert to table rows
*
* @param string $data Header or body string to be broken down into table rows
* @param string $type Indicates whether rows are sortable or not, which impacts class assigned
* @param string $line_sep Row separator (>> for header and \n for body)
* @param string $cellbeg HTML <th or <td tag and appropriate class attribute
* @param string $cellend HTML </th> or </td> ending tags
* @param numeric $widths User input for column widths
* @param string $aligns User input for horizontal text alignment within cells
* @param string $valigns User input for vertical text alignment within cells
*
* @return string $wret HTML string for the header or body rows processed
*/
function process_section($data, $type, $line_sep, $cellbeg, $cellend, $widths, $aligns, $valigns)
{
$separator = strpos($data, '~|~') === false ? '|' : '~|~';
$lines = explode($line_sep, $data);
$widths = ! empty($widths) ? explode('|', $widths) : '';
$aligns = ! empty($aligns) ? explode('|', $aligns) : '';
$valigns = ! empty($valigns) ? explode('|', $valigns) : '';
$trbeg = "\r\t\t<tr>";
$trend = "\r\t\t</tr>";
$l = 0;
$rnum1 = '';
$rnum2 = '';
$wret = '';
$row_is_odd = true;
//Each row
foreach ($lines as $line) {
$line = trim($line);
if (strlen($line) > 0) {
if ($type == 'b') {
// if ($row_is_odd) {
// $cellbeg = "\r\t\t\t" . '<td class="odd"';
// $row_is_odd = false;
// } else {
// $cellbeg = "\r\t\t\t" . '<td class="even"';
// $row_is_odd = true;
// }
//don't set odd/even class if tablesorter is on because jquery will add it
//and the classes won't alternate correctly if added here too
} elseif ($type == 'bs') {
$cellbeg = "\r\t\t\t" . '<td';
}
$c = 0;
$row = '';
$parts = explode($separator, $line);
//Each column within a row
foreach ($parts as $column) {
$colnum = 'col' . $c;
$colspan = '';
$rowspan = '';
/*
* Match / (colspan) or \ (rowspan) characters in whichever order at the beginning of the cell
* $matches[0][0] shows entire match. There are 3 strings being matched in the preg_match_all,
* so $matches[1][0] shows the character matched for the first string (\), $matches[2][0] the
* second character (/), and $matches[3][0] the third character (\)
*/
if (preg_match_all("/^(\\\\)*(\/)*(\\\\)*/", $column, $matches)) {
$column = substr($column, strlen($matches[0][0]));
//create colspan if there are / characters at beginning of cell
if ($matches[2][0]) {
$colspan = ' colspan="' . substr_count($matches[0][0], $matches[2][0]) . '"';
}
//create rowspan if there are \ characters at beginning of cell
if ($matches[1][0] || $matches[3][0]) {
if ($matches[1][0]) {
$rnum1 = substr_count($matches[0][0], $matches[1][0]);
}
if ($matches[3][0]) {
$rnum2 = substr_count($matches[0][0], $matches[3][0]);
}
$rnum = $rnum1 + $rnum2;
$rowspan = ' rowspan="' . $rnum . '"';
//If there's another rowspan still in force, bump up the column number
if (isset(${$colnum}['col']) && ${$colnum}['col'] == $c) {
if ((${$colnum}['span'] - ($l - ${$colnum}['line'])) > 0) {
$c++;
}
}
//Note the info for this new rowspan
${$colnum}['col'] = $c;
${$colnum}['line'] = $l;
${$colnum}['span'] = $rnum;
}
}
//set column style
$colstyle = '';
if (! empty($widths) || ! empty($aligns) || ! empty($valigns)) {
//If there's another rowspan still in force, bump up the column number
if (isset(${$colnum}['col']) && ${$colnum}['col'] == $c && ($l > ${$colnum}['line'])) {
if ((${$colnum}['span'] - ($l - ${$colnum}['line'])) > 0) {
$c++;
}
}
$colstyle = ' style="';
$colstyle .= ! empty($widths[$c]) ? ' width: ' . $widths[$c] . ';' : '';
$colstyle .= ! empty($aligns[$c]) ? ' text-align: ' . $aligns[$c] . ';' : '';
$colstyle .= ! empty($valigns[$c]) ? ' vertical-align: ' . $valigns[$c] : '';
$colstyle .= '"';
}
$row .= $cellbeg . $colspan . $rowspan . $colstyle . '>' . $column . $cellend;
$c++;//increment column number
}
$wret .= $trbeg . $row . $trend;
}
$l++;//increment row number
}
$ret['html'] = $wret;
$ret['cols'] = count($parts);
return $ret;
}
/**
* Replace hash strings with original tag and plugin content
*
* @param string $data Header or body text processed into table rows with hash strings replacing
* tags and plugins
* @param array $tagremove Tag hash => data pairs for tags that were replaced with hash strings
* @param array $pluginremove Plugin Hash => data pairs for plugins that were replaced with hash strings
*/
function postprocess_section(&$data, &$tagremove, &$pluginremove)
{
//first restore tag strings
$parserlib = TikiLib::lib('parser');
if (
isset($tagremove['key']) and count($tagremove['key'])
and count($tagremove['key']) == count($tagremove['data'])
) {
$data = str_replace($tagremove['key'], $tagremove['data'], $data);
}
//then restore plugin strings
$parserlib->plugins_replace($data, $pluginremove);
//reset variables since this function is run for both the header and body
$tagremove = [];
$pluginremove = [];
}