diff --git a/django/contrib/comments/moderators.py b/django/contrib/comments/moderators.py
new file mode 100644
index 0000000..13c5064
--- /dev/null
+++ b/django/contrib/comments/moderators.py
@@ -0,0 +1,477 @@
+"""
+Copyright (c) 2007, James Bennett
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+    * Neither the name of the author nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+"""
+A generic comment-moderation system which allows configuration of
+moderation options on a per-model basis.
+
+To use, do two things:
+
+1. Create or import a subclass of ``CommentModerator`` defining the
+   options you want.
+
+2. Import ``moderator`` from this module and register one or more
+   models, passing the models and the ``CommentModerator`` options
+   class you want to use.
+
+
+Example
+-------
+
+First, we define a simple model class which might represent entries in
+a weblog::
+    
+    from django.db import models
+    
+    class Entry(models.Model):
+        title = models.CharField(maxlength=250)
+        body = models.TextField()
+        pub_date = models.DateField()
+        enable_comments = models.BooleanField()
+
+Then we create a ``CommentModerator`` subclass specifying some
+moderation options::
+    
+    from django.contrib.comments.moderators import CommentModerator, moderator
+    
+    class EntryModerator(CommentModerator):
+        email_notification = True
+        enable_field = 'enable_comments'
+
+And finally register it for moderation::
+    
+    moderator.register(Entry, EntryModerator)
+
+This sample class would apply several moderation steps to each new
+comment submitted on an Entry:
+
+* If the entry's ``enable_comments`` field is set to ``False``, the
+  comment will be rejected (immediately deleted).
+
+* If the comment is successfully posted, an email notification of the
+  comment will be sent to site staff.
+
+For a full list of built-in moderation options and other
+configurability, see the documentation for the ``CommentModerator``
+class.
+
+Several example subclasses of ``CommentModerator`` are provided in
+`django-comment-utils`_, both to provide common moderation options and to
+demonstrate some of the ways subclasses can customize moderation
+behavior.
+
+.. _`django-comment-utils`: http://code.google.com/p/django-comment-utils/
+"""
+
+
+import datetime
+
+from django.conf import settings
+from django.core.mail import send_mail
+from django.db.models import signals
+from django.db.models.base import ModelBase
+from django.template import Context, loader
+from django.contrib import comments
+from django.contrib.sites.models import Site
+
+
+class AlreadyModerated(Exception):
+    """
+    Raised when a model which is already registered for moderation is
+    attempting to be registered again.
+    
+    """
+    pass
+
+
+class NotModerated(Exception):
+    """
+    Raised when a model which is not registered for moderation is
+    attempting to be unregistered.
+    
+    """
+    pass
+
+
+class CommentModerator(object):
+    """
+    Encapsulates comment-moderation options for a given model.
+    
+    This class is not designed to be used directly, since it doesn't
+    enable any of the available moderation options. Instead, subclass
+    it and override attributes to enable different options::
+    
+    ``auto_close_field``
+        If this is set to the name of a ``DateField`` or
+        ``DateTimeField`` on the model for which comments are
+        being moderated, new comments for objects of that model
+        will be disallowed (immediately deleted) when a certain
+        number of days have passed after the date specified in
+        that field. Must be used in conjunction with
+        ``close_after``, which specifies the number of days past
+        which comments should be disallowed. Default value is
+        ``None``.
+    
+    ``auto_moderate_field``
+        Like ``auto_close_field``, but instead of outright
+        deleting new comments when the requisite number of days
+        have elapsed, it will simply set the ``is_public`` field
+        of new comments to ``False`` before saving them. Must be
+        used in conjunction with ``moderate_after``, which
+        specifies the number of days past which comments should be
+        moderated. Default value is ``None``.
+    
+    ``close_after``
+        If ``auto_close_field`` is used, this must specify the
+        number of days past the value of the field specified by
+        ``auto_close_field`` after which new comments for an
+        object should be disallowed. Default value is ``None``.
+    
+    ``email_notification``
+        If ``True``, any new comment on an object of this model
+        which survives moderation will generate an email to site
+        staff. Default value is ``False``.
+    
+    ``enable_field``
+        If this is set to the name of a ``BooleanField`` on the
+        model for which comments are being moderated, new comments
+        on objects of that model will be disallowed (immediately
+        deleted) whenever the value of that field is ``False`` on
+        the object the comment would be attached to. Default value
+        is ``None``.
+    
+    ``moderate_after``
+        If ``auto_moderate`` is used, this must specify the number
+        of days past the value of the field specified by
+        ``auto_moderate_field`` after which new comments for an
+        object should be marked non-public. Default value is
+        ``None``.
+    
+    Most common moderation needs can be covered by changing these
+    attributes, but further customization can be obtained by
+    subclassing and overriding the following methods. Each method will
+    be called with two arguments: ``comment``, which is the comment
+    being submitted, and ``content_object``, which is the object the
+    comment will be attached to::
+    
+    ``allow``
+        Should return ``True`` if the comment should be allowed to
+        post on the content object, and ``False`` otherwise (in
+        which case the comment will be immediately deleted).
+    
+    ``email``
+        If email notification of the new comment should be sent to
+        site staff or moderators, this method is responsible for
+        sending the email.
+    
+    ``moderate``
+        Should return ``True`` if the comment should be moderated
+        (in which case its ``is_public`` field will be set to
+        ``False`` before saving), and ``False`` otherwise (in
+        which case the ``is_public`` field will not be changed).
+    
+    Subclasses which want to introspect the model for which comments
+    are being moderated can do so through the attribute ``_model``,
+    which will be the model class.
+    
+    """
+    auto_close_field = None
+    auto_moderate_field = None
+    close_after = None
+    email_notification = False
+    enable_field = None
+    moderate_after = None
+    
+    def __init__(self, model):
+        self._model = model
+    
+    def _get_delta(self, now, then):
+        """
+        Internal helper which will return a ``datetime.timedelta``
+        representing the time between ``now`` and ``then``. Assumes
+        ``now`` is a ``datetime.date`` or ``datetime.datetime`` later
+        than ``then``.
+        
+        If ``now`` and ``then`` are not of the same type due to one of
+        them being a ``datetime.date`` and the other being a
+        ``datetime.datetime``, both will be coerced to
+        ``datetime.date`` before calculating the delta.
+        
+        """
+        if now.__class__ is not then.__class__:
+            now = datetime.date(now.year, now.month, now.day)
+            then = datetime.date(then.year, then.month, then.day)
+        if now < then:
+            raise ValueError("Cannot determine moderation rules because date field is set to a value in the future")
+        return now - then
+        
+    def allow(self, comment, content_object):
+        """
+        Determine whether a given comment is allowed to be posted on
+        a given object.
+        
+        Return ``True`` if the comment should be allowed, ``False
+        otherwise.
+        
+        """
+        if self.enable_field:
+            if not getattr(content_object, self.enable_field):
+                return False
+        if self.auto_close_field and self.close_after:
+            if self._get_delta(datetime.datetime.now(), getattr(content_object, self.auto_close_field)).days >= self.close_after:
+                return False
+        return True
+    
+    def moderate(self, comment, content_object):
+        """
+        Determine whether a given comment on a given object should be
+        allowed to show up immediately, or should be marked non-public
+        and await approval.
+        
+        Return ``True`` if the comment should be moderated (marked
+        non-public), ``False`` otherwise.
+        
+        """
+        if self.auto_moderate_field and self.moderate_after:
+            if self._get_delta(datetime.datetime.now(), getattr(content_object, self.auto_moderate_field)).days >= self.moderate_after:
+                return True
+        return False
+    
+    def comments_open(self, obj):
+        """
+        Return ``True`` if new comments are being accepted for
+        ``obj``, ``False`` otherwise.
+        
+        The algorithm for determining this is as follows:
+        
+        1. If ``enable_field`` is set and the relevant field on
+           ``obj`` contains a false value, comments are not open.
+        
+        2. If ``close_after`` is set and the relevant date field on
+           ``obj`` is far enough in the past, comments are not open.
+        
+        3. If neither of the above checks determined that comments are
+           not open, comments are open.
+        
+        """
+        if self.enable_field:
+            if not getattr(obj, self.enable_field):
+                return False
+        if self.auto_close_field and self.close_after:
+            if self._get_delta(datetime.datetime.now(), getattr(obj, self.auto_close_field)).days >= self.close_after:
+                return False
+        return True
+    
+    def comments_moderated(self, obj):
+        """
+        Return ``True`` if new comments for ``obj`` are being
+        automatically sent to moderation, ``False`` otherwise.
+        
+        The algorithm for determining this is as follows:
+        
+        1. If ``moderate_field`` is set and the relevant field on
+           ``obj`` contains a true value, comments are moderated.
+        
+        2. If ``moderate_after`` is set and the relevant date field on
+           ``obj`` is far enough in the past, comments are moderated.
+        
+        3. If neither of the above checks decided that comments are
+           moderated, comments are not moderated.
+        
+        """
+        if self.moderate_field:
+            if getattr(obj, self.moderate_field):
+                return True
+        if self.auto_moderate_field and self.moderate_after:
+            if self._get_delta(datetime.datetime.now(), getattr(obj, self.auto_moderate_field)).days >= self.moderate_after:
+                return True
+        return False
+    
+    def email(self, comment, content_object):
+        """
+        Send email notification of a new comment to site staff when email
+        notifications have been requested.
+        
+        """
+        if not self.email_notification:
+            return
+        recipient_list = [manager_tuple[1] for manager_tuple in settings.MANAGERS]
+        t = loader.get_template('comments/comment_notification_email.txt')
+        c = Context({ 'comment': comment,
+                      'content_object': content_object })
+        subject = '[%s] New comment posted on "%s"' % (Site.objects.get_current().name,
+                                                          content_object)
+        message = t.render(c)
+        send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, recipient_list, fail_silently=True)
+
+
+class Moderator(object):
+    """
+    Handles moderation of a set of models.
+    
+    An instance of this class will maintain a list of one or more
+    models registered for comment moderation, and their associated
+    moderation classes, and apply moderation to all incoming comments.
+    
+    To register a model, obtain an instance of ``CommentModerator``
+    (this module exports one as ``moderator``), and call its
+    ``register`` method, passing the model class and a moderation
+    class (which should be a subclass of ``CommentModerator``). Note
+    that both of these should be the actual classes, not instances of
+    the classes.
+    
+    To cease moderation for a model, call the ``unregister`` method,
+    passing the model class.
+    
+    For convenience, both ``register`` and ``unregister`` can also
+    accept a list of model classes in place of a single model; this
+    allows easier registration of multiple models with the same
+    ``CommentModerator`` class.
+    
+    The actual moderation is applied in two phases: one prior to
+    saving a new comment, and the other immediately after saving. The
+    pre-save moderation may mark a comment as non-public or mark it to
+    be removed; the post-save moderation may delete a comment which
+    was disallowed (there is currently no way to prevent the comment
+    being saved once before removal) and, if the comment is still
+    around, will send any notification emails the comment generated.
+    
+    """
+    def __init__(self):
+        self._registry = {}
+        self.connect()
+    
+    def connect(self):
+        """
+        Hook up the moderation methods to pre- and post-save signals
+        from the comment models.
+        
+        """
+        signals.pre_save.connect(self.pre_save_moderation, sender=comments.get_model())
+        signals.post_save.connect(self.post_save_moderation, sender=comments.get_model())
+    
+    def register(self, model_or_iterable, moderation_class):
+        """
+        Register a model or a list of models for comment moderation,
+        using a particular moderation class.
+        
+        Raise ``AlreadyModerated`` if any of the models are already
+        registered.
+        
+        """
+        if isinstance(model_or_iterable, ModelBase):
+            model_or_iterable = [model_or_iterable]
+        for model in model_or_iterable:
+            if model in self._registry:
+                raise AlreadyModerated("The model '%s' is already being moderated" % model._meta.module_name)
+            self._registry[model] = moderation_class(model)
+    
+    def unregister(self, model_or_iterable):
+        """
+        Remove a model or a list of models from the list of models
+        whose comments will be moderated.
+        
+        Raise ``NotModerated`` if any of the models are not currently
+        registered for moderation.
+        
+        """
+        if isinstance(model_or_iterable, ModelBase):
+            model_or_iterable = [model_or_iterable]
+        for model in model_or_iterable:
+            if model not in self._registry:
+                raise NotModerated("The model '%s' is not currently being moderated" % model._meta.module_name)
+            del self._registry[model]
+    
+    def pre_save_moderation(self, sender, instance, **kwargs):
+        """
+        Apply any necessary pre-save moderation steps to new
+        comments.
+        
+        """
+        model = instance.content_type.model_class()
+        if instance.id or (model not in self._registry):
+            return
+        content_object = instance.content_object
+        moderation_class = self._registry[model]
+        if not moderation_class.allow(instance, content_object): # Comment will get deleted in post-save hook.
+            instance.moderation_disallowed = True
+            return
+        if moderation_class.moderate(instance, content_object):
+            instance.is_public = False
+    
+    def post_save_moderation(self, sender, instance, **kwargs):
+        """
+        Apply any necessary post-save moderation steps to new
+        comments.
+        
+        """
+        model = instance.content_type.model_class()
+        if model not in self._registry:
+            return
+        if hasattr(instance, 'moderation_disallowed'):
+            instance.delete()
+            return
+        self._registry[model].email(instance, instance.content_object)
+
+    def comments_open(self, obj):
+        """
+        Return ``True`` if new comments are being accepted for
+        ``obj``, ``False`` otherwise.
+        
+        If no moderation rules have been registered for the model of
+        which ``obj`` is an instance, comments are assumed to be open
+        for that object.
+        
+        """
+        model = obj.__class__
+        if model not in self._registry:
+            return True
+        return self._registry[model].comments_open(obj)
+
+    def comments_moderated(self, obj):
+        """
+        Return ``True`` if new comments for ``obj`` are being
+        automatically sent to moderation, ``False`` otherwise.
+        
+        If no moderation rules have been registered for the model of
+        which ``obj`` is an instance, comments for that object are
+        assumed not to be moderated.
+        
+        """
+        model = obj.__class__
+        if model not in self._registry:
+            return False
+        return self._registry[model].comments_moderated(obj)
+
+
+# Import this instance in your own code to use in registering
+# your models for moderation.
+moderator = Moderator()
diff --git a/docs/ref/contrib/comments/index.txt b/docs/ref/contrib/comments/index.txt
index 5aeebe3..2bca94e 100644
--- a/docs/ref/contrib/comments/index.txt
+++ b/docs/ref/contrib/comments/index.txt
@@ -212,4 +212,5 @@ More information
    settings
    signals
    upgrade
+   moderators
 
diff --git a/docs/ref/contrib/comments/moderators.txt b/docs/ref/contrib/comments/moderators.txt
new file mode 100644
index 0000000..8c1713e
--- /dev/null
+++ b/docs/ref/contrib/comments/moderators.txt
@@ -0,0 +1,208 @@
+==========================
+Generic comment moderation
+==========================
+
+Django's bundled comments application is extremely useful on its own,
+but the amount of comment spam circulating on the Web today
+essentially makes it necessary to have some sort of automatic
+moderation system in place for any application which makes use of
+comments. To make this easier to handle in a consistent fashion,
+``django.contrib.comments.moderators`` (based on `comment_utils`_) 
+provides a generic, extensible comment-moderation system which can 
+be applied to any model or set of models which want to make use of 
+Django's comment system.
+
+.. _`comment_utils`: http://code.google.com/p/django-comment-utils/
+
+
+Overview
+========
+
+The entire system is contained within ``django.contrib.comments.moderators``,
+and uses a two-step process to enable moderation for any given model:
+
+1. A subclass of ``django.contrib.comments.moderators.CommentModerator`` is
+   defined which specifies the moderation options the model wants to
+   enable.
+
+2. The model is registered with the moderation system, passing in the
+   model class and the class which specifies its moderation options.
+
+A simple example is the best illustration of this. Suppose we have the
+following model, which would represent entries in a weblog::
+
+    from django.db import models
+    
+    class Entry(models.Model):
+        title = models.CharField(maxlength=250)
+        body = models.TextField()
+        pub_date = models.DateTimeField()
+        enable_comments = models.BooleanField()
+
+Now, suppose that we want the following steps to be applied whenever a
+new comment is posted on an ``Entry``:
+
+1. If the ``Entry``'s ``enable_comments`` field is ``False``, the
+   comment will simply be disallowed (i.e., immediately deleted).
+
+2. If the ``enable_comments`` field is ``True``, the comment will be
+   allowed to save.
+
+3. Once the comment is saved, an email should be sent to site staff
+   notifying them of the new comment.
+
+Accomplishing this is fairly straightforward and requires very little
+code::
+
+    from django.contrib.comments.moderators import CommentModerator, moderator
+    
+    class EntryModerator(CommentModerator):
+        email_notification = True
+        enable_field = 'enable_comments'
+    
+    moderator.register(Entry, EntryModerator)
+
+The ``CommentModerator`` class pre-defines a number of useful
+moderation options which subclasses can enable or disable as desired,
+and ``moderator`` knows how to work with them to determine whether to
+allow a comment, whether to moderate a comment which will be allowed
+to post, and whether to email notifications of new comments.
+
+
+Built-in moderation options
+---------------------------
+
+Most common comment-moderation needs can be handled by subclassing
+``CommentModerator`` and changing the values of pre-defined
+attributes; the full range of built-in options is as follows:
+
+    ``auto_close_field``
+        If this is set to the name of a ``DateField`` or
+        ``DateTimeField`` on the model for which comments are being
+        moderated, new comments for objects of that model will be
+        disallowed (immediately deleted) when a certain number of days
+        have passed after the date specified in that field. Must be
+        used in conjunction with ``close_after``, which specifies the
+        number of days past which comments should be
+        disallowed. Default value is ``None``.
+    
+    ``auto_moderate_field``
+        Like ``auto_close_field``, but instead of outright deleting
+        new comments when the requisite number of days have elapsed,
+        it will simply set the ``is_public`` field of new comments to
+        ``False`` before saving them. Must be used in conjunction with
+        ``moderate_after``, which specifies the number of days past
+        which comments should be moderated. Default value is ``None``.
+    
+    ``close_after``
+        If ``auto_close_field`` is used, this must specify the number
+        of days past the value of the field specified by
+        ``auto_close_field`` after which new comments for an object
+        should be disallowed. Default value is ``None``.
+    
+    ``email_notification``
+        If ``True``, any new comment on an object of this model which
+        survives moderation (i.e., is not deleted) will generate an
+        email to site staff. Default value is ``False``.
+    
+    ``enable_field``
+        If this is set to the name of a ``BooleanField`` on the model
+        for which comments are being moderated, new comments on
+        objects of that model will be disallowed (immediately deleted)
+        whenever the value of that field is ``False`` on the object
+        the comment would be attached to. Default value is ``None``.
+    
+    ``moderate_after``
+        If ``auto_moderate`` is used, this must specify the number of
+        days past the value of the field specified by
+        ``auto_moderate_field`` after which new comments for an object
+        should be marked non-public. Default value is ``None``.
+
+Simply subclassing ``CommentModerator`` and changing the values of
+these options will automatically enable the various moderation methods
+for any models registered using the subclass.
+
+
+Adding custom moderation methods
+--------------------------------
+
+For situations where the built-in options listed above are not
+sufficient, subclasses of ``CommentModerator`` can also override the
+methods which actually perform the moderation, and apply any logic
+they desire. ``CommentModerator`` defines three methods which
+determine how moderation will take place; each method will be called
+by the moderation system and passed two arguments: ``comment``, which
+is the new comment being posted, and ``content_object``, which is the
+object the comment will be attached to:
+
+    ``allow``
+        Should return ``True`` if the comment should be allowed to
+        post on the content object, and ``False`` otherwise (in which
+        case the comment will be immediately deleted).
+    
+    ``email``
+        If email notification of the new comment should be sent to
+        site staff or moderators, this method is responsible for
+        sending the email.
+    
+    ``moderate``
+        Should return ``True`` if the comment should be moderated (in
+        which case its ``is_public`` field will be set to ``False``
+        before saving), and ``False`` otherwise (in which case the
+        ``is_public`` field will not be changed).
+
+
+Registering models for moderation
+---------------------------------
+
+The moderation system, represented by
+``django.contrib.comments.moderators.moderator`` is an instance of the class
+``django.contrib.comments.moderators.Moderator``, which allows registration and
+"unregistration" of models via two methods:
+
+    ``register``
+        Takes two arguments: the first should be either a model class
+        or list of model classes, and the second should be a subclass
+        of ``CommentModerator``, and register the model or models to
+        be moderated using the options defined in the
+        ``CommentModerator`` subclass. If any of the models are
+        already registered for moderation, the exception
+        ``django.contrib.comments.moderators.AlreadyModerated`` will be raised.
+
+    ``unregister``
+        Takes one argument: a model class or list of model classes,
+        and removes the model or models from the set of models which
+        are being moderated. If any of the models are not currently
+        being moderated, the exception
+        ``django.contrib.comments.moderators.NotModerated`` will be raised.
+
+
+Customizing the moderation system
+---------------------------------
+
+Most use cases will work easily with simple subclassing of
+``CommentModerator`` and registration with the provided ``moderator``
+instance, but customization of global moderation behavior can be
+achieved by subclassing ``Moderator`` and instead registering models
+with an instance of the subclass.
+
+In addition to the ``register`` and ``unregister`` methods detailed
+above, the following methods on ``Moderator`` can be overridden to
+achieve customized behavior:
+
+    ``connect``
+        Determines how moderation is set up globally. The base
+        implementation in ``Moderator`` does this by attaching
+        listeners to the ``pre_save`` and ``post_save`` signals from
+        the comment models.
+
+    ``pre_save_moderation``
+        In the base implementation, applies all pre-save moderation
+        steps (such as determining whether the comment needs to be
+        deleted, or whether it needs to be marked as non-public or
+        generate an email).
+
+    ``post_save_moderation``
+        In the base implementation, applies all post-save moderation
+        steps (currently this consists entirely of deleting comments
+        which were disallowed).
diff --git a/tests/regressiontests/comment_tests/fixtures/comment_utils.xml b/tests/regressiontests/comment_tests/fixtures/comment_utils.xml
new file mode 100644
index 0000000..a39bbf6
--- /dev/null
+++ b/tests/regressiontests/comment_tests/fixtures/comment_utils.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+  <object pk="1" model="comment_tests.entry">
+      <field type="CharField" name="title">ABC</field>
+      <field type="TextField" name="body">This is the body</field>
+      <field type="DateField" name="pub_date">2008-01-01</field>
+      <field type="BooleanField" name="enable_comments">True</field>
+  </object>
+  <object pk="2" model="comment_tests.entry">
+      <field type="CharField" name="title">XYZ</field>
+      <field type="TextField" name="body">Text here</field>
+      <field type="DateField" name="pub_date">2008-01-02</field>
+      <field type="BooleanField" name="enable_comments">False</field>
+  </object>
+</django-objects>
diff --git a/tests/regressiontests/comment_tests/models.py b/tests/regressiontests/comment_tests/models.py
index 28022e2..62f4168 100644
--- a/tests/regressiontests/comment_tests/models.py
+++ b/tests/regressiontests/comment_tests/models.py
@@ -20,3 +20,11 @@ class Article(models.Model):
     def __str__(self):
         return self.headline
 
+class Entry(models.Model):
+    title = models.CharField(max_length=250)
+    body = models.TextField()
+    pub_date = models.DateField()
+    enable_comments = models.BooleanField()
+
+    def __str__(self):
+        return self.title
diff --git a/tests/regressiontests/comment_tests/tests/__init__.py b/tests/regressiontests/comment_tests/tests/__init__.py
index 09026aa..449fea4 100644
--- a/tests/regressiontests/comment_tests/tests/__init__.py
+++ b/tests/regressiontests/comment_tests/tests/__init__.py
@@ -86,3 +86,4 @@ from regressiontests.comment_tests.tests.comment_form_tests import *
 from regressiontests.comment_tests.tests.templatetag_tests import *
 from regressiontests.comment_tests.tests.comment_view_tests import *
 from regressiontests.comment_tests.tests.moderation_view_tests import *
+from regressiontests.comment_tests.tests.comment_utils_moderators_tests import *
diff --git a/tests/regressiontests/comment_tests/tests/comment_utils_moderators_tests.py b/tests/regressiontests/comment_tests/tests/comment_utils_moderators_tests.py
new file mode 100644
index 0000000..4e6a03d
--- /dev/null
+++ b/tests/regressiontests/comment_tests/tests/comment_utils_moderators_tests.py
@@ -0,0 +1,70 @@
+from regressiontests.comment_tests.tests import CommentTestCase, CT, Site
+from django.contrib.comments.models import Comment
+from django.contrib.comments.moderators import moderator, CommentModerator, AlreadyModerated
+from regressiontests.comment_tests.models import Entry
+from django.core import mail
+
+class EntryModerator1(CommentModerator):
+    email_notification = True
+
+class EntryModerator2(CommentModerator):
+    enable_field = 'enable_comments'
+
+class EntryModerator3(CommentModerator):
+    auto_close_field = 'pub_date'
+    close_after = 7
+
+class EntryModerator4(CommentModerator):
+    auto_moderate_field = 'pub_date'
+    moderate_after = 7
+
+class CommentUtilsModeratorTests(CommentTestCase):
+    fixtures = ["comment_utils.xml"]
+
+    def createSomeComments(self):
+        c1 = Comment.objects.create(
+            content_type = CT(Entry),
+            object_pk = "1",
+            user_name = "Joe Somebody",
+            user_email = "jsomebody@example.com",
+            user_url = "http://example.com/~joe/",
+            comment = "First!",
+            site = Site.objects.get_current(),
+        )
+        c2 = Comment.objects.create(
+            content_type = CT(Entry),
+            object_pk = "2",
+            user_name = "Joe the Plumber",
+            user_email = "joetheplumber@whitehouse.gov",
+            user_url = "http://example.com/~joe/",
+            comment = "Second!",
+            site = Site.objects.get_current(),
+        )
+        return c1, c2
+
+    def tearDown(self):
+        moderator.unregister(Entry)
+
+    def testRegisterExistingModel(self):
+        moderator.register(Entry, EntryModerator1)
+        self.assertRaises(AlreadyModerated, moderator.register, Entry, EntryModerator1)
+
+    def testEmailNotification(self):
+        moderator.register(Entry, EntryModerator1)
+        c1, c2 = self.createSomeComments()
+        self.assertEquals(len(mail.outbox), 2)
+
+    def testCommentsEnabled(self):
+        moderator.register(Entry, EntryModerator2)
+        c1, c2 = self.createSomeComments()
+        self.assertEquals(Comment.objects.all().count(), 1)
+
+    def testAutoCloseField(self):
+        moderator.register(Entry, EntryModerator3)
+        c1, c2 = self.createSomeComments()
+        self.assertEquals(Comment.objects.all().count(), 0)        
+
+    def testAutoModerateField(self):
+        moderator.register(Entry, EntryModerator4)
+        c1, c2 = self.createSomeComments()
+        self.assertEquals(c2.is_public, False)       
diff --git a/tests/runtests.py b/tests/runtests.py
index cc9594b..656d669 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -110,6 +110,10 @@ def django_tests(verbosity, interactive, test_labels):
         'django.middleware.common.CommonMiddleware',
     )
     settings.SITE_ID = 1
+    # For testing comment-utils, we require the MANAGERS attribute 
+    # to be set, so that a test email is sent out which we catch
+    # in our tests.
+    settings.MANAGERS = ("admin@djangoproject.com",)
 
     # Load all the ALWAYS_INSTALLED_APPS.
     # (This import statement is intentionally delayed until after we
diff --git a/tests/templates/comments/comment_notification_email.txt b/tests/templates/comments/comment_notification_email.txt
new file mode 100644
index 0000000..63f1493
--- /dev/null
+++ b/tests/templates/comments/comment_notification_email.txt
@@ -0,0 +1,3 @@
+A comment has been posted on {{ content_object }}.
+The comment reads as follows:
+{{ comment }}
