I thought I would follow up my own post explaining how I come up with
the solution to get the comment workflow to work as I wanted, so at
least if other run into the same problem and come looking on the list
they won't find an unanswered question something I personally find
very frustrating :) It is a bit of a detailed post so apologies to
peoples inboxes.

The problems I had and things that I wanted to do with commenting in
COREBlog2 where:

1. Creating an easy to use workflow to allow my bloggers to process comments
that are submitted to their blog entries. Requirements where to allow a
simple "Publish" or "Reject" from the state drop down and that this
process be as
simple as possible, Ideally blogger would have a specific role and not
have to be made a manager in order to make the interface as simple as
possible.

2. Email notifications for comments where easy to understand and worked
in mozilla-thunderbird as by default they where unreadable in
thunderbird due to every character being spaced apart.

3. Spam via comments and trackbacks should be reduced to an absolute
minimum as to not turn off our bloggers from the process. Having them
being barraged by spam and spending all their time time rejecting
these would really reduce the appeal of a blog.

4. Commentors would be notified by email when their comment was approved.
This was something requested by our bloggers as a courteous thing to do.


1. SETTING UP A SMOOTH WORKFLOW FOR COMMENT APPROVAL

I created an editor role that had the following permissions to my
editor role in the root plone site permissions tab:


ATContentTypes: Add Document
ATContentTypes: Add Event
ATContentTypes: Add File
ATContentTypes: Add Image
ATContentTypes: Add Link
ATContentTypes: Add News Item

Access contents information
Add portal content
List folder contents
Modify portal content
Request review
View

I initially had real problems understanding what was happening in the
permissions as they didn't seem to work, but the problems was due to
me falsely assuming that all the folder objects and the articles would
by default just be aquiring their permissions from the root folder but
that was not the case. The article folder was not aquiring from the
root in case of the "modify portal content" permission nor where the
articles themselves set to aquire for that permission.

You need to look at the workflow that applies to objects as that
determines what permissions they are created with or at least the
settings of those permissions that are managed by the workflow.

There where 4 permissions managed by the cb_comments_workflow, which
was just a copy of the plone_default_workflow I had made:

Access contents information
Change portal events
Modify portal content
View

Each state has its own specific security settings which you can
control by clicking on the "Permissions" tab for that state. For
example when an object is in the "Pending" state the Reviewer role is
given all the 4 permissions while when and object is in "Published"
state only the Manager role has permission to edit the object.

For the cb_comment_workflow I just needed 3 states:

Pending (set this to the default state)
Published
Rejected ( a new state I created)

and 3 transitions:

Publish
Reject
Submit

I deleted all other states and transitions. My bloggers are added to
an Editors group which has been given the editor role. All bloggers
are also placed in the Reviewers Group.

REJECTING COMMENTS

After thinking initially that I would have the reject transition just
delete the rejected comments I thought it might be better to have a
system they are kept available for historical purposes so switching
them to a state where they are not visible to my bloggers or anonymous
users would be a better option.

One way I have thought that I could do it was to have reject be set to
make the state of the comment private and private to be only viewable by
the manager. This might have the added benefit of keeping the comments
for historical puposes. The manger could easily go in and chose the
delete option to clean out all obvious spam ones now and then but to a
person who doesn't have the manager role they would effectively deleted.

I changed the state that the reject transition changes to from visible
to private. That works the comment disappears from the review list as it
is not in the pending state but it doesn't appear to anonymous users as
the private state has the view permission only allowed in Manager and
Owner.

The manager will see the comments as still showing in recent comments
portlet but others won't. To delete the comments the manager just needs
to go to
http://simpson.wilderness.org.au:8081/test/blog/comments/folder_contents
to see the full list of comments. The ones that have been set to private
can be reviewed and cleaned out if they are spam but it might be that
ones that are rejected from legitimate people who have made
inappropriate comments could be kept, perhaps moved to another folder.
However when I tested trying to move the comments to another folder I
found that I wasn't allowed, I guess because they are strongly
associated with their parent entry.

What I found though was that if I rejected a comment as a user with the
"Editor" role they would be shown a login page as they didn't have
permission to see private comments and the default behaviour is to show
the comment in its new state. I thought that the easiest way to deal
with this would simply be to add a script to the reject transition that
redirects the user to the blog root so they can just continue on with
their review process.

This turned out to be a long exercise in frustration though as even
though I eventually figured out how to get a redirect to occur using the
ObjectMoved method which the state_change object has it would still only
work if you had permission to view objects in the private state.

This is the way you can easily do redirects on a page that you have
permissions:

return
context.REQUEST.RESPONSE.redirect(context.portal_url.getPortalPath() +
'/blog')

and you see it being used in various products. However this wouldn't do
anything when called from a script running off a transition even when
running the script logged in as manager.

The following script would perform a redirect when called from a
transition but only if you had rights to view the new object:

------------------------------
obj = state_change.object
obj.plone_log('Beginning redirect_to script')

url = obj.portal_url.getPortalObject()
obj.plone_log(url)

rootobj = context.portal_url.getPortalObject()

id = obj.id
obj.plone_log(id)

newobj = state_change.new_state
newid = newobj.id
newobj.plone_log(newid)

conid = context.id
context.plone_log(conid)

raise state_change.ObjectMoved(newobj, url)
------------------------------------

I also tried altering the comment view template to perform a redirect
but once again the permission are such that the redirect is not called
because permissions block the page before it loads the redirect.

CREATING A NEW STATE AND MODIFYING COREBLOG2

Ok I had given up on the idea of making use of the private state and
decided to see what happened if I created a state. I added a rejected
state to the cb_commments workflow and made it the state that the reject
transition moves an object to. Changing it to this state removes it from
the Review list but they still display in the comment listing and the
recent comment entries portlet.

I then had to look at changing the COREBLog2 code to not display
comments in the rejected state.

There is a portlet_global_recent_comment.pt file which is not
displayed in the portlets but offered a way to restrict the search of
comments that are displayed.

I copied this file to portlet_tws_recent_comment.pt and then added
"review_state='published'" to the portal_catalog.searchResuls keywords.

I also changed the style and the title to match the default Recent
Comments. This recent comments portlet will now only display comments
that have a state of published. Just need to swap that with the other
recent comment portlet in the left slots of the properties tab for the
blog.

The next step was to try and stop the comments from displaying in the
entry listing. It looked like the object method to do that came from the
COREBlog2/content/coreblogentry.py file and was based around a catalog
search again.

CUSTOMISING COREBLOG2 TO ONLY SHOW PUBLISHED COMMENTS

I made the following changes to 2 files to show only published comments
in the list of comments displayed with an entry.

I added the following to the COREBlog2/config.py file:

coreblogcomment_state = 'published

I then made the following changes to the
COREBlog2/content/coreblogentry.py file:

changed:

from Products.COREBlog2.config import PROJECTNAME,\
comemnt_rel_id,trackback_rel_id,\
coreblogentry_meta_type,coreblogcomment_meta_type,\
coreblogtrackback_meta_type

to

from Products.COREBlog2.config import PROJECTNAME,\
comemnt_rel_id,trackback_rel_id,\
coreblogentry_meta_type,coreblogcomment_meta_type,\
coreblogtrackback_meta_type,coreblogcomment_state

and changed this line in the getComment method:

contentFilter = {'path':path,'portal_type':coreblogcomment_meta_type}

to

contentFilter =
{'path':path,'portal_type':coreblogcomment_meta_type,'review_state':coreblogcomment_state}

the contentFilter variable is passed into a call to the searchResults
which is called via the queryCatalog in this script.

Ok that caused only published comments to be shown in the comments
listing for an entry but the count was still displaying all comments. To
fix that I just had to change the contentFilter in the countComment
method in the coreblogentry.py file to be the same as the one I had in
the getComment method.

I had to restart Zope after these changes to get them to load.

RESTRICTING ACCESS TO COMMENTS IN THE REJECTED STATE

Ok the remaining problem I have now with the comments is that I need to
restrict access to the comments folder because an anonymous user can
still go to .....t/blog/comments folder and see all comments except those
that are private or pending. What I need to do is make it so that anonymous
users don't have the right to see comments that are in an rejected state.
This will also stop them being able to access these comments by search.

Setting the permissions that you want on a particular state is quite
easy. To do it go to the workflow that is managing the content type that
you are interesting in and then click on the states tab, select the
state that you want to change permissions for and then click on the
Permissions tab.

By default it appears that when you create a new state it is set to
aquire all the permissions. I set the permissions for the rejected state
so that they wheren't aquired and the manager had rights for all
permissions and the Editor role had "View" and "Access Content
Information" permissions. The other roles had no permissions for
objects in the rejected state.

You need to click on the "Update Security Settings" button in the
portal_workflow page to apply any changes to permissions you make. Ok
that has fixed that if anonymous goes to the comments folder now they
will only see comments that are in the published state.

Ok that appears to be it as far as workflowing comments goes. Bloggers now have
a nice simple workflow and the anonymous user only sees content if they
are published.


2. FIXING CONFIRMATION EMAIL ON COMMENT SUBMISSION

The email notification that coreblog sends to people when someone
submitts a comment or trackback has a number of problems:

1. There is no subject
2. The from address is not the one specified in the blog settings but
the same as the to address.
3. The format of the email in thunderbird is unreadable with large
spaces between every character.

COREBlog doesn't use the normal mailhost that you see used in other
scripts. The author has written his own method "send_mail" in order to
bypass Zopes security and allow anonymous users the ability to send
email by submitting a comment.

What I ended up doing was changing the send_mail() method in the
COREBlogTool.py.

I changed the send_mail() from:

def send_mail(self, body,to_addr,from_addr,subject):
#
# Method to bypass Zope's security.
# For case of comment/trackback post by anonymous user
#
try:
self.MailHost.send(body,to_addr, from_addr, subject)

to:

def send_mail(self, msg):
#
# Method to bypass Zope's security.
# For case of comment/trackback post by anonymous user
#
try:
self.MailHost.send(msg)

I then worked on trying to get the message created properly.

Ok after some stuffing around to get date field right for mozilla
thunderbird people monitoring the blog now get an email that provides
the comment and the comment details with a subject in the format of
"[blog comment] comment title". I couldn't figure out exactly what was
causing the wierd formatting in mozilla but thought it could possibly be
the translate functions as they where used in the script.

Here is a diff from fcomp of the original cbaddComment.cpy and my changes:
-------------------------------------------------

FILE A: cbaddComment.cpy-original
FILE B: cbaddComment.cpy
TOTALS: 43 inserted 7 deleted 67 matched

******************** INSERT [A 15, B 15]:
from DateTime import DateTime

******************** REPLACE [A 40-41, B 41-46]:
from_addr = context.getNotify_to()
msgbody = context.translate('comment_notify_body')
******************** WITH:
from_addr = context.getNotify_from()
Date = DateTime()
commondate = DateTime.aCommon(Date)
context.plone_log(commondate)

#Get elements from the form setting them to empty strings if they don't
exist

******************** REPLACE [A 50-52, B 55-66]:
msgbody = msgbody % (elements)
msgsubject = context.translate('comment_notify_title')
mgsheader = """To: %s
******************** WITH:

#Set variables to be inserted in message
title = elements['title']
author = elements['author']
url = elements['url']
ip = elements['post_ip']
entryurl = elements['entry_url']
msgbody = elements['body']
msgsubject = '[blog comment] ' + title

message = """
To: %s

******************** INSERT [A 56, B 70-71]:
Date: %s
Subject: %s

******************** REPLACE [A 57-58, B 73-94]:
""" % (to_addr,from_addr)
cbtool.send_mail(mgsheader+msgbody, to_addr, from_addr, msgsubject)
******************** WITH:
Comment Title: %s
Author: %s
Url: %s
Client IP: %s
Entry URL: %s

%s

"""
msg = message % (
to_addr,
from_addr,
commondate,
msgsubject,
title,
author,
url,
ip,
entryurl,
msgbody
)
cbtool.send_mail(msg)
------------------------------------------

3. INSTALL ANTI-COMMENT SPAM TOOL CAPTCHA'S

- First I registered an account at http://captchas.net/registration/

- They emailed me a key to use with the username I had created.

- I then installed the plonecatpcha product using the latest version
which you can get from
http://sourceforge.net/project/showfiles.php?group_id=177298, be aware
that it unpacks just the files and not the folder so you need to create
the PloneCaptcha folder in your products directory and then unpack into
that.

- This latest version allows you to add the username and password you
got from captchas.net via the properties tab in the ZMI. To do that go
the root of your plone site and then click on plone_captcha. It will
default to displaying the properties so just enter your username and
password there.

- - I had added the following to my "cbcomment_form.pt" file above the
<div class="formControls"> line:

<div class="field"
tal:define="error errors/captcha| nothing;"
tal:attributes="class python:test(error, 'field error',
'field')">

<label i18n:translate="label_captcha_help">Verification
Code</label>
<div class="formHelp"
i18n:translate="help_plone_captcha">
This helps us prevent automated spamming.
</div>

<div tal:content="error">Validation error output</div>
<div metal:use-macro="here/captcha/macros/edit" />
</div>

as suggested in this post
http://postaria.com/pipermail/coreblog-en/2006-June/000428.html

- I had also added the validate_captcha to via the ZMI to the
portal_skins/COREBlog2/cbcomment_preview.pt file and cbentry_view file.
To save having to do this for every plone site that used the COREBlog2
product I added "validate_captcha" on to the list of validators in the
metadata files for these 2 forms cbcomment_preview.cpt.metadata and
cbentry_view.cpt.metadata.

** Note the .cpt stands for controller page template which gives you an
associated metadata file as it lets you set up validators and actions
for your page template.

validators=validateComment,validate_captcha

Trackback spam just poured in as soon as the blog got linked to an
google. I was uncertain how useful it was myself and thought that it
could just be too confusing for our bloggers so I disabled it. Setting
the permissions so that anonymous users couldn't create trackback
entries didn't appear to stop it. In the blog settings -> Edit Entries
tab I set trackbacks to "None/Hidden" and then applied the suggested
patch at http://coreblog.org/trac/coreblog2/ticket/50 now when I
create a new entry trackbacks are not shown.

4. SETUP EMAIL NOTIFICATIONS FOR COMMENTERS WHEN THEIR COMMENT IS APPROVED

It turned out to be quite a bit of stuffing around to get this to work
as i got caught up on a couple of things that wheren't documented in any
of the documentation I saw about writing scripts to send emails. This
was the fact that not all methods return strings that are in a format
that will be accepted by mailhost.send. With these you need to transform
them using the encode() method. If you get the following error when
passing a msg to mailhost.send:

Error Value
No message recipients designated

then look for some of your variables not being encoded correctly for
email and you need to use the encode method prior to passing them to
mailhost.send. If you add .encode() for email = obj.email.encode() for
example you can then get it accepted by mailhost.send.

This is what my comment_approved script that is called after the
"publish" transition:

---------------------------------------------
#This script lets people know that their comment has been published
from DateTime import DateTime

obj = state_change.object

mhost = context.MailHost
from_addr = '[EMAIL PROTECTED]'

#Get the email to send to make sure you encode it otherwise mailhost
choke on message
to_addr = obj.email.encode()

#Set the date for the date header need to use commondate format for
thunderbirds sake.
date = DateTime()
commondate = DateTime.aCommon(date)

#Get the author's name
name = obj.author.encode()

#Get the url of the blog entry
parent = obj.getParentEntry()
parenturl = parent.absolute_url()

#Get the title of the blog entry
parentTitle = parent.Title()

# the message format, %s will be filled in from data
message = """
From: %s
To: %s
Date: %s
Subject: comment on nuclear dump blog published

Hi %s

Your comment on my blog entry "%s" has been published see
%s#comments

Thank you for your input
"""

msg = message % (
from_addr,
to_addr,
commondate,
name,
parentTitle,
parenturl
)

#Log message for referece purposes
context.plone_log(msg)

mhost.send(msg)
--------------------------------------

Ok thats was it the comment process in COREBlog2 is now nice and
simple for my bloggers.

cheers
John

-- 
John Habermann
Internet Programmer, System Administrator
The Wilderness Society Inc
http://www.wilderness.org.au
_______________________________________________
COREblog-en mailing list
[email protected]
http://postaria.com/mailman/listinfo/coreblog-en
Unsubscription writing to [EMAIL PROTECTED]

Reply via email to