zoukankan      html  css  js  c++  java
  • djangocms markdown

    转载: http://www.codingsoho.com/zh/blog/djangocms-markdown/

    前言

    本文主要讨论如何利用markdown在django CMS中插入代码。

    本文环境:

    Python 2.7.10
    Django 1.10
    DjangCMS 3.5

    CMS代码插件

    通过CMS plugin里的code插入代码

    Code插入有五种方式

    Inline
    User Input
    Basic block
    Variables
    Sample output

    具体含义可参考 https://getbootstrap.com/docs/4.1/content/code/

    这个不是本文重点,不做具体描述

    DjangoCMS markdown插件

    具体研究过两个插件djangocms-markdown和cmsplugin-markdown

    djangocms-markdown

    github主页:https://github.com/ovidner/djangocms-markdown

    它的依赖库有django-markdown-duex和markdown2

    Successfully installed django-markdown-deux-1.0.5 djangocms-markdown-0.3.2 markdown2-2.3.5

    下面简单进行一下代码的走读,看看它是怎么工作的

    首先,它把markdown功能做成了template tag,这样直接在模板文件里应用就可以了,格式为{{text|markdown:”recipe”}},recipe是指定参数,默认为default,具体意义后面解释。

    djangocms_markdown emplatesdjangocms_markdownmarkdown.html

    {% load markdown_deux_tags %} 
    {{ text|markdown:"recipe" }}
    

    在markdown_deux库里实现了这个tag
    文件markdown_deux emplatetagsmarkdown_deux_tags.py

    @register.filter(name="markdown")
    def markdown_filter(value, style="default"):
        """Processes the given value as Markdown, optionally using a particular
        Markdown style/config
    
        Syntax::
    
            {{ value|markdown }} {# uses the "default" style #}
            {{ value|markdown:"mystyle" }}
    
        Markdown "styles" are defined by the `MARKDOWN_DEUX_STYLES` setting.
        """
        try:
            return mark_safe(markdown_deux.markdown(value, style))
        except ImportError:
            if settings.DEBUG:
                raise template.TemplateSyntaxError("Error in `markdown` filter: "
                    "The python-markdown2 library isn't installed.")
            return force_text(value)	
    markdown_filter.is_safe = True
    
    

    上面这个filter并没有做什么特别的处理,也就是调用了markdown2的markdown功能,把style参数传递进去了。下一章节我们会走读markdown2的代码,并结合style参数的配置来理解和使用。

    注意:style的配置在setting完成,参数名为settings.MARKDOWN_DEUX_STYLES

    文件markdown_deux.init.py封装了markdown及获取style的函数

    def markdown(text, style="default"):
        if not text:
            return ""
        import markdown2
        return markdown2.markdown(text, **get_style(style))
    
    def get_style(style):
        from markdown_deux.conf import settings
        try:
            return settings.MARKDOWN_DEUX_STYLES[style]
        except KeyError:
            return settings.MARKDOWN_DEUX_STYLES.get("default",
                settings.MARKDOWN_DEUX_DEFAULT_STYLE)
    
    

    markdown_deuxconfsettings.py

    from django.conf import settings
    
    MARKDOWN_DEUX_HELP_URL = getattr(settings, "MARKDOWN_DEUX_HELP_URL",
        "http://daringfireball.net/projects/markdown/syntax")
    
    MARKDOWN_DEUX_DEFAULT_STYLE = {
        "extras": {
            "code-friendly": None,
        },
        "safe_mode": "escape",
    }
    
    MARKDOWN_DEUX_STYLES = getattr(settings, "MARKDOWN_DEUX_STYLES",
        {"default": MARKDOWN_DEUX_DEFAULT_STYLE})
    
    DEBUG = settings.DEBUG
    
    

    cmsplugin-markdown

    依赖库有Markdown和django-markwhat,从使用看跟djangocms-markdown 没太大差别,因为没有仔细研究代码,所以不展开讨论。

    Github主页:https://github.com/bitlabstudio/cmsplugin-markdown

    pip install Django
    pip install django-cms
    pip install Markdown
    pip install django-markwhat

    djangocms_markdown之markdown2

    前面走读了djangocms-markdown的代码,主要功能的实现时通过markdown2来实现的。没有看到专门介绍代码方式的文档,从代码阅读的结果看,主要包括code span,code block和fence code block三种情况

    Python interactive shell也包含在内。

    Github主页:https://github.com/trentm/python-markdown2

    它的匹配主要通过正则表达式来完成,所以中间会穿插一点介绍。
    code span

    用法: ` ` ` code ` ` `
    效果: code

    <p class="cms-plugin cms-plugin-277">
        <code>code</code>
    </p>
    

    它的代码调用结构如下

    def markdown
        def convert(self, text):
            def _run_block_gamut(self, text):
                def _form_paragraphs(self, text):
                    def _run_span_gamut(self, text):
                        def _do_code_spans(self, text):	
                            def _code_span_sub(self, match):
                                c = match.group(2).strip(" 	")
                                c = self._encode_code(c)
                                return "<code>%s</code>" % c
    
    

    正则表达式的定义如下

        _code_span_re = re.compile(r'''
                (?<!\)
                (`+) # 1 = Opening run of `
                (?!`) # See Note A test/tm-cases/escapes.text
                (.+?) # 2 = The code block
                (?<!`)
                1 # Matching closer
                (?!`)
            ''', re.X | re.S)
    
    

    re.X(VERBOSE) 该标志通过给予更灵活的格式以便将正则表达式写得更易于理解
    re.S(DOTALL) 使.匹配包括换行在内的所有字符

    从下面函数的注释看,它对`符号的个数并没有严格的限制。

        def _do_code_spans(self, text):
            # * Backtick quotes are used for <code></code> spans.
            #
            # * You can use multiple backticks as the delimiters if you want to
            # include literal backticks in the code span. So, this input:
            #
            # Just type ``foo `bar` baz`` at the prompt.
            #
            # Will translate to:
            #
            # <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
            #
            # There's no arbitrary limit to the number of backticks you
            # can use as delimters. If you need three consecutive backticks
            # in your code, use four for delimiters, etc.
            #
            # * You can use spaces to get literal backticks at the edges:
            #
            # ... type `` `bar` `` ...
            #
            # Turns to:
            #
            # ... type <code>`bar`</code> ...
            return self._code_span_re.sub(self._code_span_sub, text)
    
    

    code block

    用法: 注意代码块前面空行,代码块前面有四个空格或tab

    ```
    代码块
    ```

    效果:

    代码块

    举例

    代码如下

    def a():
        a = a+1
    

    生成的html代码

    <pre class="prettyprint cms-plugin cms-plugin-277">
        <code>def a():
            a = a+1
        </code>
    </pre>
    
    

    特别说明一下,在bootstrap4里面好像<pre>效果取消了,所以这个是我自己添加的,具体看我后面附件节。

    可以看到,这儿的代码并没有语法高亮效果,要达到这种效果,我们需要第三方插件。

    在settings里,可以看到有个配置参数html-classes,从注释中可以看到,它可以通过google-code-prettify来着色。

            "extras": {
                # `class` attribute put on `pre` tags to enable using
                # <http://code.google.com/p/google-code-prettify/> for syntax
                # highlighting.
                "html-classes": {"pre": "prettyprint"},
            },
    
    

    用法比较简单,引入下面三个css和js

        <link href="https://cdn.bootcss.com/prettify/r298/prettify.min.css" rel='stylesheet' type='text/css'>
        <script src="https://cdn.bootcss.com/prettify/r298/prettify.min.js"></script>
    
    

    要让代码运行起来可以有两种方法,一种是引入run_prettify.js,另外一种是在body里加入一段代码调用prettyPrint函数。

        <script src="https://cdn.bootcss.com/prettify/r298/run_prettify.min.js"></script>
    
    
        <script type="text/javascript">
          window.onload = function(){
            prettyPrint();
          };
        </script>
    
    

    使用过程中发现这种方式能工作,但是网页刷新后要过段时间效果才出来,怀疑是不是跟这个是google的服务有关。

    下面看看代码里是怎么实现的。

    代码块匹配正则表达式函数如下

        def _do_code_blocks(self, text):
            """Process Markdown `<pre><code>` blocks."""
            code_block_re = re.compile(r'''
                (?:
    
    |A
    ?) 
                ( # $1 = the code block -- one or more lines, starting with a space/tab
                  (?: 
                    (?:[ ]{%d} | 	) # Lines must start with a tab or a tab-width of spaces 
                    .*
    +
                  )+
                )
                ((?=^[ ]{0,%d}S)|) # Lookahead for non-space at line-start, or end of doc 
                # Lookahead to make sure this block isn't already in a code block.
                # Needed when syntax highlighting is being used.
                (?![^<]*</code>) 
                ''' % (self.tab_width, self.tab_width),
                re.M | re.X)
            return code_block_re.sub(self._code_block_sub, text)	
    

    re的用法参考http://www.codingsoho.com/zh/blog/python-re/

    接下来我们来解释上面的代码:

    (?: |A ?) 表示以空行开始
    (?:[ ]{%d} | ) 每行行开始时,四个空格 或者 tab
    ((?: (?:[ ]{%d} | ) .* + )+ ) 每行以四个空格或tab开始然后后面跟任意字符.*并以换行结束 +,这些匹配可重复((re)+)
    ((?=^[ ]{0,%d}S)|) 匹配结束,用到了后向界定
    (?![^<]*</code>) 后向非界定,确保这段代码并没有用语法高亮

    fence code block

    要达到代码块的显示效果,我们还可以通过fence code block

    简单的列一下调用关系便于代码查找

        def convert(self, text):
            if "fenced-code-blocks" in self.extras and not self.safe_mode:
                text = self._do_fenced_code_blocks(text)
    
        def _do_fenced_code_blocks(self, text):
            """Process ```-fenced unindented code blocks ('fenced-code-blocks' extra)."""
            return self._fenced_code_block_re.sub(self._fenced_code_block_sub, text)
    
        def _fenced_code_block_sub(self, match):
            return self._code_block_sub(match, is_fenced_code_block=True)
    
        _fenced_code_block_re = re.compile(r'''
            (?:
    +|A
    ?)
            ^```s*?([w+-]+)?s*?
     # opening fence, $1 = optional lang
            (.*?) # $2 = code block content
            ^```[ 	]*
     # closing fence
            ''', re.M | re.X | re.S)
    
    

    上面的正则表达式实现了```code block```的匹配,不再详述。

    下面仔细研究一下它的语法高亮怎么做的。

    _code_block_sub 调用 _color_with_pygmentspygments是第三方工具,平时我也常用它来做代码语法高亮操作,生成的内容可以贴到word文档里。要实现该功能,首先要安装pygments。
    该函数用到了HtmlCodeFormatter类,这个处理代码格式。

        def _color_with_pygments(self, codeblock, lexer, **formatter_opts):
            import pygments
            import pygments.formatters
    
            class HtmlCodeFormatter(pygments.formatters.HtmlFormatter):
                def _wrap_code(self, inner):
                    """A function for use in a Pygments Formatter which
                    wraps in <code> tags.
                    """
                    yield 0, "<code>"
                    for tup in inner:
                        yield tup
                    yield 0, "</code>"
    
                def wrap(self, source, outfile):
                    """Return the source with a code, pre, and div."""
                    return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
    
            formatter_opts.setdefault("cssclass", "codehilite")
            formatter = HtmlCodeFormatter(**formatter_opts)
            return pygments.highlight(codeblock, lexer, formatter)
    
    
    class HtmlFormatter(Formatter):
        def __init__(self, **options):
            Formatter.__init__(self, **options)
            self.title = self._decodeifneeded(self.title)
            self.nowrap = get_bool_opt(options, 'nowrap', False)
            self.noclasses = get_bool_opt(options, 'noclasses', False)
            self.classprefix = options.get('classprefix', '')
            self.cssclass = self._decodeifneeded(options.get('cssclass', 'highlight'))
            self.cssstyles = self._decodeifneeded(options.get('cssstyles', ''))
            self.prestyles = self._decodeifneeded(options.get('prestyles', ''))
            self.cssfile = self._decodeifneeded(options.get('cssfile', ''))
            self.noclobber_cssfile = get_bool_opt(options, 'noclobber_cssfile', False)
            self.tagsfile = self._decodeifneeded(options.get('tagsfile', ''))
            self.tagurlformat = self._decodeifneeded(options.get('tagurlformat', ''))
            self.filename = self._decodeifneeded(options.get('filename', ''))
            ……
    
    

    Formatter默认的cssclasscodehilite, 生成的html代码里它会给<pre>的wrapper div添加这个class。

    HtmlFormatter完整格式定义如下

    关于fenced code blocks的设置如下

    It includes support for code syntax highlighting as per GFM. You must have the pygments Python module installed for this to work.
    To really show colors you need to add one of pygment css files, see https://github.com/richleland/pygments-css. Instead of directly using the css files, it's necessary to regenerate css files with the div class being codehilite. Simply change the .highlight into .codehilite in the Makefile and make cssfiles.

    默认配置下,css文件里的class是highlight,可以从https://github.com/richleland/pygments-css中看到

    .highlight .gd { color: #A00000 } /* Generic.Deleted */
    
    

    但是前面刚刚提到,代码里默认是codehilite

    我们可以在settings文件里修改这个配置

    MARKDOWN_DEUX_STYLES = {
        "recipe": {
            "link_patterns": [
                # Transform "Recipe 123" in a link.
                (re.compile(r"recipes+#?(d+)", re.I),
                 r"http://code.activestate.com/recipes/1/"),
            ],
            "extras": {
                "code-friendly": None,
                "pyshell": None,
                "demote-headers": 3,
                "link-patterns": None,
                # `class` attribute put on `pre` tags to enable using
                # <http://code.google.com/p/google-code-prettify/> for syntax
                # highlighting.
                "html-classes": {"pre": "prettyprint"},
                "cuddled-lists": None,
                "footnotes": None,
                "header-ids": None,
                "fenced-code-blocks" : {'cssclass': 'mycodehilite', "prestyles":"background-color: #d2dee8;"},
            },
            "safe_mode": "escape",
        }
    }
    
    

    上面代码中,我可以将cssclass的名字写成了mycodehilite,如果这样的话,同时我也需要将对应的css里的class也改成了mycodehilite

    另外,针对fenced-code-blocks,不仅可以设置cssclass,同时还可以设置其他的参数,比如prestyles,它将会给<pre>代码块添加这个style

    举例如下

    空 行
    ``` python
    import a
    import b
    ```

    生成的代码格式如下

    <div class="mycodehilite">
        <pre style="background-color: #d2dee8;">
            <span></span>
            <code>
                <span class="kn">import</span> <span class="nn">os</span>
                <span class="kn">from</span> <span class="nn">os</span> <span class="kn">import</span> <span class="n">get</span>
                <span class="k">def</span> <span class="nf">a</span><span class="p">():</span>
                <span class="k">print</span> <span class="s2">"oo"</span>
            </code>
        </pre>
    </div>
    
    

    效果图

    查看fenced-code-blocks,https://github.com/trentm/python-markdown2/wiki/fenced-code-blocks

    对应pygments-css 在,https://github.com/richleland/pygments-css

    Python interactive shell

    例子

    输入代码如下
    Here is a Python interactive shell session
    >>> import sys
    >>> sys.platform
    'darwin'

    生成代码如下

    <pre class="prettyprint cms-plugin cms-plugin-277">
        <code>
            >>> import sys
            >>> sys.platform
            'darwin'
        </code>
    </pre>
    
    

    对应setting配置

    MARKDOWN_DEUX_STYLES = {
        "recipe": {
            "extras": {
                "pyshell": None,
            },
            "safe_mode": "escape",
        }
    }
    
    

    效果图

    Here is a Python interactive shell session
    
    >>> import sys
    >>> sys.platform
    'darwin'
    

    https://github.com/trentm/python-markdown2/wiki/pyshell

    附件

    插件本身是基于bootstrap3的,但是clean blog基于bootstrap4,所以有些地方不兼容

    code
    这个原来在bootstrap3显示效果如下

    import os
    import sys
    

    bootstrap4已经去掉了这些特殊效果,所以我只能自己去添加

    pre{
     margin:15px 0;
    }
    
    pre{
     background-color:#f8f8f8;
     border:1px solid #ccc;
     font-size:13px;
     line-height:19px;
     overflow:auto;
     padding:6px 10px;
     border-radius:3px;
    }
    
    pre>code{
     margin:0;
     padding:0;
     white-space:pre;
     border:none;
     background:transparent;
    }
    
    pre code{
     background-color:transparent;
     border:none;
    }
    
    

    参考文件为 djangocms_markdownstaticdjangocms_markdown hemespreviewgithub.css

    https://www.prepbootstrap.com/converter
    https://getbootstrap.com/docs/4.1/content/code/

    db路径

    本文在对markdown2学习时用到了debug功能,我选用的eclipse。

    因为路径跟正常工作环境不一样,所以我重新写了个manage.py文件,并把它配置到eclipse中

    使用过程中发现一个问题,就是总是找不到db文件,后来查出来跟路径设置有关。

    db必须用完整路径,否则eclipse会把它放到项目的workspace下面,导致运行时找不到(临时生成一个空的)
    windows命令行运行不会出现这个问题

    DATABASES = {
        'default': {
            'CONN_MAX_AGE': 0,
            'ENGINE': 'django.db.backends.sqlite3',
            'HOST': 'localhost',
            'NAME': os.path.join(BASE_DIR, 'project1.db'),
            'PASSWORD': '',
            'PORT': '',
            'USER': ''
        }
    }
    
    

    文件管理

    djangocms自带文件管理功能,这对markdown来说是很大的方便

    关注下方公众号获取更多文章

    转载: http://www.codingsoho.com/zh/blog/djangocms-markdown/

  • 相关阅读:
    使用node-inspector调试nodejs程序<nodejs>
    2015 2月记事(1)
    设置npm安装模块目录<nodejs>
    BZOJ 1965 [AHOI2005]洗牌
    BZOJ 1924 [Sdoi2010]所驼门王的宝藏
    【NOIP2003】传染病控制
    BZOJ [Scoi2015]情报传递
    [Noi2002]Savage
    BZOJ 4025: 二分图
    BZOJ 4999 This Problem Is Too Simple!
  • 原文地址:https://www.cnblogs.com/2dogslife/p/9194818.html
Copyright © 2011-2022 走看看