added pdfcredential native support

This commit is contained in:
Spitap
2023-02-18 11:52:04 +01:00
parent 78d8db0cce
commit 00a6b63841
40 changed files with 3904 additions and 4 deletions
@@ -164,6 +164,17 @@ export default {
getMenuItems (item) {
const result = []
if (item.type === 'account') {
item.possible_actions.forEach(element => {
result.push({
label: this.$gettext(element.label),
icon: element.icon,
link: element.url,
onClick: () => {
window.open(element.url)
this.fetchIdentities()
}
})
})
result.push({ label: this.$gettext('Edit'), icon: 'mdi-circle-edit-outline', onClick: this.editAccount })
result.push({ label: this.$gettext('Delete'), icon: 'mdi-delete-outline', onClick: this.deleteAccount, color: 'red' })
} else if (item.type === 'alias') {
+29 -1
View File
@@ -14,6 +14,7 @@ from django.contrib.auth import password_validation
from rest_framework import serializers
from modoboa.admin import signals as admin_signals
from modoboa.admin.api.v1 import serializers as v1_serializers
from modoboa.core import models as core_models, signals as core_signals
from modoboa.lib import email_utils
@@ -284,6 +285,15 @@ class TagSerializer(serializers.Serializer):
type = serializers.CharField()
class IdPossibleActionsSerializer(serializers.Serializer):
"""Serialiser used to get possible actions related to identities."""
name = serializers.CharField()
icon = serializers.CharField()
label = serializers.CharField()
url = serializers.URLField()
class IdentitySerializer(serializers.Serializer):
"""Serializer used for identities."""
@@ -293,6 +303,24 @@ class IdentitySerializer(serializers.Serializer):
name_or_rcpt = serializers.CharField()
tags = TagSerializer(many=True)
def __init__(self, instance=None, data=..., **kwargs):
super().__init__(instance, data, **kwargs)
self.fields["possible_actions"] = serializers.SerializerMethodField()
def get_possible_actions(self, identity):
if not isinstance(identity, core_models.User):
# Return empty action list if identity type is an alias (not used for now)
return []
actions = admin_signals.extra_account_identities_actions.send(
self.__class__, account=identity)
cleaned_actions = []
for action in actions:
try :
serialized_data = IdPossibleActionsSerializer(action[1]).data
cleaned_actions.append(serialized_data)
except (ValidationError, AttributeError):
continue
return cleaned_actions
class AccountResourceSerializer(serializers.ModelSerializer):
"""Serializer for user resource."""
@@ -512,7 +540,7 @@ class WritableAccountSerializer(v1_serializers.WritableAccountSerializer):
instance.set_password(password)
if mailbox_data:
creator = self.context["request"].user
if hasattr(instance, "mailbox"):
if hasattr(instance, "mailbox") and "mailbox" in validated_data:
if "username" in validated_data:
mailbox_data["email"] = validated_data["username"]
instance.email = validated_data["username"]
+1
View File
@@ -29,3 +29,4 @@ get_domain_form_instances = django.dispatch.Signal(
import_object = django.dispatch.Signal(providing_args=["objtype"])
use_external_recipients = django.dispatch.Signal(providing_args=["recipients"])
new_dkim_keys = django.dispatch.Signal(providing_args=["domains"])
extra_account_identities_actions = django.dispatch.Signal(providing_args=["account"])
+1 -3
View File
@@ -1,7 +1,5 @@
"""Core settings."""
from collections import OrderedDict
import collections
from django import forms
@@ -536,7 +534,7 @@ class GeneralParametersForm(param_forms.AdminParametersForm):
]
def _add_dynamic_fields(self):
new_fields = OrderedDict()
new_fields = collections.OrderedDict()
for field, value in self.fields.items():
new_fields[field] = value
if field == "sms_provider":
@@ -87,6 +87,7 @@ MODOBOA_APPS = (
'modoboa.dnstools',
'modoboa.policyd',
'modoboa.maillog',
'modoboa.pdfcredentials',
# Modoboa extensions here.
{% for extension in extensions %} '{{ extension }}',
{% endfor %}
+3
View File
@@ -0,0 +1,3 @@
"""Generate PDF documents containing user credentials."""
default_app_config = "modoboa.pdfcredentials.apps.PDFCredentialsConfig"
+32
View File
@@ -0,0 +1,32 @@
"""Handlers for PDF credientials api v2."""
import os
from django.urls import reverse
from django.dispatch import receiver
from django.utils.translation import ugettext as _
from modoboa.admin import signals as admin_signals
from modoboa.parameters import tools as param_tools
from ...lib import get_creds_filename
@receiver(admin_signals.extra_account_identities_actions)
def extra_identities_actions(sender, account, **kwargs):
"""
Add download credential action for identity.
Used for api v2.
"""
if not param_tools.get_global_parameter("enabled_pdfcredentials"):
return []
fname = get_creds_filename(account)
if not os.path.exists(fname):
return []
return {
"name": "get_credentials",
"url": reverse("v2:get-credentials",
args=[account.id]),
"icon": "mdi-file-download-outline",
"label": _("Download PDF credentials.")
}
@@ -0,0 +1,65 @@
"""Limits serializers for API v2."""
import os
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers, status
from rest_framework.exceptions import PermissionDenied, APIException
from modoboa.core.models import User
from modoboa.parameters import tools as param_tools
from ...lib import decrypt_file, get_creds_filename
from ...constants import CONNECTION_SECURITY_MODES
class PDFCredentialsSettingsSerializer(serializers.Serializer):
"""A serializer for global parameters."""
# General
enabled_pdfcredentials = serializers.BooleanField(default=True)
# Document storage
storage_dir = serializers.CharField(default="/var/lib/modoboa/pdf_credentials")
# Security options
delete_first_dl = serializers.BooleanField(default=True)
generate_at_creation = serializers.BooleanField(default=True)
# Customization options
title = serializers.CharField(default=_("Personal account information"))
webpanel_url = serializers.URLField()
custom_message = serializers.CharField(required=False, allow_blank=True)
include_connection_settings = serializers.BooleanField(default=False)
smtp_server_address = serializers.CharField()
smtp_server_port = serializers.IntegerField(default=587)
smtp_connection_security = serializers.ChoiceField(choices=CONNECTION_SECURITY_MODES, default="starttls")
imap_server_address = serializers.CharField()
imap_server_port = serializers.IntegerField(default=143)
imap_connection_security = serializers.ChoiceField(choices=CONNECTION_SECURITY_MODES, default="starttls")
class GetAccountCredentialsSerializer(serializers.Serializer):
"""A serializer for get account credential view."""
account_id = serializers.IntegerField()
def validate(self, data):
request = self.context["request"]
account = User.objects.get(pk=data["account_id"])
if not request.user.can_access(account):
raise PermissionDenied()
self.context["account"] = account
return data
def save(self):
fname = get_creds_filename(self.context["account"])
if not os.path.exists(fname):
raise APIException( _("No document available for this user"), status.HTTP_400_BAD_REQUEST)
self.context["content"] = decrypt_file(fname)
if param_tools.get_global_parameter("delete_first_dl"):
os.remove(fname)
self.context["fname"] = fname
+153
View File
@@ -0,0 +1,153 @@
"""API v2 tests."""
import tempfile
import shutil
import os
from django.test import override_settings
from django.urls import reverse
from modoboa.admin import factories
from modoboa.core import models as core_models
from modoboa.lib.tests import ModoAPITestCase
class PDFCredentialViewTestCase(ModoAPITestCase):
@classmethod
def setUpTestData(cls): # NOQA:N802
"""Create test data."""
super().setUpTestData()
factories.populate_database()
def setUp(self):
"""Create temp. directory to store files."""
super().setUp()
self.workdir = tempfile.mkdtemp()
self.set_global_parameter("storage_dir", self.workdir)
def tearDown(self):
"""Reset test env."""
shutil.rmtree(self.workdir)
def _create_account(self, username, expected_status=201):
url = reverse("v2:account-list")
data = {
"username": f"{username}",
"role": "SimpleUsers",
"mailbox": {
"use_domain_quota": True
},
"password": "Toto12345",
"language": "fr",
}
resp = self.client.post(url, data, format="json")
self.assertEqual(resp.status_code, expected_status)
return data
def test_password_updated(self):
"""Check that document is generated at account creation/update."""
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
account = core_models.User.objects.get(username=values["username"])
# Check if link is present in listing page
response = self.client.get(reverse("v2:identities-list"), format="json")
assert_action_in_response = False
for res_dict in response.json():
for action in res_dict["possible_actions"]:
try:
if action.get("icon") == "mdi-file-download-outline":
assert_action_in_response = True
break
except KeyError:
continue
self.assertEqual(assert_action_in_response, True)
# Try to download the file
response = self.client.get(
reverse("v2:get-credentials",
args=[account.pk]))
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-Type"], "application/pdf")
# File have been deleted?
self.assertFalse(os.path.exists(fname))
# Try to download a second time
response = self.client.get(
reverse("v2:get-credentials",
args=[account.pk]))
self.assertEqual(response.json()["detail"], "No document available for this user")
# Update account
values.update({"language": "en"})
self.client.patch(
reverse("v2:account-detail", args=[account.pk]), values, format='json'
)
self.assertFalse(os.path.exists(fname))
self.set_global_parameter("generate_at_creation", False)
self.client.patch(
reverse("v2:account-detail", args=[account.pk]), values, format='json'
)
self.assertTrue(os.path.exists(fname))
def test_with_connection_settings(self):
"""Add connection settings to documents."""
self.set_global_parameter("include_connection_settings", True)
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
def test_with_custom_message(self):
"""Add custom message to documents."""
self.set_global_parameter("custom_message", "This is a test message.")
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
def test_account_delete(self):
"""Check that document is deleted with account."""
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
account = core_models.User.objects.get(username=values["username"])
url = reverse("v2:account-delete", args=[account.pk])
self.client.post(url)
self.assertFalse(os.path.exists(fname))
@override_settings(MODOBOA_LOGO="modoboa.png")
def test_with_custom_logo(self):
"""Check that document is deleted with account."""
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
def test_download_and_delete_account(self):
"""Check that document is deleted with account."""
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
account = core_models.User.objects.get(username=values["username"])
# Try to download the file
response = self.client.get(
reverse("v2:get-credentials",
args=[account.pk]))
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-Type"], "application/pdf")
# File have been deleted?
self.assertFalse(os.path.exists(fname))
# Delete account
url = reverse("v2:account-delete", args=[account.pk])
self.client.post(url)
self.assertFalse(os.path.exists(fname))
def test_storage_dir_creation(self):
"""Test storage directory creation."""
self.set_global_parameter("storage_dir", "/nonexistentdir")
self._create_account("leon@test.com", expected_status=500)
+11
View File
@@ -0,0 +1,11 @@
"""PDF Credentials API urls."""
from django.urls import path
from . import views
urlpatterns = [
path('credentials/<int:account_id>/', views.PDFCredentialView.as_view(),
name="get-credentials"),
]
+39
View File
@@ -0,0 +1,39 @@
"""PDF Credentials API v2 views."""
from django.utils.translation import ugettext as _
from django.http import HttpResponse
from rest_framework import permissions
from rest_framework.views import APIView
from modoboa.admin.models import Domain
from .serializers import GetAccountCredentialsSerializer
class PDFCredentialView(APIView):
permission_classes = (
permissions.IsAuthenticated, permissions.DjangoModelPermissions,
)
serializer_class = GetAccountCredentialsSerializer
def get_queryset(self):
"""Filter queryset based on current user."""
return Domain.objects.get_for_admin(self.request.user)
def get(self, request, *args, **kwargs):
"""View to download a document."""
data = {"account_id": kwargs["account_id"]}
serializer = GetAccountCredentialsSerializer(data=data, context={'request': request})
serializer.is_valid(raise_exception=True)
serializer.save()
content = serializer.context["content"]
fname = serializer.context["fname"]
resp = HttpResponse(content)
resp["Content-Type"] = "application/pdf"
resp["Content-Length"] = len(content)
resp["Content-Disposition"] = f"attachment; filename={fname};"
return resp
+243
View File
@@ -0,0 +1,243 @@
"""PDF Credentials forms."""
import collections
from django import forms
from django.conf import settings
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from django.contrib.sites import models as sites_models
from modoboa.lib import form_utils
from modoboa.parameters import forms as param_forms
from . import constants
PDF_CREDENTIALS_PARAMETERS_STRUCT = collections.OrderedDict([
("general", {
"label": _("General"),
"params": collections.OrderedDict([
("enabled_pdfcredentials", {
"label": _("Enable PDF credentials"),
"help_text": _(
"Enabled PDF credentials document generation on account creation."),
})
])
}),
("docstore", {
"label": _("Documents storage"),
"params": collections.OrderedDict([
("storage_dir", {
"label": _("Directory to save documents into"),
"help_text": _(
"Path to a directory where PDF documents will be saved"),
}),
])
}),
("security", {
"label": _("Security options"),
"params": collections.OrderedDict([
("delete_first_dl", {
"label": _("Delete documents after the first download"),
"help_text": _(
"Automatically delete a document just after its first download "
"from this interface"
)
}),
("generate_at_creation", {
"label": _("Generate documents only at account creation"),
"help_text": _(
"Generate a new document only when a new account is created. "
"If set to no, a new document will be created each time a "
"password is updated."
)
})
])
}),
("customization", {
"label": _("Customization options"),
"params": collections.OrderedDict([
("title", {
"label": _("Title"),
"help_text": _("The document's title")
}),
("webpanel_url", {
"label": _("Web panel url"),
"help_text": _("URL of the Modoboa web panel")
}),
("custom_message", {
"label": _("Custom message"),
"help_text": _(
"A custom message that will appear at the end of documents"
)
}),
("include_connection_settings", {
"label": _("Include mail client connection settings"),
"help_text": _(
"Include required SMTP and IMAP connection information to "
"configure a mail client, a tablet or a phone"
)
}),
("smtp_server_address", {
"label": _("SMTP server address"),
"help_text": _("Address of the SMTP server (hostname or IP)")
}),
("smtp_server_port", {
"label": _("SMTP server port"),
"help_port": _("Listening port of the SMTP server")
}),
("smtp_connection_security", {
"label": _("SMTP connection security"),
"help_text": _("Connection security mechanism")
}),
("imap_server_address", {
"label": _("IMAP server address"),
"help_text": _("Address of the IMAP server (hostname or IP)")
}),
("imap_server_port", {
"label": _("IMAP server port"),
"help_text": _("Listening port of the IMAP server")
}),
("imap_connection_security", {
"label": _("IMAP connection security"),
"help_text": _("Connection security mechanism")
})
])
})
])
class ParametersForm(param_forms.AdminParametersForm):
"""Admin parameters form."""
app = "pdfcredentials"
general = form_utils.SeparatorField(label=_("General"))
enabled_pdfcredentials = form_utils.YesNoField(
label= _("Enable PDF credentials"),
initial=True,
help_text=_("Enabled PDF credentials document generation on account creation.")
)
docstore = form_utils.SeparatorField(label=_("Documents storage"))
storage_dir = forms.CharField(
label=_("Directory to save documents into"),
initial="/var/lib/modoboa/pdf_credentials",
help_text=_("Path to a directory where PDF documents will be saved"),
)
security = form_utils.SeparatorField(label=_("Security options"))
delete_first_dl = form_utils.YesNoField(
label=_("Delete documents after the first download"),
initial=True,
help_text=_(
"Automatically delete a document just after its first download "
"from this interface"
)
)
generate_at_creation = form_utils.YesNoField(
label=_("Generate documents only at account creation"),
initial=True,
help_text=_(
"Generate a new document only when a new account is created. "
"If set to no, a new document will be created each time a "
"password is updated."
)
)
customization = form_utils.SeparatorField(label=_("Customization options"))
title = forms.CharField(
label=_("Title"),
initial=_("Personal account information"),
help_text=_("The document's title")
)
webpanel_url = forms.URLField(
label=_("Web panel url"),
help_text=_("URL of the Modoboa web panel")
)
custom_message = forms.CharField(
label=_("Custom message"),
help_text=_(
"A custom message that will appear at the end of documents"),
required=False
)
include_connection_settings = form_utils.YesNoField(
label=_("Include mail client connection settings"),
initial=False,
help_text=_(
"Include required SMTP and IMAP connection information to "
"configure a mail client, a tablet or a phone"
)
)
smtp_server_address = forms.CharField(
label=_("SMTP server address"),
help_text=_("Address of the SMTP server (hostname or IP)")
)
smtp_server_port = forms.IntegerField(
label=_("SMTP server port"),
initial=587,
help_text=_("Listening port of the SMTP server")
)
smtp_connection_security = forms.ChoiceField(
label=_("SMTP connection security"),
choices=constants.CONNECTION_SECURITY_MODES,
initial="starttls",
help_text=_("Connection security mechanism")
)
imap_server_address = forms.CharField(
label=_("IMAP server address"),
help_text=_("Address of the IMAP server (hostname or IP)")
)
imap_server_port = forms.IntegerField(
label=_("IMAP server port"),
initial=143,
help_text=_("Listening port of the IMAP server")
)
imap_connection_security = forms.ChoiceField(
label=_("IMAP connection security"),
choices=constants.CONNECTION_SECURITY_MODES,
initial="starttls",
help_text=_("Connection security mechanism")
)
visibility_rules = {
"smtp_server_address": "include_connection_settings=True",
"smtp_server_port": "include_connection_settings=True",
"smtp_connection_security": "include_connection_settings=True",
"imap_server_address": "include_connection_settings=True",
"imap_server_port": "include_connection_settings=True",
"imap_connection_security": "include_connection_settings=True",
}
@cached_property
def hostname(self):
"""Return local hostname."""
return sites_models.Site.objects.get_current().domain
def __init__(self, *args, **kwargs):
"""Set initial values."""
super(ParametersForm, self).__init__(*args, **kwargs)
if not self.fields["webpanel_url"].initial:
url = "https://{}{}".format(self.hostname, settings.LOGIN_URL)
self.fields["webpanel_url"].initial = url
if not self.fields["smtp_server_address"].initial:
self.fields["smtp_server_address"].initial = self.hostname
if not self.fields["imap_server_address"].initial:
self.fields["imap_server_address"].initial = self.hostname
+35
View File
@@ -0,0 +1,35 @@
"""AppConfig for PDF credentials."""
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy
def load_pdfcredential_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("PDF credentials"))
param_tools.registry.add2(
"global", "pdfcredentials", ugettext_lazy("PDF Credentials"),
app_settings.PDF_CREDENTIALS_PARAMETERS_STRUCT,
serializers.PDFCredentialsSettingsSerializer)
class PDFCredentialsConfig(AppConfig):
"""App configuration."""
name = "modoboa.pdfcredentials"
verbose_name = "PDF credentials for Modoboa"
def ready(self):
from . import handlers
from .api.v2 import handlers
load_pdfcredential_settings()
+10
View File
@@ -0,0 +1,10 @@
"""PDF credentials constants."""
from django.utils.translation import ugettext_lazy
CONNECTION_SECURITY_MODES = [
("none", ugettext_lazy("None")),
("starttls", "STARTTLS"),
("tls", "TLS")
]
+131
View File
@@ -0,0 +1,131 @@
"""The code used to generate PDF files (based on Reportlab)."""
from io import BytesIO
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_CENTER
from reportlab.lib import utils
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Frame,
Image
)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from django.utils.translation import ugettext as _
from modoboa.parameters import tools as param_tools
from . import lib
styles = getSampleStyleSheet()
styles.add(ParagraphStyle(
name="Footer", alignment=TA_CENTER, fontName="Helvetica-Oblique",
textColor=colors.lightgrey
))
styles.add(ParagraphStyle(
name="Warning", fontName="Helvetica-Oblique",
textColor=colors.red, fontSize=12
))
styles.add(ParagraphStyle(
name="Link",
textColor=colors.blue
))
styles.add(ParagraphStyle(
name="Greeting", alignment=TA_CENTER, fontName="Helvetica-Oblique",
fontSize=14
))
def resized_image(path, width=1*cm):
img = utils.ImageReader(path)
iw, ih = img.getSize()
aspect = ih / float(iw)
return Image(path, width=width, height=(width * aspect))
def credentials(account, password):
"""Generate a PDF document containing account credentials."""
def page_template(canvas, doc):
canvas.setTitle(_("Personal account information"))
canvas.setAuthor(account.fullname)
canvas.setCreator("Modoboa")
footer = [Paragraph(_("Powered by Modoboa - Mail hosting made simple"),
styles["Footer"])]
Frame(0, 0, 21 * cm, 2 * cm).addFromList(footer, canvas)
conf = dict(
param_tools.get_global_parameters("pdfcredentials"))
filename = lib.get_creds_filename(account)
buff = BytesIO()
doc = SimpleDocTemplate(buff, pagesize=A4)
story = []
story.append(resized_image(lib.get_document_logo(), 8*cm))
story.append(Spacer(1, 1 * cm))
story.append(Paragraph(conf["title"], styles["Title"]))
story.append(Spacer(1, 1 * cm))
story.append(Paragraph(_("""
Dear %s, this document contains the credentials you will need
to connect to Modoboa. Learn the content and destroy
the document as soon as possible.
""") % account.fullname, styles["Normal"]))
story.append(Spacer(1, 0.2 * cm))
story.append(Paragraph(_("Web panel:"), styles["h3"]))
data = [
["URL", conf["webpanel_url"]],
[_("Username"), account.username],
[_("Password"), password]
]
table = Table(data)
table.setStyle(TableStyle([
('TEXTCOLOR', (1, 0), (1, 0), colors.blue),
('GRID', (0, 0), (-1, -1), 1, colors.black),
('BACKGROUND', (0, 0), (0, -1), colors.lightgrey),
]))
story.append(table)
story.append(Spacer(1, 0.5 * cm))
story.append(Paragraph(_("""
Here you can view your emails anytime online, create filters or manage
your contacts.
"""), styles["Normal"]))
story.append(Spacer(1, 0.2 * cm))
story.append(Paragraph(
_("Please change your password!"),
styles["Warning"]))
if conf["include_connection_settings"]:
story.append(Spacer(1, 1 * cm))
story.append(Paragraph(
_("PC/Tablet/Smartphone configuration:"), styles["h3"]))
story.append(Spacer(1, 0.2 * cm))
data = [
[_("SMTP server address"), conf["smtp_server_address"]],
[_("SMTP server port"), conf["smtp_server_port"]],
[_("SMTP connection security"), conf["smtp_connection_security"]],
[_("IMAP server address"), conf["imap_server_address"]],
[_("IMAP server port"), conf["imap_server_port"]],
[_("IMAP connection security"), conf["imap_connection_security"]],
]
table = Table(data)
table.setStyle(TableStyle([
('GRID', (0, 0), (-1, -1), 1, colors.black),
('BACKGROUND', (0, 0), (0, -1), colors.lightgrey),
]))
story.append(table)
story.append(Spacer(1, 0.5 * cm))
story.append(Paragraph(
_("Use those settings for your computer, tablet or phone."),
styles["Normal"])
)
if conf["custom_message"]:
story.append(Spacer(1, 2 * cm))
story.append(Paragraph(conf["custom_message"], styles["Greeting"]))
doc.build(story, onFirstPage=page_template, onLaterPages=page_template)
length = len(buff.getvalue())
buff.seek(0)
lib.crypt_and_save_to_file(buff, filename, length)
+146
View File
@@ -0,0 +1,146 @@
"""PDF credentials forms."""
from django import forms
from django.conf import settings
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from django.contrib.sites import models as sites_models
from modoboa.lib import form_utils
from modoboa.parameters import forms as param_forms
from . import constants
class ParametersForm(param_forms.AdminParametersForm):
"""Admin parameters form."""
app = "pdfcredentials"
docstore = form_utils.SeparatorField(label=_("Documents storage"))
storage_dir = forms.CharField(
label=_("Directory to save documents into"),
initial="/var/lib/modoboa/pdf_credentials",
help_text=_("Path to a directory where PDF documents will be saved"),
)
security = form_utils.SeparatorField(label=_("Security options"))
delete_first_dl = form_utils.YesNoField(
label=_("Delete documents after the first download"),
initial=True,
help_text=_(
"Automatically delete a document just after its first download "
"from this interface"
)
)
generate_at_creation = form_utils.YesNoField(
label=_("Generate documents only at account creation"),
initial=True,
help_text=_(
"Generate a new document only when a new account is created. "
"If set to no, a new document will be created each time a "
"password is updated."
)
)
customization = form_utils.SeparatorField(label=_("Customization options"))
title = forms.CharField(
label=_("Title"),
initial=_("Personal account information"),
help_text=_("The document's title")
)
webpanel_url = forms.URLField(
label=_("Web panel url"),
help_text=_("URL of the Modoboa web panel")
)
custom_message = forms.CharField(
label=_("Custom message"),
help_text=_(
"A custom message that will appear at the end of documents"),
required=False
)
include_connection_settings = form_utils.YesNoField(
label=_("Include mail client connection settings"),
initial=False,
help_text=_(
"Include required SMTP and IMAP connection information to "
"configure a mail client, a tablet or a phone"
)
)
smtp_server_address = forms.CharField(
label=_("SMTP server address"),
help_text=_("Address of the SMTP server (hostname or IP)")
)
smtp_server_port = forms.IntegerField(
label=_("SMTP server port"),
initial=587,
help_text=_("Listening port of the SMTP server")
)
smtp_connection_security = forms.ChoiceField(
label=_("SMTP connection security"),
choices=constants.CONNECTION_SECURITY_MODES,
initial="starttls",
help_text=_("Connection security mechanism")
)
imap_server_address = forms.CharField(
label=_("IMAP server address"),
help_text=_("Address of the IMAP server (hostname or IP)")
)
imap_server_port = forms.IntegerField(
label=_("IMAP server port"),
initial=143,
help_text=_("Listening port of the IMAP server")
)
imap_connection_security = forms.ChoiceField(
label=_("IMAP connection security"),
choices=constants.CONNECTION_SECURITY_MODES,
initial="starttls",
help_text=_("Connection security mechanism")
)
visibility_rules = {
"smtp_server_address": "include_connection_settings=True",
"smtp_server_port": "include_connection_settings=True",
"smtp_connection_security": "include_connection_settings=True",
"imap_server_address": "include_connection_settings=True",
"imap_server_port": "include_connection_settings=True",
"imap_connection_security": "include_connection_settings=True",
}
@cached_property
def hostname(self):
"""Return local hostname."""
return sites_models.Site.objects.get_current().domain
def __init__(self, *args, **kwargs):
"""Set initial values."""
super(ParametersForm, self).__init__(*args, **kwargs)
if not self.fields["webpanel_url"].initial:
url = "https://{}{}".format(self.hostname, settings.LOGIN_URL)
self.fields["webpanel_url"].initial = url
if not self.fields["smtp_server_address"].initial:
self.fields["smtp_server_address"].initial = self.hostname
if not self.fields["imap_server_address"].initial:
self.fields["imap_server_address"].initial = self.hostname
def load_settings():
"""Load app settings."""
from modoboa.parameters import tools as param_tools
param_tools.registry.add(
"global", ParametersForm, _("PDF credentials"))
+53
View File
@@ -0,0 +1,53 @@
"""PDF credentials handlers."""
import os
from django.urls import reverse
from django.db.models import signals
from django.dispatch import receiver
from django.utils.translation import ugettext as _
from modoboa.core import models as core_models
from modoboa.core import signals as core_signals
from modoboa.parameters import tools as param_tools
from .documents import credentials
from .lib import init_storage_dir, delete_credentials, get_creds_filename
@receiver(core_signals.account_password_updated)
def password_updated(sender, account, password, created, **kwargs):
"""Create or update document."""
if not param_tools.get_global_parameter("enabled_pdfcredentials"):
return
generate_at_creation = param_tools.get_global_parameter(
"generate_at_creation")
if (generate_at_creation and not created) or account.is_superuser:
return
init_storage_dir()
credentials(account, password)
@receiver(signals.pre_delete, sender=core_models.User)
def account_deleted(sender, instance, **kwargs):
"""Remove document."""
delete_credentials(instance)
@receiver(core_signals.extra_account_actions)
def extra_account_actions(sender, account, **kwargs):
"""Add link to download document."""
if not param_tools.get_global_parameter("enabled_pdfcredentials"):
return []
fname = get_creds_filename(account)
if not os.path.exists(fname):
return []
return [{
"name": "get_credentials",
"url": reverse("pdfcredentials:account_credentials",
args=[account.id]),
"img": "fa fa-download",
"title": _("Retrieve user's credentials as a PDF document")
}]
+101
View File
@@ -0,0 +1,101 @@
"""Internal library."""
from io import BytesIO
import os
import struct
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from django.conf import settings
from django.utils.encoding import force_bytes, smart_bytes
from django.utils.translation import ugettext as _
from modoboa.lib.exceptions import InternalError
from modoboa.parameters import tools as param_tools
def init_storage_dir():
"""Create the directory whare documents will be stored."""
storage_dir = param_tools.get_global_parameter("storage_dir")
if os.path.exists(storage_dir):
return
try:
os.mkdir(storage_dir)
except (OSError, IOError) as inst:
raise InternalError(
_("Failed to create the directory that will contain "
"PDF documents (%s)") % inst
)
def get_creds_filename(account):
"""Return the full path of a document."""
storage_dir = param_tools.get_global_parameter("storage_dir")
return os.path.join(storage_dir, account.username + ".pdf")
def delete_credentials(account):
"""Try to delete a local file."""
fname = get_creds_filename(account)
if not os.path.exists(fname):
return
os.remove(fname)
def _get_cipher(iv):
"""Return ready-to-user Cipher."""
key = smart_bytes(settings.SECRET_KEY[:32])
backend = default_backend()
return Cipher(
algorithms.AES(force_bytes(key)),
modes.CBC(iv),
backend=backend
)
def crypt_and_save_to_file(content, filename, length, chunksize=64*512):
"""Crypt content and save it to a file."""
iv = os.urandom(16)
cipher = _get_cipher(iv)
encryptor = cipher.encryptor()
with open(filename, "wb") as fp:
fp.write(struct.pack(b"<Q", length))
fp.write(iv)
while True:
chunk = content.read(chunksize)
if not len(chunk):
break
elif len(chunk) % 16:
chunk += b" " * (16 - len(chunk) % 16)
fp.write(encryptor.update(force_bytes(chunk)))
fp.write(encryptor.finalize())
def decrypt_file(filename, chunksize=24*1024):
"""Decrypt the content of a file and return it."""
buff = BytesIO()
with open(filename, "rb") as fp:
origsize = struct.unpack(b"<Q", fp.read(struct.calcsize(b"Q")))[0]
iv = fp.read(16)
cipher = _get_cipher(iv)
decryptor = cipher.decryptor()
while True:
chunk = fp.read(chunksize)
if not len(chunk):
break
buff.write(decryptor.update(chunk))
buff.write(decryptor.finalize())
buff.truncate(origsize)
return buff.getvalue()
def get_document_logo():
"""Retrieve path to logo."""
try:
logo = settings.MODOBOA_CUSTOM_LOGO
logo = os.path.join(settings.MEDIA_ROOT, os.path.basename(logo))
except AttributeError:
logo = os.path.join(settings.STATIC_ROOT, "css/modoboa.png")
finally:
return logo
@@ -0,0 +1,211 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Peter Sveter, 2016
# Jarda Tesar <intossh@gmail.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2018-08-10 11:24+0000\n"
"Last-Translator: Jarda Tesar <intossh@gmail.com>\n"
"Language-Team: Czech (Czech Republic) (http://www.transifex.com/tonio/modoboa/language/cs_CZ/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs_CZ\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
#: constants.py:7
msgid "None"
msgstr "Žádné"
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "Informace osobního účtu"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Běží na Modoboa - Poštovní hosting zjednodušeně"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\nVážený %s, tento dokument obsahuje přístupové údaje potřebné\npro pripojení do rozhraní Modoboa. Zapamatujte si je a smazte\ntento dokument hned, jakmile to bude možné.\n"
#: documents.py:75
msgid "Web panel:"
msgstr "Webový panel:"
#: documents.py:78
msgid "Username"
msgstr "Uživatelské jméno"
#: documents.py:79
msgid "Password"
msgstr "Heslo"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr "\nZde si kdykoliv můžete prohlédnout své emaily, vytvořit filtry nebo spravovat Vaše kontakty.\n"
#: documents.py:96
msgid "Please change your password!"
msgstr "Prosím změňte si své heslo!"
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr "Nastavení PC/Tabletu/Smartphonu:"
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr "Adresa SMTP serveru"
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr "Port SMTP serveru"
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr "Zabezpečení SMTP připojení"
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr "Adresa IMAP serveru"
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr "Port IMAP serveru"
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr "Zabezpečení IMAP připojení"
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr "Použijte tato nastavení ve svém počítači, tabletu či telefonu."
#: forms.py:20
msgid "Documents storage"
msgstr "Úložný prostor pro dokumenty"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "Adresář pro uložení dokumentů"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "Cesta k adresáři kde budou uloženy PDF dokumenty"
#: forms.py:28
msgid "Security options"
msgstr "Možnosti zabezpečení"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "Smazat dokumenty po prvním stažení"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "Automaticky smazat dokument ihned po prvním stažení cez toto rozhraní"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "Vytvářet dokumenty pouze při vytváření účtu"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "Vytvářet nové dokumenty pouze při vytvoření účtu. Je-li nastaveno na hodnotu ne, bude nový dokument vytvořen pokaždé, když dojde ke změne hesla."
#: forms.py:49
msgid "Customization options"
msgstr "Možnosti vlastních úprav"
#: forms.py:52
msgid "Title"
msgstr "Název"
#: forms.py:54
msgid "The document's title"
msgstr "Název dokumentu"
#: forms.py:58
msgid "Web panel url"
msgstr "URL web panelu"
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr "URL adresa Modoboa web panelu"
#: forms.py:63
msgid "Custom message"
msgstr "Vlastní zpráva"
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr "Vlastní zpráva, která se zobrazí na konci dokumentu."
#: forms.py:70
msgid "Include mail client connection settings"
msgstr "Zahrnout nastavení mailového klienta"
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr "Zahrnout vyžadovaná nastavení SMTP a IMAP připojení pri konfiguraci mailového klienta, tabletu či telefonu"
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr "Adresa SMTP serveru (hostname nebo IP)"
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr "Port SMTP serveru"
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr "Zabezpečení připojení"
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr "Adresa IMAP serveru (hostname nebo IP)"
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr "Port IMAP serveru"
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "Získat přístupové údaje uživatele ako PDF dokument"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "Vytváření adresáře pro uložení PDF dokumentů (%s) selhalo "
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "PDF přístupové údaje"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "Vytvářet PDF dokumenty obsahující přístupové údaje uživatele"
#: views.py:30
msgid "No document available for this user"
msgstr "Pro tohoto uživatele není k dispozici žáden dokument"
@@ -0,0 +1,212 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Boris Andratzek <BAndratzek@LBV-net.de>, 2018
# Christian Wiegand, 2017
# controlcde, 2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2018-04-25 14:19+0000\n"
"Last-Translator: Boris Andratzek <BAndratzek@LBV-net.de>\n"
"Language-Team: German (http://www.transifex.com/tonio/modoboa/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: de\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: constants.py:7
msgid "None"
msgstr "kein"
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "Persönliche Kontoinformationen"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Powered by Modoboa - Mail hosting made simple"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\nLiebe/r %s, dieses Dokument enthält die Anmeldeinformationen, die Du benötigst um Dich mit Modoboa zu verbinden. Präge Dir bitte die Informationen ein und lösche anschliessend dieses Dokument.\n"
#: documents.py:75
msgid "Web panel:"
msgstr "Webanwendung:"
#: documents.py:78
msgid "Username"
msgstr "Benutzername"
#: documents.py:79
msgid "Password"
msgstr "Passwort"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr "\nHier können Sie Ihre Emails online ansehen, Filter oder Kontakte erstellen.\n"
#: documents.py:96
msgid "Please change your password!"
msgstr "Bitte ändern Sie Ihr Passwort!"
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr "PC/Tablet/Smartphone Konfiguration:"
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr "SMTP Serveradresse"
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr "SMTP Server-Port"
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr "SMTP-Verbindungssicherheit"
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr "IMAP Serveradresse"
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr "IMAP Server-Port"
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr "IMAP Verbindungssicherheit"
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr "Verwenden Sie diese Einstellungen für Ihren Computer, Ihr Tablet oder Telefon"
#: forms.py:20
msgid "Documents storage"
msgstr "Dokumentenablage"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "Speicherpfad für Dokumente"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "Dateipfad in dem die PDF-Dokumente abgelegt werden"
#: forms.py:28
msgid "Security options"
msgstr "Sicherheitseinstellungen"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "Lösche Dokumente nach dem ersten Download"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "Löscht automatisch das Dokument, wenn es das erste Mal über dieses Oberfläche heruntergeladen wurde"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "Erzeuge Dokumente nur wenn ein Konto angelegt wurde"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "Erzeuge ein neues Dokument nur wenn ein neues Konto erstellt wurde. Wenn die Option auf Nein steht wird jedes Mal ein neues Dokument erstellt, wenn ein Passwort aktualisiert wurde."
#: forms.py:49
msgid "Customization options"
msgstr "Individuell angepasste Optionen"
#: forms.py:52
msgid "Title"
msgstr "Titel"
#: forms.py:54
msgid "The document's title"
msgstr "Titel des Dokuments"
#: forms.py:58
msgid "Web panel url"
msgstr "URL der Webanwendung"
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr "URL der Modoboa Webanwendung"
#: forms.py:63
msgid "Custom message"
msgstr "Eigener Nachrichtentext"
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr "Eine eigene Nachricht, die am Ende des Dokuments angezeigt wird"
#: forms.py:70
msgid "Include mail client connection settings"
msgstr "Verbindungseinstellungen für Mailprogramme einschließen"
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr "Erforderliche SMTP und IMAP Einstellungen zur Konfiguration von Mailprogramme, Tablets oder Telefonen"
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr "Adresse des SMTP-Servers (Hostname oder IP)"
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr "Port, auf dem der SMTP-Server erreicht werden kann"
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr "Mechanismus zur Verbindungssicherung"
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr "Adresse des IMAP-Servers (Hostname oder IP)"
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr "Port, auf dem der IMAP-Servier erreicht werden kann"
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "Erhalte die Anmeldeinformationen des Benutzer als ein PDF-Dokument"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "Ein Fehler ist bei der Erstellung des Pfades, der die PDF Dokumente (%s) enthält, aufgetreten. "
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "PDF Anmeldeinformationen"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "Erzeuge PDF-Dokumente welche die Benutzeranmeldeinformationen enthält"
#: views.py:30
msgid "No document available for this user"
msgstr "Kein Dokument für diesen Benutzer verfügbar"
@@ -0,0 +1,211 @@
# 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
# Kostas Moumouris <moumouriskostas@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2017-10-12 13:30+0000\n"
"Last-Translator: Kostas Moumouris <moumouriskostas@gmail.com>\n"
"Language-Team: Greek (Greece) (http://www.transifex.com/tonio/modoboa/language/el_GR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: el_GR\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: constants.py:7
msgid "None"
msgstr "Κανένας"
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "Προσωπικές πληροφορίες λογαριασμού"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Powered by Modoboa - Mail hosting made simple"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\nΑγαπητέ/ή %s, αυτό το έγγραφο περιέχει τα διαπιστευτήρια\nπου χρειάζεστε για να συνδεθείτε με το Modoboa.\nΑποστηθίστε το περιεχόμενο και διαγράψτε το έγγραφο\nτο συντομότερο δυνατόν.\n"
#: documents.py:75
msgid "Web panel:"
msgstr "Web panel:"
#: documents.py:78
msgid "Username"
msgstr "Όνομα Χρήστη"
#: documents.py:79
msgid "Password"
msgstr "Κωδικός"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr "\nΕδώ μπορείτε να προβάλετε τα ηλεκτρονικά σας μηνύματα σε πραγματικό χρόνο, να δημιουργήσετε φίλτρα ή να διαχειριστείτε\n τις επαφές σας.\n"
#: documents.py:96
msgid "Please change your password!"
msgstr "Παρακαλώ αλλάξτε το κωδικό σας!"
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr "Ρυθμίσεις: PC/Tablet/Smartphone "
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr "Διεύθυνση SMTP Server"
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr "Θύρα SMTP server"
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr "Ασφάλεια σύνδεσης SMTP"
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr "Διεύθυνση IMAP server"
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr "θύρα IMAP Server"
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr "Ασφάλεια σύνδεσης IMAP"
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr "Χρησιμοποιήστε αυτές τις ρυθμίσεις για τον υπολογιστή, το tablet ή το τηλέφωνό σας."
#: forms.py:20
msgid "Documents storage"
msgstr "Αποθηκευτικός χώρος εγγράφων"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "Κατάλογος αρχείων για την αποθήκευση εγγράφων"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "Κατάλογος αρχείων για την αποθήκευση PDF εγγράφων"
#: forms.py:28
msgid "Security options"
msgstr "Ρυθμίσεις Ασφαλείας"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "Διαγραφή εγγράφων μετά την πρώτη λήψη"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "Αυτόματη διαγραφή εγγράφου μετά την πρώτη λήψη από αυτήν την διεπαφή"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "Δημιουργία εγγράφων μόνο κατά την δημιουργία λογαριασμού"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "Δημιουργία νέου εγγράφου μόνο κατά την δημιουργία νέου λογαριασμού. Εάν επιλέξετε όχι, ένα νέο έγγραφο θα δημιουργείται κάθε φορά που ο κωδικός χρήστη ενημερώνεται."
#: forms.py:49
msgid "Customization options"
msgstr "Επιλογές προσαρμογής"
#: forms.py:52
msgid "Title"
msgstr "Τίτλος"
#: forms.py:54
msgid "The document's title"
msgstr "Ο τίτλος του εγγράφου"
#: forms.py:58
msgid "Web panel url"
msgstr "Διεύθυνση Web panel"
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr "URL του Modoboa web panel"
#: forms.py:63
msgid "Custom message"
msgstr "Προσαρμοσμένο μήνυμα"
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr "Ένα προσαρμοσμένο μήνυμα που θα εμφανιστεί στο τέλος των εγγράφων"
#: forms.py:70
msgid "Include mail client connection settings"
msgstr "Συμπεριλάβε τις ρυθμίσεις σύνδεσης προγράμματος αλληλογραφίας"
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr "Συμπεριέλαβε της απαιτούμενες ρυθμίσης SMTP και IMAP για διασύνδεση με προγράμματα emai, τάμπλετ και τηλέφωνα"
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr "Διεύθυνση από τον SMTP server (hostname ή IP)"
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr "Οι θύρες που ακούει ό SMTP Server"
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr "Μηχανισμός ασφαλείας σύνδεσης"
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr "Διεύθυνση του IMAP server (hostname ή IP)"
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr "Η θύρα που ακούει ο IMAP Server"
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "Ανάκτηση διαπιστευτηρίων χρήστη σε αρχείο PDF"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "Σφάλμα στην δημιουργία του καταλόγου αρχείων για την αποθήκευση PDF εγγράφων (%s)"
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "Διαπιστευτήρια PDF"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "Δημιουργία PDF εγγράφων που να περιέχουν τα διαπιστευτήρια χρηστών"
#: views.py:30
msgid "No document available for this user"
msgstr "Δεν υπάρχουν διαθέσιμα έγγραφα για αυτόν τον χρήστη"
@@ -0,0 +1,209 @@
# 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: 2017-09-27 12:01+0200\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"
#: constants.py:7
msgid "None"
msgstr ""
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr ""
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr ""
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr ""
#: documents.py:75
msgid "Web panel:"
msgstr ""
#: documents.py:78
msgid "Username"
msgstr ""
#: documents.py:79
msgid "Password"
msgstr ""
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr ""
#: documents.py:96
msgid "Please change your password!"
msgstr ""
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr ""
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr ""
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr ""
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr ""
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr ""
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr ""
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr ""
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr ""
#: forms.py:20
msgid "Documents storage"
msgstr ""
#: forms.py:23
msgid "Directory to save documents into"
msgstr ""
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr ""
#: forms.py:28
msgid "Security options"
msgstr ""
#: forms.py:31
msgid "Delete documents after the first download"
msgstr ""
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr ""
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr ""
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr ""
#: forms.py:49
msgid "Customization options"
msgstr ""
#: forms.py:52
msgid "Title"
msgstr ""
#: forms.py:54
msgid "The document's title"
msgstr ""
#: forms.py:58
msgid "Web panel url"
msgstr ""
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr ""
#: forms.py:63
msgid "Custom message"
msgstr ""
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr ""
#: forms.py:70
msgid "Include mail client connection settings"
msgstr ""
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr ""
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr ""
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr ""
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr ""
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr ""
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr ""
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr ""
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr ""
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr ""
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr ""
#: views.py:30
msgid "No document available for this user"
msgstr ""
@@ -0,0 +1,210 @@
# 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: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2017-10-16 18:51+0000\n"
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
"Language-Team: French (http://www.transifex.com/tonio/modoboa/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"
#: constants.py:7
msgid "None"
msgstr "Aucune"
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "Informations de compte personnel"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Propulsé par Modoboa - L'hébergement mail simplifié"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\nCher %s, ce document contient les identifiants dont vous aurez\nbesoin pour vous connecter à Modoboa. Apprenez les puis détruisez\nce document le plus rapidement possible.\n"
#: documents.py:75
msgid "Web panel:"
msgstr "Console web :"
#: documents.py:78
msgid "Username"
msgstr "Nom d'utilisateur"
#: documents.py:79
msgid "Password"
msgstr "Mot de passe"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr "\nD'ici vous pouvez consulter vos emails en ligne n'importe quand, créer des filtres ou gérer vos contacts.\n"
#: documents.py:96
msgid "Please change your password!"
msgstr "Merci de changer votre mot de passe !"
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr "Configuration PC/Tablette/Smartphone :"
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr "Adresse du serveur SMTP"
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr "Port du serveur SMTP"
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr "Sécurité de la connexion SMTP"
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr "Adresse du serveur IMAP"
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr "Port du serveur IMAP"
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr "Sécurité de la connexion IMAP"
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr "Utilisez ces paramètres pour votre ordinateur, votre tablette ou votre téléphone."
#: forms.py:20
msgid "Documents storage"
msgstr "Stockage des documents"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "Répertoire dans lequel les documents sont sauvegardés"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "Chemin d'accès vers le répertoire ou seront stockés les documents"
#: forms.py:28
msgid "Security options"
msgstr "Options de sécurité"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "Supprimer les documents après le premier téléchargement"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "Supprimer automatiquement un document juste après son premier téléchargement depuis cette interface"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "Générer les documents à la création des comptes uniquement"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "Génère un nouveau document uniquement lors de la création d'un compte. Si défini à non, un nouveau document sera généré chaque fois qu'un mot de passe est changé."
#: forms.py:49
msgid "Customization options"
msgstr "Options de personnalisation"
#: forms.py:52
msgid "Title"
msgstr "Titre"
#: forms.py:54
msgid "The document's title"
msgstr "Le titre du document"
#: forms.py:58
msgid "Web panel url"
msgstr "URL de la console web"
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr "URL de la console web Modoboa"
#: forms.py:63
msgid "Custom message"
msgstr "Message personnalisé"
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr "Un message personnalisé qui apparaîtra en bas des documents."
#: forms.py:70
msgid "Include mail client connection settings"
msgstr "Inclure les paramètres de connexion pour client mail"
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr "Inclure les paramètres de connexion nécessaires pour configurer un client mail, une tablette ou un téléphone"
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr "Adresse du serveur SMTP (nom d'hôte ou IP)"
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr "Port d'écoute du serveur SMTP"
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr "Mécanisme de sécurisation de la connexion"
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr "Adresse du serveur IMAP (nom d'hôte ou IP)"
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr "Port d'écoute du serveur IMAP"
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "Récupérer les identifiant de l'utilisateur sous forme d'un document PDF"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "Echec lors de la création du répertoire qui contiendra les documents PDF (%s)"
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "Identifiants en PDF"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "Génère des documents PDF contenant les identifiants des utilisateurs"
#: views.py:30
msgid "No document available for this user"
msgstr "Aucun document disponible pour cet utilisateur"
@@ -0,0 +1,211 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# marcotrevisan <mrctrevisan@yahoo.it>, 2018
# Rocco <mors10@hotmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2018-11-02 15:15+0000\n"
"Last-Translator: marcotrevisan <mrctrevisan@yahoo.it>\n"
"Language-Team: Italian (http://www.transifex.com/tonio/modoboa/language/it/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: it\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: constants.py:7
msgid "None"
msgstr "Nessun valore"
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "Informazioni Personali"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Powered by Modoboa - Mail hosting made simple"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\nGentile %s, questo documento contiene le credenziali di accesso \nper accedere a Modoboa. Leggilo e elimina\n il documento il prima possibile.\n"
#: documents.py:75
msgid "Web panel:"
msgstr "Pannello web:"
#: documents.py:78
msgid "Username"
msgstr "Nome Utente"
#: documents.py:79
msgid "Password"
msgstr "Password"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr "\nQui puoi sempre visualizzare le tue email onlime, creare filtri\no gestire i tuoi contatti.\n"
#: documents.py:96
msgid "Please change your password!"
msgstr "Per favore cambia la tua password!"
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr "Configurazione PC/Tablet/Smartphone:"
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr "Indirizzo del server SMTP"
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr "Porta del server SMTP"
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr "Sicurezza della connessione al server SMTP"
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr "indirizzo del server IMAP"
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr "Porta del server IMAP"
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr "Sicurezza della connessione al server IMAP"
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr "Utilizza quelle impostazioni per il tuo computer, tablet o telefono."
#: forms.py:20
msgid "Documents storage"
msgstr "Memorizzazione Documenti"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "Cartella nella quale salvare i documenti"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "Indirizzo della cartella dove i documenti PDF saranno salvati"
#: forms.py:28
msgid "Security options"
msgstr "Opzioni di sicurezza"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "Cancella i documenti dopo averli scaricati"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "Elimina automaticamente il documento dopo che è stato scaricato da questa interfaccia"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "Genera documenti solo dopo la creazione dell'account"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "Generare un nuovo documento solo quando si crea un nuovo account. Se impostato su no , un nuovo documento verrà creato ogni volta che la password viene aggiornata."
#: forms.py:49
msgid "Customization options"
msgstr "Opzioni di personalizzazione"
#: forms.py:52
msgid "Title"
msgstr "Titolo"
#: forms.py:54
msgid "The document's title"
msgstr "Il titolo del documento"
#: forms.py:58
msgid "Web panel url"
msgstr "URL del pannello di controllo web"
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr "URL del pannello di controllo web di Modoboa"
#: forms.py:63
msgid "Custom message"
msgstr "Messaggio personalizzato"
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr "Un messaggio personalizzato comparirà alla fine dei documenti"
#: forms.py:70
msgid "Include mail client connection settings"
msgstr "Includi le impostazioni di connessione del client di posta"
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr "Includi le informazioni di connessione SMTP e IMAP richieste per configurare un client di posta, un tablet o telefono"
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr "Indirizzo del server SMTP (nome host o indirizzo IP)"
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr "Porta di ascolto del server SMTP"
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr "Meccanismo di sicurezza della connessione"
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr "Indirizzo del server IMAP (nome host o indirizzo IP)"
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr "Porta di ascolto del server IMAP"
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "Recuperare le credenziali dell'utente come documento PDF"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "Impossibile creare la cartella che conterrà i documenti PDF (%s)"
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "Credenziali PDF"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "Genera i documenti PDF con le credenziali dell'utente"
#: views.py:30
msgid "No document available for this user"
msgstr "Non ci sono documenti disponibili er questo utente"
@@ -0,0 +1,210 @@
# 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: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2017-09-27 10:02+0000\n"
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
"Language-Team: Japanese (Japan) (http://www.transifex.com/tonio/modoboa/language/ja_JP/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ja_JP\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: constants.py:7
msgid "None"
msgstr ""
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "個人アカウント情報"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Powered by Modoboa - メールホスティングをシンプルに"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\n%s さん\n\nこのドキュメントには、Modoboa へ接続するための必要な認証情報が書かれています。\n内容を理解したら、すみやかに廃棄してください。\n"
#: documents.py:75
msgid "Web panel:"
msgstr ""
#: documents.py:78
msgid "Username"
msgstr "ユーザ名"
#: documents.py:79
msgid "Password"
msgstr "パスワード"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr ""
#: documents.py:96
msgid "Please change your password!"
msgstr ""
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr ""
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr ""
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr ""
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr ""
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr ""
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr ""
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr ""
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr ""
#: forms.py:20
msgid "Documents storage"
msgstr "ドキュメントストレージ"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "ドキュメントを保存するためのディレクトリ"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "PDF ドキュメントを保存するためのディレクトリのパス"
#: forms.py:28
msgid "Security options"
msgstr "セキュリティオプション"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "最初のダウンロードの後にドキュメントを削除する"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "このインターフェースから最初にダウンロードされたあとにドキュメントを自動的に削除する"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "アカウントを作成するときだけドキュメントを生成する"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "アカウントの作成されたときだけ、新しいドキュメントを生成します。\nもし「いいえ」設定すれば、新しい文書は、パスワードが更新されるたびに作成されます。"
#: forms.py:49
msgid "Customization options"
msgstr ""
#: forms.py:52
msgid "Title"
msgstr ""
#: forms.py:54
msgid "The document's title"
msgstr ""
#: forms.py:58
msgid "Web panel url"
msgstr ""
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr ""
#: forms.py:63
msgid "Custom message"
msgstr ""
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr ""
#: forms.py:70
msgid "Include mail client connection settings"
msgstr ""
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr ""
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr ""
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr ""
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr ""
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr ""
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr ""
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "PDF ドキュメントとしてユーザー認証情報を取得します"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "PDFドキュメントを格納するディレクトリの作成に失敗しました (%s)"
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "PDF 認証情報"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "ユーザーの認証情報を書かれた PDF ドキュメントを生成"
#: views.py:30
msgid "No document available for this user"
msgstr "このユーザーにはドキュメンがありません"
@@ -0,0 +1,211 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# R Kloosterman <randx@zeelandnet.nl>, 2016
# Tuxis Internet Engineering V.O.F. <info@tuxis.nl>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2018-01-31 08:43+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"
"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"
#: constants.py:7
msgid "None"
msgstr ""
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "Persoonlijke account informatie"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Aangeboden door Modoboa - Mail hosting made simple"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\nBeste %s, dit document bevat alle gegevens dat u nodig\nheeft voor het gebruiken van Modoboa. Onthoud deze gegevens\nen verwijder dit document zo snel mogelijk.\n"
#: documents.py:75
msgid "Web panel:"
msgstr "Webpaneel:"
#: documents.py:78
msgid "Username"
msgstr "Gebruikersnaam"
#: documents.py:79
msgid "Password"
msgstr "Wachtwoord"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr "\nHier kunt u uw e-mails online bekijken, filters aanmaken, of contactpersonen beheren.\n"
#: documents.py:96
msgid "Please change your password!"
msgstr "Wijzig uw wachtwoord aub!"
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr "PC/Tablet/Smartphone configuratie:"
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr "SMTP serveradres"
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr "SMTP server poort"
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr "SMTP verbinding beveiliging"
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr "IMAP serveradres"
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr "IMAP server poort"
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr "IMAP verbinding beveiliging"
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr "Gebruik deze instellingen voor uw computer, tablet of telefoon."
#: forms.py:20
msgid "Documents storage"
msgstr "Document opslag"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "Map voor het opslaan van de documenten"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "Pad naar de map waar de PDF documenten worden opgeslagen"
#: forms.py:28
msgid "Security options"
msgstr "Beveiligings-opties"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "Documenten verwijderen na eerste download"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "Verwijder een document automatisch nadat het eenmalig is gedownload van paneel"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "Genereer een document enkel bij het aanmaken van een account"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "Genereer een document enkel wanneer een account is nieuw aangemaakt. Indien 'nee' is geselecteerd zal bij iedere wachtwoord wijziging een nieuw document worden gemaakt. "
#: forms.py:49
msgid "Customization options"
msgstr "Personaliseringsopties"
#: forms.py:52
msgid "Title"
msgstr "Titel"
#: forms.py:54
msgid "The document's title"
msgstr "Titel van het document"
#: forms.py:58
msgid "Web panel url"
msgstr "Webpaneel URL"
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr "URL van het Modoboa webpaneel"
#: forms.py:63
msgid "Custom message"
msgstr "Aangepast bericht"
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr "Een aangepast bericht dat aan het einde van de documenten komt te staan"
#: forms.py:70
msgid "Include mail client connection settings"
msgstr ""
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr ""
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr ""
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr ""
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr ""
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr ""
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr ""
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "Vraag de gebruikers gegevens op als een PDF document"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "Het aanmaken van de map voor de PDF documenten is mislukt (%s)"
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "PDF gegevens"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "Genereer PDF document met de gegevens voor de gebruiker"
#: views.py:30
msgid "No document available for this user"
msgstr "Geen document beschikbaar voor deze gebruiker"
@@ -0,0 +1,211 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# sin88 <sin@sin.waw.pl>, 2016
# Tomasz Proć <tomasz.proc@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2017-12-02 10:36+0000\n"
"Last-Translator: Tomasz Proć <tomasz.proc@gmail.com>\n"
"Language-Team: Polish (Poland) (http://www.transifex.com/tonio/modoboa/language/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"
#: constants.py:7
msgid "None"
msgstr "Brak"
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "Informacje o koncie osobistym"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Powered by Modoboa - Mail hosting made simple"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\nDrogi użytkowniku %s, ten dokument zawiera dane uwierzytelniające które będzeisz potrzebował to połączenia się. Zapoznaj się z nim i usuń go najszybciej jak to możliwe.\n"
#: documents.py:75
msgid "Web panel:"
msgstr "Panel web:"
#: documents.py:78
msgid "Username"
msgstr "Nazwa użytkownika"
#: documents.py:79
msgid "Password"
msgstr "Hasło"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr "\nPod tym adresem możesz online przeczytać swoje wiadomości, stworzyć filtry lub zarządzać\n listą kontaktów.\n"
#: documents.py:96
msgid "Please change your password!"
msgstr "Proszę zmienić swoje hasło!"
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr "konfiguracją PC/Tabletu/Smartfonu:"
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr "Adres serwera SMTP"
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr "Port serwera SMTP"
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr "Zabezpieczenia połączenia SMTP"
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr "Adres serwera IMAP"
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr "Port serwera IMAP"
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr "Zabezpieczenie połączenia IMAP"
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr "Użyj tych ustawień w swoim komputerze, tablecie lub telefonie."
#: forms.py:20
msgid "Documents storage"
msgstr "Magazyn dokumentów"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "Lokalizacja gdzie zapisywać dokumenty"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "Ścieżka do lokalizacji, gdzie będą zapisywane PDF'y"
#: forms.py:28
msgid "Security options"
msgstr "Opcje bezpieczeństwa"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "Usuwaj dokumenty po ich pierwszym pobraniu"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "Automatycznie usuwaj dokumenty jak tylko zostaną pobranie przez ten interfejs"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "Generuj dokumenty tylko podczas zakładania konta"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "Generuj nowy dokument tylko wtedy, kiedy nowe konto jest zakładane. Jeśli ustawione na nie, to nowy dokument będzie tworzony przy każdej zmianie hasła."
#: forms.py:49
msgid "Customization options"
msgstr "Opcje dostosowania"
#: forms.py:52
msgid "Title"
msgstr "Tytuł"
#: forms.py:54
msgid "The document's title"
msgstr "Tytuł dokumentu"
#: forms.py:58
msgid "Web panel url"
msgstr "URL panelu Web"
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr "URL panelu Modoboa"
#: forms.py:63
msgid "Custom message"
msgstr "Własna wiadomość"
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr "Dodatkowa wiadomość która pojawi się na końcu dokumentu."
#: forms.py:70
msgid "Include mail client connection settings"
msgstr "Załącz ustawienia połączenia dla programu pocztowego klienta"
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr "Załącz wymagane do konfiguracji, informacje o połączeniu SMTP i IMAP "
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr "Adres serwera SMTP (nazwa hosta lub IP)"
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr "Port nasłuchujący serwera SMTP"
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr "Mechanizm zabezpieczenia połączenia"
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr "Adres serwera IMAP (nazwa hosta lub IP)"
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr "Port nasłuchujący serwera IMAP"
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "Pobieranie danych uwierzytelniających użytkownika w formacie PDF"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "Nie można utworzyć katalogu do przechowywania PDF'ów (%s)"
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "Poświadczenia PDF"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "Generuj dokumenty PDF zawierające dane uwierzytelniające użytkowników"
#: views.py:30
msgid "No document available for this user"
msgstr "Brak dokumentu dla tego użytkownika"
@@ -0,0 +1,211 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Rafael Barretto <ch3yd@hotmail.com>, 2015
# Rafael Barretto <ch3yd@hotmail.com>, 2017-2018
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2018-09-25 03:25+0000\n"
"Last-Translator: Rafael Barretto <ch3yd@hotmail.com>\n"
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/tonio/modoboa/language/pt_BR/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt_BR\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: constants.py:7
msgid "None"
msgstr "Nenhuma"
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "Informações da conta pessoal"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Desenvolvido por Modoboa - Hospedagem de email de forma simples"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\nCaro(a) %s, este documento contem as credenciais que você irá precisar\npara conectar ao Modoboa. Verifique o conteúdo e destrua\neste documento assim que possível.\n"
#: documents.py:75
msgid "Web panel:"
msgstr "Painel web:"
#: documents.py:78
msgid "Username"
msgstr "Nome de usuário"
#: documents.py:79
msgid "Password"
msgstr "Senha"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr "\nAqui você poderá ver seus emails a qualquer momento online, crie filtros ou gerencie \nseus contatos.\n"
#: documents.py:96
msgid "Please change your password!"
msgstr "Por favor mude sua senha!"
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr "Configuração para PC/Tablet/Smartphone:"
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr "Endereço do servidor SMTP"
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr "Porta do servidor SMTP"
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr "Conexão segura SMTP"
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr "Endereço do servidor IMAP"
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr "Porta do servidor IMAP"
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr "Conexão segura IMAP"
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr "Use essas configurações para seu computador, tablet ou telefone."
#: forms.py:20
msgid "Documents storage"
msgstr "Armazenamento de documentos"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "Diretório para salvar documentos"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "Caminho para o diretório onde os arquivos PDF serão salvos"
#: forms.py:28
msgid "Security options"
msgstr "Opções de segurança"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "Deletar documentos após o primeiro download"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "Excluir automaticamente um documento logo após a sua primeira transferência a partir desta interface"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "Apenas gerar documentos na criação da conta"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "Gerar um novo documento apenas quando uma nova conta é criada. Se definido como não, um novo documento será criado cada vez que uma senha é atualizada."
#: forms.py:49
msgid "Customization options"
msgstr "Opções de customização"
#: forms.py:52
msgid "Title"
msgstr "Título"
#: forms.py:54
msgid "The document's title"
msgstr "Título do documento"
#: forms.py:58
msgid "Web panel url"
msgstr "URL do painel web"
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr "URL do painel web do Modoboa"
#: forms.py:63
msgid "Custom message"
msgstr "Mensagem personalizada"
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr "Uma mensagem personalizada irá aparecer no fim do documento"
#: forms.py:70
msgid "Include mail client connection settings"
msgstr "Incluir configurações de conexão do cliente de email"
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr "Incluir informações de conexão SMTP e IMAP necessárias para configurar um cliente de e-mail, um tablet ou um telefone"
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr "Endereço do servidor SMTP (hostname ou IP)"
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr ""
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr "Mecanismo de conexão segura"
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr ""
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr ""
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "Recuperar credenciais do usuário como um documento PDF"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "Falha ao criar o diretório que conterá documentos PDF (%s)"
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "Credenciais PDF"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "Gerar documentos em PDF contendo as credenciais dos usuários"
#: views.py:30
msgid "No document available for this user"
msgstr "Nenhum documento disponível para esse usuário"
@@ -0,0 +1,209 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2017-09-27 10:02+0000\n"
"Last-Translator: Antoine Nguyen <tonio@ngyn.org>\n"
"Language-Team: Portuguese (Portugal) (http://www.transifex.com/tonio/modoboa/language/pt_PT/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: pt_PT\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: constants.py:7
msgid "None"
msgstr ""
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr ""
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr ""
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr ""
#: documents.py:75
msgid "Web panel:"
msgstr ""
#: documents.py:78
msgid "Username"
msgstr ""
#: documents.py:79
msgid "Password"
msgstr ""
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr ""
#: documents.py:96
msgid "Please change your password!"
msgstr ""
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr ""
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr ""
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr ""
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr ""
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr ""
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr ""
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr ""
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr ""
#: forms.py:20
msgid "Documents storage"
msgstr ""
#: forms.py:23
msgid "Directory to save documents into"
msgstr ""
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr ""
#: forms.py:28
msgid "Security options"
msgstr ""
#: forms.py:31
msgid "Delete documents after the first download"
msgstr ""
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr ""
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr ""
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr ""
#: forms.py:49
msgid "Customization options"
msgstr ""
#: forms.py:52
msgid "Title"
msgstr ""
#: forms.py:54
msgid "The document's title"
msgstr ""
#: forms.py:58
msgid "Web panel url"
msgstr ""
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr ""
#: forms.py:63
msgid "Custom message"
msgstr ""
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr ""
#: forms.py:70
msgid "Include mail client connection settings"
msgstr ""
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr ""
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr ""
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr ""
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr ""
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr ""
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr ""
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr ""
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr ""
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr ""
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr ""
#: views.py:30
msgid "No document available for this user"
msgstr ""
@@ -0,0 +1,211 @@
# 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,2018
# Tomas Olsson <fettoemo@gmail.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: Modoboa\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 12:01+0200\n"
"PO-Revision-Date: 2018-08-14 08:40+0000\n"
"Last-Translator: Olle Gustafsson <olle@dalnix.se>\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"
#: constants.py:7
msgid "None"
msgstr "Ingen"
#: documents.py:52 forms.py:53
msgid "Personal account information"
msgstr "Personlig kontoinformation"
#: documents.py:55
msgid "Powered by Modoboa - Mail hosting made simple"
msgstr "Drivs av Modoboa - Mail hosting gjort enkelt"
#: documents.py:69
#, python-format
msgid ""
"\n"
"Dear %s, this document contains the credentials you will need\n"
"to connect to Modoboa. Learn the content and destroy\n"
"the document as soon as possible.\n"
msgstr "\nKära %s, detta dokument innehåller de kontouppgifter du behöver för att ansluta till Modoboa. Lär dig innehållet i detta dokument och förstör detta dokument så snart som möjligt\n"
#: documents.py:75
msgid "Web panel:"
msgstr "Webbpanel"
#: documents.py:78
msgid "Username"
msgstr "Användarnamn"
#: documents.py:79
msgid "Password"
msgstr "Lösenord"
#: documents.py:89
msgid ""
"\n"
"Here you can view your emails anytime online, create filters or manage\n"
"your contacts.\n"
msgstr "\nHär kan du se dina e-postmeddelanden när som helst online, skapa filter eller hantera\ndina kontakter.\n"
#: documents.py:96
msgid "Please change your password!"
msgstr "Vänligen ändra ditt lösenord!"
#: documents.py:102
msgid "PC/Tablet/Smartphone configuration:"
msgstr "PC/Tablet/Smartphone inställningar:"
#: documents.py:105 forms.py:79
msgid "SMTP server address"
msgstr "SMTP server adress"
#: documents.py:106 forms.py:84
msgid "SMTP server port"
msgstr "SMTP server port"
#: documents.py:107 forms.py:90
msgid "SMTP connection security"
msgstr "SMTP anslutningssäkerhet"
#: documents.py:108 forms.py:97
msgid "IMAP server address"
msgstr "IMAP server adress"
#: documents.py:109 forms.py:102
msgid "IMAP server port"
msgstr "IMAP server port"
#: documents.py:110 forms.py:108
msgid "IMAP connection security"
msgstr "IMAP anslutningssäkerhet"
#: documents.py:120
msgid "Use those settings for your computer, tablet or phone."
msgstr "Använd dessa inställningar för din dator, surfplatta eller telefon."
#: forms.py:20
msgid "Documents storage"
msgstr "Dokumentlagring"
#: forms.py:23
msgid "Directory to save documents into"
msgstr "Katalog att spara dokument till"
#: forms.py:25
msgid "Path to a directory where PDF documents will be saved"
msgstr "Sökväg till en katalog där PDF dokument kommer sparas"
#: forms.py:28
msgid "Security options"
msgstr "Säkerhetsinställningar"
#: forms.py:31
msgid "Delete documents after the first download"
msgstr "Radera dokument efter första nedladdning"
#: forms.py:34
msgid ""
"Automatically delete a document just after its first download from this "
"interface"
msgstr "Radera dokument automatiskt efter första nedladdningen"
#: forms.py:40
msgid "Generate documents only at account creation"
msgstr "Generera dokument endast vid kontoskapande"
#: forms.py:43
msgid ""
"Generate a new document only when a new account is created. If set to no, a "
"new document will be created each time a password is updated."
msgstr "Generera bara nya dokument när ett nytt konto skapas. Om detta sätts till nej, kommer ett nytt dokument skapas varje gång ett lösenord uppdateras."
#: forms.py:49
msgid "Customization options"
msgstr "Anpassningsalternativ"
#: forms.py:52
msgid "Title"
msgstr "Titel"
#: forms.py:54
msgid "The document's title"
msgstr "Dokumentets titel"
#: forms.py:58
msgid "Web panel url"
msgstr "Webbpanels länk "
#: forms.py:59
msgid "URL of the Modoboa web panel"
msgstr "URL till Modoboa webbpanel"
#: forms.py:63
msgid "Custom message"
msgstr "Anpassat meddelande"
#: forms.py:65
msgid "A custom message that will appear at the end of documents"
msgstr "Ett anpassat meddelande som kommer visas i slutet på dokumentet"
#: forms.py:70
msgid "Include mail client connection settings"
msgstr "Inkludera e-post klient inställningar"
#: forms.py:73
msgid ""
"Include required SMTP and IMAP connection information to configure a mail "
"client, a tablet or a phone"
msgstr "Inkludera SMTP och IMAP anslutningsinformation för att konfigurera en e-postklient, surfplatta eller telefon"
#: forms.py:80
msgid "Address of the SMTP server (hostname or IP)"
msgstr "Adress till SMTP servern (värdnamn eller IP)"
#: forms.py:86
msgid "Listening port of the SMTP server"
msgstr "Lyssningsport för SMTP servern"
#: forms.py:93 forms.py:111
msgid "Connection security mechanism"
msgstr "Anslutningssäkerhetsmekanism"
#: forms.py:98
msgid "Address of the IMAP server (hostname or IP)"
msgstr "Adress till IMAP servern (värdnamn eller IP)"
#: forms.py:104
msgid "Listening port of the IMAP server"
msgstr "Lyssningsport för IMAP servern"
#: handlers.py:46
msgid "Retrieve user's credentials as a PDF document"
msgstr "Hämta användares kontouppgifter som ett PDF dokument"
#: lib.py:29
#, python-format
msgid "Failed to create the directory that will contain PDF documents (%s)"
msgstr "Kunde inte skapa katalogen som innehåller PDF dokumenten (%s)"
#: modo_extension.py:16 modo_extension.py:24
msgid "PDF credentials"
msgstr "PDF kontouppgifter"
#: modo_extension.py:19
msgid "Generate PDF documents containing users' credentials"
msgstr "Generera PDF dokument innehållande användares kontouppgifter"
#: views.py:30
msgid "No document available for this user"
msgstr "Inga dokument tillgängliga för den här användaren"
+3
View File
@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.
+109
View File
@@ -0,0 +1,109 @@
"""
Copy of the original rfc6266 package, just the generation part.
Orginal code is available at https://github.com/g2p/rfc6266.
"""
from urllib.parse import quote
# RFC 2616
separator_chars = "()<>@,;:\\\"/[]?={} \t"
# RFC 5987
attr_chars_nonalnum = '!#$&+-.^_`|~'
def percent_encode(string, safe, encoding):
return quote(string, safe, encoding, errors='strict')
def is_token_char(ch):
# Must be ascii, and neither a control char nor a separator char
asciicode = ord(ch)
# < 128 means ascii, exclude control chars at 0-31 and 127,
# exclude separator characters.
return 31 < asciicode < 127 and ch not in separator_chars
def is_token(candidate):
return all(is_token_char(ch) for ch in candidate)
def is_ascii(text):
return all(ord(ch) < 128 for ch in text)
def fits_inside_codec(text, codec):
try:
text.encode(codec)
except UnicodeEncodeError:
return False
else:
return True
def is_lws_safe(text):
return normalize_ws(text) == text
def normalize_ws(text):
return ' '.join(text.split())
def qd_quote(text):
return text.replace('\\', '\\\\').replace('"', '\\"')
def build_header(
filename, disposition='attachment', filename_compat=None
):
"""Generate a Content-Disposition header for a given filename.
For legacy clients that don't understand the filename* parameter,
a filename_compat value may be given.
It should either be ascii-only (recommended) or iso-8859-1 only.
In the later case it should be a character string
(unicode in Python 2).
Options for generating filename_compat (only useful for legacy clients):
- ignore (will only send filename*);
- strip accents using unicode's decomposing normalisations,
which can be done from unicode data (stdlib), and keep only ascii;
- use the ascii transliteration tables from Unidecode (PyPI);
- use iso-8859-1
Ignore is the safest, and can be used to trigger a fallback
to the document location (which can be percent-encoded utf-8
if you control the URLs).
See https://tools.ietf.org/html/rfc6266#appendix-D
"""
# While this method exists, it could also sanitize the filename
# by rejecting slashes or other weirdness that might upset a receiver.
if disposition != 'attachment':
assert is_token(disposition)
rv = disposition
if is_token(filename):
rv += '; filename=%s' % (filename, )
return rv
elif is_ascii(filename) and is_lws_safe(filename):
qd_filename = qd_quote(filename)
rv += '; filename="%s"' % (qd_filename, )
if qd_filename == filename:
# RFC 6266 claims some implementations are iffy on qdtext's
# backslash-escaping, we'll include filename* in that case.
return rv
elif filename_compat:
if is_token(filename_compat):
rv += '; filename=%s' % (filename_compat, )
else:
assert is_lws_safe(filename_compat)
rv += '; filename="%s"' % (qd_quote(filename_compat), )
# alnum are already considered always-safe, but the rest isn't.
# Python encodes ~ when it shouldn't, for example.
rv += "; filename*=utf-8''%s" % (percent_encode(
filename, safe=attr_chars_nonalnum, encoding='utf-8'), )
# This will only encode filename_compat, if it used non-ascii iso-8859-1.
return rv.encode('iso-8859-1')
+147
View File
@@ -0,0 +1,147 @@
"""PDF credentials tests."""
import os
import shutil
import tempfile
from django.test import override_settings
from django.urls import reverse
from modoboa.admin import factories as admin_factories
from modoboa.core import models as core_models
from modoboa.lib.tests import ModoTestCase
class EventsTestCase(ModoTestCase):
"""Test event handlers."""
@classmethod
def setUpTestData(cls):
"""Create some data."""
super(EventsTestCase, cls).setUpTestData()
admin_factories.DomainFactory(name="test.com")
def setUp(self):
"""Create temp. directory to store files."""
super(EventsTestCase, self).setUp()
self.workdir = tempfile.mkdtemp()
self.set_global_parameter("storage_dir", self.workdir)
def tearDown(self):
"""Reset test env."""
shutil.rmtree(self.workdir)
def _create_account(self, username, expected_status=200):
"""Create a test account."""
values = {
"username": username,
"first_name": "Tester", "last_name": "Toto",
"role": "SimpleUsers", "quota_act": True,
"is_active": True, "email": username,
"random_password": True, "stepid": 2
}
response = self.client.post(
reverse("admin:account_add"), values,
HTTP_X_REQUESTED_WITH="XMLHttpRequest")
self.assertEqual(response.status_code, expected_status)
return values
def test_password_updated(self):
"""Check that document is generated at account creation/update."""
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
account = core_models.User.objects.get(username=values["username"])
# Check if link is present in listing page
response = self.ajax_get(reverse("admin:_identity_list"))
self.assertIn('name="get_credentials"', response["rows"])
# Try to download the file
response = self.client.get(
reverse("pdfcredentials:account_credentials",
args=[account.pk]))
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-Type"], "application/pdf")
# File have been deleted?
self.assertFalse(os.path.exists(fname))
# Try to download a second time
response = self.client.get(
reverse("pdfcredentials:account_credentials",
args=[account.pk]))
self.assertContains(response, "No document available for this user")
# Update account
values.update({"language": "en"})
self.ajax_post(
reverse("admin:account_change", args=[account.pk]), values
)
self.assertFalse(os.path.exists(fname))
self.set_global_parameter("generate_at_creation", False)
self.ajax_post(
reverse("admin:account_change", args=[account.pk]), values
)
self.assertTrue(os.path.exists(fname))
def test_with_connection_settings(self):
"""Add connection settings to documents."""
self.set_global_parameter("include_connection_settings", True)
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
def test_with_custom_message(self):
"""Add custom message to documents."""
self.set_global_parameter("custom_message", "This is a test message.")
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
def test_account_delete(self):
"""Check that document is deleted with account."""
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
account = core_models.User.objects.get(username=values["username"])
self.ajax_post(
reverse("admin:account_delete", args=[account.pk]), {}
)
self.assertFalse(os.path.exists(fname))
@override_settings(MODOBOA_LOGO="modoboa.png")
def test_with_custom_logo(self):
"""Check that document is deleted with account."""
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
def test_download_and_delete_account(self):
"""Check that document is deleted with account."""
values = self._create_account("leon@test.com")
fname = os.path.join(self.workdir, "{}.pdf".format(values["username"]))
self.assertTrue(os.path.exists(fname))
account = core_models.User.objects.get(username=values["username"])
# Try to download the file
response = self.client.get(
reverse("pdfcredentials:account_credentials",
args=[account.pk]))
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-Type"], "application/pdf")
# File have been deleted?
self.assertFalse(os.path.exists(fname))
# Delete account
self.ajax_post(
reverse("admin:account_delete", args=[account.pk]), {}
)
def test_storage_dir_creation(self):
"""Test storage directory creation."""
self.set_global_parameter("storage_dir", "/nonexistentdir")
self._create_account("leon@test.com", expected_status=500)
+10
View File
@@ -0,0 +1,10 @@
from django.urls import path
from . import views
app_name = "pdfcredentials"
urlpatterns = [
path('credentials/<int:accountid>/', views.get_account_credentials,
name="account_credentials"),
]
+37
View File
@@ -0,0 +1,37 @@
"""modoboa.pdfcredentials views."""
import os
from django.http import HttpResponse
from django.utils.translation import ugettext as _
from django.contrib.auth.decorators import (
login_required, permission_required
)
from modoboa.core.models import User
from modoboa.lib.exceptions import ModoboaException, PermDeniedException
from modoboa.parameters import tools as param_tools
from .lib import decrypt_file, get_creds_filename
from .rfc6266 import build_header
@login_required
@permission_required("core.add_user")
def get_account_credentials(request, accountid):
"""View to download a document."""
account = User.objects.get(pk=accountid)
if not request.user.can_access(account):
raise PermDeniedException()
fname = get_creds_filename(account)
if not os.path.exists(fname):
raise ModoboaException(_("No document available for this user"))
content = decrypt_file(fname)
if param_tools.get_global_parameter("delete_first_dl"):
os.remove(fname)
resp = HttpResponse(content)
resp["Content-Type"] = "application/pdf"
resp["Content-Length"] = len(content)
resp["Content-Disposition"] = build_header(os.path.basename(fname))
return resp
+1
View File
@@ -27,6 +27,7 @@ urlpatterns = [
path('', include("modoboa.core.urls")),
path('admin/', include("modoboa.admin.urls")),
path('dnstools/', include("modoboa.dnstools.urls")),
path('pdfcredentials', include("modoboa.pdfcredentials.urls")),
path('stats/', include("modoboa.maillog.urls")),
path('user/forward/', user_views.forward, name="user_forward"),
+1
View File
@@ -15,6 +15,7 @@ urlpatterns = [
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")),
]
urlpatterns += exts_pool.get_urls(category="api")
+3
View File
@@ -37,3 +37,6 @@ qrcode
aiosmtplib
aioredis==2.0.1
# PDF credentials
reportlab
+1
View File
@@ -89,6 +89,7 @@ MODOBOA_APPS = (
'modoboa.dnstools',
'modoboa.policyd',
'modoboa.maillog',
'modoboa.pdfcredentials',
# Modoboa extensions here.
)