Skip to content
This repository
Fetching contributors…

Cannot retrieve contributors at this time

file 194 lines (183 sloc) 6.684 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
<?php
/**
* CMarkdownParser class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2010 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/

require_once(Yii::getPathOfAlias('system.vendors.markdown.markdown').'.php');
if(!class_exists('HTMLPurifier_Bootstrap',false))
{
require_once(Yii::getPathOfAlias('system.vendors.htmlpurifier').DIRECTORY_SEPARATOR.'HTMLPurifier.standalone.php');
HTMLPurifier_Bootstrap::registerAutoload();
}

/**
* CMarkdownParser is a wrapper of {@link http://michelf.com/projects/php-markdown/extra/ MarkdownExtra_Parser}.
*
* CMarkdownParser extends MarkdownExtra_Parser by using Text_Highlighter
* to highlight code blocks with specific language syntax.
* In particular, if a code block starts with the following:
* <pre>
* [language]
* </pre>
* The syntax for the specified language will be used to highlight
* code block. The languages supported include (case-insensitive):
* ABAP, CPP, CSS, DIFF, DTD, HTML, JAVA, JAVASCRIPT,
* MYSQL, PERL, PHP, PYTHON, RUBY, SQL, XML
*
* You can also specify options to be passed to the syntax highlighter. For example:
* <pre>
* [php showLineNumbers=1]
* </pre>
* which will show line numbers in each line of the code block.
*
* For details about the standard markdown syntax, please check the following:
* <ul>
* <li>{@link http://daringfireball.net/projects/markdown/syntax official markdown syntax}</li>
* <li>{@link http://michelf.com/projects/php-markdown/extra/ markdown extra syntax}</li>
* </ul>
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @version $Id$
* @package system.utils
* @since 1.0
*/
class CMarkdownParser extends MarkdownExtra_Parser
{
/**
* @var string the css class for the div element containing
* the code block that is highlighted. Defaults to 'hl-code'.
*/
public $highlightCssClass='hl-code';
/**
* @var mixed the options to be passed to {@link http://htmlpurifier.org HTML Purifier}.
* This can be a HTMLPurifier_Config object, an array of directives (Namespace.Directive => Value)
* or the filename of an ini file.
* This property is used only when {@link safeTransform} is invoked.
* @see http://htmlpurifier.org/live/configdoc/plain.html
* @since 1.1.4
*/
public $purifierOptions=null;

/**
* Transforms the content and purifies the result.
* This method calls the transform() method to convert
* markdown content into HTML content. It then
* uses {@link CHtmlPurifier} to purify the HTML content
* to avoid XSS attacks.
* @param string $content the markdown content
* @return string the purified HTML content
* @since 1.0.1
*/
public function safeTransform($content)
{
$content=$this->transform($content);
$purifier=new HTMLPurifier($this->purifierOptions);
$purifier->config->set('Cache.SerializerPath',Yii::app()->getRuntimePath());
return $purifier->purify($content);
}

/**
* @return string the default CSS file that is used to highlight code blocks.
*/
public function getDefaultCssFile()
{
return Yii::getPathOfAlias('system.vendors.TextHighlighter.highlight').'.css';
}

/**
* Callback function when a code block is matched.
* @param array $matches matches
* @return string the highlighted code block
*/
public function _doCodeBlocks_callback($matches)
{
$codeblock = $this->outdent($matches[1]);
if(($codeblock = $this->highlightCodeBlock($codeblock)) !== null)
return "\n\n".$this->hashBlock($codeblock)."\n\n";
else
return parent::_doCodeBlocks_callback($matches);
}

/**
* Callback function when a fenced code block is matched.
* @param array $matches matches
* @return string the highlighted code block
*/
public function _doFencedCodeBlocks_callback($matches)
{
return "\n\n".$this->hashBlock($this->highlightCodeBlock($matches[2]))."\n\n";
}

/**
* Highlights the code block.
* @param string $codeblock the code block
* @return string the highlighted code block. Null if the code block does not need to highlighted
*/
protected function highlightCodeBlock($codeblock)
{
if(($tag=$this->getHighlightTag($codeblock))!==null && ($highlighter=$this->createHighLighter($tag)))
{
$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
$tagLen = strpos($codeblock, $tag)+strlen($tag);
$codeblock = ltrim(substr($codeblock, $tagLen));
$output=preg_replace('/<span\s+[^>]*>(\s*)<\/span>/', '\1', $highlighter->highlight($codeblock));
return "<div class=\"{$this->highlightCssClass}\">".$output."</div>";
}
else
return "<pre>".$codeblock."</pre>";
}

/**
* Returns the user-entered highlighting options.
* @param string $codeblock code block with highlighting options.
* @return string the user-entered highlighting options. Null if no option is entered.
*/
protected function getHighlightTag($codeblock)
{
$str = trim(current(preg_split("/\r|\n/", $codeblock,2)));
if(strlen($str) > 2 && $str[0] === '[' && $str[strlen($str)-1] === ']')
return $str;
}

/**
* Creates a highlighter instance.
* @param string $options the user-entered options
* @return Text_Highlighter the highlighter instance
*/
protected function createHighLighter($options)
{
if(!class_exists('Text_Highlighter', false))
{
require_once(Yii::getPathOfAlias('system.vendors.TextHighlighter.Text.Highlighter').'.php');
require_once(Yii::getPathOfAlias('system.vendors.TextHighlighter.Text.Highlighter.Renderer.Html').'.php');
}
$lang = current(preg_split('/\s+/', substr(substr($options,1), 0,-1),2));
$highlighter = Text_Highlighter::factory($lang);
if($highlighter)
$highlighter->setRenderer(new Text_Highlighter_Renderer_Html($this->getHiglightConfig($options)));
return $highlighter;
}

/**
* Generates the config for the highlighter.
* @param string $options user-entered options
* @return array the highlighter config
*/
public function getHiglightConfig($options)
{
$config['use_language'] = true;
if( $this->getInlineOption('showLineNumbers', $options, false) )
$config['numbers'] = HL_NUMBERS_LI;
$config['tabsize'] = $this->getInlineOption('tabSize', $options, 4);
return $config;
}

/**
* Retrieves the specified configuration.
* @param string $name the configuration name
* @param string $str the user-entered options
* @param mixed $defaultValue default value if the configuration is not present
* @return mixed the configuration value
*/
protected function getInlineOption($name, $str, $defaultValue)
{
if(preg_match('/'.$name.'(\s*=\s*(\d+))?/i', $str, $v) && count($v) > 2)
return $v[2];
else
return $defaultValue;
}
}
Something went wrong with that request. Please try again.