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"