diff --git a/README.rst b/README.rst index 1dad5c4..94282b2 100644 --- a/README.rst +++ b/README.rst @@ -53,7 +53,8 @@ Known issues Documentation ============= -Full module documentation is hosted at ReadTheDocs: http://django-moderation.readthedocs.org/ + +Full documentation is hosted at `ReadTheDocs django-moderation `_ Contributors @@ -61,8 +62,10 @@ Contributors Special thanks to all persons that contributed to this project. -- jonwd7 https://github.com/jonwd7 -- treyhunner https://github.com/treyhunner -Thank you for all ideas, bug fixes, patches. +- `jonwd7 `_ +- `treyhunner `_ +- `DmytroLitvinov `_ + +Thank you for all ideas, bug fixes, patches, maintaining. diff --git a/docs/getting_started.rst b/docs/getting_started.rst index e47815c..dc84d2a 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -4,11 +4,9 @@ Getting started quick guide Installation ------------ -Use easy_install: - .. code-block:: bash - $ easy_install django-moderation + $ pip install django-moderation Or download source code from http://github.com/dominno/django-moderation and run installation script: diff --git a/docs/history.rst b/docs/history.rst index de0bab4..079f6c7 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -104,11 +104,13 @@ Added features - Minor changes at docs -0.8.0 (2022-04-05) +0.8.0 (2022-04-09) ------------------ - Drop support of Django<2.2. Now it supports only Django>=2.2,<4 - Drop support of Python3.5. Now it supports only Python 3.6, 3.7, 3.8, 3.9 - Drop support of ``DJANGO_MODERATION_MODERATORS`` setting - Formatted code with `black` -- Drop dependency `django-model-utils` which we used for Choices functionality \ No newline at end of file +- Drop dependency `django-model-utils` which we used for Choices functionality +- Add partial support for Django 4.0 - remove ugettext, change `smart_text` to `smart_str`, + change `ifequal` template tag to `if`. \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index 2a1a64e..e9683ca 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,17 +5,17 @@ Welcome to django-moderation's documentation! Introduction ------------ -django-moderation is reusable application for Django framework, that allows to +``django-moderation`` is reusable application for Django framework, that allows to moderate any model objects. -Possible use cases: +**Possible use cases**: - User creates his profile, profile is not visible on site. It will be visible on site when moderator approves it. - User change his profile, old profile data is visible on site. New data will be visible on site when moderator approves it. -Features: +**Features**: - configurable admin integration(data changed in admin can be visible on site when moderator approves it) @@ -38,8 +38,9 @@ Contributors Special thanks to all persons that contributed to this project. -- jonwd7 http://github.com/jonwd7 -- treyhunner http://github.com/treyhunner +- `jonwd7 `_ +- `treyhunner `_ +- `DmytroLitvinov `_ Thank you for all ideas, bug fixes, patches. @@ -52,9 +53,9 @@ Screenshots Requirements ============ -Python 3.5, 3.6, 3.7 +Python 3.6, 3.7, 3.8, 3.9 -Django 1.11, 2.0, 2.1, 2.2 +Django 2.2, 3.1, 3.2 Contents: diff --git a/example_project/settings.py b/example_project/settings.py index ca99031..ffa146d 100644 --- a/example_project/settings.py +++ b/example_project/settings.py @@ -1,13 +1,7 @@ import os -ADMINS = ( - # ('Your Name', 'your_email@domain.com'), -) - DEBUG = True -MANAGERS = ADMINS - DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', @@ -20,13 +14,9 @@ } TIME_ZONE = 'UTC' - LANGUAGE_CODE = 'en' - USE_I18N = True - USE_L10N = True - USE_TZ = True LOCALE_PATHS = [ diff --git a/moderation/filterspecs.py b/moderation/filterspecs.py index 1801046..7fc4095 100644 --- a/moderation/filterspecs.py +++ b/moderation/filterspecs.py @@ -1,6 +1,6 @@ from django.contrib.admin.filters import FieldListFilter from django.contrib.contenttypes.models import ContentType -from django.utils.encoding import smart_text +from django.utils.encoding import smart_str from django.utils.translation import gettext as _ from . import moderation @@ -34,7 +34,7 @@ def choices(self, cl): } for ct_type in self.content_types: yield { - 'selected': smart_text(ct_type.id) == self.lookup_val, + 'selected': smart_str(ct_type.id) == self.lookup_val, 'query_string': cl.get_query_string({self.lookup_kwarg: ct_type.id}), 'display': str(ct_type), } diff --git a/moderation/models.py b/moderation/models.py index 3c1c44a..1507543 100644 --- a/moderation/models.py +++ b/moderation/models.py @@ -116,9 +116,9 @@ def automoderate(self, user=None): return status def _get_moderation_status_and_reason(self, obj, user): - ''' + """ Returns tuple of moderation status and reason for auto moderation - ''' + """ reason = self.moderator.is_auto_reject(obj, user) if reason: return MODERATION_STATUS_REJECTED, reason diff --git a/moderation/signals.py b/moderation/signals.py index 0d00b28..9f863ff 100644 --- a/moderation/signals.py +++ b/moderation/signals.py @@ -1,13 +1,13 @@ -import django.dispatch +from django.dispatch import Signal -pre_moderation = django.dispatch.Signal(providing_args=['instance', 'status']) +# Arguments: "instance", "status" +pre_moderation = Signal() -post_moderation = django.dispatch.Signal(providing_args={'instance', 'status'}) +# Arguments: "instance", "status" +post_moderation = Signal() -pre_many_moderation = django.dispatch.Signal( - providing_args={'queryset', 'status', 'by', 'reason'} -) +# Arguments: "queryset", "status", "by", "reason" +pre_many_moderation = Signal() -post_many_moderation = django.dispatch.Signal( - providing_args={'queryset', 'status', 'by', 'reason'} -) +# Arguments: "queryset", "status", "by", "reason" +post_many_moderation = Signal() diff --git a/moderation/templates/moderation/html_diff.html b/moderation/templates/moderation/html_diff.html index 495abb8..eeccda3 100644 --- a/moderation/templates/moderation/html_diff.html +++ b/moderation/templates/moderation/html_diff.html @@ -1,20 +1,20 @@ {% spaceless %} {% for operation in diff_operations %} - {% ifequal operation.operation "replace" %} + {% if operation.operation == "replace" %} {{ operation.deleted }} {{ operation.inserted }} - {% endifequal %} - - {% ifequal operation.operation "delete" %} + {% endif %} + + {% if operation.operation == "delete" %} {{ operation.deleted }} - {% endifequal %} - - {% ifequal operation.operation "insert" %} + {% endif %} + + {% if operation.operation == "insert" %} {{ operation.inserted }} - {% endifequal %} - - {% ifequal operation.operation "equal" %} + {% endif %} + + {% if operation.operation == "equal" %} {{ operation.inserted }} - {% endifequal %} + {% endif %} {% endfor %} {% endspaceless %} diff --git a/moderation/templates/moderation/notification_message_user.txt b/moderation/templates/moderation/notification_message_user.txt index 28bd5fd..f732682 100644 --- a/moderation/templates/moderation/notification_message_user.txt +++ b/moderation/templates/moderation/notification_message_user.txt @@ -1,6 +1,6 @@ Dear {{ user.first_name }} -Your {{ content_type }} entry has been {% ifequal moderated_object.status 0 %}rejected{% endifequal %}{% ifequal moderated_object.status 1 %}accepted{% endifequal %}. +Your {{ content_type }} entry has been {% if moderated_object.status == 0 %}rejected{% endif %}{% if moderated_object.status == 1 %}accepted{% endif %}. {% if moderated_object.reason %} Reason: {{ moderated_object.reason }} diff --git a/moderation/templates/moderation/notification_subject_user.txt b/moderation/templates/moderation/notification_subject_user.txt index 81dd5d9..be9182c 100644 --- a/moderation/templates/moderation/notification_subject_user.txt +++ b/moderation/templates/moderation/notification_subject_user.txt @@ -1 +1 @@ -{{ content_type }} entry has been {% ifequal moderated_object.status 0 %}rejected{% endifequal %}{% ifequal moderated_object.status 1 %}accepted{% endifequal %} \ No newline at end of file +{{ content_type }} entry has been {% if moderated_object.status == 0 %}rejected{% endif %}{% if moderated_object.status == 1 %}accepted{% endif %} \ No newline at end of file diff --git a/setup.py b/setup.py index 7108770..03110b9 100644 --- a/setup.py +++ b/setup.py @@ -5,11 +5,9 @@ version = __import__('moderation').__version__ tests_require = [ - 'unittest2py3k', 'django>=2.2', 'django-webtest', 'webtest', - 'mock', 'pillow', ] diff --git a/tests/models.py b/tests/models.py index 0768a33..32279c1 100644 --- a/tests/models.py +++ b/tests/models.py @@ -1,5 +1,5 @@ """ -Test models used in django-moderations tests +Test models used in django-moderation tests """ from django.conf import settings from django.contrib.auth.models import User @@ -9,22 +9,22 @@ class UserProfile(models.Model): user = models.ForeignKey( - getattr(settings, 'AUTH_USER_MODEL', 'auth.User'), + settings.AUTH_USER_MODEL, on_delete=models.CASCADE, - related_name='user_profile_set', + related_name='user_profiles', ) description = models.TextField() url = models.URLField() def __str__(self): - return "%s - %s" % (self.user, self.url) + return f"{self.user} - {self.url}" class SuperUserProfile(UserProfile): super_power = models.TextField() def __str__(self): - return "%s - %s - %s" % (self.user, self.url, self.super_power) + return f"{self.user} - {self.url} - {self.super_power}" class ModelWithSlugField(models.Model): diff --git a/tests/tests/unit/testadmin.py b/tests/tests/unit/testadmin.py index 2ebb586..d68705d 100644 --- a/tests/tests/unit/testadmin.py +++ b/tests/tests/unit/testadmin.py @@ -1,4 +1,5 @@ -import mock +from unittest import mock + from django.contrib.admin.sites import site from django.contrib.auth.models import Permission, User from django.test.testcases import TestCase diff --git a/tests/tests/unit/testmoderator.py b/tests/tests/unit/testmoderator.py index e1951cd..d64e849 100644 --- a/tests/tests/unit/testmoderator.py +++ b/tests/tests/unit/testmoderator.py @@ -1,4 +1,5 @@ import unittest +from unittest.mock import Mock from django.contrib.auth.models import Group, User from django.core import mail @@ -174,8 +175,6 @@ def test_auto_approve_for_groups_user_not_in_group(self): self.assertFalse(self.moderator.is_auto_approve(self.obj, self.user)) def test_is_auto_reject_user_is_anonymous(self): - from mock import Mock - user = Mock() user.is_anonymous = lambda: True reason = self.moderator.is_auto_reject(self.obj, user) @@ -183,8 +182,6 @@ def test_is_auto_reject_user_is_anonymous(self): self.assertEqual(reason, 'Auto-rejected: Anonymous User') def test_is_auto_reject_user_is_not_anonymous(self): - from mock import Mock - user = Mock() user.is_anonymous = lambda: False self.assertFalse(self.moderator.is_auto_reject(self.obj, user)) diff --git a/tox.ini b/tox.ini index ac3902b..472ec30 100644 --- a/tox.ini +++ b/tox.ini @@ -8,15 +8,15 @@ envlist = django2.2-py{36,37,38} django3.1-py{36,37,38,39} django3.2-py{36,37,38,39} + django4.0-py{38,39} [testenv] commands = python setup.py test deps = - mock pillow - unittest2py3k django2.2: Django>=2.2,<3.0 django3.1: Django>=3.1,<3.2 - django3.2: Django>=3.2,<4.0 \ No newline at end of file + django3.2: Django>=3.2,<4.0 + django4.0: Django>=4.0,<4.1 \ No newline at end of file