Started import work

This commit is contained in:
Spitap
2023-04-18 15:44:08 +02:00
committed by Antoine Nguyen
parent 844c1e482e
commit 415aefc764
66 changed files with 3802 additions and 8 deletions
@@ -89,6 +89,7 @@ MODOBOA_APPS = (
'modoboa.maillog',
'modoboa.pdfcredentials',
'modoboa.dmarc',
'modoboa.imap_migration',
# Modoboa extensions here.
{% for extension in extensions %} '{{ extension }}',
{% endfor %}
@@ -119,6 +120,7 @@ AUTHENTICATION_BACKENDS = (
#'modoboa.lib.authbackends.LDAPBackend',
#'modoboa.lib.authbackends.SMTPBackend',
'django.contrib.auth.backends.ModelBackend',
'modoboa.imap_migration.auth_backends.IMAPBackend',
)
# SMTP authentication
+16
View File
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
"""A plugin to migrate mailboxes using IMAP."""
from __future__ import unicode_literals
from pkg_resources import get_distribution, DistributionNotFound
try:
__version__ = get_distribution(__name__).version
except DistributionNotFound:
# package is not installed
pass
default_app_config = "modoboa.imap_migration.apps.IMAPMigrationConfig"
@@ -0,0 +1,93 @@
"""API serializers."""
from rest_framework import serializers
from modoboa.admin import models as admin_models
from modoboa.imap_migration import models
class EmailProviderDomainSerializer(serializers.ModelSerializer):
"""Serializer class for EmailProviderDomain."""
id = serializers.IntegerField(required=False)
class Meta:
extra_kwargs = {
"name": {
"validators": []
}
}
fields = ("id", "name", "new_domain")
model = models.EmailProviderDomain
class EmailProviderSerializer(serializers.ModelSerializer):
"""Serializer class for EmailProvider."""
domains = EmailProviderDomainSerializer(many=True, required=False)
class Meta:
fields = "__all__"
model = models.EmailProvider
def create(self, validated_data):
"""Create provider and domains."""
domains = validated_data.pop("domains", None)
validated_data.pop("id", None)
provider = models.EmailProvider.objects.create(**validated_data)
if domains:
to_create = []
for domain in domains:
to_create.append(models.EmailProviderDomain(
provider=provider, **domain))
models.EmailProviderDomain.objects.bulk_create(to_create)
return provider
def update(self, instance, validated_data):
"""Update provider and domains."""
domains = validated_data.pop("domains", [])
domain_ids = [domain["id"] for domain in domains if "id" in domain]
for key, value in validated_data.items():
setattr(instance, key, value)
instance.save()
for domain in instance.domains.all():
if domain.id not in domain_ids:
domain.delete()
else:
for updated_domain in domains:
if updated_domain.get("id") != domain.id:
continue
domain.name = updated_domain["name"]
domain.new_domain = updated_domain.get("new_domain")
domain.save()
domains.remove(updated_domain)
break
to_create = []
for new_domain in domains:
to_create.append(models.EmailProviderDomain(
name=new_domain["name"],
new_domain=new_domain.get("new_domain"),
provider=instance
))
models.EmailProviderDomain.objects.bulk_create(to_create)
return instance
class MailboxSerializer(serializers.ModelSerializer):
"""Simple mailbox serializer."""
class Meta:
fields = ("id", "full_address")
model = admin_models.Mailbox
class MigrationSerializer(serializers.ModelSerializer):
"""Serializer class for Migration."""
mailbox = MailboxSerializer()
class Meta:
depth = 1
fields = ("id", "provider", "mailbox", "username")
model = models.Migration
+12
View File
@@ -0,0 +1,12 @@
"""API urls."""
from rest_framework import routers
from . import viewsets
router = routers.SimpleRouter()
router.register(r"email-providers", viewsets.EmailProviderViewSet)
router.register(r"migrations", viewsets.MigrationViewSet)
urlpatterns = router.urls
+39
View File
@@ -0,0 +1,39 @@
"""API viewsets."""
from rest_framework import filters, mixins, permissions, viewsets
from ... import models
from . import serializers
class EmailProviderViewSet(viewsets.ModelViewSet):
"""ViewSet class for EmailProvider."""
filter_backends = (filters.SearchFilter, )
permission_classes = (
permissions.IsAuthenticated,
permissions.DjangoModelPermissions
)
queryset = models.EmailProvider.objects.all().prefetch_related("domains")
search_fields = ("name", )
serializer_class = serializers.EmailProviderSerializer
class MigrationViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
viewsets.GenericViewSet):
"""ViewSet class for Migration."""
filter_backends = (filters.SearchFilter, )
permission_classes = (
permissions.IsAuthenticated,
permissions.DjangoModelPermissions
)
queryset = models.Migration.objects.all().select_related(
"provider", "mailbox__domain")
search_fields = (
"username", "provider__name", "mailbox__address",
"mailbox__domain__name")
serializer_class = serializers.MigrationSerializer
@@ -0,0 +1,30 @@
"""APIv2 serializers."""
from rest_framework import serializers
class IMAPMigrationSettingsSerializer(serializers.Serializer):
"""A serializer for global parameters."""
# General
enabled_imapmigration = serializers.BooleanField(default=True)
# OfflineIMAP settings
max_sync_accounts = serializers.IntegerField(default=1)
# OfflineIMAP Filter settings
create_folders = serializers.BooleanField(default=True)
folder_filter_exclude = serializers.CharField(required=False,
allow_blank=True,
default="")
folder_filter_include = serializers.CharField(required=False,
allow_blank=True,
default="")
class CheckProviderSerializer(serializers.Serializer):
"""A serializer for checking connection to IMAP server."""
address = serializers.CharField()
port = serializers.IntegerField(min_value=0, max_value=65535)
secured = serializers.BooleanField()
+13
View File
@@ -0,0 +1,13 @@
"""API urls."""
from rest_framework import routers
from ..v1 import viewsets as v1_viewsets
from .viewsets import EmailProviderViewSet
router = routers.SimpleRouter()
router.register(r"email-providers", EmailProviderViewSet)
router.register(r"migrations", v1_viewsets.MigrationViewSet)
urlpatterns = router.urls
+35
View File
@@ -0,0 +1,35 @@
"""Viewsets for API V2 for imap_migration."""
import imaplib
import socket
import ssl
from rest_framework import serializers, response
from rest_framework.decorators import action
from modoboa.imap_migration.api.v1 import viewsets as v1_viewsets
from .serializers import CheckProviderSerializer
class EmailProviderViewSet(v1_viewsets.EmailProviderViewSet):
@action(methods=["post"], detail=False)
def check_connection(self, request, **kwargs):
"""check that provided information allow connection to imap server."""
serializer = CheckProviderSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
secured = serializer.data["secured"]
address = serializer.data["address"]
port = serializer.data["port"]
try:
if secured:
print(address)
print(port)
imaplib.IMAP4_SSL(address, port)
else:
imaplib.IMAP4(address, port)
except (socket.error, imaplib.IMAP4.error, ssl.SSLError) as error:
print(type(error))
print(error)
return response.Response({'status': 'failed'}, 400)
return response.Response(status=200)
+107
View File
@@ -0,0 +1,107 @@
"""IMAP migration forms."""
import collections
from django import forms
from django.utils.translation import ugettext_lazy as _
from modoboa.lib import form_utils
from modoboa.parameters import forms as param_forms
IMAP_MIGRATION_PARAMETERS_STRUCT = collections.OrderedDict([
("general", {
"label": _("General"),
"params": collections.OrderedDict([
("enabled_imapmigration", {
"label": _("Enable IMAP Migration"),
"help_text": _(
"Enable IMAP Migration."),
})
])
}),
("offlineimap", {
"label": _("OfflineIMAP"),
"params": collections.OrderedDict([
("max_sync_accounts", {
"label": _("Concurrent sync jobs"),
"help_text": _(
"The maximum number of concurrent synchronization jobs")
})
])
}),
("offlineimapfilter", {
"label": _("OfflineIMAP Filter"),
"params": collections.OrderedDict([
("create_folders", {
"label": _("Create Folders"),
"help_text": _(
"Allow Creation of missing folders during sync"),
}),
("folder_filter_exclude", {
"label": _("Folder Filter Exclusions"),
"help_text": _(
"Use a regular expression to explicitly include folders in sync. "
"Example: ^Trash$|Del")
}),
("folder_filter_include", {
"label": _("Folder Filter Inclusions"),
"help_text": _(
"A comma seperated list of folders to explicitly include in sync "
"even if filtered by the Folder Filter Exclusions. Example: "
"debian.user, debian.personal ")
})
])
})
])
class ParametersForm(param_forms.AdminParametersForm):
"""IMAP migration settings."""
app = "imap_migration"
sep1 = form_utils.SeparatorField(label=_("General"))
enabled_imapmigration = form_utils.YesNoField(
label=_("Enable IMAP Migration"),
initial=True,
help_text=_("Enable IMAP Migration.")
)
sep2 = form_utils.SeparatorField(label=_("OfflineIMAP settings"))
max_sync_accounts = forms.IntegerField(
label=_("Concurrent sync jobs"),
initial=1,
help_text=_("The maximum number of concurrent synchronization jobs")
)
sep3 = form_utils.SeparatorField(label=_("OfflineIMAP Filter settings"))
create_folders = form_utils.YesNoField(
label=_("Create Folders"),
initial=True,
help_text=_("Allow Creation of missing folders during sync")
)
folder_filter_exclude = forms.CharField(
required=False,
label=_("Folder Filter Exclusions"),
initial="",
help_text=_(
"Use a regular expression to explicitly include folders in sync. "
"Example: ^Trash$|Del"
)
)
folder_filter_include = forms.CharField(
required=False,
label=_("Folder Filter Inclusions"),
initial="",
help_text=_(
"A comma seperated list of folders to explicitly include in sync "
"even if filtered by the Folder Filter Exclusions. Example: "
"debian.user, debian.personal "
)
)
+35
View File
@@ -0,0 +1,35 @@
"""AppConfig for IMAP migration."""
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy
def load_imapmigration_settings():
"""Load core settings.
This function must be manually called (see :file:`urls.py`) in
order to load base settings.
"""
from modoboa.parameters import tools as param_tools
from . import app_settings
from .api.v2 import serializers
param_tools.registry.add(
"global", app_settings.ParametersForm, ugettext_lazy("IMAP Migration"))
param_tools.registry.add2(
"global", "imap_migration", ugettext_lazy("IMAP Migration"),
app_settings.IMAP_MIGRATION_PARAMETERS_STRUCT,
serializers.IMAPMigrationSettingsSerializer)
class IMAPMigrationConfig(AppConfig):
"""App configuration."""
name = "modoboa.imap_migration"
verbose_name = "Migration through IMAP for Modoboa"
def ready(self):
from . import checks # noqa
from . import handlers # noqa
load_imapmigration_settings()
+105
View File
@@ -0,0 +1,105 @@
"""IMAP authentication backend for Django."""
import imaplib
import socket
import ssl
from django.utils.encoding import smart_bytes, smart_str
from django.utils.translation import ugettext as _
from modoboa.core import models as core_models
from modoboa.lib import email_utils
from modoboa.lib.exceptions import ModoboaException
from modoboa.parameters import tools as param_tools
from . import models
class IMAPBackend:
"""IMAP authentication backend."""
def authenticate(self, request, username=None, password=None):
"""Check the username/password and return a User."""
if not param_tools.get_global_parameter("enabled_imapmigration"):
return None
condition = not param_tools.get_global_parameter(
"auto_create_domain_and_mailbox", "admin")
if condition:
# Automatic domain/mailbox is disabled. Deny auth to
# prevent further issues...
return None
self.address, domain = email_utils.split_mailbox(username)
provider_domain = models.EmailProviderDomain.objects.filter(
name=domain).select_related("provider").first()
if not provider_domain:
# Domain not allowed for migration: failure
return None
address = provider_domain.provider.address
port = provider_domain.provider.port
try:
if provider_domain.provider.secured:
conn = imaplib.IMAP4_SSL(address, port)
else:
conn = imaplib.IMAP4(address, port)
except (socket.error, imaplib.IMAP4.error, ssl.SSLError) as error:
raise ModoboaException(
_("Connection to IMAP server failed: %s") % error)
try:
typ, data = conn.login(
smart_bytes(username), smart_str(password))
except imaplib.IMAP4.error:
typ = "NO"
conn.logout()
if typ != "OK":
return None
self.provider_domain = provider_domain
return self.get_or_create_user(username, password)
def get_or_create_user(self, username, password):
"""Get a user or create it the first time.
.. note::
We assume the username is a valid email address.
"""
orig_username = username
# Check if old addresses must be converted
if self.provider_domain.new_domain:
username = u"{}@{}".format(
self.address, self.provider_domain.new_domain.name)
user, created = core_models.User.objects.get_or_create(
username__iexact=username, defaults={
"username": username.lower(), "email": username.lower()
}
)
if created:
user.set_password(password)
user.save()
core_models.populate_callback(user)
models.Migration.objects.create(
provider=self.provider_domain.provider,
mailbox=user.mailbox,
username=orig_username,
password=password
)
else:
# What happens if an account already exists?
if not hasattr(user, "mailbox"):
# No mailbox => might be an admin account
return None
qset = models.Migration.objects.filter(mailbox=user.mailbox)
if not qset.exists():
# No migration => either someone else account, or
# migration is done
return None
return user
def get_user(self, user_pk):
"""Retrieve a User instance."""
user = None
try:
user = core_models.User.objects.get(pk=user_pk)
except core_models.User.DoesNotExist:
pass
return user
+29
View File
@@ -0,0 +1,29 @@
"""Deployment checks."""
from django.core.checks import Warning, register
from django.utils.translation import ugettext as _
from modoboa.core import models as core_models
W001 = Warning(
_("Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."),
hint=_("Go to the online parameters panel (admin tab) and activate "
"this feature."),
id="modoboa-imap-migration.W001"
)
@register(deploy=True)
def check_auto_creation_is_enabled(app_configs, **kwargs):
"""Ensure automatic domain/mailbox creation is on."""
lc = core_models.LocalConfig.objects.first()
if not lc:
return []
errors = []
if not lc.parameters.get_value(
"auto_create_domain_and_mailbox", app="admin"):
errors.append(W001)
return errors
+37
View File
@@ -0,0 +1,37 @@
"""IMAP migration factories."""
import factory
from modoboa.admin import factories as admin_factories
from . import models
class EmailProviderFactory(factory.django.DjangoModelFactory):
"""Factory for EmailProvider."""
class Meta:
model = models.EmailProvider
name = factory.Sequence(lambda n: "Provider {}".format(n))
address = "127.0.0.1"
port = 143
class EmailProviderDomainFactory(factory.django.DjangoModelFactory):
"""Factory for EmailProviderDomain."""
class Meta:
model = models.EmailProviderDomain
provider = factory.SubFactory(EmailProviderFactory)
class MigrationFactory(factory.django.DjangoModelFactory):
"""Factory for Migration."""
class Meta:
model = models.Migration
mailbox = factory.SubFactory(admin_factories.MailboxFactory)
provider = factory.SubFactory(EmailProviderFactory)
+19
View File
@@ -0,0 +1,19 @@
"""IMAP migration handlers."""
from django.urls import reverse
from django.dispatch import receiver
from django.utils.translation import ugettext as _
from modoboa.core import signals as core_signals
@receiver(core_signals.extra_user_menu_entries)
def menu(sender, location, user, **kwargs):
"""Return extra menu entry."""
if location != "top_menu" or not user.is_superuser:
return []
return [
{"name": "imap_migrations",
"label": _("IMAP migrations"),
"url": reverse("imap_migration:index")}
]
@@ -0,0 +1,134 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Jan Laufik, 2016
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2016-11-28 11:43+0000\n"
"Last-Translator: Jan Laufik\n"
"Language-Team: Czech (Czech Republic) (http://www.transifex.com/tonio/"
"modoboa/language/cs_CZ/)\n"
"Language: cs_CZ\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "Spojení se serverem IMAP selhalo: %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "Nastavení OfflineIMAP"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "Souběžné synchronizační úkoly"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "Maximální počet souběžných synchronizačních úkolů"
#: forms.py:23
#, fuzzy
#| msgid "OfflineIMAP settings"
msgid "OfflineIMAP Filter settings"
msgstr "Nastavení OfflineIMAP"
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
#, fuzzy
#| msgid "IMAP migration"
msgid "IMAP migrations"
msgstr "IMAP migrace"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "IMAP migrace pomocí OfflineIMAP"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "Migrace existujících schránek pomocí IMAP protokolu a OfflineIMAP"
#: modo_extension.py:25 templates/modoboa.imap_migration/index.html:6
msgid "IMAP migration"
msgstr "IMAP migrace"
#~ msgid "Connection settings"
#~ msgstr "Konfigurace připojení"
#~ msgid "Server address"
#~ msgstr "Adresa serveru"
#~ msgid "Address of your IMAP server"
#~ msgstr "Adresa vašeho IMAP serveru"
#~ msgid "Use a secured connection"
#~ msgstr "Použít zabezpečené připojení"
#~ msgid "Use a secured connection to access IMAP server"
#~ msgstr "Použít zabezpečené připojení pro přístup k IMAP serveru"
#~ msgid "Server port"
#~ msgstr "Port serveru"
#~ msgid "Listening port of your IMAP server"
#~ msgstr "Port, na kterém naslouchá váš IMAP server"
#~ msgid "Cancel this account's migration"
#~ msgstr "Zrušit migraci tohoto účtu"
#~ msgid "Cancel the migration for %s?"
#~ msgstr "Zrušit migraci pro %s?"
#~ msgid "Unknow migration"
#~ msgstr "Nenznámá migrace"
#~ msgid "Migration cancelled successfully"
#~ msgstr "Migrace úspěšně zrušena"
@@ -0,0 +1,133 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# controlc.de, 2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2016-11-09 16:13+0000\n"
"Last-Translator: controlc.de\n"
"Language-Team: German (http://www.transifex.com/tonio/modoboa/language/de/)\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "Verbindung zu IMAP-Server fehlgeschlagen: %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "OfflineIMAP-Einstellungen"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "Gleichzeitige Synchronisierungsaufgaben"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "Die maximale Anzahl von gleichzeitigen Synchronisierungsaufgaben"
#: forms.py:23
#, fuzzy
#| msgid "OfflineIMAP settings"
msgid "OfflineIMAP Filter settings"
msgstr "OfflineIMAP-Einstellungen"
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
#, fuzzy
#| msgid "IMAP migration"
msgid "IMAP migrations"
msgstr "IMAP-Migration"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "IMAP-Migration benutzt OfflineIMAP"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "Migration existierender Postfächer benutzt IMAP und OfflineIMAP"
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr "IMAP-Migration"
#~ msgid "Connection settings"
#~ msgstr "Verbindungseinstellungen"
#~ msgid "Server address"
#~ msgstr "Server-Adresse"
#~ msgid "Address of your IMAP server"
#~ msgstr "Addresse Ihres IMAP-Servers"
#~ msgid "Use a secured connection"
#~ msgstr "Gesicherte Verbindung verwenden"
#~ msgid "Use a secured connection to access IMAP server"
#~ msgstr "Gesicherte Verbindung für Zugriff auf IMAP-Server verwenden"
#~ msgid "Server port"
#~ msgstr "Server-Port"
#~ msgid "Listening port of your IMAP server"
#~ msgstr "Port auf dem der IMAP-Server lauscht"
#~ msgid "Cancel this account's migration"
#~ msgstr "Diese Konten-Migration abbrechen"
#~ msgid "Cancel the migration for %s?"
#~ msgstr "Migration für %s abbrechen?"
#~ msgid "Unknow migration"
#~ msgstr "Unbekannte Migration"
#~ msgid "Migration cancelled successfully"
#~ msgstr "Abbruch der Migration war erfolgreich"
@@ -0,0 +1,134 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Giannis Kapetanakis <bilias@edu.physics.uoc.gr>, 2016
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2016-11-09 16:13+0000\n"
"Last-Translator: Giannis Kapetanakis <bilias@edu.physics.uoc.gr>\n"
"Language-Team: Greek (Greece) (http://www.transifex.com/tonio/modoboa/"
"language/el_GR/)\n"
"Language: el_GR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "Παρουσιάστηκε σφάλμα στη σύνδεση με τον εξυπηρετητή IMAP: %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "Ρυθμίσεις OfflineIMAP"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "Ταυτόχρονες εργασίες συγχρονισμού"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "Ο μέγιστος αριθμός ταυτόχρονων εργασιών συγχρονισμού"
#: forms.py:23
#, fuzzy
#| msgid "OfflineIMAP settings"
msgid "OfflineIMAP Filter settings"
msgstr "Ρυθμίσεις OfflineIMAP"
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
#, fuzzy
#| msgid "IMAP migration"
msgid "IMAP migrations"
msgstr "Μετάβαση IMAP"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "Μετάβαση IMAP με χρήση του OfflineIMAP"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "Μετάβαση υπαρχόντων γραμματοκιβωτίων με χρήση IMAP και του OfflineIMAP"
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr "Μετάβαση IMAP"
#~ msgid "Connection settings"
#~ msgstr "Ρυθμίσεις σύνδεσης"
#~ msgid "Server address"
#~ msgstr "Διεύθυνση εξυπηρετητή"
#~ msgid "Address of your IMAP server"
#~ msgstr "Η διεύθυνση του εξυπηρετητή IMAP"
#~ msgid "Use a secured connection"
#~ msgstr "Χρήση ασφαλής σύνδεσης"
#~ msgid "Use a secured connection to access IMAP server"
#~ msgstr "Χρήση ασφαλής σύνδεσης με τον εξυπηρετητή IMAP"
#~ msgid "Server port"
#~ msgstr "Πόρτα εξυπηρετητή"
#~ msgid "Listening port of your IMAP server"
#~ msgstr "Η πόρτα λειτουργίας του εξυπηρετητή IMAP"
#~ msgid "Cancel this account's migration"
#~ msgstr "Ακύρωση της μετάβασης αυτού του λογαριασμού"
#~ msgid "Cancel the migration for %s?"
#~ msgstr "Ακύρωση της μετάβασης του %s;"
#~ msgid "Unknow migration"
#~ msgstr "Άγνωστη μετάβαση"
#~ msgid "Migration cancelled successfully"
#~ msgstr "Η μετάβαση ακυρώθηκε με επιτυχία"
@@ -0,0 +1,95 @@
# 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 <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr ""
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr ""
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr ""
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr ""
#: forms.py:23
msgid "OfflineIMAP Filter settings"
msgstr ""
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
msgid "IMAP migrations"
msgstr ""
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr ""
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr ""
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr ""
@@ -0,0 +1,101 @@
# 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 <tonio@ngyn.org>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2019-03-01 18:09+0100\n"
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\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"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "Echec lors de la connexion au serveur IMAP : %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr "La création automatique de domaine/compte est désactivée, ce qui veut dire que l'authentification IMAP ne fonctionnera pas."
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr "Rendez vous sur le panneau de configuration des paramètres (onglet Administration) et activez cette fonctionnalité."
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "Paramètres OfflineIMAP"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "Tâche(s) de synchronisation concurrente(s)"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "Nombre maximal de tâche de synchronisation concurrente"
#: forms.py:23
msgid "OfflineIMAP Filter settings"
msgstr "Paramètres de filtragre OfflineIMAP"
#: forms.py:26
msgid "Create Folders"
msgstr "Création des dossiers"
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr "Autoriser la création des dossiers manquant pendant la synchro"
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr "Filtre d'exclusion de dossier"
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
"Utiliser une expression régulière pour explicitement inclure des dossiers à "
"synchroniser. Exemple : ^Trash$|Del"
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr "Filtre d'inclusion de dossier"
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
"Une liste de dossiers séparés par des virgules à explicitement inclure "
"pendant la synchronisation même s'ils sont concernés par le filtre "
"d'exclusion. Exemple debian.user, debian.personal"
#: handlers.py:17
msgid "IMAP migrations"
msgstr "Migrations IMAP"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "Migration IMAP basée sur OfflineIMAP"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "Migrer des boites existantes en utilisant IMAP et OfflineIMAP"
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr "Migration IMAP"
@@ -0,0 +1,134 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Rocco <mors10@hotmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2016-11-09 16:13+0000\n"
"Last-Translator: Rocco <mors10@hotmail.com>\n"
"Language-Team: Italian (http://www.transifex.com/tonio/modoboa/language/"
"it/)\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "Conennessione al server IMAP fallito: %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "Impostazioni OfflineIMAP"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "Sincronizzazioni contemporanee"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "Il numero massimo si sincronizzazioni contemporanee"
#: forms.py:23
#, fuzzy
#| msgid "OfflineIMAP settings"
msgid "OfflineIMAP Filter settings"
msgstr "Impostazioni OfflineIMAP"
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
#, fuzzy
#| msgid "IMAP migration"
msgid "IMAP migrations"
msgstr "Migrazione IMAP"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "Migrazione IMAP utilizzado OfflineIMAP"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "Migrare caselle di posta elettronica utilizzando IMAP e OfflineIMAP"
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr "Migrazione IMAP"
#~ msgid "Connection settings"
#~ msgstr "Impostazioni di connessione"
#~ msgid "Server address"
#~ msgstr "Indirizzo del Server"
#~ msgid "Address of your IMAP server"
#~ msgstr "Indirizzo del server IMAP"
#~ msgid "Use a secured connection"
#~ msgstr "Utilizza connessione sicura"
#~ msgid "Use a secured connection to access IMAP server"
#~ msgstr "Utilizza una connessione sicura per l'accesso al server IMAP"
#~ msgid "Server port"
#~ msgstr "Porta del server"
#~ msgid "Listening port of your IMAP server"
#~ msgstr "Porta in ascolto del server IMAP"
#~ msgid "Cancel this account's migration"
#~ msgstr "Cancellare l'account di questa migrazione"
#~ msgid "Cancel the migration for %s?"
#~ msgstr "Cancellare la migrazione %s?"
#~ msgid "Unknow migration"
#~ msgstr "Migrazione sconosciuta"
#~ msgid "Migration cancelled successfully"
#~ msgstr "Migrazione avvenuta con successo"
@@ -0,0 +1,134 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# cwatanab <chihiro.watanabe@live.jp>, 2016
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2016-11-09 16:13+0000\n"
"Last-Translator: cwatanab <chihiro.watanabe@live.jp>\n"
"Language-Team: Japanese (Japan) (http://www.transifex.com/tonio/modoboa/"
"language/ja_JP/)\n"
"Language: ja_JP\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "IMAP サーバーへの接続に失敗しました: %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "オフライン IMAP 設定"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "同期ジョブの並列度"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "同期ジョブを同時に行う最大数"
#: forms.py:23
#, fuzzy
#| msgid "OfflineIMAP settings"
msgid "OfflineIMAP Filter settings"
msgstr "オフライン IMAP 設定"
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
#, fuzzy
#| msgid "IMAP migration"
msgid "IMAP migrations"
msgstr "IMAP マイグレーション"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "オフライン IMAP を使用して IMAP マイグレーション"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "IMAP と オフライン IMAP を使って既存のメールボックスを移行します"
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr "IMAP マイグレーション"
#~ msgid "Connection settings"
#~ msgstr "接続設定"
#~ msgid "Server address"
#~ msgstr "サーバーアドレス"
#~ msgid "Address of your IMAP server"
#~ msgstr "IMAP サーバーのアドレス"
#~ msgid "Use a secured connection"
#~ msgstr "セキュアな接続を使う"
#~ msgid "Use a secured connection to access IMAP server"
#~ msgstr "IMAP サーバーへのアクセスにセキュアな接続を使う"
#~ msgid "Server port"
#~ msgstr "サーバーポート"
#~ msgid "Listening port of your IMAP server"
#~ msgstr "IMAP サーバーの待受ポート"
#~ msgid "Cancel this account's migration"
#~ msgstr "このアカウントのマイグレーションをキャンセルする"
#~ msgid "Cancel the migration for %s?"
#~ msgstr "%s のマイグレーションをキャンセルしますか?"
#~ msgid "Unknow migration"
#~ msgstr "不明なマイグレーション"
#~ msgid "Migration cancelled successfully"
#~ msgstr "マイグレーションのキャンセルに成功しました"
@@ -0,0 +1,134 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Tuxis Internet Engineering V.O.F. <info@tuxis.nl>, 2016
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2016-12-20 14:22+0000\n"
"Last-Translator: Tuxis Internet Engineering V.O.F. <info@tuxis.nl>\n"
"Language-Team: Dutch (Netherlands) (http://www.transifex.com/tonio/modoboa/"
"language/nl_NL/)\n"
"Language: nl_NL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "Verbinding met de IMAP server mislukt: %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "OfflineIMAP instellingen"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "Gelijktijdige synchronisatietaken"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "Het maximale aantal gelijktijdige synchronisatietaken"
#: forms.py:23
#, fuzzy
#| msgid "OfflineIMAP settings"
msgid "OfflineIMAP Filter settings"
msgstr "OfflineIMAP instellingen"
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
#, fuzzy
#| msgid "IMAP migration"
msgid "IMAP migrations"
msgstr "IMAP migratie"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "IMAP migratie door middel van OfflineIMAP"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "Migreer bestaande mailboxen door middel van IMAP en OfflineIMAP"
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr "IMAP migratie"
#~ msgid "Connection settings"
#~ msgstr "Verbinding instellingen"
#~ msgid "Server address"
#~ msgstr "Serveradres"
#~ msgid "Address of your IMAP server"
#~ msgstr "Adres van uw IMAP-server"
#~ msgid "Use a secured connection"
#~ msgstr "Gebruik een beveiligde verbinding"
#~ msgid "Use a secured connection to access IMAP server"
#~ msgstr "Gebruik een beveiligde verbinding met de IMAP server"
#~ msgid "Server port"
#~ msgstr "Serverpoort"
#~ msgid "Listening port of your IMAP server"
#~ msgstr "Poort van uw IMAP server"
#~ msgid "Cancel this account's migration"
#~ msgstr "Annuleer de migratie van dit account"
#~ msgid "Cancel the migration for %s?"
#~ msgstr "Annuleer de migratie van %s?"
#~ msgid "Unknow migration"
#~ msgstr "Onbekende migratie"
#~ msgid "Migration cancelled successfully"
#~ msgstr "Migratie succesvol geannuleerd"
@@ -0,0 +1,134 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Paulino Michelazzo <paulino@michelazzo.com.br>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2016-11-09 16:13+0000\n"
"Last-Translator: Paulino Michelazzo <paulino@michelazzo.com.br>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/tonio/modoboa/"
"language/pt_BR/)\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "Falha ao conectar no servidor IMAP: %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "Configurações OfflineIMAP"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "Tarefas de sincronismo concorrentes"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "O número máximo de tarefas de sincronismo concorrentes"
#: forms.py:23
#, fuzzy
#| msgid "OfflineIMAP settings"
msgid "OfflineIMAP Filter settings"
msgstr "Configurações OfflineIMAP"
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
#, fuzzy
#| msgid "IMAP migration"
msgid "IMAP migrations"
msgstr "Migração IMAP"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "Migração IMAP usando OfflineIMAP"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "Migrar as caixas postais existentes usando IMAP e OfflineIMAP"
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr "Migração IMAP"
#~ msgid "Connection settings"
#~ msgstr "Configurações de conexão"
#~ msgid "Server address"
#~ msgstr "Endereço do servidor"
#~ msgid "Address of your IMAP server"
#~ msgstr "Endereço do seu servidor IMAP"
#~ msgid "Use a secured connection"
#~ msgstr "Usar conexão segura"
#~ msgid "Use a secured connection to access IMAP server"
#~ msgstr "Usar uma conexão segura para acessar o servidor IMAP"
#~ msgid "Server port"
#~ msgstr "Porta do servidor"
#~ msgid "Listening port of your IMAP server"
#~ msgstr "Porta de escuta do seu servidor IMAP"
#~ msgid "Cancel this account's migration"
#~ msgstr "Cancelar esta migração de conta"
#~ msgid "Cancel the migration for %s?"
#~ msgstr "Cancelar a migração para %s?"
#~ msgid "Unknow migration"
#~ msgstr "Migração desconhecida"
#~ msgid "Migration cancelled successfully"
#~ msgstr "Migração cancelada com sucesso"
@@ -0,0 +1,135 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Gregor <info@gregorjev.net>, 2016
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2016-11-09 16:13+0000\n"
"Last-Translator: Gregor <info@gregorjev.net>\n"
"Language-Team: Slovenian (Slovenia) (http://www.transifex.com/tonio/modoboa/"
"language/sl_SI/)\n"
"Language: sl_SI\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
"%100==4 ? 2 : 3);\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "Povezava z IMAP strežnikom ni uspela: %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "IMAP nastavitve brez povezave"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "Concurrent sync jobs"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "The maximum number of concurrent synchronization jobs"
#: forms.py:23
#, fuzzy
#| msgid "OfflineIMAP settings"
msgid "OfflineIMAP Filter settings"
msgstr "IMAP nastavitve brez povezave"
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
#, fuzzy
#| msgid "IMAP migration"
msgid "IMAP migrations"
msgstr "Selitev IMAP"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "IMAP selitev z uporabo IMAP brez povezave"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "Preseli obstoječe poštne predale z uporabo IMAP in IMAP brez povezave"
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr "Selitev IMAP"
#~ msgid "Connection settings"
#~ msgstr "Nastavitve povezave"
#~ msgid "Server address"
#~ msgstr "Naslov strežnika"
#~ msgid "Address of your IMAP server"
#~ msgstr "Naslov vašega IMAP strežnika"
#~ msgid "Use a secured connection"
#~ msgstr "Uporabi zavarovano povezavo"
#~ msgid "Use a secured connection to access IMAP server"
#~ msgstr "Za dostop do IMAP strežnika, uporabite zavarovano povezavo"
#~ msgid "Server port"
#~ msgstr "Vrata strežnika"
#~ msgid "Listening port of your IMAP server"
#~ msgstr "Vrata vašega IMAP strežnika"
#~ msgid "Cancel this account's migration"
#~ msgstr "Prekliči selitev računa"
#~ msgid "Cancel the migration for %s?"
#~ msgstr "Prekličem selitev za %s?"
#~ msgid "Unknow migration"
#~ msgstr "Neznana selitev"
#~ msgid "Migration cancelled successfully"
#~ msgstr "Selivet je uspešno preklicana"
@@ -0,0 +1,134 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Olle Gustafsson <olle@dalnix.se>, 2016
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-01 17:42+0100\n"
"PO-Revision-Date: 2016-11-09 16:13+0000\n"
"Last-Translator: Olle Gustafsson <olle@dalnix.se>\n"
"Language-Team: Swedish (http://www.transifex.com/tonio/modoboa/language/"
"sv/)\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: auth_backends.py:45
#, python-format
msgid "Connection to IMAP server failed: %s"
msgstr "Anslutning till IMAP server misslyckades: %s"
#: checks.py:11
msgid ""
"Automatic domain/mailbox creation is disabled which means IMAP "
"authentication won't work."
msgstr ""
#: checks.py:13
msgid ""
"Go to the online parameters panel (admin tab) and activate this feature."
msgstr ""
#: forms.py:15
msgid "OfflineIMAP settings"
msgstr "OfflineIMAP inställningar"
#: forms.py:18
msgid "Concurrent sync jobs"
msgstr "Samtidiga synkroniserings jobb"
#: forms.py:20
msgid "The maximum number of concurrent synchronization jobs"
msgstr "Det maximala antalet samtidiga synkroniserings jobb"
#: forms.py:23
#, fuzzy
#| msgid "OfflineIMAP settings"
msgid "OfflineIMAP Filter settings"
msgstr "OfflineIMAP inställningar"
#: forms.py:26
msgid "Create Folders"
msgstr ""
#: forms.py:28
msgid "Allow Creation of missing folders during sync"
msgstr ""
#: forms.py:33
msgid "Folder Filter Exclusions"
msgstr ""
#: forms.py:36
msgid ""
"Use a regular expression to explicitly include folders in sync. Example: "
"^Trash$|Del"
msgstr ""
#: forms.py:43
msgid "Folder Filter Inclusions"
msgstr ""
#: forms.py:46
msgid ""
"A comma seperated list of folders to explicitly include in sync even if "
"filtered by the Folder Filter Exclusions. Example: debian.user, debian."
"personal "
msgstr ""
#: handlers.py:17
#, fuzzy
#| msgid "IMAP migration"
msgid "IMAP migrations"
msgstr "IMAP migrering"
#: modo_extension.py:16
msgid "IMAP migration using OfflineIMAP"
msgstr "IMAP migrering med hjälp av OfflineIMAP"
#: modo_extension.py:19
msgid "Migrate existing mailboxes using IMAP and OfflineIMAP"
msgstr "Migrera existerande brevlådor med hjälp av IMAP och OfflineIMAP"
#: modo_extension.py:25 templates/modoboa_imap_migration/index.html:6
msgid "IMAP migration"
msgstr "IMAP migrering"
#~ msgid "Connection settings"
#~ msgstr "Anslutningsinställningar"
#~ msgid "Server address"
#~ msgstr "Server adress"
#~ msgid "Address of your IMAP server"
#~ msgstr "Adress till din IMAP server"
#~ msgid "Use a secured connection"
#~ msgstr "Använd en säker anslutning"
#~ msgid "Use a secured connection to access IMAP server"
#~ msgstr "Använd en säker anslutning för åtkomst till IMAP server"
#~ msgid "Server port"
#~ msgstr "Server port"
#~ msgid "Listening port of your IMAP server"
#~ msgstr "Lyssningsport för din IMAP server"
#~ msgid "Cancel this account's migration"
#~ msgstr "Avbryt detta kontos migrering"
#~ msgid "Cancel the migration for %s?"
#~ msgstr "Avbryt migreringen för %s?"
#~ msgid "Unknow migration"
#~ msgstr "Okänd migrering"
#~ msgid "Migration cancelled successfully"
#~ msgstr "Migrering avbröts framgångsrikt"
@@ -0,0 +1,44 @@
"""A management command to create an offlineimap configuration file."""
import os
import stat
from django.core.management.base import BaseCommand
from django.template.loader import render_to_string
from modoboa.admin.app_settings import load_admin_settings
from modoboa.parameters import tools as param_tools
from ...models import Migration
class Command(BaseCommand):
"""Command definition."""
help = "Generate an offlineimap configuration file."
def add_arguments(self, parser):
"""Add extra arguments to command line."""
parser.add_argument(
"--output", default="/tmp/offlineimap.conf",
help="Path of the generated file")
def handle(self, *args, **options):
"""Entry point."""
load_admin_settings()
conf = dict(
param_tools.get_global_parameters("imap_migration"))
context = {
"imap_create_folders": conf["create_folders"],
"imap_folder_filter_exclude": conf["folder_filter_exclude"],
"imap_folder_filter_include": ", ".join(
"'{0}'".format(w)
for w in conf["folder_filter_include"].split(",")),
"migrations": Migration.objects.select_related(
"provider", "mailbox__domain"),
}
with open(options["output"], "w") as fpo:
content = render_to_string(
"imap_migration/offlineimap.conf", context)
fpo.write(content)
os.chmod(options["output"], stat.S_IRUSR | stat.S_IWUSR)
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('admin', '__first__'),
]
operations = [
migrations.CreateModel(
name='Migration',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('_password', models.CharField(max_length=100)),
('mailbox', models.ForeignKey(to='admin.Mailbox', on_delete=models.CASCADE)),
],
options={
},
bases=(models.Model,),
),
]
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('imap_migration', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='migration',
name='_password',
field=models.CharField(max_length=255),
),
]
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2019-02-07 08:19
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
def create_default_provider(apps, schema_editor):
"""Create default provider based on old settings."""
EmailProvider = apps.get_model("imap_migration", "EmailProvider")
Migration = apps.get_model("imap_migration", "Migration")
LocalConfig = apps.get_model("core", "LocalConfig")
lc = LocalConfig.objects.first()
if not lc:
return
params = lc._parameters.get("imap_migration")
if params is None:
return
provider = EmailProvider.objects.create(
name="Default",
address=params["server_address"],
port=params["server_port"],
secured=params["secured"]
)
for migration in Migration.objects.select_related("mailbox__domain"):
migration.provider = provider
migration.username = (
migration.mailbox.address + "@" + migration.mailbox.domain.name)
migration.save(update_fields=["provider", "username"])
def backwards(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
('admin', '0014_auto_20181017_1628'),
('imap_migration', '0002_password'),
]
operations = [
migrations.CreateModel(
name='EmailProvider',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
('address', models.CharField(max_length=200)),
('port', models.PositiveIntegerField(default=143)),
('secured', models.BooleanField(default=False)),
],
options={
'ordering': ['name'],
},
),
migrations.CreateModel(
name='EmailProviderDomain',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('new_domain', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='admin.Domain')),
('provider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='imap_migration.EmailProvider')),
],
options={
'ordering': ['provider', 'name'],
},
),
migrations.AddField(
model_name='migration',
name='username',
field=models.CharField(max_length=254, null=True),
),
migrations.AddField(
model_name='migration',
name='provider',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='imap_migration.EmailProvider'),
),
migrations.RunPython(create_default_provider, backwards)
]
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2019-02-07 08:39
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('imap_migration', '0003_auto_20190207_0919'),
]
operations = [
migrations.AlterField(
model_name='migration',
name='provider',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='imap_migration.EmailProvider'),
),
migrations.AlterField(
model_name='migration',
name='username',
field=models.CharField(max_length=254),
),
]
@@ -0,0 +1,24 @@
# Generated by Django 2.2.12 on 2021-04-01 07:38
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('imap_migration', '0004_auto_20190207_0939'),
]
operations = [
migrations.AlterField(
model_name='emailproviderdomain',
name='provider',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='domains', to='imap_migration.EmailProvider'),
),
migrations.AlterField(
model_name='migration',
name='username',
field=models.CharField(max_length=254, unique=True),
),
]
+55
View File
@@ -0,0 +1,55 @@
"""
IMAP migration models.
"""
from django.db import models
from modoboa.admin.models import Mailbox
from modoboa.lib.cryptutils import encrypt, decrypt
class EmailProvider(models.Model):
"""Email provider model."""
name = models.CharField(max_length=50)
address = models.CharField(max_length=200)
port = models.PositiveIntegerField(default=143)
secured = models.BooleanField(default=False)
class Meta:
ordering = ["name"]
class EmailProviderDomain(models.Model):
"""Email provider domain class."""
provider = models.ForeignKey(
EmailProvider, on_delete=models.CASCADE, related_name="domains")
name = models.CharField(max_length=100, unique=True)
new_domain = models.ForeignKey(
"admin.Domain", on_delete=models.SET_NULL, null=True, blank=True)
class Meta:
ordering = ["provider", "name"]
class Migration(models.Model):
"""Represent mailboxes to migrate."""
provider = models.ForeignKey(EmailProvider, on_delete=models.CASCADE)
mailbox = models.ForeignKey(Mailbox, on_delete=models.CASCADE)
username = models.CharField(max_length=254, unique=True)
_password = models.CharField(max_length=255)
def __str__(self):
return self.username
@property
def password(self):
"""Password getter."""
return decrypt(self._password)
@password.setter
def password(self, value):
"""Password setter."""
self._password = encrypt(value)
@@ -0,0 +1,27 @@
{% extends "fluid.html" %}
{% load i18n lib_tags static %}
{% load render_bundle from webpack_loader %}
{% block pagetitle %}{% trans "IMAP migration" %}{% endblock %}
{% block extra_css %}
{% if not debug %}
{% render_bundle 'chunk-vendors' 'css' 'IMAP_MIGRATION' %}
{% render_bundle 'app' 'css' 'IMAP_MIGRATION' %}
{% endif %}
{% endblock %}
{% block extra_js %}
{% if not debug %}
{% render_bundle 'chunk-vendors' 'js' 'IMAP_MIGRATION' %}
{% endif %}
{% render_bundle 'app' 'js' 'IMAP_MIGRATION' %}
{% endblock %}
{% block container_content %}
<div id="app">
</div>
{% endblock %}
@@ -0,0 +1,40 @@
[general]
accounts = {{ migrations|join:", " }}
{% for migration in migrations %}
[Account {{ migration }}]
localrepository = Local_{{ migration }}
remoterepository = Remote_{{ migration }}
[Repository Local_{{ migration }}]
type = IMAP
ssl = false
remotehost = localhost
remoteuser = {{ migration }}
remotepass = {{ migration.password }}
{% if imap_create_folders == False %}
createfolders = False
{% endif %}
[Repository Remote_{{ migration }}]
type = IMAP
remotehost = {{ migration.provider.address }}
remoteport = {{ migration.provider.port }}
{% if migration.provider.secured %}
ssl = yes
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
{% else %}
ssl = no
{% endif %}
remoteuser = {{ migration }}
remotepass = {{ migration.password }}
readonly = true
{% autoescape off %}
{% if imap_folder_filter_exclude|length > 0 %}
folderfilter = lambda folder: not re.search('({{ imap_folder_filter_exclude }})', folder)
{% endif %}
{% if imap_folder_filter_include|length > 2 %}
folderincludes = [{{ imap_folder_filter_include }}]
{% endif %}
{% endautoescape %}
{% endfor %}
+204
View File
@@ -0,0 +1,204 @@
"""IMAP migration tests."""
import os
import shutil
import tempfile
from unittest import mock
from six.moves import configparser
from django.core.management import call_command
from django.urls import reverse
from modoboa.admin import factories as admin_factories
from modoboa.admin import models as admin_models
from modoboa.core import factories as core_factories
from modoboa.core import models as core_models
from modoboa.lib.tests import ModoTestCase, ModoAPITestCase
from . import checks
from . import factories
from . import models
class DataMixin(object):
"""A mixin to provide test data."""
@classmethod
def setUpTestData(cls):
super(DataMixin, cls).setUpTestData()
admin_factories.populate_database()
cls.mb = admin_models.Mailbox.objects.get(
user__username="user@test.com")
cls.migration = factories.MigrationFactory(
password="Toto1234", mailbox=cls.mb, username="user@test.com")
# class ViewsTestCase(DataMixin, ModoTestCase):
# """Views test cases."""
# def test_index(self):
# """Test index view."""
# url = reverse("modoboa_imap_migration:index")
# response = self.client.get(url)
# self.assertContains(response, '<div id="app">')
class AuthenticationTestCase(DataMixin, ModoTestCase):
"""IMAP authentication test case."""
@mock.patch("imaplib.IMAP4")
def test_authentication(self, mock_imap):
"""Check IMAP authentication."""
factories.EmailProviderDomainFactory(name="test.com")
mock_imap.return_value.login.return_value = ["OK", b""]
url = reverse("core:login")
data = {"username": "new_user@test.com", "password": "Toto1234"}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 302)
user = core_models.User.objects.get(username="new_user@test.com")
self.assertEqual(user.mailbox.domain.name, "test.com")
mock_imap.return_value.login.return_value = ["NO", b"Error"]
data = {"username": "new_user@test.com", "password": "Toto123"}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 401)
mock_imap.return_value.login.return_value = ["OK", b""]
data = {"username": "new_user@test2.com", "password": "Toto1234"}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 401)
self.set_global_parameter(
"auto_create_domain_and_mailbox", False, app="admin")
data = {"username": "new_user2@test.com", "password": "Toto1234"}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 401)
@mock.patch("imaplib.IMAP4")
def test_authenticate_and_rename(self, mock_imap):
"""Check address renaming."""
domain = admin_models.Domain.objects.get(name="test.com")
factories.EmailProviderDomainFactory(
name="gmail.com", new_domain=domain)
mock_imap.return_value.login.return_value = ["OK", b""]
url = reverse("core:login")
data = {"username": "new_user@gmail.com", "password": "Toto1234"}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 302)
user = core_models.User.objects.get(username="new_user@test.com")
self.assertEqual(user.mailbox.domain.name, "test.com")
@mock.patch("imaplib.IMAP4")
def test_authenticate_conflicts(self, mock_imap):
"""Check potential conflicts."""
core_factories.UserFactory(
username="admin2@test.com", groups=("DomainAdmins", ),
password="{PLAIN}toto"
)
domain = admin_models.Domain.objects.get(name="test.com")
factories.EmailProviderDomainFactory(
name="gmail.com", new_domain=domain)
mock_imap.return_value.login.return_value = ["OK", b""]
url = reverse("core:login")
data = {"username": "admin@gmail.com", "password": "Toto1234"}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 401)
data = {"username": "admin2@gmail.com", "password": "Toto1234"}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 401)
class ManagementCommandTestCase(DataMixin, ModoTestCase):
"""Management command test cases."""
def setUp(self):
super(ManagementCommandTestCase, self).setUp()
self.workdir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.workdir)
def test_generate_offlineimap_config(self):
"""Test generate_offlineimap_config command."""
path = os.path.join(self.workdir, "offlineimap.conf")
call_command("generate_offlineimap_config", "--output", path)
self.assertTrue(os.path.exists(path))
conf = configparser.ConfigParser()
conf.read(path)
self.assertTrue(conf.has_section("Account user@test.com"))
class ViewSetTestCase(DataMixin, ModoAPITestCase):
"""ViewSet related tests."""
def test_create_provider(self):
url = reverse("api:emailprovider-list")
data = {
"name": "Google",
"address": "imap.google.com",
"port": 143,
"secured": True,
"domains": [
{"id": 1000,
"name": "gmail.com"}
]
}
response = self.client.post(url, data, format="json")
self.assertEqual(response.status_code, 201)
self.assertTrue(
models.EmailProviderDomain.objects.filter(name="gmail.com")
.exists()
)
def test_update_provider(self):
provider_domain = factories.EmailProviderDomainFactory(
name="outlook.com")
url = reverse(
"api:emailprovider-detail", args=[provider_domain.provider.pk])
data = {
"id": provider_domain.provider.id,
"name": provider_domain.provider.name,
"address": provider_domain.provider.address,
"port": provider_domain.provider.port,
"domains": [
{"id": provider_domain.id,
"name": "hotmail.com",
"new_domain": None},
{"name": "gmail.com",
"new_domain": admin_models.Domain.objects.get(
name="test.com").pk}
]
}
response = self.client.put(url, data, format="json")
self.assertEqual(response.status_code, 200)
provider_domain.refresh_from_db()
self.assertEqual(provider_domain.name, data["domains"][0]["name"])
self.assertTrue(
models.EmailProviderDomain.objects.filter(name="gmail.com")
.exists()
)
data["domains"].pop()
response = self.client.put(url, data, format="json")
self.assertEqual(response.status_code, 200)
self.assertFalse(
models.EmailProviderDomain.objects.filter(name="gmail.com")
.exists()
)
class ChecksTestCase(ModoTestCase):
"""Deploy checks test case."""
def test_auto_creation_check(self):
self.assertEqual(
checks.check_auto_creation_is_enabled(None),
[]
)
self.set_global_parameter(
"auto_create_domain_and_mailbox", False, app="admin")
self.assertEqual(
checks.check_auto_creation_is_enabled(None),
[checks.W001]
)
+11
View File
@@ -0,0 +1,11 @@
"""Custom urls."""
from django.urls import path
from . import views
app_name = "imap_migration"
urlpatterns = [
path("", views.IndexView.as_view(), name="index"),
]
+19
View File
@@ -0,0 +1,19 @@
"""Views for the IMAP migration extension."""
from django.contrib.auth import mixins as auth_mixins
from django.views import generic
class IndexView(auth_mixins.LoginRequiredMixin,
auth_mixins.PermissionRequiredMixin,
generic.TemplateView):
"""Index view."""
permission_required = "modoboa.imap_migration.add_migration"
template_name = "modoboa.imap_migration/index.html"
def get_context_data(self, **kwargs):
"""Set menu selection."""
kwargs.update({"selection": "imap_migrations"})
return super(IndexView, self).get_context_data(**kwargs)
+1 -1
View File
@@ -12,7 +12,7 @@ class ParametersAPITestCase(ModoAPITestCase):
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
# core, admin, limits, pdf credentials
self.assertEqual(len(resp.json()), 5)
self.assertEqual(len(resp.json()), 6)
def test_get_structure(self):
url = reverse("v2:parameter-structure")
+1
View File
@@ -30,6 +30,7 @@ urlpatterns = [
path('pdfcredentials', include("modoboa.pdfcredentials.urls")),
path('stats/', include("modoboa.maillog.urls")),
path('dmarc/', include("modoboa.dmarc.urls")),
path('imap_migration/', include("modoboa.imap_migration.urls")),
path('user/forward/', user_views.forward, name="user_forward"),
+1
View File
@@ -9,6 +9,7 @@ app_name = "api"
urlpatterns = [
path('', include("modoboa.core.api.v1.urls")),
path('', include("modoboa.admin.api.v1.urls")),
path('', include("modoboa.imap_migration.api.v1.urls")),
path('', include("modoboa.limits.api.v1.urls")),
path('', include("modoboa.relaydomains.api.v1.urls"))
]
+2 -1
View File
@@ -10,13 +10,14 @@ urlpatterns = [
path('', include("modoboa.core.api.v2.urls")),
path('', include("modoboa.admin.api.v2.urls")),
path('', include("modoboa.parameters.api.v2.urls")),
path('', include("modoboa.imap_migration.api.v2.urls")),
path('', include("modoboa.limits.api.v1.urls")),
path('', include("modoboa.relaydomains.api.v1.urls")),
path('', include("modoboa.dmarc.api.v2.urls")),
path('', include("modoboa.dnstools.api.v2.urls")),
path('', include("modoboa.maillog.api.v2.urls")),
path('', include("modoboa.transport.api.v2.urls")),
path('', include("modoboa.pdfcredentials.api.v2.urls")),
path('', include("modoboa.pdfcredentials.api.v2.urls"))
]
urlpatterns += exts_pool.get_urls(category="api")