Commit 45691d5a authored by Patrik Dufresne's avatar Patrik Dufresne

Merge branch 'develop/url-image-substitution' into 'master'

Develop/url image substitution

See merge request !3
parents 0dc58aa2 3977c90a
Pipeline #254 passed with stages
in 1 minute and 10 seconds
......@@ -4,9 +4,19 @@ Created on Jun 8, 2018
@author: Patrik Dufresne
'''
import markdown
from markdown.extensions import Extension
from markdown.inlinepatterns import LinkPattern
import types
from weakref import ref as weakref
from lektor.context import get_ctx, url_to
from lektor.pluginsystem import Plugin
from lektor.types import Type
import markdown
from lektor.utils import bool_from_string
from markupsafe import Markup
from werkzeug.urls import url_parse
SECTION_EXTENSIONS = "extensions"
SECTION_MARKDOWN = "markdown"
......@@ -15,39 +25,62 @@ DEFAULT_EXTENTIONS = {
}
def pythonmarkdown_to_html(text, cfg):
# TODO Call events.
return markdown.markdown(text, **cfg.options)
def sanitize_url(self, link):
"""
Patched function to resolve the url using Lektor.
"""
try:
url = url_parse(link)
if not url.scheme:
link = url_to("!" + link)
except:
# Do not fail if something went wrong during the url parsing.
pass
return LinkPattern.sanitize_url(self, link)
# Wrapper with an __html__ method prevents Lektor from escaping HTML tags.
class HTML(object):
def __init__(self, html):
self.html = html
class LektorMarkdownExtension(Extension):
"""
This class represent an extension into the python-markdown to implement
some logic in url handling to mimic the current behaviour of default
markdown rendered (mistune).
"""
def _patch(self, p):
"""
Monkey patch the sanitize_url method.
"""
p.sanitize_url = types.MethodType(sanitize_url, p)
def __html__(self):
return self.html
def extendMarkdown(self, md, md_globals):
self._patch(md.inlinePatterns['link'])
self._patch(md.inlinePatterns['image_link'])
self._patch(md.inlinePatterns['image_reference'])
self._patch(md.inlinePatterns['reference'])
def _value(v):
"This function tries to convert the configuration value to a sane type."
if v.lower() in ['true', 'false']:
return v.lower() == 'true'
try:
return int(v)
except:
return v
class PythonMarkdownConfig(object):
"""
Define configuration of python-markdown.
"""
def _section_as_dict(self, name):
def _value(v):
"This function tries to convert the configuration value to a sane type."
w = bool_from_string(v)
if w is not None:
return w
try:
return int(v)
except:
return v
return {k: _value(v) for k, v in self.plugin_config.section_as_dict(name).items()}
def _builtin_extensions(self):
return [LektorMarkdownExtension()]
def _extensions(self):
return [
e
......@@ -58,30 +91,92 @@ class PythonMarkdownConfig(object):
self.plugin_config = plugin_config
self.options = self._section_as_dict(SECTION_MARKDOWN)
self.options.update({
"extensions": self._extensions(),
"extensions": self._extensions() + self._builtin_extensions(),
"extension_configs": {e: self._section_as_dict(e) for e in self._extensions()},
})
def pythonmarkdown_to_html(text, record=None):
"""
Convert python-markdown into html.
"""
ctx = get_ctx()
if ctx is None:
raise RuntimeError('Context is required for python-markdown rendering')
env = get_ctx().env
plugin = env.plugins.get('pythonmarkdown', None)
if not plugin:
raise RuntimeError('PythonMarkdownPLugin is required for python-markdown rendering')
cfg = PythonMarkdownConfig(plugin.get_config())
# TODO May need to emit event to let other plugin hook into this one.
return markdown.markdown(text, **cfg.options)
class PythonMarkdown(object):
def __init__(self, source, record=None):
self.source = source
self.__record = weakref(record) if record is not None else lambda: None
self.__html = None
self.__meta = None
def __bool__(self):
return bool(self.source)
__nonzero__ = __bool__
def __render(self):
if self.__html is None:
self.__html = pythonmarkdown_to_html(self.source, self.__record())
self.__meta = None
@property
def meta(self):
self.__render()
return self.__meta
@property
def html(self):
self.__render()
return Markup(self.__html)
def __getitem__(self, name):
return self.meta[name]
def __unicode__(self):
self.__render()
return self.__html
def __html__(self):
self.__render()
return Markup(self.__html)
class PythonMarkdownDescriptor(object):
def __init__(self, source):
self.source = source
def __get__(self, obj, type=None):
if obj is None:
return self
return PythonMarkdown(self.source, record=obj)
class PythonMarkdownType(Type):
widget = 'multiline-text'
def value_from_raw(self, raw):
"""
Called to convert the raw value (markdown) into html.
"""
return PythonMarkdownDescriptor(raw.value or u'')
class PythonMarkdownPlugin(Plugin):
name = u'pythonmarkdown'
description = u'Adds AsciiDoc field type to Lektor.'
description = u'Add pythonmarkdownn field type to Lektor to make use of python-markdown as a renderer.'
def on_setup_env(self, **extra):
plugin_config = self.get_config()
# We declare this type as an internal class inside the plugin to get
# access to the get_config() function from the Type.
#
# The name of the class is used a key for the fields type.
class PythonMarkdownType(Type):
widget = 'multiline-text'
def value_from_raw(self, raw):
"""
Called to convert the raw value (markdown) into html.
"""
cfg = PythonMarkdownConfig(plugin_config)
return HTML(pythonmarkdown_to_html(raw.value or u'', cfg))
self.env.add_type(PythonMarkdownType)
......@@ -7,3 +7,17 @@ body:
```
code here
```
## Check url & image substitution
[Link to Sub Page](sub-page)
![alttxt](sub-page)
## Check references
I get 10 times more traffic from [Sub Page] [1] than from
[Yahoo] [2].
[1]: sub-page/ "Sub Page"
[2]: http://search.yahoo.com/ "Yahoo Search"
body:
Nothing special
\ No newline at end of file
......@@ -5,17 +5,18 @@ Created on Jun 8, 2018
@author: Patrik Dufresne
'''
from lektor.builder import Builder
from lektor.db import Database
from lektor.environment import Environment
from lektor.project import Project
import os
import shutil
import tempfile
import unittest
from lektor.builder import Builder
from lektor.db import Database
from lektor.environment import Environment
from lektor.project import Project
class TestLektorAsciidoc(unittest.TestCase):
class TestLektorPythonMarkdown(unittest.TestCase):
def setUp(self):
self.project = Project.from_path(os.path.join(os.path.dirname(__file__), 'demo-project'))
......@@ -40,6 +41,13 @@ class TestLektorAsciidoc(unittest.TestCase):
assert '<h2 class="customclass" id="header-2">Header 2</h2>' in html
# The output changes depending on the version of python-markdown uses.
assert '<pre class="codehilite"><code class="linenums">code here</code></pre>' in html
# Check url & image substitution
assert '<a href="./sub-page">Link to Sub Page</a>' in html
assert '<img alt="alttxt" src="./sub-page" />' in html
# Check references
assert '<a href="./sub-page/" title="Sub Page">Sub Page</a>' in html
assert '<a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>' in html
if __name__ == "__main__":
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment