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( - "