diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..73e8dca --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +|:---------:|:------------------:| +| 2.0.x | :heavy_check_mark: | +| <2.0.x | :x: | + +## Reporting a Vulnerability + +* Do not create issues to report security vulnerabilities. +* Instead, please e-mail one of the maintainers at [chronobserver@disroot.org](mailto:chronobserver@disroot.org) or [angelos.h@hotmail.com](mailto:angelos.h@hotmail.com). +* Do not include any confidential information in the e-mail. +* Provide your GitHub username (if available), so that we can invite you to collaborate on a [security advisory][advisories] on GitHub. + +[advisories]: https://help.github.com/en/articles/about-maintainer-security-advisories + + diff --git a/.gitignore b/.gitignore index 4e61525..7c25208 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.egg-info .tox/ docs/_build +.venv/ diff --git a/.travis.yml b/.travis.yml index f32d10e..2a8806f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,10 +37,9 @@ matrix: - python: 3.7 env: TOXENV=py37-django-master allow_failures: - - env: TOXENV=py35-django-master - env: TOXENV=py36-django-master - env: TOXENV=py37-django-master install: -- pip install tox + - pip install tox script: -- tox + - tox diff --git a/.tx/config b/.tx/config deleted file mode 100644 index d0a7d45..0000000 --- a/.tx/config +++ /dev/null @@ -1,9 +0,0 @@ -[main] -host = https://www.transifex.com -lang_map = sr@latin:sr_Latn -type = PO - -[django-contrib-comments.main] -file_filter = django_comments/locale//LC_MESSAGES/django.po -source_file = django_comments/locale/en/LC_MESSAGES/django.po -source_lang = en diff --git a/HISTORY.rst b/HISTORY.rst index ffaafe1..735789d 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,11 @@ History ======= +2.0.0 (upcoming) +---------------- + +* Forked and rebranded into ``django-commentary``. + 1.9.1 (2019-02-20) ------------------ @@ -80,7 +85,6 @@ History * Fixed migrations not working when installed as an egg. - 1.6.0 (2016-04-29) ------------------ diff --git a/LICENSE.txt b/LICENSE.txt index 173b0e9..6f9ba73 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,3 +1,4 @@ +Copyright (c) 2019, MangAdventure Copyright (c) 2013, Django Software Foundation and individual contributors All rights reserved. diff --git a/MANIFEST.in b/MANIFEST.in index bebf28d..8191ab6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,7 +3,7 @@ include HISTORY.rst include LICENSE.txt recursive-include docs * recursive-include tests * -recursive-include django_comments/templates * -recursive-include django_comments/locale * +recursive-include commentary/templates * +recursive-include commentary/locale * recursive-exclude * __pycache__ recursive-exclude * *.py[co] diff --git a/README.rst b/README.rst index 6295056..9a303d4 100644 --- a/README.rst +++ b/README.rst @@ -1,19 +1,31 @@ -=========================== -Django "excontrib" Comments -=========================== +================= +Django Commentary +================= -.. image:: https://img.shields.io/pypi/v/django-contrib-comments.svg - :target: https://pypi.python.org/pypi/django-contrib-comments +.. image:: https://img.shields.io/pypi/v/django-commentary.svg?label=PyPI&logo=pypi + :target: https://pypi.python.org/pypi/django-commentary + :alt: PyPI -.. image:: https://img.shields.io/travis/django/django-contrib-comments.svg - :target: http://travis-ci.org/django/django-contrib-comments +.. image:: https://img.shields.io/travis/mangadventure/django-commentary?label=Travis&logo=travis + :target: https://travis-ci.org/mangadventure/django-commentary + :alt: Travis CI -Django used to include a comments framework; since Django 1.6 it's been -separated to a separate project. This is that project. +This is a fork of `django-contrib-comments`__ with the following changes: + +* |c| Only users can post comments. +* |c| Previews have been removed. +* |c| Posting a comment redirects to the comment page. +* |u| Post/flag/approve/delete redirect back to the page. +* |u| Users can edit and delete their own comments. +* |u| Comments are threaded. This framework can be used to attach comments to any model, so you can use it for comments on blog entries, photos, book chapters, or anything else. For details, `consult the documentation`__. -__ https://django-contrib-comments.readthedocs.io/ +__ https://github.com/django/django-contrib-comments +__ https://django-commentary.readthedocs.io + +.. |u| unicode:: U+2610 +.. |c| unicode:: U+2611 diff --git a/commentary/__init__.py b/commentary/__init__.py new file mode 100644 index 0000000..061fd9b --- /dev/null +++ b/commentary/__init__.py @@ -0,0 +1,119 @@ +from importlib import import_module + +from django.apps import apps +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.urls import reverse +from django.utils.module_loading import import_string + + +def _get_setting(setting, default): + return getattr(settings, 'COMMENTS_' + setting, default) + + +COMMENTS_TIMEOUT = _get_setting('TIMEOUT', 360) # 1h +COMMENTS_ALLOW_HTML = _get_setting('ALLOW_HTML', False) +COMMENTS_HIDE_REMOVED = _get_setting('HIDE_REMOVED', True) +COMMENTS_WIDGET = _get_setting('WIDGET', 'django.forms.Textarea') + +if isinstance(COMMENTS_WIDGET, str): + COMMENTS_WIDGET = import_string(COMMENTS_WIDGET) + +DEFAULT_COMMENTS_APP = 'commentary' + + +def get_comment_app(): + """ + Get the comment app (i.e. "commentary") as defined in the settings + """ + # Make sure the app's in INSTALLED_APPS + comments_app = _get_setting('APP', DEFAULT_COMMENTS_APP) + if not apps.is_installed(comments_app): + raise ImproperlyConfigured( + 'The COMMENTS_APP (%r) must be in INSTALLED_APPS' % comments_app + ) + # Try to import the package + try: + package = import_module(comments_app) + except ImportError as e: + raise ImproperlyConfigured( + 'The COMMENTS_APP setting refers ' + 'to a non-existent package. (%s)' % e + ) + + return package + + +def _check_attr(attr): + app = get_comment_app() + return app.__name__ != DEFAULT_COMMENTS_APP and hasattr(app, attr) + + +def get_model(): + """ + Returns the comment model class. + """ + if _check_attr('get_model'): + return get_comment_app().get_model() + else: + from commentary.models import Comment + return Comment + + +def get_form(): + from commentary.forms import CommentForm + """ + Returns the comment ModelForm class. + """ + if _check_attr('get_form'): + return get_comment_app().get_form() + else: + return CommentForm + + +def get_form_target(): + """ + Returns the target URL for the comment form submission view. + """ + if _check_attr('get_form_target'): + return get_comment_app().get_form_target() + else: + return reverse('comments-post-comment') + + +def get_flag_url(comment): + """ + Get the URL for the "flag this comment" view. + """ + if _check_attr('get_flag_url'): + return get_comment_app().get_flag_url(comment) + else: + return reverse('comments-flag', args=(comment.id,)) + + +def get_delete_url(comment): + """ + Get the URL for the "delete this comment" view. + """ + if _check_attr('get_delete_url'): + return get_comment_app().get_delete_url(comment) + else: + return reverse('comments-delete', args=(comment.id,)) + + +def get_approve_url(comment): + """ + Get the URL for the "approve this comment from moderation" view. + """ + if _check_attr('get_approve_url'): + return get_comment_app().get_approve_url(comment) + else: + return reverse('comments-approve', args=(comment.id,)) + + +def get_user_display(user): + """Get the full name or username to display for a user.""" + if _check_attr('get_user_display'): + return get_comment_app().get_user_display(user) + else: + return user.get_full_name() or user.get_username() diff --git a/commentary/abstracts.py b/commentary/abstracts.py new file mode 100644 index 0000000..027c505 --- /dev/null +++ b/commentary/abstracts.py @@ -0,0 +1,138 @@ +from __future__ import unicode_literals + +from django.conf import settings +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType +from django.contrib.sites.models import Site +from django.db import models +from django.urls import reverse +from django.utils.encoding import python_2_unicode_compatible +from django.utils.html import strip_tags +from django.utils.functional import cached_property +from django.utils.translation import ugettext_lazy as _ + +from . import COMMENTS_ALLOW_HTML, get_user_display +from .managers import CommentManager + + +class BaseCommentAbstractModel(models.Model): + """ + An abstract base class that any custom comment models probably should + subclass. + """ + # Content-object field + content_type = models.ForeignKey( + ContentType, verbose_name=_('content type'), + related_name='content_type_set_for_%(class)s', on_delete=models.CASCADE + ) + object_pk = models.CharField(_('object ID'), max_length=255) + content_object = GenericForeignKey( + ct_field='content_type', fk_field='object_pk' + ) + + # Metadata about the comment + site = models.ForeignKey(Site, on_delete=models.CASCADE) + + class Meta: + abstract = True + + def get_content_object_url(self): + """ + Get a URL suitable for redirecting to the content object. + """ + return reverse( + 'comments-url-redirect', + args=(self.content_type_id, self.object_pk) + ) + + +@python_2_unicode_compatible +class CommentAbstractModel(BaseCommentAbstractModel): + """ + A user comment about some object. + """ + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + related_name='%(class)s_comments', + verbose_name=_('user'), blank=True, + null=True, on_delete=models.SET_NULL + ) + + body = models.TextField(_('comment'), db_column='comment') + + submit_date = models.DateTimeField( + _('date/time submitted'), auto_now_add=True, db_index=True + ) + edit_date = models.DateTimeField( + _('date/time of last edit'), auto_now=True, db_index=True + ) + + is_public = models.BooleanField( + _('is public'), default=True, help_text=_( + 'Uncheck this box to make the comment ' + 'effectively disappear from the site.' + ) + ) + is_removed = models.BooleanField( + _('is removed'), default=False, help_text=_( + 'Check this box if the comment is inappropriate. A "This ' + 'comment has been removed" message will be displayed instead.' + ) + ) + + parent = models.ForeignKey( + 'self', related_name='replies', blank=True, + null=True, on_delete=models.CASCADE + ) + + # TODO: add upvotes & downvotes + + # Manager + objects = CommentManager() + + class Meta: + abstract = True + ordering = ('submit_date',) + permissions = ( + ('can_moderate', 'Can moderate comments'), + ) + verbose_name = _('comment') + verbose_name_plural = _('comments') + + def __str__(self): + return '%s: %s...' % (self.user_display, strip_tags(self.body)[:50]) + + @property + def is_edited(self): + """Check whether this comment has been edited.""" + return self.submit_date != self.edit_date + + @property + def user_display(self): + """Display the full name/username of the commenter.""" + return get_user_display(self.user) + + @cached_property + def _date(self): + return self.submit_date.date() + + def get_absolute_url(self, anchor_pattern='#c%(id)s'): + return self.get_content_object_url() + (anchor_pattern % self.__dict__) + + def strip_body(self): + return strip_tags(self.body) if COMMENTS_ALLOW_HTML else self.body + + def get_as_text(self): + """ + Return this comment as plain text. Useful for emails. + """ + return _( + 'Posted by %(user)s at %(date)s\n\n' + '%(comment)s\n\nhttp://%(domain)s%(url)s' + ) % { + 'user': self.user_display, + 'date': self.submit_date, + 'comment': self.strip_body(), + 'domain': self.site.domain, + 'url': self.get_absolute_url() + } diff --git a/commentary/admin.py b/commentary/admin.py new file mode 100644 index 0000000..62b8b87 --- /dev/null +++ b/commentary/admin.py @@ -0,0 +1,86 @@ +from __future__ import unicode_literals + +from django.contrib import admin +from django.contrib.auth import get_user_model +from django.utils.translation import ugettext_lazy as _, ungettext + +from commentary import get_model +from commentary.views.moderation import ( + perform_flag, perform_approve, perform_delete +) + +USERNAME_FIELD = 'user__' + get_user_model().USERNAME_FIELD + + +class CommentsAdmin(admin.ModelAdmin): + list_display = ( + 'user', 'content_type', 'object_pk', + 'submit_date', 'is_public', 'is_removed' + ) + list_filter = ( + 'submit_date', 'user', 'content_type', + 'is_public', 'is_removed' + ) + date_hierarchy = 'submit_date' + ordering = ('-submit_date',) + raw_id_fields = ('user',) + search_fields = ('body', USERNAME_FIELD) + actions = ('flag_comments', 'approve_comments', 'remove_comments') + + def get_actions(self, request): + actions = super(CommentsAdmin, self).get_actions(request) + # Only superusers should be able to delete the comments from the DB. + if not request.user.is_superuser: + actions.pop('delete_selected', None) + if not request.user.has_perm('commentary.can_moderate'): + actions.pop('approve_comments', None) + actions.pop('remove_comments', None) + return actions + + def flag_comments(self, request, queryset): + self._bulk_flag( + request, queryset, perform_flag, + lambda n: ungettext('flagged', 'flagged', n) + ) + + flag_comments.short_description = _('Flag selected comments') + + def approve_comments(self, request, queryset): + self._bulk_flag( + request, queryset, perform_approve, + lambda n: ungettext('approved', 'approved', n) + ) + + approve_comments.short_description = _('Approve selected comments') + + def remove_comments(self, request, queryset): + self._bulk_flag( + request, queryset, perform_delete, + lambda n: ungettext('removed', 'removed', n) + ) + + remove_comments.short_description = _('Remove selected comments') + + def _bulk_flag(self, request, queryset, action, done_message): + """ + Flag, approve, or remove some comments from an admin action. + Actually calls the `action` argument to perform the heavy lifting. + """ + n_comments = 0 + for comment in queryset: + action(request, comment) + n_comments += 1 + msg = ungettext( + '%(count)s comment was successfully %(action)s.', + '%(count)s comments were successfully %(action)s.', n_comments + ) + self.message_user(request, msg % { + 'count': n_comments, 'action': done_message(n_comments) + }) + + +# Only register the default admin if the model is the built-in comment model +# (this won't be true if there's a custom comment app). +Klass = get_model() +if Klass._meta.app_label == 'commentary': + admin.site.register(Klass, CommentsAdmin) diff --git a/django_comments/feeds.py b/commentary/feeds.py similarity index 50% rename from django_comments/feeds.py rename to commentary/feeds.py index bc8c501..132e322 100644 --- a/django_comments/feeds.py +++ b/commentary/feeds.py @@ -2,32 +2,42 @@ from django.contrib.syndication.views import Feed from django.utils.translation import ugettext as _ -import django_comments +from . import get_model class LatestCommentFeed(Feed): """Feed of latest comments on the current site.""" - def __call__(self, request, *args, **kwargs): self.site = get_current_site(request) - return super(LatestCommentFeed, self).__call__(request, *args, **kwargs) + return super(LatestCommentFeed, self).__call__( + request, *args, **kwargs + ) + @property def title(self): - return _("%(site_name)s comments") % dict(site_name=self.site.name) - - def link(self): - return "http://%s/" % (self.site.domain) + return _('%(site_name)s comments') % { + 'site_name': self.site.name + } + @property def description(self): - return _("Latest comments on %(site_name)s") % dict(site_name=self.site.name) + return _('Latest comments on %(site_name)s') % { + 'site_name': self.site.name + } + @property def items(self): - qs = django_comments.get_model().objects.filter( + return get_model().objects.filter( site__pk=self.site.pk, is_public=True, is_removed=False, - ) - return qs.order_by('-submit_date')[:40] + ).order_by('-submit_date')[:40] def item_pubdate(self, item): return item.submit_date + + def item_updateddate(self, item): + return item.edit_date + + def item_author_name(self, item): + return item.user_display diff --git a/commentary/forms.py b/commentary/forms.py new file mode 100644 index 0000000..80b9e01 --- /dev/null +++ b/commentary/forms.py @@ -0,0 +1,136 @@ +from time import time + +from django import forms +from django.conf import settings +from django.contrib.contenttypes.models import ContentType +from django.forms.utils import ErrorDict +from django.utils.crypto import salted_hmac, constant_time_compare +from django.utils.encoding import force_text +from django.utils.text import get_text_list +from django.utils.translation import ( + pgettext_lazy, ungettext, ugettext, ugettext_lazy as _ +) + +from . import COMMENTS_TIMEOUT, COMMENTS_WIDGET, get_model +from .models import Comment + + +class CommentSecurityForm(forms.Form): + """ + Handles the security aspects (anti-spoofing) for comment forms. + """ + content_type = forms.CharField(widget=forms.HiddenInput) + object_pk = forms.CharField(widget=forms.HiddenInput) + timestamp = forms.IntegerField(widget=forms.HiddenInput) + security_hash = forms.CharField( + min_length=40, max_length=40, widget=forms.HiddenInput + ) + honeypot = forms.CharField( + required=False, label=_( + 'If you enter anything in this field ' + 'your comment will be treated as spam' + ) + ) + + def __init__(self, target_object, data=None, initial=None, **kwargs): + self.target_object = target_object + if initial is None: + initial = {} + initial.update(self.generate_security_data()) + super(CommentSecurityForm, self).__init__( + data=data, initial=initial, **kwargs + ) + + @property + def security_errors(self): + """Return just those errors associated with security""" + errors = ('honeypot', 'timestamp', 'security_hash') + return ErrorDict({ + f: self.errors[f] for f in errors if f in self.errors + }) + + def clean_honeypot(self): + if self.cleaned_data['honeypot']: + raise forms.ValidationError(self.fields['honeypot'].label) + return '' + + def clean_security_hash(self): + """Check the security hash.""" + expected_hash = self.generate_security_hash( + self.data.get('content_type', ''), + self.data.get('object_pk', ''), + self.data.get('timestamp', '') + ) + actual_hash = self.cleaned_data['security_hash'] + if not constant_time_compare(expected_hash, actual_hash): + raise forms.ValidationError('Security hash check failed.') + return actual_hash + + def clean_timestamp(self): + """ + Make sure the timestamp isn't too far (default is 1 hour) in the past. + """ + ts = self.cleaned_data['timestamp'] + if time() - ts > COMMENTS_TIMEOUT: + raise forms.ValidationError('Timestamp check failed.') + return ts + + def generate_security_data(self): + """Generate a dict of security data for "initial" data.""" + timestamp = int(time()) + return { + 'content_type': str(self.target_object._meta), + 'object_pk': str(self.target_object._get_pk_val()), + 'timestamp': str(timestamp), + 'security_hash': self.initial_security_hash(timestamp) + } + + def initial_security_hash(self, timestamp): + """ + Generate the initial security hash from self.content_object + and a (unix) timestamp. + """ + return self.generate_security_hash( + str(self.target_object._meta), + str(self.target_object._get_pk_val()), + str(timestamp) + ) + + def generate_security_hash(self, content_type, object_pk, timestamp): + """ + Generate a HMAC security hash from the provided info. + """ + info = '-'.join([content_type, object_pk, timestamp]) + key_salt = 'commentary.forms.CommentSecurityForm' + return salted_hmac(key_salt, info).hexdigest() + + +class CommentForm(CommentSecurityForm): + # Translators: 'Comment' is a noun here. + comment = forms.CharField( + label=_('Comment'), widget=COMMENTS_WIDGET + ) + + _model = get_model() + + def get_comment_object(self, site_id=settings.SITE_ID): + if not self.is_valid(): + raise ValueError('Invalid form') + return self._prevent_duplicates(self._model( + content_type=ContentType.objects.get_for_model(self.target_object), + object_pk=force_text(self.target_object._get_pk_val()), + body=self.cleaned_data['comment'], site_id=site_id + )) + + def _prevent_duplicates(self, new): + comments = self._model.objects.filter( + content_type=new.content_type, user=new.user, + object_pk=new.object_pk, body=new.body + ) + return next((c for c in comments if c._date == new._date), new) + + class Meta: + fields = ( + 'content_type', 'object_pk', 'timestamp', + 'security_hash', 'honeypot', 'comment' + ) diff --git a/django_comments/locale/af/LC_MESSAGES/django.mo b/commentary/locale/af/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/af/LC_MESSAGES/django.mo rename to commentary/locale/af/LC_MESSAGES/django.mo diff --git a/django_comments/locale/af/LC_MESSAGES/django.po b/commentary/locale/af/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/af/LC_MESSAGES/django.po rename to commentary/locale/af/LC_MESSAGES/django.po diff --git a/django_comments/locale/ar/LC_MESSAGES/django.mo b/commentary/locale/ar/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ar/LC_MESSAGES/django.mo rename to commentary/locale/ar/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ar/LC_MESSAGES/django.po b/commentary/locale/ar/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ar/LC_MESSAGES/django.po rename to commentary/locale/ar/LC_MESSAGES/django.po diff --git a/django_comments/locale/az/LC_MESSAGES/django.mo b/commentary/locale/az/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/az/LC_MESSAGES/django.mo rename to commentary/locale/az/LC_MESSAGES/django.mo diff --git a/django_comments/locale/az/LC_MESSAGES/django.po b/commentary/locale/az/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/az/LC_MESSAGES/django.po rename to commentary/locale/az/LC_MESSAGES/django.po diff --git a/django_comments/locale/be/LC_MESSAGES/django.mo b/commentary/locale/be/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/be/LC_MESSAGES/django.mo rename to commentary/locale/be/LC_MESSAGES/django.mo diff --git a/django_comments/locale/be/LC_MESSAGES/django.po b/commentary/locale/be/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/be/LC_MESSAGES/django.po rename to commentary/locale/be/LC_MESSAGES/django.po diff --git a/django_comments/locale/bg/LC_MESSAGES/django.mo b/commentary/locale/bg/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/bg/LC_MESSAGES/django.mo rename to commentary/locale/bg/LC_MESSAGES/django.mo diff --git a/django_comments/locale/bg/LC_MESSAGES/django.po b/commentary/locale/bg/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/bg/LC_MESSAGES/django.po rename to commentary/locale/bg/LC_MESSAGES/django.po diff --git a/django_comments/locale/bn/LC_MESSAGES/django.mo b/commentary/locale/bn/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/bn/LC_MESSAGES/django.mo rename to commentary/locale/bn/LC_MESSAGES/django.mo diff --git a/django_comments/locale/bn/LC_MESSAGES/django.po b/commentary/locale/bn/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/bn/LC_MESSAGES/django.po rename to commentary/locale/bn/LC_MESSAGES/django.po diff --git a/django_comments/locale/br/LC_MESSAGES/django.mo b/commentary/locale/br/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/br/LC_MESSAGES/django.mo rename to commentary/locale/br/LC_MESSAGES/django.mo diff --git a/django_comments/locale/br/LC_MESSAGES/django.po b/commentary/locale/br/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/br/LC_MESSAGES/django.po rename to commentary/locale/br/LC_MESSAGES/django.po diff --git a/django_comments/locale/bs/LC_MESSAGES/django.mo b/commentary/locale/bs/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/bs/LC_MESSAGES/django.mo rename to commentary/locale/bs/LC_MESSAGES/django.mo diff --git a/django_comments/locale/bs/LC_MESSAGES/django.po b/commentary/locale/bs/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/bs/LC_MESSAGES/django.po rename to commentary/locale/bs/LC_MESSAGES/django.po diff --git a/django_comments/locale/ca/LC_MESSAGES/django.mo b/commentary/locale/ca/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ca/LC_MESSAGES/django.mo rename to commentary/locale/ca/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ca/LC_MESSAGES/django.po b/commentary/locale/ca/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ca/LC_MESSAGES/django.po rename to commentary/locale/ca/LC_MESSAGES/django.po diff --git a/django_comments/locale/cs/LC_MESSAGES/django.mo b/commentary/locale/cs/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/cs/LC_MESSAGES/django.mo rename to commentary/locale/cs/LC_MESSAGES/django.mo diff --git a/django_comments/locale/cs/LC_MESSAGES/django.po b/commentary/locale/cs/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/cs/LC_MESSAGES/django.po rename to commentary/locale/cs/LC_MESSAGES/django.po diff --git a/django_comments/locale/cy/LC_MESSAGES/django.mo b/commentary/locale/cy/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/cy/LC_MESSAGES/django.mo rename to commentary/locale/cy/LC_MESSAGES/django.mo diff --git a/django_comments/locale/cy/LC_MESSAGES/django.po b/commentary/locale/cy/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/cy/LC_MESSAGES/django.po rename to commentary/locale/cy/LC_MESSAGES/django.po diff --git a/django_comments/locale/da/LC_MESSAGES/django.mo b/commentary/locale/da/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/da/LC_MESSAGES/django.mo rename to commentary/locale/da/LC_MESSAGES/django.mo diff --git a/django_comments/locale/da/LC_MESSAGES/django.po b/commentary/locale/da/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/da/LC_MESSAGES/django.po rename to commentary/locale/da/LC_MESSAGES/django.po diff --git a/django_comments/locale/de/LC_MESSAGES/django.mo b/commentary/locale/de/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/de/LC_MESSAGES/django.mo rename to commentary/locale/de/LC_MESSAGES/django.mo diff --git a/django_comments/locale/de/LC_MESSAGES/django.po b/commentary/locale/de/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/de/LC_MESSAGES/django.po rename to commentary/locale/de/LC_MESSAGES/django.po diff --git a/django_comments/locale/el/LC_MESSAGES/django.mo b/commentary/locale/el/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/el/LC_MESSAGES/django.mo rename to commentary/locale/el/LC_MESSAGES/django.mo diff --git a/django_comments/locale/el/LC_MESSAGES/django.po b/commentary/locale/el/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/el/LC_MESSAGES/django.po rename to commentary/locale/el/LC_MESSAGES/django.po diff --git a/commentary/locale/en/LC_MESSAGES/django.mo b/commentary/locale/en/LC_MESSAGES/django.mo new file mode 100644 index 0000000..5f3ab3b Binary files /dev/null and b/commentary/locale/en/LC_MESSAGES/django.mo differ diff --git a/django_comments/locale/en/LC_MESSAGES/django.po b/commentary/locale/en/LC_MESSAGES/django.po similarity index 66% rename from django_comments/locale/en/LC_MESSAGES/django.po rename to commentary/locale/en/LC_MESSAGES/django.po index 2ff4d0f..9715fa7 100644 --- a/django_comments/locale/en/LC_MESSAGES/django.po +++ b/commentary/locale/en/LC_MESSAGES/django.po @@ -16,59 +16,59 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: django_comments/abstracts.py:26 +#: commentary/abstracts.py:26 msgid "content type" msgstr "content type" -#: django_comments/abstracts.py:29 +#: commentary/abstracts.py:29 msgid "object ID" msgstr "object ID" -#: django_comments/abstracts.py:57 django_comments/models.py:32 +#: commentary/abstracts.py:57 commentary/models.py:32 msgid "user" msgstr "user" -#: django_comments/abstracts.py:60 +#: commentary/abstracts.py:60 msgid "user's name" msgstr "user's name" -#: django_comments/abstracts.py:62 +#: commentary/abstracts.py:62 msgid "user's email address" msgstr "user's email address" -#: django_comments/abstracts.py:64 +#: commentary/abstracts.py:64 msgid "user's URL" msgstr "user's URL" #. Translators: 'comment' is a noun here. -#: django_comments/abstracts.py:66 django_comments/abstracts.py:86 -#: django_comments/models.py:37 +#: commentary/abstracts.py:66 commentary/abstracts.py:86 +#: commentary/models.py:37 msgid "comment" msgstr "comment" -#: django_comments/abstracts.py:69 +#: commentary/abstracts.py:69 msgid "date/time submitted" msgstr "date/time submitted" -#: django_comments/abstracts.py:70 +#: commentary/abstracts.py:70 msgid "IP address" msgstr "IP address" -#: django_comments/abstracts.py:71 +#: commentary/abstracts.py:71 msgid "is public" msgstr "is public" -#: django_comments/abstracts.py:72 +#: commentary/abstracts.py:72 msgid "" "Uncheck this box to make the comment effectively disappear from the site." msgstr "" "Uncheck this box to make the comment effectively disappear from the site." -#: django_comments/abstracts.py:74 +#: commentary/abstracts.py:74 msgid "is removed" msgstr "is removed" -#: django_comments/abstracts.py:75 +#: commentary/abstracts.py:75 msgid "" "Check this box if the comment is inappropriate. A \"This comment has been " "removed\" message will be displayed instead." @@ -76,11 +76,11 @@ msgstr "" "Check this box if the comment is inappropriate. A \"This comment has been " "removed\" message will be displayed instead." -#: django_comments/abstracts.py:87 +#: commentary/abstracts.py:87 msgid "comments" msgstr "comments" -#: django_comments/abstracts.py:132 +#: commentary/abstracts.py:132 msgid "" "This comment was posted by an authenticated user and thus the name is read-" "only." @@ -88,7 +88,7 @@ msgstr "" "This comment was posted by an authenticated user and thus the name is read-" "only." -#: django_comments/abstracts.py:143 +#: commentary/abstracts.py:143 msgid "" "This comment was posted by an authenticated user and thus the email is read-" "only." @@ -96,7 +96,7 @@ msgstr "" "This comment was posted by an authenticated user and thus the email is read-" "only." -#: django_comments/abstracts.py:171 +#: commentary/abstracts.py:171 #, python-format msgid "" "Posted by %(user)s at %(date)s\n" @@ -111,45 +111,45 @@ msgstr "" "\n" "http://%(domain)s%(url)s" -#: django_comments/admin.py:28 +#: commentary/admin.py:28 msgid "Content" msgstr "Content" -#: django_comments/admin.py:32 +#: commentary/admin.py:32 msgid "Metadata" msgstr "Metadata" -#: django_comments/admin.py:59 +#: commentary/admin.py:59 msgid "flagged" msgid_plural "flagged" msgstr[0] "flagged" msgstr[1] "flagged" -#: django_comments/admin.py:61 +#: commentary/admin.py:61 msgid "Flag selected comments" msgstr "Flag selected comments" -#: django_comments/admin.py:65 +#: commentary/admin.py:65 msgid "approved" msgid_plural "approved" msgstr[0] "approved" msgstr[1] "approved" -#: django_comments/admin.py:67 +#: commentary/admin.py:67 msgid "Approve selected comments" msgstr "Approve selected comments" -#: django_comments/admin.py:71 +#: commentary/admin.py:71 msgid "removed" msgid_plural "removed" msgstr[0] "removed" msgstr[1] "removed" -#: django_comments/admin.py:73 +#: commentary/admin.py:73 msgid "Remove selected comments" msgstr "Remove selected comments" -#: django_comments/admin.py:85 +#: commentary/admin.py:85 #, fuzzy, python-format #| msgid "1 comment was successfully %(action)s." #| msgid_plural "%(count)s comments were successfully %(action)s." @@ -158,164 +158,164 @@ msgid_plural "%(count)s comments were successfully %(action)s." msgstr[0] "1 comment was successfully %(action)s." msgstr[1] "%(count)s comments were successfully %(action)s." -#: django_comments/feeds.py:16 +#: commentary/feeds.py:16 #, python-format msgid "%(site_name)s comments" msgstr "%(site_name)s comments" -#: django_comments/feeds.py:22 +#: commentary/feeds.py:22 #, python-format msgid "Latest comments on %(site_name)s" msgstr "Latest comments on %(site_name)s" -#: django_comments/forms.py:101 +#: commentary/forms.py:101 msgctxt "Person name" msgid "Name" msgstr "Name" -#: django_comments/forms.py:102 +#: commentary/forms.py:102 msgid "Email address" msgstr "Email address" -#: django_comments/forms.py:103 +#: commentary/forms.py:103 msgid "URL" msgstr "URL" #. Translators: 'Comment' is a noun here. -#: django_comments/forms.py:105 +#: commentary/forms.py:105 msgid "Comment" msgstr "Comment" -#: django_comments/forms.py:183 +#: commentary/forms.py:183 #, python-format msgid "Watch your mouth! The word %s is not allowed here." msgid_plural "Watch your mouth! The words %s are not allowed here." msgstr[0] "Watch your mouth! The word %s is not allowed here." msgstr[1] "Watch your mouth! The words %s are not allowed here." -#: django_comments/forms.py:187 -#: django_comments/templates/comments/preview.html:17 +#: commentary/forms.py:187 +#: commentary/templates/comments/preview.html:17 msgid "and" msgstr "and" -#: django_comments/forms.py:193 +#: commentary/forms.py:193 msgid "" "If you enter anything in this field your comment will be treated as spam" msgstr "" "If you enter anything in this field your comment will be treated as spam" #. Translators: 'flag' is a noun here. -#: django_comments/models.py:40 +#: commentary/models.py:40 msgid "flag" msgstr "flag" -#: django_comments/models.py:39 +#: commentary/models.py:39 msgid "date" msgstr "date" -#: django_comments/models.py:49 +#: commentary/models.py:49 msgid "comment flag" msgstr "comment flag" -#: django_comments/models.py:50 +#: commentary/models.py:50 msgid "comment flags" msgstr "comment flags" -#: django_comments/moderation.py:253 +#: commentary/moderation.py:253 #, python-format msgid "[%(site)s] New comment posted on \"%(object)s\"" msgstr "" -#: django_comments/templates/comments/approve.html:4 +#: commentary/templates/comments/approve.html:4 msgid "Approve a comment" msgstr "Approve a comment" -#: django_comments/templates/comments/approve.html:7 +#: commentary/templates/comments/approve.html:7 msgid "Really make this comment public?" msgstr "Really make this comment public?" -#: django_comments/templates/comments/approve.html:13 +#: commentary/templates/comments/approve.html:13 msgid "Approve" msgstr "Approve" -#: django_comments/templates/comments/approved.html:4 +#: commentary/templates/comments/approved.html:4 msgid "Thanks for approving" msgstr "Thanks for approving" -#: django_comments/templates/comments/approved.html:7 -#: django_comments/templates/comments/deleted.html:7 -#: django_comments/templates/comments/flagged.html:7 +#: commentary/templates/comments/approved.html:7 +#: commentary/templates/comments/deleted.html:7 +#: commentary/templates/comments/flagged.html:7 msgid "" "Thanks for taking the time to improve the quality of discussion on our site" msgstr "" "Thanks for taking the time to improve the quality of discussion on our site" -#: django_comments/templates/comments/delete.html:4 +#: commentary/templates/comments/delete.html:4 msgid "Remove a comment" msgstr "Remove a comment" -#: django_comments/templates/comments/delete.html:7 +#: commentary/templates/comments/delete.html:7 msgid "Really remove this comment?" msgstr "Really remove this comment?" -#: django_comments/templates/comments/delete.html:13 +#: commentary/templates/comments/delete.html:13 msgid "Remove" msgstr "Remove" -#: django_comments/templates/comments/deleted.html:4 +#: commentary/templates/comments/deleted.html:4 msgid "Thanks for removing" msgstr "Thanks for removing" -#: django_comments/templates/comments/flag.html:4 +#: commentary/templates/comments/flag.html:4 msgid "Flag this comment" msgstr "Flag this comment" -#: django_comments/templates/comments/flag.html:7 +#: commentary/templates/comments/flag.html:7 msgid "Really flag this comment?" msgstr "Really flag this comment?" -#: django_comments/templates/comments/flag.html:13 +#: commentary/templates/comments/flag.html:13 msgid "Flag" msgstr "Flag" -#: django_comments/templates/comments/flagged.html:4 +#: commentary/templates/comments/flagged.html:4 msgid "Thanks for flagging" msgstr "Thanks for flagging" -#: django_comments/templates/comments/form.html:18 -#: django_comments/templates/comments/preview.html:34 +#: commentary/templates/comments/form.html:18 +#: commentary/templates/comments/preview.html:34 msgid "Post" msgstr "Post" -#: django_comments/templates/comments/form.html:19 -#: django_comments/templates/comments/preview.html:35 +#: commentary/templates/comments/form.html:19 +#: commentary/templates/comments/preview.html:35 msgid "Preview" msgstr "Preview" -#: django_comments/templates/comments/posted.html:4 +#: commentary/templates/comments/posted.html:4 msgid "Thanks for commenting" msgstr "Thanks for commenting" -#: django_comments/templates/comments/posted.html:7 +#: commentary/templates/comments/posted.html:7 msgid "Thank you for your comment" msgstr "Thank you for your comment" -#: django_comments/templates/comments/preview.html:4 -#: django_comments/templates/comments/preview.html:14 +#: commentary/templates/comments/preview.html:4 +#: commentary/templates/comments/preview.html:14 msgid "Preview your comment" msgstr "Preview your comment" -#: django_comments/templates/comments/preview.html:12 +#: commentary/templates/comments/preview.html:12 msgid "Please correct the error below" msgid_plural "Please correct the errors below" msgstr[0] "Please correct the error below" msgstr[1] "Please correct the errors below" -#: django_comments/templates/comments/preview.html:17 +#: commentary/templates/comments/preview.html:17 msgid "Post your comment" msgstr "Post your comment" #. Translators: This string follows the 'Post your comment' submit button. -#: django_comments/templates/comments/preview.html:20 +#: commentary/templates/comments/preview.html:20 msgid "or make changes" msgstr "or make changes" diff --git a/django_comments/locale/en_GB/LC_MESSAGES/django.mo b/commentary/locale/en_GB/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/en_GB/LC_MESSAGES/django.mo rename to commentary/locale/en_GB/LC_MESSAGES/django.mo diff --git a/django_comments/locale/en_GB/LC_MESSAGES/django.po b/commentary/locale/en_GB/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/en_GB/LC_MESSAGES/django.po rename to commentary/locale/en_GB/LC_MESSAGES/django.po diff --git a/django_comments/locale/eo/LC_MESSAGES/django.mo b/commentary/locale/eo/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/eo/LC_MESSAGES/django.mo rename to commentary/locale/eo/LC_MESSAGES/django.mo diff --git a/django_comments/locale/eo/LC_MESSAGES/django.po b/commentary/locale/eo/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/eo/LC_MESSAGES/django.po rename to commentary/locale/eo/LC_MESSAGES/django.po diff --git a/django_comments/locale/es/LC_MESSAGES/django.mo b/commentary/locale/es/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/es/LC_MESSAGES/django.mo rename to commentary/locale/es/LC_MESSAGES/django.mo diff --git a/django_comments/locale/es/LC_MESSAGES/django.po b/commentary/locale/es/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/es/LC_MESSAGES/django.po rename to commentary/locale/es/LC_MESSAGES/django.po diff --git a/django_comments/locale/es_AR/LC_MESSAGES/django.mo b/commentary/locale/es_AR/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/es_AR/LC_MESSAGES/django.mo rename to commentary/locale/es_AR/LC_MESSAGES/django.mo diff --git a/django_comments/locale/es_AR/LC_MESSAGES/django.po b/commentary/locale/es_AR/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/es_AR/LC_MESSAGES/django.po rename to commentary/locale/es_AR/LC_MESSAGES/django.po diff --git a/django_comments/locale/es_MX/LC_MESSAGES/django.mo b/commentary/locale/es_MX/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/es_MX/LC_MESSAGES/django.mo rename to commentary/locale/es_MX/LC_MESSAGES/django.mo diff --git a/django_comments/locale/es_MX/LC_MESSAGES/django.po b/commentary/locale/es_MX/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/es_MX/LC_MESSAGES/django.po rename to commentary/locale/es_MX/LC_MESSAGES/django.po diff --git a/django_comments/locale/et/LC_MESSAGES/django.mo b/commentary/locale/et/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/et/LC_MESSAGES/django.mo rename to commentary/locale/et/LC_MESSAGES/django.mo diff --git a/django_comments/locale/et/LC_MESSAGES/django.po b/commentary/locale/et/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/et/LC_MESSAGES/django.po rename to commentary/locale/et/LC_MESSAGES/django.po diff --git a/django_comments/locale/eu/LC_MESSAGES/django.mo b/commentary/locale/eu/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/eu/LC_MESSAGES/django.mo rename to commentary/locale/eu/LC_MESSAGES/django.mo diff --git a/django_comments/locale/eu/LC_MESSAGES/django.po b/commentary/locale/eu/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/eu/LC_MESSAGES/django.po rename to commentary/locale/eu/LC_MESSAGES/django.po diff --git a/django_comments/locale/fa/LC_MESSAGES/django.mo b/commentary/locale/fa/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/fa/LC_MESSAGES/django.mo rename to commentary/locale/fa/LC_MESSAGES/django.mo diff --git a/django_comments/locale/fa/LC_MESSAGES/django.po b/commentary/locale/fa/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/fa/LC_MESSAGES/django.po rename to commentary/locale/fa/LC_MESSAGES/django.po diff --git a/django_comments/locale/fi/LC_MESSAGES/django.mo b/commentary/locale/fi/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/fi/LC_MESSAGES/django.mo rename to commentary/locale/fi/LC_MESSAGES/django.mo diff --git a/django_comments/locale/fi/LC_MESSAGES/django.po b/commentary/locale/fi/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/fi/LC_MESSAGES/django.po rename to commentary/locale/fi/LC_MESSAGES/django.po diff --git a/django_comments/locale/fr/LC_MESSAGES/django.mo b/commentary/locale/fr/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/fr/LC_MESSAGES/django.mo rename to commentary/locale/fr/LC_MESSAGES/django.mo diff --git a/django_comments/locale/fr/LC_MESSAGES/django.po b/commentary/locale/fr/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/fr/LC_MESSAGES/django.po rename to commentary/locale/fr/LC_MESSAGES/django.po diff --git a/django_comments/locale/fy_NL/LC_MESSAGES/django.mo b/commentary/locale/fy_NL/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/fy_NL/LC_MESSAGES/django.mo rename to commentary/locale/fy_NL/LC_MESSAGES/django.mo diff --git a/django_comments/locale/fy_NL/LC_MESSAGES/django.po b/commentary/locale/fy_NL/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/fy_NL/LC_MESSAGES/django.po rename to commentary/locale/fy_NL/LC_MESSAGES/django.po diff --git a/django_comments/locale/ga/LC_MESSAGES/django.mo b/commentary/locale/ga/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ga/LC_MESSAGES/django.mo rename to commentary/locale/ga/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ga/LC_MESSAGES/django.po b/commentary/locale/ga/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ga/LC_MESSAGES/django.po rename to commentary/locale/ga/LC_MESSAGES/django.po diff --git a/django_comments/locale/gd/LC_MESSAGES/django.mo b/commentary/locale/gd/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/gd/LC_MESSAGES/django.mo rename to commentary/locale/gd/LC_MESSAGES/django.mo diff --git a/django_comments/locale/gd/LC_MESSAGES/django.po b/commentary/locale/gd/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/gd/LC_MESSAGES/django.po rename to commentary/locale/gd/LC_MESSAGES/django.po diff --git a/django_comments/locale/gl/LC_MESSAGES/django.mo b/commentary/locale/gl/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/gl/LC_MESSAGES/django.mo rename to commentary/locale/gl/LC_MESSAGES/django.mo diff --git a/django_comments/locale/gl/LC_MESSAGES/django.po b/commentary/locale/gl/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/gl/LC_MESSAGES/django.po rename to commentary/locale/gl/LC_MESSAGES/django.po diff --git a/django_comments/locale/he/LC_MESSAGES/django.mo b/commentary/locale/he/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/he/LC_MESSAGES/django.mo rename to commentary/locale/he/LC_MESSAGES/django.mo diff --git a/django_comments/locale/he/LC_MESSAGES/django.po b/commentary/locale/he/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/he/LC_MESSAGES/django.po rename to commentary/locale/he/LC_MESSAGES/django.po diff --git a/django_comments/locale/hi/LC_MESSAGES/django.mo b/commentary/locale/hi/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/hi/LC_MESSAGES/django.mo rename to commentary/locale/hi/LC_MESSAGES/django.mo diff --git a/django_comments/locale/hi/LC_MESSAGES/django.po b/commentary/locale/hi/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/hi/LC_MESSAGES/django.po rename to commentary/locale/hi/LC_MESSAGES/django.po diff --git a/django_comments/locale/hr/LC_MESSAGES/django.mo b/commentary/locale/hr/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/hr/LC_MESSAGES/django.mo rename to commentary/locale/hr/LC_MESSAGES/django.mo diff --git a/django_comments/locale/hr/LC_MESSAGES/django.po b/commentary/locale/hr/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/hr/LC_MESSAGES/django.po rename to commentary/locale/hr/LC_MESSAGES/django.po diff --git a/django_comments/locale/hu/LC_MESSAGES/django.mo b/commentary/locale/hu/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/hu/LC_MESSAGES/django.mo rename to commentary/locale/hu/LC_MESSAGES/django.mo diff --git a/django_comments/locale/hu/LC_MESSAGES/django.po b/commentary/locale/hu/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/hu/LC_MESSAGES/django.po rename to commentary/locale/hu/LC_MESSAGES/django.po diff --git a/django_comments/locale/ia/LC_MESSAGES/django.mo b/commentary/locale/ia/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ia/LC_MESSAGES/django.mo rename to commentary/locale/ia/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ia/LC_MESSAGES/django.po b/commentary/locale/ia/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ia/LC_MESSAGES/django.po rename to commentary/locale/ia/LC_MESSAGES/django.po diff --git a/django_comments/locale/id/LC_MESSAGES/django.mo b/commentary/locale/id/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/id/LC_MESSAGES/django.mo rename to commentary/locale/id/LC_MESSAGES/django.mo diff --git a/django_comments/locale/id/LC_MESSAGES/django.po b/commentary/locale/id/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/id/LC_MESSAGES/django.po rename to commentary/locale/id/LC_MESSAGES/django.po diff --git a/django_comments/locale/is/LC_MESSAGES/django.mo b/commentary/locale/is/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/is/LC_MESSAGES/django.mo rename to commentary/locale/is/LC_MESSAGES/django.mo diff --git a/django_comments/locale/is/LC_MESSAGES/django.po b/commentary/locale/is/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/is/LC_MESSAGES/django.po rename to commentary/locale/is/LC_MESSAGES/django.po diff --git a/django_comments/locale/it/LC_MESSAGES/django.mo b/commentary/locale/it/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/it/LC_MESSAGES/django.mo rename to commentary/locale/it/LC_MESSAGES/django.mo diff --git a/django_comments/locale/it/LC_MESSAGES/django.po b/commentary/locale/it/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/it/LC_MESSAGES/django.po rename to commentary/locale/it/LC_MESSAGES/django.po diff --git a/django_comments/locale/ja/LC_MESSAGES/django.mo b/commentary/locale/ja/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ja/LC_MESSAGES/django.mo rename to commentary/locale/ja/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ja/LC_MESSAGES/django.po b/commentary/locale/ja/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ja/LC_MESSAGES/django.po rename to commentary/locale/ja/LC_MESSAGES/django.po diff --git a/django_comments/locale/ka/LC_MESSAGES/django.mo b/commentary/locale/ka/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ka/LC_MESSAGES/django.mo rename to commentary/locale/ka/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ka/LC_MESSAGES/django.po b/commentary/locale/ka/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ka/LC_MESSAGES/django.po rename to commentary/locale/ka/LC_MESSAGES/django.po diff --git a/django_comments/locale/kk/LC_MESSAGES/django.mo b/commentary/locale/kk/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/kk/LC_MESSAGES/django.mo rename to commentary/locale/kk/LC_MESSAGES/django.mo diff --git a/django_comments/locale/kk/LC_MESSAGES/django.po b/commentary/locale/kk/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/kk/LC_MESSAGES/django.po rename to commentary/locale/kk/LC_MESSAGES/django.po diff --git a/django_comments/locale/km/LC_MESSAGES/django.mo b/commentary/locale/km/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/km/LC_MESSAGES/django.mo rename to commentary/locale/km/LC_MESSAGES/django.mo diff --git a/django_comments/locale/km/LC_MESSAGES/django.po b/commentary/locale/km/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/km/LC_MESSAGES/django.po rename to commentary/locale/km/LC_MESSAGES/django.po diff --git a/django_comments/locale/kn/LC_MESSAGES/django.mo b/commentary/locale/kn/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/kn/LC_MESSAGES/django.mo rename to commentary/locale/kn/LC_MESSAGES/django.mo diff --git a/django_comments/locale/kn/LC_MESSAGES/django.po b/commentary/locale/kn/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/kn/LC_MESSAGES/django.po rename to commentary/locale/kn/LC_MESSAGES/django.po diff --git a/django_comments/locale/ko/LC_MESSAGES/django.mo b/commentary/locale/ko/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ko/LC_MESSAGES/django.mo rename to commentary/locale/ko/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ko/LC_MESSAGES/django.po b/commentary/locale/ko/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ko/LC_MESSAGES/django.po rename to commentary/locale/ko/LC_MESSAGES/django.po diff --git a/django_comments/locale/lt/LC_MESSAGES/django.mo b/commentary/locale/lt/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/lt/LC_MESSAGES/django.mo rename to commentary/locale/lt/LC_MESSAGES/django.mo diff --git a/django_comments/locale/lt/LC_MESSAGES/django.po b/commentary/locale/lt/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/lt/LC_MESSAGES/django.po rename to commentary/locale/lt/LC_MESSAGES/django.po diff --git a/django_comments/locale/lv/LC_MESSAGES/django.mo b/commentary/locale/lv/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/lv/LC_MESSAGES/django.mo rename to commentary/locale/lv/LC_MESSAGES/django.mo diff --git a/django_comments/locale/lv/LC_MESSAGES/django.po b/commentary/locale/lv/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/lv/LC_MESSAGES/django.po rename to commentary/locale/lv/LC_MESSAGES/django.po diff --git a/django_comments/locale/mk/LC_MESSAGES/django.mo b/commentary/locale/mk/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/mk/LC_MESSAGES/django.mo rename to commentary/locale/mk/LC_MESSAGES/django.mo diff --git a/django_comments/locale/mk/LC_MESSAGES/django.po b/commentary/locale/mk/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/mk/LC_MESSAGES/django.po rename to commentary/locale/mk/LC_MESSAGES/django.po diff --git a/django_comments/locale/ml/LC_MESSAGES/django.mo b/commentary/locale/ml/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ml/LC_MESSAGES/django.mo rename to commentary/locale/ml/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ml/LC_MESSAGES/django.po b/commentary/locale/ml/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ml/LC_MESSAGES/django.po rename to commentary/locale/ml/LC_MESSAGES/django.po diff --git a/django_comments/locale/mn/LC_MESSAGES/django.mo b/commentary/locale/mn/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/mn/LC_MESSAGES/django.mo rename to commentary/locale/mn/LC_MESSAGES/django.mo diff --git a/django_comments/locale/mn/LC_MESSAGES/django.po b/commentary/locale/mn/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/mn/LC_MESSAGES/django.po rename to commentary/locale/mn/LC_MESSAGES/django.po diff --git a/django_comments/locale/nb/LC_MESSAGES/django.mo b/commentary/locale/nb/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/nb/LC_MESSAGES/django.mo rename to commentary/locale/nb/LC_MESSAGES/django.mo diff --git a/django_comments/locale/nb/LC_MESSAGES/django.po b/commentary/locale/nb/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/nb/LC_MESSAGES/django.po rename to commentary/locale/nb/LC_MESSAGES/django.po diff --git a/django_comments/locale/ne/LC_MESSAGES/django.mo b/commentary/locale/ne/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ne/LC_MESSAGES/django.mo rename to commentary/locale/ne/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ne/LC_MESSAGES/django.po b/commentary/locale/ne/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ne/LC_MESSAGES/django.po rename to commentary/locale/ne/LC_MESSAGES/django.po diff --git a/django_comments/locale/nl/LC_MESSAGES/django.mo b/commentary/locale/nl/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/nl/LC_MESSAGES/django.mo rename to commentary/locale/nl/LC_MESSAGES/django.mo diff --git a/django_comments/locale/nl/LC_MESSAGES/django.po b/commentary/locale/nl/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/nl/LC_MESSAGES/django.po rename to commentary/locale/nl/LC_MESSAGES/django.po diff --git a/django_comments/locale/nn/LC_MESSAGES/django.mo b/commentary/locale/nn/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/nn/LC_MESSAGES/django.mo rename to commentary/locale/nn/LC_MESSAGES/django.mo diff --git a/django_comments/locale/nn/LC_MESSAGES/django.po b/commentary/locale/nn/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/nn/LC_MESSAGES/django.po rename to commentary/locale/nn/LC_MESSAGES/django.po diff --git a/django_comments/locale/pa/LC_MESSAGES/django.mo b/commentary/locale/pa/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/pa/LC_MESSAGES/django.mo rename to commentary/locale/pa/LC_MESSAGES/django.mo diff --git a/django_comments/locale/pa/LC_MESSAGES/django.po b/commentary/locale/pa/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/pa/LC_MESSAGES/django.po rename to commentary/locale/pa/LC_MESSAGES/django.po diff --git a/django_comments/locale/pl/LC_MESSAGES/django.mo b/commentary/locale/pl/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/pl/LC_MESSAGES/django.mo rename to commentary/locale/pl/LC_MESSAGES/django.mo diff --git a/django_comments/locale/pl/LC_MESSAGES/django.po b/commentary/locale/pl/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/pl/LC_MESSAGES/django.po rename to commentary/locale/pl/LC_MESSAGES/django.po diff --git a/django_comments/locale/pt/LC_MESSAGES/django.mo b/commentary/locale/pt/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/pt/LC_MESSAGES/django.mo rename to commentary/locale/pt/LC_MESSAGES/django.mo diff --git a/django_comments/locale/pt/LC_MESSAGES/django.po b/commentary/locale/pt/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/pt/LC_MESSAGES/django.po rename to commentary/locale/pt/LC_MESSAGES/django.po diff --git a/django_comments/locale/pt_BR/LC_MESSAGES/django.mo b/commentary/locale/pt_BR/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/pt_BR/LC_MESSAGES/django.mo rename to commentary/locale/pt_BR/LC_MESSAGES/django.mo diff --git a/django_comments/locale/pt_BR/LC_MESSAGES/django.po b/commentary/locale/pt_BR/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/pt_BR/LC_MESSAGES/django.po rename to commentary/locale/pt_BR/LC_MESSAGES/django.po diff --git a/django_comments/locale/ro/LC_MESSAGES/django.mo b/commentary/locale/ro/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ro/LC_MESSAGES/django.mo rename to commentary/locale/ro/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ro/LC_MESSAGES/django.po b/commentary/locale/ro/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ro/LC_MESSAGES/django.po rename to commentary/locale/ro/LC_MESSAGES/django.po diff --git a/django_comments/locale/ru/LC_MESSAGES/django.mo b/commentary/locale/ru/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ru/LC_MESSAGES/django.mo rename to commentary/locale/ru/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ru/LC_MESSAGES/django.po b/commentary/locale/ru/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ru/LC_MESSAGES/django.po rename to commentary/locale/ru/LC_MESSAGES/django.po diff --git a/django_comments/locale/sk/LC_MESSAGES/django.mo b/commentary/locale/sk/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/sk/LC_MESSAGES/django.mo rename to commentary/locale/sk/LC_MESSAGES/django.mo diff --git a/django_comments/locale/sk/LC_MESSAGES/django.po b/commentary/locale/sk/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/sk/LC_MESSAGES/django.po rename to commentary/locale/sk/LC_MESSAGES/django.po diff --git a/django_comments/locale/sl/LC_MESSAGES/django.mo b/commentary/locale/sl/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/sl/LC_MESSAGES/django.mo rename to commentary/locale/sl/LC_MESSAGES/django.mo diff --git a/django_comments/locale/sl/LC_MESSAGES/django.po b/commentary/locale/sl/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/sl/LC_MESSAGES/django.po rename to commentary/locale/sl/LC_MESSAGES/django.po diff --git a/django_comments/locale/sq/LC_MESSAGES/django.mo b/commentary/locale/sq/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/sq/LC_MESSAGES/django.mo rename to commentary/locale/sq/LC_MESSAGES/django.mo diff --git a/django_comments/locale/sq/LC_MESSAGES/django.po b/commentary/locale/sq/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/sq/LC_MESSAGES/django.po rename to commentary/locale/sq/LC_MESSAGES/django.po diff --git a/django_comments/locale/sr/LC_MESSAGES/django.mo b/commentary/locale/sr/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/sr/LC_MESSAGES/django.mo rename to commentary/locale/sr/LC_MESSAGES/django.mo diff --git a/django_comments/locale/sr/LC_MESSAGES/django.po b/commentary/locale/sr/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/sr/LC_MESSAGES/django.po rename to commentary/locale/sr/LC_MESSAGES/django.po diff --git a/django_comments/locale/sr_Latn/LC_MESSAGES/django.mo b/commentary/locale/sr_Latn/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/sr_Latn/LC_MESSAGES/django.mo rename to commentary/locale/sr_Latn/LC_MESSAGES/django.mo diff --git a/django_comments/locale/sr_Latn/LC_MESSAGES/django.po b/commentary/locale/sr_Latn/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/sr_Latn/LC_MESSAGES/django.po rename to commentary/locale/sr_Latn/LC_MESSAGES/django.po diff --git a/django_comments/locale/sv/LC_MESSAGES/django.mo b/commentary/locale/sv/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/sv/LC_MESSAGES/django.mo rename to commentary/locale/sv/LC_MESSAGES/django.mo diff --git a/django_comments/locale/sv/LC_MESSAGES/django.po b/commentary/locale/sv/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/sv/LC_MESSAGES/django.po rename to commentary/locale/sv/LC_MESSAGES/django.po diff --git a/django_comments/locale/sw/LC_MESSAGES/django.mo b/commentary/locale/sw/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/sw/LC_MESSAGES/django.mo rename to commentary/locale/sw/LC_MESSAGES/django.mo diff --git a/django_comments/locale/sw/LC_MESSAGES/django.po b/commentary/locale/sw/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/sw/LC_MESSAGES/django.po rename to commentary/locale/sw/LC_MESSAGES/django.po diff --git a/django_comments/locale/ta/LC_MESSAGES/django.mo b/commentary/locale/ta/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ta/LC_MESSAGES/django.mo rename to commentary/locale/ta/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ta/LC_MESSAGES/django.po b/commentary/locale/ta/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ta/LC_MESSAGES/django.po rename to commentary/locale/ta/LC_MESSAGES/django.po diff --git a/django_comments/locale/te/LC_MESSAGES/django.mo b/commentary/locale/te/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/te/LC_MESSAGES/django.mo rename to commentary/locale/te/LC_MESSAGES/django.mo diff --git a/django_comments/locale/te/LC_MESSAGES/django.po b/commentary/locale/te/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/te/LC_MESSAGES/django.po rename to commentary/locale/te/LC_MESSAGES/django.po diff --git a/django_comments/locale/th/LC_MESSAGES/django.mo b/commentary/locale/th/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/th/LC_MESSAGES/django.mo rename to commentary/locale/th/LC_MESSAGES/django.mo diff --git a/django_comments/locale/th/LC_MESSAGES/django.po b/commentary/locale/th/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/th/LC_MESSAGES/django.po rename to commentary/locale/th/LC_MESSAGES/django.po diff --git a/django_comments/locale/tr/LC_MESSAGES/django.mo b/commentary/locale/tr/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/tr/LC_MESSAGES/django.mo rename to commentary/locale/tr/LC_MESSAGES/django.mo diff --git a/django_comments/locale/tr/LC_MESSAGES/django.po b/commentary/locale/tr/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/tr/LC_MESSAGES/django.po rename to commentary/locale/tr/LC_MESSAGES/django.po diff --git a/django_comments/locale/tt/LC_MESSAGES/django.mo b/commentary/locale/tt/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/tt/LC_MESSAGES/django.mo rename to commentary/locale/tt/LC_MESSAGES/django.mo diff --git a/django_comments/locale/tt/LC_MESSAGES/django.po b/commentary/locale/tt/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/tt/LC_MESSAGES/django.po rename to commentary/locale/tt/LC_MESSAGES/django.po diff --git a/django_comments/locale/uk/LC_MESSAGES/django.mo b/commentary/locale/uk/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/uk/LC_MESSAGES/django.mo rename to commentary/locale/uk/LC_MESSAGES/django.mo diff --git a/django_comments/locale/uk/LC_MESSAGES/django.po b/commentary/locale/uk/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/uk/LC_MESSAGES/django.po rename to commentary/locale/uk/LC_MESSAGES/django.po diff --git a/django_comments/locale/ur/LC_MESSAGES/django.mo b/commentary/locale/ur/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/ur/LC_MESSAGES/django.mo rename to commentary/locale/ur/LC_MESSAGES/django.mo diff --git a/django_comments/locale/ur/LC_MESSAGES/django.po b/commentary/locale/ur/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/ur/LC_MESSAGES/django.po rename to commentary/locale/ur/LC_MESSAGES/django.po diff --git a/django_comments/locale/vi/LC_MESSAGES/django.mo b/commentary/locale/vi/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/vi/LC_MESSAGES/django.mo rename to commentary/locale/vi/LC_MESSAGES/django.mo diff --git a/django_comments/locale/vi/LC_MESSAGES/django.po b/commentary/locale/vi/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/vi/LC_MESSAGES/django.po rename to commentary/locale/vi/LC_MESSAGES/django.po diff --git a/django_comments/locale/zh_CN/LC_MESSAGES/django.mo b/commentary/locale/zh_CN/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/zh_CN/LC_MESSAGES/django.mo rename to commentary/locale/zh_CN/LC_MESSAGES/django.mo diff --git a/django_comments/locale/zh_CN/LC_MESSAGES/django.po b/commentary/locale/zh_CN/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/zh_CN/LC_MESSAGES/django.po rename to commentary/locale/zh_CN/LC_MESSAGES/django.po diff --git a/django_comments/locale/zh_TW/LC_MESSAGES/django.mo b/commentary/locale/zh_TW/LC_MESSAGES/django.mo similarity index 100% rename from django_comments/locale/zh_TW/LC_MESSAGES/django.mo rename to commentary/locale/zh_TW/LC_MESSAGES/django.mo diff --git a/django_comments/locale/zh_TW/LC_MESSAGES/django.po b/commentary/locale/zh_TW/LC_MESSAGES/django.po similarity index 100% rename from django_comments/locale/zh_TW/LC_MESSAGES/django.po rename to commentary/locale/zh_TW/LC_MESSAGES/django.po diff --git a/django_comments/managers.py b/commentary/managers.py similarity index 76% rename from django_comments/managers.py rename to commentary/managers.py index 9e1fc77..7e1f704 100644 --- a/django_comments/managers.py +++ b/commentary/managers.py @@ -20,3 +20,11 @@ def for_model(self, model): if isinstance(model, models.Model): qs = qs.filter(object_pk=force_text(model._get_pk_val())) return qs + + def top_level(self, model=None): + """QuerySet for all top level comments.""" + if model is None: + qs = self.get_queryset() + else: + qs = self.for_model(model) + return qs.filter(parent=None) diff --git a/django_comments/migrations/0001_initial.py b/commentary/migrations/0001_initial.py similarity index 96% rename from django_comments/migrations/0001_initial.py rename to commentary/migrations/0001_initial.py index b0a39b5..6cbaae0 100644 --- a/django_comments/migrations/0001_initial.py +++ b/commentary/migrations/0001_initial.py @@ -42,7 +42,7 @@ class Migration(migrations.Migration): ], options={ 'ordering': ('submit_date',), - 'db_table': 'django_comments', + 'db_table': 'commentary', 'verbose_name': 'comment', 'verbose_name_plural': 'comments', 'permissions': [('can_moderate', 'Can moderate comments')], @@ -56,7 +56,7 @@ class Migration(migrations.Migration): ('flag', models.CharField(max_length=30, verbose_name='flag', db_index=True)), ('flag_date', models.DateTimeField(default=None, verbose_name='date')), ('comment', models.ForeignKey(related_name='flags', verbose_name='comment', - to='django_comments.Comment', on_delete=models.CASCADE)), + to='commentary.Comment', on_delete=models.CASCADE)), ('user', models.ForeignKey(related_name='comment_flags', verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], diff --git a/django_comments/migrations/0002_update_user_email_field_length.py b/commentary/migrations/0002_update_user_email_field_length.py similarity index 90% rename from django_comments/migrations/0002_update_user_email_field_length.py rename to commentary/migrations/0002_update_user_email_field_length.py index 5e424e5..d456879 100644 --- a/django_comments/migrations/0002_update_user_email_field_length.py +++ b/commentary/migrations/0002_update_user_email_field_length.py @@ -7,7 +7,7 @@ class Migration(migrations.Migration): dependencies = [ - ('django_comments', '0001_initial'), + ('commentary', '0001_initial'), ] operations = [ diff --git a/django_comments/migrations/0003_add_submit_date_index.py b/commentary/migrations/0003_add_submit_date_index.py similarity index 86% rename from django_comments/migrations/0003_add_submit_date_index.py rename to commentary/migrations/0003_add_submit_date_index.py index 4a5dadf..dadc29b 100644 --- a/django_comments/migrations/0003_add_submit_date_index.py +++ b/commentary/migrations/0003_add_submit_date_index.py @@ -7,7 +7,7 @@ class Migration(migrations.Migration): dependencies = [ - ('django_comments', '0002_update_user_email_field_length'), + ('commentary', '0002_update_user_email_field_length'), ] operations = [ diff --git a/commentary/migrations/0004_commentary.py b/commentary/migrations/0004_commentary.py new file mode 100644 index 0000000..a281733 --- /dev/null +++ b/commentary/migrations/0004_commentary.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('commentary', '0003_add_submit_date_index'), + ] + + operations = [ + migrations.AlterModelOptions( + name='comment', + options={ + 'ordering': ('submit_date',), + 'permissions': ( + ('can_moderate', 'Can moderate comments'), + ), + 'verbose_name': 'comment', 'verbose_name_plural': 'comments' + }, + ), + migrations.RenameField( + model_name='comment', old_name='comment', new_name='body' + ), + migrations.AlterField( + model_name='comment', name='body', + field=models.TextField(db_column='comment', verbose_name='comment'), + ), + migrations.AlterField( + model_name='comment', name='object_pk', + field=models.CharField(max_length=255, verbose_name='object ID'), + ), + migrations.AlterField( + model_name='comment', name='submit_date', + field=models.DateTimeField( + auto_now_add=True, db_index=True, + verbose_name='date/time submitted' + ), + ), + migrations.AddField( + model_name='comment', name='edit_date', + field=models.DateTimeField( + auto_now=True, db_index=True, + verbose_name='date/time of last edit' + ), + ), + migrations.AddField( + model_name='comment', name='parent', + field=models.ForeignKey( + blank=True, null=True, on_delete=models.deletion.CASCADE, + related_name='replies', to='commentary.Comment' + ), + ), + migrations.RemoveField(model_name='comment', name='ip_address'), + migrations.RemoveField(model_name='comment', name='user_email'), + migrations.RemoveField(model_name='comment', name='user_name'), + migrations.RemoveField(model_name='comment', name='user_url'), + migrations.AlterIndexTogether( + name='comment', index_together={('content_type', 'object_pk')}, + ), + migrations.AlterField( + model_name='commentflag', + name='flag_date', + field=models.DateTimeField(auto_now=True, verbose_name='date'), + ), + ] diff --git a/django_comments/migrations/__init__.py b/commentary/migrations/__init__.py similarity index 100% rename from django_comments/migrations/__init__.py rename to commentary/migrations/__init__.py diff --git a/django_comments/models.py b/commentary/models.py similarity index 51% rename from django_comments/models.py rename to commentary/models.py index ebf1bc5..c6739b7 100644 --- a/django_comments/models.py +++ b/commentary/models.py @@ -1,26 +1,35 @@ from django.conf import settings from django.db import models -from django.utils import timezone from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ -from .abstracts import ( - COMMENT_MAX_LENGTH, BaseCommentAbstractModel, CommentAbstractModel, -) +from . import get_user_display +from .abstracts import BaseCommentAbstractModel, CommentAbstractModel class Comment(CommentAbstractModel): + def is_removable_by(self, user): + return user == self.user or \ + user.perms.has_perm('commentary.can_moderate') + + def is_editable_by(self, user): + return user == self.user + class Meta(CommentAbstractModel.Meta): - db_table = "django_comments" + db_table = 'commentary' + index_together = ( + ('content_type', 'object_pk') + ) @python_2_unicode_compatible class CommentFlag(models.Model): """ - Records a flag on a comment. This is intentionally flexible; right now, a - flag could be: + Records a flag on a comment. This is intentionally flexible; + right now, a flag could be: - * A "removal suggestion" -- where a user suggests a comment for (potential) removal. + * A "removal suggestion" -- where a user suggests a comment + for (potential) removal. * A "moderator deletion" -- used when a moderator deletes a comment. @@ -29,34 +38,32 @@ class CommentFlag(models.Model): if you want rating look elsewhere. """ user = models.ForeignKey( - settings.AUTH_USER_MODEL, verbose_name=_('user'), related_name="comment_flags", - on_delete=models.CASCADE, + settings.AUTH_USER_MODEL, verbose_name=_('user'), + related_name='comment_flags', on_delete=models.CASCADE, ) comment = models.ForeignKey( # Translators: 'comment' is a noun here. - Comment, verbose_name=_('comment'), related_name="flags", on_delete=models.CASCADE, + Comment, verbose_name=_('comment'), + related_name='flags', on_delete=models.CASCADE, ) # Translators: 'flag' is a noun here. flag = models.CharField(_('flag'), max_length=30, db_index=True) - flag_date = models.DateTimeField(_('date'), default=None) + flag_date = models.DateTimeField(_('date'), auto_now=True) # Constants for flag types - SUGGEST_REMOVAL = "removal suggestion" - MODERATOR_DELETION = "moderator deletion" - MODERATOR_APPROVAL = "moderator approval" + SUGGEST_REMOVAL = 'removal suggestion' + MODERATOR_DELETION = 'moderator deletion' + MODERATOR_APPROVAL = 'moderator approval' class Meta: db_table = 'django_comment_flags' - unique_together = [('user', 'comment', 'flag')] + unique_together = ( + ('user', 'comment', 'flag'), + ) verbose_name = _('comment flag') verbose_name_plural = _('comment flags') def __str__(self): - return "%s flag of comment ID %s by %s" % ( - self.flag, self.comment_id, self.user.get_username() + return '%s flag of comment ID %s by %s' % ( + self.flag, self.comment_id, get_user_display(self.user) ) - - def save(self, *args, **kwargs): - if self.flag_date is None: - self.flag_date = timezone.now() - super(CommentFlag, self).save(*args, **kwargs) diff --git a/django_comments/moderation.py b/commentary/moderation.py similarity index 88% rename from django_comments/moderation.py rename to commentary/moderation.py index 3e5c412..cfea086 100644 --- a/django_comments/moderation.py +++ b/commentary/moderation.py @@ -29,7 +29,7 @@ class Entry(models.Model): Then we create a ``CommentModerator`` subclass specifying some moderation options:: - from django_comments.moderation import CommentModerator, moderator + from commentary.moderation import CommentModerator, moderator class EntryModerator(CommentModerator): email_notification = True @@ -54,7 +54,7 @@ class EntryModerator(CommentModerator): """ -import datetime +from datetime import date from django.conf import settings from django.contrib.sites.shortcuts import get_current_site @@ -64,8 +64,7 @@ class EntryModerator(CommentModerator): from django.utils import timezone from django.utils.translation import ugettext as _ -import django_comments -from django_comments import signals +from . import get_model, signals class AlreadyModerated(Exception): @@ -193,10 +192,13 @@ def _get_delta(self, now, then): """ 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) + now = date(now.year, now.month, now.day) + then = 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") + 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, request): @@ -213,9 +215,9 @@ def allow(self, comment, content_object, request): return False if self.auto_close_field and self.close_after is not None: close_after_date = getattr(content_object, self.auto_close_field) - if close_after_date is not None and self._get_delta(timezone.now(), - close_after_date).days >= self.close_after: - return False + days_delta = self._get_delta(timezone.now(), close_after_date).days + if close_after_date is not None: + return days_delta <= self.close_after return True def moderate(self, comment, content_object, request): @@ -229,10 +231,10 @@ def moderate(self, comment, content_object, request): """ if self.auto_moderate_field and self.moderate_after is not None: - moderate_after_date = getattr(content_object, self.auto_moderate_field) - if moderate_after_date is not None and self._get_delta(timezone.now(), - moderate_after_date).days >= self.moderate_after: - return True + mod_after_date = getattr(content_object, self.auto_moderate_field) + days_delta = self._get_delta(timezone.now(), mod_after_date).days + if mod_after_date is not None: + return days_delta >= self.moderate_after return False def email(self, comment, content_object, request): @@ -243,18 +245,20 @@ def email(self, comment, content_object, request): """ if not self.email_notification: return - recipient_list = [manager_tuple[1] for manager_tuple in settings.MANAGERS] + recipient_list = [manager[1] for manager in settings.MANAGERS] t = loader.get_template('comments/comment_notification_email.txt') - c = { - 'comment': comment, - 'content_object': content_object, - } subject = _('[%(site)s] New comment posted on "%(object)s"') % { 'site': get_current_site(request).name, 'object': content_object, } - message = t.render(c) - send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, recipient_list, fail_silently=True) + message = t.render({ + 'comment': comment, + 'content_object': content_object, + }) + send_mail( + subject, message, settings.DEFAULT_FROM_EMAIL, + recipient_list, fail_silently=True + ) class Moderator(object): @@ -288,7 +292,6 @@ class Moderator(object): around, will send any notification emails the comment generated. """ - def __init__(self): self._registry = {} self.connect() @@ -299,8 +302,12 @@ def connect(self): from the comment models. """ - signals.comment_will_be_posted.connect(self.pre_save_moderation, sender=django_comments.get_model()) - signals.comment_was_posted.connect(self.post_save_moderation, sender=django_comments.get_model()) + signals.comment_will_be_posted.connect( + self.pre_save_moderation, sender=get_model() + ) + signals.comment_was_posted.connect( + self.post_save_moderation, sender=get_model() + ) def register(self, model_or_iterable, moderation_class): """ @@ -315,7 +322,8 @@ def register(self, model_or_iterable, moderation_class): 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.model_name) + err = "The model '%s' is already being moderated" + raise AlreadyModerated(err % model._meta.model_name) self._registry[model] = moderation_class(model) def unregister(self, model_or_iterable): @@ -331,7 +339,8 @@ def unregister(self, model_or_iterable): 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.model_name) + err = "The model '%s' is not currently being moderated" + raise NotModerated(err % model._meta.model_name) del self._registry[model] def pre_save_moderation(self, sender, comment, request, **kwargs): @@ -345,11 +354,9 @@ def pre_save_moderation(self, sender, comment, request, **kwargs): return content_object = comment.content_object moderation_class = self._registry[model] - # Comment will be disallowed outright (HTTP 403 response) if not moderation_class.allow(comment, content_object, request): return False - if moderation_class.moderate(comment, content_object, request): comment.is_public = False @@ -364,6 +371,7 @@ def post_save_moderation(self, sender, comment, request, **kwargs): return self._registry[model].email(comment, comment.content_object, request) + # Import this instance in your own code to use in registering # your models for moderation. moderator = Moderator() diff --git a/django_comments/signals.py b/commentary/signals.py similarity index 69% rename from django_comments/signals.py rename to commentary/signals.py index 079afaf..592d515 100644 --- a/django_comments/signals.py +++ b/commentary/signals.py @@ -5,17 +5,23 @@ # Sent just before a comment will be posted (after it's been approved and # moderated; this can be used to modify the comment (in place) with posting -# details or other such actions. If any receiver returns False the comment will be -# discarded and a 400 response. This signal is sent at more or less +# details or other such actions. If any receiver returns False the comment will +# be discarded and a 400 response. This signal is sent at more or less # the same time (just before, actually) as the Comment object's pre-save signal, # except that the HTTP request is sent along with this signal. -comment_will_be_posted = Signal(providing_args=["comment", "request"]) +comment_will_be_posted = Signal( + providing_args=('comment', 'request') +) # Sent just after a comment was posted. See above for how this differs # from the Comment object's post-save signal. -comment_was_posted = Signal(providing_args=["comment", "request"]) +comment_was_posted = Signal( + providing_args=('comment', 'request') +) # Sent after a comment was "flagged" in some way. Check the flag to see if this # was a user requesting removal of a comment, a moderator approving/removing a # comment, or some other custom user flag. -comment_was_flagged = Signal(providing_args=["comment", "flag", "created", "request"]) +comment_was_flagged = Signal( + providing_args=('comment', 'flag', 'created', 'request') +) diff --git a/commentary/templates/comments/400-debug.html b/commentary/templates/comments/400-debug.html new file mode 100644 index 0000000..b3cb67f --- /dev/null +++ b/commentary/templates/comments/400-debug.html @@ -0,0 +1,108 @@ + + + + + Comment post not allowed (400) + + + + +
+

Comment post not allowed (400)

+ + + + + +
Why:{{ why }}
+
+
+

+ The comment you tried to post to this view wasn't saved because something + tampered with the security information in the comment form. The message + above should explain the problem, or you can check the + comment documentation + for more help. +

+
+
+

+ You're seeing this error because you have DEBUG = True in + your Django settings file. Change that to False, and Django + will display a standard 400 error page. +

+
+ + diff --git a/django_comments/templates/comments/approve.html b/commentary/templates/comments/approve.html similarity index 64% rename from django_comments/templates/comments/approve.html rename to commentary/templates/comments/approve.html index 85b2ba1..604a7fa 100644 --- a/django_comments/templates/comments/approve.html +++ b/commentary/templates/comments/approve.html @@ -1,16 +1,15 @@ {% extends "comments/base.html" %} {% load i18n %} - {% block title %}{% trans "Approve a comment" %}{% endblock %} - {% block content %}

{% trans "Really make this comment public?" %}

-
{{ comment|linebreaks }}
+
{{ comment }}
{% csrf_token %} {% if next %} -
{% endif %} + + {% endif %}

- or cancel + or cancel

{% endblock %} diff --git a/commentary/templates/comments/base.html b/commentary/templates/comments/base.html new file mode 100644 index 0000000..952408e --- /dev/null +++ b/commentary/templates/comments/base.html @@ -0,0 +1,10 @@ + + + + + {% block title %}{% endblock %} + + + {% block content %}{% endblock %} + + diff --git a/django_comments/templates/comments/delete.html b/commentary/templates/comments/delete.html similarity index 73% rename from django_comments/templates/comments/delete.html rename to commentary/templates/comments/delete.html index 042442c..63a24d5 100644 --- a/django_comments/templates/comments/delete.html +++ b/commentary/templates/comments/delete.html @@ -1,16 +1,15 @@ {% extends "comments/base.html" %} {% load i18n %} - {% block title %}{% trans "Remove a comment" %}{% endblock %} - {% block content %}

{% trans "Really remove this comment?" %}

{{ comment|linebreaks }}
{% csrf_token %} {% if next %} -
{% endif %} + + {% endif %}

- or cancel + or cancel

{% endblock %} diff --git a/django_comments/templates/comments/flag.html b/commentary/templates/comments/flag.html similarity index 71% rename from django_comments/templates/comments/flag.html rename to commentary/templates/comments/flag.html index c5fe743..e818060 100644 --- a/django_comments/templates/comments/flag.html +++ b/commentary/templates/comments/flag.html @@ -1,17 +1,15 @@ {% extends "comments/base.html" %} {% load i18n %} - {% block title %}{% trans "Flag this comment" %}{% endblock %} - {% block content %}

{% trans "Really flag this comment?" %}

{{ comment|linebreaks }}
{% csrf_token %} {% if next %} -
{% endif %} + + {% endif %}

- or cancel + or cancel

{% endblock %} diff --git a/django_comments/templates/comments/form.html b/commentary/templates/comments/form.html similarity index 60% rename from django_comments/templates/comments/form.html rename to commentary/templates/comments/form.html index 858d5eb..a3f9877 100644 --- a/django_comments/templates/comments/form.html +++ b/commentary/templates/comments/form.html @@ -1,21 +1,21 @@ {% load comments i18n %}
{% csrf_token %} {% if next %} -
{% endif %} + + {% endif %} {% for field in form %} {% if field.is_hidden %} -
{{ field }}
+ {{ field }} {% else %} {% if field.errors %}{{ field.errors }}{% endif %} - {% endif %} {% endfor %}

- - + +

diff --git a/django_comments/templates/comments/list.html b/commentary/templates/comments/list.html similarity index 52% rename from django_comments/templates/comments/list.html rename to commentary/templates/comments/list.html index 1246adf..24679df 100644 --- a/django_comments/templates/comments/list.html +++ b/commentary/templates/comments/list.html @@ -1,10 +1,11 @@ +{% load comments %}
{% for comment in comment_list %}
- {{ comment.submit_date }} - {{ comment.name }} + {{ comment.submit_date }} - {{ comment.user_display }}
-

{{ comment.comment }}

+

{{ comment.body|safe_comment }}

{% endfor %}
diff --git a/django_comments/templatetags/__init__.py b/commentary/templatetags/__init__.py similarity index 100% rename from django_comments/templatetags/__init__.py rename to commentary/templatetags/__init__.py diff --git a/django_comments/templatetags/comments.py b/commentary/templatetags/comments.py similarity index 79% rename from django_comments/templatetags/comments.py rename to commentary/templatetags/comments.py index 9b2d1a4..80b5048 100644 --- a/django_comments/templatetags/comments.py +++ b/commentary/templatetags/comments.py @@ -4,8 +4,9 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.sites.shortcuts import get_current_site from django.utils.encoding import smart_text +from django.utils.html import linebreaks, mark_safe -import django_comments +import commentary register = template.Library() @@ -19,15 +20,21 @@ class BaseCommentNode(template.Node): @classmethod def handle_token(cls, parser, token): - """Class method to parse get_comment_list/count/form and return a Node.""" + """ + Class method to parse get_comment_list/count/form and return a Node. + """ tokens = token.split_contents() if tokens[1] != 'for': - raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0]) + raise template.TemplateSyntaxError( + "Second argument in %r tag must be 'for'" % tokens[0] + ) # {% get_whatever for obj as varname %} if len(tokens) == 5: if tokens[3] != 'as': - raise template.TemplateSyntaxError("Third argument in %r must be 'as'" % tokens[0]) + raise template.TemplateSyntaxError( + "Third argument in %r must be 'as'" % tokens[0] + ) return cls( object_expr=parser.compile_filter(tokens[2]), as_varname=tokens[4], @@ -36,7 +43,9 @@ def handle_token(cls, parser, token): # {% get_whatever for app.model pk as varname %} elif len(tokens) == 6: if tokens[4] != 'as': - raise template.TemplateSyntaxError("Fourth argument in %r must be 'as'" % tokens[0]) + raise template.TemplateSyntaxError( + "Fourth argument in %r must be 'as'" % tokens[0] + ) return cls( ctype=BaseCommentNode.lookup_content_type(tokens[2], tokens[0]), object_pk_expr=parser.compile_filter(tokens[3]), @@ -44,7 +53,9 @@ def handle_token(cls, parser, token): ) else: - raise template.TemplateSyntaxError("%r tag requires 4 or 5 arguments" % tokens[0]) + raise template.TemplateSyntaxError( + "%r tag requires 4 or 5 arguments" % tokens[0] + ) @staticmethod def lookup_content_type(token, tagname): @@ -52,15 +63,24 @@ def lookup_content_type(token, tagname): app, model = token.split('.') return ContentType.objects.get_by_natural_key(app, model) except ValueError: - raise template.TemplateSyntaxError("Third argument in %r must be in the format 'app.model'" % tagname) + raise template.TemplateSyntaxError( + "Third argument in %r must be in " + "the format 'app.model'" % tagname + ) except ContentType.DoesNotExist: - raise template.TemplateSyntaxError("%r tag has non-existant content-type: '%s.%s'" % (tagname, app, model)) + raise template.TemplateSyntaxError( + "%r tag has non-existant content-type:" + " '%s.%s'" % (tagname, app, model) + ) - def __init__(self, ctype=None, object_pk_expr=None, object_expr=None, as_varname=None, comment=None): + def __init__(self, ctype=None, object_pk_expr=None, + object_expr=None, as_varname=None, comment=None): if ctype is None and object_expr is None: raise template.TemplateSyntaxError( - "Comment nodes must be given either a literal object or a ctype and object pk.") - self.comment_model = django_comments.get_model() + 'Comment nodes must be given either a ' + 'literal object or a ctype and object pk.' + ) + self.comment_model = commentary.get_model() self.as_varname = as_varname self.ctype = ctype self.object_pk_expr = object_pk_expr @@ -69,7 +89,8 @@ def __init__(self, ctype=None, object_pk_expr=None, object_expr=None, as_varname def render(self, context): qs = self.get_queryset(context) - context[self.as_varname] = self.get_context_value_from_queryset(context, qs) + context[self.as_varname] = \ + self.get_context_value_from_queryset(context, qs) return '' def get_queryset(self, context): @@ -79,7 +100,7 @@ def get_queryset(self, context): # Explicit SITE_ID takes precedence over request. This is also how # get_current_site operates. - site_id = getattr(settings, "SITE_ID", None) + site_id = getattr(settings, 'SITE_ID', None) if not site_id and ('request' in context): site_id = get_current_site(context['request']).pk @@ -96,7 +117,7 @@ def get_queryset(self, context): field_names = [f.name for f in self.comment_model._meta.fields] if 'is_public' in field_names: qs = qs.filter(is_public=True) - if getattr(settings, 'COMMENTS_HIDE_REMOVED', True) and 'is_removed' in field_names: + if commentary.COMMENTS_HIDE_REMOVED and 'is_removed' in field_names: qs = qs.filter(is_removed=False) if 'user' in field_names: qs = qs.select_related('user') @@ -110,7 +131,9 @@ def get_target_ctype_pk(self, context): return None, None return ContentType.objects.get_for_model(obj), obj.pk else: - return self.ctype, self.object_pk_expr.resolve(context, ignore_failures=True) + return self.ctype, self.object_pk_expr.resolve( + context, ignore_failures=True + ) def get_context_value_from_queryset(self, context, qs): """Subclasses should override this.""" @@ -137,7 +160,7 @@ class CommentFormNode(BaseCommentNode): def get_form(self, context): obj = self.get_object(context) if obj: - return django_comments.get_form()(obj) + return commentary.get_form()(obj) else: return None @@ -148,8 +171,9 @@ def get_object(self, context): except template.VariableDoesNotExist: return None else: - object_pk = self.object_pk_expr.resolve(context, - ignore_failures=True) + object_pk = self.object_pk_expr.resolve( + context, ignore_failures=True + ) return self.ctype.get_object_for_this_type(pk=object_pk) def render(self, context): @@ -165,7 +189,9 @@ def handle_token(cls, parser, token): """Class method to parse render_comment_form and return a Node.""" tokens = token.split_contents() if tokens[1] != 'for': - raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0]) + raise template.TemplateSyntaxError( + "Second argument in %r tag must be 'for'" % tokens[0] + ) # {% render_comment_form for obj %} if len(tokens) == 3: @@ -202,7 +228,9 @@ def handle_token(cls, parser, token): """Class method to parse render_comment_list and return a Node.""" tokens = token.split_contents() if tokens[1] != 'for': - raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0]) + raise template.TemplateSyntaxError( + "Second argument in %r tag must be 'for'" % tokens[0] + ) # {% render_comment_list for obj %} if len(tokens) == 3: @@ -225,7 +253,8 @@ def render(self, context): ] qs = self.get_queryset(context) context_dict = context.flatten() - context_dict['comment_list'] = self.get_context_value_from_queryset(context, qs) + context_dict['comment_list'] = \ + self.get_context_value_from_queryset(context, qs) liststr = render_to_string(template_search_list, context_dict) return liststr else: @@ -316,8 +345,8 @@ def get_comment_form(parser, token): @register.tag def render_comment_form(parser, token): """ - Render the comment form (as returned by ``{% render_comment_form %}``) through - the ``comments/form.html`` template. + Render the comment form (as returned by ``{% render_comment_form %}``) + through the ``comments/form.html`` template. Syntax:: @@ -336,7 +365,7 @@ def comment_form_target():
""" - return django_comments.get_form_target() + return commentary.get_form_target() @register.simple_tag @@ -352,3 +381,15 @@ def get_comment_permalink(comment, anchor_pattern=None): if anchor_pattern: return comment.get_absolute_url(anchor_pattern) return comment.get_absolute_url() + + +@register.filter +def safe_comment(comment): + """ + If ``COMMENTS_ALLOW_HTML`` is True, mark the comment as safe. + Otherwise, strip any HTML tags. + """ + if commentary.COMMENTS_ALLOW_HTML: + return mark_safe(linebreaks(comment)) + else: + return linebreaks(comment, autoescape=True) diff --git a/django_comments/urls.py b/commentary/urls.py similarity index 50% rename from django_comments/urls.py rename to commentary/urls.py index 45599dc..d50ff48 100644 --- a/django_comments/urls.py +++ b/commentary/urls.py @@ -1,21 +1,14 @@ from django.conf.urls import url from django.contrib.contenttypes.views import shortcut -from .views.comments import post_comment, comment_done -from .views.moderation import ( - flag, flag_done, delete, delete_done, approve, approve_done, -) +from .views.comments import post_comment +from .views.moderation import flag, delete, approve urlpatterns = [ url(r'^post/$', post_comment, name='comments-post-comment'), - url(r'^posted/$', comment_done, name='comments-comment-done'), url(r'^flag/(\d+)/$', flag, name='comments-flag'), - url(r'^flagged/$', flag_done, name='comments-flag-done'), url(r'^delete/(\d+)/$', delete, name='comments-delete'), - url(r'^deleted/$', delete_done, name='comments-delete-done'), url(r'^approve/(\d+)/$', approve, name='comments-approve'), - url(r'^approved/$', approve_done, name='comments-approve-done'), - url(r'^cr/(\d+)/(.+)/$', shortcut, name='comments-url-redirect'), ] diff --git a/django_comments/views/__init__.py b/commentary/views/__init__.py similarity index 100% rename from django_comments/views/__init__.py rename to commentary/views/__init__.py diff --git a/commentary/views/comments.py b/commentary/views/comments.py new file mode 100644 index 0000000..e770b10 --- /dev/null +++ b/commentary/views/comments.py @@ -0,0 +1,113 @@ +from __future__ import absolute_import + +from django import http +from django.apps import apps +from django.conf import settings +from django.contrib.auth.decorators import login_required +from django.contrib.sites.shortcuts import get_current_site +from django.contrib.messages import error +from django.core.exceptions import ObjectDoesNotExist, ValidationError +from django.shortcuts import render +from django.template.loader import render_to_string +from django.utils.html import escape +from django.views.decorators.cache import patch_cache_control +from django.views.decorators.csrf import csrf_protect +from django.views.decorators.http import require_POST + +from commentary import get_form, get_user_display, signals + + +class CommentPostBadRequest(http.HttpResponseBadRequest): + """ + Response returned when a comment post is invalid. If ``DEBUG`` is on a + nice-ish error message will be displayed (for debugging purposes), but in + production mode a simple opaque 400 page will be displayed. + """ + def __init__(self, why): + super(CommentPostBadRequest, self).__init__() + if settings.DEBUG: + self.content = render_to_string( + 'comments/400-debug.html', {'why': why} + ) + + +@csrf_protect +@require_POST +@login_required +def post_comment(request, next=None, using=None): + """Post a comment. HTTP POST is required.""" + # Look up the object we're trying to comment about + ctype = request.POST.get('content_type') + object_pk = request.POST.get('object_pk') + if ctype is None or object_pk is None: + return CommentPostBadRequest('Missing content_type or object_pk field.') + try: + model = apps.get_model(*ctype.split('.', 1)) + target = model._default_manager.using(using).get(pk=object_pk) + except TypeError: + return CommentPostBadRequest( + 'Invalid content_type value: %r' % escape(ctype) + ) + except AttributeError: + return CommentPostBadRequest( + 'The given content-type %r does not ' + 'resolve to a valid model.' % escape(ctype) + ) + except ObjectDoesNotExist: + return CommentPostBadRequest( + 'No object matching content-type %r and ' + 'object PK %r exists.' % ( + escape(ctype), escape(object_pk) + ) + ) + except (ValueError, ValidationError) as e: + return CommentPostBadRequest( + 'Attempting to get content-type %r ' + 'and object PK %r exists raised %s' % ( + escape(ctype), escape(object_pk), e.__class__.__name__ + ) + ) + + # Construct the comment form + form = get_form()(target, data=request.POST) + + # Check security information + if form.security_errors: + return CommentPostBadRequest( + 'The comment form failed security verification: %s' % escape( + str(form.security_errors) + ) + ) + + if form.errors: + for err in form.errors: + error(request, err, 'comment') + return http.HttpResponseRedirect(target.get_absolute_url()) + + # Create the comment + comment = form.get_comment_object(site_id=get_current_site(request).id) + comment.user = request.user + + # Signal that the comment is about to be saved + responses = signals.comment_will_be_posted.send( + sender=comment.__class__, + comment=comment, + request=request + ) + + for receiver, response in responses: + if response is False: + return CommentPostBadRequest( + 'comment_will_be_posted receiver %r ' + 'killed the comment' % receiver.__name__ + ) + + # Save the comment and signal that it was saved + comment.save() + signals.comment_was_posted.send( + sender=comment.__class__, + comment=comment, + request=request + ) + + return http.HttpResponseRedirect(comment.get_absolute_url()) diff --git a/django_comments/views/moderation.py b/commentary/views/moderation.py similarity index 54% rename from django_comments/views/moderation.py rename to commentary/views/moderation.py index 04c665f..6d58f55 100644 --- a/django_comments/views/moderation.py +++ b/commentary/views/moderation.py @@ -2,12 +2,18 @@ from django.contrib.auth.decorators import login_required, permission_required from django.contrib.sites.shortcuts import get_current_site +from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.views.decorators.csrf import csrf_protect -import django_comments -from django_comments import signals -from django_comments.views.utils import next_redirect, confirmation_view +from commentary import get_model, models, signals + + +def next_redirect(request, next=None): + return HttpResponseRedirect( + next or request.POST.get('next') or + request.META.get('HTTP_REFERER', '/') + ) @csrf_protect @@ -21,23 +27,21 @@ def flag(request, comment_id, next=None): comment the flagged `comments.comment` object """ - comment = get_object_or_404(django_comments.get_model(), - pk=comment_id, - site__pk=get_current_site(request).pk) - + comment = get_object_or_404( + get_model(), pk=comment_id, + site__pk=get_current_site(request).pk + ) # Flag on POST if request.method == 'POST': perform_flag(request, comment) - return next_redirect(request, fallback=next or 'comments-flag-done', - c=comment.pk) - - # Render a form on GET - else: - return render(request, 'comments/flag.html', {'comment': comment, "next": next}) + return next_redirect(request, next) + return render(request, 'comments/flag.html', { + 'comment': comment, 'next': next + }) @csrf_protect -@permission_required("django_comments.can_moderate") +@permission_required('commentary.can_moderate') def delete(request, comment_id, next=None): """ Deletes a comment. Confirmation on GET, action on POST. Requires the "can @@ -48,24 +52,22 @@ def delete(request, comment_id, next=None): comment the flagged `comments.comment` object """ - comment = get_object_or_404(django_comments.get_model(), - pk=comment_id, - site__pk=get_current_site(request).pk) - + comment = get_object_or_404( + get_model(), pk=comment_id, + site__pk=get_current_site(request).pk + ) # Delete on POST if request.method == 'POST': # Flag the comment as deleted instead of actually deleting it. perform_delete(request, comment) - return next_redirect(request, fallback=next or 'comments-delete-done', - c=comment.pk) - - # Render a form on GET - else: - return render(request, 'comments/delete.html', {'comment': comment, "next": next}) + return next_redirect(request, next) + return render(request, 'comments/delete.html', { + 'comment': comment, 'next': next + }) @csrf_protect -@permission_required("django_comments.can_moderate") +@permission_required("commentary.can_moderate") def approve(request, comment_id, next=None): """ Approve a comment (that is, mark it as public and non-removed). Confirmation @@ -76,20 +78,18 @@ def approve(request, comment_id, next=None): comment the `comments.comment` object for approval """ - comment = get_object_or_404(django_comments.get_model(), - pk=comment_id, - site__pk=get_current_site(request).pk) - - # Delete on POST + comment = get_object_or_404( + get_model(), pk=comment_id, + site__pk=get_current_site(request).pk + ) + # Approve on POST if request.method == 'POST': # Flag the comment as approved. perform_approve(request, comment) - return next_redirect(request, fallback=next or 'comments-approve-done', - c=comment.pk) - - # Render a form on GET - else: - return render(request, 'comments/approve.html', {'comment': comment, "next": next}) + return next_redirect(request, next) + return render(request, 'comments/approve.html', { + 'comment': comment, 'next': next + }) # The following functions actually perform the various flag/aprove/delete @@ -100,10 +100,10 @@ def perform_flag(request, comment): """ Actually perform the flagging of a comment from a request. """ - flag, created = django_comments.models.CommentFlag.objects.get_or_create( + flag, created = models.CommentFlag.objects.get_or_create( comment=comment, user=request.user, - flag=django_comments.models.CommentFlag.SUGGEST_REMOVAL + flag=models.CommentFlag.SUGGEST_REMOVAL ) signals.comment_was_flagged.send( sender=comment.__class__, @@ -115,10 +115,10 @@ def perform_flag(request, comment): def perform_delete(request, comment): - flag, created = django_comments.models.CommentFlag.objects.get_or_create( + flag, created = models.CommentFlag.objects.get_or_create( comment=comment, user=request.user, - flag=django_comments.models.CommentFlag.MODERATOR_DELETION + flag=models.CommentFlag.MODERATOR_DELETION ) comment.is_removed = True comment.save() @@ -132,16 +132,14 @@ def perform_delete(request, comment): def perform_approve(request, comment): - flag, created = django_comments.models.CommentFlag.objects.get_or_create( + flag, created = models.CommentFlag.objects.get_or_create( comment=comment, user=request.user, - flag=django_comments.models.CommentFlag.MODERATOR_APPROVAL, + flag=models.CommentFlag.MODERATOR_APPROVAL, ) - comment.is_removed = False comment.is_public = True comment.save() - signals.comment_was_flagged.send( sender=comment.__class__, comment=comment, @@ -149,18 +147,3 @@ def perform_approve(request, comment): created=created, request=request, ) - -# Confirmation views. - -flag_done = confirmation_view( - template="comments/flagged.html", - doc='Displays a "comment was flagged" success page.' -) -delete_done = confirmation_view( - template="comments/deleted.html", - doc='Displays a "comment was deleted" success page.' -) -approve_done = confirmation_view( - template="comments/approved.html", - doc='Displays a "comment was approved" success page.' -) diff --git a/django_comments/__init__.py b/django_comments/__init__.py deleted file mode 100644 index 9eab747..0000000 --- a/django_comments/__init__.py +++ /dev/null @@ -1,101 +0,0 @@ -from importlib import import_module - -from django.apps import apps -from django.conf import settings -from django.core.exceptions import ImproperlyConfigured -from django.urls import reverse - - -DEFAULT_COMMENTS_APP = 'django_comments' - - -def get_comment_app(): - """ - Get the comment app (i.e. "django_comments") as defined in the settings - """ - # Make sure the app's in INSTALLED_APPS - comments_app = get_comment_app_name() - if not apps.is_installed(comments_app): - raise ImproperlyConfigured( - "The COMMENTS_APP (%r) must be in INSTALLED_APPS" % comments_app - ) - - # Try to import the package - try: - package = import_module(comments_app) - except ImportError as e: - raise ImproperlyConfigured( - "The COMMENTS_APP setting refers to a non-existing package. (%s)" % e - ) - - return package - - -def get_comment_app_name(): - """ - Returns the name of the comment app (either the setting value, if it - exists, or the default). - """ - return getattr(settings, 'COMMENTS_APP', DEFAULT_COMMENTS_APP) - - -def get_model(): - """ - Returns the comment model class. - """ - if get_comment_app_name() != DEFAULT_COMMENTS_APP and hasattr(get_comment_app(), "get_model"): - return get_comment_app().get_model() - else: - from django_comments.models import Comment - return Comment - - -def get_form(): - from django_comments.forms import CommentForm - """ - Returns the comment ModelForm class. - """ - if get_comment_app_name() != DEFAULT_COMMENTS_APP and hasattr(get_comment_app(), "get_form"): - return get_comment_app().get_form() - else: - return CommentForm - - -def get_form_target(): - """ - Returns the target URL for the comment form submission view. - """ - if get_comment_app_name() != DEFAULT_COMMENTS_APP and hasattr(get_comment_app(), "get_form_target"): - return get_comment_app().get_form_target() - else: - return reverse("comments-post-comment") - - -def get_flag_url(comment): - """ - Get the URL for the "flag this comment" view. - """ - if get_comment_app_name() != DEFAULT_COMMENTS_APP and hasattr(get_comment_app(), "get_flag_url"): - return get_comment_app().get_flag_url(comment) - else: - return reverse("comments-flag", args=(comment.id,)) - - -def get_delete_url(comment): - """ - Get the URL for the "delete this comment" view. - """ - if get_comment_app_name() != DEFAULT_COMMENTS_APP and hasattr(get_comment_app(), "get_delete_url"): - return get_comment_app().get_delete_url(comment) - else: - return reverse("comments-delete", args=(comment.id,)) - - -def get_approve_url(comment): - """ - Get the URL for the "approve this comment from moderation" view. - """ - if get_comment_app_name() != DEFAULT_COMMENTS_APP and hasattr(get_comment_app(), "get_approve_url"): - return get_comment_app().get_approve_url(comment) - else: - return reverse("comments-approve", args=(comment.id,)) diff --git a/django_comments/abstracts.py b/django_comments/abstracts.py deleted file mode 100644 index afda611..0000000 --- a/django_comments/abstracts.py +++ /dev/null @@ -1,169 +0,0 @@ -from __future__ import unicode_literals - -from django.conf import settings -from django.contrib.contenttypes.fields import GenericForeignKey -from django.contrib.contenttypes.models import ContentType -from django.contrib.sites.models import Site -from django.db import models -from django.urls import reverse -from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext_lazy as _ - -from .managers import CommentManager - -COMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000) - - -class BaseCommentAbstractModel(models.Model): - """ - An abstract base class that any custom comment models probably should - subclass. - """ - - # Content-object field - content_type = models.ForeignKey(ContentType, - verbose_name=_('content type'), - related_name="content_type_set_for_%(class)s", - on_delete=models.CASCADE) - object_pk = models.TextField(_('object ID')) - content_object = GenericForeignKey(ct_field="content_type", fk_field="object_pk") - - # Metadata about the comment - site = models.ForeignKey(Site, on_delete=models.CASCADE) - - class Meta: - abstract = True - - def get_content_object_url(self): - """ - Get a URL suitable for redirecting to the content object. - """ - return reverse( - "comments-url-redirect", - args=(self.content_type_id, self.object_pk) - ) - - -@python_2_unicode_compatible -class CommentAbstractModel(BaseCommentAbstractModel): - """ - A user comment about some object. - """ - - # Who posted this comment? If ``user`` is set then it was an authenticated - # user; otherwise at least user_name should have been set and the comment - # was posted by a non-authenticated user. - user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'), - blank=True, null=True, related_name="%(class)s_comments", - on_delete=models.SET_NULL) - user_name = models.CharField(_("user's name"), max_length=50, blank=True) - user_email = models.EmailField(_("user's email address"), blank=True) - user_url = models.URLField(_("user's URL"), blank=True) - - comment = models.TextField(_('comment'), max_length=COMMENT_MAX_LENGTH) - - # Metadata about the comment - submit_date = models.DateTimeField(_('date/time submitted'), default=None, db_index=True) - ip_address = models.GenericIPAddressField(_('IP address'), unpack_ipv4=True, blank=True, null=True) - is_public = models.BooleanField(_('is public'), default=True, - help_text=_('Uncheck this box to make the comment effectively ' - 'disappear from the site.')) - is_removed = models.BooleanField(_('is removed'), default=False, - help_text=_('Check this box if the comment is inappropriate. ' - 'A "This comment has been removed" message will ' - 'be displayed instead.')) - - # Manager - objects = CommentManager() - - class Meta: - abstract = True - ordering = ('submit_date',) - permissions = [("can_moderate", "Can moderate comments")] - verbose_name = _('comment') - verbose_name_plural = _('comments') - - def __str__(self): - return "%s: %s..." % (self.name, self.comment[:50]) - - def save(self, *args, **kwargs): - if self.submit_date is None: - self.submit_date = timezone.now() - super(CommentAbstractModel, self).save(*args, **kwargs) - - def _get_userinfo(self): - """ - Get a dictionary that pulls together information about the poster - safely for both authenticated and non-authenticated comments. - - This dict will have ``name``, ``email``, and ``url`` fields. - """ - if not hasattr(self, "_userinfo"): - userinfo = { - "name": self.user_name, - "email": self.user_email, - "url": self.user_url - } - if self.user_id: - u = self.user - if u.email: - userinfo["email"] = u.email - - # If the user has a full name, use that for the user name. - # However, a given user_name overrides the raw user.username, - # so only use that if this comment has no associated name. - if u.get_full_name(): - userinfo["name"] = self.user.get_full_name() - elif not self.user_name: - userinfo["name"] = u.get_username() - self._userinfo = userinfo - return self._userinfo - - userinfo = property(_get_userinfo, doc=_get_userinfo.__doc__) - - def _get_name(self): - return self.userinfo["name"] - - def _set_name(self, val): - if self.user_id: - raise AttributeError(_("This comment was posted by an authenticated " - "user and thus the name is read-only.")) - self.user_name = val - - name = property(_get_name, _set_name, doc="The name of the user who posted this comment") - - def _get_email(self): - return self.userinfo["email"] - - def _set_email(self, val): - if self.user_id: - raise AttributeError(_("This comment was posted by an authenticated " - "user and thus the email is read-only.")) - self.user_email = val - - email = property(_get_email, _set_email, doc="The email of the user who posted this comment") - - def _get_url(self): - return self.userinfo["url"] - - def _set_url(self, val): - self.user_url = val - - url = property(_get_url, _set_url, doc="The URL given by the user who posted this comment") - - def get_absolute_url(self, anchor_pattern="#c%(id)s"): - return self.get_content_object_url() + (anchor_pattern % self.__dict__) - - def get_as_text(self): - """ - Return this comment as plain text. Useful for emails. - """ - d = { - 'user': self.user or self.name, - 'date': self.submit_date, - 'comment': self.comment, - 'domain': self.site.domain, - 'url': self.get_absolute_url() - } - return _('Posted by %(user)s at %(date)s\n\n%(comment)s\n\nhttp://%(domain)s%(url)s') % d diff --git a/django_comments/admin.py b/django_comments/admin.py deleted file mode 100644 index d4af2d9..0000000 --- a/django_comments/admin.py +++ /dev/null @@ -1,94 +0,0 @@ -from __future__ import unicode_literals - -from django.contrib import admin -from django.contrib.auth import get_user_model -from django.utils.translation import ugettext_lazy as _, ungettext - -from django_comments import get_model -from django_comments.views.moderation import perform_flag, perform_approve, perform_delete - - -class UsernameSearch(object): - """The User object may not be auth.User, so we need to provide - a mechanism for issuing the equivalent of a .filter(user__username=...) - search in CommentAdmin. - """ - - def __str__(self): - return 'user__%s' % get_user_model().USERNAME_FIELD - - -class CommentsAdmin(admin.ModelAdmin): - fieldsets = ( - ( - None, - {'fields': ('content_type', 'object_pk', 'site')} - ), - ( - _('Content'), - {'fields': ('user', 'user_name', 'user_email', 'user_url', 'comment')} - ), - ( - _('Metadata'), - {'fields': ('submit_date', 'ip_address', 'is_public', 'is_removed')} - ), - ) - - list_display = ('name', 'content_type', 'object_pk', 'ip_address', 'submit_date', 'is_public', 'is_removed') - list_filter = ('submit_date', 'site', 'is_public', 'is_removed') - date_hierarchy = 'submit_date' - ordering = ('-submit_date',) - raw_id_fields = ('user',) - search_fields = ('comment', UsernameSearch(), 'user_name', 'user_email', 'user_url', 'ip_address') - actions = ["flag_comments", "approve_comments", "remove_comments"] - - def get_actions(self, request): - actions = super(CommentsAdmin, self).get_actions(request) - # Only superusers should be able to delete the comments from the DB. - if not request.user.is_superuser and 'delete_selected' in actions: - actions.pop('delete_selected') - if not request.user.has_perm('django_comments.can_moderate'): - if 'approve_comments' in actions: - actions.pop('approve_comments') - if 'remove_comments' in actions: - actions.pop('remove_comments') - return actions - - def flag_comments(self, request, queryset): - self._bulk_flag(request, queryset, perform_flag, - lambda n: ungettext('flagged', 'flagged', n)) - - flag_comments.short_description = _("Flag selected comments") - - def approve_comments(self, request, queryset): - self._bulk_flag(request, queryset, perform_approve, - lambda n: ungettext('approved', 'approved', n)) - - approve_comments.short_description = _("Approve selected comments") - - def remove_comments(self, request, queryset): - self._bulk_flag(request, queryset, perform_delete, - lambda n: ungettext('removed', 'removed', n)) - - remove_comments.short_description = _("Remove selected comments") - - def _bulk_flag(self, request, queryset, action, done_message): - """ - Flag, approve, or remove some comments from an admin action. Actually - calls the `action` argument to perform the heavy lifting. - """ - n_comments = 0 - for comment in queryset: - action(request, comment) - n_comments += 1 - - msg = ungettext('%(count)s comment was successfully %(action)s.', - '%(count)s comments were successfully %(action)s.', - n_comments) - self.message_user(request, msg % {'count': n_comments, 'action': done_message(n_comments)}) - -# Only register the default admin if the model is the built-in comment model -# (this won't be true if there's a custom comment app). -Klass = get_model() -if Klass._meta.app_label == "django_comments": - admin.site.register(Klass, CommentsAdmin) diff --git a/django_comments/compat.py b/django_comments/compat.py deleted file mode 100644 index 7b7d56d..0000000 --- a/django_comments/compat.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Module to store compatiblity imports to prevent Django deprecation warnings. -""" diff --git a/django_comments/forms.py b/django_comments/forms.py deleted file mode 100644 index b3cbae5..0000000 --- a/django_comments/forms.py +++ /dev/null @@ -1,202 +0,0 @@ -import time - -from django import forms -from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from django.forms.utils import ErrorDict -from django.utils.crypto import salted_hmac, constant_time_compare -from django.utils.encoding import force_text -from django.utils.text import get_text_list -from django.utils import timezone -from django.utils.translation import pgettext_lazy, ungettext, ugettext, ugettext_lazy as _ - -from . import get_model - -COMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000) -DEFAULT_COMMENTS_TIMEOUT = getattr(settings, 'COMMENTS_TIMEOUT', (2 * 60 * 60)) # 2h - - -class CommentSecurityForm(forms.Form): - """ - Handles the security aspects (anti-spoofing) for comment forms. - """ - content_type = forms.CharField(widget=forms.HiddenInput) - object_pk = forms.CharField(widget=forms.HiddenInput) - timestamp = forms.IntegerField(widget=forms.HiddenInput) - security_hash = forms.CharField(min_length=40, max_length=40, widget=forms.HiddenInput) - - def __init__(self, target_object, data=None, initial=None, **kwargs): - self.target_object = target_object - if initial is None: - initial = {} - initial.update(self.generate_security_data()) - super(CommentSecurityForm, self).__init__(data=data, initial=initial, **kwargs) - - def security_errors(self): - """Return just those errors associated with security""" - errors = ErrorDict() - for f in ["honeypot", "timestamp", "security_hash"]: - if f in self.errors: - errors[f] = self.errors[f] - return errors - - def clean_security_hash(self): - """Check the security hash.""" - security_hash_dict = { - 'content_type': self.data.get("content_type", ""), - 'object_pk': self.data.get("object_pk", ""), - 'timestamp': self.data.get("timestamp", ""), - } - expected_hash = self.generate_security_hash(**security_hash_dict) - actual_hash = self.cleaned_data["security_hash"] - if not constant_time_compare(expected_hash, actual_hash): - raise forms.ValidationError("Security hash check failed.") - return actual_hash - - def clean_timestamp(self): - """Make sure the timestamp isn't too far (default is > 2 hours) in the past.""" - ts = self.cleaned_data["timestamp"] - if time.time() - ts > DEFAULT_COMMENTS_TIMEOUT: - raise forms.ValidationError("Timestamp check failed") - return ts - - def generate_security_data(self): - """Generate a dict of security data for "initial" data.""" - timestamp = int(time.time()) - security_dict = { - 'content_type': str(self.target_object._meta), - 'object_pk': str(self.target_object._get_pk_val()), - 'timestamp': str(timestamp), - 'security_hash': self.initial_security_hash(timestamp), - } - return security_dict - - def initial_security_hash(self, timestamp): - """ - Generate the initial security hash from self.content_object - and a (unix) timestamp. - """ - - initial_security_dict = { - 'content_type': str(self.target_object._meta), - 'object_pk': str(self.target_object._get_pk_val()), - 'timestamp': str(timestamp), - } - return self.generate_security_hash(**initial_security_dict) - - def generate_security_hash(self, content_type, object_pk, timestamp): - """ - Generate a HMAC security hash from the provided info. - """ - info = (content_type, object_pk, timestamp) - key_salt = "django.contrib.forms.CommentSecurityForm" - value = "-".join(info) - return salted_hmac(key_salt, value).hexdigest() - - -class CommentDetailsForm(CommentSecurityForm): - """ - Handles the specific details of the comment (name, comment, etc.). - """ - name = forms.CharField(label=pgettext_lazy("Person name", "Name"), max_length=50) - email = forms.EmailField(label=_("Email address")) - url = forms.URLField(label=_("URL"), required=False) - # Translators: 'Comment' is a noun here. - comment = forms.CharField(label=_('Comment'), widget=forms.Textarea, - max_length=COMMENT_MAX_LENGTH) - - def get_comment_object(self, site_id=None): - """ - Return a new (unsaved) comment object based on the information in this - form. Assumes that the form is already validated and will throw a - ValueError if not. - - Does not set any of the fields that would come from a Request object - (i.e. ``user`` or ``ip_address``). - """ - if not self.is_valid(): - raise ValueError("get_comment_object may only be called on valid forms") - - CommentModel = self.get_comment_model() - new = CommentModel(**self.get_comment_create_data(site_id=site_id)) - new = self.check_for_duplicate_comment(new) - - return new - - def get_comment_model(self): - """ - Get the comment model to create with this form. Subclasses in custom - comment apps should override this, get_comment_create_data, and perhaps - check_for_duplicate_comment to provide custom comment models. - """ - return get_model() - - def get_comment_create_data(self, site_id=None): - """ - Returns the dict of data to be used to create a comment. Subclasses in - custom comment apps that override get_comment_model can override this - method to add extra fields onto a custom comment model. - """ - return dict( - content_type=ContentType.objects.get_for_model(self.target_object), - object_pk=force_text(self.target_object._get_pk_val()), - user_name=self.cleaned_data["name"], - user_email=self.cleaned_data["email"], - user_url=self.cleaned_data["url"], - comment=self.cleaned_data["comment"], - submit_date=timezone.now(), - site_id=site_id or getattr(settings, "SITE_ID", None), - is_public=True, - is_removed=False, - ) - - def check_for_duplicate_comment(self, new): - """ - Check that a submitted comment isn't a duplicate. This might be caused - by someone posting a comment twice. If it is a dup, silently return the *previous* comment. - """ - possible_duplicates = self.get_comment_model()._default_manager.using( - self.target_object._state.db - ).filter( - content_type=new.content_type, - object_pk=new.object_pk, - user_name=new.user_name, - user_email=new.user_email, - user_url=new.user_url, - ) - for old in possible_duplicates: - if old.submit_date.date() == new.submit_date.date() and old.comment == new.comment: - return old - - return new - - def clean_comment(self): - """ - If COMMENTS_ALLOW_PROFANITIES is False, check that the comment doesn't - contain anything in PROFANITIES_LIST. - """ - comment = self.cleaned_data["comment"] - if (not getattr(settings, 'COMMENTS_ALLOW_PROFANITIES', False) and - getattr(settings, 'PROFANITIES_LIST', False)): - bad_words = [w for w in settings.PROFANITIES_LIST if w in comment.lower()] - if bad_words: - raise forms.ValidationError(ungettext( - "Watch your mouth! The word %s is not allowed here.", - "Watch your mouth! The words %s are not allowed here.", - len(bad_words)) % get_text_list( - ['"%s%s%s"' % (i[0], '-' * (len(i) - 2), i[-1]) - for i in bad_words], ugettext('and'))) - return comment - - -class CommentForm(CommentDetailsForm): - honeypot = forms.CharField(required=False, - label=_('If you enter anything in this field ' - 'your comment will be treated as spam')) - - def clean_honeypot(self): - """Check that nothing's been entered into the honeypot.""" - value = self.cleaned_data["honeypot"] - if value: - raise forms.ValidationError(self.fields["honeypot"].label) - return value diff --git a/django_comments/locale/en/LC_MESSAGES/django.mo b/django_comments/locale/en/LC_MESSAGES/django.mo deleted file mode 100644 index c810b1b..0000000 Binary files a/django_comments/locale/en/LC_MESSAGES/django.mo and /dev/null differ diff --git a/django_comments/templates/comments/400-debug.html b/django_comments/templates/comments/400-debug.html deleted file mode 100644 index af74676..0000000 --- a/django_comments/templates/comments/400-debug.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - Comment post not allowed (400) - - - - -
-

Comment post not allowed (400)

- - - - - -
Why:{{ why }}
-
-
-

- The comment you tried to post to this view wasn't saved because something - tampered with the security information in the comment form. The message - above should explain the problem, or you can check the comment - documentation for more help. -

-
- -
-

- You're seeing this error because you have DEBUG = True in - your Django settings file. Change that to False, and Django - will display a standard 400 error page. -

-
- - diff --git a/django_comments/templates/comments/approved.html b/django_comments/templates/comments/approved.html deleted file mode 100644 index d4ba245..0000000 --- a/django_comments/templates/comments/approved.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "comments/base.html" %} -{% load i18n %} - -{% block title %}{% trans "Thanks for approving" %}.{% endblock %} - -{% block content %} -

{% trans "Thanks for taking the time to improve the quality of discussion on our site" %}.

-{% endblock %} diff --git a/django_comments/templates/comments/base.html b/django_comments/templates/comments/base.html deleted file mode 100644 index 540c25c..0000000 --- a/django_comments/templates/comments/base.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - {% block title %}{% endblock %} - - -{% block content %}{% endblock %} - - diff --git a/django_comments/templates/comments/deleted.html b/django_comments/templates/comments/deleted.html deleted file mode 100644 index e608481..0000000 --- a/django_comments/templates/comments/deleted.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "comments/base.html" %} -{% load i18n %} - -{% block title %}{% trans "Thanks for removing" %}.{% endblock %} - -{% block content %} -

{% trans "Thanks for taking the time to improve the quality of discussion on our site" %}.

-{% endblock %} diff --git a/django_comments/templates/comments/flagged.html b/django_comments/templates/comments/flagged.html deleted file mode 100644 index e558019..0000000 --- a/django_comments/templates/comments/flagged.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "comments/base.html" %} -{% load i18n %} - -{% block title %}{% trans "Thanks for flagging" %}.{% endblock %} - -{% block content %} -

{% trans "Thanks for taking the time to improve the quality of discussion on our site" %}.

-{% endblock %} diff --git a/django_comments/templates/comments/posted.html b/django_comments/templates/comments/posted.html deleted file mode 100644 index 76f7f6d..0000000 --- a/django_comments/templates/comments/posted.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "comments/base.html" %} -{% load i18n %} - -{% block title %}{% trans "Thanks for commenting" %}.{% endblock %} - -{% block content %} -

{% trans "Thank you for your comment" %}.

-{% endblock %} diff --git a/django_comments/templates/comments/preview.html b/django_comments/templates/comments/preview.html deleted file mode 100644 index e335466..0000000 --- a/django_comments/templates/comments/preview.html +++ /dev/null @@ -1,40 +0,0 @@ -{% extends "comments/base.html" %} -{% load i18n %} - -{% block title %}{% trans "Preview your comment" %}{% endblock %} - -{% block content %} - {% load comments %} - {% csrf_token %} - {% if next %} -
{% endif %} - {% if form.errors %} -

{% blocktrans count counter=form.errors|length %}Please correct the error below{% plural %}Please correct the errors below{% endblocktrans %}

- {% else %} -

{% trans "Preview your comment" %}

-
{{ comment|linebreaks }}
-

- {% trans "and" %} - {# Translators: This string follows the 'Post your comment' submit button. #} - {% trans "or make changes" %}: -

- {% endif %} - {% for field in form %} - {% if field.is_hidden %} -
{{ field }}
- {% else %} - {% if field.errors %}{{ field.errors }}{% endif %} - - {% endif %} - {% endfor %} -

- - -

-
-{% endblock %} diff --git a/django_comments/views/comments.py b/django_comments/views/comments.py deleted file mode 100644 index be1c7a0..0000000 --- a/django_comments/views/comments.py +++ /dev/null @@ -1,136 +0,0 @@ -from __future__ import absolute_import - -from django import http -from django.apps import apps -from django.conf import settings -from django.contrib.sites.shortcuts import get_current_site -from django.core.exceptions import ObjectDoesNotExist, ValidationError -from django.shortcuts import render -from django.template.loader import render_to_string -from django.utils.html import escape -from django.views.decorators.csrf import csrf_protect -from django.views.decorators.http import require_POST - -import django_comments -from django_comments import signals -from django_comments.views.utils import next_redirect, confirmation_view - - -class CommentPostBadRequest(http.HttpResponseBadRequest): - """ - Response returned when a comment post is invalid. If ``DEBUG`` is on a - nice-ish error message will be displayed (for debugging purposes), but in - production mode a simple opaque 400 page will be displayed. - """ - - def __init__(self, why): - super(CommentPostBadRequest, self).__init__() - if settings.DEBUG: - self.content = render_to_string("comments/400-debug.html", {"why": why}) - - -@csrf_protect -@require_POST -def post_comment(request, next=None, using=None): - """ - Post a comment. - - HTTP POST is required. If ``POST['submit'] == "preview"`` or if there are - errors a preview template, ``comments/preview.html``, will be rendered. - """ - # Fill out some initial data fields from an authenticated user, if present - data = request.POST.copy() - if request.user.is_authenticated: - if not data.get('name', ''): - data["name"] = request.user.get_full_name() or request.user.get_username() - if not data.get('email', ''): - data["email"] = request.user.email - - # Look up the object we're trying to comment about - ctype = data.get("content_type") - object_pk = data.get("object_pk") - if ctype is None or object_pk is None: - return CommentPostBadRequest("Missing content_type or object_pk field.") - try: - model = apps.get_model(*ctype.split(".", 1)) - target = model._default_manager.using(using).get(pk=object_pk) - except TypeError: - return CommentPostBadRequest( - "Invalid content_type value: %r" % escape(ctype)) - except AttributeError: - return CommentPostBadRequest( - "The given content-type %r does not resolve to a valid model." % escape(ctype)) - except ObjectDoesNotExist: - return CommentPostBadRequest( - "No object matching content-type %r and object PK %r exists." % ( - escape(ctype), escape(object_pk))) - except (ValueError, ValidationError) as e: - return CommentPostBadRequest( - "Attempting go get content-type %r and object PK %r exists raised %s" % ( - escape(ctype), escape(object_pk), e.__class__.__name__)) - - # Do we want to preview the comment? - preview = "preview" in data - - # Construct the comment form - form = django_comments.get_form()(target, data=data) - - # Check security information - if form.security_errors(): - return CommentPostBadRequest( - "The comment form failed security verification: %s" % escape(str(form.security_errors()))) - - # If there are errors or if we requested a preview show the comment - if form.errors or preview: - template_list = [ - # These first two exist for purely historical reasons. - # Django v1.0 and v1.1 allowed the underscore format for - # preview templates, so we have to preserve that format. - "comments/%s_%s_preview.html" % (model._meta.app_label, model._meta.model_name), - "comments/%s_preview.html" % model._meta.app_label, - # Now the usual directory based template hierarchy. - "comments/%s/%s/preview.html" % (model._meta.app_label, model._meta.model_name), - "comments/%s/preview.html" % model._meta.app_label, - "comments/preview.html", - ] - return render(request, template_list, { - "comment": form.data.get("comment", ""), - "form": form, - "next": data.get("next", next), - }, - ) - - # Otherwise create the comment - comment = form.get_comment_object(site_id=get_current_site(request).id) - comment.ip_address = request.META.get("REMOTE_ADDR", None) or None - if request.user.is_authenticated: - comment.user = request.user - - # Signal that the comment is about to be saved - responses = signals.comment_will_be_posted.send( - sender=comment.__class__, - comment=comment, - request=request - ) - - for (receiver, response) in responses: - if response is False: - return CommentPostBadRequest( - "comment_will_be_posted receiver %r killed the comment" % receiver.__name__) - - # Save the comment and signal that it was saved - comment.save() - signals.comment_was_posted.send( - sender=comment.__class__, - comment=comment, - request=request - ) - - return next_redirect(request, fallback=next or 'comments-comment-done', - c=comment._get_pk_val()) - - -comment_done = confirmation_view( - template="comments/posted.html", - doc="""Display a "comment was posted" success page.""" -) diff --git a/django_comments/views/utils.py b/django_comments/views/utils.py deleted file mode 100644 index a5f5c11..0000000 --- a/django_comments/views/utils.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -A few bits of helper functions for comment views. -""" - -import textwrap - -try: - from urllib.parse import urlencode -except ImportError: # Python 2 - from urllib import urlencode - -from django.http import HttpResponseRedirect -from django.shortcuts import render, resolve_url -from django.core.exceptions import ObjectDoesNotExist -from django.utils.http import is_safe_url - -import django_comments - - -def next_redirect(request, fallback, **get_kwargs): - """ - Handle the "where should I go next?" part of comment views. - - The next value could be a - ``?next=...`` GET arg or the URL of a given view (``fallback``). See - the view modules for examples. - - Returns an ``HttpResponseRedirect``. - """ - next = request.POST.get('next') - if not is_safe_url(url=next, allowed_hosts={request.get_host()}): - next = resolve_url(fallback) - - if get_kwargs: - if '#' in next: - tmp = next.rsplit('#', 1) - next = tmp[0] - anchor = '#' + tmp[1] - else: - anchor = '' - - joiner = ('?' in next) and '&' or '?' - next += joiner + urlencode(get_kwargs) + anchor - return HttpResponseRedirect(next) - - -def confirmation_view(template, doc="Display a confirmation view."): - """ - Confirmation view generator for the "comment was - posted/flagged/deleted/approved" views. - """ - - def confirmed(request): - comment = None - if 'c' in request.GET: - try: - comment = django_comments.get_model().objects.get(pk=request.GET['c']) - except (ObjectDoesNotExist, ValueError): - pass - return render(request, template, {'comment': comment}) - - confirmed.__doc__ = textwrap.dedent("""\ - %s - - Templates: :template:`%s`` - Context: - comment - The posted comment - """ % (doc, template) - ) - return confirmed diff --git a/docs/conf.py b/docs/conf.py index ccdaa13..0b79d11 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,6 +14,8 @@ import os import sys +import sphinx_rtd_theme + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -22,7 +24,7 @@ # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. @@ -35,88 +37,88 @@ source_suffix = '.txt' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. -project = u'Django Comments' -copyright = u'2013, Django Software Foundation and contributors' +project = 'Django User Comments' +copyright = '2019, MangAdventure' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '1.7' +version = '2.0' # The full version, including alpha/beta/rc tags. -release = '1.7' +release = '2.0.0.dev2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -125,88 +127,89 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'DjangoCommentsdoc' +htmlhelp_basename = 'DjangoUserCommentsdoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # Additional stuff for the LaTeX preamble. + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('newindex', 'DjangoComments.tex', u'Django Comments Documentation', - u'Django Software Foundation and contributors', 'manual'), + ('newindex', 'DjangoUserComments.tex', + 'Django User Comments Documentation', + 'MangAdventure', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- @@ -214,12 +217,13 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('newindex', 'djangocomments', u'Django Comments Documentation', - [u'Django Software Foundation and contributors'], 1) + ('newindex', 'djangousercomments', + 'Django User Comments Documentation', + ['MangAdventure'], 1) ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ @@ -228,66 +232,70 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('newindex', 'DjangoComments', u'Django Comments Documentation', - u'Django Software Foundation and contributors', 'DjangoComments', 'One line description of project.', - 'Miscellaneous'), + ('newindex', 'DjangoUserComments', + 'Django User Comments Documentation', + 'MangAdventure', 'DjangoUserComments', + 'A fork of django-contrib-comments.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # -- Options for Epub output --------------------------------------------------- # Bibliographic Dublin Core info. -epub_title = u'Django Comments' -epub_author = u'Django Software Foundation and contributors' -epub_publisher = u'Django Software Foundation and contributors' -epub_copyright = u'2013, Django Software Foundation and contributors' +epub_title = 'Django User Comments' +epub_author = 'MangAdventure' +epub_publisher = 'MangAdventure' +epub_copyright = '2019, MangAdventure' # The language of the text. It defaults to the language option # or en if the language is not set. -#epub_language = '' +# epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' +# epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. -#epub_identifier = '' +# epub_identifier = '' # A unique identification for the text. -#epub_uid = '' +# epub_uid = '' # A tuple containing the cover image and cover page html template filenames. -#epub_cover = () +# epub_cover = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. -#epub_pre_files = [] +# epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. -#epub_post_files = [] +# epub_post_files = [] # A list of files that should not be packed into the epub file. -#epub_exclude_files = [] +# epub_exclude_files = [] # The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 +# epub_tocdepth = 3 # Allow duplicate toc entries. -#epub_tocdup = True +# epub_tocdup = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'python': ('http://docs.python.org/', None), - 'django': ('http://docs.djangoproject.com/en/stable', 'http://docs.djangoproject.com/en/stable/_objects'), + 'django': ( + 'http://docs.djangoproject.com/en/stable', + 'http://docs.djangoproject.com/en/stable/_objects' + ), } diff --git a/docs/custom.txt b/docs/custom.txt index 4027d9d..fc42e1d 100644 --- a/docs/custom.txt +++ b/docs/custom.txt @@ -2,7 +2,7 @@ Customizing the comments framework ================================== -.. currentmodule:: django_comments +.. currentmodule:: commentary If the comment framework doesn't quite fit your needs, you can extend the comment app's behavior to add custom data and logic. The comments framework @@ -61,15 +61,15 @@ the ``my_comment_app`` directory:: In the ``models.py`` we'll define a ``CommentWithTitle`` model:: from django.db import models - from django_comments.abstracts import CommentAbstractModel + from commentary.abstracts import CommentAbstractModel class CommentWithTitle(CommentAbstractModel): title = models.CharField(max_length=300) Most custom comment models will subclass the -:class:`~django_comments.abstracts.CommentAbstractModel` model. However, +:class:`~commentary.abstracts.CommentAbstractModel` model. However, if you want to substantially remove or change the fields available in the -:class:`~django_comments.abstracts.CommentAbstractModel` model, but don't want +:class:`~commentary.abstracts.CommentAbstractModel` model, but don't want to rewrite the templates, you could try subclassing from ``BaseCommentAbstractModel``. @@ -79,7 +79,7 @@ tricky: we have to both create a form and override field:: from django import forms - from django_comments.forms import CommentForm + from commentary.forms import CommentForm from my_comment_app.models import CommentWithTitle class CommentFormWithTitle(CommentForm): @@ -92,7 +92,7 @@ field:: return data Django provides a couple of "helper" classes to make writing certain types of -custom comment forms easier; see :mod:`django_comments.forms` for +custom comment forms easier; see :mod:`commentary.forms` for more. Finally, we'll define a couple of methods in ``my_comment_app/__init__.py`` to @@ -129,7 +129,7 @@ explained in the next section. Custom comment app API ====================== -The :mod:`django_comments` app defines the following methods; any +The :mod:`commentary` app defines the following methods; any custom comment app must define at least one of them. All are optional, however. @@ -137,11 +137,11 @@ however. Return the :class:`~django.db.models.Model` class to use for comments. This model should inherit from - ``django_comments.abstracts.BaseCommentAbstractModel``, which + ``commentary.abstracts.BaseCommentAbstractModel``, which defines necessary core fields. The default implementation returns - :class:`django_comments.models.Comment`. + :class:`commentary.models.Comment`. .. function:: get_form() @@ -152,7 +152,7 @@ however. attached to. The default implementation returns - :class:`django_comments.forms.CommentForm`. + :class:`commentary.forms.CommentForm`. .. note:: @@ -176,25 +176,27 @@ however. want to use the default ``post_comment()`` view, you will need to be aware that it requires the model and form to have certain additional attributes and methods: see the - ``django_comments.views.post_comment()`` view for details. + ``commentary.views.post_comment()`` view for details. .. function:: get_flag_url() Return the URL for the "flag this comment" view. The default implementation returns a reverse-resolved URL pointing - to the ``django_comments.views.moderation.flag()`` view. + to the ``commentary.views.moderation.flag()`` view. .. function:: get_delete_url() Return the URL for the "delete this comment" view. The default implementation returns a reverse-resolved URL pointing - to the ``django_comments.views.moderation.delete()`` view. + to the ``commentary.views.moderation.delete()`` view. .. function:: get_approve_url() Return the URL for the "approve this comment from moderation" view. The default implementation returns a reverse-resolved URL pointing - to the ``django_comments.views.moderation.approve()`` view. + to the ``commentary.views.moderation.approve()`` view. + +.. vim:ft=rst: diff --git a/docs/example.txt b/docs/example.txt index 68b1bfc..5d86aa3 100644 --- a/docs/example.txt +++ b/docs/example.txt @@ -141,7 +141,7 @@ enable it in your project's ``urls.py``: .. code-block:: python from django.conf.urls import url - from django_comments.feeds import LatestCommentFeed + from commentary.feeds import LatestCommentFeed urlpatterns = [ # ... @@ -175,7 +175,7 @@ following way: .. code-block:: python from django.db import models - from django_comments.moderation import CommentModerator, moderator + from commentary.moderation import CommentModerator, moderator class Post(models.Model): title = models.CharField(max_length = 255) @@ -203,3 +203,5 @@ The moderator can ``Flag``, ``Approve`` or ``Remove`` comments using the Only a super-user will be able to delete comments from the database. ``Remove Comments`` only sets the ``is_public`` attribute to ``False``. + +.. vim:ft=rst: diff --git a/docs/forms.txt b/docs/forms.txt index e0a66c0..fbea0e7 100644 --- a/docs/forms.txt +++ b/docs/forms.txt @@ -2,10 +2,10 @@ Comment form classes ==================== -.. module:: django_comments.forms +.. module:: commentary.forms :synopsis: Forms for dealing with the comment model. -The ``django_comments.forms`` module contains a handful of forms +The ``commentary.forms`` module contains a handful of forms you'll use when writing custom views dealing with comments, or when writing :doc:`custom comment apps `. @@ -13,7 +13,7 @@ you'll use when writing custom views dealing with comments, or when writing The main comment form representing the standard way of handling submitted comments. This is the class used by all the views - :mod:`django_comments` to handle submitted comments. + :mod:`commentary` to handle submitted comments. If you want to build custom views that are similar to django_comment's built-in comment handling views, you'll probably want to use this form. @@ -44,3 +44,5 @@ forms that you can subclass to reuse pieces of the form handling logic: This class contains the ``name``, ``email``, ``url``, and the ``comment`` field itself, along with the associated validation logic. + +.. vim:ft=rst: diff --git a/docs/index.txt b/docs/index.txt index dcc8df4..e7f2b7a 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -1,17 +1,15 @@ -=========================== -Django "excontrib" Comments -=========================== +================= +Django Commentary +================= -.. module:: django_comments - :synopsis: The package formerly known as "django.contrib.comments". +.. module:: commentary + :synopsis: A fork of "django-contrib-comments". .. highlightlang:: html+django -Django used to include a comments framework; since Django 1.6 it's been -separated to a separate project. This is that project. - -This framework can be used to attach comments to any model, so you can use it -for comments on blog entries, photos, book chapters, or anything else. +This framework is a fork of ``django-contrib-comments`` which used to be +included in Django. It can be used to attach comments to any model, so +you can use it on blog entries, photos, book chapters, or anything else. Contents ================ @@ -27,5 +25,5 @@ Contents moderation example settings - porting +.. vim:ft=rst: diff --git a/docs/models.txt b/docs/models.txt index a57df4e..d385caf 100644 --- a/docs/models.txt +++ b/docs/models.txt @@ -2,7 +2,7 @@ The comment models ================== -.. module:: django_comments.models +.. module:: commentary.models :synopsis: The comment models .. class:: Comment @@ -77,3 +77,5 @@ The comment models ``True`` if the comment was removed. Used to keep track of removed comments instead of just deleting them. + +.. vim:ft=rst: diff --git a/docs/moderation.txt b/docs/moderation.txt index 0989d5e..342b361 100644 --- a/docs/moderation.txt +++ b/docs/moderation.txt @@ -2,7 +2,7 @@ Generic comment moderation ========================== -.. module:: django_comments.moderation +.. module:: commentary.moderation :synopsis: Support for automatic comment moderation. Django's bundled comments application is extremely useful on its own, @@ -10,14 +10,14 @@ 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_comments.moderation`` provides a generic, extensible +``commentary.moderation`` 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. Overview ======== -The entire system is contained within ``django_comments.moderation``, +The entire system is contained within ``commentary.moderation``, and uses a two-step process to enable moderation for any given model: 1. A subclass of :class:`CommentModerator` @@ -53,7 +53,7 @@ new comment is posted on an ``Entry``: Accomplishing this is fairly straightforward and requires very little code:: - from django_comments.moderation import CommentModerator, moderator + from commentary.moderation import CommentModerator, moderator class EntryModerator(CommentModerator): email_notification = True @@ -172,7 +172,7 @@ Registering models for moderation --------------------------------- The moderation system, represented by -``django_comments.moderation.moderator`` is an instance of the class +``commentary.moderation.moderator`` is an instance of the class :class:`Moderator`, which allows registration and "unregistration" of models via two methods: @@ -214,8 +214,8 @@ models with an instance of the subclass. Determines how moderation is set up globally. The base implementation in :class:`Moderator` does this by - attaching listeners to the :data:`~django_comments.signals.comment_will_be_posted` - and :data:`~django_comments.signals.comment_was_posted` signals from the + attaching listeners to the :data:`~commentary.signals.comment_will_be_posted` + and :data:`~commentary.signals.comment_was_posted` signals from the comment models. .. method:: pre_save_moderation(sender, comment, request, **kwargs) @@ -230,3 +230,5 @@ models with an instance of the subclass. In the base implementation, applies all post-save moderation steps (currently this consists entirely of deleting comments which were disallowed). + +.. vim:ft=rst: diff --git a/docs/porting.txt b/docs/porting.txt index 9b32f84..9548d98 100644 --- a/docs/porting.txt +++ b/docs/porting.txt @@ -1,48 +1,50 @@ =============================================================== -Porting to ``django_comments`` from ``django.contrib.comments`` +Porting to ``commentary`` from ``django.contrib.comments`` =============================================================== -To move from ``django.contrib.comments`` to ``django_comments``, +To move from ``django.contrib.comments`` to ``commentary``, follow these steps: -#. Install the comments app by running ``pip install django-contrib-comments``. +#. Install the comments app by running ``pip install django-commentary``. #. In :setting:`INSTALLED_APPS`, replace ``'django.contrib.comments'`` - with ``'django_comments'``. + with ``'commentary'``. .. code-block:: python INSTALLED_APPS = ( ... - 'django_comments', + 'commentary', ... ) #. In your project's ``urls.py``, replace the url include - ``django.contrib.comments.urls`` with ``'django_comments.urls'``: + ``django.contrib.comments.urls`` with ``'commentary.urls'``: .. code-block:: python urlpatterns = [ ... - url(r'^comments/', include('django_comments.urls')), + url(r'^comments/', include('commentary.urls')), ... ] #. If your project had :doc:`customized the comments framework - `, then update your imports to use the ``django_comments`` + `, then update your imports to use the ``commentary`` module instead of ``django.contrib.comments``. For example: .. code-block:: python from django.contrib.comments.models import Comment # old - from django_comments.models import Comment # new + from commentary.models import Comment # new from django.contrib.comments.forms import CommentForm # old - from django_comments.forms import CommentForm # new + from commentary.forms import CommentForm # new #. If your database schema already contains the tables and data for existing comments and you get an error like - ``django.db.utils.ProgrammingError: relation "django_comments" already exists`` + ``django.db.utils.ProgrammingError: relation "commentary" already exists`` in your first subsequent migration, run - ``manage.py migrate django_comments --fake-initial``. + ``manage.py migrate commentary --fake-initial``. + +.. vim:ft=rst: diff --git a/docs/quickstart.txt b/docs/quickstart.txt index da44c5c..73df4a6 100644 --- a/docs/quickstart.txt +++ b/docs/quickstart.txt @@ -4,13 +4,13 @@ Quick start guide To get started using the ``comments`` app, follow these steps: -#. Install the comments app by running ``pip install django-contrib-comments``. +#. Install the comments app by running ``pip install django-commentary``. #. :ref:`Enable the "sites" framework ` by adding ``'django.contrib.sites'`` to :setting:`INSTALLED_APPS` and defining :setting:`SITE_ID`. -#. Install the comments framework by adding ``'django_comments'`` to +#. Install the comments framework by adding ``'commentary'`` to :setting:`INSTALLED_APPS`. #. Run ``manage.py migrate`` so that Django will create the comment tables. @@ -21,7 +21,7 @@ To get started using the ``comments`` app, follow these steps: urlpatterns = [ ... - url(r'^comments/', include('django_comments.urls')), + url(r'^comments/', include('commentary.urls')), ... ] @@ -115,7 +115,7 @@ For example:: ... {% endfor %} -This returns a list of :class:`~django_comments.models.Comment` objects; +This returns a list of :class:`~commentary.models.Comment` objects; see :doc:`the comment model documentation ` for details. @@ -335,3 +335,5 @@ output the CSRF token and cookie. .. _cross site request forgery protection: https://docs.djangoproject.com/en/stable/ref/csrf/ .. _honeypot: http://en.wikipedia.org/wiki/Honeypot_(computing) + +.. vim:ft=rst: diff --git a/docs/settings.txt b/docs/settings.txt index 4465852..e28fde4 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -1,7 +1,7 @@ .. _settings-comments: ============================================= -Settings that ``django_comments`` understands +Settings that ``commentary`` understands ============================================= .. setting:: COMMENTS_HIDE_REMOVED @@ -39,3 +39,5 @@ COMMENT_TIMEOUT The maximum comment form timeout in seconds. The default value is ``2 * 60 * 60`` (2 hours). + +.. vim:ft=rst: diff --git a/docs/signals.txt b/docs/signals.txt index 4a6d5fe..7a75188 100644 --- a/docs/signals.txt +++ b/docs/signals.txt @@ -2,7 +2,7 @@ Signals sent by the comments app ================================ -.. module:: django_comments.signals +.. module:: commentary.signals :synopsis: Signals sent by the comment module. The comment app sends a series of signals_ to allow for @@ -15,7 +15,7 @@ signals. comment_will_be_posted ====================== -.. data:: django_comments.signals.comment_will_be_posted +.. data:: commentary.signals.comment_will_be_posted :module: Sent just before a comment will be saved, after it's been sanity checked and @@ -44,7 +44,7 @@ Arguments sent with this signal: comment_was_posted ================== -.. data:: django_comments.signals.comment_was_posted +.. data:: commentary.signals.comment_was_posted :module: Sent just after the comment is saved. @@ -65,7 +65,7 @@ Arguments sent with this signal: comment_was_flagged =================== -.. data:: django_comments.signals.comment_was_flagged +.. data:: commentary.signals.comment_was_flagged :module: Sent after a comment was "flagged" in some way. Check the flag to see if this @@ -83,7 +83,7 @@ Arguments sent with this signal: :meth:`~django.db.models.Model.save` again. ``flag`` - The ``django_comments.models.CommentFlag`` that's been attached to + The ``commentary.models.CommentFlag`` that's been attached to the comment. ``created`` @@ -91,3 +91,5 @@ Arguments sent with this signal: ``request`` The :class:`~django.http.HttpRequest` that posted the comment. + +.. vim:ft=rst: diff --git a/setup.py b/setup.py index 32177b8..3ccb681 100644 --- a/setup.py +++ b/setup.py @@ -9,13 +9,15 @@ long_description = readme + history setup( - name='django-contrib-comments', - version='1.9.1', - url="https://github.com/django/django-contrib-comments", - description='The code formerly known as django.contrib.comments.', + name='django-commentary', + version='2.0.0.dev0', + url='https://github.com/mangadventure/django-commentary', + description='A fork of django-contrib-comments.', long_description=long_description, author='Django Software Foundation', author_email='jacob@jacobian.org', + maintainer='MangAdventure', + maintainer_email='chronobserver@disroot.org', license='BSD', platforms='any', zip_safe=False, diff --git a/tests/runtests.py b/tests/runtests.py index 61b7ee7..39ea7c0 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -22,7 +22,7 @@ "django.contrib.sessions", "django.contrib.sites", "django.contrib.admin", - "django_comments", + "commentary", "testapp", "custom_comments", ], diff --git a/tests/testapp/tests/__init__.py b/tests/testapp/tests/__init__.py index cb06b6d..a1a06c4 100644 --- a/tests/testapp/tests/__init__.py +++ b/tests/testapp/tests/__init__.py @@ -6,8 +6,8 @@ from django.test import TestCase from django.test.utils import override_settings -from django_comments.forms import CommentForm -from django_comments.models import Comment +from commentary.forms import CommentForm +from commentary.models import Comment from testapp.models import Article, Author diff --git a/tests/testapp/tests/test_app_api.py b/tests/testapp/tests/test_app_api.py index 7dceee0..900c953 100644 --- a/tests/testapp/tests/test_app_api.py +++ b/tests/testapp/tests/test_app_api.py @@ -3,9 +3,9 @@ from django.core.exceptions import ImproperlyConfigured from django.test.utils import modify_settings, override_settings -import django_comments -from django_comments.models import Comment -from django_comments.forms import CommentForm +import commentary +from commentary.models import Comment +from commentary.forms import CommentForm from . import CommentTestCase @@ -14,31 +14,31 @@ class CommentAppAPITests(CommentTestCase): """Tests for the "comment app" API""" def testGetCommentApp(self): - self.assertEqual(django_comments.get_comment_app(), django_comments) + self.assertEqual(commentary.get_comment_app(), commentary) - @modify_settings(INSTALLED_APPS={'remove': 'django_comments'}) + @modify_settings(INSTALLED_APPS={'remove': 'commentary'}) def testGetMissingCommentApp(self): - msg = "The COMMENTS_APP ('django_comments') must be in INSTALLED_APPS" + msg = "The COMMENTS_APP ('commentary') must be in INSTALLED_APPS" with self.assertRaisesMessage(ImproperlyConfigured, msg): - django_comments.get_comment_app() + commentary.get_comment_app() def testGetForm(self): - self.assertEqual(django_comments.get_form(), CommentForm) + self.assertEqual(commentary.get_form(), CommentForm) def testGetFormTarget(self): - self.assertEqual(django_comments.get_form_target(), "/post/") + self.assertEqual(commentary.get_form_target(), "/post/") def testGetFlagURL(self): c = Comment(id=12345) - self.assertEqual(django_comments.get_flag_url(c), "/flag/12345/") + self.assertEqual(commentary.get_flag_url(c), "/flag/12345/") def getGetDeleteURL(self): c = Comment(id=12345) - self.assertEqual(django_comments.get_delete_url(c), "/delete/12345/") + self.assertEqual(commentary.get_delete_url(c), "/delete/12345/") def getGetApproveURL(self): c = Comment(id=12345) - self.assertEqual(django_comments.get_approve_url(c), "/approve/12345/") + self.assertEqual(commentary.get_approve_url(c), "/approve/12345/") @override_settings( @@ -48,27 +48,27 @@ class CustomCommentTest(CommentTestCase): def testGetCommentApp(self): import custom_comments - self.assertEqual(django_comments.get_comment_app(), custom_comments) + self.assertEqual(commentary.get_comment_app(), custom_comments) def testGetModel(self): from custom_comments.models import CustomComment - self.assertEqual(django_comments.get_model(), CustomComment) + self.assertEqual(commentary.get_model(), CustomComment) def testGetForm(self): from custom_comments.forms import CustomCommentForm - self.assertEqual(django_comments.get_form(), CustomCommentForm) + self.assertEqual(commentary.get_form(), CustomCommentForm) def testGetFormTarget(self): - self.assertEqual(django_comments.get_form_target(), "/post/") + self.assertEqual(commentary.get_form_target(), "/post/") def testGetFlagURL(self): c = Comment(id=12345) - self.assertEqual(django_comments.get_flag_url(c), "/flag/12345/") + self.assertEqual(commentary.get_flag_url(c), "/flag/12345/") def getGetDeleteURL(self): c = Comment(id=12345) - self.assertEqual(django_comments.get_delete_url(c), "/delete/12345/") + self.assertEqual(commentary.get_delete_url(c), "/delete/12345/") def getGetApproveURL(self): c = Comment(id=12345) - self.assertEqual(django_comments.get_approve_url(c), "/approve/12345/") + self.assertEqual(commentary.get_approve_url(c), "/approve/12345/") diff --git a/tests/testapp/tests/test_comment_form.py b/tests/testapp/tests/test_comment_form.py index 8f7addc..6390abd 100644 --- a/tests/testapp/tests/test_comment_form.py +++ b/tests/testapp/tests/test_comment_form.py @@ -5,8 +5,8 @@ from django.conf import settings from django.contrib.sites.models import Site -from django_comments.forms import CommentForm -from django_comments.models import Comment +from commentary.forms import CommentForm +from commentary.models import Comment from . import CommentTestCase from testapp.models import Article diff --git a/tests/testapp/tests/test_comment_utils_moderators.py b/tests/testapp/tests/test_comment_utils_moderators.py index c440c60..46b4b10 100644 --- a/tests/testapp/tests/test_comment_utils_moderators.py +++ b/tests/testapp/tests/test_comment_utils_moderators.py @@ -3,8 +3,8 @@ from django.core import mail from django.test.utils import override_settings -from django_comments.models import Comment -from django_comments.moderation import (moderator, CommentModerator, +from commentary.models import Comment +from commentary.moderation import (moderator, CommentModerator, AlreadyModerated) from . import CommentTestCase diff --git a/tests/testapp/tests/test_comment_views.py b/tests/testapp/tests/test_comment_views.py index b197db1..5b71af3 100644 --- a/tests/testapp/tests/test_comment_views.py +++ b/tests/testapp/tests/test_comment_views.py @@ -3,8 +3,8 @@ from django.conf import settings from django.contrib.auth.models import User -from django_comments import signals -from django_comments.models import COMMENT_MAX_LENGTH, Comment +from commentary import signals +from commentary.models import COMMENT_MAX_LENGTH, Comment from . import CommentTestCase from testapp.models import Article, Book diff --git a/tests/testapp/tests/test_feeds.py b/tests/testapp/tests/test_feeds.py index 8954fcb..ce54c65 100644 --- a/tests/testapp/tests/test_feeds.py +++ b/tests/testapp/tests/test_feeds.py @@ -7,7 +7,7 @@ from django.contrib.sites.models import Site from django.test.utils import override_settings -from django_comments.models import Comment +from commentary.models import Comment from . import CommentTestCase from testapp.models import Article diff --git a/tests/testapp/tests/test_models.py b/tests/testapp/tests/test_models.py index 40b1931..4bf4015 100644 --- a/tests/testapp/tests/test_models.py +++ b/tests/testapp/tests/test_models.py @@ -1,6 +1,6 @@ from __future__ import absolute_import -from django_comments.models import Comment +from commentary.models import Comment from . import CommentTestCase from testapp.models import Author, Article diff --git a/tests/testapp/tests/test_moderation_views.py b/tests/testapp/tests/test_moderation_views.py index b9005be..a12d4cf 100644 --- a/tests/testapp/tests/test_moderation_views.py +++ b/tests/testapp/tests/test_moderation_views.py @@ -5,8 +5,8 @@ from django.test.utils import override_settings from django.utils import translation -from django_comments import signals -from django_comments.models import Comment, CommentFlag +from commentary import signals +from commentary.models import Comment, CommentFlag from . import CommentTestCase @@ -274,7 +274,7 @@ def setUp(self): u = User.objects.get(username="normaluser") u.is_staff = True perms = Permission.objects.filter( - content_type__app_label='django_comments', + content_type__app_label='commentary', codename__endswith='comment' ) for perm in perms: @@ -284,26 +284,26 @@ def setUp(self): def testActionsNonModerator(self): self.createSomeComments() self.client.login(username="normaluser", password="normaluser") - response = self.client.get("/admin/django_comments/comment/") + response = self.client.get("/admin/commentary/comment/") self.assertNotContains(response, "approve_comments") def testActionsModerator(self): self.createSomeComments() makeModerator("normaluser") self.client.login(username="normaluser", password="normaluser") - response = self.client.get("/admin/django_comments/comment/") + response = self.client.get("/admin/commentary/comment/") self.assertContains(response, "approve_comments") def testActionsDisabledDelete(self): "Tests a CommentAdmin where 'delete_selected' has been disabled." self.createSomeComments() self.client.login(username="normaluser", password="normaluser") - response = self.client.get('/admin2/django_comments/comment/') + response = self.client.get('/admin2/commentary/comment/') self.assertEqual(response.status_code, 200) self.assertNotContains(response, '