Browse Source

basic support for [quote] bbcode

Rafał Pitoń 8 years ago
parent
commit
a3d365fda7

+ 62 - 1
misago/markup/bbcode/blocks.py

@@ -1,6 +1,16 @@
+from __future__ import unicode_literals
+
 import re
 import re
 
 
-from markdown.blockprocessors import HRProcessor
+from django.utils.crypto import get_random_string
+import markdown
+from markdown.blockprocessors import BlockProcessor, HRProcessor
+from markdown.preprocessors import Preprocessor
+from markdown.util import etree
+
+
+QUOTE_START = get_random_string(32)
+QUOTE_END = get_random_string(32)
 
 
 
 
 class BBCodeHRProcessor(HRProcessor):
 class BBCodeHRProcessor(HRProcessor):
@@ -8,3 +18,54 @@ class BBCodeHRProcessor(HRProcessor):
 
 
     # Detect hr on any line of a block.
     # Detect hr on any line of a block.
     SEARCH_RE = re.compile(RE, re.MULTILINE | re.IGNORECASE)
     SEARCH_RE = re.compile(RE, re.MULTILINE | re.IGNORECASE)
+
+
+class QuoteExtension(markdown.Extension):
+    def extendMarkdown(self, md):
+        md.registerExtension(self)
+
+        md.preprocessors.add('misago_bbcode_quote', QuotePreprocessor(md), '_end')
+        md.parser.blockprocessors.add('misago_bbcode_quote', QuoteBlockProcessor(md.parser), '<paragraph')
+
+
+class QuotePreprocessor(Preprocessor):
+    QUOTE_BLOCK_RE = re.compile(r'''
+\[quote\](?P<text>.*?)\[/quote\]
+'''.strip(), re.IGNORECASE | re.MULTILINE | re.DOTALL);
+
+
+    def run(self, lines):
+        text = '\n'.join(lines)
+        while '[quote]' in text and '[/quote]' in text:
+            text = self.QUOTE_BLOCK_RE.sub(self.replace, text)
+        return text.split('\n')
+
+    def replace(self, matchobj):
+        text = matchobj.group('text')
+        return '\n\n{}\n\n{}\n\n{}\n\n'.format(QUOTE_START, text, QUOTE_END)
+
+
+class QuoteBlockProcessor(BlockProcessor):
+    def __init__(self, *args, **kwargs):
+        super(QuoteBlockProcessor, self).__init__(*args, **kwargs)
+        self._quote = 0
+        self._children = []
+
+    def test(self, parent, block):
+        return block.strip() == QUOTE_START or self._quote
+
+    def run(self, parent, blocks):
+        block = blocks.pop(0)
+        if block.strip() == QUOTE_START:
+            self._quote += 1
+
+        self._children.append(block)
+
+        if block.strip() == QUOTE_END:
+            self._quote -= 1
+
+        if not self._quote:
+            children, self._children = self._children[1:-1], []
+
+            blockquote = etree.SubElement(parent, 'blockquote')
+            self.parser.parseBlocks(blockquote, children)

+ 2 - 5
misago/markup/md/shortimgs.py

@@ -1,5 +1,3 @@
-import re
-
 import markdown
 import markdown
 from markdown.inlinepatterns import LinkPattern
 from markdown.inlinepatterns import LinkPattern
 from markdown.util import etree
 from markdown.util import etree
@@ -11,9 +9,8 @@ IMAGES_RE =  r'\!(\s?)\((<.*?>|([^\)]*))\)'
 class ShortImagesExtension(markdown.Extension):
 class ShortImagesExtension(markdown.Extension):
     def extendMarkdown(self, md):
     def extendMarkdown(self, md):
         md.registerExtension(self)
         md.registerExtension(self)
-        md.inlinePatterns.add('misago_short_images',
-                              ShortImagePattern(IMAGES_RE, md),
-                              '_end')
+        md.inlinePatterns.add(
+            'misago_short_images', ShortImagePattern(IMAGES_RE, md), '_end')
 
 
 
 
 class ShortImagePattern(LinkPattern):
 class ShortImagePattern(LinkPattern):

+ 12 - 0
misago/markup/md/striketrough.py

@@ -0,0 +1,12 @@
+import markdown
+from markdown.inlinepatterns import SimpleTagPattern
+
+
+STRIKETROUGH_RE = r'(~{2})(.+?)\2'
+
+
+class StriketroughExtension(markdown.Extension):
+    def extendMarkdown(self, md):
+        md.registerExtension(self)
+        md.inlinePatterns.add(
+            'misago_striketrough', SimpleTagPattern(STRIKETROUGH_RE, 'del'), '_end')

+ 9 - 1
misago/markup/parser.py

@@ -11,6 +11,7 @@ from htmlmin.minify import html_minify
 
 
 from .bbcode import blocks, inline
 from .bbcode import blocks, inline
 from .md.shortimgs import ShortImagesExtension
 from .md.shortimgs import ShortImagesExtension
+from .md.striketrough import StriketroughExtension
 from .mentions import add_mentions
 from .mentions import add_mentions
 from .pipeline import pipeline
 from .pipeline import pipeline
 
 
@@ -88,6 +89,10 @@ def md_factory(allow_links=True, allow_images=True, allow_blocks=True):
     md.inlinePatterns.add('bb_i', inline.italics, '<emphasis')
     md.inlinePatterns.add('bb_i', inline.italics, '<emphasis')
     md.inlinePatterns.add('bb_u', inline.underline, '<emphasis2')
     md.inlinePatterns.add('bb_u', inline.underline, '<emphasis2')
 
 
+    # Add ~~deleted~~
+    striketrough_md = StriketroughExtension()
+    striketrough_md.extendMarkdown(md)
+
     if not allow_links:
     if not allow_links:
         # Remove links
         # Remove links
         del md.inlinePatterns['link']
         del md.inlinePatterns['link']
@@ -103,8 +108,11 @@ def md_factory(allow_links=True, allow_images=True, allow_blocks=True):
         del md.inlinePatterns['image_link']
         del md.inlinePatterns['image_link']
 
 
     if allow_blocks:
     if allow_blocks:
-        # Add [hr], [quote] and [code] blocks
+        # Add [hr] and [quote] blocks
         md.parser.blockprocessors.add('bb_hr', blocks.BBCodeHRProcessor(md.parser), '>hr')
         md.parser.blockprocessors.add('bb_hr', blocks.BBCodeHRProcessor(md.parser), '>hr')
+
+        quote_bbcode = blocks.QuoteExtension()
+        quote_bbcode.extendMarkdown(md)
     else:
     else:
         # Remove blocks
         # Remove blocks
         del md.parser.blockprocessors['hashheader']
         del md.parser.blockprocessors['hashheader']

+ 45 - 0
misago/markup/tests/test_parser.py

@@ -252,3 +252,48 @@ Lorem ipsum: http://somewhere.com/somewhere-something/
 
 
         result = parse(test_text, MockRequest(), MockPoster(), minify=True)
         result = parse(test_text, MockRequest(), MockPoster(), minify=True)
         self.assertEqual(expected_result, result['parsed_text'])
         self.assertEqual(expected_result, result['parsed_text'])
+
+
+class StriketroughTests(TestCase):
+    def test_striketrough(self):
+        """striketrough markdown deletes test"""
+        test_text = """
+Lorem ~~ipsum, dolor~~ met.
+""".strip()
+
+        expected_result = """
+<p>Lorem <del>ipsum, dolor</del> met.</p>
+""".strip()
+
+        result = parse(test_text, MockRequest(), MockPoster(), minify=False)
+        self.assertEqual(expected_result, result['parsed_text'])
+
+
+class QuoteTests(TestCase):
+    def test_quotes(self):
+        """bbcode for quote is supported"""
+        test_text = """
+Lorem ipsum.
+[quote]Dolor met[/quote]
+[quote]Dolor <b>met</b>[/quote]
+[quote]Dolor **met**[quote]Dolor met[/quote][/quote]
+""".strip()
+
+        expected_result = """
+<p>Lorem ipsum.</p>
+<blockquote>
+<p>Dolor met</p>
+</blockquote>
+<blockquote>
+<p>Dolor &lt;b&gt;met&lt;/b&gt;</p>
+</blockquote>
+<blockquote>
+<p>Dolor <strong>met</strong></p>
+<blockquote>
+<p>Dolor met</p>
+</blockquote>
+</blockquote>
+""".strip()
+
+        result = parse(test_text, MockRequest(), MockPoster(), minify=False)
+        self.assertEqual(expected_result, result['parsed_text'])