/** * Highlights css * * @param string $block : 2 - the code * @return string highlighted css */ function highlight_css($block) { // highlight all tag/class names and id's $block = callback_on_non_matching_blocks($block, '#\\{.+?}#s', array($this, 'highlight_names')); $block = callback_on_non_matching_blocks($block, '~<span([\\s\\S]+?)</span>~', array($this, 'highlight_rest')); return '<span class="amc_default">' . $block . '</span>'; }
/** * Highlights code ready for displaying * * @param string $block - the code * @return string highlighted code */ function highlight_code($block) { // highlight all < ?xml - ? >, CDATA and comment blocks $block = preg_replace(array('~(<\\!--(.*?)-->)~', '~(<\\!\\[CDATA\\[([\\s\\S]*?)]]>)~', '~(<\\?(.*?)\\?>)~'), array('<span class="amc_comment"><!−−$2−−></span>', '<span class="amc_comment">$1</span>', '<span class="amc_keyword">$1</span>'), $block); // highlight remaining tags, attributes and strings $block = callback_on_non_matching_blocks($block, '~<span([\\s\\S]+?)</span>~', array($this, 'highlight_xml_tags')); return $this->parent->tidy_code_output('<span class="amc_default">' . $block . '</span>'); }
/** * 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; }
/** * 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']; if (!empty($params['Item'])) { // Get Item from params $Item =& $params['Item']; } elseif (!empty($params['Comment'])) { // Get Item from Comment $Comment =& $params['Comment']; $Item =& $Comment->get_Item(); } if (!empty($Item)) { // We are rendering Item or Comment now, Get the settings depending on Blog $item_Blog =& $Item->get_Blog(); $text_styles_enabled = $this->get_coll_setting('text_styles', $item_Blog); $links_enabled = $this->get_coll_setting('links', $item_Blog); $images_enabled = $this->get_coll_setting('images', $item_Blog); $min_h_level = $this->get_coll_setting('min_h_level', $item_Blog); } elseif (!empty($params['Message'])) { // We are rendering Message now, Use FALSE by default because we don't have the settings $text_styles_enabled = false; $links_enabled = false; $images_enabled = false; $min_h_level = 1; // Use default value } else { // Unknown call, Don't render this case return; } // Init parser class with blog settings $Parsedown = Parsedown::instance(); $Parsedown->parse_font_styles = $text_styles_enabled; $Parsedown->parse_links = $links_enabled; $Parsedown->parse_images = $images_enabled; $Parsedown->min_h_level = $min_h_level; // Parse markdown code to HTML if (stristr($content, '<code') !== false || stristr($content, '<pre') !== false) { // Call replace_content() on everything outside code/pre: $content = callback_on_non_matching_blocks($content, '~<(code|pre)[^>]*>.*?</\\1>~is', array($Parsedown, 'parse')); } else { // No code/pre blocks, replace on the whole thing $content = $Parsedown->parse($content); } return true; }
/** * 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']; if (!empty($params['Item'])) { // Get Item from params $Item =& $params['Item']; } elseif (!empty($params['Comment'])) { // Get Item from Comment $Comment =& $params['Comment']; $Item =& $Comment->get_Item(); } if (!empty($Item)) { // We are rendering Item or Comment now, Get a setting depending on Blog $item_Blog =& $Item->get_Blog(); $this->min_h_level = $this->get_coll_setting('min_h_level', $item_Blog); } if ($this->min_h_level > 2 && $this->min_h_level <= 6) { // Restrict <h_> tags by minimum heading level foreach ($this->replace as $r => $replace) { // Do replace $this->replace[$r] = preg_replace_callback('#([^<]*<)(h[2-6])(>[^<]*</)\\2(>)#i', array($this, 'restrict_min_h_level'), $replace); } } $content = replace_content_outcode($this->search, $this->replace, $content); // Find bullet lists 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, 'find_bullet_lists')); } else { // No code/pre blocks, replace on the whole thing $content = $this->find_bullet_lists($content); } return true; }
/** * Convert inline file tags like [image|file:123:link title:.css_class_name] or [inline:123:.css_class_name] into HTML img tags * * @param string Source content * @param array Params * @return string Content */ function render_inline_files($content, $params = array()) { if (isset($params['check_code_block']) && $params['check_code_block'] && (stristr($content, '<code') !== false || stristr($content, '<pre') !== false)) { // Call $this->render_inline_files() on everything outside code/pre: $params['check_code_block'] = false; $content = callback_on_non_matching_blocks($content, '~<(code|pre)[^>]*>.*?</\\1>~is', array($this, 'render_inline_files'), array($params)); return $content; } // No code/pre blocks, replace on the whole thing $params = array_merge(array('before' => '<div>', 'before_image' => '<div class="image_block">', 'before_image_legend' => '<div class="image_legend">', 'after_image_legend' => '</div>', 'after_image' => '</div>', 'after' => '</div>', 'image_size' => 'fit-400x320', 'image_link_to' => 'original', 'limit' => 1000), $params); // Find all matches with inline tags preg_match_all('/\\[(image|file|inline):(\\d+)(:?)([^\\]]*)\\]/i', $content, $inlines); if (!empty($inlines[0])) { // There are inline tags in the content if (!isset($LinkList)) { // Get list of attached Links only first time $LinkOwner = new LinkItem($this); $LinkList = $LinkOwner->get_attachment_LinkList($params['limit'], 'inline'); } if (empty($LinkList)) { // This Item has not the inline attached files, Exit here return $content; } foreach ($inlines[0] as $i => $current_link_tag) { $inline_type = $inlines[1][$i]; // image|file|inline $current_link_ID = (int) $inlines[2][$i]; if (empty($current_link_ID)) { // Invalid link ID, Go to next match continue; } if (!($Link =& $LinkList->get_by_field('link_ID', $current_link_ID))) { // Link is not found by ID continue; } if (!($File =& $Link->get_File())) { // No File object global $Debuglog; $Debuglog->add(sprintf('Link ID#%d of item #%d does not have a file object!', $Link->ID, $this->ID), array('error', 'files')); continue; } if (!$File->exists()) { // File doesn't exist global $Debuglog; $Debuglog->add(sprintf('File linked to item #%d does not exist (%s)!', $this->ID, $File->get_full_path()), array('error', 'files')); continue; } $current_image_params = $params; $current_file_params = array(); if (!empty($inlines[3][$i])) { // Get the inline params: caption and class $inline_params = explode(':.', $inlines[4][$i]); if (!empty($inline_params[0])) { // Image caption is set, so overwrite the image link title if ($inline_params[0] == '-') { // Image caption display is disabled $current_image_params['image_link_title'] = ''; $current_image_params['hide_image_link_title'] = true; } else { // New image caption was set $current_image_params['image_link_title'] = strip_tags($inline_params[0]); } $current_image_params['image_desc'] = $current_image_params['image_link_title']; $current_file_params['title'] = $inline_params[0]; } $class_index = $inline_type == 'inline' ? 0 : 1; // [inline] tag doesn't have a caption, so 0 index is for class param if (!empty($inline_params[$class_index])) { // A class name is set for the inline tags $image_extraclass = strip_tags(trim(str_replace('.', ' ', $inline_params[$class_index]))); if (preg_match('#^[A-Za-z0-9\\s\\-_]+$#', $image_extraclass)) { // Overwrite 'before_image' setting to add an extra class name $current_image_params['before_image'] = '<div class="image_block ' . $image_extraclass . '">'; // 'after_image' setting must be also defined, becuase it may be different than the default '</div>' $current_image_params['after_image'] = '</div>'; // Set class for file inline tags $current_file_params['class'] = $image_extraclass; } } } if ($inline_type == 'image' && $File->is_image()) { // Generate the IMG tag with all the alt, title and desc if available $link_tag = $this->get_attached_image_tag($Link, $current_image_params); } elseif ($inline_type == 'inline') { // Generate simple IMG tag with original image size if ($File->is_image()) { // Only when file is really image file $link_tag = '<img src="' . $File->get_url() . '"' . (empty($current_file_params['class']) ? '' : ' class="' . $current_file_params['class'] . '"') . ' />'; } else { // Display original inline tag when file is not image $link_tag = $current_link_tag; } } else { // Display icon+caption if file is not an image if (empty($current_file_params['title'])) { // Use real file name as title when it is not defined for inline tag $file_title = $File->get('title'); $current_file_params['title'] = ' ' . (empty($file_title) ? $File->get_name() : $file_title); } elseif ($current_file_params['title'] == '-') { // Don't display a title in this case, Only file icon will be displayed $current_file_params['title'] = ''; } else { // Add a space between file icon and title $current_file_params['title'] = ' ' . $current_file_params['title']; } $link_tag = '<a href="' . $File->get_url() . '"' . (empty($current_file_params['class']) ? '' : ' class="' . $current_file_params['class'] . '"') . '>' . $File->get_icon($current_file_params) . $current_file_params['title'] . '</a>'; } // Replace inline image tag with HTML img tag $content = str_replace($current_link_tag, $link_tag, $content); } } return $content; }
/** * 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']; $content = replace_content_outcode($this->search, $this->replace, $content); // Find bullet lists if (stristr($content, '<code') !== false || stristr($content, '<pre') !== false) { // Call replace_content() on everything outside code/pre: $content = callback_on_non_matching_blocks($content, '~<(code|pre)[^>]*>.*?</\\1>~is', array($this, 'find_bullet_lists')); } else { // No code/pre blocks, replace on the whole thing $content = $this->find_bullet_lists($content); } return true; }
/** * Tests {@link callback_on_non_matching_blocks()}. */ function test_callback_on_non_matching_blocks() { $this->assertEqual(callback_on_non_matching_blocks('foo bar', '~\\s~', array(&$this, 'helper_test_callback_on_non_matching_blocks')), '[[foo]] [[bar]]'); $this->assertEqual(callback_on_non_matching_blocks(' foo bar ', '~\\s~', array(&$this, 'helper_test_callback_on_non_matching_blocks')), ' [[foo]] [[bar]] '); // Replace anything outside <pre></pre> and <code></code> that's not in a tag (smilies plugin): $this->assertEqual(callback_on_non_matching_blocks('foo <code>FOOBAR</code> bar ', '~<(code|pre)[^>]*>.*?</\\1>~is', 'callback_on_non_matching_blocks', array('~<[^>]*>~', array(&$this, 'helper_test_callback_on_non_matching_blocks'))), '[[foo]] <code>FOOBAR</code> [[bar]] '); }
private function parse_inline_elements($text) { $map = array(); $index = 0; # Code Span if (strpos($text, '`') !== FALSE and preg_match_all('/`(.+?)`/', $text, $matches, PREG_SET_ORDER)) { foreach ($matches as $matches) { $element_text = $matches[1]; # Decodes escape sequences. $this->escape_sequence_map and strpos($element_text, "") !== FALSE and $element_text = strtr($element_text, $this->escape_sequence_map); # Composes element. $element = '<code class="codespan">' . $element_text . '</code>'; # Encodes element. $code = "" . '$' . $index; $text = str_replace($matches[0], $code, $text); $map[$code] = $element; $index++; } } if ($this->parse_images || $this->parse_links) { // Parse images or links # Inline Link / Image if (strpos($text, '](') !== FALSE) { $text = str_replace('"', '"', $text); // revert from html entity if (preg_match_all('/(!?)(\\[((?:[^][]+|(?2))*)\\])\\(([^"]*?)( "([^"]+)")?\\)/', $text, $matches, PREG_SET_ORDER)) { foreach ($matches as $matches) { if ($matches[1]) { if ($this->parse_images) { // Parse images only if it is enabled $element = '<img src="' . $matches[4] . '" alt="' . $matches[3] . '"' . (!empty($matches[6]) ? ' title="' . $matches[6] . '"' : '') . '>'; } } else { if ($this->parse_links) { // Parse links only if it is enabled $element_text = $this->parse_inline_elements($matches[3]); $element = '<a href="' . $matches[4] . '"' . (!empty($matches[6]) ? ' title="' . $matches[6] . '"' : '') . '>' . $element_text . '</a>'; } } if (!isset($element)) { continue; } $element_text = $this->parse_inline_elements($matches[1]); # ~ $code = "" . '$' . $index; $text = str_replace($matches[0], $code, $text); $map[$code] = $element; $index++; unset($element); } } } # Reference(d) Link / Image if ($this->reference_map and strpos($text, '[') !== FALSE and preg_match_all('/(!?)\\[(.+?)\\](?:\\n?[ ]?\\[(.*?)\\])?/ms', $text, $matches, PREG_SET_ORDER)) { foreach ($matches as $matches) { $link_difinition = isset($matches[3]) && $matches[3] ? $matches[3] : $matches[2]; # implicit $link_difinition = strtolower($link_difinition); if (isset($this->reference_map[$link_difinition])) { $url = $this->reference_map[$link_difinition]; if ($matches[1]) { if ($this->parse_images) { // Parse images only if it is enabled $element = '<img alt="' . $matches[2] . '" src="' . $url . '">'; } } else { if ($this->parse_links) { // Parse links only if it is enabled $element_text = $this->parse_inline_elements($matches[2]); $element = '<a href="' . $url . '">' . $element_text . '</a>'; } } if (!isset($element)) { continue; } # ~ $code = "" . '$' . $index; $text = str_replace($matches[0], $code, $text); $map[$code] = $element; $index++; unset($element); } } } if ($this->parse_links) { // Parse links only if it is enabled if (strpos($text, '<') !== FALSE and preg_match_all('/<((https?|ftp|dict):[^\\^\\s]+?)>/i', $text, $matches, PREG_SET_ORDER)) { foreach ($matches as $matches) { $element = '<a href=":href">:text</a>'; $element = str_replace(':text', $matches[1], $element); $element = str_replace(':href', $matches[1], $element); # ~ $code = "" . '$' . $index; $text = str_replace($matches[0], $code, $text); $map[$code] = $element; $index++; } } } } if ($this->parse_font_styles) { // Parse bold & italic styles only if it is enabled // Render the font style out side html tag attributes $text = callback_on_non_matching_blocks($text, '~<[^>]+>~i', array($this, 'render_font_styles_callback')); } $text = strtr($text, $map); return $text; }
/** * Prepare item content * * @param string Content * @return string Content */ function prepare_item_content($content) { // Convert the content separators to new format: $old_separators = array('<!--more-->', '<!--more-->', '<p>[teaserbreak]</p>', '<!--nextpage-->', '<!--nextpage-->', '<p>[pagebreak]</p>'); $new_separators = array('[teaserbreak]', '[teaserbreak]', '[teaserbreak]', '[pagebreak]', '[pagebreak]', '[pagebreak]'); if (strpos($content, '<code') !== false || strpos($content, '<pre') !== false) { // Call prepare_item_content_callback() on everything outside code/pre: $content = callback_on_non_matching_blocks($content, '~<(code|pre)[^>]*>.*?</\\1>~is', 'replace_content', array($old_separators, $new_separators, 'str')); } else { // No code/pre blocks, replace on the whole thing $content = str_replace($old_separators, $new_separators, $content); } return $content; }
/** * Replace text outside of html tags * * @param string * @return string */ function replace_out_tags($text) { return callback_on_non_matching_blocks($text, '~<[^>]*>~s', array($this, 'replace_callback')); }
/** * Make links clickable in a given text. * * It replaces only text which is not between <a> tags already. * * @uses callback_on_non_matching_blocks() * * {@internal This function gets tested in misc.funcs.simpletest.php.}} * * @return string */ function make_clickable($text, $moredelim = '&') { $text = callback_on_non_matching_blocks($text, '~<a[^>]*("[^"]"|\'[^\']\')?[^>]*>.*?</a>~is', 'make_clickable_callback', array($moredelim)); return $text; }
/** * Split a content by separators outside <code> and <pre> blocks * * @param string|array Separators * @param string Content * @param boolean TRUE - parenthesized expression of separator will be captured and returned as well * @return array The result of explode() function */ function split_outcode($separators, $content, $capture_separator = false) { // Check if the separators exists in content if (!is_array($separators)) { // Convert string to array with one element $separators = array($separators); } $separators_exists = false; if (is_array($separators)) { // Find in array foreach ($separators as $separator) { if (strpos($content, $separator) !== false) { // Separator is found $separators_exists = true; break; } } } if ($separators_exists) { // There are separators in content, Split the content: // Initialize temp values for replace the separators if ($capture_separator) { $rplc_separators = array(); foreach ($separators as $s => $separator) { $rplc_separators[] = '#separator' . $s . '=' . md5(rand()) . '#'; } } else { $rplc_separators = '#separator=' . md5(rand()) . '#'; } // Replace the content separators with temp value if (strpos($content, '<code') !== false || strpos($content, '<pre') !== false) { // Call replace_separators_callback() on everything outside code/pre: $content = callback_on_non_matching_blocks($content, '~<(code|pre)[^>]*>.*?</\\1>~is', 'replace_content', array($separators, $rplc_separators, 'str')); } else { // No code/pre blocks, replace on the whole thing $content = str_replace($separators, $rplc_separators, $content); } if ($capture_separator) { // Save the separators $split_regexp = '~(' . implode('|', $rplc_separators) . ')~s'; $content_parts = preg_split($split_regexp, $content, -1, PREG_SPLIT_DELIM_CAPTURE); foreach ($content_parts as $c => $content_part) { if (($s = array_search($content_part, $rplc_separators)) !== false) { // Replace original separator back $content_parts[$c] = $separators[$s]; } } return $content_parts; } else { // Return only splitted content(without separators) return explode($rplc_separators, $content); } } else { // No separators in content, Return whole content as one element of array return array($content); } }
/** * Convert inline image tags like [image:123:abc] into HTML img tags * * @param string Source content * @param array Params * @return string Content */ function render_inline_images($content, $params = array()) { if (isset($params['check_code_block']) && $params['check_code_block'] && (stristr($content, '<code') !== false || stristr($content, '<pre') !== false)) { // Call $this->render_inline_images() on everything outside code/pre: $params['check_code_block'] = false; $content = callback_on_non_matching_blocks($content, '~<(code|pre)[^>]*>.*?</\\1>~is', array($this, 'render_inline_images'), array($params)); return $content; } // No code/pre blocks, replace on the whole thing $params = array_merge(array('before' => '<div>', 'before_image' => '<div class="image_block">', 'before_image_legend' => '<div class="image_legend">', 'after_image_legend' => '</div>', 'after_image' => '</div>', 'after' => '</div>', 'image_size' => 'fit-400x320', 'image_link_to' => 'original', 'limit' => 1000), $params); // Find all matches with image inline tags preg_match_all('/\\[image:(\\d+)(:?)([^\\]]*)\\]/i', $content, $images); if (!empty($images[0])) { // There are image inline tags in the content foreach ($images[0] as $i => $current_link_tag) { $current_link_ID = (int) $images[1][$i]; $current_link_caption = empty($images[2][$i]) ? '#' : $images[3][$i]; if (empty($current_link_ID)) { // Invalid link ID, Go to next match continue; } if (!isset($FileList)) { // Get list of attached files only first time $LinkOnwer = new LinkItem($this); $FileList = $LinkOnwer->get_attachment_FileList($params['limit'], 'inline'); if (empty($FileList)) { // This Item has not the inline attached files, Exit here break; } } if ($File =& $FileList->get_by_field('link_ID', $current_link_ID)) { // File is found by link ID if (!$File->exists()) { global $Debuglog; $Debuglog->add(sprintf('File linked to item #%d does not exist (%s)!', $this->ID, $File->get_full_path()), array('error', 'files')); break; } elseif ($File->is_image()) { // Generate the IMG tag with all the alt, title and desc if available $image_tag = $this->get_attached_image_tag($File, $params); // Replace inline image tag with HTML img tag $content = str_replace($current_link_tag, $image_tag, $content); } else { // Display error if file is not an image $content = str_replace($current_link_tag, '<div class="error">' . sprintf(T_('This file cannot be included here because it is not an image: %s'), $current_link_tag) . '</div>', $content); } } } } return $content; }
/** * Replace smilies in non-HTML-tag portions of the text. * @uses callback_on_non_matching_blocks() */ function ReplaceTagSafe($text) { return callback_on_non_matching_blocks($text, '~<[^>]*>~', array(&$this, 'preg_insert_smilies_callback')); }
/** * Replace content outside blocks <code></code> & <pre></pre> * * @param array|string Search list * @param array|string Replace list * @param string Source content * @return string Replaced content */ function replace_content_outcode($search, $replace, $content, $replace_function_callback = 'replace_content') { if (!empty($search) && !empty($replace)) { if (stristr($content, '<code') !== false || stristr($content, '<pre') !== false) { // Call replace_content() on everything outside code/pre: $content = callback_on_non_matching_blocks($content, '~<(code|pre)[^>]*>.*?</\\1>~is', $replace_function_callback, array($search, $replace)); } else { // No code/pre blocks, replace on the whole thing $content = call_user_func($replace_function_callback, $content, $search, $replace); } } return $content; }
/** * 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) { // texturize all content not in code/pre blocks $params['data'] = callback_on_non_matching_blocks($params['data'], '#<(pre|code)[\\s\\S]+?/\\1>#i', array($this, 'texturize_block')); }
/** * Replace smilies in non inline placeholders portions of the text. * @uses callback_on_non_matching_blocks() */ function ReplaceInlinePlaceholderSafe($text) { return callback_on_non_matching_blocks($text, '~\\[(image|file|inline):\\d+:?[^\\]]*\\]~', array(&$this, 'preg_insert_smilies_callback')); }
/** * Add a javascript ban action icon after each url in the given content * * @param string Comment content * @return string the content with a ban icon after each url if the user has spamblacklist permission, the incoming content otherwise */ function add_ban_icons($content) { global $current_User; if (!$current_User->check_perm('spamblacklist', 'edit')) { // Current user has no permission to edit the spam contents return $content; } if (stristr($content, '<code') !== false || stristr($content, '<pre') !== false) { // Add icons only outside <code> and <pre> return callback_on_non_matching_blocks($content, '~<(code|pre)[^>]+class="codeblock"[^>]*>.*?</\\1>~is', 'add_ban_icons_callback'); } else { return add_ban_icons_callback($content); } }