"""Comments module for aether In your _code.py, insert something like from comments import * and then use the new [comments], [comments_form], and [comments_closed] markup in your pages. You must always explicitly enable comments on a page by writing [comments] [comments_form] You can later disable comments by changing it to simply [comments] Comments can be written in an HTML subset, specified by safe_tags. These can have attributes which are specified in safe_attrs as (tag,attr) pairs. This module will replace your 'markup_blog' to add the [NNN comments] link. """ from __main__ import * __all__ = ['markup_author', 'markup_blog', 'markup_comments', 'markup_comments_if_blogentry', 'markup_comments_closed', 'markup_comments_form', 'handle_comment', 'page_markup' ] # For python2.2 try: enumerate except NameError: def enumerate(l): return zip(range(len(l)), l) import cgi, sgmllib safe_tags = [ 'a', 'b', 'blockquote', 'br', 'cite', 'code', 'dd', 'div', 'dl', 'dt', 'em', 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'strong', 'sub', 'sup', 'tt', 'ul' ] safe_attrs = [('a', 'href'), ('a', 'rel'), ('img', 'src')] def rfind(l, k): r = -1 for i, x in enumerate(l): if x == k: r = i return r class HTMLStripper(sgmllib.SGMLParser): def __init__(self, *args, **kw): sgmllib.SGMLParser.__init__(self, *args, **kw) def handle_data(self, data): if data: self.result.append(cgi.escape(data)) def unknown_starttag(self, tag, attrs): if tag not in safe_tags: return self.tagstack.append(tag) attrs = [(k, cgi.escape(v, True)) for (k, v) in attrs if (tag, k) in safe_attrs] tag = "<%s"%tag if attrs: tag += " " + " ".join(["%s=\"%s\"" % i for i in attrs]) tag += ">" self.result.append(tag) def handle_img(self, attrs): src = '' for k, v in attrs: if k == 'src': src = v src = src.strip('"\'') if not src.startswith("http://") and not src.startswith("ftp://"): return self.unknown_starttag('img', [('src', src)]) def start_a(self, attrs): href = '' for k, v in attrs: if k == 'href': href = v href = href.strip('"\'') if not href.startswith("http://") and not href.startswith("ftp://"): return self.unknown_starttag('a', [('href', href), ('rel', 'nofollow')]) def unknown_endtag(self, tag): if tag in safe_tags: index = rfind(self.tagstack, tag) if index != -1: self.result.append("%s>" % tag) del self.tagstack[index:] def reset(self): self.result = [] self.tagstack = [] sgmllib.SGMLParser.reset(self) def getvalue(self): return "".join(self.result) from __main__ import * def markup_comments(text, meta): location = meta['name'] names = list_names(location) #names.reverse() result = [] for name, full_name in names: end = re.match('c[0-9]+', name) if not end: continue comment_meta = {'author': 'anonymous', 'title': 'No Title', 'name': full_name, 'outer_name': meta['name']} comment_text = load(full_name) comment_text = markup(comment_text, comment_meta) comment_time = long(name[1:end.end()]) result.append("[link [page %s][html
by %s on %s%s]][html
%s]"
% (
full_name,
quote_markup(comment_meta.get('author')),
describe_time(comment_time),
quote_markup(comment_meta.get('title')),
quote_markup(comment_text)))
if result:
result.insert(0, "[html ][heading Comments:]")
return markup("".join(result), meta, False)
def markup_comments_closed(text, meta):
meta['has_comments_form'] = False
return ""
def markup_comments_form(text, meta, parent = None, title="", author="", comment=""):
if meta.has_key('has_comments_form'): return ''
meta['has_comments_form'] = True
if parent is None:
parent = meta.get('name', '')
form = make_form(
'
Your name: | ' ' |
Title: | ' ' |
Comment: (Restricted HTML)' ' | |
' ' |
' ' ' % ( quote_html(author), quote_html(title), quote_html(comment)), action='comment', parent=parent) return markup('[heading Add a comment:]', meta) + form def is_comment(name): if not isinstance(name, (str,unicode)) or not re.search('(^|/)c[0-9][^/]*$', name): return False return True def is_blogentry(name): if not isinstance(name, (str,unicode)) or not re.search('(^|/)[0-9][^/]*$', name): return False return True def accepts_comments(name): meta = {} markup(load(name), meta, False) if meta.has_key('has_comments_form'): return meta.get('has_comments_form') return is_blogentry(name) def handle_comment(name, query): comment_text = query.get('comment', '') comment_title = query.get('title', '') comment_author = query.get('author', '') comment_parent = query.get('parent', '') if not accepts_comments(comment_parent): return make_error("Comments are not permitted on this page") h = HTMLStripper() h.feed(comment_text) comment_text = h.getvalue() comment_title = re.sub(r'\[|\]|\\', lambda s: "\\" + s.group(0), comment_title) comment_author = re.sub(r'\[|\]|\\', lambda s: "\\" + s.group(0), comment_author) comment_text = re.sub(r'\[|\]|\\', lambda s: "\\" + s.group(0), comment_text) open("/tmp/comment.txt", "w").write(comment_text) if 'save' in query: result = "[title %s][html %s][author %s]" % ( comment_title, comment_text, comment_author) new_name = path.join(comment_parent, "c" + now()) save(result, new_name) return make_redirect(comment_parent) else: result ="[html
by %s (preview)%s
%s]" % (
comment_author, comment_title, comment_text)
return make_http_page(result + "[html " + markup_comments_form('', {}, comment_parent, comment_title, comment_author, comment_text) + "]")
def markup_author(text, meta):
"""Specify the author of a blog entry. Put at the bottom, as it appends "--
authorname" to the HTML."""
meta['author'] = text
if meta.has_key('outer_name'):
return u''
else:
return u'
'
def markup_comments_if_blogentry(text, meta):
if meta.has_key('outer_name'):
return u''
name = meta.get('name')
if is_comment(name):
return markup(u'[line][line [link [page ..] Return to original article]]', meta, False)
if not is_blogentry(name):
return u''
return markup(u'[comments]\n[comments_form]', meta, False)
def count_comments(location):
names = list_names(location)
result = 0
for name, full_name in names:
end = re.match('c[0-9]+', name)
if not end: continue
result += 1
return result
if page_markup.find("[comments_if_blogentry]") == -1:
page_markup = page_markup.replace(
"[_insert_html _page]", "[_insert_html _page]\n[comments_if_blogentry]")
context_blog = [ 'location', 'author', 'email', 'tagline', 'length',
'tail_length', 'keyword', 'no_interface' ]
def markup_blog(text, meta, location=None, author=None, email=None, tagline=None, length=u'10', tail_length=u'20', keyword=u'', no_interface=None):
""" Insert a blog.
Optional parameters:
author - Your name.
email - Your email address.
tagline - A short description of the blog.
length - Number of entries to display on this page (default is 10).
tail-length - Number of headings for older entries to display
(default is 20).
location - Path to blog entries (defaults to current page, which is
what you want unless you're doing something weird).
no-interface - (flag) no [new] box, atom feed, or older entries link
(allows more than one blog per page, for example a
separate blog in a sidebar. Suggest also setting up a
distinct page for the sidebar to provide atom feed,
archives, etc)
keyword - Only show blog entries with this keyword.
(note: keyword implies [no-interface], currently)
Also provides an Atom XML feed, which should be detected automatically
by news aggregators.
You will be able to create new blog entries using the [new ___]
item at the bottom of the page.
Examples:
[title Joe blog]
[blog
[author Joe]
[email joe@joe.joe]
[tagline The life and times of Joe.]
] """
if location == None:
location = meta['name']
else:
location = unquote_html(location).strip()
keyword = unquote_html(keyword).strip()
names = list_names(location)
names.reverse()
if meta.get('_blog_all'):
length = 0
shorts_length = len(names)
else:
length = int(length.strip())
shorts_length = int(tail_length.strip()) + length
result = [ ]
entries = [ ]
shorts = [ ]
n_entries = 0
latest = 0L
for name, full_name in names:
if not exists(full_name):
continue # directory
end = re.match('[0-9]*', name).end()
if end == 0:
continue
entry_text = load(full_name)
entry_meta = {'name' : full_name,
'outer_name' : meta['name']}
entry_text = markup(entry_text, entry_meta)
entry_text = entry_meta.get('summary', entry_text)
if keyword and keyword not in entry_meta.get('keywords',[ ]):
continue
entry_time = long(name[:end])
latest = max(latest, entry_time)
entry_title = entry_meta.get('title', 'Entry')
if n_entries >= length and n_entries < shorts_length:
if n_entries == length:
result.append('
')
short = (
u'
'+describe_time(entry_time)+u''+
markup_link(quote_html(entry_title), { }, page=quote_html(full_name))
)
result.append(short)
shorts.append(short)
elif n_entries < length:
if 'summary' in entry_meta:
link_text = 'read more...'
else:
link_text = '[permalink]'
c = count_comments(full_name)
if c == 1:
comment_link_text = "[1 comment]"
comment_link = ' ' + markup_link(comment_link_text, { },
page=quote_html(full_name)+"#comments")
elif c:
comment_link_text = "[%d comments]" % c
comment_link = ' ' + markup_link(comment_link_text, { },
page=quote_html(full_name)+"#comments")
else:
if entry_meta.get('has_comments_form', True):
comment_link_text = "[No comments]"
comment_link = ' ' + markup_link(comment_link_text, { },
page=quote_html(full_name)+"#comments")
else:
comment_link = ''
result.append(
u'
'+describe_time(entry_time)+u''+
u''+entry_title+u''+
u'
'+entry_text+
u'
'+ markup_link(link_text, { }, page=quote_html(full_name)) + comment_link + u'
'
)
entries.append(
u'
All older entries') entry_name = path.join(location,now()) meta['_blog_entries'] = entries meta['_blog_shorts'] = shorts #(for index page) meta['_blog_modified'] = latest if author: meta['_blog_author'] = unquote_html(author) if email: meta['_blog_email'] = unquote_html(email) if tagline: meta['_blog_tagline'] = unquote_html(tagline) atom_url = quote_html( host_url + script_base + u'?action=atom;hasname=1;name=' + quote_paranoid(meta['name']) ) meta['_head_extra'] = meta.get('_head_extra',u'') + ( u'' ) try: from __main__ import password except: password = '' if password: password_field = u'' else: password_field = u'' meta['_control_bar_extra'] = meta.get('_control_bar_extra',u'') + ( u' [atom feed] ' + make_form( u'' + password_field, action=u'edit', name=entry_name)) return string.join(result, u'')