<?php
/**
* This file implements the Wiki Tables plugin for b2evolution
*
* Wiki Tables
*
* b2evolution - {@link http://b2evolution.net/}
* Released under GNU GPL License - {@link http://b2evolution.net/about/gnu-gpl-license}
* @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
*
* @package plugins
* @ignore
*/
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
/**
* @package plugins
*/
class wikitables_plugin extends Plugin
{
var $code = 'b2evWiTa';
var $name = 'Wiki Tables';
var $priority = 15;
var $version = '7.2.3';
var $group = 'rendering';
var $short_desc;
var $long_desc;
var $help_topic = 'wiki-tables-plugin';
var $number_of_installs = 1;
/**
* Init
*/
function PluginInit( & $params )
{
// Load the parsers for wiki tables
require_once( dirname( __FILE__ ).'/_sanitizer.inc.php' );
require_once( dirname( __FILE__ ).'/_string_utils.inc.php' );
require_once( dirname( __FILE__ ).'/_utf_normal_util.inc.php' );
$this->short_desc = T_('Wiki Tables converter');
$this->long_desc = T_('You can create tables with accepted format:<br />
{| table start<br />
<br />
|- table row<br />
<br />
|} table end<br />
See manual for more.');
}
/**
* Define here default collection/blog settings that are to be made available in the backoffice.
*
* @param array Associative array of parameters.
* @return array See {@link Plugin::get_coll_setting_definitions()}.
*/
function get_coll_setting_definitions( & $params )
{
$default_params = array(
'default_post_rendering' => 'opt-out'
);
$tmp_params = array_merge( $params, $default_params );
return parent::get_coll_setting_definitions( $tmp_params );
}
/**
* Perform rendering
*
* @param array Associative array of parameters
* 'data': the data (by reference). You probably want to modify this.
* 'format': see {@link format_to_output()}. Only 'htmlbody' and 'entityencoded' will arrive here.
* @return boolean true if we can render something for the required output format
*/
function RenderItemAsHtml( & $params )
{
$content = & $params['data'];
// Parse wiki tables
if( stristr( $content, '<code' ) !== false || stristr( $content, '<pre' ) !== false || strstr( $content, '```' ) !== false )
{ // Call replace_content() on everything outside code/pre:
$content = callback_on_non_matching_blocks( $content,
'~(```|<(code|pre)[^>]*>).*?(\1|</\2>)~is',
array( $this, 'parse_tables' ) );
}
else
{ // No code/pre blocks, replace on the whole thing
$content = $this->parse_tables( $content );
}
return true;
}
/**
* Parse tables
*
* @param string Content
* @return string Content
*/
function parse_tables( $content )
{
$lines = explode( "\n", $content );
$out = '';
$td_history = array(); // Is currently a td tag open?
$last_tag_history = array(); // Save history of last lag activated (td, th or caption)
$tr_history = array(); // Is currently a tr tag open?
$tr_attributes = array(); // history of tr attributes
$has_opened_tr = array(); // Did this table open a <tr> element?
$indent_level = 0; // indent level of the table
foreach( $lines as $outLine )
{
$line = trim( $outLine );
if( $line === '' )
{ // empty line, go to next line
$out .= $outLine . "\n";
continue;
}
$first_character = $line[0];
$matches = array();
if( preg_match( '/^(:*)\{\|(.*)$/', $line, $matches ) )
{ // First check if we are starting a new table
$indent_level = strlen( $matches[1] );
$attributes = $this->fix_tag_attributes( $matches[2], 'table' );
$outLine = str_repeat( '<dl><dd>', $indent_level ) . "<table{$attributes}>";
array_push( $td_history, false );
array_push( $last_tag_history, '' );
array_push( $tr_history, false );
array_push( $tr_attributes, '' );
array_push( $has_opened_tr, false );
}
elseif( count( $td_history ) == 0 )
{ // Don't do any of the following
$out .= $outLine . "\n";
continue;
}
elseif( substr( $line, 0, 2 ) === '|}' )
{ // We are ending a table
$line = '</table>' . substr( $line, 2 );
$last_tag = array_pop( $last_tag_history );
if( !array_pop( $has_opened_tr ) )
{
$line = "<tr><td></td></tr>{$line}";
}
if( array_pop( $tr_history ) )
{
$line = "</tr>{$line}";
}
if( array_pop( $td_history ) )
{
$line = "</{$last_tag}>{$line}";
}
array_pop( $tr_attributes );
$outLine = $line . str_repeat( '</dd></dl>', $indent_level );
}
elseif( substr( $line, 0, 2 ) === '|-' )
{ // Now we have a table row
$line = preg_replace( '#^\|-+#', '', $line );
// Whats after the tag is now only attributes
$attributes = $this->fix_tag_attributes( $line, 'tr' );
array_pop( $tr_attributes );
array_push( $tr_attributes, $attributes );
$line = '';
$last_tag = array_pop( $last_tag_history );
array_pop( $has_opened_tr );
array_push( $has_opened_tr, true );
if ( array_pop( $tr_history ) )
{
$line = '</tr>';
}
if( array_pop( $td_history ) )
{
$line = "</{$last_tag}>{$line}";
}
$outLine = $line;
array_push( $tr_history, false );
array_push( $td_history, false );
array_push( $last_tag_history, '' );
}
elseif ( $first_character === '|' || $first_character === '!' || substr( $line, 0, 2 ) === '|+' )
{ // This might be cell elements, td, th or captions
if( substr( $line, 0, 2 ) === '|+' )
{
$first_character = '+';
$line = substr( $line, 1 );
}
$line = substr( $line, 1 );
if( $first_character === '!' )
{
$line = str_replace( '!!', '||', $line );
}
// Split up multiple cells on the same line.
$cells = explode( '||', $line );
$outLine = '';
// Loop through each table cell
foreach( $cells as $cell )
{
$previous = '';
if( $first_character !== '+' )
{
$tr_after = array_pop( $tr_attributes );
if( !array_pop( $tr_history ) )
{
$previous = "<tr{$tr_after}>\n";
}
array_push( $tr_history, true );
array_push( $tr_attributes, '' );
array_pop( $has_opened_tr );
array_push( $has_opened_tr, true );
}
$last_tag = array_pop( $last_tag_history );
if( array_pop( $td_history ) )
{
$previous = "</{$last_tag}>\n{$previous}";
}
if( $first_character === '|' )
{
$last_tag = 'td';
}
elseif ( $first_character === '!' )
{
$last_tag = 'th';
}
elseif ( $first_character === '+' )
{
$last_tag = 'caption';
}
else
{
$last_tag = '';
}
array_push( $last_tag_history, $last_tag );
// A cell could contain both parameters and data
$cell_data = explode( '|', $cell, 2 );
// Set attribute to allow render markdown inside tables:
$markdown_attribute = ' markdown="1"';
if( strpos( $cell_data[0], '[[' ) !== false )
{
$cell = "{$previous}<{$last_tag}{$markdown_attribute}>{$cell}";
}
elseif ( count( $cell_data ) == 1 )
{
$cell = "{$previous}<{$last_tag}{$markdown_attribute}>{$cell_data[0]}";
}
else
{
$attributes = $this->fix_tag_attributes( $cell_data[0], $last_tag );
$cell = "{$previous}<{$last_tag}{$markdown_attribute}{$attributes}>{$cell_data[1]}";
}
$outLine .= $cell;
array_push( $td_history, true );
}
}
$out .= $outLine . "\n";
}
// Closing open td, tr && table
while( count( $td_history ) > 0 )
{
if( array_pop( $td_history ) )
{
$out .= "</td>\n";
}
if( array_pop( $tr_history ) )
{
$out .= "</tr>\n";
}
if( !array_pop( $has_opened_tr ) )
{
$out .= "<tr><td></td></tr>\n";
}
$out .= "</table>\n";
}
if( substr( $out, -1 ) === "\n" )
{ // Remove trailing line-ending (b/c)
$out = substr( $out, 0, -1 );
}
if( $out === "<table>\n<tr><td></td></tr>\n</table>" )
{ // special case: don't return empty table
$out = '';
}
return $out;
}
/**
* Event handler: Called at the beginning of the skin's HTML HEAD section.
*
* Use this to add any HTML HEAD lines (like CSS styles or links to resource files (CSS, JavaScript, ..)).
*
* @param array Associative array of parameters
*/
function SkinBeginHtmlHead( & $params )
{
global $Collection, $Blog;
if( ! isset( $Blog ) || (
$this->get_coll_setting( 'coll_apply_rendering', $Blog ) == 'never' &&
$this->get_coll_setting( 'coll_apply_comment_rendering', $Blog ) == 'never' ) )
{ // Don't load css/js files when plugin is not enabled
return;
}
$this->require_css_async( 'wikitables.css', false, 'footerlines' );
}
/**
* Event handler: Called when ending the admin html head section.
*
* @param array Associative array of parameters
* @return boolean did we do something?
*/
function AdminEndHtmlHead( & $params )
{
$this->SkinBeginHtmlHead( $params );
}
/**
* Fix tag attributes
*
* @param string Attributes, e.g. 'style="color:green;" data-display-condition="cur=eur"'
* @param string Element name, e.g. 'tr', 'td', 'table'
* @return string Fixed attributes
*/
function fix_tag_attributes( $attributes, $element )
{
$extended_attributes = '';
if( $attributes !== '' &&
in_array( $element, array( 'table', 'tr', 'td', 'th' ) ) &&
preg_match_all( '#(^|\s)data-[^\s]+="[^"]*"+#', $attributes, $data_attributes ) )
{ // Allow extended attributes for several tags:
$extended_attributes = implode( '', $data_attributes[0] );
}
// Fix attributes by Sanitizer and append data-*
return $extended_attributes.Sanitizer::fixTagAttributes( $attributes, $element );
}
}
?>