false, 'store_function' => 'tikiwiki_save_data', 'load_function' => 'tikiwiki_parse_data', 'verify_function' => 'tikiwiki_verify_data', 'description' => 'TikiWiki Syntax Format Parser', 'edit_label' => 'Tiki Wiki Syntax', 'edit_field' => PLUGIN_GUID_TIKIWIKI, 'help_page' => 'TikiWikiSyntax', 'plugin_type' => FORMAT_PLUGIN, 'linebreak' => "\r\n", ]; $gLibertySystem->registerPlugin( PLUGIN_GUID_TIKIWIKI, $pluginParams ); /** * tikiwiki_save_data */ function tikiwiki_save_data( &$pParamHash ) { static $parser; if( empty( $parser ) ) { $parser = new TikiWikiParser(); } } function tikiwiki_verify_data( &$pParamHash ) { $errorMsg = null; $pParamHash['content_store']['data'] = $pParamHash['edit']; return $errorMsg; } function tikiwiki_parse_data( &$pParseHash, &$pCommonObject ) { global $gBitSystem; $ret = ''; static $parser; if( empty( $parser ) ) { $parser = new TikiWikiParser(); } $ret = $parser->parseData( $pParseHash, $pCommonObject ); return $ret; } /** * TikiWikiParser * * @package kernel */ class TikiWikiParser extends BitBase { function TikiWikiParser() { parent::__construct(); } // This function handles wiki codes for those special HTML characters // that textarea won't leave alone. function parseHtmlchar( &$pData ) { // cleaning some user input $pData = preg_replace( "/&(?!([a-z]{1,7};))/", "&", $pData ); // oft-used characters (case insensitive) $patterns = [ "~bull~" => "•", "~bs~" => "\", "~hs~" => " ", "~amp~" => "&", "~ldq~" => "“", "~rdq~" => "”", "~lsq~" => "‘", "~rsq~" => "’", "~copy~" => "©", "~c~" => "©", "~--~" => "—", " -- " => " — ", "~lt~" => "<", "~gt~" => ">", "~euro~" => "€", ]; foreach( $patterns as $pattern => $replace ) { $pData = str_ireplace( $pattern, $replace, $pData ); } // add an easy method to clear floats $pData = preg_replace( "/(\r|\n)?~clear~/i", '
', $pData ); // HTML numeric character entities $pData = preg_replace( "/~([0-9]+)~/", "&#$1;", $pData ); } function getLinks( $pData ) { $links = []; // Match things like [...], but ignore things like [[foo]. // -Robin if( preg_match_all( "/(?"; $lines = explode( "\n", str_replace( "\\n", "
", $table_data )); $row = 0; foreach( $lines as $line ) { if(( substr( $line, 0, 1 ) == '|' ) || ( substr( $line, 0, 1 ) == '!' )) { if( preg_match( '/^\|\+\s*(.+)$/', $line, $row_matches )) { $content .= "$row_matches[1]"; } elseif( preg_match( '/^\|-\s*(.+)?$/', $line, $row_matches )) { if( $row ) { $content .= ''; $row++; } else { $row = 1; } if( !empty( $row_matches[1] )) { $row_matches[1] = preg_replace( $xhtmlfix['pattern'], $xhtmlfix['replace'], trim( $row_matches[1] )); $content .= ""; } else { $content .= ''; } } elseif( preg_match( '/^([\|!])\s*([^\|]+\s*\|)?\s*(.*)$/', $line, $row_matches )) { if( !$row ) { $content .= ''; $row = 1; } if( !empty( $row_matches[2] )) { $row_matches[2] = preg_replace( $xhtmlfix['pattern'], $xhtmlfix['replace'], trim( $row_matches[2] )); } $td = 't'.(( $row_matches[1] == '!' ) ? 'h' : 'd' ); $content .= "<$td".(( !empty( $row_matches[2] )) ? ' '.trim( substr( $row_matches[2], 0, -1 )) : '' ).'>'.$row_matches[3].""; } else { $content .= ""; } } else { $content .= ""; } } $content .= ''; $pData = str_replace( $matches[0], $content, $pData ); } return $pData; } function parseData( $pParseHash, &$pCommonObject ) { global $gBitSystem, $gLibertySystem, $gBitUser, $page; $data = $pParseHash['data']; $contentId = $pParseHash['content_id']; // this is used for setting the links when section editing is enabled $section_count = 1; if( $gBitSystem->isPackageActive( 'wiki' ) ) { // force wiki page to load global $gLibertySystem; $gLibertySystem->getContentClassName( 'bitpage' ); } // only strip out html if needed if( $gBitSystem->isFeatureActive( 'content_allow_html' ) || $gBitSystem->isFeatureActive( 'content_force_allow_html' )) { // we allow html unconditionally with this parser } else { // we are parsing this page and we either have no way of checking permissions or we have no need for html $data = htmlspecialchars( $data, ENT_NOQUOTES, 'UTF-8' ); } // Extract [link] sections (to be re-inserted later) $noparsedlinks = []; // This section matches [...]. // Added handling for [[foo] sections. -rlpowell preg_match_all("/(?parseHtmlchar( $data ); //$data = strip_tags($data); // BiDi markers $bidiCount = 0; $bidiCount = preg_match_all("/(\{l2r\})/", $data, $pages); $bidiCount += preg_match_all("/(\{r2l\})/", $data, $pages); $data = preg_replace("/\{l2r\}/", "
", $data); $data = preg_replace("/\{r2l\}/", "
", $data); $data = preg_replace("/\{lm\}/", "‎", $data); $data = preg_replace("/\{rm\}/", "‏", $data); // Parse MediaWiki-style pipe syntax tables. if(( strpos( $data, "{|" ) === 0 || strpos( $data, "\n{|" ) !== false ) && strpos( $data, "\n|}" ) !== false ) { $data = $this->parseMediawikiTables($data); } // ============================================= this should go - xing // Replace dynamic variables // Dynamic variables are similar to dynamic content but they are editable // from the page directly, intended for short data, not long text but text // will work too // Now won't match HTML-style '%nn' letter codes. /* if (preg_match_all("/%([^% 0-9][^% 0-9][^% ]*)%/",$data,$dvars)) { // remove repeated elements $dvars = array_unique($dvars[1]); // Now replace each dynamic variable by a pair composed of the // variable value and a text field to edit the variable. Each foreach($dvars as $dvar) { $query = "select `data` from `".BIT_DB_PREFIX."liberty_dynamic_variables` where `name`=?"; $result = $this->mDb->query($query,Array($dvar)); if($result->numRows()) { $value = $result->fetchRow(); $value = $value["data"]; } else { //Default value is null $value = "NaV"; } // Now build 2 divs $id = 'dyn_'.$dvar; if( $gBitUser->hasPermission( 'p_wiki_edit_dynvar' ) ) { $span1 = "$value"; $span2 = ""; } else { $span1 = "$value"; $span2 = ''; } $html = $span1.$span2; //It's important to replace only once $dvar_preg = preg_quote( $dvar ); $data = preg_replace("+%$dvar_preg%+",$html,$data,1); //Further replacements only with the value $data = str_replace("%$dvar%",$value,$data); } //At the end put an update button //
$data='
'.$data.'
'; } */ // Replace boxes - add a new line that we can have something like: ^!heading^ without the need for a \n after the initial ^ - \n will be removed below $data = preg_replace("/\^([^\^]+)\^/", "
\n$1
", $data); // Replace colors ~~color:text~~ $data = preg_replace("/\~\~([^\:]+):([^\~]+)\~\~/", "$2", $data); // Replace background colors ++color:text++ $data = preg_replace("/\+\+([^\s][^\: ]+):([^\+]+)\+\+/", "$2", $data); // Underlined text $data = preg_replace("/===([^\=]+)===/", "$1", $data); // Center text $data = preg_replace("/::(.+?)::/", "
$1
", $data); // Line breaks $data = preg_replace('/%%%/', '
', $data); // reinsert hash-replaced links into page foreach ($noparsedlinks as $np) { $data = str_replace($np["key"], $np["data"], $data); } $links = $this->getLinks( $data ); // Note that there're links that are replaced foreach( $links as $link ) { $attributes = strstr( $link, $_SERVER["SERVER_NAME"] ) || !strstr( $link, '//' ) ? '' : 'class="external"'; // comments and anonymously created pages get nofollow if( is_object( $pCommonObject ) && ( $pCommonObject::class == 'comments' || ( isset( $pCommonObject->mInfo['user_id'] ) && $pCommonObject->mInfo['user_id'] == ANONYMOUS_USER_ID ))) { $attributes .= ' rel="nofollow" '; } // The (?$1", $data ); $pattern = "/(?$link", $data ); } // Handle double square brackets. -rlpowell $data = str_replace( "[[", "[", $data ); // now that all links have been taken care of, we can replace all email addresses with the encoded form // this will also encode email addressed that have not been linked using [] $data = KernelTools::encode_email_addresses( $data ); if ($gBitSystem->getConfig('wiki_tables') != 'new') { // New syntax for tables if (preg_match_all("/\|\|(.*)\|\|/", $data, $tables)) { $maxcols = 1; $cols = []; for ($i = 0; $i < count($tables[0]); $i++) { $rows = explode('||', $tables[0][$i]); $col[$i] = []; for ($j = 0; $j < count($rows); $j++) { $cols[$i][$j] = explode('|', $rows[$j]); if (count($cols[$i][$j]) > $maxcols) $maxcols = count($cols[$i][$j]); } } for ($i = 0; $i < count($tables[0]); $i++) { $repl = ''; for ($j = 0; $j < count($cols[$i]); $j++) { $ncols = count($cols[$i][$j]); if ($ncols == 1 && !$cols[$i][$j][0]) continue; $repl .= ''; for ($k = 0; $k < $ncols; $k++) { $repl .= ''; } $repl .= '
'; } $repl .= '
'; $data = str_replace($tables[0][$i], $repl, $data); } } } else { // New syntax for tables // REWRITE THIS CODE if( preg_match_all( "/\|\|(.*?)\|\|/s", $data, $tables ) ) { $maxcols = 1; $cols = []; for( $i = 0; $i < count( $tables[0] ); $i++ ) { $rows = preg_split( "/(\n|\)/", $tables[0][$i] ); $col[$i] = []; for( $j = 0; $j < count( $rows ); $j++ ) { $rows[$j] = str_replace( '||', '', $rows[$j] ); $cols[$i][$j] = explode( '|', $rows[$j] ); if( count( $cols[$i][$j] ) > $maxcols ) { $maxcols = count( $cols[$i][$j] ); } } } for( $i = 0; $i < count( $tables[0] ); $i++ ) { $repl = ''; $th = preg_match( "#^~#", $cols[$i][0][0] ) && $cols[$i][0][0] = preg_replace( "#^~#", "", $cols[$i][0][0] ) ? true : false; for( $j = 0; $j < count( $cols[$i] ); $j++ ) { $ncols = count( $cols[$i][$j] ); if( $ncols == 1 && !$cols[$i][$j][0] ) { continue; } if( $j == 0 && $th ) { $repl .= ''; } else { $repl .= ''; } for( $k = 0; $k < $ncols; $k++ ) { $thd = ( $j == 0 && $th ) ? 'th' : 'td'; $repl .= "<$thd"; if( $k == $ncols - 1 && $ncols < $maxcols ) { $repl .= ' colspan="'.( $maxcols - $k ).'"'; } $repl .= ">".( str_replace( "\\n", "
", $cols[$i][$j][$k] ) ).""; } $repl .= ''; } $repl .= '
'; $data = str_replace($tables[0][$i], $repl, $data); } } } // Now tokenize the expression and process the tokens // Use tab and newline as tokenizing characters as well //// $lines = explode("\n", $data); $data = ''; $listbeg = []; $divdepth = []; $inTable = 0; // loop: process all lines foreach ($lines as $line) { // bitweaver now ignores leading space because it is *VERY* disturbing to unaware users - spiderr // unless 'feature_wiki_preserve_leading_blanks is set'. This is used for sites that have // migrated from TikiWiki and have lots of pages whose formatting depends on the presevation of leading spaces if (!$gBitSystem->isFeatureActive('wiki_preserve_leading_blanks')) { $line = trim( $line ); } // check if we are inside a table, if so, ignore monospaced and do // not insert
$inTable += substr_count($line, "isFeatureActive('wiki_monosp') && $inTable == 0) { // This is not list item -- must close lists currently opened while (count($listbeg)) $data .= array_shift($listbeg); // If the first character is space then // change spaces for   $line = '' . str_replace(' ', ' ', substr($line, 1)). ''; } // Title bars $line = preg_replace( "/\-\=([^=]+)\=\-/", "
$1
", $line ); // Monospaced text $line = preg_replace( "/-\+(.*?)\+-/", "$1", $line ); // Bold text $line = preg_replace( "/__(.*?)__/", "$1", $line ); // Italics $line = preg_replace( "/''(.*?)''/", "$1", $line ); // Definition lists $line = preg_replace( "/^;([^:]+):(.+)/", "
$1
$2
", $line ); // This line is parseable then we have to see what we have if (substr($line, 0, 3) == '---') { // This is not list item -- must close lists currently opened while (count($listbeg)) $data .= array_shift($listbeg); $line = '
'; } else { $litype = substr($line, 0, 1); if ($litype == '*' || $litype == '#') { $listlevel = $this->howManyAtStart($line, $litype); $liclose = ''; $addremove = 0; if ($listlevel < count($listbeg)) { while ($listlevel != count($listbeg)) $data .= array_shift($listbeg); if (substr(current($listbeg), 0, 5) != '') $liclose = ''; } elseif ($listlevel > count($listbeg)) { $listyle = ''; while ($listlevel != count($listbeg)) { array_unshift($listbeg, $litype == '*' ? '' : '' ); if ($listlevel == count($listbeg)) { $listate = substr($line, $listlevel, 1); if (($listate == '+' || $listate == '-') && !($litype == '*' && !strstr(current($listbeg), '') || $litype == '#' && !strstr(current($listbeg), ''))) { $thisid = 'id' . (int) ( microtime(true) * 1000000 ); $data .= '
[' . ($listate == '-' ? '+' : '-') . ']'; $listyle = ' id="' . $thisid . '" style="display:' . ($listate == '+' ? 'block' : 'none') . ';"'; $addremove = 1; } } $data .= $litype == '*' ? "" : ""; } $liclose = ''; } if ($litype == '*' && !strstr(current($listbeg), '') || $litype == '#' && !strstr(current($listbeg), '')) { $data .= array_shift($listbeg); $listyle = ''; $listate = substr($line, $listlevel, 1); if (($listate == '+' || $listate == '-')) { $thisid = 'id' . microtime() * 1000000; $data .= '
[' . ($listate == '-' ? '+' : '-') . ']'; $listyle = ' id="' . $thisid . '" style="display:' . ($listate == '+' ? 'block' : 'none') . ';"'; $addremove = 1; } $data .= $litype == '*' ? "" : ""; $liclose = ''; array_unshift($listbeg, $litype == '*' ? '' : ''); } $line = $liclose . '
  • ' . substr($line, $listlevel + $addremove); if (substr(current($listbeg), 0, 5) != '
  • ') array_unshift($listbeg, '' . array_shift($listbeg)); } elseif ($litype == '+') { // Must append paragraph for list item of given depth... $listlevel = $this->howManyAtStart($line, $litype); // Close lists down to requested level while ($listlevel < count($listbeg)) $data .= array_shift($listbeg); if (count($listbeg)) { if (substr(current($listbeg), 0, 5) != '') { array_unshift($listbeg, '' . array_shift($listbeg)); $liclose = '
  • '; } else $liclose = '
    '; } else $liclose = ''; $line = $liclose . substr($line, count($listbeg)); } else { // This is not list item -- must close lists currently opened while (count($listbeg)) $data .= array_shift($listbeg); // Get count of (possible) header signs at start $hdrlevel = $this->howManyAtStart($line, '!'); // If 1st char on line is '!' and its count less than 6 (max in HTML) if ($litype == '!' && $hdrlevel > 0 && $hdrlevel <= 6) { // OK. Parse headers here... $aclose = ''; $edit_link = ''; $addremove = 0; // Close lower level divs if opened for (;current($divdepth) >= $hdrlevel; array_shift($divdepth)) { $data .= '
  • '; } // May be spesial signs present after '!'s? $divstate = substr($line, $hdrlevel, 1); if ($divstate == '+' || $divstate == '-') { // OK. Must insert flipper after HEADER, and then open new div... $thisid = 'id' . microtime() * 1000000; $aclose = '[' . ($divstate == '-' ? '+' : '-') . ']'; $aclose .= '
    '; array_unshift($divdepth, $hdrlevel); $addremove = 1; } if( $gBitSystem->isFeatureActive( 'wiki_section_edit' ) && $gBitUser->hasPermission( 'p_wiki_update_page' ) ) { if( $hdrlevel == $gBitSystem->getConfig( 'wiki_section_edit' ) ) { $edit_url = WIKI_PKG_URL."edit.php?content_id=".$contentId."&section=".$section_count++; $edit_link = '['.KernelTools::tra( "edit" ).']'; } } $hTagLevel = $hdrlevel + 1; // there should only be 1

    per html document $line = $edit_link . "" . substr($line, $hdrlevel + $addremove) . "" . $aclose ; } elseif (!strcmp($line, "...page...")) { // Close lists and divs currently opened while (count($listbeg)) { $data .= array_shift($listbeg); } while (count($divdepth)) { $data .= '

    '; array_shift ($divdepth); } // Leave line unchanged... index.php will split wiki here $line = "...page..."; } else { // Usual paragraph. if ($inTable == 0) { $line .= '
    '; } } } } $data .= $line; } // Close lists may remains opened while (count($listbeg)) { $data .= array_shift($listbeg); } // Close header divs may remains opened for ($i = 1; $i <= count($divdepth); $i++) { $data .= '
    '; } // Close BiDi DIVs if any for ($i = 0; $i < $bidiCount; $i++) { $data .= ""; } $data = str_replace( "
    ", "", $data ); return $data; } }