Adding some tests and a improved version of the wikilinks extension
parent
06170b868f
commit
be5e37187c
|
@ -8,6 +8,7 @@ from . import hidden_hilite
|
||||||
from . import semi_sane_lists
|
from . import semi_sane_lists
|
||||||
from . import spaced_link
|
from . import spaced_link
|
||||||
from . import strikethrough
|
from . import strikethrough
|
||||||
|
from . import wikilinks
|
||||||
|
|
||||||
AutolinkExtension = autolink.AutolinkExtension
|
AutolinkExtension = autolink.AutolinkExtension
|
||||||
AutomailExtension = automail.AutomailExtension
|
AutomailExtension = automail.AutomailExtension
|
||||||
|
@ -15,3 +16,4 @@ HiddenHiliteExtension = hidden_hilite.HiddenHiliteExtension
|
||||||
SemiSaneListExtension = semi_sane_lists.SemiSaneListExtension
|
SemiSaneListExtension = semi_sane_lists.SemiSaneListExtension
|
||||||
SpacedLinkExtension = spaced_link.SpacedLinkExtension
|
SpacedLinkExtension = spaced_link.SpacedLinkExtension
|
||||||
StrikethroughExtension = strikethrough.StrikethroughExtension
|
StrikethroughExtension = strikethrough.StrikethroughExtension
|
||||||
|
WikiLinkExtension = wikilinks.WikiLinkExtension
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from markdown import Extension
|
||||||
|
from markdown.inlinepatterns import Pattern
|
||||||
|
from markdown.util import etree
|
||||||
|
import re
|
||||||
|
|
||||||
|
def build_url(label, base, end):
|
||||||
|
""" Build a url from the label, a base, and an end. """
|
||||||
|
clean_label = re.sub(r'([ ]+_)|(_[ ]+)|([ ]+)', '_', label)
|
||||||
|
return '%s%s%s'% (base, clean_label, end)
|
||||||
|
|
||||||
|
|
||||||
|
class WikiLinkExtension(Extension):
|
||||||
|
def __init__(self, configs):
|
||||||
|
# set extension defaults
|
||||||
|
self.config = {
|
||||||
|
'base_url' : ['/', 'String to append to beginning or URL.'],
|
||||||
|
'end_url' : ['/', 'String to append to end of URL.'],
|
||||||
|
'html_class' : ['wikilink', 'CSS hook. Leave blank for none.'],
|
||||||
|
'build_url' : [build_url, 'Callable formats URL from label.'],
|
||||||
|
}
|
||||||
|
configs = dict(configs) or {}
|
||||||
|
# Override defaults with user settings
|
||||||
|
for key, value in configs.items():
|
||||||
|
self.setConfig(key, value)
|
||||||
|
|
||||||
|
def extendMarkdown(self, md, md_globals):
|
||||||
|
self.md = md
|
||||||
|
|
||||||
|
# append to end of inline patterns
|
||||||
|
WIKILINK_RE = r'\[\[([\w0-9_ -]+)(\|[\w0-9_ -]+)?\]\]'
|
||||||
|
wikilinkPattern = WikiLinks(WIKILINK_RE, self.getConfigs())
|
||||||
|
wikilinkPattern.md = md
|
||||||
|
md.inlinePatterns.add('wikilink', wikilinkPattern, "<not_strong")
|
||||||
|
|
||||||
|
|
||||||
|
class WikiLinks(Pattern):
|
||||||
|
def __init__(self, pattern, config):
|
||||||
|
super(WikiLinks, self).__init__(pattern)
|
||||||
|
self.config = config
|
||||||
|
|
||||||
|
def handleMatch(self, m):
|
||||||
|
if m.group(2).strip():
|
||||||
|
base_url, end_url, html_class = self._getMeta()
|
||||||
|
label = m.group(2).strip()
|
||||||
|
url = self.config['build_url'](label, base_url, end_url)
|
||||||
|
|
||||||
|
if m.group(3):
|
||||||
|
title = m.group(3).strip()[1:]
|
||||||
|
else:
|
||||||
|
title = label
|
||||||
|
|
||||||
|
a = etree.Element('a')
|
||||||
|
a.text = title
|
||||||
|
a.set('href', url)
|
||||||
|
if html_class:
|
||||||
|
a.set('class', html_class)
|
||||||
|
else:
|
||||||
|
a = ''
|
||||||
|
return a
|
||||||
|
|
||||||
|
def _getMeta(self):
|
||||||
|
""" Return meta data or config data. """
|
||||||
|
base_url = self.config['base_url']
|
||||||
|
end_url = self.config['end_url']
|
||||||
|
html_class = self.config['html_class']
|
||||||
|
if hasattr(self.md, 'Meta'):
|
||||||
|
if 'wiki_base_url' in self.md.Meta:
|
||||||
|
base_url = self.md.Meta['wiki_base_url'][0]
|
||||||
|
if 'wiki_end_url' in self.md.Meta:
|
||||||
|
end_url = self.md.Meta['wiki_end_url'][0]
|
||||||
|
if 'wiki_html_class' in self.md.Meta:
|
||||||
|
html_class = self.md.Meta['wiki_html_class'][0]
|
||||||
|
return base_url, end_url, html_class
|
||||||
|
|
||||||
|
|
||||||
|
def makeExtension(configs=None) :
|
||||||
|
return WikiLinkExtension(configs=configs)
|
|
@ -5,7 +5,7 @@ from django.core.cache import cache
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
|
|
||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
from markdown.extensions.wikilinks import WikiLinkExtension
|
#from markdown.extensions.wikilinks import WikiLinkExtension
|
||||||
from fn import F
|
from fn import F
|
||||||
|
|
||||||
from .gfm import AutolinkExtension
|
from .gfm import AutolinkExtension
|
||||||
|
@ -14,6 +14,7 @@ from .gfm import HiddenHiliteExtension
|
||||||
from .gfm import SemiSaneListExtension
|
from .gfm import SemiSaneListExtension
|
||||||
from .gfm import SpacedLinkExtension
|
from .gfm import SpacedLinkExtension
|
||||||
from .gfm import StrikethroughExtension
|
from .gfm import StrikethroughExtension
|
||||||
|
from .gfm import WikiLinkExtension
|
||||||
|
|
||||||
from .processors.emoji import emoji
|
from .processors.emoji import emoji
|
||||||
from .processors.mentions import mentions
|
from .processors.mentions import mentions
|
||||||
|
|
|
@ -6,10 +6,15 @@ import taiga.base
|
||||||
from taiga.mdrender.processors import emoji
|
from taiga.mdrender.processors import emoji
|
||||||
from taiga.mdrender.processors import mentions
|
from taiga.mdrender.processors import mentions
|
||||||
from taiga.mdrender.processors import references
|
from taiga.mdrender.processors import references
|
||||||
|
from taiga.mdrender.service import render
|
||||||
|
|
||||||
class DummyClass:
|
class DummyClass:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
dummy_project = DummyClass()
|
||||||
|
dummy_project.id = 1
|
||||||
|
dummy_project.slug = "test"
|
||||||
|
|
||||||
def test_proccessor_valid_emoji():
|
def test_proccessor_valid_emoji():
|
||||||
result = emoji.emoji("<b>:smile:</b>")
|
result = emoji.emoji("<b>:smile:</b>")
|
||||||
assert result == '<b><img class="emoji" title="smile" alt="smile" height="20" width="20" src="http://localhost:8000/static/img/emojis/smile.png" align="top"></b>'
|
assert result == '<b><img class="emoji" title="smile" alt="smile" height="20" width="20" src="http://localhost:8000/static/img/emojis/smile.png" align="top"></b>'
|
||||||
|
@ -49,11 +54,7 @@ def test_proccessor_valid_us_reference():
|
||||||
UserStoryBack = references.UserStory
|
UserStoryBack = references.UserStory
|
||||||
references.UserStory = MockModelWithInstance
|
references.UserStory = MockModelWithInstance
|
||||||
|
|
||||||
DummyProject = DummyClass()
|
result = references.references(dummy_project, "**#us1**")
|
||||||
DummyProject.id = 1
|
|
||||||
DummyProject.slug = "test"
|
|
||||||
|
|
||||||
result = references.references(DummyProject, "**#us1**")
|
|
||||||
assert result == '**[#us1](/#/project/test/user-story/1 "test-subject")**'
|
assert result == '**[#us1](/#/project/test/user-story/1 "test-subject")**'
|
||||||
|
|
||||||
references.UserStory = UserStoryBack
|
references.UserStory = UserStoryBack
|
||||||
|
@ -65,14 +66,10 @@ def test_proccessor_invalid_us_reference():
|
||||||
def filter(*args, **kwargs):
|
def filter(*args, **kwargs):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
DummyProject = DummyClass()
|
|
||||||
DummyProject.id = 1
|
|
||||||
DummyProject.slug = "test"
|
|
||||||
|
|
||||||
UserStoryBack = references.UserStory
|
UserStoryBack = references.UserStory
|
||||||
references.UserStory = MockModelEmpty
|
references.UserStory = MockModelEmpty
|
||||||
|
|
||||||
result = references.references(DummyProject, "**#us1**")
|
result = references.references(dummy_project, "**#us1**")
|
||||||
assert result == "**#us1**"
|
assert result == "**#us1**"
|
||||||
|
|
||||||
references.UserStory = UserStoryBack
|
references.UserStory = UserStoryBack
|
||||||
|
@ -87,11 +84,7 @@ def test_proccessor_valid_issue_reference():
|
||||||
IssueBack = references.Issue
|
IssueBack = references.Issue
|
||||||
references.Issue = MockModelWithInstance
|
references.Issue = MockModelWithInstance
|
||||||
|
|
||||||
DummyProject = DummyClass()
|
result = references.references(dummy_project, "**#issue1**")
|
||||||
DummyProject.id = 1
|
|
||||||
DummyProject.slug = "test"
|
|
||||||
|
|
||||||
result = references.references(DummyProject, "**#issue1**")
|
|
||||||
assert result == '**[#issue1](/#/project/test/issues/1 "test-subject")**'
|
assert result == '**[#issue1](/#/project/test/issues/1 "test-subject")**'
|
||||||
|
|
||||||
references.Issue = IssueBack
|
references.Issue = IssueBack
|
||||||
|
@ -103,14 +96,10 @@ def test_proccessor_invalid_issue_reference():
|
||||||
def filter(*args, **kwargs):
|
def filter(*args, **kwargs):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
DummyProject = DummyClass()
|
|
||||||
DummyProject.id = 1
|
|
||||||
DummyProject.slug = "test"
|
|
||||||
|
|
||||||
IssueBack = references.Issue
|
IssueBack = references.Issue
|
||||||
references.Issue = MockModelEmpty
|
references.Issue = MockModelEmpty
|
||||||
|
|
||||||
result = references.references(DummyProject, "**#issue1**")
|
result = references.references(dummy_project, "**#issue1**")
|
||||||
assert result == "**#issue1**"
|
assert result == "**#issue1**"
|
||||||
|
|
||||||
references.Issue = IssueBack
|
references.Issue = IssueBack
|
||||||
|
@ -125,11 +114,7 @@ def test_proccessor_valid_task_reference():
|
||||||
TaskBack = references.Task
|
TaskBack = references.Task
|
||||||
references.Task = MockModelWithInstance
|
references.Task = MockModelWithInstance
|
||||||
|
|
||||||
DummyProject = DummyClass()
|
result = references.references(dummy_project, "**#task1**")
|
||||||
DummyProject.id = 1
|
|
||||||
DummyProject.slug = "test"
|
|
||||||
|
|
||||||
result = references.references(DummyProject, "**#task1**")
|
|
||||||
assert result == '**[#task1](/#/project/test/tasks/1 "test-subject")**'
|
assert result == '**[#task1](/#/project/test/tasks/1 "test-subject")**'
|
||||||
|
|
||||||
references.Task = TaskBack
|
references.Task = TaskBack
|
||||||
|
@ -141,14 +126,10 @@ def test_proccessor_invalid_task_reference():
|
||||||
def filter(*args, **kwargs):
|
def filter(*args, **kwargs):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
DummyProject = DummyClass()
|
|
||||||
DummyProject.id = 1
|
|
||||||
DummyProject.slug = "test"
|
|
||||||
|
|
||||||
TaskBack = references.Task
|
TaskBack = references.Task
|
||||||
references.Task = MockModelEmpty
|
references.Task = MockModelEmpty
|
||||||
|
|
||||||
result = references.references(DummyProject, "**#task1**")
|
result = references.references(dummy_project, "**#task1**")
|
||||||
assert result == "**#task1**"
|
assert result == "**#task1**"
|
||||||
|
|
||||||
references.Task = TaskBack
|
references.Task = TaskBack
|
||||||
|
@ -156,3 +137,40 @@ def test_proccessor_invalid_task_reference():
|
||||||
def test_proccessor_invalid_type_reference():
|
def test_proccessor_invalid_type_reference():
|
||||||
result = references.references(None, "**#invalid1**")
|
result = references.references(None, "**#invalid1**")
|
||||||
assert result == "**#invalid1**"
|
assert result == "**#invalid1**"
|
||||||
|
|
||||||
|
def test_render_wiki_strong():
|
||||||
|
assert render(dummy_project, "**test**") == "<p><strong>test</strong></p>"
|
||||||
|
|
||||||
|
def test_render_absolute_link():
|
||||||
|
assert render(dummy_project, "[test](/test)") == "<p><a href=\"/test\">test</a></p>"
|
||||||
|
|
||||||
|
def test_render_relative_link():
|
||||||
|
assert render(dummy_project, "[test](test)") == "<p><a href=\"test\">test</a></p>"
|
||||||
|
|
||||||
|
def test_render_wikilink():
|
||||||
|
expected_result = "<p><a class=\"wikilink\" href=\"#/project/test/wiki/test\">test</a></p>"
|
||||||
|
assert render(dummy_project, "[[test]]") == expected_result
|
||||||
|
|
||||||
|
def test_render_wikilink_with_custom_title():
|
||||||
|
expected_result = "<p><a class=\"wikilink\" href=\"#/project/test/wiki/test\">custom</a></p>"
|
||||||
|
assert render(dummy_project, "[[test|custom]]") == expected_result
|
||||||
|
|
||||||
|
def test_render_absolute_image():
|
||||||
|
assert render(dummy_project, "") == "<p><img alt=\"test\" src=\"/test.png\" /></p>"
|
||||||
|
|
||||||
|
def test_render_relative_image():
|
||||||
|
assert render(dummy_project, "") == "<p><img alt=\"test\" src=\"test.png\" /></p>"
|
||||||
|
|
||||||
|
# def test_render_wikilink_attachment():
|
||||||
|
# assert render(dummy_project, "![[test.png]]") == "<p><img alt=\"test.png\" src=\"test.png\" /></p>"
|
||||||
|
#
|
||||||
|
# def test_render_wikilink_attachment_with_custom_alt():
|
||||||
|
# assert render(dummy_project, "![[test.png|test]]") == "<p><img alt=\"test\" src=\"test.png\" /></p>"
|
||||||
|
|
||||||
|
def test_render_triple_quote_code():
|
||||||
|
expected_result = "<div class=\"codehilite\"><pre><span class=\"n\">print</span><span class=\"p\">(</span><span class=\"s\">"test"</span><span class=\"p\">)</span>\n</pre></div>"
|
||||||
|
assert render(dummy_project, "```\nprint(\"test\")\n```") == expected_result
|
||||||
|
|
||||||
|
def test_render_triple_quote_and_lang_code():
|
||||||
|
expected_result = "<div class=\"codehilite\"><pre><span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s\">"test"</span><span class=\"p\">)</span>\n</pre></div>"
|
||||||
|
assert render(dummy_project, "```python\nprint(\"test\")\n```") == expected_result
|
||||||
|
|
Loading…
Reference in New Issue