"""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("" % 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'\n' u'' + entry_title + u'\n' + u'' + w3c_time(entry_time) + u'\n' + u'' + w3c_time(entry_time) + u'\n' + u'' + quote_html(name_to_url(full_name)) + u'\n' + u'\n'+ u'' + quote_html(entry_text) + u'\n' + u'' ) n_entries += 1 if no_interface == None: if '_blog_entries' in meta: raise Error('Two blogs on one page: ' 'please use [no-interface] flag in one of the blogs.') result.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'')