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