Index: quintagroup.plonecomments/trunk/docs/HISTORY.txt
===================================================================
--- quintagroup.plonecomments/trunk/docs/HISTORY.txt	(revision 1178)
+++ quintagroup.plonecomments/trunk/docs/HISTORY.txt	(revision 1201)
@@ -1,5 +1,12 @@
-=========
 Changelog
 =========
+
+
+4.0.4 (unreleased)
+==================
+
+- Added "report abuse" feature contributed by jcbrand.
+  [liebster]
+
 
 4.0.1 (2009-06-05)
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/browser/comments.pt
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/browser/comments.pt	(revision 1178)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/browser/comments.pt	(revision 1201)
@@ -4,5 +4,9 @@
                    isAnon view/is_anonymous;
                    ifModerate view/is_moderation_enabled;
-                   isModerationAllowed view/can_moderate"
+                   isModerationAllowed view/can_moderate;
+                   isAuthenticatedReportAbuseEnabled view/authenticated_report_abuse_enabled;
+                   isAjaxReportAbuseEnabled view/ajax_report_abuse_enabled;
+                   isAnonymousReportAbuseEnabled view/anonymous_report_abuse_enabled;
+                   isReportAbuseEnabled python:isAnonymousReportAbuseEnabled or not isAnon and isAuthenticatedReportAbuseEnabled"
                    i18n:domain="plone">
     <div class="discussion"
@@ -36,4 +40,5 @@
                 tal:define="indent python:reply_dict['depth']*2;
                             reply python:reply_dict['object'];
+                            comment_id python:reply_dict['object'].id;
                             isPublished python:reply.review_state=='published';"
                 tal:attributes="style string:margin-left:${indent}em;">
@@ -90,4 +95,26 @@
                            />
                 </form>
+                <tal:if tal:condition="python:isReportAbuseEnabled and isPublished">
+                    <form class="report_abuse"
+                          name="report_abuse"
+                          method="post"
+                          style="display: inline;"
+                          tal:attributes="action string:${reply/absolute_url}/report_abuse_form#discussion">
+                        <input type="hidden" name="comment_id" tal:attributes="value comment_id" />
+                        <input class="standalone"
+                            type="submit"
+                            value="Report Abuse"
+                            tal:define="onclick string:javascript:render_abuse_report_form(${comment_id})"
+                            tal:attributes="onclick python:isAjaxReportAbuseEnabled and onclick or '';
+                                            id string:input-render-abuse-cancel-${comment_id}"
+                            i18n:attributes="value label_report_abuse;"
+                            />
+                    </form>
+                    <span tal:attributes="id string:span-reply-form-holder-${comment_id}"
+                            tal:condition="isReportAbuseEnabled">
+                        <metal:macro use-macro="here/report_abuse_form/macros/form"/>
+                    </span>
+                    <span tal:attributes="id string:span-abuse-count-holder-${comment_id}"></span>
+                </tal:if>
                 <form name="publish_discussion"
                       action=""
@@ -108,5 +135,4 @@
                      i18n:domain="quintagroup.plonecomments"
                      tal:condition="not:isPublished">Comment must be approved before replies to comment accepted.</div>
-
             </div>
         </tal:getreplies>
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/browser/comments.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/browser/comments.py	(revision 1178)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/browser/comments.py	(revision 1201)
@@ -3,13 +3,17 @@
 from Acquisition import aq_inner
 from AccessControl import getSecurityManager
+
+from Products.CMFPlone.utils import IndexIterator
 from Products.CMFPlone.utils import getToolByName
+from Products.CMFFormController.ControllerState import ControllerState
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+from quintagroup.plonecomments.utils import manage_mails
 
-from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
 from plone.app.layout.viewlets import comments
+from plone.app.kss.plonekssview import PloneKSSView
 
 class CommentsViewlet(comments.CommentsViewlet):
     """A custom version of the comments viewlet
     """
-
     render = ViewPageTemplateFile('comments.pt')
 
@@ -52,2 +56,109 @@
 
         return gravatar_url
+
+    def authenticated_report_abuse_enabled(self):
+        """ """
+        portal_properties = getToolByName(self.context, 'portal_properties')
+        prop_sheet = portal_properties['qPloneComments']
+        value =  prop_sheet.getProperty('enable_authenticated_report_abuse', False)
+        return value
+
+    def anonymous_report_abuse_enabled(self):
+        """ """
+        portal_properties = getToolByName(self.context, 'portal_properties')
+        prop_sheet = portal_properties['qPloneComments']
+        value =  prop_sheet.getProperty('enable_anonymous_report_abuse', False)
+        return value
+
+    def ajax_report_abuse_enabled(self):
+        """ """
+        portal_properties = getToolByName(self.context, 'portal_properties')
+        prop_sheet = portal_properties['qPloneComments']
+        value =  prop_sheet.getProperty('enable_ajax_report_abuse', False)
+        return value
+
+    def email_from_address(self):
+        """ """
+        portal_url = getToolByName(self.context, 'portal_url')
+        portal = portal_url.getPortalObject()
+        return portal.email_from_address
+
+    def member(self):
+        """ """
+        pm = getToolByName(self.context, 'portal_membership')
+        return pm.getAuthenticatedMember()
+
+    def tabindex(self):
+        """ Needed for BBB, tabindex has been deprecated.
+        """
+        return IndexIterator()
+
+    def portal_url(self):
+        """ """
+        return getToolByName(self.context, 'portal_url')
+
+
+
+class CommentsKSS(PloneKSSView):
+    """ Operations on the report abuse form using KSS.
+    """   
+
+    def submit_abuse_report(self):
+        """ Send an email with the abuse report message and hide abuse report form.
+        """
+        errors = {}
+        context = aq_inner(self.context)
+        request = context.REQUEST
+        portal = getToolByName(self.context, 'portal_url').getPortalObject()
+        if hasattr(context, 'captcha_validator'):
+            dummy_controller_state = ControllerState(
+                                            id='comments.pt',
+                                            context=context,
+                                            button='submit',
+                                            status='success',
+                                            errors={},
+                                            next_action=None,)
+            # get the form controller
+            controller = portal.portal_form_controller
+            # send the validate script to the form controller with the dummy state object
+            controller_state = controller.validate(dummy_controller_state, request, ['captcha_validator',])
+            errors.update(controller_state.errors)
+
+        message = request.get('message')
+        if not message:
+            errors.update({'message': 'Please provide a message'})
+
+        mtool = getToolByName(self.context, "portal_membership")
+        member = mtool.getAuthenticatedMember()
+        comment_id = self.context.request.get('comment_id')
+        ksscore = self.getCommandSet('core')
+        if errors:
+            html = self.macroContent('context/report_abuse_form/macros/form',
+                                     errors=errors,
+                                     show_form=True,
+                                     tabindex=IndexIterator(),
+                                     member=member,
+                                     **request.form)
+            node = ksscore.getHtmlIdSelector('span-reply-form-holder-%s' % comment_id)
+            ksscore.replaceInnerHTML(node,  html)
+            return self.render()
+
+        # report_abuse(context, context, message, comment)
+        manage_mails(context, self.context, 'report_abuse')
+
+        html = self.macroContent('context/report_abuse_form/macros/form',
+                                 tabindex=IndexIterator(),
+                                 member=member,
+                                 **request.form)
+        node = ksscore.getHtmlIdSelector('span-reply-form-holder-%s' % comment_id)
+        html = '<br/><span style="color:red">You have reported this comment for abuse.</span>'
+        self.commands.addCommand('remove_abuse_report_form', 
+                                 node, 
+                                 comment_id=comment_id, 
+                                 html=html)
+
+        node = ksscore.getHtmlIdSelector('div-captcha-%s' % comment_id)
+        html = self.macroContent('context/report_abuse_form/macros/captcha',
+                                 **request.form)
+        ksscore.replaceInnerHTML(node,  html)
+        return self.render()
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/browser/configure.zcml
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/browser/configure.zcml	(revision 1178)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/browser/configure.zcml	(revision 1201)
@@ -2,5 +2,20 @@
     xmlns="http://namespaces.zope.org/zope"
     xmlns:browser="http://namespaces.zope.org/browser"
+    xmlns:kss="http://namespaces.zope.org/kss"
     i18n_domain="quintagroup.plonecomments">
 
+    <browser:page
+        for="*"
+        attribute="submit_abuse_report"
+        class=".comments.CommentsKSS"
+        name="kss_submit_abuse_report"
+        permission="zope2.View"
+        /> 
+
+    <kss:action
+        name="remove_abuse_report_form"
+        command_factory="selector"
+        jsfile="../skins/quintagroup_plonecomments/qcomments.js"
+        />
+
 </configure>
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/configure.zcml
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/configure.zcml	(revision 1178)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/configure.zcml	(revision 1201)
@@ -6,16 +6,17 @@
     i18n_domain="quintagroup.plonecomments">
 
-  <five:registerPackage package="." />
-  <i18n:registerTranslations directory="locales" />
+    <five:registerPackage package="." />
 
-  <include package="plone.browserlayer" />
-  <include package=".browser" />
-  <include file="skins.zcml" />
-  <include file="profiles.zcml" />
+    <include package="plone.browserlayer" />
+    <include package=".browser" />
+    <include file="skins.zcml" />
+    <include file="profiles.zcml" />
 
-  <permission
-      id="moderate.discussion"
-      title="Moderate Discussion"
-      />
+    <i18n:registerTranslations directory="locales" />
+
+    <permission
+        id="moderate.discussion"
+        title="Moderate Discussion"
+        />
 
 </configure>
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/profiles/default/jsregistry.xml
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/profiles/default/jsregistry.xml	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/profiles/default/jsregistry.xml	(revision 1201)
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<object name="portal_javascripts" meta_type="JavaScripts Registry">
+    <javascript cacheable="true" compression="safe" cookable="true"
+        enabled="true" expression="" id="qcomments.js" inline="false"/>
+</object>
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/profiles/default/kssregistry.xml
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/profiles/default/kssregistry.xml	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/profiles/default/kssregistry.xml	(revision 1201)
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<object name="portal_kss" meta_type="KSS Registry">
+ <kineticstylesheet cacheable="True" compression="safe" cookable="True"
+    enabled="1" expression="" id="qcomments.kss"/>
+</object>
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/profiles/default/propertiestool.xml
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/profiles/default/propertiestool.xml	(revision 1178)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/profiles/default/propertiestool.xml	(revision 1201)
@@ -1,4 +1,6 @@
 <?xml version="1.0"?>
-<object name="portal_properties">
+<object name="portal_properties"
+    xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+    i18n:domain="quintagroup.plonecomments">
  <object name="qPloneComments" meta_type="Plone Property Sheet">
   <property name="title">qPloneComments properties</property>
@@ -11,4 +13,7 @@
   <property name="enable_published_notification" type="boolean">True</property>
   <property name="enable_approve_notification" type="boolean">True</property>
+  <property name="enable_authenticated_report_abuse" type="boolean">True</property>
+  <property name="enable_anonymous_report_abuse" type="boolean">True</property>
+  <property name="enable_ajax_report_abuse" type="boolean">False</property>
   <property name="email_discussion_manager" type="string"></property>
   <property name="email_subject_prefix" type="string"></property>
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/prefs_comments_setup_form.cpt
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/prefs_comments_setup_form.cpt	(revision 1178)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/prefs_comments_setup_form.cpt	(revision 1201)
@@ -113,4 +113,71 @@
                     <label i18n:translate="label_anonymous_commenting" 
                            for="isEnabledAnonymousCommenting">Allow comments from Anonymous users</label>
+                </div>
+
+                <div class="field"
+                     tal:define="property_id string:enable_authenticated_report_abuse;
+                                 isEnabledReportAbuse python:props_sheet.getProperty(property_id, False);"
+                     tal:condition="python:props_sheet.hasProperty(property_id)">
+
+                    <input type="checkbox" 
+                           class="noborder"
+                           name="isEnabledReportAbuse"
+                           id="isEnabledReportAbuse"
+                           tabindex =""
+                           tal:attributes="name property_id;
+                                           value isEnabledReportAbuse;
+                                           checked python:test(isEnabledReportAbuse, 'True', '');
+                                           tabindex tabindex/next;"/>
+
+                    <label i18n:translate="label_enable_report_abuse"
+                           for="isEnabledReportAbuse">Enable authenticated users to report abusive comments</label>
+                </div>
+
+                <div class="field"
+                     tal:define="property_id string:enable_anonymous_report_abuse;
+                                 isEnabledAnonymousReportAbuse python:props_sheet.getProperty(property_id, False);"
+                     tal:condition="python:props_sheet.hasProperty(property_id)">
+
+                    <input type="checkbox" 
+                           class="noborder"
+                           name="isEnabledAnonymousReportAbuse"
+                           id="isEnabledAnonymousReportAbuse"
+                           tabindex =""
+                           tal:attributes="name property_id;
+                                           value isEnabledAnonymousReportAbuse;
+                                           checked python:test(isEnabledAnonymousReportAbuse, 'True', '');
+                                           tabindex tabindex/next;"/>
+
+                    <label i18n:translate="label_enable_anonymous_report_abuse"
+                           for="isEnabledAnonymousReportAbuse">Enable anonymous users to report abusive comments</label>
+
+                    <div class="formHelp" id="anonymous_report_abuse_help"
+                         i18n:translate="help_enable_anonymous_report_abuse">
+                        This setting will allow ALL users, including non logged in users to report abusive comments.
+                    </div>
+                </div>
+
+                <div class="field"
+                     tal:define="property_id string:enable_ajax_report_abuse;
+                                 isEnabledAjaxReportAbuse python:props_sheet.getProperty(property_id, False);"
+                     tal:condition="python:props_sheet.hasProperty(property_id)">
+
+                    <input type="checkbox" 
+                           class="noborder"
+                           name="isEnabledAjaxReportAbuse"
+                           id="isEnabledAjaxReportAbuse"
+                           tabindex =""
+                           tal:attributes="name property_id;
+                                           value isEnabledAjaxReportAbuse;
+                                           checked python:test(isEnabledAjaxReportAbuse, 'True', '');
+                                           tabindex tabindex/next;"/>
+
+                    <label i18n:translate="label_enable_ajax_report_abuse"
+                           for="isEnabledAjaxReportAbuse">Enable AJAX powered abuse reports</label>
+
+                    <div class="formHelp" id="ajax_report_abuse_help"
+                         i18n:translate="help_enable_ajax_report_abuse">
+                        This setting will only take effect if abuse reports are enabled.
+                    </div>
                 </div>
 
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/qcomments.js
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/qcomments.js	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/qcomments.js	(revision 1201)
@@ -0,0 +1,34 @@
+function render_abuse_report_form(comment_id) {
+    jq('form.report_abuse').bind("submit", function(event){
+        event.preventDefault();
+    });
+    var render_button = 'input#input-render-abuse-cancel-' + comment_id;
+    jq(render_button).attr('disabled', 'disabled');
+    var form = 'span#span-reply-form-' + comment_id;
+    jq(form).slideToggle(500);
+    var holder = 'span#span-reply-form-holder-' + comment_id;
+    var cancel_button = holder + ' input#input-report-abuse-cancel';
+    var qq = jq(cancel_button);
+    jq(cancel_button).attr('comment_id', comment_id);
+}
+
+function remove_abuse_report_form(comment_id, html) {
+    jq('form.report_abuse').bind("submit", function(event){
+        event.preventDefault();
+    });
+    var form = 'span#span-reply-form-' + comment_id;
+    jq(form).fadeOut();
+    var render_button = 'input#input-render-abuse-cancel-' + comment_id;
+    jq(render_button).attr('disabled', '');
+    if (html != undefined) {
+        var holder = 'span#span-abuse-count-holder-' + comment_id;
+        jq(holder).append(html);
+    }
+}
+
+kukit.actionsGlobalRegistry.register("remove_abuse_report_form", function(oper) {
+    var comment_id = oper.parms.comment_id;
+    var html = oper.parms.html
+    remove_abuse_report_form(comment_id, html);
+});
+kukit.commandsGlobalRegistry.registerFromAction('remove_abuse_report_form', kukit.cr.makeSelectorCommand);
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/qcomments.kss
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/qcomments.kss	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/qcomments.kss	(revision 1201)
@@ -0,0 +1,5 @@
+input.input-report-abuse-send:click {
+    evt-click-preventdefault: True;
+    action-server: kss_submit_abuse_report;
+    kss_submit_abuse_report-kssSubmitForm: currentForm();
+}
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse.cpy
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse.cpy	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse.cpy	(revision 1201)
@@ -0,0 +1,76 @@
+## Script (Python) "report_abuse"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind state=state
+##bind subpath=traverse_subpath
+##parameters=message,text_format='plain',username=None,password=None
+##title=Reply to content
+
+from Products.PythonScripts.standard import url_quote_plus
+from Products.CMFCore.utils import getToolByName
+from Products.CMFPlone import MessageFactory
+from Products.CMFPlone import PloneMessageFactory
+from quintagroup.plonecomments.utils import manage_mails
+
+_ = MessageFactory('quintagroup.plonecomments')
+
+req = context.REQUEST
+mtool = getToolByName(context, 'portal_membership')
+dtool = getToolByName(context, 'portal_discussion')
+pp = getToolByName(context,'portal_properties')
+
+isForAnonymous = pp['qPloneComments'].getProperty('enable_anonymous_report_abuse', False)
+
+if username or password:
+    # The user username/password inputs on on the comment form were used,
+    # which might happen when anonymous commenting is enabled. If they typed
+    # something in to either of the inputs, we send them to 'logged_in'.
+    # 'logged_in' will redirect them back to this script if authentication
+    # succeeds with a query string which will post the message appropriately
+    # and show them the result.  if 'logged_in' fails, the user will be
+    # presented with the stock login failure page.  This all depends
+    # heavily on cookiecrumbler, but I believe that is a Plone requirement.
+    came_from = '%s?subject=%s&amp;body_text=%s' % (req['URL'], subject, body_text)
+    came_from = url_quote_plus(came_from)
+    portal_url = context.portal_url()
+
+    return req.RESPONSE.redirect(
+        '%s/logged_in?__ac_name=%s'
+        '&amp;__ac_password=%s'
+        '&amp;came_from=%s' % (portal_url,
+                               url_quote_plus(username),
+                               url_quote_plus(password),
+                               came_from,
+                               )
+        )
+
+comment_creator = req.get('Creator', None)
+if isForAnonymous and comment_creator:
+    # Get entered anonymous name
+    comment_creator = comment_creator
+else:
+    member = mtool.getAuthenticatedMember()
+    comment_creator = member.getUserName()
+
+if mtool.isAnonymousUser():
+    email = req.get('email', '')
+else:
+    email = mtool.getAuthenticatedMember().getProperty('email')
+
+tb = dtool.getDiscussionFor(context)
+
+# Send notification e-mail
+manage_mails(context, context, 'report_abuse')
+
+# return to the discussable object.
+redirect_target = context.plone_utils.getDiscussionThread(tb)[0]
+view = redirect_target.getTypeInfo().getActionInfo('object/view',
+                                                   redirect_target)['url']
+
+portal_status_message=_(u'Your abuse report has been sent.')
+context.plone_utils.addPortalMessage(portal_status_message)
+target = '%s' % view
+return req.RESPONSE.redirect(target)
+
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse.cpy.metadata
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse.cpy.metadata	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse.cpy.metadata	(revision 1201)
@@ -0,0 +1,6 @@
+[validators]
+validators=
+
+[actions]
+action.success=redirect_to_action:string:view
+action.failure=redirect_to_action:string:view
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse_form.cpt
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse_form.cpt	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse_form.cpt	(revision 1201)
@@ -0,0 +1,214 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      metal:use-macro="here/main_template/macros/master"
+      i18n:domain="plone">
+    <body>
+
+        <metal:block fill-slot="top_slot"
+                    tal:define="dummy python:request.set('disable_border',1)" />
+
+        <metal:block fill-slot="sub" />
+
+        <div metal:fill-slot="main"
+            tal:define="errors options/state/getErrors;
+                        thread python:putils.getDiscussionThread(here);
+                        discussable python:thread[0];
+                        replies python:thread[1:];
+                        props_sheet here/portal_properties/qPloneComments;
+                        require_email python:props_sheet.getProperty('require_email')">
+
+            <tal:discussable define="template python:getattr(here, discussable.getLayout(), None);
+                                    macros template/macros | nothing;
+                                    main_macro macros/main | nothing;
+                                    showDiscussable python:main_macro;">
+
+                <div class="comment"
+                    tal:condition="showDiscussable"
+                    tal:define="here python:discussable">
+                    <div metal:use-macro="main_macro"/>
+                </div>
+            </tal:discussable>
+
+            <tal:thread tal:repeat="reply replies">
+                <tal:block tal:define="level repeat/reply/index">
+                    <div class="comment"
+                        tal:attributes="style python:'margin-left:'+str((int(level)+1)*2)+'em'">
+                    <h3>
+                        <tal:block replace="structure portal/discussionitem_icon.gif"/>
+                        <span tal:replace="reply/pretty_title_or_id">Comment title</span>
+                    </h3>
+                    <div class="documentByLine"
+                            tal:define="anonymous_creator python:reply.Creator()=='Anonymous User'">
+                        <span i18n:translate="label_comment_by">Posted by</span>
+                        <span tal:content="reply/Creator"
+                                tal:condition="not:anonymous_creator">Poster Name</span>
+                        <span i18n:translate="label_anonymous_user"
+                                tal:condition="anonymous_creator">Anonymous User</span>
+                        <span i18n:translate="label_commented_at">at</span>
+                        <span tal:replace="python:toLocalizedTime(reply.ModificationDate(),
+                                            long_format=1)">8/23/2001 12:40:44 PM</span>
+                    </div>
+                    <div class="commentBody"
+                            tal:content="structure reply/CookedBody">
+                            This is the body text of the comment.
+                    </div>
+                </div>
+
+                </tal:block>
+            </tal:thread>
+
+            <a name="discussion"></a>
+            <form name="edit_form"
+                class="enableAutoFocus"
+                method="post"
+                action="talkback"
+                tal:attributes="action string:$here_url/$template_id"
+                tal:define="isForAnonymous python:here.portal_properties.qPloneComments.getProperty('enable_anonymous_commenting', 0)">
+
+                <tal:set define="show_form python:True;
+                                comment_id context/id">
+                    <metal:macro define-macro="form"
+                            tal:define="isAjaxReportAbuseEnabled view/ajax_report_abuse_enabled|python:portal.portal_properties.qPloneComments.enable_ajax_report_abuse;
+                                        visible show_form|request/show_form|nothing;
+                                        portal_url portal_url|view/portal_url;
+                                        tabindex tabindex|request/tabindex|view/tabindex;
+                                        member member|request/member|view/member;">
+
+                        <span tal:define="email_from_address portal/email_from_address|view/email_from_address;
+                                            comment_id comment_id|request/comment_id"
+                            tal:attributes="style python:not visible and 'display: none' or '';
+                                            id string:span-reply-form-${comment_id}">
+
+                            <tal:if_not condition="not:email_from_address">
+                                <p class="documentDescription"
+                                i18n:translate="text_no_email_setup">
+                                    This site doesn't have a valid email setup, so you cannot use
+                                    the report abuse form. 
+                                </p>
+                            </tal:if_not>
+                            <tal:if condition="email_from_address">
+                                <form name="feedback_form"
+                                    method="post"
+                                    class="enableAutoFocus"
+                                    tal:attributes="action string:${here/absolute_url}/${template/id}"
+                                    tal:define="errors errors|request/errors|python:{}" >
+
+                                    <input type="hidden" name="comment_id" tal:attributes="value comment_id" />
+
+                                    <fieldset>
+                                        <h1 i18n:translate="heading_report_abuse_form">Report Abuse</h1>
+
+                                        <tal:feedbackForm condition="isAnon">
+                                            <input type="hidden" name="Creator" tal:condition="not:isAnon" tal:attributes="value member/getUserName" />
+
+                                            <div class="field"
+                                                tal:define="error errors/creator|nothing"
+                                                tal:attributes="class python:error and 'field error' or 'field'">
+                                                <label for="creator" i18n:translate="label_creator">
+                                                    Name
+                                                </label>
+                                                <span class="fieldRequired" title="Required"
+                                                        i18n:attributes="title title_required;"
+                                                        i18n:translate="label_required">(Required)</span>
+                                                <div class="formHelp" i18n:translate="help_creator">
+                                                    Please enter your full name
+                                                </div>
+                                                <div tal:content="error">Validation error output</div>
+                                                <input type="text"
+                                                        id="creator"
+                                                        name="creator"
+                                                        size="40"
+                                                        value=""
+                                                        tal:attributes="value request/creator|nothing"
+                                                        />
+                                            </div>
+                                            <div class="field"
+                                                tal:define="error errors/email|nothing"
+                                                tal:attributes="class python:error and 'field error' or 'field'">
+                                                <label for="email" i18n:translate="label_email">
+                                                    E-Mail
+                                                </label>
+                                                <span class="fieldRequired" title="Required"
+                                                        i18n:attributes="title title_required;"
+                                                        i18n:translate="label_required">(Required)</span>
+                                                <div class="formHelp" i18n:translate="help_email">
+                                                    Please enter your e-mail address
+                                                </div>
+                                                <div tal:content="error">Validation error output</div>
+                                                <input type="text"
+                                                        id="email"
+                                                        name="email"
+                                                        size="40"
+                                                        value=""
+                                                        tal:attributes="value request/email|nothing"
+                                                        />
+                                            </div>
+                                        </tal:feedbackForm>
+                                        <tal:feedbackForm condition="not:isAnon" 
+                                                        define="fullname python:request.get('fullname', member.getProperty('fullname', ''));
+                                                                email python:request.get('email', member.getProperty('email',''));">
+                                            <input type="hidden" name="sender_fullname" value="fullname" tal:attributes="value fullname" />
+                                            <input type="hidden" name="email" value="email" tal:attributes="value email" />
+                                        </tal:feedbackForm>
+                                        <div class="field"
+                                            tal:define="error errors/message|nothing"
+                                            tal:attributes="class python:error and 'field error' or 'field'">
+                                            <label for="message" i18n:translate="label_message">
+                                                Description
+                                            </label>
+                                            <span class="fieldRequired" title="Required"
+                                                    i18n:attributes="title title_required;"
+                                                    i18n:translate="label_required">(Required)</span>
+
+                                            <div class="formHelp" i18n:translate="help_message">
+                                                Please state your reasons why you would like to report this comment as abusive.
+                                                Our administrator will be notified with your message.
+                                            </div>
+                                            <div tal:content="error">Validation error output</div>
+                                            <textarea name="message" id="message" rows="5" cols="25" tal:content="request/message|nothing"> </textarea>
+                                        </div>
+                                        <metal:macro define-macro="captcha">
+                                            <div tal:define="comment_id comment_id|request/comment_id" 
+                                                tal:attributes="id string:div-captcha-${comment_id}" 
+                                                tal:condition="nocall:here/captcha_widget|nothing">
+                                                <div metal:use-macro="here/captcha_widget/macros/captcha" />
+                                            </div>
+                                        </metal:macro>
+
+                                        <div class="formControls">
+                                            <input type="hidden" name="form.submitted" value="1" />
+
+                                            <input type="hidden" name="require_email:boolean" value=""
+                                                tal:attributes="value python:isAnon" />
+
+                                            <input 
+                                                value="Send"
+                                                type="submit"
+                                                tal:attributes="comment_id comment_id;
+                                                                class python:isAjaxReportAbuseEnabled  and 'context input-report-abuse-send' or 'context'"
+                                                i18n:attributes="value label_send;"
+                                                />
+
+                                            <input 
+                                                id="input-report-abuse-cancel"
+                                                class="destructive"
+                                                value="Cancel"
+                                                type="button"
+                                                tal:condition="isAjaxReportAbuseEnabled"
+                                                tal:attributes="onclick string:javascript:remove_abuse_report_form(${comment_id})"
+                                                i18n:attributes="value label_cancel;"
+                                                />
+                                        </div>
+                                    </fieldset>
+                                </form>
+                            </tal:if>
+                        </span>
+                    </metal:macro>
+                </tal:set>
+
+            </form>
+        </div>
+    </body>
+</html>
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse_form.cpt.metadata
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse_form.cpt.metadata	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse_form.cpt.metadata	(revision 1201)
@@ -0,0 +1,9 @@
+[default]
+title=Reply to Comment
+
+[validators]
+validators = validate_report_abuse
+
+[actions]
+action.success = traverse_to:string:report_abuse
+action.failure = traverse_to:string:report_abuse_form
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse_template.pt
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse_template.pt	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/report_abuse_template.pt	(revision 1201)
@@ -0,0 +1,42 @@
+<div i18n:domain="quintagroup.plonecomments"
+     tal:omit-tag=""
+     tal:define="charset here/portal_properties/site_properties/default_charset|string:utf-8;
+                 dummy python:request.RESPONSE.setHeader('Content-Type', 'text/html;;charset=%s' % charset);
+                 organization_name options/organization_name;
+                 obj nocall:options/obj;
+                 comment_id nocall:options/comment_id|nothing;
+                 comment_desc nocall:options/comment_desc|nothing;
+                 comment_text nocall:options/comment_text|nothing;
+                 message options/message;">
+
+<tal:x replace="options/name"/>,
+<tal:notify define="obj nocall:options/obj" 
+            i18n:translate="notifyreject_link">
+
+A comment '<tal:x replace="string:${obj/absolute_url}/#${comment_id}" i18n:name="url"/>' has been reported as abusive.
+
+
+<tal:render_if condition="python:comment_desc and comment_text">
+The comment:
+------------
+<tal:message replace="comment_desc"/>
+
+<tal:message replace="comment_text"/>
+
+
+</tal:render_if>
+
+The abuse report:
+-----------------
+<tal:message replace="message"/>
+
+
+</tal:notify>
+--
+<tal:signature i18n:translate="signature"> 
+    <tal:organization_name condition="organization_name" 
+                           replace="string:$organization_name" 
+                           i18n:name="organization_name"/>
+    Support Team
+</tal:signature>
+</div>
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/validate_report_abuse.vpy
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/validate_report_abuse.vpy	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/skins/quintagroup_plonecomments/validate_report_abuse.vpy	(revision 1201)
@@ -0,0 +1,60 @@
+## Controller Script Python "validate_talkback"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind state=state
+##bind subpath=traverse_subpath
+##parameters=message='', email='', require_email, comment_id
+##title=validates a discussion entry
+from Products.CMFCore.utils import getToolByName
+from Products.CMFPlone import MessageFactory
+from Products.CMFPlone import PloneMessageFactory
+_ = MessageFactory('quintagroup.plonecomments')
+
+try:
+    content = context.parentsInThread()[0]
+except AttributeError:
+    content = context
+
+pm = getToolByName(context, 'portal_membership')
+pp = getToolByName(context, 'portal_properties')
+if pm.isAnonymousUser():
+    if not pp.qPloneComments.getProperty('enable_anonymous_report_abuse'):
+        raise Exception, "Abuse Report not allowed."
+else:
+    if not pp.qPloneComments.getProperty('enable_authenticated_report_abuse'):
+        raise Exception, "Abuse Report not allowed."
+
+if not message:
+    state.setError('message', PloneMessageFactory(u'Please submit a message.'), 'message_required')
+
+if require_email:
+    if not (email and context.portal_registration.isValidEmail(email)):
+        state.setError('email', _(u'Please submit an email address.'), 'email_required')
+
+if hasattr(context, 'captcha_validator'):
+    context.captcha_validator()
+
+isForAnonymous = pp['qPloneComments'].getProperty('enable_anonymous_commenting', None)
+if isForAnonymous:
+    pm = getToolByName(context, 'portal_membership')
+    isAnonym = pm.isAnonymousUser()
+    if isAnonym:
+        req = context.REQUEST
+        if not (req.form.has_key('creator') and not req.form['creator'] == ""):
+            state.setError('creator', _(u'Please enter your name.'), 'name_required')
+
+        try:
+            captcha_view = context.restrictedTraverse('@@captcha')
+            if not captcha_view.verify(captcha):
+                state.setError('captcha', _(u'You must correctly enter the word.'), 'captcha')
+        except:
+            # no collective.captcha installed
+            pass
+
+if state.getErrors():
+    context.plone_utils.addPortalMessage(_(u'Please correct the indicated errors.'))
+    return state.set(status='failure')
+else:
+    return state
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsReportAbuse.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsReportAbuse.py	(revision 1201)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/tests/testQPloneCommentsReportAbuse.py	(revision 1201)
@@ -0,0 +1,67 @@
+from base import getToolByName, TestCase
+from config import *
+
+from testQPloneCommentsCommenting import TestCommBase
+from zExceptions import Unauthorized
+
+USERS = {# Common Members
+         'admin':{'passw': 'secret_admin', 'roles': ['Manager']},
+         'owner':{'passw': 'secret_owner', 'roles': ['Owner']},
+         'member':{'passw': 'secret_member', 'roles': ['Member']},
+         'reviewer':{'passw': 'secret_reviewer', 'roles': ['Reviewer']},
+         # Members for discussion manager group
+         'dm_admin':{'passw': 'secret_dm_admin', 'roles': ['Manager']},
+         'dm_owner':{'passw': 'secret_dm_owner', 'roles': ['Owner']},
+         'dm_member':{'passw': 'secret_dm_member', 'roles': ['Member']},
+         'dm_reviewer':{'passw': 'secret_dm_reviewer', 'roles': ['Reviewer']},
+        }
+COMMON_USERS_IDS = [u for u in USERS.keys() if not u.startswith('dm_')]
+COMMON_USERS_IDS.append('anonym')
+DM_USERS_IDS = [u for u in USERS.keys() if u.startswith('dm_')]
+
+
+class TestReportAbuse(TestCommBase):
+
+    def afterSetUp(self):
+        TestCommBase.afterSetUp(self)
+        self.testAnonymousReportAbuse()
+        self.testAuthenticatedReportAbuse()
+
+    def testAnonymousReportAbuse(self):
+        self.login('dm_admin')
+        doc_obj = getattr(self.portal, "doc_anonym")
+        discussion = self.discussion.getDiscussionFor(doc_obj)
+        comment = discussion._container.values()[0]
+        self.logout()
+        # Add abuse report on document.
+        doc_obj.REQUEST.set('comment_id', comment.id)
+        try:
+            doc_obj.report_abuse("Anonymous Report Abuse") 
+        except:
+            raise "Anonymous user CAN'T report abuse in turned ON *Anonymous report abuse mode*."
+
+
+    def testAuthenticatedReportAbuse(self):
+        not_anonym_users = [u for u in self.all_users_id if not u=='anonym']
+        failed_users = []
+        for u in not_anonym_users:
+            self.login('dm_admin')
+            doc_id = "doc_%s" % u
+            doc_obj = getattr(self.portal, doc_id)
+            discussion = self.discussion.getDiscussionFor(doc_obj)
+            comment = discussion._container.values()[0]
+            doc_obj.REQUEST.set('comment_id', comment.id)
+            self.login(u)
+            try:
+                doc_obj.report_abuse("Anonymous Report Abuse") 
+            except:
+                failed_users.append(u)
+
+        self.assert_(not failed_users, "%s - user(s) can not report abuse" % failed_users)
+
+
+def test_suite():
+    from unittest import TestSuite, makeSuite
+    suite = TestSuite()
+    suite.addTest(makeSuite(TestReportAbuse))
+    return suite
Index: quintagroup.plonecomments/trunk/quintagroup/plonecomments/utils.py
===================================================================
--- quintagroup.plonecomments/trunk/quintagroup/plonecomments/utils.py	(revision 1178)
+++ quintagroup.plonecomments/trunk/quintagroup/plonecomments/utils.py	(revision 1201)
@@ -1,4 +1,7 @@
+import smtplib
 from Products.CMFPlone import MessageFactory
 from Products.CMFCore.utils import getToolByName
+from Products.CMFPlone import PloneMessageFactory as _
+from config import warning
 
 # Get apropriate property from (propery_sheeet) configlet
@@ -38,5 +41,7 @@
                               'enable_published_notification'),
                 'onDelete' :  ('enable_rejected_user_notification',),
-                'onApprove': ('enable_approve_notification',)}
+                'onApprove': ('enable_approve_notification',),
+                'onAnonymousReportAbuse': ('enable_anonymous_report_abuse',),
+                'onAuthenticatedReportAbuse': ('enable_authenticated_report_abuse')}
 
     if action == 'publishing':
@@ -48,4 +53,11 @@
     elif action == 'aproving':
         sendMails(props, actions, 'onApprove')
+
+    elif action == 'report_abuse':
+        pm = getToolByName(context, 'portal_membership')
+        if pm.isAnonymousUser():
+            sendMails(props, actions, 'onAnonymousReportAbuse')
+        else:
+            sendMails(props, actions, 'onAuthenticatedReportAbuse')
 
 def getMsg(context, template, args):
@@ -163,4 +175,28 @@
             args = {}
 
+    elif state in ('enable_authenticated_report_abuse', 'enable_anonymous_report_abuse'):
+        template = 'report_abuse_template'
+        user_email = getProp(context, "email_discussion_manager", None)
+        if user_email:
+            message = context.REQUEST.get('message')
+            comment_id = context.REQUEST.get('comment_id')
+            pd = context.portal_discussion
+            dl = pd.getDiscussionFor(context)
+            comment = dl._container.get(comment_id)
+            args = {'mto': user_email,
+                    'mfrom': admin_email,
+                    'obj': reply_parent,
+                    'message':message,
+                    'organization_name': organization_name,
+                    'name': creator_name,
+                    'comment_id':comment_id,
+                    'comment_desc':comment.description,
+                    'comment_text':comment.text
+                    }
+            subject = '[%s] A comment on "%s" has been reported for abuse.' \
+                            % (organization_name, getParent(context).Title())
+        else:
+            args = {}
+
     if args:
         msg = getMsg(context, template, args)
@@ -168,10 +204,14 @@
         site_props = context.portal_properties.site_properties
         host = p_utils.getMailHost()
-        host.secureSend(msg, user_email, admin_email,
-                        subject = subject,
-                        subtype = 'plain',
-                        debug = False,
-                        charset = site_props.getProperty('default_charset', 'utf-8'),
-                        From = admin_email)
+        try:
+            host.secureSend(msg, user_email, admin_email,
+                            subject = subject,
+                            subtype = 'plain',
+                            debug = False,
+                            charset = site_props.getProperty('default_charset', 'utf-8'),
+                            From = admin_email)
+        except smtplib.SMTPRecipientsRefused:
+            log.error(_('SMTPRecipientsRefused: Could not send the email'
+            'notification. Have you configured an email server for Plone?'))
 
 def setStatusMsg(state, context, msg):
