John, Nice. Thanks for this.
Rob On Thursday 15 March 2007 10:35:47 pm john habermann wrote: > 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':coreblo >gcomment_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 anborder="0" d 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 -- Robert B. Hawkins http://www.neohawk.org 440-210-1621 (home) 440-829-4993 (cell) [EMAIL PROTECTED] _______________________________________________ COREblog-en mailing list [email protected] http://postaria.com/mailman/listinfo/coreblog-en Unsubscription writing to [EMAIL PROTECTED]
