# -*- coding: latin-1 -*- """ Post Markup Author: Will McGugan (http://www.willmcgugan.com) """ __version__ = "1.0.4" import re from urllib import quote, unquote, quote_plus from urlparse import urlparse, urlunparse from copy import copy pygments_available = True try: from pygments import highlight from pygments.lexers import get_lexer_by_name, ClassNotFound from pygments.formatters import HtmlFormatter except ImportError: pygments_available = False def create(include=None, exclude=None, use_pygments=True): """Create a postmarkup object that coverts bbcode to XML snippets. include -- List or similar iterable containing the names of the tags to use If omitted, all tags will be used exclude -- List or similar iterable containing the names of the tags to exclude. If omitted, no tags will be excluded use_pygments -- If True, Pygments (http://pygments.org/) will be used for the code tag, otherwise it will use
code""" markup = PostMarkup() def add_tag(name, tag_class, *args): if include is None or name in include: if exclude is not None and name in exclude: return markup.add_tag(name, tag_class, *args) add_tag(u'b', SimpleTag, u'b', u'strong') add_tag(u'i', SimpleTag, u'i', u'em') add_tag(u'u', SimpleTag, u'u', u'u') add_tag(u's', SimpleTag, u's', u'strike') add_tag(u'link', LinkTag, u'link') add_tag(u'url', LinkTag, u'url') add_tag(u'quote', QuoteTag) add_tag(u'img', ImgTag, u'img') add_tag(u'wiki', SearchTag, u'wiki', u"http://en.wikipedia.org/wiki/Special:Search?search=%s", u'wikipedia.com') add_tag(u'google', SearchTag, u'google', u"http://www.google.com/search?hl=en&q=%s&btnG=Google+Search", u'google.com') add_tag(u'dictionary', SearchTag, u'dictionary', u"http://dictionary.reference.com/browse/%s", u'dictionary.com') add_tag(u'dict', SearchTag, u'dict', u"http://dictionary.reference.com/browse/%s", u'dictionary.com') add_tag(u'list', ListTag) add_tag(u'*', ListItemTag) if use_pygments: assert pygments_available, "Could Not import pygments (http://pygments.org/)" add_tag(u'code', PygmentsCodeTag, u'code') else: add_tag(u'code', SimpleTag, u'code', u'pre') return markup _bbcode_postmarkup = None def render_bbcode(bbcode, encoding="ascii"): """Renders a bbcode string in to XHTML. This is a shortcut if you don't need to customize any tags. bbcode -- A string containing the bbcode encoding -- If bbcode is not unicode, then then it will be encoded with this encoding (defaults to 'ascii'). Ignore the coding if you already have a unicode string """ global _bbcode_postmarkup if _bbcode_postmarkup is None: _bbcode_postmarkup = create() return _bbcode_postmarkup(bbcode, encoding) re_html=re.compile('<.*?>|\&.*?\;') def textilize(s): """Remove markup from html""" return re_html.sub("", s) re_excerpt = re.compile(r'\[".*?\]+?.*?\[/".*?\]+?', re.DOTALL) re_remove_markup = re.compile(r'\[.*?\]', re.DOTALL) def remove_markup(post): """Removes html tags from a string.""" return re_remove_markup.sub("", post) def get_excerpt(post): """Returns an excerpt between ["] and [/"] post -- BBCode string""" match = re_excerpt.search(post) if match is None: return "" excerpt = match.group(0) excerpt = excerpt.replace(u'\n', u"
%s" class SearchTag(TagBase): """ Creates a link to a search term. """ def __init__(self, name, url, label=u""): TagBase.__init__(self, name) self.url = url self.search = u"" self.label = label or name def __unicode__(self): link = u''%self.url if u'%' in link: return link%quote_plus(self.get_tag_contents().encode('latin-1')) else: return link def open(self, open_pos): self.open_pos = open_pos return TagStringify(self._open, self.raw) def close(self, close_pos, content): self.close_pos = close_pos self.content = content return TagStringify(self._close, self.raw) def _open(self): if self.params: search=self.params else: search=self.get_tag_contents() link = u''%self.url if u'%' in link: return link%quote_plus(search.encode('latin-1')) else: return link def _close(self): if self.label: return u''+self.annotate_link(self.label) else: return u'' def annotate_link(self, domain): return u" [%s]"%domain class ImgTag(TagBase): def __init__(self, name): TagBase.__init__(self, name) self.enclosed=True def open(self, open_pos): self.open_pos = open_pos return TagStringify(self._open, self.raw) def close(self, close_pos, content): self.close_pos = close_pos self.content = content return TagStringify(self._close, self.raw) def _open(self): contents = self.get_raw_tag_contents() contents = contents.replace(u'"', "%22") return u'
'%(self.params) def close(self, close_pos, content): return u"
%s'%contents formatter = HtmlFormatter(linenos=False, cssclass="code") code = self.get_raw_tag_contents() result = highlight(code, lexer, formatter) return result + u"\n" # http://effbot.org/zone/python-replace.htm class MultiReplace: def __init__(self, repl_dict): # "compile" replacement dictionary # assume char to char mapping charmap = map(chr, range(256)) for k, v in repl_dict.items(): if len(k) != 1 or len(v) != 1: self.charmap = None break charmap[ord(k)] = v else: self.charmap = string.join(charmap, "") return # string to string mapping; use a regular expression keys = repl_dict.keys() keys.sort() # lexical order keys.reverse() # use longest match first pattern = "|".join(re.escape(key) for key in keys) self.pattern = re.compile(pattern) self.dict = repl_dict def replace(self, str): # apply replacement dictionary to string if self.charmap: return string.translate(str, self.charmap) def repl(match, get=self.dict.get): item = match.group(0) return get(item, item) return self.pattern.sub(repl, str) class StringToken(object): def __init__(self, raw): self.raw = raw def __unicode__(self): ret = PostMarkup.standard_replace.replace(self.raw) return ret def Escape(s): return PostMarkup.standard_replace.replace(s.rstrip('\n')) class PostMarkup(object): standard_replace = MultiReplace({ u'<':u'<', u'>':u'>', u'&':u'&', u'\n':u'
%s"%str(test.encode("ascii", "xmlcharrefreplace")) print u"
%s
"%str(post_markup(test).encode("ascii", "xmlcharrefreplace")) print u"