Re: comments enhancements
Steven Armstrong <[email protected]>
| Newsgroups | gmane.comp.web.pyblosxom.devel |
|---|---|
| Message-ID | <[email protected]> |
I also made a few local changes. The attached patch includes Will's patch from: http://sourceforge.net/mailarchive/message.php?msg_id=10245717 It adds the following: 1. drop the url if it's not absolute. this prevents links like: http://host.domain.tld/weblog/www.someone.tld 2. Secret number image thingy. Depends on my plugin 'nospam', which itself depends on 'session'. They can be found here: http://www.c-area.ch/code/pyblosxom/plugins/ 3. Remember what the user entered, so in case of an error (anti-spam check failed), the form can be populated with the users entries. The following variables are made available in the comment-form template: $cmt_title, $cmt_author, $cmt_email, $cmt_url, $cmt_body, $cmt_nospam_error I use them like this: <input name="author" id="author" type="text" value="$cmt_author" /> <input name="email" id="email" type="text" value="$cmt_email" /> ... <div style="color:red;">$cmt_nospam_error</div> cheers Steven note: I changed Wills patch no.3 back to what it was before cause it fux0r'd me. Both versions are in the patch, just comment/uncomment the one you like.
comments.py.diff
(text/plain, 12.5 KB)
Index: comments.py
===================================================================
RCS file: /cvsroot/pyblosxom/pyblosxom/contrib/plugins/comments/plugins/comments.py,v
retrieving revision 1.39
diff -u -r1.39 comments.py
--- comments.py 20 Nov 2004 08:14:18 -0000 1.39
+++ comments.py 9 Dec 2004 01:49:28 -0000
@@ -11,6 +11,9 @@
this defaults to datadir + "comments".
comment_ext - the file extension used to denote a comment file.
this defaults to "cmt".
+ comment_draft_ext - the file extension used for new comments that have
+ not been manually approved by you. this defaults
+ to comment_ext (i.e. there is no draft stage)
comment_smtp_server - the smtp server to send comments notifications
through.
@@ -18,6 +21,9 @@
If you omit this, the from address will be the
e-mail address as input in the comment form
comment_smtp_to - the person to send comment notifications to.
+ comment_rejected_words - the list of words that will cause automatic
+ rejection of the comment--this is a very
+ poor man's spam reducer.
Comments are stored 1 per file in a parallel hierarchy to the datadir
@@ -36,15 +42,23 @@
Also, for any entry that you don't want to have comments, just add
"nocomments" to the properties of the entry.
+
+Revisions:
+ 1.4 (20041209) - Steven Armstrong - integrated Will Guaraldi's patch: http://sourceforge.net/mailarchive/message.php?msg_id=10245717
+ 1.3 (20041205) - Steven Armstrong - added remember/forget calls
+ 1.2 (20041201) - Steven Armstrong - hacked so that comments are only shown if the QS contains ?cmt=1
+ 1.1 (20041116) - Steven Armstrong - integration with nospam plugin, human verification system
+ 1.0 ? original writing.
"""
+__author__ = "?"
+__version__ = "1.4 (20041209)"
+__url__ = "?"
+__description__ = "Comments Plugin"
+
import cgi, glob, os.path, re, time, cPickle, os
from xml.sax.saxutils import escape
from Pyblosxom import tools
from Pyblosxom.entries.base import EntryBase
-try:
- from email.Utils import formatdate
-except ImportError:
- from rfc822 import formatdate
def cb_start(args):
@@ -59,6 +73,8 @@
config['comment_dir'] = os.path.join(config['datadir'],'comments')
if not config.has_key('comment_ext'):
config['comment_ext'] = 'cmt'
+ if not config.has_key('comment_draft_ext'):
+ config['comment_draft_ext'] = config['comment_ext']
def verify_installation(request):
config = request.getConfiguration()
@@ -81,7 +97,7 @@
print("Missing comment SMTP property: '%s'" % i)
retval = 0
- optional_keys = ['comment_dir', 'comment_ext']
+ optional_keys = ['comment_dir', 'comment_ext', 'comment_draft_ext']
for i in optional_keys:
if not config.has_key(i):
print("missing optional property: '%s'" % i)
@@ -131,7 +147,7 @@
@returns: a string containing the regular expression for comment entries
"""
- cmtDir = os.path.join(config['comment_dir'],entry['absolute_path'])
+ cmtDir = os.path.join(config['comment_dir'], entry['absolute_path'])
cmtExpr = os.path.join(cmtDir,entry['fn']+'-*.'+config['comment_ext'])
return cmtExpr
@@ -170,12 +186,17 @@
cmt['cmt_time'] = cmt['cmt_pubDate'] # timestamp as float for comment anchor
cmt['cmt_pubDate'] = time.ctime(float(cmt['cmt_pubDate']))
story.close()
+ # added new variable cmt_author_link.
+ if cmt['cmt_link'] != '':
+ cmt['cmt_author_link'] = '<a href="%s">%s</a>' % (cmt['cmt_link'], cmt['cmt_author'])
+ else:
+ cmt['cmt_author_link'] = cmt['cmt_author']
except:
tools.log("Couldn't read: ", filename)
story.close()
return cmt
-def writeComment(config, data, comment):
+def writeComment(request, config, data, comment):
"""
Write a comment
@@ -187,32 +208,39 @@
@param comment: dict containing comment info
@type comment: dict
+
+ @return: The success or failure of creating the comment.
+ @rtype: string
"""
entry = data['entry_list'][0]
- cdir = os.path.join(config['comment_dir'],entry['absolute_path'])
+ cdir = os.path.join(config['comment_dir'], entry['absolute_path'])
cdir = os.path.normpath(cdir)
if not os.path.isdir(cdir):
os.makedirs(cdir)
- cfn = os.path.join(cdir,entry['fn']+"-"+comment['pubDate']+"."+config['comment_ext'])
-
+ cfn = os.path.join(cdir,entry['fn']+"-"+comment['pubDate']+"."+config['comment_draft_ext'])
+
+ argdict = { "request": request, "comment": comment }
+ reject = tools.run_callback("comment_reject",
+ argdict,
+ donefunc=lambda x:x)
+ if reject == 1:
+ return "Comment rejected."
+
# write comment
cfile = None
try :
cfile = open(cfn, "w")
except:
tools.log("Couldn't open comment file %s for writing" % cfn)
- return
+ return "Error: Couldn't open comment file for writing."
else:
pass
def makeXMLField(name, field):
return "<"+name+">"+cgi.escape(field[name])+"</"+name+">\n";
try:
- try:
- comment[description].decode(utf-8)
- cfile.write('<?xml version=1.0 encoding=utf-8?>\n')
- except:
- cfile.write('<?xml version="1.0" encoding="iso-8859-1"?>\n')
+ encoding = config.get('blog_encoding', 'iso-8859-1')
+ cfile.write('<?xml version="1.0" encoding="%s"?>\n' % encoding)
cfile.write("<item>\n")
cfile.write(makeXMLField('title',comment))
cfile.write(makeXMLField('author',comment))
@@ -226,14 +254,14 @@
tools.log("Error writing comment data for ", cfn)
cfile.close()
- #write latest pickle
+ # write latest pickle
latest = None
latestFilename = os.path.join(config['comment_dir'],'LATEST.cmt')
try:
latest = open(latestFilename,"w")
except:
tools.log("Couldn't open latest comment pickle for writing")
- return
+ return "Error: Couldn't open latest comment pickle for writing."
else:
modTime = float(comment['pubDate'])
@@ -244,23 +272,31 @@
# should log or e-mail
if latest:
latest.close()
- return
+ return "Error: Problem dumping the pickle."
# if the right config keys are set, notify by e-mail
- if config.has_key('comment_smtp_server') and \
- config.has_key('comment_smtp_to'):
+ if config.has_key('comment_smtp_server') and config.has_key('comment_smtp_to'):
+
+ # import the formatdate function which is in a different
+ # place in Python 2.3 and up.
+ try:
+ from email.Utils import formatdate
+ except ImportError:
+ from rfc822 import formatdate
+
import smtplib
author = escape_SMTP_commands(clean_author(comment['author']))
description = escape_SMTP_commands(comment['description'])
+ message = [] # moved this up here cause otherwise the except: block has no access to it
+
if comment.has_key('email'):
email = comment['email']
else:
- email = config['comment_smtp_from']
+ email = config.get('comment_smtp_from', "blah-C/[email protected]")
try:
server = smtplib.SMTP(config['comment_smtp_server'])
curl = config['base_url']+'/'+entry['file_path']
- message = []
message.append("From: %s" % email)
message.append("To: %s" % config["comment_smtp_to"])
message.append("Date: %s" % formatdate(modTime))
@@ -273,7 +309,12 @@
server.quit()
except:
tools.log("Error sending mail: %s" % message)
- pass
+ return "Error: Problem sending notification email."
+
+ msg = "Success: Comment has been registered."
+ if config["comment_draft_ext"] != config["comment_ext"]:
+ msg = msg + " Comment will not appear until it has been manually approved by the owner of this web-site."
+ return msg
def clean_author(s):
"""
@@ -383,7 +424,42 @@
return body
-
+
+_cmt_messages = {}
+def _rememberComment(form, url):
+ _cmt_messages["cmt_title"] = form['title'].value
+ _cmt_messages["cmt_author"] = form['author'].value
+ _cmt_messages["cmt_email"] = (form.has_key('email') and [form['email'].value] or [''])[0]
+ _cmt_messages["cmt_url"] = url
+ _cmt_messages["cmt_body"] = form['body'].value
+
+def _forgetComment():
+ for k, v in _cmt_messages.items():
+ _cmt_messages[k] = ""
+
+
+def _nospam_check(request, url):
+ session = request.getSession()
+ form = request.getHttp()['form']
+
+ try:
+ nospam = int(form["nospam"].value)
+ sess_nospam = int(session["nospam"])
+ except:
+ nospam = 0
+ sess_nospam = 1
+
+ if nospam != sess_nospam:
+ # remember what the user entered so we can populate the form with it, see cb_story_end
+ _cmt_messages["cmt_nospam_error"] = "Secret number did not match."
+ _rememberComment(form, url)
+ return False
+ else:
+ # forget what the user entered, see cb_story_end
+ _forgetComment()
+ return True
+
+
def cb_prepare(args):
"""
Handle comment related HTTP POST's.
@@ -398,23 +474,30 @@
if form.has_key("title") and form.has_key("author") and form.has_key("body"):
- body = form['body'].value
-
- body = sanitize(body)
-
# Check if the form has a URL
url = (form.has_key('url') and [form['url'].value] or [''])[0]
-
- cdict = {'title': form['title'].value, \
- 'author' : form['author'].value, \
- 'pubDate' : str(time.time()), \
- 'link' : url, \
- 'source' : '', \
- 'description' : body }
- if form.has_key('email'):
- cdict['email'] = form['email'].value
-
- writeComment(config, data, cdict)
+ # Drop the url if it's not absolute.
+ # One could add the http:// to it instead.
+ if not "://" in url:
+ url = ""
+
+ # anti-spam check
+ if _nospam_check(request, url):
+ # only save the comment it the nospam check returns True
+
+ body = form['body'].value
+ body = sanitize(body)
+
+ cdict = {'title': form['title'].value, \
+ 'author' : form['author'].value, \
+ 'pubDate' : str(time.time()), \
+ 'link' : url, \
+ 'source' : '', \
+ 'description' : body }
+ if form.has_key('email'):
+ cdict['email'] = form['email'].value
+
+ data["comment_message"] = writeComment(request, config, data, cdict)
def cb_head(args):
@@ -439,12 +522,20 @@
entry = args['entry']
template = args['template']
request = args["request"]
+ form = request.getHttp()['form']
config = request.getConfiguration()
+
if len(renderer.getContent()) == 1 \
and renderer.flavour.has_key('comment-story') \
+ and form.has_key("cmt") \
and not entry.has_key("nocomments"):
template = renderer.flavour.get('comment-story','')
+ # make this additive so that it doesn't toally fux0r me.
+ #args['template'] = args['template'] + template
+ # sar: changed this back again so that it doesn't fux0r me. Pick your poison.
args['template'] = template
+ else:
+ _forgetComment()
entry['num_comments'] = getCommentCount(entry, config)
return template
@@ -454,9 +545,18 @@
entry = args['entry']
template = args['template']
request = args["request"]
+ form = request.getHttp()['form']
+
+ # populate entry dict with the data remembered from the comment form,
+ # resp. remove the data we remembered before
+ if len(_cmt_messages) > 0:
+ for k,v in _cmt_messages.items():
+ entry[k] = v
+
config = request.getConfiguration()
if len(renderer.getContent()) == 1 \
and renderer.flavour.has_key('comment-story') \
+ and form.has_key("cmt") \
and not entry.has_key("nocomments"):
output = []
entry['comments'] = readComments(entry, config)