diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 4ff4bec52..b1c69ef2f 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,5 +1,9 @@
# Impacted versions
+* OS Type: Debian/Ubuntu, Centos/Redhat, Arch, Gentoo, Slack, ...
+* OS Version: Number or Name
+* Database Type: PostgreSQL/MySQL
+* Database version: X.y
* Modoboa: X.X.x
* installer used: Yes/No ?
* Webserver: Nginx/Apache
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..491deae0a
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,7 @@
+version: 2
+updates:
+- package-ecosystem: pip
+ directory: "/"
+ schedule:
+ interval: daily
+ open-pull-requests-limit: 10
diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 000000000..eacc2eb55
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,28 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 60
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 14
+# Issues with these labels will never be considered stale
+exemptLabels:
+ - pinned
+ - security
+ - bug
+ - dependencies
+ - enhancement
+ - looking-for-sponsors
+ - documentation
+# Label to use when marking an issue as stale
+staleLabel: stale
+# Set to true to ignore issues in a milestone (defaults to false)
+exemptMilestones: true
+# Set to true to ignore issues with an assignee (defaults to false)
+exemptAssignees: true
+# Limit to only `issues` or `pulls`
+only: issues
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs. Thank you
+ for your contributions.
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false
diff --git a/.github/workflows/modoboa.yml b/.github/workflows/modoboa.yml
new file mode 100644
index 000000000..cfd765302
--- /dev/null
+++ b/.github/workflows/modoboa.yml
@@ -0,0 +1,180 @@
+# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
+
+name: Modoboa App
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+env:
+ POSTGRES_HOST: localhost
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ services:
+ postgres:
+ image: postgres:11
+ env:
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ POSTGRES_DB: postgres
+ ports:
+ # will assign a random free host port
+ - 5432/tcp
+ # needed because the postgres container does not provide a healthcheck
+ options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
+ mysql:
+ image: mysql:8.0
+ env:
+ MYSQL_ROOT_PASSWORD: root
+ MYSQL_USER: modoboa
+ MYSQL_PASSWORD: modoboa
+ MYSQL_DATABASE: modoboa
+ ports:
+ - 3306/tcp
+ options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+ redis:
+ image: redis
+ ports:
+ - 6379/tcp
+ openldap:
+ image: docker.pkg.github.com/modoboa/docker-openldap/docker-openldap
+ ports:
+ - 389/tcp
+ env:
+ LDAP_DOMAIN: example.com
+ LDAP_ADMIN_PASSWORD: test
+ credentials:
+ username: tonioo
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ strategy:
+ matrix:
+ database: ['postgres', 'mysql']
+ python-version: [3.6, 3.7, 3.8]
+ fail-fast: false
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install dependencies
+ run: |
+ sudo apt-get install postfix librrd-dev rrdtool libldap2-dev libsasl2-dev libssl-dev
+ python -m pip install --upgrade pip
+ pip install -r requirements.txt
+ pip install -r ldap-requirements.txt
+ pip install -r test-requirements.txt
+ python setup.py develop
+ - name: Install postgres requirements
+ if: ${{ matrix.database == 'postgres' }}
+ run: |
+ pip install -r postgresql-requirements.txt
+ pip install coverage
+ echo "DB=postgres" >> $GITHUB_ENV
+ - name: Install mysql requirements
+ if: ${{ matrix.database == 'mysql' }}
+ run: |
+ pip install -r mysql-requirements.txt
+ echo "DB=mysql" >> $GITHUB_ENV
+ - name: Test with pytest
+ if: ${{ matrix.python-version != '3.8' || matrix.database != 'postgres' }}
+ run: |
+ python ./tests.py
+ cd test_project
+ python3 manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.transport modoboa.relaydomains modoboa.dnstools modoboa.ldapsync modoboa.policyd modoboa.maillog
+ 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
+ POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }} # get randomly assigned published port
+ MYSQL_HOST: 127.0.0.1
+ MYSQL_PORT: ${{ job.services.mysql.ports[3306] }} # get randomly assigned published port
+ MYSQL_USER: root
+ REDIS_PORT: ${{ job.services.redis.ports[6379] }}
+ LDAP_SERVER_PORT: ${{ job.services.openldap.ports[389] }}
+
+ - name: Test with pytest and coverage
+ if: ${{ matrix.python-version == '3.8' && matrix.database == 'postgres' }}
+ run: |
+ python ./tests.py
+ cd test_project
+ coverage run manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.transport modoboa.relaydomains modoboa.dnstools modoboa.ldapsync modoboa.policyd modoboa.maillog
+ coverage combine
+ 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
+ POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }} # get randomly assigned published port
+ MYSQL_HOST: 127.0.0.1
+ MYSQL_PORT: ${{ job.services.mysql.ports[3306] }} # get randomly assigned published port
+ MYSQL_USER: root
+ REDIS_PORT: ${{ job.services.redis.ports[6379] }}
+ LDAP_SERVER_PORT: ${{ job.services.openldap.ports[389] }}
+ - name: Upload coverage result
+ if: ${{ matrix.python-version == '3.8' }}
+ uses: actions/upload-artifact@v2
+ with:
+ name: coverage-results
+ path: test_project/.coverage
+
+ coverage:
+ needs: test
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.8'
+ - name: Install dependencies
+ run: |
+ pip install codecov
+ - name: Download coverage results
+ uses: actions/download-artifact@v2
+ with:
+ name: coverage-results
+ - name: Report coverage
+ run: |
+ coverage report
+ codecov
+
+ release:
+ needs: coverage
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.8'
+ - name: Install dependencies
+ run: |
+ sudo apt-get install librrd-dev rrdtool libssl-dev gettext
+ python -m pip install --upgrade pip setuptools wheel
+ pip install -r requirements.txt
+ cd modoboa
+ django-admin compilemessages
+ cd ..
+ python setup.py sdist bdist_wheel
+ - name: Publish to Test PyPI
+ if: endsWith(github.event.ref, '/master')
+ uses: pypa/gh-action-pypi-publish@master
+ with:
+ user: __token__
+ password: ${{ secrets.test_pypi_password }}
+ repository_url: https://test.pypi.org/legacy/
+ skip_existing: true
+ - name: Publish distribution to PyPI
+ if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release'
+ uses: pypa/gh-action-pypi-publish@master
+ with:
+ user: __token__
+ password: ${{ secrets.pypi_password }}
+ skip_existing: true
diff --git a/.mergify.yml b/.mergify.yml
new file mode 100644
index 000000000..803677c60
--- /dev/null
+++ b/.mergify.yml
@@ -0,0 +1,40 @@
+pull_request_rules:
+ - name: automatic merge on CI success and review
+ conditions:
+ - check-success=test (postgres, 3.7)
+ - "#approved-reviews-by>=1"
+ - "#review-requested=0"
+ - "#changes-requested-reviews-by=0"
+ - "#commented-reviews-by=0"
+ - label=ready-to-merge
+ actions:
+ merge:
+ method: squash
+ strict: true
+ strict_method: rebase
+ - name: delete head branch after merge
+ conditions: []
+ actions:
+ delete_head_branch: {}
+ - name: automatic merge for pyUp pull requests
+ conditions:
+ - author=pyup-bot
+ - check-success=test (postgres, 3.7)
+ - label=dependencies
+ - "#approved-reviews-by>=1"
+ actions:
+ merge:
+ method: squash
+ strict: true
+ strict_method: rebase
+ - name: automatic merge for dependabot pull requests
+ conditions:
+ - author~=^dependabot(|-preview)\[bot\]$
+ - check-success=test (postgres, 3.7)
+ - label=dependencies
+ - "#approved-reviews-by>=1"
+ actions:
+ merge:
+ method: squash
+ strict: true
+ strict_method: rebase
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 41ae74815..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,77 +0,0 @@
-language: python
-python:
- - '3.5'
- - '3.6'
- - '3.7'
- - '3.8'
-
-env:
- global:
- - PYTHONWARNINGS="default,ignore::PendingDeprecationWarning,ignore::ResourceWarning"
- matrix:
- - DB="POSTGRESQL"
- - DB="MYSQL"
-
-sudo: false
-
-addons:
- postgresql: '9.6'
- mysql: '5.5'
- apt:
- packages:
- - ldap-utils
- - postfix
- - slapd
- - librrd-dev
- - rrdtool
-
-cache: pip
-
-services:
- - postgres
- - mysql
- - redis-server
-
-before_install:
- - pip install codecov
-
-install:
- - pip install -r requirements.txt
- - pip install -r ldap-requirements.txt
- - pip install -r test-requirements.txt
- - if [ $DB = POSTGRESQL ]; then pip install -r postgresql-requirements.txt ; fi
- - if [ $DB = MYSQL ]; then pip install -r mysql-requirements.txt ; fi
- - python setup.py develop
-
-before_script:
- - mkdir /tmp/slapd
- - slapd -f test_data/slapd.conf -h ldap://localhost:3389 &
- - sleep 3
- - ldapadd -h localhost:3389 -D cn=admin,dc=example,dc=com -w test -f test_data/base.ldif
-
-script:
- - python ./tests.py
- - cd test_project
- - coverage run manage.py test modoboa.core modoboa.lib modoboa.admin modoboa.limits modoboa.transport modoboa.relaydomains modoboa.dnstools modoboa.ldapsync modoboa.policyd modoboa.maillog
-
-after_success:
- - coverage combine
- - coverage report
- - codecov
-
-before_deploy:
- - cd ../modoboa
- - django-admin compilemessages
- - cd ..
-
-deploy:
- provider: pypi
- user: tonio
- password:
- secure: SKFrH4cri56YQziqMZ4uvbh5Vto9YrQuBlYRnO9BwcjXbcp6jMpZ+9j+aytMdvcN+xG5of6DmgSIiZsLCOl4LDoPn2oPwWMzZA9T8knSVYt9v1uiBTwDmssIt9QH3wD/o3edpTsSoKw+O2cTT2GaE75+3aTK01+CX4ZsUtkGbTk=
- skip_cleanup: true
- distributions: sdist bdist_wheel
- on:
- tags: true
- python: '3.8'
- condition: "$DB = POSTGRESQL"
diff --git a/Dockerfile.dev b/Dockerfile.dev
index 8ecde4047..52eca494f 100644
--- a/Dockerfile.dev
+++ b/Dockerfile.dev
@@ -1,21 +1,23 @@
FROM python:3.6-alpine as base
MAINTAINER Antoine Nguyen
+ENV VIRTUAL_ENV=/opt/venv
+ENV PATH="$VIRTUAL_ENV/bin:$PATH"
-RUN apk add --update openssl python3-dev libffi-dev gcc musl-dev libxml2-dev libxslt-dev \
- libressl-dev jpeg-dev \
+FROM base as build
+
+RUN apk add --update openssl librrd python3-dev libffi-dev gcc musl-dev libxml2-dev libxslt-dev \
+ libressl-dev jpeg-dev rrdtool-dev file make \
&& rm -rf /var/cache/apk/*
-
+RUN python3 -m venv $VIRTUAL_ENV
WORKDIR /tmp
COPY requirements.txt /tmp
COPY test-requirements.txt /tmp
-RUN pip install --user -r requirements.txt --user -r test-requirements.txt
+RUN pip install -r requirements.txt -r test-requirements.txt
-FROM python:3.6-alpine
-COPY --from=base /root/.local /root/.local
-ENV PATH=/root/.local/bin:$PATH
-RUN apk add --no-cache --update libxml2-dev libxslt-dev\
+FROM base as run
+COPY --from=build $VIRTUAL_ENV $VIRTUAL_ENV
+RUN apk add --no-cache --update librrd libxslt libjpeg libressl git rrdtool \
&& rm -rf /var/cache/apk/*
-
RUN mkdir /code
WORKDIR /code
diff --git a/README.rst b/README.rst
index e5ec8a035..f04c87da1 100644
--- a/README.rst
+++ b/README.rst
@@ -2,7 +2,7 @@
`Modoboa `_
################################
-|travis| |codecov| |latest-version|
+|workflow| |codecov| |latest-version|
Modoboa is a mail hosting and management platform including a modern
and simplified Web User Interface. It provides useful components such
@@ -73,7 +73,6 @@ The following external libraries are provided with Modoboa:
.. |latest-version| image:: https://img.shields.io/pypi/v/modoboa.svg
:target: https://pypi.python.org/pypi/modoboa/
:alt: Latest version on Pypi
-.. |travis| image:: https://travis-ci.org/modoboa/modoboa.png?branch=master
- :target: https://travis-ci.org/modoboa/modoboa
+.. |workflow| image:: https://github.com/modoboa/modoboa/workflows/Modoboa%20App/badge.svg
.. |codecov| image:: http://codecov.io/github/modoboa/modoboa/coverage.svg?branch=master
:target: http://codecov.io/github/modoboa/modoboa?branch=master
diff --git a/doc/index.rst b/doc/index.rst
index 4ae989fcc..1a3a01322 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -53,10 +53,6 @@ It is extensible by nature and comes with a lot of additional extensions:
|`modoboa-sievefilters |A Sieve filters (rules) |https://modoboa-sievefilters.readthedocs.io |
|`_ |editor | |
+---------------------------------------------------------+-------------------------+------------------------------------------------------+
-|`modoboa-stats |Graphical statistics |https://modoboa-stats.readthedocs.io |
-|`_ |(message traffic and | |
-| |more) | |
-+---------------------------------------------------------+-------------------------+------------------------------------------------------+
|`modoboa-webmail |A simple webmail |https://modoboa-webmail.readthedocs.io |
|`_ | | |
+---------------------------------------------------------+-------------------------+------------------------------------------------------+
@@ -74,4 +70,3 @@ Table of contents
rest_api
contributing
contributors
-
diff --git a/doc/manual_installation/postfix.rst b/doc/manual_installation/postfix.rst
index 6d331addb..4d9d7e9ea 100644
--- a/doc/manual_installation/postfix.rst
+++ b/doc/manual_installation/postfix.rst
@@ -76,7 +76,7 @@ content to the :file:`/etc/postfix/main.cf` file::
smtpd_recipient_restrictions =
# ...
- check_policy_service inet:127.0.0.1:9999
+ check_policy_service inet:localhost:9999
# ...
And reload postfix.
diff --git a/docker-compose.yml b/docker-compose.yml
index 5630bfb11..e7009ea76 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -13,6 +13,6 @@ services:
PYTHONPATH: ".."
DB: "SQLITE"
DEBUG: "True"
- command: sh -c "cd test_project && python3 manage.py migrate && python3 manage.py load_initial_data && python3 manage.py runserver 0:8000"
+ command: sh -c "python3 setup.py develop && cd test_project && python3 manage.py migrate && python3 manage.py load_initial_data && python3 manage.py runserver 0:8000"
volumes:
- .:/code
diff --git a/modoboa/admin/api.py b/modoboa/admin/api.py
index d8b617069..29c4845de 100644
--- a/modoboa/admin/api.py
+++ b/modoboa/admin/api.py
@@ -5,6 +5,7 @@ from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _
from django_filters import rest_framework as dj_filters
+from drf_spectacular.utils import extend_schema, extend_schema_view
from rest_framework import filters, renderers, status, viewsets
from rest_framework.decorators import action
from rest_framework.exceptions import ParseError
@@ -19,17 +20,22 @@ from modoboa.lib import viewsets as lib_viewsets
from . import lib, models, serializers
+@extend_schema_view(
+ retrieve=extend_schema(
+ description="Retrieve a particular domain",
+ summary="Retrieve a particular domain"
+ ),
+ list=extend_schema(
+ description="Retrieve a list of domains",
+ summary="Retrieve a list of domains"
+ ),
+ create=extend_schema(
+ description="Create a new domain",
+ summary="Create a new domain"
+ )
+)
class DomainViewSet(viewsets.ModelViewSet):
- """
- retrieve:
- Return the given domain.
-
- list:
- Return a list of all existing domains.
-
- create:
- Create a new domain instance.
- """
+ """Domain viewset."""
permission_classes = [IsAuthenticated, DjangoModelPermissions, ]
serializer_class = serializers.DomainSerializer
diff --git a/modoboa/admin/forms/account.py b/modoboa/admin/forms/account.py
index ff5e1e0c7..f2ca42690 100644
--- a/modoboa/admin/forms/account.py
+++ b/modoboa/admin/forms/account.py
@@ -204,6 +204,11 @@ class AccountFormGeneral(forms.ModelForm):
account.set_password(self.cleaned_data["password2"])
account.save()
account.role = self.cleaned_data["role"]
+ if hasattr(account, "mailbox"):
+ # Update forward status according to account status
+ models.Alias.objects.filter(
+ address=account.email, internal=False).update(
+ enabled=account.is_active)
return account
diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py
index 01121f829..b83c17a89 100644
--- a/modoboa/admin/serializers.py
+++ b/modoboa/admin/serializers.py
@@ -8,6 +8,8 @@ from django.utils.encoding import force_text
from django.utils.translation import ugettext as _, ugettext_lazy
from rest_flex_fields import FlexFieldsModelSerializer
+from drf_spectacular.types import OpenApiTypes
+from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from modoboa.admin import models as admin_models
@@ -193,6 +195,7 @@ class AccountSerializer(serializers.ModelSerializer):
if not user.is_superuser:
del self.fields["master_user"]
+ @extend_schema_field(OpenApiTypes.STR)
def get_role(self, account):
"""Return role."""
return account.role
diff --git a/modoboa/admin/tests/test_account.py b/modoboa/admin/tests/test_account.py
index f5df49a44..4eb722c45 100644
--- a/modoboa/admin/tests/test_account.py
+++ b/modoboa/admin/tests/test_account.py
@@ -119,6 +119,29 @@ class AccountTestCase(ModoTestCase):
account = User.objects.get(username=values["username"])
self.assertEqual(account.language, "fr")
+ def test_forward_updated_on_edit(self):
+ account = User.objects.get(username="user@test.com")
+ forward = models.Alias.objects.create(address=account.email, internal=False)
+ values = {
+ "username": "user@test.com",
+ "role": "SimpleUsers",
+ "quota_act": True,
+ "is_active": False,
+ "email": "user@test.com",
+ "language": "en"
+ }
+ self.ajax_post(
+ reverse("admin:account_change", args=[account.id]), values
+ )
+ forward.refresh_from_db()
+ self.assertFalse(forward.enabled)
+ values["is_active"] = True
+ self.ajax_post(
+ reverse("admin:account_change", args=[account.id]), values
+ )
+ forward.refresh_from_db()
+ self.assertTrue(forward.enabled)
+
def test_aliases_update_on_rename(self):
"""Check if aliases are updated on mailbox rename."""
account = User.objects.get(username="user@test.com")
diff --git a/modoboa/admin/tests/test_user.py b/modoboa/admin/tests/test_user.py
index 312a6c804..346300cce 100644
--- a/modoboa/admin/tests/test_user.py
+++ b/modoboa/admin/tests/test_user.py
@@ -34,9 +34,22 @@ class ForwardTestCase(ModoTestCase):
domadmin = User.objects.get(username="admin@test.com")
self.assertTrue(domadmin.can_access(forward))
+ selfalias = Alias.objects.get(address="user@test.com", internal=True)
+ self.assertTrue(selfalias.enabled)
+
+ # Deactivate local copies
+ self.ajax_post(
+ url,
+ {"dest": "user@extdomain.com", "keepcopies": False}
+ )
+ selfalias.refresh_from_db()
+ self.assertFalse(selfalias.enabled)
+
# Now, empty forward
self.ajax_post(url, {"dest": "", "keepcopies": False})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
content = response.json()
self.assertNotIn("user@extdomain.com", content["content"])
+ selfalias.refresh_from_db()
+ self.assertTrue(selfalias.enabled)
diff --git a/modoboa/admin/views/user.py b/modoboa/admin/views/user.py
index 308cdf268..8edc6fa09 100644
--- a/modoboa/admin/views/user.py
+++ b/modoboa/admin/views/user.py
@@ -21,18 +21,34 @@ def forward(request, tplname="admin/forward.html"):
if request.method == "POST":
form = ForwardForm(request.POST)
if form.is_valid():
- if al is None:
- al = Alias.objects.create(
- address=mb.full_address, domain=mb.domain,
- enabled=mb.user.is_active)
recipients = form.cleaned_data["dest"]
- if form.cleaned_data["keepcopies"]:
- recipients.append(mb.full_address)
- al.set_recipients(recipients)
- if len(recipients) == 0:
- al.delete()
+ if not recipients:
+ Alias.objects.filter(
+ address=mb.full_address, internal=False).delete()
+ # Make sure internal self-alias is enabled
+ Alias.objects.filter(
+ address=mb.full_address, internal=True
+ ).update(enabled=True)
else:
- al.post_create(request.user)
+ if al is None:
+ al = Alias.objects.create(
+ address=mb.full_address,
+ domain=mb.domain,
+ enabled=mb.user.is_active
+ )
+ al.post_create(request.user)
+ if form.cleaned_data["keepcopies"]:
+ # Make sure internal self-alias is enabled
+ Alias.objects.filter(
+ address=mb.full_address, internal=True
+ ).update(enabled=True)
+ else:
+ # Deactivate internal self-alias to avoid storing
+ # local copies...
+ Alias.objects.filter(
+ address=mb.full_address, internal=True
+ ).update(enabled=False)
+ al.set_recipients(recipients)
return render_to_json_response(_("Forward updated"))
return render_to_json_response(
diff --git a/modoboa/core/commands/templates/settings.py.tpl b/modoboa/core/commands/templates/settings.py.tpl
index 9a54462ea..f7e619919 100644
--- a/modoboa/core/commands/templates/settings.py.tpl
+++ b/modoboa/core/commands/templates/settings.py.tpl
@@ -64,6 +64,7 @@ INSTALLED_APPS = (
'ckeditor_uploader',
'rest_framework',
'rest_framework.authtoken',
+ 'drf_spectacular',
'phonenumber_field',
'django_otp',
'django_otp.plugins.otp_totp',
@@ -190,6 +191,14 @@ REST_FRAMEWORK = {
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
+ 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
+}
+
+SPECTACULAR_SETTINGS = {
+ 'SCHEMA_PATH_PREFIX': r'/api/v1',
+ 'TITLE': 'Modoboa API',
+ 'VERSION': '1.0.0',
+ 'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAuthenticated'],
}
# Modoboa settings
diff --git a/modoboa/core/tests/test_api.py b/modoboa/core/tests/test_api.py
index ef1fe5b77..043222304 100644
--- a/modoboa/core/tests/test_api.py
+++ b/modoboa/core/tests/test_api.py
@@ -4,16 +4,13 @@ from modoboa.lib.tests import ModoTestCase
class OpenAPITestCase(ModoTestCase):
- openapi_schema_url = reverse_lazy('openapi_schema')
+ openapi_schema_url = reverse_lazy('schema')
def test_unauthorized(self):
self.client.logout()
- response = self.client.get("/docs/api/")
- self.assertRedirects(response, "/accounts/login/?next=/docs/api/")
-
response = self.client.get(self.openapi_schema_url)
- self.assertEqual(response.status_code, 403)
+ self.assertEqual(response.status_code, 401)
def test_get_schema(self):
self.assertEqual(self.openapi_schema_url, "/docs/openapi.json")
@@ -24,7 +21,3 @@ class OpenAPITestCase(ModoTestCase):
'title': "Modoboa API",
'version': "1.0.0",
})
-
- def test_get_swagger_ui(self):
- response = self.client.get("/docs/api/")
- self.assertContains(response, "Modoboa API")
diff --git a/modoboa/core/tests/test_ldap.py b/modoboa/core/tests/test_ldap.py
index a1b843319..bc666ccca 100644
--- a/modoboa/core/tests/test_ldap.py
+++ b/modoboa/core/tests/test_ldap.py
@@ -2,6 +2,7 @@
from unittest import skipIf
+from django.conf import settings
from django.test import override_settings
from django.urls import reverse
from django.utils.functional import cached_property
@@ -25,7 +26,7 @@ class LDAPTestCaseMixin(object):
"""Modify settings."""
self.set_global_parameters({
"authentication_type": "ldap",
- "ldap_server_port": 3389
+ "ldap_server_port": settings.LDAP_SERVER_PORT
})
def restore_user_password(self, username, new_password):
diff --git a/modoboa/core/viewsets.py b/modoboa/core/viewsets.py
index bd5a226db..a7305f0be 100644
--- a/modoboa/core/viewsets.py
+++ b/modoboa/core/viewsets.py
@@ -7,6 +7,7 @@ from django_otp.plugins.otp_static.models import StaticDevice, StaticToken
from django_otp.plugins.otp_totp.models import TOTPDevice
from rest_framework import permissions, response, viewsets
from rest_framework.decorators import action
+from drf_spectacular.utils import extend_schema
from modoboa.admin import serializers as admin_serializers
@@ -37,6 +38,9 @@ class AccountViewSet(viewsets.ViewSet):
)
return response.Response()
+ @extend_schema(
+ request=serializers.CheckTFASetupSerializer
+ )
@action(methods=["post"], detail=False, url_path="tfa/setup/check")
def tfa_setup_check(self, request):
"""Check TFA setup."""
@@ -66,6 +70,7 @@ class AccountViewSet(viewsets.ViewSet):
request.user.save()
return response.Response()
+ @extend_schema(tags=['account'])
@action(methods=["post"], detail=False, url_path="tfa/reset_codes")
def tfa_reset_codes(self, request, *args, **kwargs):
"""Reset recovery codes."""
diff --git a/modoboa/ldapsync/locale/nl_NL/LC_MESSAGES/django.po b/modoboa/ldapsync/locale/nl_NL/LC_MESSAGES/django.po
index 2fe38569f..0b7d94546 100644
--- a/modoboa/ldapsync/locale/nl_NL/LC_MESSAGES/django.po
+++ b/modoboa/ldapsync/locale/nl_NL/LC_MESSAGES/django.po
@@ -2,33 +2,37 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
-#
+#
+# Translators:
+# J Rosier , 2020
+#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-06 13:15+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
+"PO-Revision-Date: 2020-05-11 18:45+0000\n"
+"Last-Translator: J Rosier , 2020\n"
+"Language-Team: Dutch (Netherlands) (https://www.transifex.com/tonio/teams/13749/nl_NL/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Language: nl_NL\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ldapsync/lib.py:84
msgid "Failed to create LDAP account: {}"
-msgstr ""
+msgstr "Aanmaken LDAP account mislukt: {}"
#: ldapsync/lib.py:124
msgid "Failed to update LDAP account: {}"
-msgstr ""
+msgstr "Wijzigen LDAP account mislukt: {}"
#: ldapsync/lib.py:138
msgid "Failed to delete LDAP account: {}"
-msgstr ""
+msgstr "Verwijderen LDAP account mislukt: {}"
#: ldapsync/lib.py:149
msgid "Failed to disable LDAP account: {}"
-msgstr ""
+msgstr "Uitschakelen LDAP account mislukt: {}"
diff --git a/modoboa/ldapsync/locale/pl_PL/LC_MESSAGES/django.po b/modoboa/ldapsync/locale/pl_PL/LC_MESSAGES/django.po
index 2fe38569f..293d4df2c 100644
--- a/modoboa/ldapsync/locale/pl_PL/LC_MESSAGES/django.po
+++ b/modoboa/ldapsync/locale/pl_PL/LC_MESSAGES/django.po
@@ -2,33 +2,37 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
-#
+#
+# Translators:
+# Pedro M , 2020
+#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-06 13:15+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
+"PO-Revision-Date: 2020-05-11 18:45+0000\n"
+"Last-Translator: Pedro M , 2020\n"
+"Language-Team: Polish (Poland) (https://www.transifex.com/tonio/teams/13749/pl_PL/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Language: pl_PL\n"
+"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
#: ldapsync/lib.py:84
msgid "Failed to create LDAP account: {}"
-msgstr ""
+msgstr "Niepowodzenie w trakcie dodawania konta LDAP"
#: ldapsync/lib.py:124
msgid "Failed to update LDAP account: {}"
-msgstr ""
+msgstr "Niepowodzenie w trakcie aktualizacji konta LDAP"
#: ldapsync/lib.py:138
msgid "Failed to delete LDAP account: {}"
-msgstr ""
+msgstr "Niepowodzenie w trakcie usuwania konta LDAP"
#: ldapsync/lib.py:149
msgid "Failed to disable LDAP account: {}"
-msgstr ""
+msgstr "Niepowodzenie w trakcie wyłaczania konta LDAP"
diff --git a/modoboa/ldapsync/locale/sv/LC_MESSAGES/django.po b/modoboa/ldapsync/locale/sv/LC_MESSAGES/django.po
index c26d6d26b..52fd6442b 100644
--- a/modoboa/ldapsync/locale/sv/LC_MESSAGES/django.po
+++ b/modoboa/ldapsync/locale/sv/LC_MESSAGES/django.po
@@ -2,34 +2,37 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
-#
+#
+# Translators:
+# Olle Gustafsson , 2020
+#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-06 13:15+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
+"PO-Revision-Date: 2020-05-11 18:45+0000\n"
+"Last-Translator: Olle Gustafsson , 2020\n"
+"Language-Team: Swedish (https://www.transifex.com/tonio/teams/13749/sv/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Language: sv\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ldapsync/lib.py:84
msgid "Failed to create LDAP account: {}"
-msgstr ""
+msgstr "Misslyckades med att skapa LDAP konto: {}"
#: ldapsync/lib.py:124
msgid "Failed to update LDAP account: {}"
-msgstr ""
+msgstr "Misslyckades med att uppdatera LDAP konto: {}"
#: ldapsync/lib.py:138
msgid "Failed to delete LDAP account: {}"
-msgstr ""
+msgstr "Misslyckades med att radera LDAP konto: {}"
#: ldapsync/lib.py:149
msgid "Failed to disable LDAP account: {}"
-msgstr ""
+msgstr "Misslyckades med att inaktivera LDAP konto: {}"
diff --git a/modoboa/ldapsync/tests.py b/modoboa/ldapsync/tests.py
index 398de8603..0429cebdb 100644
--- a/modoboa/ldapsync/tests.py
+++ b/modoboa/ldapsync/tests.py
@@ -2,6 +2,7 @@
from unittest import skipIf
+from django.conf import settings
from django.core.management import call_command
from django.utils.encoding import force_bytes, force_str
@@ -23,7 +24,7 @@ class LDAPExportTestCase(ModoTestCase):
super().setUp()
self.set_global_parameters({
"ldap_enable_sync": True,
- "ldap_server_port": 3389,
+ "ldap_server_port": settings.LDAP_SERVER_PORT,
"ldap_sync_bind_dn": "cn=admin,dc=example,dc=com",
"ldap_sync_bind_password": "test",
"ldap_sync_account_dn_template": (
@@ -111,7 +112,7 @@ class LDAPImportTestCase(ModoTestCase):
super().setUp()
self.set_global_parameters({
"ldap_enable_import": True,
- "ldap_server_port": 3389,
+ "ldap_server_port": settings.LDAP_SERVER_PORT,
"ldap_sync_bind_dn": "cn=admin,dc=example,dc=com",
"ldap_sync_bind_password": "test",
"ldap_import_search_base": "ou=users,dc=example,dc=com",
diff --git a/modoboa/locale/en/LC_MESSAGES/django.po b/modoboa/locale/en/LC_MESSAGES/django.po
index 6fb2f1d25..96f737f68 100644
--- a/modoboa/locale/en/LC_MESSAGES/django.po
+++ b/modoboa/locale/en/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-05-06 13:15+0200\n"
+"POT-Creation-Date: 2020-11-05 15:36+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -520,46 +520,50 @@ msgstr ""
msgid "Dummy"
msgstr ""
-#: core/forms.py:21
+#: core/forms.py:23
msgid "Username"
msgstr ""
-#: core/forms.py:25
+#: core/forms.py:27
msgid "Password"
msgstr ""
-#: core/forms.py:38
+#: core/forms.py:40
msgid "Old password"
msgstr ""
-#: core/forms.py:42
+#: core/forms.py:44
msgid "New password"
msgstr ""
-#: core/forms.py:46
+#: core/forms.py:48
msgid "Confirmation"
msgstr ""
-#: core/forms.py:74
+#: core/forms.py:76
msgid "Old password mismatchs"
msgstr ""
-#: core/forms.py:83
+#: core/forms.py:85
msgid "Passwords mismatch"
msgstr ""
-#: core/forms.py:103
+#: core/forms.py:105
msgid "Enable API access"
msgstr ""
-#: core/forms.py:137
+#: core/forms.py:139
msgid "Verification code"
msgstr ""
-#: core/forms.py:148
+#: core/forms.py:150
msgid "Invalid code"
msgstr ""
+#: core/forms.py:163
+msgid "This code is invalid"
+msgstr ""
+
#: core/handlers.py:36
msgid "added"
msgstr ""
@@ -602,35 +606,35 @@ msgstr ""
msgid "[modoboa] Update(s) available"
msgstr ""
-#: core/models.py:55
+#: core/models.py:52
msgid "Allow mailboxes access"
msgstr ""
-#: core/models.py:57
+#: core/models.py:54
msgid "Allow this administrator to access user mailboxes"
msgstr ""
-#: core/models.py:60
+#: core/models.py:57
msgid "password"
msgstr ""
-#: core/models.py:63
+#: core/models.py:60
msgid "language"
msgstr ""
-#: core/models.py:66
+#: core/models.py:63
msgid "Prefered language to display pages."
msgstr ""
-#: core/models.py:70
+#: core/models.py:67
msgid "Phone number"
msgstr ""
-#: core/models.py:72
+#: core/models.py:69
msgid "Secondary email"
msgstr ""
-#: core/models.py:75
+#: core/models.py:72
msgid "An alternative e-mail address, can be used for recovery needs."
msgstr ""
@@ -692,6 +696,14 @@ msgstr[1] ""
msgid "Your password must contain a combination of different character types."
msgstr ""
+#: core/serializers.py:15
+msgid "Invalid PIN code"
+msgstr ""
+
+#: core/serializers.py:21
+msgid "2FA is already enabled"
+msgstr ""
+
#: core/sms_backends/ovh.py:19
msgid "API endpoint"
msgstr ""
@@ -920,6 +932,79 @@ msgstr ""
msgid "Click here to update your password"
msgstr ""
+#: core/templates/core/user_security.html:2 core/templatetags/core_tags.py:124
+msgid "Security"
+msgstr ""
+
+#: core/templates/core/user_security.html:6
+msgid "Two-Factor authentication"
+msgstr ""
+
+#: core/templates/core/user_security.html:17
+msgid ""
+"Install a soft token authenticator like FreeOTP or Google Authenticator from "
+"your application repository and use that app to scan this QR code."
+msgstr ""
+
+#: core/templates/core/user_security.html:25
+msgid "Pin code"
+msgstr ""
+
+#: core/templates/core/user_security.html:28
+msgid "Register"
+msgstr ""
+
+#: core/templates/core/user_security.html:33
+msgid ""
+"Congratulations! Two-Factor Authentication is now enabled for your account."
+msgstr ""
+
+#: core/templates/core/user_security.html:38
+#: core/templates/core/user_security.html:89
+msgid ""
+"The following recovery codes can be used one time each to let you regain "
+"access to your account, in case you lose your phone for example. Make sure "
+"to save them in a safe place, otherwise you won't be able to access your "
+"account anymore."
+msgstr ""
+
+#: core/templates/core/user_security.html:53
+msgid "Two-Factor Authentication is enabled for your account."
+msgstr ""
+
+#: core/templates/core/user_security.html:58
+msgid "Disable 2FA"
+msgstr ""
+
+#: core/templates/core/user_security.html:61
+msgid "Reset recovery codes"
+msgstr ""
+
+#: core/templates/core/user_security.html:65
+msgid ""
+"Two-Factor Authentication (2FA) is not yet activated for your account. "
+"Enabling this feature will increase your account's security."
+msgstr ""
+
+#: core/templates/core/user_security.html:73
+msgid "Enable 2FA"
+msgstr ""
+
+#: core/templates/core/user_security.html:83
+#: core/templates/core/user_security.html:99
+#: templates/common/generic_modal.html:6 templates/common/generic_modal.html:13
+#: templates/common/wizard_forms.html:6 templates/common/wizard_forms.html:27
+msgid "Close"
+msgstr ""
+
+#: core/templates/core/user_security.html:84
+msgid "New recovery codes"
+msgstr ""
+
+#: core/templates/core/user_security.html:88
+msgid "Here are your new recovery codes. "
+msgstr ""
+
#: core/templatetags/core_tags.py:34
msgid "Modoboa"
msgstr ""
@@ -928,7 +1013,7 @@ msgstr ""
msgid "Logout"
msgstr ""
-#: core/templatetags/core_tags.py:127
+#: core/templatetags/core_tags.py:131
msgid "API"
msgstr ""
@@ -940,51 +1025,55 @@ msgstr ""
msgid "Parameters saved"
msgstr ""
-#: core/views/auth.py:59
+#: core/views/auth.py:61
#, python-format
msgid "Password scheme mismatch. Updating %s password"
msgstr ""
-#: core/views/auth.py:66
+#: core/views/auth.py:68
#, python-format
msgid "Password hash parameter missmatch. Updating %s password"
msgstr ""
-#: core/views/auth.py:82
+#: core/views/auth.py:84
#, python-format
msgid "User '%s' successfully logged in"
msgstr ""
-#: core/views/auth.py:90
+#: core/views/auth.py:92
msgid "Your username and password didn't match. Please try again."
msgstr ""
-#: core/views/auth.py:125
+#: core/views/auth.py:127
msgid "User '{}' successfully logged out"
msgstr ""
-#: core/views/auth.py:170 core/views/auth.py:225
+#: core/views/auth.py:172 core/views/auth.py:227
msgid "Please use the following code to recover your Modoboa password: {}"
msgstr ""
-#: core/views/user.py:53
+#: core/views/user.py:60
msgid "Profile updated"
msgstr ""
-#: core/views/user.py:81
+#: core/views/user.py:88
msgid "Preferences saved"
msgstr ""
-#: core/views/user.py:103
+#: core/views/user.py:110
msgid "Access updated"
msgstr ""
+#: core/viewsets.py:52
+msgid "2FA is not enabled"
+msgstr ""
+
#: lib/db_utils.py:23
#, python-format
msgid "Connection to database %s not configured"
msgstr ""
-#: lib/email_utils.py:141 lib/email_utils.py:360
+#: lib/email_utils.py:143 lib/email_utils.py:362
msgid "unable to determine encoding of string"
msgstr ""
@@ -1068,11 +1157,6 @@ msgstr ""
msgid "Help:"
msgstr ""
-#: templates/common/generic_modal.html:6 templates/common/generic_modal.html:13
-#: templates/common/wizard_forms.html:6 templates/common/wizard_forms.html:27
-msgid "Close"
-msgstr ""
-
#: templates/common/wizard_forms.html:31
msgid "Previous"
msgstr ""
diff --git a/modoboa/locale/en/LC_MESSAGES/djangojs.po b/modoboa/locale/en/LC_MESSAGES/djangojs.po
index d4f7a4f01..263146b0a 100644
--- a/modoboa/locale/en/LC_MESSAGES/djangojs.po
+++ b/modoboa/locale/en/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-03-09 16:48+0100\n"
+"POT-Creation-Date: 2020-11-05 15:47+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -37,7 +37,7 @@ msgstr ""
msgid "Cancel"
msgstr ""
-#: static/js/global.js:342
+#: static/js/global.js:347
msgid "Internal error"
msgstr ""
@@ -52,3 +52,11 @@ msgstr ""
#: static/js/searchbar.js:28
msgid "Search..."
msgstr ""
+
+#: static/js/twocols_nav.js:170
+msgid "Are you sure?"
+msgstr ""
+
+#: static/js/twocols_nav.js:171
+msgid "This will invalidate your registered application."
+msgstr ""
diff --git a/modoboa/locale/fr/LC_MESSAGES/django.po b/modoboa/locale/fr/LC_MESSAGES/django.po
index b361b4306..745856a20 100644
--- a/modoboa/locale/fr/LC_MESSAGES/django.po
+++ b/modoboa/locale/fr/LC_MESSAGES/django.po
@@ -1,7 +1,7 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
-#
+#
# Translators:
# Antoine Nguyen , 2016-2017,2020
# Christophe CHAUVET , 2018
@@ -10,14 +10,14 @@ msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-05-06 13:15+0200\n"
-"PO-Revision-Date: 2020-05-06 11:19+0000\n"
+"POT-Creation-Date: 2020-11-05 15:36+0100\n"
+"PO-Revision-Date: 2020-11-05 15:49+0100\n"
"Last-Translator: Antoine Nguyen \n"
"Language-Team: French (http://www.transifex.com/tonio/modoboa/language/fr/)\n"
+"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: core/app_settings.py:26
@@ -46,7 +46,8 @@ msgstr "Méthode de chiffrement par défaut"
#: core/app_settings.py:56
msgid "Scheme used to crypt mailbox passwords"
-msgstr "Méthode utilisée pour chiffrer les mots de passe des boites aux lettres"
+msgstr ""
+"Méthode utilisée pour chiffrer les mots de passe des boites aux lettres"
#: core/app_settings.py:60
msgid "Rounds"
@@ -56,7 +57,9 @@ msgstr "Tours"
msgid ""
"Number of rounds to use (only used by sha256crypt and sha512crypt). Must be "
"between 1000 and 999999999, inclusive."
-msgstr "Nombre de tours à utiliser (sha256crypt et sha512crypt uniquement). Doit être compris entre 1000 et 999999999 inclue."
+msgstr ""
+"Nombre de tours à utiliser (sha256crypt et sha512crypt uniquement). Doit "
+"être compris entre 1000 et 999999999 inclue."
#: core/app_settings.py:69
msgid "Update password scheme at login"
@@ -64,7 +67,9 @@ msgstr "Mettre à jour la méthode de chiffrement du mot de passe à la connexio
#: core/app_settings.py:72
msgid "Update user password at login to use the default password scheme"
-msgstr "Mise à jour du mot de passe utilisateur lors d'une connexion pour utiliser la méthode de chiffrement par défaut"
+msgstr ""
+"Mise à jour du mot de passe utilisateur lors d'une connexion pour utiliser "
+"la méthode de chiffrement par défaut"
#: core/app_settings.py:77
msgid "Default password"
@@ -91,7 +96,10 @@ msgid ""
"The URL of an external page where users will be able to update their "
"password. It applies only to non local users, ie. those automatically "
"created after a successful external authentication (LDAP, SMTP)."
-msgstr "L'URL d'une page externe où les utilisateurs pourront mettre à jour leur mot de passe. Elle s'applique uniquement aux utilisateurs non locaux, c'est à dire ceux créés après une authentification externe réussie (LDAP, SMTP)."
+msgstr ""
+"L'URL d'une page externe où les utilisateurs pourront mettre à jour leur mot "
+"de passe. Elle s'applique uniquement aux utilisateurs non locaux, c'est à "
+"dire ceux créés après une authentification externe réussie (LDAP, SMTP)."
#: core/app_settings.py:104
msgid "Password recovery announcement"
@@ -99,7 +107,9 @@ msgstr "Annonce pour la récupération de mot de passe"
#: core/app_settings.py:109
msgid "A temporary message that will be displayed on the reset password page."
-msgstr "Un message temporaire qui sera affiché sur la page de récupération de mot de passe"
+msgstr ""
+"Un message temporaire qui sera affiché sur la page de récupération de mot de "
+"passe"
#: core/app_settings.py:115
msgid "Enable password recovery by SMS"
@@ -107,7 +117,9 @@ msgstr "Activer la récupération de mot de passe par SMS"
#: core/app_settings.py:118
msgid "Enable password recovery by SMS for users who filled a phone number."
-msgstr "Activer la récupération de mot de passe par SMS pour les utilisateurs ayant renseigné un numéro de téléphone"
+msgstr ""
+"Activer la récupération de mot de passe par SMS pour les utilisateurs ayant "
+"renseigné un numéro de téléphone"
#: core/app_settings.py:124
msgid "SMS provider"
@@ -115,7 +127,8 @@ msgstr "Fournisseur SMS"
#: core/app_settings.py:127
msgid "Choose a provider to send password recovery SMS"
-msgstr "Choisir un fournisseur pour envoyer les SMS de récupération de mot de passe"
+msgstr ""
+"Choisir un fournisseur pour envoyer les SMS de récupération de mot de passe"
#: core/app_settings.py:133
msgid "LDAP settings"
@@ -144,7 +157,9 @@ msgstr "Activer le serveur secondaire (secours)"
#: core/app_settings.py:152
msgid ""
"Enable a secondary LDAP server which will be used if the primary one fails"
-msgstr "Activer un serveur LDAP secondaire qui sera utilisé si le principal est en échec"
+msgstr ""
+"Activer un serveur LDAP secondaire qui sera utilisé si le principal est en "
+"échec"
#: core/app_settings.py:158
msgid "Secondary server address"
@@ -184,9 +199,12 @@ msgstr "Groupes administrateurs"
#: core/app_settings.py:190
msgid ""
-"Members of those LDAP Posix groups will be created as domain administrators."
-" Use ';' characters to separate groups."
-msgstr "Les membres de ces groupes LDAP (Posix) seront créés en tant qu'administrateurs de domaine. Utiliser des caractères ';' pour séparer les groupes."
+"Members of those LDAP Posix groups will be created as domain administrators. "
+"Use ';' characters to separate groups."
+msgstr ""
+"Les membres de ces groupes LDAP (Posix) seront créés en tant "
+"qu'administrateurs de domaine. Utiliser des caractères ';' pour séparer les "
+"groupes."
#: core/app_settings.py:197
msgid "Group type"
@@ -202,7 +220,9 @@ msgstr "Base de recherche des groupes"
#: core/app_settings.py:209
msgid "The distinguished name of the search base used to find groups"
-msgstr "Le nom distinctif de la base de recherche utilisée pour rechercher les groupes"
+msgstr ""
+"Le nom distinctif de la base de recherche utilisée pour rechercher les "
+"groupes"
#: core/app_settings.py:215
msgid "Password attribute"
@@ -240,7 +260,9 @@ msgstr "ND de connexion"
msgid ""
"The distinguished name to use when binding to the LDAP server. Leave empty "
"for an anonymous bind"
-msgstr "Le nom distinctif à utiliser pour les connections au serveur LDAP. Laisser vide pour une connexion anonyme"
+msgstr ""
+"Le nom distinctif à utiliser pour les connections au serveur LDAP. Laisser "
+"vide pour une connexion anonyme"
#: core/app_settings.py:243 core/app_settings.py:297
msgid "Bind password"
@@ -248,7 +270,9 @@ msgstr "Mot de passe de connexion"
#: core/app_settings.py:246 core/app_settings.py:300
msgid "The password to use when binding to the LDAP server (with 'Bind DN')"
-msgstr "Le mot de passe à utiliser pour les connections au serveur LDAP (avec 'ND de connexion')"
+msgstr ""
+"Le mot de passe à utiliser pour les connections au serveur LDAP (avec 'ND de "
+"connexion')"
#: core/app_settings.py:254 core/app_settings.py:346
msgid "Users search base"
@@ -256,7 +280,9 @@ msgstr "Base de recherche des utilisateurs"
#: core/app_settings.py:257 core/app_settings.py:349
msgid "The distinguished name of the search base used to find users"
-msgstr "Le nom distinctif de la base de recherche utilisée pour rechercher les utilisateurs"
+msgstr ""
+"Le nom distinctif de la base de recherche utilisée pour rechercher les "
+"utilisateurs"
#: core/app_settings.py:263 core/app_settings.py:355
msgid "Search filter"
@@ -266,7 +292,9 @@ msgstr "Filtre de recherche"
msgid ""
"An optional filter string (e.g. '(objectClass=person)'). In order to be "
"valid, it must be enclosed in parentheses."
-msgstr "Un filtre de recherche optionel (ex. '(objectClass=person)'). Pour être valide, il doit être entouré de parenthèses."
+msgstr ""
+"Un filtre de recherche optionel (ex. '(objectClass=person)'). Pour être "
+"valide, il doit être entouré de parenthèses."
#: core/app_settings.py:273
msgid "User DN template"
@@ -277,7 +305,9 @@ msgstr "Modèle de nom distinctif utilisateur"
msgid ""
"The template used to construct a user's DN. It should contain one "
"placeholder (ie. %(user)s)"
-msgstr "Le modèle utilisé pour construire le nom distinctif d'un utilisateur. Il doit contenir un espace réservé (ex. %(user)s)"
+msgstr ""
+"Le modèle utilisé pour construire le nom distinctif d'un utilisateur. Il "
+"doit contenir un espace réservé (ex. %(user)s)"
#: core/app_settings.py:284
msgid "LDAP synchronization settings"
@@ -290,7 +320,9 @@ msgstr "Activer l'export LDAP"
#: core/app_settings.py:311
msgid ""
"Enable automatic synchronization between local database and LDAP directory"
-msgstr "Activer la synchronisation automatique entre la base de données locale et l'annuaire LDAP"
+msgstr ""
+"Activer la synchronisation automatique entre la base de données locale et "
+"l'annuaire LDAP"
#: core/app_settings.py:317
msgid "Delete remote LDAP account when local account is deleted"
@@ -300,7 +332,9 @@ msgstr "Supprimer un compte LDAP distant lorsqu'un compte local est supprimé"
msgid ""
"Delete remote LDAP account when local account is deleted, otherwise it will "
"be disabled."
-msgstr "Supprimer un compte LDAP distant lorsqu'un compte local supprimé, autrement il sera désactivé."
+msgstr ""
+"Supprimer un compte LDAP distant lorsqu'un compte local supprimé, autrement "
+"il sera désactivé."
#: core/app_settings.py:327
msgid "Account DN template"
@@ -311,7 +345,9 @@ msgstr "Modèle des DN de compte"
msgid ""
"The template used to construct an account's DN. It should contain one "
"placeholder (ie. %(user)s)"
-msgstr "Le modèle utilisé pour construite un DN de compte (nom unique). Il doit contenir un emplacement (c'est à dire 1%(user)s)"
+msgstr ""
+"Le modèle utilisé pour construite un DN de compte (nom unique). Il doit "
+"contenir un emplacement (c'est à dire 1%(user)s)"
#: core/app_settings.py:337
msgid "Enable import from LDAP"
@@ -319,7 +355,9 @@ msgstr "Activer l'import LDAP"
#: core/app_settings.py:340
msgid "Enable account synchronization from LDAP directory to local database"
-msgstr "Activer la synchronisation des comptes entre un annuaire LDAP et la base de données locale"
+msgstr ""
+"Activer la synchronisation des comptes entre un annuaire LDAP et la base de "
+"données locale"
#: core/app_settings.py:365
msgid "Username attribute"
@@ -327,7 +365,8 @@ msgstr "Attribut nom d'utilisateur"
#: core/app_settings.py:368
msgid "The name of the LDAP attribute where the username can be found."
-msgstr "Le nom de l'attribut LDAP dans lequel le nom d'utilisateur peut être trouvé."
+msgstr ""
+"Le nom de l'attribut LDAP dans lequel le nom d'utilisateur peut être trouvé."
#: core/app_settings.py:373
msgid "Enable Dovecot LDAP sync"
@@ -335,7 +374,9 @@ msgstr "Activer la synchronisation LDAP pour Dovecot"
#: core/app_settings.py:376
msgid "LDAP authentication settings will be applied to Dovecot configuration."
-msgstr "Les paramètres d'authentification LDAP seront appliqués à la configuration de Dovecot."
+msgstr ""
+"Les paramètres d'authentification LDAP seront appliqués à la configuration "
+"de Dovecot."
#: core/app_settings.py:382
msgid "Dovecot LDAP config file"
@@ -344,7 +385,9 @@ msgstr "Fichier de configuration Dovecot pour LDAP"
#: core/app_settings.py:386
msgid ""
"Location of the configuration file which contains Dovecot LDAP settings."
-msgstr "Emplacement du fichier de configuration qui contient les paramètres LDAP de Dovecot"
+msgstr ""
+"Emplacement du fichier de configuration qui contient les paramètres LDAP de "
+"Dovecot"
#: core/app_settings.py:391 core/templates/core/dashboard.html:5
msgid "Dashboard"
@@ -356,7 +399,9 @@ msgstr "Flux RSS personnalisé"
#: core/app_settings.py:397
msgid "Display custom RSS feed to resellers and domain administrators"
-msgstr "Afficher un flux RSS personnalisé aux revendeurs et administrateurs de domaine"
+msgstr ""
+"Afficher un flux RSS personnalisé aux revendeurs et administrateurs de "
+"domaine"
#: core/app_settings.py:402
msgid "Hide features widget"
@@ -364,7 +409,9 @@ msgstr "Masquer le widget des fonctionnalités"
#: core/app_settings.py:405
msgid "Hide features widget for resellers and domain administrators"
-msgstr "Masquer le widget des fonctionnalités pour les revendeurs et les administrateurs de domaine"
+msgstr ""
+"Masquer le widget des fonctionnalités pour les revendeurs et les "
+"administrateurs de domaine"
#: core/app_settings.py:409
msgid "Notifications"
@@ -404,7 +451,9 @@ msgstr "Envoyer un email quand de nouvelles versions sont trouvées"
#: core/app_settings.py:439
msgid "Send an email to notify admins about new versions"
-msgstr "Envoyer un email pour notifier les administrateurs à propos des nouvelles versions"
+msgstr ""
+"Envoyer un email pour notifier les administrateurs à propos des nouvelles "
+"versions"
#: core/app_settings.py:443
msgid "Recipient"
@@ -420,7 +469,9 @@ msgstr "Envoyer des statistiques"
#: core/app_settings.py:454
msgid "Send statistics to Modoboa public API (counters and used extensions)"
-msgstr "Envoyer des statistiques à l'API publique de Modoboa (compteurs et extensions utilisées)"
+msgstr ""
+"Envoyer des statistiques à l'API publique de Modoboa (compteurs et "
+"extensions utilisées)"
#: core/app_settings.py:458
msgid "Miscellaneous"
@@ -432,9 +483,11 @@ msgstr "Seuil des comptes inactifs"
#: core/app_settings.py:464
msgid ""
-"An account with a last login date greater than this threshold (in days) will"
-" be considered as inactive"
-msgstr "Un compte avec une date de dernière connexion antérieure à ce seuil sera considéré comme inactif"
+"An account with a last login date greater than this threshold (in days) will "
+"be considered as inactive"
+msgstr ""
+"Un compte avec une date de dernière connexion antérieure à ce seuil sera "
+"considéré comme inactif"
#: core/app_settings.py:470
msgid "Top notifications check interval"
@@ -466,7 +519,8 @@ msgstr "Redirection par défaut"
#: core/app_settings.py:494
msgid "The default redirection used when no application is specified"
-msgstr "La redirection appliquée par défaut quand aucune application n'est spécifiée"
+msgstr ""
+"La redirection appliquée par défaut quand aucune application n'est spécifiée"
#: core/app_settings.py:548 core/app_settings.py:556 core/app_settings.py:564
msgid "Invalid syntax"
@@ -489,7 +543,10 @@ msgid ""
"You have USE_TZ set to False, this may result in issues during transitions "
"between summer/winter time (ie the same local time occuring twice due to "
"clock change)."
-msgstr "Vous avez défini USE_TZ à False, ce qui peut entraîner des problèmes lors des transitions entre l'heure d'été et l'heure d'hiver (c.-à-d. La même heure locale se produisant deux fois en raison d'un changement d'horloge)."
+msgstr ""
+"Vous avez défini USE_TZ à False, ce qui peut entraîner des problèmes lors "
+"des transitions entre l'heure d'été et l'heure d'hiver (c.-à-d. La même "
+"heure locale se produisant deux fois en raison d'un changement d'horloge)."
#: core/checks/settings_checks.py:9
msgid "Set `USE_TZ = True` in settings.py"
@@ -523,46 +580,50 @@ msgstr "Choisir un fournisseur"
msgid "Dummy"
msgstr "Test"
-#: core/forms.py:21
+#: core/forms.py:23
msgid "Username"
msgstr "Nom d'utilisateur"
-#: core/forms.py:25
+#: core/forms.py:27
msgid "Password"
msgstr "Mot de passe"
-#: core/forms.py:38
+#: core/forms.py:40
msgid "Old password"
msgstr "Ancien mot de passe"
-#: core/forms.py:42
+#: core/forms.py:44
msgid "New password"
msgstr "Nouveau mot de passe"
-#: core/forms.py:46
+#: core/forms.py:48
msgid "Confirmation"
msgstr "Confirmation"
-#: core/forms.py:74
+#: core/forms.py:76
msgid "Old password mismatchs"
msgstr "L'ancien mot de passe est incorrect"
-#: core/forms.py:83
+#: core/forms.py:85
msgid "Passwords mismatch"
msgstr "Les mots de passe diffèrent"
-#: core/forms.py:103
+#: core/forms.py:105
msgid "Enable API access"
msgstr "Activer l'accès API"
-#: core/forms.py:137
+#: core/forms.py:139
msgid "Verification code"
msgstr "Code de vérification"
-#: core/forms.py:148
+#: core/forms.py:150
msgid "Invalid code"
msgstr "Code incorrect"
+#: core/forms.py:163
+msgid "This code is invalid"
+msgstr "Ce code est incorrect"
+
#: core/handlers.py:36
msgid "added"
msgstr "ajouté"
@@ -605,41 +666,42 @@ msgstr "Une ou plusieurs mises à jour sont disponibles!"
msgid "[modoboa] Update(s) available"
msgstr "[modoboa] Mise(s) à jour disponible(s)"
-#: core/models.py:55
+#: core/models.py:52
msgid "Allow mailboxes access"
msgstr "Autoriser l'accès aux boites"
-#: core/models.py:57
+#: core/models.py:54
msgid "Allow this administrator to access user mailboxes"
msgstr "Autoriser cet administrateur à accéder aux boites des utilisateurs"
-#: core/models.py:60
+#: core/models.py:57
msgid "password"
msgstr "mot de passe"
-#: core/models.py:63
+#: core/models.py:60
msgid "language"
msgstr "langue"
-#: core/models.py:66
+#: core/models.py:63
msgid "Prefered language to display pages."
msgstr "Langage préféré pour afficher les pages"
-#: core/models.py:70
+#: core/models.py:67
msgid "Phone number"
msgstr "Numéro de téléphone"
-#: core/models.py:72
+#: core/models.py:69
msgid "Secondary email"
msgstr "Email secondaire"
-#: core/models.py:75
+#: core/models.py:72
msgid "An alternative e-mail address, can be used for recovery needs."
msgstr "Une adresse e-mail alternative ou de secours."
#: core/models.py:126
msgid "Failed to update password: LDAP module not installed"
-msgstr "Echec de la mise à jour du mot de passe : le module LDAP n'est pas installé"
+msgstr ""
+"Echec de la mise à jour du mot de passe : le module LDAP n'est pas installé"
#: core/models.py:155
msgid "account"
@@ -693,7 +755,17 @@ msgstr[1] "Le mot de passe doit contenir au moins {} caractères spéciaux."
#: core/password_validation.py:65
msgid "Your password must contain a combination of different character types."
-msgstr "Votre mot de passe doit contenir une combinaison de différents types de caractère."
+msgstr ""
+"Votre mot de passe doit contenir une combinaison de différents types de "
+"caractère."
+
+#: core/serializers.py:15
+msgid "Invalid PIN code"
+msgstr "Code PIN incorrect"
+
+#: core/serializers.py:21
+msgid "2FA is already enabled"
+msgstr "La 2FA est déjà activée"
#: core/sms_backends/ovh.py:19
msgid "API endpoint"
@@ -763,7 +835,9 @@ msgstr "Dernières nouvelles"
msgid ""
"Visit the official "
"weblog for more information."
-msgstr "Visitez le blog officiel pour plus d'information."
+msgstr ""
+"Visitez le blog "
+"officiel pour plus d'information."
#: core/templates/core/api_access.html:3
msgid "API access"
@@ -776,9 +850,11 @@ msgstr "Contrôlez votre accès à l'API de Modoboa"
#: core/templates/core/api_access.html:7
#, python-format
msgid ""
-"A documentation of the API is available here."
-msgstr "Une documentation de l'API est disponible ici."
+"A documentation of the API is available here."
+msgstr ""
+"Une documentation de l'API est disponible ici."
#: core/templates/core/api_access.html:14
msgid "API access token"
@@ -808,7 +884,8 @@ msgstr "Une ou plusieurs mises à jour sont disponibles!"
#: core/templates/core/information.html:6
msgid "Check the following list to find the component(s) that can be updated."
-msgstr "Consulter la liste suivante pour trouver le(s) composant(s) concerné(s)."
+msgstr ""
+"Consulter la liste suivante pour trouver le(s) composant(s) concerné(s)."
#: core/templates/core/information.html:10
msgid "Applications"
@@ -874,8 +951,12 @@ msgid ""
msgid_plural ""
"\n"
" The following updates are available:\n"
-msgstr[0] "\nLa mise à jour suivante est disponible :\n"
-msgstr[1] "\nLes mises à jour suivantes sont disponibles :\n"
+msgstr[0] ""
+"\n"
+"La mise à jour suivante est disponible :\n"
+msgstr[1] ""
+"\n"
+"Les mises à jour suivantes sont disponibles :\n"
#: core/templates/core/parameters.html:2 core/templatetags/core_tags.py:71
msgid "Parameters"
@@ -923,6 +1004,79 @@ msgstr "Mettez à jour vos informations personnelles"
msgid "Click here to update your password"
msgstr "Cliquez ici pour mettre à jour votre mot de passe"
+#: core/templates/core/user_security.html:2 core/templatetags/core_tags.py:124
+msgid "Security"
+msgstr "Sécurité"
+
+#: core/templates/core/user_security.html:6
+msgid "Two-Factor authentication"
+msgstr "Authentification à double facteur"
+
+#: core/templates/core/user_security.html:17
+msgid ""
+"Install a soft token authenticator like FreeOTP or Google Authenticator from "
+"your application repository and use that app to scan this QR code."
+msgstr "Installer une application de génération de mots de passe à usage unique comme FreeOTP ou Google Athenticator depuis votre magasin d'applications et utiliser le pour scanner ce code QR."
+
+#: core/templates/core/user_security.html:25
+msgid "Pin code"
+msgstr "Code PIN"
+
+#: core/templates/core/user_security.html:28
+msgid "Register"
+msgstr "Enregistrer"
+
+#: core/templates/core/user_security.html:33
+msgid ""
+"Congratulations! Two-Factor Authentication is now enabled for your account."
+msgstr "Félicitations ! L'authentification à double facteur est activée pour votre compte."
+
+#: core/templates/core/user_security.html:38
+#: core/templates/core/user_security.html:89
+msgid ""
+"The following recovery codes can be used one time each to let you regain "
+"access to your account, in case you lose your phone for example. Make sure "
+"to save them in a safe place, otherwise you won't be able to access your "
+"account anymore."
+msgstr "Les codes de récuparation suivants peuvent être utilisés une fois chacun pour récupérer l'accès à votre compte, par exemple si vous perdez votre téléphone. Assurez vous de les sauvegarder dans un endroit sûr, autrement vous ne pourrez plus accéder à votre compte."
+
+#: core/templates/core/user_security.html:53
+msgid "Two-Factor Authentication is enabled for your account."
+msgstr "L'authentication à double facteur est activée pour votre compte."
+
+#: core/templates/core/user_security.html:58
+msgid "Disable 2FA"
+msgstr "Désactiver la 2FA"
+
+#: core/templates/core/user_security.html:61
+msgid "Reset recovery codes"
+msgstr "Réinitialiser les codes de récupération"
+
+#: core/templates/core/user_security.html:65
+msgid ""
+"Two-Factor Authentication (2FA) is not yet activated for your account. "
+"Enabling this feature will increase your account's security."
+msgstr "L'authentification à double facteur (2FA) n'est pas encore activée pour votre compte. Activer cette fonctionnalité améliorera la sécurité de votre compte."
+
+#: core/templates/core/user_security.html:73
+msgid "Enable 2FA"
+msgstr "Activer la 2FA"
+
+#: core/templates/core/user_security.html:83
+#: core/templates/core/user_security.html:99
+#: templates/common/generic_modal.html:6 templates/common/generic_modal.html:13
+#: templates/common/wizard_forms.html:6 templates/common/wizard_forms.html:27
+msgid "Close"
+msgstr "Fermer"
+
+#: core/templates/core/user_security.html:84
+msgid "New recovery codes"
+msgstr "Nouveaux codes de récupération"
+
+#: core/templates/core/user_security.html:88
+msgid "Here are your new recovery codes. "
+msgstr "Voici vos nouveaux codes de récupération."
+
#: core/templatetags/core_tags.py:34
msgid "Modoboa"
msgstr "Modoboa"
@@ -931,7 +1085,7 @@ msgstr "Modoboa"
msgid "Logout"
msgstr "Déconnexion"
-#: core/templatetags/core_tags.py:127
+#: core/templatetags/core_tags.py:131
msgid "API"
msgstr "API"
@@ -943,51 +1097,61 @@ msgstr "Le coeur de Modoboa"
msgid "Parameters saved"
msgstr "Paramètres enregistrés"
-#: core/views/auth.py:59
+#: core/views/auth.py:61
#, python-format
msgid "Password scheme mismatch. Updating %s password"
-msgstr "La méthode de chiffrement du mot de passe diffère. Mise à jour du mot de passe de %s"
+msgstr ""
+"La méthode de chiffrement du mot de passe diffère. Mise à jour du mot de "
+"passe de %s"
-#: core/views/auth.py:66
+#: core/views/auth.py:68
#, python-format
msgid "Password hash parameter missmatch. Updating %s password"
msgstr ""
-#: core/views/auth.py:82
+#: core/views/auth.py:84
#, python-format
msgid "User '%s' successfully logged in"
msgstr "Utiliseur '%s' connecté avec succès"
-#: core/views/auth.py:90
+#: core/views/auth.py:92
msgid "Your username and password didn't match. Please try again."
-msgstr "Votre nom d'utilisateur et mot de passe ne correspondent pas. Veuillez réessayer."
+msgstr ""
+"Votre nom d'utilisateur et mot de passe ne correspondent pas. Veuillez "
+"réessayer."
-#: core/views/auth.py:125
+#: core/views/auth.py:127
msgid "User '{}' successfully logged out"
msgstr "Utilisateur '{}' déconnecté avec succès"
-#: core/views/auth.py:170 core/views/auth.py:225
+#: core/views/auth.py:172 core/views/auth.py:227
msgid "Please use the following code to recover your Modoboa password: {}"
-msgstr "Veuillez utiliser le code suivant pour récupérer votre mot de passe Modoboa : {}"
+msgstr ""
+"Veuillez utiliser le code suivant pour récupérer votre mot de passe "
+"Modoboa : {}"
-#: core/views/user.py:53
+#: core/views/user.py:60
msgid "Profile updated"
msgstr "Profil mis à jour"
-#: core/views/user.py:81
+#: core/views/user.py:88
msgid "Preferences saved"
msgstr "Préférences enregistrées"
-#: core/views/user.py:103
+#: core/views/user.py:110
msgid "Access updated"
msgstr "Accès mis à jour"
+#: core/viewsets.py:52
+msgid "2FA is not enabled"
+msgstr "La 2FA n'est pas activée"
+
#: lib/db_utils.py:23
#, python-format
msgid "Connection to database %s not configured"
msgstr "Connexion à la base de données %s non configurée"
-#: lib/email_utils.py:141 lib/email_utils.py:360
+#: lib/email_utils.py:143 lib/email_utils.py:362
msgid "unable to determine encoding of string"
msgstr "Impossible de déterminer l'encodage de la chaîne de caractères"
@@ -1071,12 +1235,6 @@ msgstr "Erreur :"
msgid "Help:"
msgstr "Aide :"
-#: templates/common/generic_modal.html:6
-#: templates/common/generic_modal.html:13 templates/common/wizard_forms.html:6
-#: templates/common/wizard_forms.html:27
-msgid "Close"
-msgstr "Fermer"
-
#: templates/common/wizard_forms.html:31
msgid "Previous"
msgstr "Précédent"
@@ -1107,7 +1265,9 @@ msgstr "Mot de passe oublié ?"
msgid ""
"Your password has been set. You may go ahead and sign "
"in now."
-msgstr "Votre mot de passe a été défini. Vous pouvez continuer et vous connecter dès maintenant."
+msgstr ""
+"Votre mot de passe a été défini. Vous pouvez continuer et vous connecter dès maintenant."
#: templates/registration/password_reset_confirm.html:7
msgid "Change password"
@@ -1117,7 +1277,10 @@ msgstr "Changer le mot de passe"
msgid ""
"The password reset link was invalid, possibly because it has already been "
"used. Please request a new password reset."
-msgstr "Le lien de réinitialisation du mot de passe était invalide, probablement parce qu'il a déjà été utilisé. Merci de demander une nouvelle réinitialisation de mot de passe."
+msgstr ""
+"Le lien de réinitialisation du mot de passe était invalide, probablement "
+"parce qu'il a déjà été utilisé. Merci de demander une nouvelle "
+"réinitialisation de mot de passe."
#: templates/registration/password_reset_confirm_code.html:20
msgid "Enter the code you've just received by SMS"
@@ -1133,20 +1296,29 @@ msgid ""
"exists with the email you entered. You should receive them shortly.
"
"
If you don't receive an email, please make sure you've entered your "
"primary address, and check your spam folder.
"
-msgstr "
Nous vous avons envoyé par email les instructions pour définir votre mot de passe, pour peu qu'un compte avec l'email que vous avez renseigné existe. Vous devriez les recevoir rapidement.
Si vous ne recevez pas d'email, merci de vérifier que vous avez renseigné votre adresse principale et de vérifier votre dossier spam.
"
+msgstr ""
+"
Nous vous avons envoyé par email les instructions pour définir votre mot "
+"de passe, pour peu qu'un compte avec l'email que vous avez renseigné existe. "
+"Vous devriez les recevoir rapidement.
Si vous ne recevez pas d'email, "
+"merci de vérifier que vous avez renseigné votre adresse principale et de "
+"vérifier votre dossier spam.
"
#: templates/registration/password_reset_email.html:4
#, python-format
msgid ""
"To initiate the password reset process for your %(username)s Modoboa "
"account, click the link below:"
-msgstr "Pour initier la procédure de réinitialisation du mot de passe de votre compte Modoboa %(username)s, cliquez sur le lien ci-dessous :"
+msgstr ""
+"Pour initier la procédure de réinitialisation du mot de passe de votre "
+"compte Modoboa %(username)s, cliquez sur le lien ci-dessous :"
#: templates/registration/password_reset_email.html:10
msgid ""
"If clicking the link above doesn't work, please copy and paste the URL in a "
"new browser window instead."
-msgstr "Si cliquer sur lien ci-dessous ne fonctionne pas, merci de copier / coller cette URL dans une nouvelle fenêtre navigateur à la place."
+msgstr ""
+"Si cliquer sur lien ci-dessous ne fonctionne pas, merci de copier / coller "
+"cette URL dans une nouvelle fenêtre navigateur à la place."
#: templates/registration/password_reset_email.html:14
msgid "Sincerely, The Modoboa Team."
diff --git a/modoboa/locale/fr/LC_MESSAGES/djangojs.po b/modoboa/locale/fr/LC_MESSAGES/djangojs.po
index 2574ef4e2..76b21d816 100644
--- a/modoboa/locale/fr/LC_MESSAGES/djangojs.po
+++ b/modoboa/locale/fr/LC_MESSAGES/djangojs.po
@@ -2,23 +2,22 @@
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
-#
+#
# Translators:
# Antoine Nguyen , 2020
-#
-#, fuzzy
+#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-03-09 16:48+0100\n"
-"PO-Revision-Date: 2020-05-11 18:45+0000\n"
+"POT-Creation-Date: 2020-11-05 15:47+0100\n"
+"PO-Revision-Date: 2020-11-05 15:48+0100\n"
"Last-Translator: Antoine Nguyen , 2020\n"
"Language-Team: French (https://www.transifex.com/tonio/teams/13749/fr/)\n"
+"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: fr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: bower_components/jquery-ui/jquery-ui.min.js:8
@@ -41,7 +40,7 @@ msgstr "Avertissement"
msgid "Cancel"
msgstr "Annuler"
-#: static/js/global.js:342
+#: static/js/global.js:347
msgid "Internal error"
msgstr "Erreur interne"
@@ -56,3 +55,11 @@ msgstr "Chercher"
#: static/js/searchbar.js:28
msgid "Search..."
msgstr "Chercher..."
+
+#: static/js/twocols_nav.js:170
+msgid "Are you sure?"
+msgstr "Etes vous certain ?"
+
+#: static/js/twocols_nav.js:171
+msgid "This will invalidate your registered application."
+msgstr "Cela annulera votre application enregistrée."
diff --git a/modoboa/locale/nl_NL/LC_MESSAGES/django.po b/modoboa/locale/nl_NL/LC_MESSAGES/django.po
index 6cf8fe3f6..251b31fcf 100644
--- a/modoboa/locale/nl_NL/LC_MESSAGES/django.po
+++ b/modoboa/locale/nl_NL/LC_MESSAGES/django.po
@@ -4,9 +4,10 @@
#
# Translators:
# Andrew , 2014
-# Chris de Gelder , 2017
+# ABC, 2017
# Fred Durst , 2019
# Harrie v.d. Vegt , 2016
+# J Rosier , 2020
# Peter van den Heuvel , 2017
# Remco de Hiep , 2017
# R Kloosterman , 2016
@@ -19,8 +20,8 @@ msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-06 13:15+0200\n"
-"PO-Revision-Date: 2020-05-06 11:16+0000\n"
-"Last-Translator: Antoine Nguyen \n"
+"PO-Revision-Date: 2020-10-03 09:44+0000\n"
+"Last-Translator: J Rosier \n"
"Language-Team: Dutch (Netherlands) (http://www.transifex.com/tonio/modoboa/language/nl_NL/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -68,7 +69,7 @@ msgstr "Nummer van te gebruiken randomizeringsrondes (alleen bij sha256crypt en
#: core/app_settings.py:69
msgid "Update password scheme at login"
-msgstr ""
+msgstr "Update wachtwoordschema bij inloggen"
#: core/app_settings.py:72
msgid "Update user password at login to use the default password scheme"
@@ -84,7 +85,7 @@ msgstr "Standaard wachtwoord voor automatisch gecreëerde accounts."
#: core/app_settings.py:84
msgid "Random password length"
-msgstr "Lengte willekeurig wachtwoord"
+msgstr "Lengte van willekeurig wachtwoord"
#: core/app_settings.py:88
msgid "Length of randomly generated passwords."
@@ -92,38 +93,38 @@ msgstr "Lengte van willekeurig gegenereerde wachtwoorden"
#: core/app_settings.py:92
msgid "Update password service URL"
-msgstr ""
+msgstr "Service-url voor wachtwoorden updaten"
#: core/app_settings.py:96
msgid ""
"The URL of an external page where users will be able to update their "
"password. It applies only to non local users, ie. those automatically "
"created after a successful external authentication (LDAP, SMTP)."
-msgstr ""
+msgstr "De URL van een externe pagina waar gebruikers hun wachtwoord kunnen bijwerken. Alleen van toepassing op niet-lokale gebruikers die automatisch worden aangemaakt na een succesvolle externe authenticatie (LDAP, SMTP)."
#: core/app_settings.py:104
msgid "Password recovery announcement"
-msgstr ""
+msgstr "Melding wachtwoordherstel"
#: core/app_settings.py:109
msgid "A temporary message that will be displayed on the reset password page."
-msgstr ""
+msgstr "Een tijdelijke melding die wordt getoond op de wachtwoordresetpagina."
#: core/app_settings.py:115
msgid "Enable password recovery by SMS"
-msgstr ""
+msgstr "Wachtwoordherstel via SMS activeren"
#: core/app_settings.py:118
msgid "Enable password recovery by SMS for users who filled a phone number."
-msgstr ""
+msgstr "Wachtwoordherstel via SMS activeren voor gebruikers die een telefoonnummer hebben ingevuld."
#: core/app_settings.py:124
msgid "SMS provider"
-msgstr ""
+msgstr "SMS provider"
#: core/app_settings.py:127
msgid "Choose a provider to send password recovery SMS"
-msgstr ""
+msgstr "Kies een provider voor het sturen van een SMS voor wachtwoordherstel."
#: core/app_settings.py:133
msgid "LDAP settings"
@@ -147,28 +148,28 @@ msgstr "Het TCP poortnummer van de LDAP-server"
#: core/app_settings.py:149
msgid "Enable secondary server (fallback)"
-msgstr ""
+msgstr "Secundaire server activeren (fallback)"
#: core/app_settings.py:152
msgid ""
"Enable a secondary LDAP server which will be used if the primary one fails"
-msgstr ""
+msgstr "Activeer secundaire LDAP server, te gebruiken als de primaire server wegvalt"
#: core/app_settings.py:158
msgid "Secondary server address"
-msgstr ""
+msgstr "Adres van secundaire server"
#: core/app_settings.py:161
msgid "The IP address or the DNS name of the seondary LDAP server"
-msgstr ""
+msgstr "Het IP adres of DNS naam van de secundaire LDAP server"
#: core/app_settings.py:165
msgid "Secondary server port"
-msgstr ""
+msgstr "secundaire-serverpoort"
#: core/app_settings.py:168
msgid "The TCP port number used by the LDAP secondary server"
-msgstr ""
+msgstr "TCP poortnummer van de secundaire LDAP server"
#: core/app_settings.py:172
msgid "Use a secured connection"
@@ -222,7 +223,7 @@ msgstr "Het attribuut dat gebruikt wordt om wachtwoorden op te slaan"
#: core/app_settings.py:222
msgid "LDAP authentication settings"
-msgstr ""
+msgstr "LDAP authenticatie-instellingen"
#: core/app_settings.py:225
msgid "Authentication method"
@@ -289,70 +290,70 @@ msgstr "Het template dat gebruikt wordt om een gebruiker zijn/haar DN op te bouw
#: core/app_settings.py:284
msgid "LDAP synchronization settings"
-msgstr ""
+msgstr "LDAP synchronisatie-instellingen"
#: core/app_settings.py:308
msgid "Enable export to LDAP"
-msgstr ""
+msgstr "Activeer export naar LDAP"
#: core/app_settings.py:311
msgid ""
"Enable automatic synchronization between local database and LDAP directory"
-msgstr ""
+msgstr "Activeer automatische synchronisatie tussen lokale database en LDAP directory"
#: core/app_settings.py:317
msgid "Delete remote LDAP account when local account is deleted"
-msgstr ""
+msgstr "Verwijder extern LDAP account als het lokale account wordt verwijderd"
#: core/app_settings.py:321
msgid ""
"Delete remote LDAP account when local account is deleted, otherwise it will "
"be disabled."
-msgstr ""
+msgstr "Verwijder extern LDAP account als het lokale account wordt verwijderd. Zo niet, dan wordt het gedeactiveerd."
#: core/app_settings.py:327
msgid "Account DN template"
-msgstr ""
+msgstr "Account DN sjabloon"
#: core/app_settings.py:330
#, python-format
msgid ""
"The template used to construct an account's DN. It should contain one "
"placeholder (ie. %(user)s)"
-msgstr ""
+msgstr "Het sjabloon voor het opbouwen van het DN (distinguishedname). Het moet een tijdelijke aanduiding (placeholder) bevatten (bijvoorbeeld %(user)s)"
#: core/app_settings.py:337
msgid "Enable import from LDAP"
-msgstr ""
+msgstr "Activeer import van LDAP"
#: core/app_settings.py:340
msgid "Enable account synchronization from LDAP directory to local database"
-msgstr ""
+msgstr "Activeer accountsynchronisatie van LDAP directory naar lokale database"
#: core/app_settings.py:365
msgid "Username attribute"
-msgstr ""
+msgstr "Gebruikersnaamattribuut"
#: core/app_settings.py:368
msgid "The name of the LDAP attribute where the username can be found."
-msgstr ""
+msgstr "De naam van het LDAP-attribuut waar de gebruikersnaam staat."
#: core/app_settings.py:373
msgid "Enable Dovecot LDAP sync"
-msgstr ""
+msgstr "Activeer Dovecot LDAP synchronisatie"
#: core/app_settings.py:376
msgid "LDAP authentication settings will be applied to Dovecot configuration."
-msgstr ""
+msgstr "LDAP authenticatie-instellingen worden toegepast op Dovecot-configuratie."
#: core/app_settings.py:382
msgid "Dovecot LDAP config file"
-msgstr ""
+msgstr "Dovecot LDAP configuratiebestand"
#: core/app_settings.py:386
msgid ""
"Location of the configuration file which contains Dovecot LDAP settings."
-msgstr ""
+msgstr "Locatie van het configuratiebestand met Dovecot LDAP-instellingen."
#: core/app_settings.py:391 core/templates/core/dashboard.html:5
msgid "Dashboard"
@@ -408,19 +409,19 @@ msgstr "Controleert automatisch of een nieuwere versie beschikbaar is"
#: core/app_settings.py:436
msgid "Send an email when new versions are found"
-msgstr ""
+msgstr "Stuur een e-mail als er nieuwe versies worden gevonden"
#: core/app_settings.py:439
msgid "Send an email to notify admins about new versions"
-msgstr ""
+msgstr "Stuur een e-mail naar administrators over nieuwe versies"
#: core/app_settings.py:443
msgid "Recipient"
-msgstr ""
+msgstr "Ontvanger"
#: core/app_settings.py:446
msgid "Recipient of new versions notification emails."
-msgstr ""
+msgstr "Ontvanger van notificatiemails over nieuwe versies"
#: core/app_settings.py:451
msgid "Send statistics"
@@ -525,11 +526,11 @@ msgstr "Nee"
#: core/constants.py:102
msgid "Choose a provider"
-msgstr ""
+msgstr "Kies een provider"
#: core/constants.py:107
msgid "Dummy"
-msgstr ""
+msgstr "Dummy"
#: core/forms.py:21
msgid "Username"
@@ -565,11 +566,11 @@ msgstr "Activeer API-toegang"
#: core/forms.py:137
msgid "Verification code"
-msgstr ""
+msgstr "Verificatiecode"
#: core/forms.py:148
msgid "Invalid code"
-msgstr ""
+msgstr "Ongeldige code"
#: core/handlers.py:36
msgid "added"
@@ -595,7 +596,7 @@ msgstr "verwijderd"
#: core/handlers.py:69
msgid "user {}"
-msgstr "gebruiker{}"
+msgstr "gebruiker {}"
#: core/handlers.py:71
msgid "management command"
@@ -611,7 +612,7 @@ msgstr "Er zijn een of meer updates beschikbaar"
#: core/management/commands/communicate_with_public_api.py:44
msgid "[modoboa] Update(s) available"
-msgstr ""
+msgstr "[modoboa] update(s) beschikbaar"
#: core/models.py:55
msgid "Allow mailboxes access"
@@ -705,47 +706,47 @@ msgstr "Uw wachtwoord moet verschillende tekens bevatten."
#: core/sms_backends/ovh.py:19
msgid "API endpoint"
-msgstr ""
+msgstr "API eindpunt"
#: core/sms_backends/ovh.py:22
msgid "OVH Europe"
-msgstr ""
+msgstr "OVH Europa"
#: core/sms_backends/ovh.py:23
msgid "OVH US"
-msgstr ""
+msgstr "OVH Verenigde Staten"
#: core/sms_backends/ovh.py:24
msgid "OVH North-America"
-msgstr ""
+msgstr "OVH Noord-Amerika"
#: core/sms_backends/ovh.py:25
msgid "So you Start Europe"
-msgstr ""
+msgstr "So you Start Europa"
#: core/sms_backends/ovh.py:26
msgid "So you Start North America"
-msgstr ""
+msgstr "So you Start Noord-Amerika"
#: core/sms_backends/ovh.py:27
msgid "Kimsufi Europe"
-msgstr ""
+msgstr "Kimsufi Europa"
#: core/sms_backends/ovh.py:28
msgid "Kimsufi North America"
-msgstr ""
+msgstr "Kimsufi Noord-Amerika"
#: core/sms_backends/ovh.py:35
msgid "Application key"
-msgstr ""
+msgstr "Applicatiesleutel"
#: core/sms_backends/ovh.py:42
msgid "Application secret"
-msgstr ""
+msgstr "Applicatiegeheim"
#: core/sms_backends/ovh.py:50
msgid "Consumer key"
-msgstr ""
+msgstr "Consumentensleutel"
#: core/templates/core/_current_features.html:6
msgid "Features looking for sponsoring"
@@ -882,8 +883,8 @@ msgid ""
msgid_plural ""
"\n"
" The following updates are available:\n"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "\n De volgende update is beschikbaar:\n"
+msgstr[1] "\nDe volgende updates zijn beschikbaar:\n"
#: core/templates/core/parameters.html:2 core/templatetags/core_tags.py:71
msgid "Parameters"
@@ -929,7 +930,7 @@ msgstr "Persoonlijke informatie aanpassen"
#: core/templates/core/user_profile.html:13
msgid "Click here to update your password"
-msgstr ""
+msgstr "Klik hier om je wachtwoord te wijzigen"
#: core/templatetags/core_tags.py:34
msgid "Modoboa"
@@ -959,7 +960,7 @@ msgstr "Wachtwoord schema matcht niet. Updating %s wachtwoord"
#: core/views/auth.py:66
#, python-format
msgid "Password hash parameter missmatch. Updating %s password"
-msgstr ""
+msgstr "Hashparameter van het wachtwoord klopt niet. Wachtwoord %s wijzigen"
#: core/views/auth.py:82
#, python-format
@@ -976,7 +977,7 @@ msgstr "Gebruiker '{}' is uitgelogd."
#: core/views/auth.py:170 core/views/auth.py:225
msgid "Please use the following code to recover your Modoboa password: {}"
-msgstr ""
+msgstr "Gebruik de volgende code om het Modoboa wachtwoord te herstellen: {}"
#: core/views/user.py:53
msgid "Profile updated"
@@ -1129,11 +1130,11 @@ msgstr "Deze wachtwoord herstel link is ongeldig, misschien is hij al gebruikt.
#: templates/registration/password_reset_confirm_code.html:20
msgid "Enter the code you've just received by SMS"
-msgstr ""
+msgstr "Voer SMS code in"
#: templates/registration/password_reset_confirm_code.html:27
msgid "Resend code"
-msgstr ""
+msgstr "Stuur de code opnieuw"
#: templates/registration/password_reset_done.html:7
msgid ""
@@ -1162,7 +1163,7 @@ msgstr "Met vriendelijke groet, Het Modoboa Team,"
#: templates/registration/password_reset_form.html:24
msgid "Please fill-in your primary email address"
-msgstr ""
+msgstr "Vul uw primaire e-mailadres in"
#: templates/registration/password_reset_subject.txt:3
msgid "Modoboa password reset"
diff --git a/modoboa/locale/nl_NL/LC_MESSAGES/djangojs.po b/modoboa/locale/nl_NL/LC_MESSAGES/djangojs.po
index ec3742949..929bffdf3 100644
--- a/modoboa/locale/nl_NL/LC_MESSAGES/djangojs.po
+++ b/modoboa/locale/nl_NL/LC_MESSAGES/djangojs.po
@@ -1,19 +1,21 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
#
# Translators:
-# R Kloosterman , 2016
-# TuxBrother , 2014
-# TuxBrother , 2014
+# Antoine Nguyen , 2020
+# J Rosier , 2020
+#
+#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: Modoboa\n"
+"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-09 16:48+0100\n"
-"PO-Revision-Date: 2020-03-09 15:49+0000\n"
-"Last-Translator: Antoine Nguyen \n"
-"Language-Team: Dutch (Netherlands) (http://www.transifex.com/tonio/modoboa/language/nl_NL/)\n"
+"PO-Revision-Date: 2020-05-11 18:45+0000\n"
+"Last-Translator: J Rosier , 2020\n"
+"Language-Team: Dutch (Netherlands) (https://www.transifex.com/tonio/teams/13749/nl_NL/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -26,7 +28,7 @@ msgstr "'"
#: bower_components/swagger-ui/dist/swagger-ui-bundle.js:133
msgid "operation"
-msgstr ""
+msgstr "bewerking"
#: core/static/core/js/settings.js:16
msgid "No more log entry to show"
diff --git a/modoboa/locale/pl_PL/LC_MESSAGES/djangojs.po b/modoboa/locale/pl_PL/LC_MESSAGES/djangojs.po
index 1dbda6226..c304efd6d 100644
--- a/modoboa/locale/pl_PL/LC_MESSAGES/djangojs.po
+++ b/modoboa/locale/pl_PL/LC_MESSAGES/djangojs.po
@@ -1,19 +1,21 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
#
# Translators:
-# sin88 , 2016
-# sin88 , 2016
-# Wojtek Gajda , 2018
+# Antoine Nguyen , 2020
+# Pedro M , 2020
+#
+#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: Modoboa\n"
+"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-09 16:48+0100\n"
-"PO-Revision-Date: 2020-03-09 15:49+0000\n"
-"Last-Translator: Antoine Nguyen \n"
-"Language-Team: Polish (Poland) (http://www.transifex.com/tonio/modoboa/language/pl_PL/)\n"
+"PO-Revision-Date: 2020-05-11 18:45+0000\n"
+"Last-Translator: Pedro M , 2020\n"
+"Language-Team: Polish (Poland) (https://www.transifex.com/tonio/teams/13749/pl_PL/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -26,7 +28,7 @@ msgstr "'"
#: bower_components/swagger-ui/dist/swagger-ui-bundle.js:133
msgid "operation"
-msgstr ""
+msgstr "działanie"
#: core/static/core/js/settings.js:16
msgid "No more log entry to show"
diff --git a/modoboa/locale/sv/LC_MESSAGES/django.po b/modoboa/locale/sv/LC_MESSAGES/django.po
index c78f2b9a1..83afea1e0 100644
--- a/modoboa/locale/sv/LC_MESSAGES/django.po
+++ b/modoboa/locale/sv/LC_MESSAGES/django.po
@@ -1,24 +1,23 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
-#
+#
# Translators:
-# Martin Persson , 2014
+# Martin Persson , 2014,2017
# Olle Gustafsson , 2013
-# Olle Gustafsson , 2013,2015-2017
+# Olle Gustafsson , 2013,2015-2018,2020
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-06 13:15+0200\n"
-"PO-Revision-Date: 2017-07-07 09:30+0000\n"
-"Last-Translator: Antoine Nguyen \n"
-"Language-Team: Swedish (http://www.transifex.com/tonio/modoboa/language/"
-"sv/)\n"
-"Language: sv\n"
+"PO-Revision-Date: 2020-10-02 18:56+0000\n"
+"Last-Translator: Olle Gustafsson \n"
+"Language-Team: Swedish (http://www.transifex.com/tonio/modoboa/language/sv/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Language: sv\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: core/app_settings.py:26
@@ -57,19 +56,15 @@ msgstr "Antal omgångar"
msgid ""
"Number of rounds to use (only used by sha256crypt and sha512crypt). Must be "
"between 1000 and 999999999, inclusive."
-msgstr ""
-"Antal omgångar att använda (används endast av sha256crypt och sha512crypt). "
-"Måste vara mellan 1000 och 999999999."
+msgstr "Antal omgångar att använda (används endast av sha256crypt och sha512crypt). Måste vara mellan 1000 och 999999999."
#: core/app_settings.py:69
-#, fuzzy
-#| msgid "Default password scheme"
msgid "Update password scheme at login"
-msgstr "Standard lösenords schema"
+msgstr "Uppdatera lösenordsschema vid inloggning"
#: core/app_settings.py:72
msgid "Update user password at login to use the default password scheme"
-msgstr ""
+msgstr "Uppdatera användarlösenord vid inloggning för att använda standard schema"
#: core/app_settings.py:77
msgid "Default password"
@@ -80,51 +75,47 @@ msgid "Default password for automatically created accounts."
msgstr "Standardlösenord för automatiskt skapade konton."
#: core/app_settings.py:84
-#, fuzzy
-#| msgid "Modoboa password reset"
msgid "Random password length"
-msgstr "Modoboa lösenordsåterställning."
+msgstr "Slumpmässig längd på lösenord"
#: core/app_settings.py:88
msgid "Length of randomly generated passwords."
-msgstr ""
+msgstr "Längd på slumpmässigt genererat lösenord"
#: core/app_settings.py:92
-#, fuzzy
-#| msgid "Default password scheme"
msgid "Update password service URL"
-msgstr "Standard lösenords schema"
+msgstr "Uppdatera lösenords service URL"
#: core/app_settings.py:96
msgid ""
"The URL of an external page where users will be able to update their "
"password. It applies only to non local users, ie. those automatically "
"created after a successful external authentication (LDAP, SMTP)."
-msgstr ""
+msgstr "Webbadressen till en extern sida där användare kan uppdatera sitt lösenord. Det gäller endast icke-lokala användare, dvs. de som skapas automatiskt efter en lyckad extern autentisering (LDAP, SMTP)."
#: core/app_settings.py:104
msgid "Password recovery announcement"
-msgstr ""
+msgstr "Meddelande om återställning av lösenord"
#: core/app_settings.py:109
msgid "A temporary message that will be displayed on the reset password page."
-msgstr ""
+msgstr "Ett tillfälligt meddelande som kommer att visas på sidan för återställning av lösenord."
#: core/app_settings.py:115
msgid "Enable password recovery by SMS"
-msgstr ""
+msgstr "Aktivera lösenordsåterställning via SMS"
#: core/app_settings.py:118
msgid "Enable password recovery by SMS for users who filled a phone number."
-msgstr ""
+msgstr "Aktivera återställning av lösenord via SMS för användare som fyllt i ett telefonnummer."
#: core/app_settings.py:124
msgid "SMS provider"
-msgstr ""
+msgstr "SMS provider"
#: core/app_settings.py:127
msgid "Choose a provider to send password recovery SMS"
-msgstr ""
+msgstr "Välj en leverantör för att skicka SMS för lösenordsåterställning"
#: core/app_settings.py:133
msgid "LDAP settings"
@@ -148,46 +139,36 @@ msgstr "TCP port som används av LDAP servern"
#: core/app_settings.py:149
msgid "Enable secondary server (fallback)"
-msgstr ""
+msgstr "Aktivera sekundär server (reserv)"
#: core/app_settings.py:152
msgid ""
"Enable a secondary LDAP server which will be used if the primary one fails"
-msgstr ""
+msgstr "Aktivera en sekundär LDAP-server som kommer att användas om den primära misslyckas"
#: core/app_settings.py:158
-#, fuzzy
-#| msgid "Server address"
msgid "Secondary server address"
-msgstr "Server adress"
+msgstr "Sekundär serveradress"
#: core/app_settings.py:161
-#, fuzzy
-#| msgid "The IP address or the DNS name of the LDAP server"
msgid "The IP address or the DNS name of the seondary LDAP server"
-msgstr "IP adressen eller DNS namn för LDAP server"
+msgstr "IP-adressen eller DNS-namnet på den sekundära LDAP-servern"
#: core/app_settings.py:165
-#, fuzzy
-#| msgid "Server port"
msgid "Secondary server port"
-msgstr "Server port"
+msgstr "Sekundär serverport"
#: core/app_settings.py:168
-#, fuzzy
-#| msgid "The TCP port number used by the LDAP server"
msgid "The TCP port number used by the LDAP secondary server"
-msgstr "TCP port som används av LDAP servern"
+msgstr "TCP-portnumret som används av LDAP sekundärserver"
#: core/app_settings.py:172
msgid "Use a secured connection"
msgstr "Använd en säker anslutning"
#: core/app_settings.py:176
-#, fuzzy
-#| msgid "Use an SSL/TLS connection to access the LDAP server"
msgid "Use an SSL/STARTTLS connection to access the LDAP server"
-msgstr "Använd SSL/TLS anslutning för åtkomst till LDAP servern"
+msgstr "Använd en SSL / STARTTLS-anslutning för att komma åt LDAP-servern"
#: core/app_settings.py:180
msgid "Active Directory"
@@ -203,11 +184,9 @@ msgstr "Administratörsgrupper"
#: core/app_settings.py:190
msgid ""
-"Members of those LDAP Posix groups will be created as domain administrators. "
-"Use ';' characters to separate groups."
-msgstr ""
-"Användare av dessa LDAP Posix grupper kommer skapas som "
-"domänadministratörer. Använd ';' för att separera grupper."
+"Members of those LDAP Posix groups will be created as domain administrators."
+" Use ';' characters to separate groups."
+msgstr "Användare av dessa LDAP Posix grupper kommer skapas som domänadministratörer. Använd ';' för att separera grupper."
#: core/app_settings.py:197
msgid "Group type"
@@ -234,10 +213,8 @@ msgid "The attribute used to store user passwords"
msgstr "Attribut som används för att lagra användarlösenord"
#: core/app_settings.py:222
-#, fuzzy
-#| msgid "Authentication method"
msgid "LDAP authentication settings"
-msgstr "Autentiseringsmetod"
+msgstr "LDAP autentiseringsinställningar"
#: core/app_settings.py:225
msgid "Authentication method"
@@ -263,9 +240,7 @@ msgstr "Bind DN"
msgid ""
"The distinguished name to use when binding to the LDAP server. Leave empty "
"for an anonymous bind"
-msgstr ""
-"Det unika namnet för att använda vid bindningar till LDAP-servern. Lämna "
-"tomt för en anonym bindning"
+msgstr "Det unika namnet för att använda vid bindningar till LDAP-servern. Lämna tomt för en anonym bindning"
#: core/app_settings.py:243 core/app_settings.py:297
msgid "Bind password"
@@ -273,8 +248,7 @@ msgstr "Bind lösenord"
#: core/app_settings.py:246 core/app_settings.py:300
msgid "The password to use when binding to the LDAP server (with 'Bind DN')"
-msgstr ""
-"Lösenordet ska användas vid bindning till LDAP-servern (med \"Bind DN ')"
+msgstr "Lösenordet ska användas vid bindning till LDAP-servern (med \"Bind DN ')"
#: core/app_settings.py:254 core/app_settings.py:346
msgid "Users search base"
@@ -292,9 +266,7 @@ msgstr "Sökfilter"
msgid ""
"An optional filter string (e.g. '(objectClass=person)'). In order to be "
"valid, it must be enclosed in parentheses."
-msgstr ""
-"En valfri filtersträng (t.ex. \"(objectClass = personer) '). För att vara "
-"giltigt måste det omges av parenteser."
+msgstr "En valfri filtersträng (t.ex. \"(objectClass = personer) '). För att vara giltigt måste det omges av parenteser."
#: core/app_settings.py:273
msgid "User DN template"
@@ -305,89 +277,74 @@ msgstr "Anvädar DN mall"
msgid ""
"The template used to construct a user's DN. It should contain one "
"placeholder (ie. %(user)s)"
-msgstr ""
-"Mallen som används för att konstruera en användares DN. Den bör innehålla en "
-"platshållare (dvs. %(user)s)"
+msgstr "Mallen som används för att konstruera en användares DN. Den bör innehålla en platshållare (dvs. %(user)s)"
#: core/app_settings.py:284
-#, fuzzy
-#| msgid "LDAP settings"
msgid "LDAP synchronization settings"
-msgstr "LDAP inställningar"
+msgstr "LDAP synkroniseringsinställningar"
#: core/app_settings.py:308
msgid "Enable export to LDAP"
-msgstr ""
+msgstr "Aktivera export till LDAP"
#: core/app_settings.py:311
msgid ""
"Enable automatic synchronization between local database and LDAP directory"
-msgstr ""
+msgstr "Aktivera automatisk synkronisering mellan lokal databas och LDAP-katalog"
#: core/app_settings.py:317
msgid "Delete remote LDAP account when local account is deleted"
-msgstr ""
+msgstr "Ta bort fjärran LDAP-konto när lokalt konto raderas"
#: core/app_settings.py:321
msgid ""
"Delete remote LDAP account when local account is deleted, otherwise it will "
"be disabled."
-msgstr ""
+msgstr "Radera LDAP-fjärrkonto när det lokala kontot raderas, annars inaktiveras det."
#: core/app_settings.py:327
-#, fuzzy
-#| msgid "User DN template"
msgid "Account DN template"
-msgstr "Anvädar DN mall"
+msgstr "DN-mall för kontot"
#: core/app_settings.py:330
-#, fuzzy, python-format
-#| msgid ""
-#| "The template used to construct a user's DN. It should contain one "
-#| "placeholder (ie. %(user)s)"
+#, python-format
msgid ""
"The template used to construct an account's DN. It should contain one "
"placeholder (ie. %(user)s)"
-msgstr ""
-"Mallen som används för att konstruera en användares DN. Den bör innehålla en "
-"platshållare (dvs. %(user)s)"
+msgstr "Mallen som används för att skapa ett kontos DN. Den ska innehålla en platshållare (ex.%(user)s)"
#: core/app_settings.py:337
msgid "Enable import from LDAP"
-msgstr ""
+msgstr "Aktiver import från LDAP"
#: core/app_settings.py:340
msgid "Enable account synchronization from LDAP directory to local database"
-msgstr ""
+msgstr "Aktivera kontosynkronisering från LDAP-katalog till lokal databas"
#: core/app_settings.py:365
-#, fuzzy
-#| msgid "Password attribute"
msgid "Username attribute"
-msgstr "Lösenordsattribut"
+msgstr "Attribut för användarnamn"
#: core/app_settings.py:368
msgid "The name of the LDAP attribute where the username can be found."
-msgstr ""
+msgstr "Namnet på LDAP-attributet där användarnamnet finns."
#: core/app_settings.py:373
-#, fuzzy
-#| msgid "Enable communication"
msgid "Enable Dovecot LDAP sync"
-msgstr "Aktivera kommunikation"
+msgstr "Aktivera Dovecot LDAP-synkronisering"
#: core/app_settings.py:376
msgid "LDAP authentication settings will be applied to Dovecot configuration."
-msgstr ""
+msgstr "LDAP-autentiseringsinställningar tillämpas på Dovecot-konfigurationen."
#: core/app_settings.py:382
msgid "Dovecot LDAP config file"
-msgstr ""
+msgstr "Dovecot LDAP-konfigurationsfil"
#: core/app_settings.py:386
msgid ""
"Location of the configuration file which contains Dovecot LDAP settings."
-msgstr ""
+msgstr "Plats för konfigurationsfilen som innehåller Dovecot LDAP-inställningar."
#: core/app_settings.py:391 core/templates/core/dashboard.html:5
msgid "Dashboard"
@@ -399,8 +356,7 @@ msgstr "Användardefinierat RSS-flöde"
#: core/app_settings.py:397
msgid "Display custom RSS feed to resellers and domain administrators"
-msgstr ""
-"Visa användardefinierat RSS-flöde för återförsäljare och domänadministratörer"
+msgstr "Visa användardefinierat RSS-flöde för återförsäljare och domänadministratörer"
#: core/app_settings.py:402
msgid "Hide features widget"
@@ -444,19 +400,19 @@ msgstr "Kontrollera automatiskt om en nyare version finns"
#: core/app_settings.py:436
msgid "Send an email when new versions are found"
-msgstr ""
+msgstr "Skicka ett mejl när nya versioner hittas"
#: core/app_settings.py:439
msgid "Send an email to notify admins about new versions"
-msgstr ""
+msgstr "Skicka ett e-postmeddelande för att meddela administratörer om nya versioner"
#: core/app_settings.py:443
msgid "Recipient"
-msgstr ""
+msgstr "Mottagare"
#: core/app_settings.py:446
msgid "Recipient of new versions notification emails."
-msgstr ""
+msgstr "Mottagare av e-postmeddelanden om nya versioner."
#: core/app_settings.py:451
msgid "Send statistics"
@@ -464,8 +420,7 @@ msgstr "Skicka statistik"
#: core/app_settings.py:454
msgid "Send statistics to Modoboa public API (counters and used extensions)"
-msgstr ""
-"Skicka statistik för Modoboas publika API (räknare och använda tillägg)"
+msgstr "Skicka statistik för Modoboas publika API (räknare och använda tillägg)"
#: core/app_settings.py:458
msgid "Miscellaneous"
@@ -473,13 +428,13 @@ msgstr "Övrigt"
#: core/app_settings.py:461
msgid "Inactive account threshold"
-msgstr ""
+msgstr "Gräns för inaktiva konton"
#: core/app_settings.py:464
msgid ""
-"An account with a last login date greater than this threshold (in days) will "
-"be considered as inactive"
-msgstr ""
+"An account with a last login date greater than this threshold (in days) will"
+" be considered as inactive"
+msgstr "Ett konto som inte har loggat in innan denna gräns (antal dagar) kommer anses som inaktivt"
#: core/app_settings.py:470
msgid "Top notifications check interval"
@@ -534,11 +489,11 @@ msgid ""
"You have USE_TZ set to False, this may result in issues during transitions "
"between summer/winter time (ie the same local time occuring twice due to "
"clock change)."
-msgstr ""
+msgstr "Du måste ange USE_TZ till False, detta kan leda till problem under övergångar mellan sommar / vintertid (dvs samma lokal tid som uppträder två gånger på grund av klockbyte)."
#: core/checks/settings_checks.py:9
msgid "Set `USE_TZ = True` in settings.py"
-msgstr ""
+msgstr "Ange `USE_TZ = True` i settings.py"
#: core/constants.py:6
msgid "Simple user"
@@ -562,11 +517,11 @@ msgstr "Nej"
#: core/constants.py:102
msgid "Choose a provider"
-msgstr ""
+msgstr "Välj leverantör"
#: core/constants.py:107
msgid "Dummy"
-msgstr ""
+msgstr "Dummy"
#: core/forms.py:21
msgid "Username"
@@ -601,16 +556,12 @@ msgid "Enable API access"
msgstr "Aktivera API access"
#: core/forms.py:137
-#, fuzzy
-#| msgid "Notifications"
msgid "Verification code"
-msgstr "Notifikationer"
+msgstr "Verifikationskod"
#: core/forms.py:148
-#, fuzzy
-#| msgid "Invalid line"
msgid "Invalid code"
-msgstr "Ogiltig rad"
+msgstr "Ogiltig kod"
#: core/handlers.py:36
msgid "added"
@@ -626,10 +577,9 @@ msgid "%(object)s '%(name)s' %(action)s by user %(user)s"
msgstr "%(object)s '%(name)s' %(action)s av användare %(user)s"
#: core/handlers.py:63
-#, fuzzy, python-format
-#| msgid "%(object)s '%(name)s' %(action)s by user %(user)s"
+#, python-format
msgid "%(object)s '%(name)s' %(action)s by "
-msgstr "%(object)s '%(name)s' %(action)s av användare %(user)s"
+msgstr "%(object)s '%(name)s' %(action)s av"
#: core/handlers.py:65
msgid "deleted"
@@ -637,11 +587,11 @@ msgstr "raderad"
#: core/handlers.py:69
msgid "user {}"
-msgstr ""
+msgstr "användare {}"
#: core/handlers.py:71
msgid "management command"
-msgstr ""
+msgstr "management kommando"
#: core/handlers.py:95
msgid "You can't delete your own account"
@@ -652,10 +602,8 @@ msgid "One or more updates are available"
msgstr "En eller fler uppdateringar finns tillgängliga"
#: core/management/commands/communicate_with_public_api.py:44
-#, fuzzy
-#| msgid "One or more updates are available"
msgid "[modoboa] Update(s) available"
-msgstr "En eller fler uppdateringar finns tillgängliga"
+msgstr "[modoboa] Uppdatering(ar) finns"
#: core/models.py:55
msgid "Allow mailboxes access"
@@ -749,51 +697,47 @@ msgstr "Ditt lösenord måste innehålla en kombination av olika typer av tecken
#: core/sms_backends/ovh.py:19
msgid "API endpoint"
-msgstr ""
+msgstr "API endpoint"
#: core/sms_backends/ovh.py:22
msgid "OVH Europe"
-msgstr ""
+msgstr "OVH Europe"
#: core/sms_backends/ovh.py:23
msgid "OVH US"
-msgstr ""
+msgstr "OVH US"
#: core/sms_backends/ovh.py:24
msgid "OVH North-America"
-msgstr ""
+msgstr "OVH Nordamerika"
#: core/sms_backends/ovh.py:25
msgid "So you Start Europe"
-msgstr ""
+msgstr "So you Start Europa"
#: core/sms_backends/ovh.py:26
msgid "So you Start North America"
-msgstr ""
+msgstr "So you Start Nordamerika"
#: core/sms_backends/ovh.py:27
msgid "Kimsufi Europe"
-msgstr ""
+msgstr "Kimsufi Europa"
#: core/sms_backends/ovh.py:28
msgid "Kimsufi North America"
-msgstr ""
+msgstr "Kimsufi Nordamerika"
#: core/sms_backends/ovh.py:35
-#, fuzzy
-#| msgid "Applications"
msgid "Application key"
-msgstr "Applikationer"
+msgstr "Applikationsnyckel"
#: core/sms_backends/ovh.py:42
-#, fuzzy
-#| msgid "Applications"
msgid "Application secret"
-msgstr "Applikationer"
+msgstr "Applikationshemlighet"
#: core/sms_backends/ovh.py:50
msgid "Consumer key"
-msgstr ""
+msgstr "Konsumentnyckel"
#: core/templates/core/_current_features.html:6
msgid "Features looking for sponsoring"
@@ -819,9 +763,7 @@ msgstr "Senaste nytt"
msgid ""
"Visit the official "
"weblog for more information."
-msgstr ""
-"Besök den officiella "
-"bloggen för mer information."
+msgstr "Besök den officiella bloggen för mer information."
#: core/templates/core/api_access.html:3
msgid "API access"
@@ -834,11 +776,9 @@ msgstr "Kontrollera din åtskomst till Modoboa API"
#: core/templates/core/api_access.html:7
#, python-format
msgid ""
-"A documentation of the API is available here."
-msgstr ""
-"Dokumentation över API:et finns här."
+"A documentation of the API is available here."
+msgstr "Dokumentation över API:et finns här."
#: core/templates/core/api_access.html:14
msgid "API access token"
@@ -868,8 +808,7 @@ msgstr "En eller flera uppdateringar finns tillgängliga!"
#: core/templates/core/information.html:6
msgid "Check the following list to find the component(s) that can be updated."
-msgstr ""
-"Kontrollera följande lista för att se de komponenter som kan uppdateras."
+msgstr "Kontrollera följande lista för att se de komponenter som kan uppdateras."
#: core/templates/core/information.html:10
msgid "Applications"
@@ -929,16 +868,14 @@ msgid "Message"
msgstr "Meddelande"
#: core/templates/core/notifications/update_available.html:3
-#, fuzzy
-#| msgid "One or more updates are available"
msgid ""
"\n"
" The following update is available:\n"
msgid_plural ""
"\n"
" The following updates are available:\n"
-msgstr[0] "En eller fler uppdateringar finns tillgängliga"
-msgstr[1] "En eller fler uppdateringar finns tillgängliga"
+msgstr[0] "\n Följande uppdateringar finns tillgängliga:\n"
+msgstr[1] "\n Följande uppdateringar finns tillgängliga:\n"
#: core/templates/core/parameters.html:2 core/templatetags/core_tags.py:71
msgid "Parameters"
@@ -983,10 +920,8 @@ msgid "Update your personal information"
msgstr "Uppdatera personlig information"
#: core/templates/core/user_profile.html:13
-#, fuzzy
-#| msgid "Failed to update password: {}"
msgid "Click here to update your password"
-msgstr "Misslyckades med att uppdatera lösenord: {}"
+msgstr "Klicka här för att uppdatera ditt lösenord"
#: core/templatetags/core_tags.py:34
msgid "Modoboa"
@@ -1011,12 +946,12 @@ msgstr "Parametrar sparade"
#: core/views/auth.py:59
#, python-format
msgid "Password scheme mismatch. Updating %s password"
-msgstr ""
+msgstr "Fel lösenordsschema. Uppdaterar %s lösenord"
#: core/views/auth.py:66
#, python-format
msgid "Password hash parameter missmatch. Updating %s password"
-msgstr ""
+msgstr "Fel lösenordshash. Uppdaterar %s lösenord"
#: core/views/auth.py:82
#, python-format
@@ -1033,7 +968,7 @@ msgstr "Användare '{}' loggade ut framgångsrikt"
#: core/views/auth.py:170 core/views/auth.py:225
msgid "Please use the following code to recover your Modoboa password: {}"
-msgstr ""
+msgstr "Använd följande kod för att återställa ditt Modoboa lösenord: {}"
#: core/views/user.py:53
msgid "Profile updated"
@@ -1054,7 +989,7 @@ msgstr "Anslutning till databasen %s är inte konfigurerad"
#: lib/email_utils.py:141 lib/email_utils.py:360
msgid "unable to determine encoding of string"
-msgstr ""
+msgstr "kan inte bestämma kodning av sträng"
#: lib/exceptions.py:67
#, python-format
@@ -1136,8 +1071,9 @@ msgstr "Fel:"
msgid "Help:"
msgstr "Hjälp:"
-#: templates/common/generic_modal.html:6 templates/common/generic_modal.html:13
-#: templates/common/wizard_forms.html:6 templates/common/wizard_forms.html:27
+#: templates/common/generic_modal.html:6
+#: templates/common/generic_modal.html:13 templates/common/wizard_forms.html:6
+#: templates/common/wizard_forms.html:27
msgid "Close"
msgstr "Stäng"
@@ -1171,9 +1107,7 @@ msgstr "Glömt ditt lösenord?"
msgid ""
"Your password has been set. You may go ahead and sign "
"in now."
-msgstr ""
-"Ditt lösenord har ändrats. Du kan fortsätta med att logga in."
+msgstr "Ditt lösenord har ändrats. Du kan fortsätta med att logga in."
#: templates/registration/password_reset_confirm.html:7
msgid "Change password"
@@ -1183,17 +1117,15 @@ msgstr "Ändra lösenord"
msgid ""
"The password reset link was invalid, possibly because it has already been "
"used. Please request a new password reset."
-msgstr ""
-"Lösenordets återställningslänk är ogiltig, förmodligen för att den redan "
-"använts. Var vänlig begär en ny återställning av lösenord."
+msgstr "Lösenordets återställningslänk är ogiltig, förmodligen för att den redan använts. Var vänlig begär en ny återställning av lösenord."
#: templates/registration/password_reset_confirm_code.html:20
msgid "Enter the code you've just received by SMS"
-msgstr ""
+msgstr "Ange koden du precis fick via SMS"
#: templates/registration/password_reset_confirm_code.html:27
msgid "Resend code"
-msgstr ""
+msgstr "Skicka kod igen"
#: templates/registration/password_reset_done.html:7
msgid ""
@@ -1201,28 +1133,20 @@ msgid ""
"exists with the email you entered. You should receive them shortly. "
"
If you don't receive an email, please make sure you've entered your "
"primary address, and check your spam folder.
"
-msgstr ""
-"
Vi har e-postat dig instruktioner för att ändra ditt lösenord, om ett "
-"konto finns med den e-post adress du angav. Du borde få detta brev inom kort."
-"
Om du inte fått något brev, var vänlig kontrollera att du angav rätt "
-"e-post adress, och kolla din skräpkatalog.
"
+msgstr "
Vi har e-postat dig instruktioner för att ändra ditt lösenord, om ett konto finns med den e-post adress du angav. Du borde få detta brev inom kort.
Om du inte fått något brev, var vänlig kontrollera att du angav rätt e-post adress, och kolla din skräpkatalog.
"
#: templates/registration/password_reset_email.html:4
#, python-format
msgid ""
"To initiate the password reset process for your %(username)s Modoboa "
"account, click the link below:"
-msgstr ""
-"För att starta processen för återställning av lösenord för ditt %(username)s "
-"Modoboa konto, klicka på länken nedanför:"
+msgstr "För att starta processen för återställning av lösenord för ditt %(username)s Modoboa konto, klicka på länken nedanför:"
#: templates/registration/password_reset_email.html:10
msgid ""
"If clicking the link above doesn't work, please copy and paste the URL in a "
"new browser window instead."
-msgstr ""
-"Om det ej går att klicka på länken, klipp och klistra länken i ett nytt "
-"webbläsarfönster istället."
+msgstr "Om det ej går att klicka på länken, klipp och klistra länken i ett nytt webbläsarfönster istället."
#: templates/registration/password_reset_email.html:14
msgid "Sincerely, The Modoboa Team."
@@ -1230,32 +1154,8 @@ msgstr "Vänligen, Modoboa teamet."
#: templates/registration/password_reset_form.html:24
msgid "Please fill-in your primary email address"
-msgstr ""
+msgstr "Var vänlig fyll i din primära e-postadress"
#: templates/registration/password_reset_subject.txt:3
msgid "Modoboa password reset"
msgstr "Modoboa lösenordsåterställning."
-
-#~ msgid "md5crypt (weak)"
-#~ msgstr "md5crypt (svag)"
-
-#~ msgid "sha256 (weak)"
-#~ msgstr "sha256 (svag)"
-
-#~ msgid "md5 (weak)"
-#~ msgstr "md5 (svag)"
-
-#~ msgid "crypt (weak)"
-#~ msgstr "crypt (svag)"
-
-#~ msgid "plain (weak)"
-#~ msgstr "okrypterad (svag)"
-
-#~ msgid "Secret key"
-#~ msgstr "Hemlig nyckel"
-
-#~ msgid "Key used to encrypt/decrypt passwords"
-#~ msgstr "Nyckel som används för att kryptera / dekryptera lösenord"
-
-#~ msgid "Key must be either 16, 24, or 32 bytes long"
-#~ msgstr "Nyckel måste vara endera 16, 24 eller 32 bytes lång"
diff --git a/modoboa/locale/sv/LC_MESSAGES/djangojs.po b/modoboa/locale/sv/LC_MESSAGES/djangojs.po
index 034817530..ae62e87d8 100644
--- a/modoboa/locale/sv/LC_MESSAGES/djangojs.po
+++ b/modoboa/locale/sv/LC_MESSAGES/djangojs.po
@@ -1,18 +1,21 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
#
# Translators:
-# Olle Gustafsson , 2013
-# Olle Gustafsson , 2013,2015-2016
+# Antoine Nguyen , 2020
+# Olle Gustafsson , 2020
+#
+#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: Modoboa\n"
+"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-03-09 16:48+0100\n"
-"PO-Revision-Date: 2020-03-09 15:49+0000\n"
-"Last-Translator: Antoine Nguyen \n"
-"Language-Team: Swedish (http://www.transifex.com/tonio/modoboa/language/sv/)\n"
+"PO-Revision-Date: 2020-05-11 18:45+0000\n"
+"Last-Translator: Olle Gustafsson , 2020\n"
+"Language-Team: Swedish (https://www.transifex.com/tonio/teams/13749/sv/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
@@ -25,7 +28,7 @@ msgstr "'"
#: bower_components/swagger-ui/dist/swagger-ui-bundle.js:133
msgid "operation"
-msgstr ""
+msgstr "operation"
#: core/static/core/js/settings.js:16
msgid "No more log entry to show"
diff --git a/modoboa/policyd/management/commands/policy_daemon.py b/modoboa/policyd/management/commands/policy_daemon.py
index a454e032d..e0b135853 100644
--- a/modoboa/policyd/management/commands/policy_daemon.py
+++ b/modoboa/policyd/management/commands/policy_daemon.py
@@ -25,7 +25,7 @@ class Command(BaseCommand):
def add_arguments(self, parser):
"""Add command line arguments."""
- parser.add_argument("--host", type=str, default="127.0.0.1")
+ parser.add_argument("--host", type=str, default="localhost")
parser.add_argument("--port", type=int, default=9999)
parser.add_argument("--debug", action="store_true",
help="Enable debug mode")
diff --git a/modoboa/policyd/tests.py b/modoboa/policyd/tests.py
index a53b5b790..1461b7e47 100644
--- a/modoboa/policyd/tests.py
+++ b/modoboa/policyd/tests.py
@@ -9,6 +9,7 @@ import time
import redis
+from django import db
from django.conf import settings
from django.core.management import call_command
from django.test import TransactionTestCase
@@ -55,6 +56,7 @@ class PolicyDaemonTestCase(
self.admin = core_models.User.objects.get(username="user@test2.com")
self.admin.role = "SuperAdmins"
+ db.connections.close_all()
patcher = patch("aiosmtplib.send")
self.send_mock = patcher.start()
self.process = Process(target=start_policy_daemon)
diff --git a/modoboa/templates/swagger-ui.html b/modoboa/templates/swagger-ui.html
deleted file mode 100644
index efbd02f35..000000000
--- a/modoboa/templates/swagger-ui.html
+++ /dev/null
@@ -1,57 +0,0 @@
-
-{% load static %}
-
-
-
-
- Modoboa API
-
-
-
-
-
-
-
-
-
diff --git a/modoboa/test_settings.py b/modoboa/test_settings.py
index cee94b28b..669959437 100644
--- a/modoboa/test_settings.py
+++ b/modoboa/test_settings.py
@@ -3,17 +3,17 @@ import os
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
-DB = os.environ.get("DB", "POSTGRESQL")
+DB = os.environ.get("DB", "postgres").lower()
-if DB == "MYSQL":
+if DB == "mysql":
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "modoboa",
- "USER": "root",
- "PASSWORD": "",
- "HOST": "localhost",
- "PORT": "",
+ "USER": os.environ.get("MYSQL_USER", "root"),
+ "PASSWORD": os.environ.get("MYSQL_PASSWORD", "root"),
+ "HOST": "127.0.0.1",
+ "PORT": os.environ.get("MYSQL_PORT", "3306"),
"ATOMIC_REQUESTS": True,
# MySQL's Strict Mode fixes many data integrity problems in MySQL,
# such as data truncation upon insertion, by escalating warnings
@@ -27,6 +27,7 @@ if DB == "MYSQL":
"SET innodb_strict_mode = ON;"
),
"charset": "utf8",
+ "connect_timeout": 10,
},
"TEST": {
"CHARSET": "utf8",
@@ -34,7 +35,7 @@ if DB == "MYSQL":
},
},
}
-elif DB == "SQLITE":
+elif DB == "sqlite":
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
@@ -49,9 +50,9 @@ else:
"ENGINE": "django.db.backends.postgresql",
"NAME": "modoboa",
"USER": "postgres",
- "PASSWORD": "",
+ "PASSWORD": "postgres",
"HOST": "localhost",
- "PORT": "",
+ "PORT": os.environ.get("POSTGRES_PORT", "5432"),
"ATOMIC_REQUESTS": True,
"OPTIONS": {
"client_encoding": "UTF8",
diff --git a/modoboa/urls.py b/modoboa/urls.py
index 98c998ae0..cebbecfda 100644
--- a/modoboa/urls.py
+++ b/modoboa/urls.py
@@ -6,11 +6,11 @@ from django.conf import settings
from django.contrib.auth import views as auth_views
from django.contrib.auth.decorators import login_required
from django.urls import include, path
-from django.views.generic import TemplateView
from django.views.i18n import JavaScriptCatalog
-from rest_framework.renderers import JSONOpenAPIRenderer
-from rest_framework.schemas import get_schema_view
+from drf_spectacular.views import (
+ SpectacularJSONAPIView, SpectacularSwaggerView, SpectacularRedocView
+)
from modoboa.admin.views import user as user_views
from modoboa.core import signals as core_signals, views as core_views
@@ -59,17 +59,13 @@ if extra_routes:
urlpatterns += extra_routes
# API urls
-schema_view = get_schema_view(
- title="Modoboa API",
- version="1.0.0",
- public=False,
- renderer_classes=[JSONOpenAPIRenderer],
-)
urlpatterns += [
- path('docs/openapi.json', schema_view, name="openapi_schema"),
- path('docs/api/', login_required(
- TemplateView.as_view(template_name="swagger-ui.html")),
- name="docs-index"),
+ path('docs/openapi.json', SpectacularJSONAPIView.as_view(), name='schema'),
+ path('docs/api/',
+ SpectacularSwaggerView.as_view(url_name='schema'),
+ name='docs-index'),
+ path('docs/api/redoc/', SpectacularRedocView.as_view(url_name='schema'),
+ name='redoc'),
path('api/v1/', include("modoboa.urls_api", namespace="v1")),
path('api/v2/', include("modoboa.urls_api", namespace="v2")),
]
diff --git a/mysql-requirements.txt b/mysql-requirements.txt
index ea498b097..4b7bd761c 100644
--- a/mysql-requirements.txt
+++ b/mysql-requirements.txt
@@ -1 +1 @@
-mysqlclient<=1.4.99
+mysqlclient<2.0.2
diff --git a/requirements.txt b/requirements.txt
index 2ecac1034..2a0846e20 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,25 +1,26 @@
django>=2.2,<2.3
django-braces==1.14.0
-django-ckeditor==5.8.0
-django-phonenumber-field[phonenumbers]==4.0.0
-django-reversion==3.0.5
+django-ckeditor==6.0.0
+django-phonenumber-field[phonenumbers]==5.0.0
+django-reversion==3.0.8
django-xforwardedfor-middleware==2.0
django-webpack-loader
-django-otp==1.0.1
+django-otp==1.0.2
dj-database-url
-djangorestframework>=3.11,<3.12
+djangorestframework>=3.11,<3.13
+drf-spectacular
uritemplate
-passlib==1.7.2
+passlib~=1.7.4
bcrypt # Requires libffi-dev and python-dev
dnspython==1.16.0
-feedparser==5.2.1
-greenlet==0.4.16
-gevent==1.5.0
-jsonfield==2.0.2
+feedparser==6.0.2
+greenlet==0.4.17
+gevent==20.9.0
+jsonfield==3.1.0
Pillow # Optional dependency for django-ckeditor
progressbar33==2.4
py-dateutil==2.2
diff --git a/setup.py b/setup.py
index 19e19ac07..654824d25 100644
--- a/setup.py
+++ b/setup.py
@@ -45,6 +45,11 @@ if __name__ == "__main__":
with open(path.join(HERE, "README.rst")) as readme:
LONG_DESCRIPTION = readme.read()
+ def local_scheme(version):
+ """Skip the local version (eg. +xyz of 0.6.1.dev4+gdf99fe2)
+ to be able to upload to Test PyPI"""
+ return ""
+
setup(
name="modoboa",
description="Mail hosting made simple",
@@ -74,7 +79,7 @@ if __name__ == "__main__":
zip_safe=False,
scripts=["bin/modoboa-admin.py"],
install_requires=INSTALL_REQUIRES,
- use_scm_version=True,
+ use_scm_version={"local_scheme": local_scheme},
python_requires=">=3.4",
setup_requires=["setuptools_scm"],
extras_require={
diff --git a/test-requirements.txt b/test-requirements.txt
index 4a6ebba6f..6006e2bb4 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,6 +1,6 @@
argon2-cffi>=16.1.0
factory-boy<=2.12.0
-httmock==1.3.0
-testfixtures==6.10.3
+httmock==1.4.0
+testfixtures==6.15.0
tox
asynctest
diff --git a/test_project/test_project/settings.py b/test_project/test_project/settings.py
index fd05a151e..ffea9aea9 100644
--- a/test_project/test_project/settings.py
+++ b/test_project/test_project/settings.py
@@ -48,8 +48,8 @@ SITE_ID = 1
# Security settings
X_FRAME_OPTIONS = "SAMEORIGIN"
-CSRF_COOKIE_SECURE = True
-SESSION_COOKIE_SECURE = True
+CSRF_COOKIE_SECURE = False
+SESSION_COOKIE_SECURE = False
# Application definition
@@ -65,6 +65,7 @@ INSTALLED_APPS = (
'ckeditor_uploader',
'rest_framework',
'rest_framework.authtoken',
+ 'drf_spectacular',
'django_otp',
'django_otp.plugins.otp_totp',
'django_otp.plugins.otp_static',
@@ -186,6 +187,14 @@ REST_FRAMEWORK = {
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
+ 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
+}
+
+SPECTACULAR_SETTINGS = {
+ 'SCHEMA_PATH_PREFIX': r'/api/v1',
+ 'TITLE': 'Modoboa API',
+ 'VERSION': '1.0.0',
+ 'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAuthenticated'],
}
# Modoboa settings
@@ -198,7 +207,7 @@ MODOBOA_API_URL = 'https://api.modoboa.org/1/'
# REDIS
REDIS_HOST = 'localhost'
-REDIS_PORT = 6379
+REDIS_PORT = os.environ.get('REDIS_PORT', 6379)
REDIS_QUOTA_DB = 0
REDIS_URL = 'redis://{}:{}/{}'.format(REDIS_HOST, REDIS_PORT, REDIS_QUOTA_DB)
@@ -283,6 +292,9 @@ LOGGING = {
},
'modoboa': {
'class': 'modoboa.core.loggers.SQLHandler',
+ },
+ 'console': {
+ 'class': 'logging.StreamHandler'
}
},
'loggers': {
@@ -300,7 +312,11 @@ LOGGING = {
'handlers': ['modoboa'],
'level': 'INFO',
'propagate': False
- }
+ },
+ # 'django_auth_ldap': {
+ # 'level': 'DEBUG',
+ # 'handlers': ['console']
+ # },
}
}
@@ -315,3 +331,5 @@ from modoboa.admin import settings as admin_settings # noqa
admin_settings.apply(globals())
# Load settings from extensions
+
+LDAP_SERVER_PORT = os.environ.get('LDAP_SERVER_PORT', 3389)
diff --git a/tests.py b/tests.py
index d34d331d3..df463c12e 100644
--- a/tests.py
+++ b/tests.py
@@ -5,22 +5,29 @@ import unittest
from modoboa.lib.sysutils import exec_cmd
DB = os.environ.get("DB", "postgres")
+if DB.lower() == "postgres":
+ PORT = os.environ.get("POSTGRES_PORT", "5432")
+ DB = "postgres"
+else:
+ PORT = os.environ.get("MYSQL_PORT", "3306")
+ DB = "mysql"
class DeployTest(unittest.TestCase):
- dbtype = DB.lower()
+ dbtype = DB
dbhost = "localhost"
+ dbport = PORT
projname = "modoboa_test"
- dbuser = DB == "MYSQL" and "modoboa" or "postgres"
- dbpassword = DB == "MYSQL" and "modoboa" or ""
+ dbuser = DB == "mysql" and "modoboa" or "postgres"
+ dbpassword = DB == "mysql" and "modoboa" or ""
def setUp(self):
self.workdir = tempfile.mkdtemp()
def test_silent(self):
- dburl = "default:%s://%s:%s@%s/%s" \
+ dburl = "default:%s://%s:%s@%s:%s/%s" \
% (self.dbtype, self.dbuser, self.dbpassword,
- self.dbhost, self.projname)
+ self.dbhost, self.dbport, self.projname)
cmd = (
"modoboa-admin.py deploy --collectstatic "
"--dburl %s --domain %s --admin-username admin %s"