getAliasContaining($page, true); if (! $page_info && count($aliases) > 0) { $error_title = tra("Cannot create aliased page"); $error_msg = tra("You attempted to create the following page:") . " " . "$page.\n
\n"; $error_msg .= tra("That page is an alias for the following pages") . ": "; foreach ($aliases as $an_alias) { $error_msg .= '' . $an_alias['fromPage'] . ', '; } $error_msg .= "\n
\n"; $error_msg .= tra("If you want to create the page, you must first edit each of the pages above to remove the alias link they may contain. This link should look something like this"); $error_msg .= ": (alias($page))"; $access->display_error(page, $error_title, "", true, $error_msg); } } public function user_needs_to_specify_language_of_page_to_be_created($page, $page_info, $new_page_inherited_attributes = null) { global $prefs; if (isset($_REQUEST['need_lang']) && $_REQUEST['need_lang'] == 'n') { return false; } if ($prefs['feature_multilingual'] == 'n') { return false; } if ($page_info && isset($page_info['lang']) && $page_info['lang'] != '') { return false; } if (isset($_REQUEST['lang']) && $_REQUEST['lang'] != '') { return false; } if ( $new_page_inherited_attributes != null && isset($new_page_inherited_attributes['lang']) && $new_page_inherited_attributes['lang'] != '' ) { return false; } return true; } // translation functions public function isTranslationMode() { return $this->isUpdateTranslationMode() || $this->isNewTranslationMode(); } public function isNewTranslationMode() { global $prefs; if ($prefs['feature_multilingual'] != 'y') { return false; } if ( isset($_REQUEST['translationOf']) && ! empty($_REQUEST['translationOf']) ) { return true; } if ( isset($_REQUEST['is_new_translation']) && $_REQUEST['is_new_translation'] == 'y' ) { return true; } return false; } public function isUpdateTranslationMode() { return isset($_REQUEST['source_page']) && isset($_REQUEST['oldver']) && (! isset($_REQUEST['is_new_translation']) || $_REQUEST['is_new_translation'] == 'n') && isset($_REQUEST['newver']); } public function prepareTranslationData() { $this->setTranslationSourceAndTargetPageNames(); $this->setTranslationSourceAndTargetVersions(); } private function setTranslationSourceAndTargetPageNames() { $smarty = TikiLib::lib('smarty'); if (! $this->isTranslationMode()) { return; } $this->targetPageName = null; if (isset($_REQUEST['target_page'])) { $this->targetPageName = $_REQUEST['target_page']; } elseif (isset($_REQUEST['page'])) { $this->targetPageName = $_REQUEST['page']; } $smarty->assign('target_page', $this->targetPageName); $this->sourcePageName = null; if (isset($_REQUEST['translationOf']) && $_REQUEST['translationOf']) { $this->sourcePageName = $_REQUEST['translationOf']; } elseif (isset($_REQUEST['source_page'])) { $this->sourcePageName = $_REQUEST['source_page']; } $smarty->assign('source_page', $this->sourcePageName); if ($this->isNewTranslationMode()) { $smarty->assign('translationIsNew', 'y'); } else { $smarty->assign('translationIsNew', 'n'); } } private function setTranslationSourceAndTargetVersions() { global $_REQUEST, $tikilib; if (isset($_REQUEST['oldver'])) { $this->oldSourceVersion = $_REQUEST['oldver']; } else { // Note: -1 means a "virtual" empty version. $this->oldsourceVersion = -1; } if (isset($_REQUEST['newver'])) { $this->newSourceVersion = $_REQUEST['newver']; } else { // Note: version number of 0 means the most recent version. $this->newSourceVersion = 0; } } public function aTranslationWasSavedAs($complete_or_partial) { if ( ! $this->isTranslationMode() || ! isset($_REQUEST['save']) ) { return false; } // We are saving a translation. Is it partial or complete? if ($complete_or_partial == 'complete' && isset($_REQUEST['partial_save'])) { return false; } elseif ($complete_or_partial == 'partial' && ! isset($_REQUEST['partial_save'])) { return false; } return true; } /** * Convert rgb() color definiton to hex color definiton * * @param unknown_type $col * @return The hex representation */ public function parseColor(&$col) { if (preg_match("/^rgb\( *(\d+) *, *(\d+) *, *(\d+) *\)$/", $col, $parts)) { $hex = str_pad(dechex($parts[1]), 2, '0', STR_PAD_LEFT) . str_pad(dechex($parts[2]), 2, '0', STR_PAD_LEFT) . str_pad(dechex($parts[3]), 2, '0', STR_PAD_LEFT); $hex = '#' . TikiLib::strtoupper($hex); } else { $hex = $col; } return $hex; } /** * Utility for walk_and_parse to process links * * @param array $args the attributes of the link * @param array $text the link text * @param string $src output string * @param array $p ['stack'] = closing strings stack */ private function parseLinkTag(&$args, &$text, &$src, &$p) { global $prefs; $link = ''; $link_open = ''; $link_close = ''; /* * parse the link classes */ $cl_wiki = false; $cl_wiki_page = false; // Wiki page $cl_ext_page = false; // external Wiki page $cl_external = false; // external web page $cl_semantic = ''; // semantic link if ($prefs['feature_semantic'] === 'y') { $semantic_tokens = TikiLib::lib('semantic')->getAllTokens(); } else { $semantic_tokens = []; } $ext_wiki_name = ''; if (isset($args['class']) && isset($args['href'])) { $matches = []; preg_match_all('/([^ ]+)/', $args['class']['value'], $matches); $classes = $matches[0]; for ($i = 0, $count_classes = count($classes); $i < $count_classes; $i++) { $cl = $classes[$i]; switch ($cl) { case 'wiki': $cl_wiki = true; break; case 'wiki_page': $cl_wiki_page = true; break; case 'ext_page': $cl_ext_page = true; break; case 'external': $cl_external = true; break; default: // if the preceding class was 'ext_page', then we have the name of the external Wiki if ($i > 0 && $classes[$i - 1] == 'ext_page') { $ext_wiki_name = $cl; } if (in_array($cl, $semantic_tokens)) { $cl_semantic = $cl; } } } } /* * extract the target and the anchor from the href */ if (isset($args['href'])) { $href = urldecode($args['href']['value']); // replace pipe chars as that's the delimiter in a wiki/external link $href = str_replace('|', '%7C', $href); $matches = explode('#', $href); if (count($matches) == 2) { $target = $matches[0]; $anchor = '#' . $matches[1]; } else { $target = $href; $anchor = ''; } } else { $target = ''; $anchor = ''; } /* * treat invalid external Wikis as web links */ if ($cl_ext_page) { // retrieve the definitions from the database if ($this->external_wikis == null) { global $tikilib; $query = 'SELECT `name`, `extwiki` FROM `tiki_extwiki`'; $this->external_wikis = $tikilib->fetchMap($query); } // name must be set and defined if (! $ext_wiki_name || ! isset($this->external_wikis[$ext_wiki_name])) { $cl_ext_page = false; $cl_wiki_page = false; } }; /* * construct the links according to the defined classes */ if ($cl_wiki_page) { /* * link to wiki page -> (( )) */ $link_open = "($cl_semantic("; $link_close = '))'; // remove the html part of the target $target = preg_replace('/tiki\-index\.php\?page\=/', '', $target); // construct the link $link = $target; if ($anchor) { $link .= '|' . $anchor; } } elseif ($cl_ext_page) { /* * link to external Wiki page ((:)) */ $link_open = '(('; $link_close = '))'; // remove the extwiki definition from the target $def = preg_replace('/\$page/', '', $this->external_wikis[$ext_wiki_name]); $def = preg_quote($def, '/'); $target = preg_replace('/^' . $def . '/', '', $target); // construct the link $link = $ext_wiki_name . ':' . $target; if ($anchor) { $link .= '|' . $anchor; } } elseif ($cl_wiki && ! $cl_external && ! $target && strlen($anchor) > 0 && substr($anchor, 0, 1) == '#') { /* * inpage links [#] */ $link_open = '['; $link_close = ']'; // construct the link $link = $target = $anchor; $anchor = ''; } elseif ($cl_wiki && ! $cl_external) { /* * other tiki resources [] * -> articles, ... */ $link_open = '['; $link_close = ']'; // construct the link $link = $target; } elseif (! $cl_wiki && ! $cl_external && ! $text && isset($args['id']) && isset($args['id']['value'])) { /* * anchor */ $link_open = '{ANAME()}'; $link_close = '{ANAME}'; $link = $args['id']['value']; } else { /* * other links [] */ $link_open = '['; $link_close = ']'; /* * parse the rel attribute */ $box = ''; if (isset($args['class']) && isset($args['rel'])) { $matches = []; preg_match_all('/([^ ]+)/', $args['rel']['value'], $matches); $rels = $matches[0]; for ($i = 0, $count_rels = count($rels); $i < $count_rels; $i++) { $r = $rels[$i]; if (preg_match('/^box/', $r)) { $box = $r; } } } // construct the link $link = $target; if ($anchor) { $link .= $anchor; } // the box must be appended to the text if ($box) { $text .= '|' . $box; } } // convert links /* * flush the constructed link */ if ($link_open && $link_close) { $p['wiki_lbr']++; // force wiki line break mode // does the link text match the target? if ($target == trim($text)) { $text = ''; // make sure walk_and_parse() does not append any text. } else { $link .= '|'; // the text will be appended by walk_and_parse() } // process the tag and update the output $this->processWikiTag('a', $src, $p, $link_open, $link_close, true); $src .= $link; } } /** * Utility for walk_and_parse to process p and div tags * * @param bool $isPar True if we process a
, false if a
(.*?)<\/pre>/ims', [$this, 'parseToWikiPlugin'], $parsed); // rempve plugin wrappers
$parsed = $this->parse_html($parsed);
$parsed = preg_replace('/\{img\(? src=.*?img\/smiles\/icon_([\w\-]*?)\..*\}/im', '(:$1:)', $parsed); // "unfix" smilies
$parsed = preg_replace('/ /m', ' ', $parsed); // spaces
$parsed = preg_replace('/!(?:\d\.)+/', '!#', $parsed); // numbered headings
if ($prefs['feature_use_three_colon_centertag'] == 'y') { // numbered and centerd headings
$parsed = preg_replace('/!:::(?:\d\.)+ *(.*):::/', '!#:::\1:::', $parsed);
} else {
$parsed = preg_replace('/!::(?:\d\.)+ *(.*)::/', '!#::\1::', $parsed);
}
// remove empty center tags
if ($prefs['feature_use_three_colon_centertag'] == 'y') { // numbered and centerd headings
$parsed = preg_replace('/::: *:::\n/', '', $parsed);
} else {
$parsed = preg_replace('/:: *::\n/', '', $parsed);
}
// Put back htmlentities as normal char
$parsed = htmlspecialchars_decode($parsed, ENT_QUOTES);
return $parsed;
}
public function parseToWikiPlugin($matches)
{
if (count($matches) > 1) {
return nl2br($matches[1]);
}
}
/**
* Render html to send to ckeditor, including parsing plugins for wysiwyg editing
* From both wiki page source (for wysiwyg_htmltowiki) and "html" modes
*
* @param $inData string page data, can be wiki or mixed html/wiki
* @param bool $fromWiki set if converting from wiki page using "switch editor"
* @param bool $isHtml true if $inData is HTML, false if wiki
* @return string html to send to ckeditor
*/
public function parseToWysiwyg($inData, $fromWiki = false, $isHtml = false, $options = [])
{
global $tikiroot, $prefs;
$allowImageLazyLoad = $prefs['allowImageLazyLoad'];
$prefs['allowImageLazyLoad'] = 'n';
// Parsing page data for wysiwyg editor
$inData = $this->partialParseWysiwygToWiki($inData); // remove any wysiwyg plugins so they don't get double parsed
$parsed = preg_replace('/(!!*)[\+\-]/m', '$1', $inData); // remove show/hide headings
$parsed = preg_replace('/'/', '\'', $parsed); // catch single quotes at html entities
if (preg_match('/^\|\|.*\|\|$/', $parsed)) { // new tables get newlines converted to
then to %%%
$parsed = str_replace('
', "\n", $parsed);
}
$parsed = TikiLib::lib('parser')->parse_data(
$parsed,
array_merge([
'absolute_links' => true,
'noheaderinc' => true,
'suppress_icons' => true,
'ck_editor' => true,
'is_html' => ($isHtml && ! $fromWiki),
'process_wiki_paragraphs' => (! $isHtml || $fromWiki),
'process_double_brackets' => 'y'
], $options)
);
if ($fromWiki) {
$parsed = preg_replace('/^\s* [\s]*<\/p>\s*/iu', '', $parsed); // remove added empty
}
$parsed = preg_replace('/(.*?)<\/span>/im', '$1', $parsed); // remove spans round img's
// Workaround Wysiwyg Image Plugin Editor in IE7 erases image on insert http://dev.tiki.org/item3615
$parsed2 = preg_replace('/(<\/span>)<\/p>/is', '$1
', $parsed);
if ($parsed2 !== null) {
$parsed = $parsed2;
}
// Fix IE7 wysiwyg editor always adding absolute path
if (isset($_SERVER['HTTP_HOST'])) {
$search = '/(]+href=\")https?\:\/\/' . preg_quote($_SERVER['HTTP_HOST'] . $tikiroot, '/') . '([^>]+_cke_saved_href)/i';
} else {
$search = '/(]+href=\")https?\:\/\/' . preg_quote($_SERVER['SERVER_NAME'] . $tikiroot, '/') . '([^>]+_cke_saved_href)/i';
}
$parsed = preg_replace($search, '$1$2', $parsed);
if (! $isHtml) {
// Fix for plugin being the last item in a page making it impossible to add new lines (new text ends up inside the plugin)
$parsed = preg_replace('/<\/(span|div)>(<\/p>)?$/', '$1> $2', $parsed);
// also if first
$parsed = preg_replace('/^<(div|span) class="tiki_plugin"/', ' <$1 class="tiki_plugin"', $parsed);
}
$prefs['allowImageLazyLoad'] = $allowImageLazyLoad;
return $parsed;
}
/**
* Converts wysiwyg plugins into wiki.
* Also processes headings by removing surrounding
* Also used by ajax preview in Services_Edit_Controller
*
* @param string $inData page data - mostly html but can have a bit of wiki in it
* @return string html with wiki plugins
*/
public static function partialParseWysiwygToWiki($inData)
{
if (empty($inData)) {
return '';
}
// de-protect ck_protected comments
$ret = preg_replace('//i', '', $inData);
if (! $ret) {
$ret = $inData;
trigger_error(tr('Parse To Wiki %0: preg_replace error #%1', 1, preg_last_error()));
}
// remove the wysiwyg plugin elements leaving the syntax only remaining
$ret2 = preg_replace('/<(?:div|span)[^>]*syntax="(.*)".*end tiki_plugin --><\/(?:div|span)>/Umis', "$1", $ret);
// preg_replace blows up here with a PREG_BACKTRACK_LIMIT_ERROR on pages with "corrupted" plugins
if (! $ret2) {
trigger_error(tr('Parse To Wiki %0: preg_replace error #%1', 2, preg_last_error()));
} else {
$ret = $ret2;
}
// take away the
that f/ck introduces around wiki heading ! to have maketoc/edit section working
$ret2 = preg_replace('/
\s*!(.*)<\/p>/Umis', "!$1\n", $ret);
if (! $ret2) {
trigger_error(tr('Parse To Wiki %0: preg_replace error #%1', 3, preg_last_error()));
} else {
$ret = $ret2;
}
// strip the last empty
tag generated somewhere (ckeditor 3.6, Tiki 10)
$ret2 = preg_replace('/\s*
\s*<\/p>\s*$/Umis', "$1\n", $ret);
if (! $ret2) {
trigger_error(tr('Parse To Wiki %0: preg_replace error #%1', 4, preg_last_error()));
} else {
$ret = $ret2;
}
// convert tikicomment tags back to ~tc~tiki comments~/tc~
$ret2 = preg_replace('/(.*)<\/tikicomment>/Umis', '~tc~$1~/tc~', $ret);
if (! $ret2) {
trigger_error(tr('Parse To Wiki %0: preg_replace error #%1', 5, preg_last_error()));
} else {
$ret = $ret2;
}
return $ret;
}
// parse HTML functions
/**
* \brief Parsed HTML tree walker (used by HTML sucker)
*
* This is initial implementation (stupid... w/o any intellegence (almost :))
* It is rapidly designed version... just for test: 'can this feature be useful'.
* Later it should be replaced by well designed one :) don't bash me now :)
*
* \param &$c array -- parsed HTML
* \param &$src string -- output string
* \param &$p array -- ['stack'] = closing strings stack,
['listack'] = stack of list types currently opened
['first_td'] = flag: 'is was just before this '
['first_tr'] = flag: 'is was just before this '
*/
public function walk_and_parse(&$c, &$src, &$p, $head_url)
{
global $prefs;
// If no string
if (! $c) {
return;
}
$parserlib = TikiLib::lib('parser');
for ($i = 0; $i <= $c['contentpos']; $i++) {
$node = $c[$i];
// If content type 'text' output it to destination...
if ($node['type'] == 'text') {
if (! ctype_space($node['data'])) {
$add = $node['data'];
$noparsed = [];
$parserlib->plugins_remove($add, $noparsed);
$add = str_replace(["\r","\n"], '', $add);
$add = str_replace(' ', ' ', $add);
$add = str_replace('[', '[[', $add); // escape square brackets to prevent accidental wiki links
$parserlib->plugins_replace($add, $noparsed, true);
$src .= $add;
} else {
$src .= str_replace(["\n", "\r"], '', $node['data']); // keep the spaces
}
} elseif ($node['type'] == 'comment') {
$src .= preg_replace('//', "~/hc~\n", $node['data']));
} elseif ($node['type'] == 'tag') {
if ($node['data']['type'] == 'open') {
// Open tag type
// deal with plugins - could be either span of div so process before the switch statement
if (isset($node['pars']['plugin']) && isset($node['pars']['syntax'])) { // handling for tiki plugins
$src .= html_entity_decode($node['pars']['syntax']['value']);
$more_spans = 1;
$elem_type = $node['data']['name'];
$other_elements = 0;
$j = $i + 1;
while ($j < $c['contentpos']) { // loop through contents of this span and discard everything
if ($c[$j]['data']['name'] == $elem_type && $c[$j]['data']['type'] == 'close') {
$more_spans--;
if ($more_spans === 0) {
break;
}
// } else if ($c[$j]['data']['name'] == 'br' && $more_spans === 1 && $other_elements === 0) {
} elseif ($c[$j]['data']['name'] == $elem_type && $c[$j]['data']['type'] == 'open') {
$more_spans++;
} elseif ($c[$j]['data']['type'] == 'open' && $c[$j]['data']['name'] != 'br' && $c[$j]['data']['name'] != 'img' && $c[$j]['data']['name'] != 'input') {
$other_elements++;
} elseif ($c[$j]['data']['type'] == 'close') {
$other_elements--;
}
$j++;
}
$i = $j; // skip everything that was inside this span
}
$isPar = false; // assuming "div" when calling parseParDivTag()
switch ($node['data']['name']) {
// Tags we don't want at all.
case 'meta':
case 'link':
$node['content'] = '';
break;
case 'script':
$node['content'] = '';
if (! isset($node['pars']['src'])) {
$i++;
while ($node['type'] === 'text' || ($node['data']['name'] !== 'script' && $node['data']['type'] !== 'close' && $i <= $c['contentpos'])) {
$i++; // skip contents of script tag
}
}
break;
case 'style':
$node['content'] = '';
$i++;
while ($node['data']['name'] !== 'style' && $node['data']['type'] !== 'close' && $i <= $c['contentpos']) {
$i++; // skip contents of script tag
}
break;
// others we do want
case 'br':
if ($p['wiki_lbr']) { // "%%%" or "\n" ?
$src .= ' %%% ';
} else {
// close all wiki tags
foreach (array_reverse($p['wikistack']) as $wiki_arr) {
foreach (array_reverse($wiki_arr['end']) as $end) {
$src .= $end;
}
}
$src .= "\n";
// for lists, we must prepend '+' to keep the indentation
if ($p['listack']) {
$src .= str_repeat('+', count($p['listack']));
}
// reopen all wikitags
foreach ($p['wikistack'] as $wiki_arr) {
foreach ($wiki_arr['begin'] as $begin) {
$src .= $begin;
}
}
}
break;
case 'hr':
$src .= $this->startNewLine($src) . '---';
break;
case 'title':
$src .= "\n!";
$p['stack'][] = ['tag' => 'title', 'string' => "\n"];
break;
case 'p':
$isPar = true;
if ($src && $prefs['feature_wiki_paragraph_formatting'] !== 'y') {
$src .= "\n";
}
// no break
case 'div': // Wiki parsing creates divs for center
if (isset($node['pars'])) {
$this->parseParDivTag($isPar, $node['pars'], $src, $p);
} elseif (! empty($p['table'])) {
$src .= '%%%';
} else { // normal para or div
$src .= $this->startNewLine($src);
$p['stack'][] = ['tag' => $node['data']['name'], 'string' => "\n\n"];
}
break;
case 'span':
if (isset($node['pars'])) {
$this->parseSpanTag($node['pars'], $src, $p);
}
break;
case 'b':
$this->processWikiTag('b', $src, $p, '__', '__', true);
break;
case 'i':
$this->processWikiTag('i', $src, $p, '\'\'', '\'\'', true);
break;
case 'em':
$this->processWikiTag('em', $src, $p, '\'\'', '\'\'', true);
break;
case 'strong':
$this->processWikiTag('strong', $src, $p, '__', '__', true);
break;
case 'u':
$this->processWikiTag('u', $src, $p, '===', '===', true);
break;
case 'strike':
$this->processWikiTag('strike', $src, $p, '--', '--', true);
break;
case 'del':
$this->processWikiTag('del', $src, $p, '--', '--', true);
break;
case 'center':
if ($prefs['feature_use_three_colon_centertag'] == 'y') {
$src .= ':::';
$p['stack'][] = ['tag' => 'center', 'string' => ':::'];
} else {
$src .= '::';
$p['stack'][] = ['tag' => 'center', 'string' => '::'];
}
break;
case 'code':
$src .= '-+';
$p['stack'][] = ['tag' => 'code', 'string' => '+-'];
break;
case 'dd':
$src .= ':';
$p['stack'][] = ['tag' => 'dd', 'string' => "\n"];
break;
case 'dt':
$src .= ';';
$p['stack'][] = ['tag' => 'dt', 'string' => ''];
break;
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6':
$p['wiki_lbr']++; // force wiki line break mode
$hlevel = (int) $node['data']['name'][1];
if (isset($node['pars']['style']['value']) && strpos($node['pars']['style']['value'], 'text-align: center;') !== false) {
if ($prefs['feature_use_three_colon_centertag'] == 'y') {
$src .= $this->startNewLine($src) . str_repeat('!', $hlevel) . ':::';
$p['stack'][] = ['tag' => $node['data']['name'], 'string' => ":::\n"];
} else {
$src .= $this->startNewLine($src) . str_repeat('!', $hlevel) . '::';
$p['stack'][] = ['tag' => $node['data']['name'], 'string' => "::\n"];
}
} else { // normal para or div
$src .= $this->startNewLine($src) . str_repeat('!', $hlevel);
$p['stack'][] = ['tag' => $node['data']['name'], 'string' => "\n"];
}
break;
case 'pre':
$src .= "~pre~\n";
$p['stack'][] = ['tag' => 'pre', 'string' => "~/pre~\n"];
break;
case 'sub':
$src .= '{SUB()}';
$p['stack'][] = ['tag' => 'sub', 'string' => '{SUB}'];
break;
case 'sup':
$src .= '{SUP()}';
$p['stack'][] = ['tag' => 'sup', 'string' => '{SUP}'];
break;
case 'tt':
$src .= '{DIV(type="tt")}';
$p['stack'][] = ['tag' => 'tt', 'string' => '{DIV}'];
break;
case 's':
$src .= $this->processWikiTag('s', $src, $p, '--', '--', true);
break;
// Table parser
case 'table':
$src .= $this->startNewLine($src) . '||';
$p['stack'][] = ['tag' => 'table', 'string' => '||'];
$p['first_tr'] = true;
$p['table'] = true;
break;
case 'tr':
if (! $p['first_tr']) {
$this->startNewLine($src);
}
$p['first_tr'] = false;
$p['first_td'] = true;
$p['wiki_lbr']++; // force wiki line break mode
break;
case 'td':
if ($p['first_td']) {
$src .= '';
} else {
$src .= '|';
}
$p['first_td'] = false;
break;
// Lists parser
case 'ul':
$p['listack'][] = '*';
break;
case 'ol':
$p['listack'][] = '#';
break;
case 'li':
// Generate wiki list item according to current list depth.
$src .= $this->startNewLine($src) . str_repeat(end($p['listack']), count($p['listack']));
break;
case 'font':
// If color attribute present in tag
if (isset($node['pars']['color']['value'])) {
$src .= '~~' . $node['pars']['color']['value'] . ':';
$p['stack'][] = ['tag' => 'font', 'string' => '~~'];
}
break;
case 'img':
// If src attribute present in
tag
if (isset($node['pars']['src']['value'])) {
// Note what it produce (img) not {img}! Will fix this below...
if (strstr($node['pars']['src']['value'], 'http:')) {
$src .= '{img src="' . $node['pars']['src']['value'] . '"}';
} else {
$src .= '{img src="' . $head_url . $node['pars']['src']['value'] . '"}';
}
}
break;
case 'a':
if (isset($node['pars'])) {
// get the link text
$text = '';
if ($i < count($c)) {
$next_token = &$c[$i + 1];
if (isset($next_token['type']) && $next_token['type'] == 'text' && isset($next_token['data'])) {
$text = &$next_token['data'];
}
}
// parse the link
$this->parseLinkTag($node['pars'], $text, $src, $p);
}
// deactivated by mauriz, will be replaced by the routine above
// If href attribute present in tag
/*
if (isset($c[$i]['pars']['href']['value'])) {
if ( strstr( $c[$i]['pars']['href']['value'], 'http:' )) {
$src .= '['.$c[$i]['pars']['href']['value'].'|';
} else {
$src .= '['.$head_url.$c[$i]['pars']['href']['value'].'|';
}
$p['stack'][] = array('tag' => 'a', 'string' => ']');
}
if ( isset($c[$i]['pars']['name']['value'])) {
$src .= '{ANAME()}'.$c[$i]['pars']['name']['value'].'{ANAME}';
}
*/
break;
} // end switch on tag name
} else {
// This is close tag type. Is that smth we r waiting for?
switch ($node['data']['name']) {
case 'ul':
if (end($p['listack']) == '*') {
array_pop($p['listack']);
}
if (empty($p['listack'])) {
$src .= "\n";
}
break;
case 'ol':
if (end($p['listack']) == '#') {
array_pop($p['listack']);
}
if (empty($p['listack'])) {
$src .= "\n";
}
break;
default:
$e = end($p['stack']);
if (isset($e['tag']) && $node['data']['name'] == $e['tag']) {
$src .= $e['string'];
array_pop($p['stack']);
}
if ($node['data']['name'] === 'table') {
$p['table'] = false;
}
break;
}
// update the wiki stack
if (isset($e['wikitags']) && $e['wikitags']) {
for ($i_wiki = 0; $i_wiki < $e['wikitags']; $i_wiki++) {
array_pop($p['wikistack']);
}
}
// can we leave wiki line break mode ?
switch ($node['data']['name']) {
case 'a':
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6':
case 'tr':
$p['wiki_lbr']--;
break;
}
}
}
// Recursive call on tags with content...
if (! empty($node['content'])) {
if (substr($src, -1) != ' ') {
$src .= ' ';
}
$this->walk_and_parse($node['content'], $src, $p, $head_url);
}
}
if (substr($src, -2) == "\n\n") { // seem to always get too many line ends
$src = substr($src, 0, -2);
}
}
public function startNewLine(&$str)
{
if (strlen($str) && substr($str, -1) != "\n") {
$str .= "\n";
}
}
/**
* wrapper around zaufi's HTML sucker code just to use the html to wiki bit
*
* @param string $inHtml -- HTML in
* @return null|string
* @throws Exception
*/
public function parse_html(&$inHtml)
{
include(__DIR__ . '/../htmlparser/htmlparser.inc');
// Read compiled (serialized) grammar
$grammarfile = TIKI_PATH . '/lib/htmlparser/htmlgrammar.cmp';
if (! $fp = @fopen($grammarfile, 'r')) {
$smarty = TikiLib::lib('smarty');
$smarty->assign('msg', tra("Can't parse HTML data - no grammar file"));
$smarty->display("error.tpl");
die;
}
$grammar = unserialize(fread($fp, filesize($grammarfile)));
fclose($fp);
// process a few ckeditor artifacts
$inHtml = str_replace('', '', $inHtml); // empty p tags are invisible
// create parser object, insert html code and parse it
$htmlparser = new HtmlParser($inHtml, $grammar, '', 0);
$htmlparser->Parse();
// Should I try to convert HTML to wiki?
$out_data = '';
/*
* ['stack'] = array
* Speacial keys introduced to convert to Wiki
* - ['wikitags'] = the number of 'wikistack' entries produced by the html tag
*
* ['wikistack'] = array(), is used to save the wiki markup for the linebreak handling (1 array = 1 html tag)
* Each array entry contains the following keys:
* - ['begin'] = array() of begin markups (1 style definition = 1 array entry)
* - ['end'] = array() of end markups
*
* wiki_lbr = true if we must use '%%%' for linebreaks instead of '\n'
*/
$p = ['stack' => [], 'listack' => [], 'wikistack' => [],
'wiki_lbr' => 0, 'first_td' => false, 'first_tr' => false];
$this->walk_and_parse($htmlparser->content, $out_data, $p, '');
// Is some tags still opened? (It can be if HTML not valid, but this is not reason
// to produce invalid wiki :)
while (count($p['stack'])) {
$e = end($p['stack']);
$out_data .= $e['string'];
array_pop($p['stack']);
}
// Unclosed lists r ignored... wiki have no special start/end lists syntax....
// OK. Things remains to do:
// 1) fix linked images
$out_data = preg_replace(',\[(.*)\|\(img src=(.*)\)\],mU', '{img src=$2 link=$1}', $out_data);
// 2) fix remains images (not in links)
$out_data = preg_replace(',\(img src=(.*)\),mU', '{img src=$1}', $out_data);
// 3) remove empty lines
$out_data = preg_replace(",[\n]+,mU", "\n", $out_data);
// 4) remove nbsp's
$out_data = preg_replace(", ,mU", " ", $out_data);
return $out_data;
}
public function get_new_page_attributes_from_parent_pages($page, $page_info)
{
$tikilib = TikiLib::lib('tiki');
$wikilib = TikiLib::lib('wiki');
$new_page_attrs = [];
$parent_pages = $wikilib->get_parent_pages($page);
$parent_pages_info = [];
foreach ($parent_pages as $a_parent_page_name) {
$this_parent_page_info = $tikilib->get_page_info($a_parent_page_name);
$parent_pages_info[] = $this_parent_page_info;
}
$new_page_attrs = $this->get_newpage_language_from_parent_page($page, $page_info, $parent_pages_info, $new_page_attrs);
// Note: in the future, may add some methods below to guess things like
// categories, workspaces, etc...
return $new_page_attrs;
}
public function get_newpage_language_from_parent_page($page, $page_info, $parent_pages_info, $new_page_attrs)
{
if (! isset($page_info['lang'])) {
$lang = null;
foreach ($parent_pages_info as $this_parent_page_info) {
if (isset($this_parent_page_info['lang'])) {
if ($lang != null and $lang != $this_parent_page_info['lang']) {
// If more than one parent pages and they have different languages
// then we can't guess which is the right one.
$lang = null;
break;
} else {
$lang = $this_parent_page_info['lang'];
}
}
}
if ($lang != null) {
$new_page_attrs['lang'] = $lang;
}
}
return $new_page_attrs;
}
/**
* Bound to tiki.save to find and notifiy users of any mentions
*
* @param array $args
*
* @throws Exception
*/
final public function process_mentions(array $arguments): void
{
global $prefs;
// Notify users/usergroups
if ($prefs['feature_notify_users_mention'] === 'y' && $prefs['feature_tag_users'] === 'y') {
if ($arguments['type'] === 'wiki page') {
$oldData = $arguments['old_data'] ?? '';
$newData = $arguments['data'] ?? '';
} elseif ($arguments['type'] === 'trackeritem' && isset($arguments['values_by_permname'])) {
$oldData = implode("\n", array_values(array_diff($arguments['old_values_by_permname'], $arguments['values_by_permname'])));
$newData = implode("\n", array_values(array_diff($arguments['values_by_permname'], $arguments['old_values_by_permname'])));
} elseif (isset($arguments['content'])) { // 'forum post', 'blog post', comments
$oldData = '';
$newData = $arguments['content'] ?? '';
} else {
$oldData = '';
$newData = '';
}
if ($oldData || $newData) {
$oldData = explode("\n", $oldData);
$newData = explode("\n", $newData);
$sections = [];
require_once('lib/diff/difflib.php');
$textDiff = new Text_Diff($oldData, $newData, true);
if (! $textDiff->isEmpty()) {
foreach ($textDiff->_edits as $edit) {
if (is_a($edit, 'Text_Diff_Op_add')) { // new content
$sections[] = findMentions($edit->final, 'new');
} elseif (is_a($edit, 'Text_Diff_Op_change')) { // change or new content
$sections[] = findMentionsOnChange($edit);
} elseif (is_a($edit, 'Text_Diff_Op_copy')) { // no diffs on content
$sections[] = findMentions($edit->final, 'old');
}
}
}
$count = 1;
$tikiLibUser = TikiLib::lib('user');
$smarty = TikiLib::lib('smarty');
$smarty->loadPlugin('smarty_function_object_link');
$objectUrl = smarty_function_object_link(
['type' => $arguments['type'], 'id' => $arguments['object']],
$smarty->getEmptyInternalTemplate()
);
// strip out html to get the plain url and title?
if (preg_match('/(.*?)<\/a>/', $objectUrl, $matches)) {
$objectUrl = $matches[1];
$title = html_entity_decode($matches[2], ENT_QUOTES);
}
foreach (array_filter($sections) as $sec) {
$notifiedUsers = [];
foreach ($sec as $possibleUser) {
if (isset($possibleUser) && $possibleUser['state'] == 'new') {
$mentionedBy = $tikiLibUser->clean_user($arguments['user']); // gets realName
$mentionedUser = substr($possibleUser['mention'], strpos($possibleUser['mention'], "@") + 1);
if ($mentionedUser) {
$userInfo = $tikiLibUser->get_user_info($mentionedUser);
if (is_array($userInfo) && $userInfo['userId'] > 0) {
if (! in_array($mentionedUser, $notifiedUsers)) {
$url = $objectUrl .= '#mentioned-' . $mentionedUser . '-section-' . $count;
$emailData = [
'siteName' => TikiLib::lib('tiki')->get_preference('browsertitle'),
'mentionedBy' => $mentionedBy,
'url' => $url,
'type' => $arguments['type'],
'title' => $title ?? $arguments['object'],
];
Tiki\Notifications\Email::sendMentionNotification(
'mention_notification_subject.tpl',
'mention_notification.tpl',
$emailData,
[$userInfo]
);
$notifiedUsers[] = $mentionedUser;
}
$count++;
}
}
}
}
}
}
}
}
}