diff --git a/.github/workflows/plugin.yml b/.github/workflows/plugin.yml index cf78c171..84ee1641 100644 --- a/.github/workflows/plugin.yml +++ b/.github/workflows/plugin.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest services: postgres: - image: postgres:11 + image: postgres:12 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -40,7 +40,7 @@ jobs: strategy: matrix: database: ['postgres', 'mysql'] - python-version: [3.7, 3.8, 3.9] + python-version: [3.8, 3.9, '3.10'] fail-fast: false steps: @@ -56,20 +56,25 @@ jobs: #pip install -e git+https://github.com/modoboa/modoboa.git#egg=modoboa pip install -r requirements.txt pip install -r test-requirements.txt + cd .. + git clone https://github.com/modoboa/modoboa.git + cd modoboa + python setup.py develop + cd ../modoboa-amavis python setup.py develop - name: Install postgres requirements if: ${{ matrix.database == 'postgres' }} run: | - pip install 'psycopg2-binary>=2.8,<2.9' + pip install 'psycopg[binary]>=3.1' pip install coverage echo "DB=postgres" >> $GITHUB_ENV - name: Install mysql requirements if: ${{ matrix.database == 'mysql' }} run: | - pip install 'mysqlclient<2.0.4' + pip install 'mysqlclient<2.1.2' echo "DB=mysql" >> $GITHUB_ENV - name: Test with pytest - if: ${{ matrix.python-version != '3.9' || matrix.database != 'postgres' }} + if: ${{ matrix.python-version != '3.10' || matrix.database != 'postgres' }} run: | cd test_project python3 manage.py test modoboa_amavis @@ -82,10 +87,12 @@ jobs: MYSQL_USER: root - name: Test with pytest and coverage - if: ${{ matrix.python-version == '3.9' && matrix.database == 'postgres' }} + if: ${{ matrix.python-version == '3.10' && matrix.database == 'postgres' }} run: | cd test_project coverage run --source ../modoboa_amavis manage.py test modoboa_amavis + coverage xml + coverage report env: # use localhost for the host here because we are running the job on the VM. # If we were running the job on in a container this would be postgres @@ -94,44 +101,38 @@ jobs: MYSQL_PORT: ${{ job.services.mysql.ports[3306] }} # get randomly assigned published port MYSQL_USER: root - name: Upload coverage result - if: ${{ matrix.python-version == '3.9' }} - uses: actions/upload-artifact@v2 + if: ${{ matrix.python-version == '3.10' && matrix.database == 'postgres' }} + uses: actions/upload-artifact@v3 with: name: coverage-results - path: test_project/.coverage + path: test_project/coverage.xml coverage: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - name: Install dependencies - run: | - pip install codecov - name: Download coverage results - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: coverage-results - - name: Report coverage - run: | - coverage report - codecov + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./coverage.xml release: + if: github.event_name != 'pull_request' needs: coverage runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Set up Python 3.9 + - name: Set up Python 3.10 uses: actions/setup-python@v4 with: - python-version: '3.9' + python-version: '3.10' - name: Build packages run: | sudo apt-get install librrd-dev rrdtool libssl-dev gettext diff --git a/.gitignore b/.gitignore index db4561ea..120e7e0c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] +.eggs # C extensions *.so diff --git a/modoboa_amavis/checks/settings_checks.py b/modoboa_amavis/checks/settings_checks.py index 9e029390..3fe101f4 100644 --- a/modoboa_amavis/checks/settings_checks.py +++ b/modoboa_amavis/checks/settings_checks.py @@ -5,7 +5,7 @@ from django.conf import settings from django.core.checks import Warning, register from django.db import connections -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ W001 = Warning( _("AMAVIS_DEFAULT_DATABASE_ENCODING does not match the character " diff --git a/modoboa_amavis/constants.py b/modoboa_amavis/constants.py index d12bbdac..680fc9b4 100644 --- a/modoboa_amavis/constants.py +++ b/modoboa_amavis/constants.py @@ -6,7 +6,7 @@ import collections -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ MESSAGE_TYPES = collections.OrderedDict(( ("C", _("Clean")), diff --git a/modoboa_amavis/forms.py b/modoboa_amavis/forms.py index 6a6aab5d..d7e72c44 100644 --- a/modoboa_amavis/forms.py +++ b/modoboa_amavis/forms.py @@ -5,7 +5,7 @@ """ from django import forms -from django.utils.translation import ugettext as _, ugettext_lazy +from django.utils.translation import gettext as _, gettext_lazy from modoboa.lib import form_utils from modoboa.parameters import forms as param_forms, tools as param_tools @@ -100,144 +100,144 @@ class ParametersForm(param_forms.AdminParametersForm): app = "modoboa_amavis" amavis_settings_sep = form_utils.SeparatorField( - label=ugettext_lazy("Amavis settings")) + label=gettext_lazy("Amavis settings")) localpart_is_case_sensitive = form_utils.YesNoField( - label=ugettext_lazy("Localpart is case sensitive"), + label=gettext_lazy("Localpart is case sensitive"), initial=False, - help_text=ugettext_lazy("Value should match amavisd.conf variable %s" + help_text=gettext_lazy("Value should match amavisd.conf variable %s" % "$localpart_is_case_sensitive") ) recipient_delimiter = forms.CharField( - label=ugettext_lazy("Recipient delimiter"), + label=gettext_lazy("Recipient delimiter"), initial="", - help_text=ugettext_lazy("Value should match amavisd.conf variable %s" + help_text=gettext_lazy("Value should match amavisd.conf variable %s" % "$recipient_delimiter"), widget=forms.TextInput(attrs={"class": "form-control"}), required=False ) qsettings_sep = form_utils.SeparatorField( - label=ugettext_lazy("Quarantine settings")) + label=gettext_lazy("Quarantine settings")) max_messages_age = forms.IntegerField( - label=ugettext_lazy("Maximum message age"), + label=gettext_lazy("Maximum message age"), initial=14, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Quarantine messages maximum age (in days) before deletion" ) ) - sep1 = form_utils.SeparatorField(label=ugettext_lazy("Messages releasing")) + sep1 = form_utils.SeparatorField(label=gettext_lazy("Messages releasing")) released_msgs_cleanup = form_utils.YesNoField( - label=ugettext_lazy("Remove released messages"), + label=gettext_lazy("Remove released messages"), initial=False, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Remove messages marked as released while cleaning up " "the database" ) ) am_pdp_mode = forms.ChoiceField( - label=ugettext_lazy("Amavis connection mode"), + label=gettext_lazy("Amavis connection mode"), choices=[("inet", "inet"), ("unix", "unix")], initial="unix", - help_text=ugettext_lazy("Mode used to access the PDP server"), + help_text=gettext_lazy("Mode used to access the PDP server"), widget=form_utils.HorizontalRadioSelect() ) am_pdp_host = forms.CharField( - label=ugettext_lazy("PDP server address"), + label=gettext_lazy("PDP server address"), initial="localhost", - help_text=ugettext_lazy("PDP server address (if inet mode)"), + help_text=gettext_lazy("PDP server address (if inet mode)"), widget=forms.TextInput(attrs={"class": "form-control"}) ) am_pdp_port = forms.IntegerField( - label=ugettext_lazy("PDP server port"), + label=gettext_lazy("PDP server port"), initial=9998, - help_text=ugettext_lazy("PDP server port (if inet mode)") + help_text=gettext_lazy("PDP server port (if inet mode)") ) am_pdp_socket = forms.CharField( - label=ugettext_lazy("PDP server socket"), + label=gettext_lazy("PDP server socket"), initial="/var/amavis/amavisd.sock", - help_text=ugettext_lazy("Path to the PDP server socket (if unix mode)") + help_text=gettext_lazy("Path to the PDP server socket (if unix mode)") ) user_can_release = form_utils.YesNoField( - label=ugettext_lazy("Allow direct release"), + label=gettext_lazy("Allow direct release"), initial=False, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Allow users to directly release their messages") ) self_service = form_utils.YesNoField( - label=ugettext_lazy("Enable self-service mode"), + label=gettext_lazy("Enable self-service mode"), initial=False, - help_text=ugettext_lazy("Activate the 'self-service' mode") + help_text=gettext_lazy("Activate the 'self-service' mode") ) notifications_sender = forms.EmailField( - label=ugettext_lazy("Notifications sender"), + label=gettext_lazy("Notifications sender"), initial="notification@modoboa.org", - help_text=ugettext_lazy( + help_text=gettext_lazy( "The e-mail address used to send notitications") ) - lsep = form_utils.SeparatorField(label=ugettext_lazy("Manual learning")) + lsep = form_utils.SeparatorField(label=gettext_lazy("Manual learning")) manual_learning = form_utils.YesNoField( - label=ugettext_lazy("Enable manual learning"), + label=gettext_lazy("Enable manual learning"), initial=True, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Allow super administrators to manually train Spamassassin" ) ) sa_is_local = form_utils.YesNoField( - label=ugettext_lazy("Is Spamassassin local?"), + label=gettext_lazy("Is Spamassassin local?"), initial=True, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Tell if Spamassassin is running on the same server than modoboa" ) ) default_user = forms.CharField( - label=ugettext_lazy("Default user"), + label=gettext_lazy("Default user"), initial="amavis", - help_text=ugettext_lazy( + help_text=gettext_lazy( "Name of the user owning the default bayesian database" ) ) spamd_address = forms.CharField( - label=ugettext_lazy("Spamd address"), + label=gettext_lazy("Spamd address"), initial="127.0.0.1", - help_text=ugettext_lazy("The IP address where spamd can be reached") + help_text=gettext_lazy("The IP address where spamd can be reached") ) spamd_port = forms.IntegerField( - label=ugettext_lazy("Spamd port"), + label=gettext_lazy("Spamd port"), initial=783, - help_text=ugettext_lazy("The TCP port spamd is listening on") + help_text=gettext_lazy("The TCP port spamd is listening on") ) domain_level_learning = form_utils.YesNoField( - label=ugettext_lazy("Enable per-domain manual learning"), + label=gettext_lazy("Enable per-domain manual learning"), initial=False, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Allow domain administrators to train Spamassassin " "(within dedicated per-domain databases)" ) ) user_level_learning = form_utils.YesNoField( - label=ugettext_lazy("Enable per-user manual learning"), + label=gettext_lazy("Enable per-user manual learning"), initial=False, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Allow simple users to personally train Spamassassin " "(within a dedicated database)" ) @@ -262,11 +262,11 @@ class UserSettings(param_forms.UserParametersForm): app = "modoboa_amavis" - dsep = form_utils.SeparatorField(label=ugettext_lazy("Display")) + dsep = form_utils.SeparatorField(label=gettext_lazy("Display")) messages_per_page = forms.IntegerField( initial=40, - label=ugettext_lazy("Number of displayed emails per page"), - help_text=ugettext_lazy( + label=gettext_lazy("Number of displayed emails per page"), + help_text=gettext_lazy( "Set the maximum number of messages displayed in a page") ) diff --git a/modoboa_amavis/handlers.py b/modoboa_amavis/handlers.py index b1595aa6..42ac6887 100644 --- a/modoboa_amavis/handlers.py +++ b/modoboa_amavis/handlers.py @@ -8,7 +8,7 @@ from django.dispatch import receiver from django.template import Context, Template from django.urls import reverse -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from modoboa.admin import models as admin_models, signals as admin_signals from modoboa.core import signals as core_signals diff --git a/modoboa_amavis/lib.py b/modoboa_amavis/lib.py index bc901302..4e5b8666 100644 --- a/modoboa_amavis/lib.py +++ b/modoboa_amavis/lib.py @@ -12,7 +12,7 @@ from django.conf import settings from django.contrib.auth.views import redirect_to_login from django.urls import reverse -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from modoboa.admin import models as admin_models from modoboa.lib.email_utils import ( @@ -23,7 +23,7 @@ from modoboa.lib.web_utils import NavigationParameters from modoboa.parameters import tools as param_tools from .models import Policy, Users -from .utils import smart_bytes, smart_text +from .utils import smart_bytes, smart_str def selfservice(ssfunc=None): @@ -84,7 +84,7 @@ def sendreq(self, mailid, secretid, recipient, *others): quar_type=Q recipient=%s -""" % (smart_text(mailid), smart_text(secretid), smart_text(recipient)))) +""" % (smart_str(mailid), smart_str(secretid), smart_str(recipient)))) answer = self.sock.recv(1024) answer = self.decode(answer) if re.search(br"250 [\d\.]+ Ok", answer): @@ -129,7 +129,7 @@ def _find_binary(self, name): """Find path to binary.""" code, output = exec_cmd("which {}".format(name)) if not code: - return smart_text(output).strip() + return smart_str(output).strip() known_paths = getattr(settings, "SA_LOOKUP_PATH", ("/usr/bin", )) for path in known_paths: bpath = os.path.join(path, name) @@ -207,7 +207,7 @@ def _learn(self, rcpt, msg, mtype): cmd, pinput=smart_bytes(msg), **self._learn_cmd_kwargs) if code in self._expected_exit_codes: return True - self.error = smart_text(output) + self.error = smart_str(output) return False def learn_spam(self, rcpt, msg): diff --git a/modoboa_amavis/management/commands/amnotify.py b/modoboa_amavis/management/commands/amnotify.py index eddf4db5..ffc29594 100644 --- a/modoboa_amavis/management/commands/amnotify.py +++ b/modoboa_amavis/management/commands/amnotify.py @@ -7,7 +7,7 @@ from django.core.management.base import BaseCommand from django.template.loader import render_to_string from django.urls import reverse -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from modoboa.admin.models import Domain from modoboa.core.models import User diff --git a/modoboa_amavis/models.py b/modoboa_amavis/models.py index 35da3ba7..5005f0d2 100644 --- a/modoboa_amavis/models.py +++ b/modoboa_amavis/models.py @@ -13,7 +13,7 @@ # Original Amavis version : 2.6.2 from django.db import models -from django.utils.translation import ugettext_lazy +from django.utils.translation import gettext_lazy class Maddr(models.Model): @@ -94,34 +94,34 @@ class Policy(models.Model): banned_files_lover = models.CharField(max_length=3, blank=True, null=True) bad_header_lover = models.CharField(max_length=3, blank=True, null=True) bypass_virus_checks = models.CharField( - ugettext_lazy("Virus filter"), default="", null=True, - choices=(("N", ugettext_lazy("yes")), - ("Y", ugettext_lazy("no")), - ("", ugettext_lazy("default"))), + gettext_lazy("Virus filter"), default="", null=True, + choices=(("N", gettext_lazy("yes")), + ("Y", gettext_lazy("no")), + ("", gettext_lazy("default"))), max_length=3, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Bypass virus checks or not. Choose 'default' to use global " "settings." ) ) bypass_spam_checks = models.CharField( - ugettext_lazy("Spam filter"), default="", null=True, - choices=(("N", ugettext_lazy("yes")), - ("Y", ugettext_lazy("no")), - ("", ugettext_lazy("default"))), + gettext_lazy("Spam filter"), default="", null=True, + choices=(("N", gettext_lazy("yes")), + ("Y", gettext_lazy("no")), + ("", gettext_lazy("default"))), max_length=3, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Bypass spam checks or not. Choose 'default' to use global " "settings." ) ) bypass_banned_checks = models.CharField( - ugettext_lazy("Banned filter"), default="", null=True, - choices=(("N", ugettext_lazy("yes")), - ("Y", ugettext_lazy("no")), - ("", ugettext_lazy("default"))), + gettext_lazy("Banned filter"), default="", null=True, + choices=(("N", gettext_lazy("yes")), + ("Y", gettext_lazy("no")), + ("", gettext_lazy("default"))), max_length=3, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Bypass banned checks or not. Choose 'default' to use global " "settings." ) @@ -186,9 +186,9 @@ class Policy(models.Model): spam_admin = models.CharField(max_length=192, blank=True, null=True) spam_subject_tag = models.CharField(max_length=192, blank=True, null=True) spam_subject_tag2 = models.CharField( - ugettext_lazy("Spam marker"), default=None, + gettext_lazy("Spam marker"), default=None, max_length=192, blank=True, null=True, - help_text=ugettext_lazy( + help_text=gettext_lazy( "Modify spam subject using the specified text. " "Choose 'default' to use global settings." ) diff --git a/modoboa_amavis/modo_extension.py b/modoboa_amavis/modo_extension.py index de37d15c..59418bcf 100644 --- a/modoboa_amavis/modo_extension.py +++ b/modoboa_amavis/modo_extension.py @@ -12,7 +12,7 @@ from __future__ import unicode_literals -from django.utils.translation import ugettext_lazy +from django.utils.translation import gettext_lazy from modoboa.admin.models import Domain from modoboa.core.extensions import ModoExtension, exts_pool @@ -25,16 +25,16 @@ class Amavis(ModoExtension): """The Amavis extension.""" name = "modoboa_amavis" - label = ugettext_lazy("Amavis frontend") + label = gettext_lazy("Amavis frontend") version = __version__ - description = ugettext_lazy("Simple amavis management frontend") + description = gettext_lazy("Simple amavis management frontend") url = "quarantine" available_for_topredirection = True def load(self): param_tools.registry.add("global", forms.ParametersForm, "Amavis") param_tools.registry.add( - "user", forms.UserSettings, ugettext_lazy("Quarantine")) + "user", forms.UserSettings, gettext_lazy("Quarantine")) def load_initial_data(self): """Create records for existing domains and co.""" diff --git a/modoboa_amavis/sql_connector.py b/modoboa_amavis/sql_connector.py index 2180a231..78316579 100644 --- a/modoboa_amavis/sql_connector.py +++ b/modoboa_amavis/sql_connector.py @@ -12,7 +12,7 @@ from .lib import cleanup_email_address, make_query_args from .models import Maddr, Msgrcpt, Quarantine from .utils import ( - ConvertFrom, fix_utf8_encoding, smart_bytes, smart_text + ConvertFrom, fix_utf8_encoding, smart_bytes, smart_str ) @@ -174,9 +174,9 @@ def fetch(self, start=None, stop=None): "from": cleanup_email_address( fix_utf8_encoding(qm["mail__from_addr"]) ), - "to": smart_text(qm["rid__email"]), + "to": smart_str(qm["rid__email"]), "subject": fix_utf8_encoding(qm["mail__subject"]), - "mailid": smart_text(qm["mail__mail_id"]), + "mailid": smart_str(qm["mail__mail_id"]), "date": datetime.datetime.fromtimestamp(qm["mail__time_num"]), "type": qm["content"], "score": qm["bspam_level"], @@ -240,6 +240,6 @@ def get_mail_content(self, mailid): ]) content = decode( content_bytes, "utf-8", - append_to_error=("; mail_id=%s" % smart_text(mailid)) + append_to_error=("; mail_id=%s" % smart_str(mailid)) ) return content diff --git a/modoboa_amavis/sql_email.py b/modoboa_amavis/sql_email.py index 429f8bb9..bc7f6b83 100644 --- a/modoboa_amavis/sql_email.py +++ b/modoboa_amavis/sql_email.py @@ -10,7 +10,7 @@ from modoboa.lib.email_utils import Email from .sql_connector import SQLconnector -from .utils import fix_utf8_encoding, smart_text +from .utils import fix_utf8_encoding, smart_str class SQLemail(Email): @@ -54,7 +54,7 @@ def body(self): h.images_to_alt = True mail_text = h.handle(self.contents["html"]) self.contents["plain"] = self._post_process_plain( - smart_text(mail_text)) + smart_str(mail_text)) self._body = self.viewmail_plain() self._body = fix_utf8_encoding(self._body) diff --git a/modoboa_amavis/templatetags/amavis_tags.py b/modoboa_amavis/templatetags/amavis_tags.py index d94adb7c..c2037a21 100644 --- a/modoboa_amavis/templatetags/amavis_tags.py +++ b/modoboa_amavis/templatetags/amavis_tags.py @@ -10,7 +10,7 @@ from django.template.loader import render_to_string from django.urls import reverse from django.utils.safestring import mark_safe -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from .. import constants, lib diff --git a/modoboa_amavis/tests/test_sql_email.py b/modoboa_amavis/tests/test_sql_email.py index 446e52f6..22d4b26f 100644 --- a/modoboa_amavis/tests/test_sql_email.py +++ b/modoboa_amavis/tests/test_sql_email.py @@ -5,7 +5,7 @@ import os from django.test import TestCase -from django.utils.encoding import smart_text +from django.utils.encoding import smart_str from ..sql_email import SQLemail @@ -44,7 +44,7 @@ def _get_expected_output(self, message_id, **kwargs): with open(message_path, "rb") as fp: # output should always be unicode (py2) or str (py3) - mail_text = smart_text(fp.read()) + mail_text = smart_str(fp.read()) return mail_text diff --git a/modoboa_amavis/tests/test_views.py b/modoboa_amavis/tests/test_views.py index 07f3a32e..3350fa17 100644 --- a/modoboa_amavis/tests/test_views.py +++ b/modoboa_amavis/tests/test_views.py @@ -14,7 +14,7 @@ from modoboa.core import models as core_models from modoboa.lib.tests import ModoTestCase from .. import factories -from ..utils import smart_text +from ..utils import smart_str class TestDataMixin(object): @@ -62,17 +62,17 @@ def test_index(self): msgrcpt = factories.create_virus("user@test.com") response = self.ajax_get("{}?msgtype=V".format(url)) self.assertIn( - "".format(smart_text(msgrcpt.mail.mail_id)), + "".format(smart_str(msgrcpt.mail.mail_id)), response["listing"]) self.assertNotIn( - "".format(smart_text(self.msgrcpt.mail.mail_id)), + "".format(smart_str(self.msgrcpt.mail.mail_id)), response["listing"]) def test_viewmail(self): """Test view_mail view.""" - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) url = reverse("modoboa_amavis:mail_detail", args=[mail_id]) - url = "{}?rcpt={}".format(url, smart_text(self.msgrcpt.rid.email)) + url = "{}?rcpt={}".format(url, smart_str(self.msgrcpt.rid.email)) response = self.ajax_get(url) self.assertIn("menu", response) content_url = reverse("modoboa_amavis:mailcontent_get", args=[mail_id]) @@ -91,29 +91,29 @@ def test_viewmail_selfservice(self): """Test view_mail in self-service mode.""" self.client.logout() - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) url = reverse("modoboa_amavis:mail_detail", args=[mail_id]) url = "{}?secret_id={}".format( - url, smart_text(self.msgrcpt.mail.secret_id)) + url, smart_str(self.msgrcpt.mail.secret_id)) response = self.client.get(url) self.assertEqual(response.status_code, 302) self.set_global_parameter("self_service", True) response = self.client.get(url) self.assertEqual(response.status_code, 404) - url = "{}&rcpt={}".format(url, smart_text(self.msgrcpt.rid.email)) + url = "{}&rcpt={}".format(url, smart_str(self.msgrcpt.rid.email)) response = self.client.get(url) self.assertEqual(response.status_code, 200) url = reverse("modoboa_amavis:mailcontent_get", args=[mail_id]) url = "{}?secret_id={}".format( - url, smart_text(self.msgrcpt.mail.secret_id)) + url, smart_str(self.msgrcpt.mail.secret_id)) response = self.client.get(url) self.assertEqual(response.status_code, 200) def test_viewheaders(self): """Test headers display.""" - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) url = reverse("modoboa_amavis:headers_detail", args=[mail_id]) response = self.client.get(url) self.assertContains(response, b"X-Spam-Flag: YES") @@ -125,9 +125,9 @@ def test_delete_msg(self): url = reverse("modoboa_amavis:_mail_list") response = self.ajax_get(url) - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) url = reverse("modoboa_amavis:mail_delete", args=[mail_id]) - data = {"rcpt": smart_text(self.msgrcpt.rid.email)} + data = {"rcpt": smart_str(self.msgrcpt.rid.email)} response = self.ajax_post(url, data) self.msgrcpt.refresh_from_db() self.assertEqual(self.msgrcpt.rs, "D") @@ -137,13 +137,13 @@ def test_delete_msg(self): def test_delete_selfservice(self): """Test delete view in self-service mode.""" self.client.logout() - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) url = reverse("modoboa_amavis:mail_delete", args=[mail_id]) url = "{}?secret_id={}".format( - url, smart_text(self.msgrcpt.mail.secret_id)) + url, smart_str(self.msgrcpt.mail.secret_id)) self.set_global_parameter("self_service", True) self.ajax_get(url, status=400) - url = "{}&rcpt={}".format(url, smart_text(self.msgrcpt.rid.email)) + url = "{}&rcpt={}".format(url, smart_str(self.msgrcpt.rid.email)) self.ajax_get(url) self.msgrcpt.refresh_from_db() self.assertEqual(self.msgrcpt.rs, "D") @@ -157,9 +157,9 @@ def test_release(self, mock_socket): response = self.ajax_get(url) mock_socket.return_value.recv.return_value = b"250 1234 Ok\r\n" - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) url = reverse("modoboa_amavis:mail_release", args=[mail_id]) - data = {"rcpt": smart_text(self.msgrcpt.rid.email)} + data = {"rcpt": smart_str(self.msgrcpt.rid.email)} response = self.ajax_post(url, data) self.msgrcpt.refresh_from_db() self.assertEqual(self.msgrcpt.rs, "R") @@ -171,7 +171,7 @@ def test_release_selfservice(self, mock_socket): """Test release view.""" mock_socket.return_value.recv.return_value = b"250 1234 Ok\r\n" self.client.logout() - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) base_url = reverse("modoboa_amavis:mail_release", args=[mail_id]) self.set_global_parameter("self_service", True) # Missing rcpt -> fails @@ -179,15 +179,15 @@ def test_release_selfservice(self, mock_socket): self.ajax_get(url, status=400) # Wrong secret_id -> fails url = "{}?secret_id=1234&rcpt={}".format( - base_url, smart_text(self.msgrcpt.rid.email)) + base_url, smart_str(self.msgrcpt.rid.email)) self.ajax_get(url, status=400) # Wrong rcpt -> fails url = "{}?secret_id=1234&rcpt=test@bad.com".format(base_url) self.ajax_get(url, status=400) # Request mode url = "{}?secret_id={}&rcpt={}".format( - base_url, smart_text(self.msgrcpt.mail.secret_id), - smart_text(self.msgrcpt.rid.email) + base_url, smart_str(self.msgrcpt.mail.secret_id), + smart_str(self.msgrcpt.rid.email) ) self.ajax_get(url) self.msgrcpt.refresh_from_db() @@ -207,9 +207,9 @@ def test_release_request(self): url = reverse("modoboa_amavis:_mail_list") response = self.ajax_get(url) - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) url = reverse("modoboa_amavis:mail_release", args=[mail_id]) - data = {"rcpt": smart_text(self.msgrcpt.rid.email)} + data = {"rcpt": smart_str(self.msgrcpt.rid.email)} response = self.ajax_post(url, data) self.msgrcpt.refresh_from_db() self.assertEqual(self.msgrcpt.rs, "p") @@ -221,9 +221,9 @@ def test_release_request(self): def _test_mark_message(self, action, status): """Mark message common code.""" - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) url = reverse("modoboa_amavis:mail_mark_as_" + action, args=[mail_id]) - data = {"rcpt": smart_text(self.msgrcpt.rid.email)} + data = {"rcpt": smart_str(self.msgrcpt.rid.email)} response = self.ajax_post(url, data) self.assertEqual( response["message"], "1 message processed successfully") @@ -245,8 +245,8 @@ def test_mark_as_ham(self): # Check domain level learning selection = "{} {}".format( - smart_text(self.msgrcpt.rid.email), - smart_text(self.msgrcpt.mail.mail_id)) + smart_str(self.msgrcpt.rid.email), + smart_str(self.msgrcpt.mail.mail_id)) self.set_global_parameter("domain_level_learning", True) url = reverse("modoboa_amavis:learning_recipient_set") url = "{}?type=ham&selection={}".format(url, selection) @@ -280,9 +280,9 @@ def test_manual_learning_as_user(self): """Test learning when connected as a simple user.""" user = core_models.User.objects.get(username="user@test.com") self.client.force_login(user) - mail_id = smart_text(self.msgrcpt.mail.mail_id) + mail_id = smart_str(self.msgrcpt.mail.mail_id) url = reverse("modoboa_amavis:mail_mark_as_ham", args=[mail_id]) - data = {"rcpt": smart_text(self.msgrcpt.rid.email)} + data = {"rcpt": smart_str(self.msgrcpt.rid.email)} response = self.ajax_post(url, data) self.assertEqual(response, {"status": "ok"}) self.set_global_parameter("user_level_learning", True) @@ -299,17 +299,17 @@ def test_process(self, mock_socket): url = reverse("modoboa_amavis:mail_process") selection = [ "{} {}".format( - smart_text(self.msgrcpt.rid.email), - smart_text(self.msgrcpt.mail.mail_id)), + smart_str(self.msgrcpt.rid.email), + smart_str(self.msgrcpt.mail.mail_id)), "{} {}".format( - smart_text(msgrcpt.rid.email), - smart_text(msgrcpt.mail.mail_id)), + smart_str(msgrcpt.rid.email), + smart_str(msgrcpt.mail.mail_id)), ] mock_socket.return_value.recv.side_effect = ( b"250 1234 Ok\r\n", b"250 1234 Ok\r\n") data = { "action": "release", - "rcpt": smart_text(self.msgrcpt.rid.email), + "rcpt": smart_str(self.msgrcpt.rid.email), "selection": ",".join(selection) } self.set_global_parameter("am_pdp_mode", "inet") @@ -329,7 +329,7 @@ def test_process(self, mock_socket): data = { "action": "delete", - "rcpt": smart_text(self.msgrcpt.rid.email), + "rcpt": smart_str(self.msgrcpt.rid.email), "selection": ",".join(selection) } response = self.ajax_post(url, data) diff --git a/modoboa_amavis/utils.py b/modoboa_amavis/utils.py index d967715b..024563e8 100644 --- a/modoboa_amavis/utils.py +++ b/modoboa_amavis/utils.py @@ -8,7 +8,7 @@ from django.db.models.expressions import Func from django.utils.encoding import ( smart_bytes as django_smart_bytes, smart_str as django_smart_str, - smart_text as django_smart_text + smart_str as django_smart_str ) @@ -22,7 +22,7 @@ Djangos smart_* functions don't work as expected, you must call `tobytes()` on the memoryview for them to work. -For convenience use smart_bytes and smart_text from this file in modoboa_amavis +For convenience use smart_bytes and smart_str from this file in modoboa_amavis to avoid any headaches. """ @@ -39,10 +39,10 @@ def smart_str(value, *args, **kwargs): return django_smart_str(value, *args, **kwargs) -def smart_text(value, *args, **kwargs): +def smart_str(value, *args, **kwargs): if isinstance(value, memoryview): value = value.tobytes() - return django_smart_text(value, *args, **kwargs) + return django_smart_str(value, *args, **kwargs) def fix_utf8_encoding(value): diff --git a/modoboa_amavis/views.py b/modoboa_amavis/views.py index b15ec888..5a9a2dc8 100644 --- a/modoboa_amavis/views.py +++ b/modoboa_amavis/views.py @@ -11,7 +11,7 @@ from django.shortcuts import render from django.template import loader from django.urls import reverse -from django.utils.translation import ugettext as _, ungettext +from django.utils.translation import gettext as _, ngettext from django.views.decorators.csrf import csrf_exempt from modoboa.admin.models import Domain, Mailbox @@ -29,7 +29,7 @@ from .sql_connector import SQLconnector from .sql_email import SQLemail from .templatetags.amavis_tags import quar_menu, viewm_menu -from .utils import smart_text +from .utils import smart_str def empty_quarantine(): @@ -240,7 +240,7 @@ def delete(request, mail_id): if valid_addresses and r not in valid_addresses: continue connector.set_msgrcpt_status(r, i, "D") - message = ungettext("%(count)d message deleted successfully", + message = ngettext("%(count)d message deleted successfully", "%(count)d messages deleted successfully", len(mail_id)) % {"count": len(mail_id)} return render_to_json_response({ @@ -260,7 +260,7 @@ def release_selfservice(request, mail_id): msgrcpt = connector.get_recipient_message(rcpt, mail_id) except Msgrcpt.DoesNotExist: raise BadRequest(_("Invalid request")) - if secret_id != smart_text(msgrcpt.mail.secret_id): + if secret_id != smart_str(msgrcpt.mail.secret_id): raise BadRequest(_("Invalid request")) if not param_tools.get_global_parameter("user_can_release"): connector.set_msgrcpt_status(rcpt, mail_id, "p") @@ -295,9 +295,9 @@ def release(request, mail_id): not param_tools.get_global_parameter("user_can_release"): for i, msgrcpt in msgrcpts: connector.set_msgrcpt_status( - smart_text(msgrcpt.rid.email), i, "p" + smart_str(msgrcpt.rid.email), i, "p" ) - message = ungettext("%(count)d request sent", + message = ngettext("%(count)d request sent", "%(count)d requests sent", len(mail_id)) % {"count": len(mail_id)} return render_to_json_response({ @@ -314,13 +314,13 @@ def release(request, mail_id): result = amr.sendreq(mid, mail.secret_id, rcpt.rid.email) if result: connector.set_msgrcpt_status( - smart_text(rcpt.rid.email), mid, "R") + smart_str(rcpt.rid.email), mid, "R") else: error = result break if not error: - message = ungettext("%(count)d message released successfully", + message = ngettext("%(count)d message released successfully", "%(count)d messages released successfully", len(mail_id)) % {"count": len(mail_id)} else: @@ -359,7 +359,7 @@ def mark_messages(request, selection, mtype, recipient_db=None): ) if saclient.error is None: saclient.done() - message = ungettext("%(count)d message processed successfully", + message = ngettext("%(count)d message processed successfully", "%(count)d messages processed successfully", len(selection)) % {"count": len(selection)} else: diff --git a/requirements.txt b/requirements.txt index 2af24ccd..4f1db3e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -modoboa>=2.0.0 chardet html2text idna diff --git a/test-requirements.txt b/test-requirements.txt index 3eae8223..f4359204 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,4 +1,4 @@ factory-boy>=2.4 testfixtures==7.1.0 -psycopg2>=2.5.4 -mysqlclient>=1.3.3 +psycopg[binary]>=3.1 +mysqlclient<2.1.2 diff --git a/test_project/test_project/urls.py b/test_project/test_project/urls.py index 136bd8a1..7e9306ee 100644 --- a/test_project/test_project/urls.py +++ b/test_project/test_project/urls.py @@ -1,7 +1,8 @@ from __future__ import unicode_literals -from django.conf.urls import include, url +from django.conf.urls import include +from django.urls import re_path urlpatterns = [ - url(r"", include("modoboa.urls")), + re_path(r"", include("modoboa.urls")), ]