mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
New fixes
This commit is contained in:
@@ -161,7 +161,7 @@ class DomainAliasAPITestCase(ModoAPITestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # NOQA:N802
|
||||
"""Create test data."""
|
||||
super(DomainAliasAPITestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
factories.populate_database()
|
||||
cls.dom_alias1 = factories.DomainAliasFactory(
|
||||
name="dalias1.com", target__name="test.com"
|
||||
@@ -185,7 +185,7 @@ class DomainAliasAPITestCase(ModoAPITestCase):
|
||||
self.assertEqual(response.data["name"], "dalias1.com")
|
||||
|
||||
url = reverse("v1:domain_alias-list")
|
||||
response = self.client.get("{}?domain=test.com".format(url))
|
||||
response = self.client.get(f"{url}?domain=test.com")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), 1)
|
||||
|
||||
@@ -246,7 +246,7 @@ class AccountAPITestCase(ModoAPITestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # NOQA:N802
|
||||
"""Create test data."""
|
||||
super(AccountAPITestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
factories.populate_database()
|
||||
cls.da_token = Token.objects.create(
|
||||
user=core_models.User.objects.get(username="admin@test.com")
|
||||
@@ -271,17 +271,17 @@ class AccountAPITestCase(ModoAPITestCase):
|
||||
response = response.json()
|
||||
self.assertEqual(len(response), 5)
|
||||
|
||||
response = self.client.get("{}?domain=test.com".format(url))
|
||||
response = self.client.get(f"{url}?domain=test.com")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = response.json()
|
||||
self.assertEqual(len(response), 2)
|
||||
|
||||
response = self.client.get("{}?domain=pouet.com".format(url))
|
||||
response = self.client.get(f"{url}?domain=pouet.com")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = response.json()
|
||||
self.assertEqual(len(response), 0)
|
||||
|
||||
response = self.client.get("{}?search=user@test.com".format(url))
|
||||
response = self.client.get(f"{url}?search=user@test.com")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = response.json()
|
||||
self.assertEqual(len(response), 1)
|
||||
@@ -608,7 +608,7 @@ class AliasAPITestCase(ModoAPITestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # NOQA:N802
|
||||
"""Create test data."""
|
||||
super(AliasAPITestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
cls.localconfig.parameters.set_value("enable_admin_limits", False, app="limits")
|
||||
cls.localconfig.save()
|
||||
factories.populate_database()
|
||||
@@ -624,7 +624,7 @@ class AliasAPITestCase(ModoAPITestCase):
|
||||
response = response.json()
|
||||
self.assertEqual(len(response), 3)
|
||||
|
||||
response = self.client.get("{}?domain=test.com".format(url))
|
||||
response = self.client.get(f"{url}?domain=test.com")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = response.json()
|
||||
self.assertEqual(len(response), 3)
|
||||
@@ -744,7 +744,7 @@ class SenderAddressAPITestCase(ModoAPITestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # NOQA:N802
|
||||
"""Create test data."""
|
||||
super(SenderAddressAPITestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
cls.localconfig.parameters.set_value("enable_admin_limits", False, app="limits")
|
||||
cls.localconfig.save()
|
||||
factories.populate_database()
|
||||
|
||||
@@ -136,7 +136,7 @@ class DomainSerializer(v1_serializers.DomainSerializer):
|
||||
core_signals.can_create_object.send(
|
||||
self.__class__, context=user, klass=models.Alias
|
||||
)
|
||||
address = "postmaster@{}".format(domain.name)
|
||||
address = f"postmaster@{domain.name}"
|
||||
alias = models.Alias.objects.create(
|
||||
address=address, domain=domain, enabled=True
|
||||
)
|
||||
@@ -364,9 +364,7 @@ class AccountMeSerializer(v1_serializers.AccountSerializer):
|
||||
permissions = (
|
||||
user.user_permissions.all() | Permission.objects.filter(group__user=user)
|
||||
).select_related("content_type")
|
||||
return [
|
||||
"{}.{}".format(p.content_type.app_label, p.codename) for p in permissions
|
||||
]
|
||||
return [f"{p.content_type.app_label}.{p.codename}" for p in permissions]
|
||||
|
||||
|
||||
class AccountSerializer(v1_serializers.AccountSerializer):
|
||||
|
||||
@@ -15,7 +15,7 @@ class DomainFactory(PermissionFactory):
|
||||
model = models.Domain
|
||||
django_get_or_create = ("name",)
|
||||
|
||||
name = factory.Sequence(lambda n: "domain{}.test".format(n))
|
||||
name = factory.Sequence(lambda n: f"domain{n}.test")
|
||||
type = "domain" # NOQA:A003
|
||||
quota = 0
|
||||
default_mailbox_quota = 10
|
||||
|
||||
@@ -292,12 +292,12 @@ class AccountFormMail(forms.Form, DynamicForm):
|
||||
self.fields["email"].required = True
|
||||
qset = self.mb.aliasrecipient_set.filter(alias__internal=False)
|
||||
for cpt, ralias in enumerate(qset):
|
||||
name = "aliases_{}".format(cpt + 1)
|
||||
name = f"aliases_{cpt + 1}"
|
||||
self._create_field(
|
||||
lib_fields.UTF8AndEmptyUserEmailField, name, ralias.alias.address
|
||||
)
|
||||
for cpt, saddress in enumerate(self.mb.senderaddress_set.all()):
|
||||
name = "senderaddress_{}".format(cpt + 1)
|
||||
name = f"senderaddress_{cpt + 1}"
|
||||
self._create_field(
|
||||
lib_fields.UTF8AndEmptyUserEmailField, name, saddress.address
|
||||
)
|
||||
@@ -336,14 +336,14 @@ class AccountFormMail(forms.Form, DynamicForm):
|
||||
try:
|
||||
self.domain = models.Domain.objects.get(name=domname)
|
||||
except models.Domain.DoesNotExist:
|
||||
raise forms.ValidationError(_("Domain does not exist"))
|
||||
raise forms.ValidationError(_("Domain does not exist")) from None
|
||||
if not self.mb:
|
||||
try:
|
||||
core_signals.can_create_object.send(
|
||||
sender=self.__class__, context=self.domain, object_type="mailboxes"
|
||||
)
|
||||
except lib_exceptions.ModoboaException as inst:
|
||||
raise forms.ValidationError(inst)
|
||||
raise forms.ValidationError(inst) from None
|
||||
return email
|
||||
|
||||
def clean_quota(self):
|
||||
|
||||
@@ -123,9 +123,7 @@ class AliasForm(forms.ModelForm, DynamicForm):
|
||||
"""Custom save method."""
|
||||
alias = super(AliasForm, self).save(commit=False)
|
||||
if self.cleaned_data.get("random_address"):
|
||||
alias.address = "{}@{}".format(
|
||||
Alias.generate_random_address(), alias.domain
|
||||
)
|
||||
alias.address = f"{Alias.generate_random_address()}@{alias.domain}"
|
||||
else:
|
||||
local_part, domname = split_mailbox(self.cleaned_data["address"])
|
||||
alias.domain = Domain.objects.get(name=domname)
|
||||
|
||||
@@ -80,7 +80,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
if "instance" in kwargs:
|
||||
self.old_dkim_key_length = kwargs["instance"].dkim_key_length
|
||||
self.oldname = kwargs["instance"].name
|
||||
super(DomainFormGeneral, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
params = dict(param_tools.get_global_parameters("admin"))
|
||||
self.fields["quota"].initial = params["default_domain_quota"]
|
||||
if params["default_domain_message_limit"] is not None:
|
||||
@@ -97,7 +97,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
elif "instance" in kwargs:
|
||||
d = kwargs["instance"]
|
||||
for pos, dalias in enumerate(d.domainalias_set.all()):
|
||||
name = "aliases_%d" % (pos + 1)
|
||||
name = f"aliases_{pos + 1}"
|
||||
self._create_field(forms.CharField, name, dalias.name, 3)
|
||||
|
||||
def clean_name(self):
|
||||
@@ -148,7 +148,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
|
||||
The validation way is not very smart...
|
||||
"""
|
||||
cleaned_data = super(DomainFormGeneral, self).clean()
|
||||
cleaned_data = super().clean()
|
||||
if self._errors:
|
||||
return cleaned_data
|
||||
condition = (
|
||||
@@ -203,7 +203,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
quotas update.
|
||||
|
||||
"""
|
||||
d = super(DomainFormGeneral, self).save(commit=False)
|
||||
d = super().save(commit=False)
|
||||
core_signals.can_create_object.send(
|
||||
sender=self.__class__, context=user, klass=Domain, instance=d
|
||||
)
|
||||
@@ -280,7 +280,7 @@ class DomainFormOptions(forms.Form):
|
||||
)
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(DomainFormOptions, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
results = core_signals.user_can_set_role.send(
|
||||
sender=self.__class__, user=user, role="DomainAdmins"
|
||||
)
|
||||
@@ -296,7 +296,7 @@ class DomainFormOptions(forms.Form):
|
||||
|
||||
def clean(self):
|
||||
"""Check required values."""
|
||||
cleaned_data = super(DomainFormOptions, self).clean()
|
||||
cleaned_data = super().clean()
|
||||
if cleaned_data.get("create_dom_admin"):
|
||||
if not cleaned_data.get("dom_admin_username"):
|
||||
self.add_error("dom_admin_username", _("This field is required."))
|
||||
@@ -311,7 +311,7 @@ class DomainFormOptions(forms.Form):
|
||||
return
|
||||
user = kwargs.pop("user")
|
||||
domain = kwargs.pop("domain")
|
||||
username = "%s@%s" % (self.cleaned_data["dom_admin_username"], domain.name)
|
||||
username = f"{self.cleaned_data['dom_admin_username']}@{domain.name}"
|
||||
try:
|
||||
da = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
@@ -349,7 +349,7 @@ class DomainFormOptions(forms.Form):
|
||||
core_signals.can_create_object.send(
|
||||
self.__class__, context=user, klass=Alias
|
||||
)
|
||||
address = "postmaster@{}".format(domain.name)
|
||||
address = f"postmaster@{domain.name}"
|
||||
alias = Alias.objects.create(
|
||||
address=address, domain=domain, enabled=True
|
||||
)
|
||||
@@ -386,7 +386,7 @@ class DomainForm(TabForms):
|
||||
self.forms += reduce(lambda a, b: a + b, [result[1] for result in results])
|
||||
if not self.forms:
|
||||
self.active_id = "admins"
|
||||
super(DomainForm, self).__init__(request, *args, **kwargs)
|
||||
super().__init__(request, *args, **kwargs)
|
||||
|
||||
def extra_context(self, context):
|
||||
"""Add information to template context."""
|
||||
@@ -409,7 +409,7 @@ class DomainForm(TabForms):
|
||||
if "general" in self.instances:
|
||||
instance = self.instances["general"]
|
||||
instance.oldname = instance.name
|
||||
return super(DomainForm, self).is_valid()
|
||||
return super().is_valid()
|
||||
|
||||
def save(self):
|
||||
"""Custom save method.
|
||||
@@ -437,7 +437,7 @@ class DomainWizard(WizardForm):
|
||||
"""Domain creation wizard."""
|
||||
|
||||
def __init__(self, request):
|
||||
super(DomainWizard, self).__init__(request)
|
||||
super().__init__(request)
|
||||
self.add_step(
|
||||
WizardStep(
|
||||
"general",
|
||||
|
||||
@@ -27,13 +27,9 @@ def update_domain_mxs_and_mailboxes(sender, instance, **kwargs):
|
||||
)
|
||||
if instance.old_mail_homes is None:
|
||||
return
|
||||
qset = models.Quota.objects.filter(
|
||||
username__contains="@{}".format(instance.oldname)
|
||||
)
|
||||
qset = models.Quota.objects.filter(username__contains=f"@{instance.oldname}")
|
||||
for q in qset:
|
||||
username = q.username.replace(
|
||||
"@{}".format(instance.oldname), "@{}".format(instance.name)
|
||||
)
|
||||
username = q.username.replace(f"@{instance.oldname}", f"@{instance.name}")
|
||||
models.Quota.objects.create(
|
||||
username=username, bytes=q.bytes, messages=q.messages
|
||||
)
|
||||
@@ -62,17 +58,17 @@ def create_alias_for_domainalias(sender, instance, **kwargs):
|
||||
if not kwargs.get("created"):
|
||||
return
|
||||
alias = models.Alias.objects.create(
|
||||
address="@{}".format(instance.name), enabled=True, internal=True
|
||||
address=f"@{instance.name}", enabled=True, internal=True
|
||||
)
|
||||
models.AliasRecipient.objects.create(
|
||||
address="@{}".format(instance.target.name), alias=alias
|
||||
address=f"@{instance.target.name}", alias=alias
|
||||
)
|
||||
|
||||
|
||||
@receiver(signals.post_delete, sender=models.DomainAlias)
|
||||
def remove_alias_for_domainalias(sender, instance, **kwargs):
|
||||
"""Remove the alias associated to domain alias."""
|
||||
models.Alias.objects.filter(address="@{}".format(instance.name)).delete()
|
||||
models.Alias.objects.filter(address=f"@{instance.name}").delete()
|
||||
|
||||
|
||||
@receiver(signals.post_save, sender=models.Mailbox)
|
||||
@@ -256,7 +252,7 @@ def import_account_mailbox(sender, user, account, row, **kwargs):
|
||||
raise exceptions.BadRequest(
|
||||
_("Account import failed (%s): wrong quota value")
|
||||
% account.username
|
||||
)
|
||||
) from None
|
||||
use_domain_quota = True if not quota else False
|
||||
mb = models.Mailbox(
|
||||
address=mailbox,
|
||||
|
||||
@@ -25,7 +25,7 @@ class Command(BaseCommand):
|
||||
help = "Handles rename and delete operations on mailboxes" # NOQA:A003
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Command, self).__init__(*args, **kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.logger = logging.getLogger("modoboa.admin")
|
||||
|
||||
def add_arguments(self, parser):
|
||||
@@ -44,9 +44,9 @@ class Command(BaseCommand):
|
||||
if not os.path.exists(dirname):
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
except os.error as e:
|
||||
raise OperationError(str(e))
|
||||
code, output = exec_cmd("mv %s %s" % (operation.argument, new_mail_home))
|
||||
except OSError as e:
|
||||
raise OperationError(str(e)) from None
|
||||
code, output = exec_cmd(f"mv {operation.argument} {new_mail_home}")
|
||||
if code:
|
||||
raise OperationError(output)
|
||||
|
||||
@@ -57,7 +57,7 @@ class Command(BaseCommand):
|
||||
|
||||
def onerror(function, path, excinfo):
|
||||
"""Handle errors."""
|
||||
self.logger.critical("delete failed (reason: {})".format(excinfo))
|
||||
self.logger.critical(f"delete failed (reason: {excinfo})")
|
||||
|
||||
shutil.rmtree(operation.argument, False, onerror)
|
||||
|
||||
@@ -71,7 +71,7 @@ class Command(BaseCommand):
|
||||
with open(path) as fp:
|
||||
pid = fp.read().strip()
|
||||
code, output = exec_cmd(
|
||||
"grep handle_mailbox_operations /proc/%s/cmdline" % pid
|
||||
f"grep handle_mailbox_operations /proc/{pid}/cmdline"
|
||||
)
|
||||
if not code:
|
||||
return False
|
||||
@@ -88,7 +88,7 @@ class Command(BaseCommand):
|
||||
return
|
||||
for ope in MailboxOperation.objects.all():
|
||||
try:
|
||||
f = getattr(self, "%s_mailbox" % ope.type)
|
||||
f = getattr(self, f"{ope.type}_mailbox")
|
||||
except AttributeError:
|
||||
continue
|
||||
try:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Django management command to import admin objects."""
|
||||
|
||||
import csv
|
||||
import io
|
||||
import os
|
||||
|
||||
import progressbar
|
||||
@@ -49,12 +48,12 @@ class ImportCommand(BaseCommand):
|
||||
if not os.path.isfile(filename):
|
||||
raise CommandError("File not found")
|
||||
|
||||
num_lines = sum(1 for line in io.open(filename, encoding=encoding) if line)
|
||||
num_lines = sum(1 for line in open(filename, encoding=encoding) if line)
|
||||
pbar = progressbar.ProgressBar(
|
||||
widgets=[progressbar.Percentage(), progressbar.Bar(), progressbar.ETA()],
|
||||
maxval=num_lines,
|
||||
).start()
|
||||
with io.open(filename, encoding=encoding, newline="") as f:
|
||||
with open(filename, encoding=encoding, newline="") as f:
|
||||
reader = csv.reader(f, delimiter=options["sepchar"])
|
||||
i = 0
|
||||
for row in reader:
|
||||
@@ -76,7 +75,7 @@ class ImportCommand(BaseCommand):
|
||||
"Object already exists at line {}: {}".format(
|
||||
i + 1, options["sepchar"].join(row[:2])
|
||||
)
|
||||
)
|
||||
) from None
|
||||
i += 1
|
||||
pbar.update(i)
|
||||
|
||||
@@ -101,7 +100,7 @@ class ImportCommand(BaseCommand):
|
||||
)
|
||||
)
|
||||
detector = UniversalDetector()
|
||||
with io.open(filename, "rb") as fp:
|
||||
with open(filename, "rb") as fp:
|
||||
for line in fp:
|
||||
detector.feed(line)
|
||||
if detector.done:
|
||||
|
||||
@@ -19,7 +19,7 @@ class ManageDKIMKeys(BaseCommand):
|
||||
def create_new_dkim_key(self, domain):
|
||||
"""Create a new DKIM key."""
|
||||
storage_dir = param_tools.get_global_parameter("dkim_keys_storage_dir")
|
||||
pkey_path = os.path.join(storage_dir, "{}.pem".format(domain.name))
|
||||
pkey_path = os.path.join(storage_dir, f"{domain.name}.pem")
|
||||
|
||||
alarm_qset = domain.alarms.filter(internal_name=DKIM_WRITE_ERROR)
|
||||
if not os.access(storage_dir, os.W_OK):
|
||||
@@ -43,26 +43,20 @@ class ManageDKIMKeys(BaseCommand):
|
||||
if domain.dkim_key_length
|
||||
else self.default_key_length
|
||||
)
|
||||
code, output = sysutils.exec_cmd(
|
||||
"openssl genrsa -out {} {}".format(pkey_path, key_size)
|
||||
)
|
||||
code, output = sysutils.exec_cmd(f"openssl genrsa -out {pkey_path} {key_size}")
|
||||
if code:
|
||||
print(
|
||||
"Failed to generate DKIM private key for domain {}: {}".format(
|
||||
domain.name, smart_str(output)
|
||||
)
|
||||
f"Failed to generate DKIM private key for domain {domain.name}: {smart_str(output)}"
|
||||
)
|
||||
domain.alarms.create(
|
||||
title=_("Failed to generate DKIM private key"), internal_name=DKIM_ERROR
|
||||
)
|
||||
return
|
||||
domain.dkim_private_key_path = pkey_path
|
||||
code, output = sysutils.exec_cmd("openssl rsa -in {} -pubout".format(pkey_path))
|
||||
code, output = sysutils.exec_cmd(f"openssl rsa -in {pkey_path} -pubout")
|
||||
if code:
|
||||
print(
|
||||
"Failed to generate DKIM public key for domain {}: {}".format(
|
||||
domain.name, smart_str(output)
|
||||
)
|
||||
f"Failed to generate DKIM public key for domain {domain.name}: {smart_str(output)}"
|
||||
)
|
||||
domain.alarms.create(
|
||||
title=_("Failed to generate DKIM public key"), internal_name=DKIM_ERROR
|
||||
|
||||
@@ -87,7 +87,7 @@ class CheckMXRecords(BaseCommand):
|
||||
else:
|
||||
delim = "." if ip.version == 4 else ":"
|
||||
reverse = delim.join(ip.exploded.split(delim)[::-1])
|
||||
pattern = "{}.{}.".format(reverse, provider)
|
||||
pattern = f"{reverse}.{provider}."
|
||||
try:
|
||||
result = socket.gethostbyname(pattern)
|
||||
# result from dnsbl is in ipv4 format
|
||||
@@ -128,7 +128,7 @@ class CheckMXRecords(BaseCommand):
|
||||
dnsbl_result.save()
|
||||
if not dnsbl_result.status and result:
|
||||
trigger = True
|
||||
alarm_name = "domain_mx_in_dnsbl_{}".format(provider)
|
||||
alarm_name = f"domain_mx_in_dnsbl_{provider}"
|
||||
if trigger:
|
||||
title = _("MX {} listed by DNSBL provider {}").format(mx.name, provider)
|
||||
domain.alarms.create(
|
||||
@@ -165,7 +165,7 @@ class CheckMXRecords(BaseCommand):
|
||||
"""
|
||||
alerts = []
|
||||
check = False
|
||||
mxs = [(mx, ipaddress.ip_address("%s" % mx.address)) for mx in mx_list]
|
||||
mxs = [(mx, ipaddress.ip_address(str(mx.address))) for mx in mx_list]
|
||||
valid_mxs = self.valid_mxs
|
||||
if not mxs:
|
||||
alarm, created = domain.alarms.get_or_create(
|
||||
@@ -187,7 +187,7 @@ class CheckMXRecords(BaseCommand):
|
||||
mx.managed = check = True
|
||||
mx.save()
|
||||
if check is False:
|
||||
mx_names = ["{0.name} ({0.address})".format(mx) for mx in mx_list]
|
||||
mx_names = [f"{mx.name} ({mx.address})" for mx in mx_list]
|
||||
alarm, created = domain.alarms.get_or_create(
|
||||
internal_name="domain_invalid_mx",
|
||||
status=constants.ALARM_OPENED,
|
||||
|
||||
@@ -76,18 +76,16 @@ def sometimes_mailbox_have_no_alias(**options):
|
||||
)
|
||||
if created:
|
||||
alias_created += 1
|
||||
log("Alias {0} created".format(alias), **options)
|
||||
log(f"Alias {alias} created", **options)
|
||||
recipient, created = models.AliasRecipient.objects.get_or_create(
|
||||
alias=alias, address=instance.full_address, r_mailbox=instance
|
||||
)
|
||||
if created:
|
||||
recipient_created += 1
|
||||
log("AliasRecipient {0} created".format(recipient), **options)
|
||||
log(f"AliasRecipient {recipient} created", **options)
|
||||
if alias_created or recipient_created:
|
||||
log(
|
||||
"{0} alias created. {1} alias recipient created".format(
|
||||
alias_created, recipient_created
|
||||
),
|
||||
f"{alias_created} alias created. {recipient_created} alias recipient created",
|
||||
**options,
|
||||
)
|
||||
|
||||
@@ -129,5 +127,5 @@ class Repair(BaseCommand):
|
||||
for func in known_problems:
|
||||
title = func.__doc__.strip()
|
||||
log("", **options)
|
||||
log("Checking for... {}...".format(title), **options)
|
||||
log(f"Checking for... {title}...", **options)
|
||||
func(**options)
|
||||
|
||||
@@ -48,9 +48,7 @@ class Alarm(models.Model):
|
||||
ordering = ["created"]
|
||||
|
||||
def __str__(self):
|
||||
return "[{}] {} - {}".format(
|
||||
self.created, self.domain, self.get_status_display()
|
||||
)
|
||||
return f"[{self.created}] {self.domain} - {self.get_status_display()}"
|
||||
|
||||
def close(self):
|
||||
"""Close this alarm."""
|
||||
|
||||
@@ -306,4 +306,4 @@ class AliasRecipient(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
"""Return alias and recipient."""
|
||||
return smart_str("{} -> {}".format(self.alias.address, self.address))
|
||||
return smart_str(f"{self.alias.address} -> {self.address}")
|
||||
|
||||
@@ -284,9 +284,7 @@ class Domain(mixins.MessageLimitMixin, AdminObject):
|
||||
split_record.append(' "%s"' % record)
|
||||
break
|
||||
record = "\n".join(split_record)
|
||||
return "{}._domainkey.{}. IN TXT (\n{})".format(
|
||||
self.dkim_key_selector, self.name, record
|
||||
)
|
||||
return f"{self.dkim_key_selector}._domainkey.{self.name}. IN TXT (\n{record})"
|
||||
|
||||
def add_admin(self, account):
|
||||
"""Add a new administrator to this domain.
|
||||
|
||||
@@ -25,7 +25,7 @@ class QuotaManager(models.Manager):
|
||||
|
||||
def get_domain_usage(self, domain):
|
||||
"""Return current usage for domain."""
|
||||
qset = self.get_queryset().filter(username__endswith="@{}".format(domain.name))
|
||||
qset = self.get_queryset().filter(username__endswith=f"@{domain.name}")
|
||||
result = qset.aggregate(usage=models.Sum("bytes")).get("usage", 0)
|
||||
if result is None:
|
||||
result = 0
|
||||
|
||||
@@ -45,7 +45,7 @@ class MXRecordManager(models.Manager):
|
||||
record = self.get_queryset().create(
|
||||
domain=domain,
|
||||
name="{}".format(mx_addr.strip(".")),
|
||||
address="{}".format(mx_ip_addr),
|
||||
address=f"{mx_ip_addr}",
|
||||
updated=now + delta,
|
||||
)
|
||||
yield record
|
||||
@@ -68,7 +68,7 @@ class MXRecord(models.Model):
|
||||
return bool(param_tools.get_global_parameter("valid_mxs").strip())
|
||||
|
||||
def __str__(self):
|
||||
return "{0.name} ({0.address}) for {0.domain} ".format(self)
|
||||
return f"{self.name} ({self.address}) for {self.domain} "
|
||||
|
||||
|
||||
class DNSBLQuerySet(models.QuerySet):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Map file definitions for postfix."""
|
||||
|
||||
|
||||
class DomainsMap(object):
|
||||
class DomainsMap:
|
||||
"""Map to list all domains."""
|
||||
|
||||
filename = "sql-domains.cf"
|
||||
@@ -18,7 +18,7 @@ class DomainsMap(object):
|
||||
)
|
||||
|
||||
|
||||
class DomainsAliasesMap(object):
|
||||
class DomainsAliasesMap:
|
||||
"""Map to list all domain aliases."""
|
||||
|
||||
filename = "sql-domain-aliases.cf"
|
||||
@@ -39,7 +39,7 @@ class DomainsAliasesMap(object):
|
||||
)
|
||||
|
||||
|
||||
class AliasesMap(object):
|
||||
class AliasesMap:
|
||||
"""A map to list all mailbox aliases."""
|
||||
|
||||
filename = "sql-aliases.cf"
|
||||
@@ -63,7 +63,7 @@ class AliasesMap(object):
|
||||
)
|
||||
|
||||
|
||||
class MaintainMap(object):
|
||||
class MaintainMap:
|
||||
"""Map files to list non available mailboxes."""
|
||||
|
||||
filename = "sql-maintain.cf"
|
||||
@@ -90,7 +90,7 @@ class MaintainMap(object):
|
||||
)
|
||||
|
||||
|
||||
class SenderLoginMap(object):
|
||||
class SenderLoginMap:
|
||||
"""Map file to list authorized sender addresses for a given account:
|
||||
* Its mailbox
|
||||
* Its aliases
|
||||
|
||||
@@ -576,14 +576,14 @@ class AccountTestCase(ModoTestCase):
|
||||
self.assertIn("user@test.com", response["rows"])
|
||||
old_rows = response["rows"]
|
||||
|
||||
response = self.ajax_get("{}?sort_order=-quota_value__bytes".format(url))
|
||||
response = self.ajax_get(f"{url}?sort_order=-quota_value__bytes")
|
||||
self.assertNotEqual(old_rows, response["rows"])
|
||||
old_rows = response["rows"]
|
||||
|
||||
response = self.ajax_get("{}?sort_order=-quota_usage".format(url))
|
||||
response = self.ajax_get(f"{url}?sort_order=-quota_usage")
|
||||
self.assertEqual(old_rows, response["rows"])
|
||||
|
||||
response = self.ajax_get("{}?sort_order=-unknown".format(url), status=400)
|
||||
response = self.ajax_get(f"{url}?sort_order=-unknown", status=400)
|
||||
|
||||
|
||||
@skipIf(NO_LDAP, "No ldap module installed")
|
||||
@@ -615,7 +615,7 @@ class PermissionsTestCase(ModoTestCase):
|
||||
super(PermissionsTestCase, cls).setUpTestData()
|
||||
parameters = {}
|
||||
for name, _definition in limits_utils.get_user_limit_templates():
|
||||
parameters["deflt_user_{0}_limit".format(name)] = 2
|
||||
parameters[f"deflt_user_{name}_limit"] = 2
|
||||
cls.localconfig.parameters.set_values(parameters, app="limits")
|
||||
cls.localconfig.save()
|
||||
factories.populate_database()
|
||||
|
||||
@@ -364,10 +364,10 @@ class DomainTestCase(ModoTestCase):
|
||||
self.assertIn("test.com", response["rows"])
|
||||
|
||||
old_rows = response["rows"]
|
||||
response = self.ajax_get("{}?sort_order=-name".format(url))
|
||||
response = self.ajax_get(f"{url}?sort_order=-name")
|
||||
self.assertNotEqual(old_rows, response["rows"])
|
||||
|
||||
response = self.ajax_get("{}?sort_order=allocated_quota".format(url))
|
||||
response = self.ajax_get(f"{url}?sort_order=allocated_quota")
|
||||
self.assertNotEqual(old_rows, response["rows"])
|
||||
|
||||
def test_domain_logs_list_view(self):
|
||||
@@ -386,10 +386,10 @@ class DomainTestCase(ModoTestCase):
|
||||
self.assertIn("ID1", response["rows"])
|
||||
|
||||
old_rows = response["rows"]
|
||||
response = self.ajax_get("{}?sort_order=-sender".format(url))
|
||||
response = self.ajax_get(f"{url}?sort_order=-sender")
|
||||
self.assertNotEqual(old_rows, response["rows"])
|
||||
|
||||
response = self.ajax_get("{}?searchquery=ID1".format(url))
|
||||
response = self.ajax_get(f"{url}?searchquery=ID1")
|
||||
self.assertNotEqual(old_rows, response["rows"])
|
||||
|
||||
admin = User.objects.get(username="admin@test.com")
|
||||
@@ -421,7 +421,7 @@ class DomainTestCase(ModoTestCase):
|
||||
response = self.ajax_get(url)
|
||||
self.assertIn("handle_mailboxes", response)
|
||||
self.assertIn("listalarms", response["rows"])
|
||||
response = self.ajax_get("{}?objtype=quota".format(url))
|
||||
response = self.ajax_get(f"{url}?objtype=quota")
|
||||
self.assertIn("progress-bar", response["rows"])
|
||||
|
||||
|
||||
|
||||
@@ -21,15 +21,13 @@ class DomainAliasTestCase(ModoTestCase):
|
||||
domal.target = dom
|
||||
domal.save()
|
||||
self.assertEqual(dom.domainalias_count, 1)
|
||||
self.assertTrue(Alias.objects.filter(address="@{}".format(domal.name)).exists())
|
||||
self.assertTrue(Alias.objects.filter(address=f"@{domal.name}").exists())
|
||||
|
||||
domal.name = "domalias.org"
|
||||
domal.save()
|
||||
|
||||
domal.delete()
|
||||
self.assertFalse(
|
||||
Alias.objects.filter(address="@{}".format(domal.name)).exists()
|
||||
)
|
||||
self.assertFalse(Alias.objects.filter(address=f"@{domal.name}").exists())
|
||||
|
||||
def test_form(self):
|
||||
dom = Domain.objects.get(name="test.com")
|
||||
|
||||
@@ -21,7 +21,7 @@ class ImportTestCase(ModoTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # NOQA:N802
|
||||
"""Create test data."""
|
||||
super(ImportTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
cls.localconfig.parameters.set_value("enable_admin_limits", False, app="limits")
|
||||
cls.localconfig.save()
|
||||
factories.populate_database()
|
||||
@@ -326,9 +326,7 @@ alias;user@test.com;True;admin@test.com
|
||||
f = ContentFile(
|
||||
"""domain; dómªin1.com; 1000; 100; True
|
||||
dómain; dómªin2.com; 1000; 100; True
|
||||
""".encode(
|
||||
"utf8"
|
||||
),
|
||||
""".encode(),
|
||||
name="dómains.csv",
|
||||
)
|
||||
self.client.post(reverse("admin:domain_import"), {"sourcefile": f})
|
||||
|
||||
@@ -13,7 +13,7 @@ from modoboa.lib.tests import ModoTestCase
|
||||
from .. import factories, models
|
||||
|
||||
|
||||
@override_settings(DOVECOT_LOOKUP_PATH=["{}/dovecot".format(os.path.dirname(__file__))])
|
||||
@override_settings(DOVECOT_LOOKUP_PATH=[f"{os.path.dirname(__file__)}/dovecot"])
|
||||
class MailboxOperationTestCase(ModoTestCase):
|
||||
"""Test management command."""
|
||||
|
||||
@@ -27,7 +27,7 @@ class MailboxOperationTestCase(ModoTestCase):
|
||||
"""Initiate test env."""
|
||||
super(MailboxOperationTestCase, self).setUp()
|
||||
self.workdir = tempfile.mkdtemp()
|
||||
path = "{}/test.com/admin".format(self.workdir)
|
||||
path = f"{self.workdir}/test.com/admin"
|
||||
os.makedirs(path)
|
||||
self.set_global_parameter("handle_mailboxes", True)
|
||||
self.set_global_parameter("enable_admin_limits", False, app="limits")
|
||||
@@ -39,7 +39,7 @@ class MailboxOperationTestCase(ModoTestCase):
|
||||
@mock.patch("modoboa.admin.models.Mailbox.mail_home")
|
||||
def test_delete_account(self, mail_home_mock):
|
||||
"""Check delete operation."""
|
||||
path = "{}/test.com/admin".format(self.workdir)
|
||||
path = f"{self.workdir}/test.com/admin"
|
||||
mail_home_mock.__get__ = mock.Mock(return_value=path)
|
||||
mb = models.Mailbox.objects.select_related("user").get(
|
||||
address="admin", domain__name="test.com"
|
||||
@@ -52,7 +52,7 @@ class MailboxOperationTestCase(ModoTestCase):
|
||||
@mock.patch("modoboa.admin.models.Mailbox.mail_home")
|
||||
def test_rename_account(self, mail_home_mock):
|
||||
"""Check rename operation."""
|
||||
path = "{}/test.com/admin".format(self.workdir)
|
||||
path = f"{self.workdir}/test.com/admin"
|
||||
mail_home_mock.__get__ = mock.Mock(return_value=path)
|
||||
mb = models.Mailbox.objects.select_related("user").get(
|
||||
address="admin", domain__name="test.com"
|
||||
@@ -67,7 +67,7 @@ class MailboxOperationTestCase(ModoTestCase):
|
||||
"content": "content",
|
||||
}
|
||||
self.ajax_post(reverse("admin:account_change", args=[mb.user.pk]), values)
|
||||
path = "{}/test.com/admin2".format(self.workdir)
|
||||
path = f"{self.workdir}/test.com/admin2"
|
||||
mail_home_mock.__get__ = mock.Mock(return_value=path)
|
||||
call_command("handle_mailbox_operations")
|
||||
self.assertFalse(models.MailboxOperation.objects.exists())
|
||||
@@ -76,7 +76,7 @@ class MailboxOperationTestCase(ModoTestCase):
|
||||
@mock.patch("modoboa.admin.models.Mailbox.mail_home")
|
||||
def test_delete_domain(self, mail_home_mock):
|
||||
"""Check delete operation."""
|
||||
path = "{}/test.com/admin".format(self.workdir)
|
||||
path = f"{self.workdir}/test.com/admin"
|
||||
mail_home_mock.__get__ = mock.Mock(return_value=path)
|
||||
domain = models.Domain.objects.get(name="test.com")
|
||||
self.ajax_post(reverse("admin:domain_delete", args=[domain.pk]))
|
||||
|
||||
@@ -110,7 +110,7 @@ class MXTestCase(ModoTestCase):
|
||||
mb = factories.MailboxFactory(
|
||||
address="admin",
|
||||
domain=domain,
|
||||
user__username="admin@{}".format(domain.name),
|
||||
user__username=f"admin@{domain.name}",
|
||||
user__groups=("DomainAdmins",),
|
||||
)
|
||||
domain.add_admin(mb.user)
|
||||
|
||||
@@ -11,13 +11,13 @@ from dns.rdtypes.ANY.TXT import TXT
|
||||
from dns.resolver import NXDOMAIN, NoAnswer, NoNameservers, Timeout
|
||||
|
||||
|
||||
class RRset(object):
|
||||
class RRset:
|
||||
|
||||
def __init__(self):
|
||||
self.address = "192.0.2.1"
|
||||
|
||||
|
||||
class RRsetInvalid(object):
|
||||
class RRsetInvalid:
|
||||
|
||||
def __init__(self):
|
||||
self.address = "000.0.0.0"
|
||||
|
||||
@@ -150,10 +150,10 @@ def list_quotas(request):
|
||||
domains = Domain.objects.get_for_admin(request.user)
|
||||
domains = domains.exclude(quota=0)
|
||||
if sort_order in ["name", "quota"]:
|
||||
domains = domains.order_by("{}{}".format(sort_dir, sort_order))
|
||||
domains = domains.order_by(f"{sort_dir}{sort_order}")
|
||||
elif sort_order == "allocated_quota":
|
||||
domains = domains.annotate(allocated_quota=Sum("mailbox__quota")).order_by(
|
||||
"{}{}".format(sort_dir, sort_order)
|
||||
f"{sort_dir}{sort_order}"
|
||||
)
|
||||
page = get_listing_page(domains, request.GET.get("page", 1))
|
||||
context = {
|
||||
@@ -182,7 +182,7 @@ def list_logs(request):
|
||||
)
|
||||
else:
|
||||
logs = ml_models.Maillog.objects.all()
|
||||
logs = logs.order_by("{}{}".format(sort_dir, sort_order))
|
||||
logs = logs.order_by(f"{sort_dir}{sort_order}")
|
||||
if search:
|
||||
logs = logs.filter(
|
||||
Q(sender__icontains=search)
|
||||
@@ -266,7 +266,7 @@ class DomainDetailView(auth_mixins.PermissionRequiredMixin, generic.DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Include extra widgets."""
|
||||
context = super(DomainDetailView, self).get_context_data(**kwargs)
|
||||
context = super().get_context_data(**kwargs)
|
||||
result = signals.extra_domain_dashboard_widgets.send(
|
||||
self.__class__, user=self.request.user, domain=self.object
|
||||
)
|
||||
|
||||
@@ -27,7 +27,7 @@ def _export(content, filename):
|
||||
resp = HttpResponse(content)
|
||||
resp["Content-Type"] = "text/csv"
|
||||
resp["Content-Length"] = len(content)
|
||||
resp["Content-Disposition"] = 'attachment; filename="{}"'.format(filename)
|
||||
resp["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||
return resp
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from importlib import import_module
|
||||
|
||||
|
||||
class CalendarBackend(object):
|
||||
class CalendarBackend:
|
||||
"""Base backend class."""
|
||||
|
||||
def __init__(self, calendar=None):
|
||||
@@ -29,8 +29,8 @@ class CalendarBackend(object):
|
||||
|
||||
def get_backend(name, *args, **kwargs):
|
||||
"""Return a backend instance."""
|
||||
module = import_module("modoboa.calendars.backends.{}".format(name))
|
||||
return getattr(module, "{}Backend".format(name.capitalize()))(*args, **kwargs)
|
||||
module = import_module(f"modoboa.calendars.backends.{name}")
|
||||
return getattr(module, f"{name.capitalize()}Backend")(*args, **kwargs)
|
||||
|
||||
|
||||
def get_backend_from_request(name, request, calendar=None):
|
||||
|
||||
@@ -94,7 +94,7 @@ class Caldav_Backend(CalendarBackend):
|
||||
def update_event(self, uid, original_data):
|
||||
"""Update an existing event."""
|
||||
data = dict(original_data)
|
||||
url = "{}/{}.ics".format(self.remote_cal.url.geturl(), uid)
|
||||
url = f"{self.remote_cal.url.geturl()}/{uid}.ics"
|
||||
cal = self.remote_cal.event_by_url(url)
|
||||
orig_evt = cal.vobject_instance.vevent
|
||||
if "title" in data:
|
||||
@@ -125,7 +125,7 @@ class Caldav_Backend(CalendarBackend):
|
||||
# Calendar has been changed, remove old event first.
|
||||
self.remote_cal.client.delete(url)
|
||||
remote_cal = Calendar(self.client, data["calendar"].encoded_path)
|
||||
url = "{}/{}.ics".format(remote_cal.url.geturl(), uid)
|
||||
url = f"{remote_cal.url.geturl()}/{uid}.ics"
|
||||
else:
|
||||
remote_cal = self.remote_cal
|
||||
remote_cal.add_event(cal.instance)
|
||||
@@ -133,7 +133,7 @@ class Caldav_Backend(CalendarBackend):
|
||||
|
||||
def get_event(self, uid):
|
||||
"""Retrieve and event using its uid."""
|
||||
url = "{}/{}.ics".format(self.remote_cal.url.geturl(), uid)
|
||||
url = f"{self.remote_cal.url.geturl()}/{uid}.ics"
|
||||
event = self.remote_cal.event_by_url(url)
|
||||
return self._serialize_event(event)
|
||||
|
||||
@@ -147,7 +147,7 @@ class Caldav_Backend(CalendarBackend):
|
||||
|
||||
def delete_event(self, uid):
|
||||
"""Delete an event using its uid."""
|
||||
url = "{}/{}.ics".format(self.remote_cal.url.geturl(), uid)
|
||||
url = f"{self.remote_cal.url.geturl()}/{uid}.ics"
|
||||
self.remote_cal.client.delete(url)
|
||||
|
||||
def import_events(self, fp):
|
||||
|
||||
@@ -48,7 +48,7 @@ def set_user_calendar_path(sender, instance, **kwargs):
|
||||
"""Set path at creation."""
|
||||
if instance.pk:
|
||||
return
|
||||
instance._path = "{}/{}".format(instance.mailbox.full_address, instance.name)
|
||||
instance._path = f"{instance.mailbox.full_address}/{instance.name}"
|
||||
if not instance.access_token:
|
||||
instance.access_token = token_hex(16)
|
||||
|
||||
@@ -58,6 +58,6 @@ def set_shared_calendar_path(sender, instance, **kwargs):
|
||||
"""Set path at creation."""
|
||||
if instance.pk:
|
||||
return
|
||||
instance._path = "{}/{}".format(instance.domain.name, instance.name)
|
||||
instance._path = f"{instance.domain.name}/{instance.name}"
|
||||
if not instance.access_token:
|
||||
instance.access_token = token_hex(16)
|
||||
|
||||
@@ -30,21 +30,20 @@ class Command(BaseCommand):
|
||||
def _generate_acr(self, name, user, collection, perm="rw", comment=None):
|
||||
"""Write a new access control rule to the config file."""
|
||||
if comment is not None:
|
||||
self._cfgfile.write("\n# %s" % comment)
|
||||
self._cfgfile.write(f"\n# {comment}")
|
||||
self._cfgfile.write(
|
||||
"""
|
||||
[%s]
|
||||
user = %s
|
||||
collection = %s
|
||||
permissions = %s
|
||||
f"""
|
||||
[{name}]
|
||||
user = {user}
|
||||
collection = {collection}
|
||||
permissions = {perm}
|
||||
"""
|
||||
% (name, user, collection, perm)
|
||||
)
|
||||
|
||||
def _user_access_rules(self):
|
||||
"""Create user access rules."""
|
||||
for acr in models.AccessRule.objects.select_related().all():
|
||||
section = "%s-to-%s-acr" % (acr.mailbox, acr.calendar)
|
||||
section = f"{acr.mailbox}-to-{acr.calendar}-acr"
|
||||
permission = ""
|
||||
if acr.read:
|
||||
permission += "r"
|
||||
@@ -61,7 +60,7 @@ permissions = %s
|
||||
"""Create token access rules."""
|
||||
for model in [models.UserCalendar, models.SharedCalendar]:
|
||||
for calendar in model.objects.all():
|
||||
section = "token-{}-access".format(calendar._path)
|
||||
section = f"token-{calendar._path}-access"
|
||||
self._generate_acr(
|
||||
section,
|
||||
calendar.access_token,
|
||||
@@ -73,15 +72,15 @@ permissions = %s
|
||||
def _super_admin_rules(self):
|
||||
"""Generate access rules for super administrators."""
|
||||
for sa in User.objects.filter(is_superuser=True):
|
||||
section = "sa-%s-acr" % sa.username
|
||||
section = f"sa-{sa.username}-acr"
|
||||
self._generate_acr(section, sa.username, ".*")
|
||||
|
||||
def _domain_admin_rules(self):
|
||||
"""Generate access rules for domain adminstrators."""
|
||||
for da in User.objects.filter(groups__name="DomainAdmins"):
|
||||
for domain in Domain.objects.get_for_admin(da):
|
||||
section = "da-%s-to-%s-acr" % (da.email, domain.name)
|
||||
self._generate_acr(section, da.email, "%s/user/.*" % domain.name)
|
||||
section = f"da-{da.email}-to-{domain.name}-acr"
|
||||
self._generate_acr(section, da.email, f"{domain.name}/user/.*")
|
||||
|
||||
def _generate_file(self, target):
|
||||
"""
|
||||
@@ -89,11 +88,10 @@ permissions = %s
|
||||
"""
|
||||
self._cfgfile = open(target, "w")
|
||||
self._cfgfile.write(
|
||||
"""# Rights management file for Radicale
|
||||
# This file was generated by Modoboa on %s
|
||||
f"""# Rights management file for Radicale
|
||||
# This file was generated by Modoboa on {datetime.datetime.today()}
|
||||
# DO NOT EDIT MANUALLY!
|
||||
"""
|
||||
% datetime.datetime.today()
|
||||
)
|
||||
|
||||
allow_calendars_administration = param_tools.get_global_parameter(
|
||||
|
||||
@@ -31,7 +31,7 @@ END:VCALENDAR
|
||||
"""
|
||||
|
||||
|
||||
class Url(object):
|
||||
class Url:
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
@@ -85,5 +85,5 @@ class DAVClientMock:
|
||||
def proppatch(self, url, body, dummy=None):
|
||||
return Response(200)
|
||||
|
||||
def request(self, url, method="GET", body="", headers={}):
|
||||
def request(self, url, method="GET", body="", headers=None):
|
||||
return Response(200)
|
||||
|
||||
@@ -53,7 +53,7 @@ class Calendar(models.Model):
|
||||
@property
|
||||
def share_url(self):
|
||||
"""Return calendar share url."""
|
||||
return "{}?token={}".format(self.full_url, self.access_token)
|
||||
return f"{self.full_url}?token={self.access_token}"
|
||||
|
||||
@property
|
||||
def encoded_url(self):
|
||||
|
||||
+16
-18
@@ -1,5 +1,3 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""Radicale extension unit tests."""
|
||||
|
||||
import os
|
||||
@@ -24,13 +22,13 @@ from . import models
|
||||
from . import mocks
|
||||
|
||||
|
||||
class TestDataMixin(object):
|
||||
class TestDataMixin:
|
||||
"""Create some data."""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
super(TestDataMixin, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
admin_factories.populate_database()
|
||||
cls.account = core_models.User.objects.get(username="user@test.com")
|
||||
cls.calendar = factories.UserCalendarFactory(mailbox=cls.account.mailbox)
|
||||
@@ -60,12 +58,12 @@ class AccessRuleTestCase(ModoTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
super(AccessRuleTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
populate_database()
|
||||
|
||||
def setUp(self):
|
||||
"""Initialize tests."""
|
||||
super(AccessRuleTestCase, self).setUp()
|
||||
super().setUp()
|
||||
self.rights_file_path = tempfile.mktemp()
|
||||
self.set_global_parameter(
|
||||
"rights_file_path", self.rights_file_path, app="calendars"
|
||||
@@ -394,12 +392,12 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
# FIXME: drf nested routers does not handle reverse() properly
|
||||
url = "/api/v2/user-calendars/{}/events/{}/".format(self.calendar.pk, 1234)
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/{1234}/"
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.json()["title"].startswith("Bastille"))
|
||||
|
||||
url = "/api/v2/user-calendars/{}/events/".format(self.calendar.pk)
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/"
|
||||
url = "{}?start={}&end={}".format(url, "20060712T182145Z", "20070712T182145Z")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -411,12 +409,12 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
# FIXME: drf nested routers does not handle reverse() properly
|
||||
url = "/api/v2/shared-calendars/{}/events/{}/".format(self.scalendar.pk, 1234)
|
||||
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/{1234}/"
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.json()["title"].startswith("Bastille"))
|
||||
|
||||
url = "/api/v2/shared-calendars/{}/events/".format(self.scalendar.pk)
|
||||
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/"
|
||||
url = "{}?start={}&end={}".format(url, "20060712T182145Z", "20070712T182145Z")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -427,7 +425,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
url = "/api/v2/user-calendars/{}/events/".format(self.calendar.pk)
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/"
|
||||
data = {
|
||||
"title": "Test event",
|
||||
"start": "2018-03-27T00:00:00Z",
|
||||
@@ -441,7 +439,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertIn("id", response.json())
|
||||
|
||||
url = "/api/v2/shared-calendars/{}/events/".format(self.scalendar.pk)
|
||||
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/"
|
||||
data = {
|
||||
"title": "Test event 2",
|
||||
"start": "2018-03-27T00:00:00Z",
|
||||
@@ -460,7 +458,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
url = "/api/v2/user-calendars/{}/events/1234/".format(self.calendar.pk)
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/1234/"
|
||||
data = {
|
||||
"title": "Test event",
|
||||
"start": "2018-03-27T00:00:00Z",
|
||||
@@ -483,7 +481,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
url = "/api/v2/user-calendars/{}/events/1234/".format(self.calendar.pk)
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/1234/"
|
||||
data = {
|
||||
"start": "2018-03-27T00:00:00Z",
|
||||
"end": "2018-03-28T00:00:00Z",
|
||||
@@ -506,7 +504,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
"calendar": self.scalendar.pk,
|
||||
"new_calendar_type": "shared",
|
||||
}
|
||||
url = "/api/v2/user-calendars/{}/events/1234/".format(self.calendar.pk)
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/1234/"
|
||||
response = self.client.put(url, data=data, format="json")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@@ -515,7 +513,7 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
url = "/api/v2/shared-calendars/{}/events/1234/".format(self.scalendar.pk)
|
||||
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/1234/"
|
||||
response = self.client.delete(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@@ -546,7 +544,7 @@ class AttendeeViewSetTestCase(ModoAPITestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
super(AttendeeViewSetTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
populate_database()
|
||||
cls.account = core_models.User.objects.get(username="user@test.com")
|
||||
|
||||
@@ -567,7 +565,7 @@ class MailboxViewSetTestCase(ModoAPITestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
super(MailboxViewSetTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
populate_database()
|
||||
cls.account = core_models.User.objects.get(username="user@test.com")
|
||||
|
||||
|
||||
@@ -51,9 +51,7 @@ class ContactFactory(factory.django.DjangoModelFactory):
|
||||
|
||||
first_name = "Homer"
|
||||
last_name = "Simpson"
|
||||
display_name = factory.LazyAttribute(
|
||||
lambda c: "{}{}".format(c.first_name, c.last_name)
|
||||
)
|
||||
display_name = factory.LazyAttribute(lambda c: f"{c.first_name}{c.last_name}")
|
||||
|
||||
@factory.post_generation
|
||||
def categories(self, create, extracted, **dummy_kwargs):
|
||||
|
||||
@@ -53,9 +53,9 @@ nsmap = {
|
||||
|
||||
|
||||
def ns(prefix, tag=None):
|
||||
name = "{%s}" % nsmap[prefix]
|
||||
name = f"{{{nsmap[prefix]}}}"
|
||||
if tag is not None:
|
||||
name = "%s%s" % (name, tag)
|
||||
name = f"{name}{tag}"
|
||||
return name
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class SyncCollectionQuery(elements.base.BaseElement):
|
||||
tag = ns("D", "sync-collection")
|
||||
|
||||
|
||||
class PyCardDAV(object):
|
||||
class PyCardDAV:
|
||||
"""class for interacting with a CardDAV server
|
||||
|
||||
Since PyCardDAV relies heavily on Requests [1] its SSL verification is also
|
||||
|
||||
@@ -98,10 +98,10 @@ def report_mock(url, request):
|
||||
def get_mock(url, request):
|
||||
"""Simulate a GET request."""
|
||||
uid = url.path.split("/")[-1]
|
||||
content = """
|
||||
content = f"""
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
UID:{}
|
||||
UID:{uid}
|
||||
N:Gump;Forrest
|
||||
FN:Forrest Gump
|
||||
ORG:Bubba Gump Shrimp Co.
|
||||
@@ -111,7 +111,5 @@ TEL;TYPE=HOME;VOICE:(404) 555-1212
|
||||
ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
|
||||
EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
|
||||
END:VCARD
|
||||
""".format(
|
||||
uid
|
||||
)
|
||||
"""
|
||||
return {"status_code": 200, "content": content}
|
||||
|
||||
@@ -82,18 +82,16 @@ class Contact(models.Model):
|
||||
"""Set uid for new object."""
|
||||
super().__init__(*args, **kwargs)
|
||||
if not self.pk:
|
||||
self.uid = "{}.vcf".format(uuid.uuid4())
|
||||
self.uid = f"{uuid.uuid4()}.vcf"
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return "{}/{}.vcf".format(self.addressbook.url, self.uid)
|
||||
return f"{self.addressbook.url}/{self.uid}.vcf"
|
||||
|
||||
def to_vcard(self):
|
||||
"""Convert this contact to a vCard."""
|
||||
card = vobject.vCard()
|
||||
card.add("prodid").value = "-//Modoboa//Contacts plugin {}//EN".format(
|
||||
__version__
|
||||
)
|
||||
card.add("prodid").value = f"-//Modoboa//Contacts plugin {__version__}//EN"
|
||||
card.add("uid").value = self.uid
|
||||
card.add("n").value = vobject.vcard.Name(
|
||||
family=self.last_name, given=self.first_name
|
||||
@@ -193,4 +191,4 @@ class PhoneNumber(models.Model):
|
||||
db_table = "modoboa_contacts_phonenumber"
|
||||
|
||||
def __str__(self):
|
||||
return "{}: {}".format(self.type, self.number)
|
||||
return f"{self.type}: {self.number}"
|
||||
|
||||
@@ -38,7 +38,7 @@ class EmailAddressWithNameSerializer(serializers.ModelSerializer):
|
||||
"""Return display name."""
|
||||
if obj.contact.display_name:
|
||||
return obj.contact.display_name
|
||||
return "{} {}".format(obj.contact.first_name, obj.contact.last_name)
|
||||
return f"{obj.contact.first_name} {obj.contact.last_name}"
|
||||
|
||||
|
||||
class PhoneNumberSerializer(serializers.ModelSerializer):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# coding: utf-8
|
||||
"""Contacts backend tests."""
|
||||
|
||||
import os
|
||||
@@ -172,14 +171,14 @@ class ContactViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), 3)
|
||||
|
||||
response = self.client.get("{}?search=homer".format(url))
|
||||
response = self.client.get(f"{url}?search=homer")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), 1)
|
||||
|
||||
def test_filter_by_category(self):
|
||||
"""Check filtering."""
|
||||
url = reverse("api:contact-list")
|
||||
response = self.client.get("{}?category={}".format(url, self.category.name))
|
||||
response = self.client.get(f"{url}?category={self.category.name}")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), 1)
|
||||
|
||||
@@ -300,15 +299,15 @@ class EmailAddressViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), 3)
|
||||
|
||||
response = self.client.get("{}?search=homer".format(url))
|
||||
response = self.client.get(f"{url}?search=homer")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), 1)
|
||||
|
||||
response = self.client.get("{}?search=Marge".format(url))
|
||||
response = self.client.get(f"{url}?search=Marge")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), 1)
|
||||
|
||||
response = self.client.get("{}?search=Simpson".format(url))
|
||||
response = self.client.get(f"{url}?search=Simpson")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.data), 3)
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class AccountViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
|
||||
def tfa_setup(self, request):
|
||||
"""Initiate TFA setup."""
|
||||
instance, created = TOTPDevice.objects.get_or_create(
|
||||
user=request.user, defaults={"name": "{} TOTP device".format(request.user)}
|
||||
user=request.user, defaults={"name": f"{request.user} TOTP device"}
|
||||
)
|
||||
return response.Response()
|
||||
|
||||
@@ -43,7 +43,7 @@ class AccountViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
|
||||
serializer.is_valid(raise_exception=True)
|
||||
# create static device for recovery purposes
|
||||
device = StaticDevice.objects.create(
|
||||
user=request.user, name="{} static device".format(request.user)
|
||||
user=request.user, name=f"{request.user} static device"
|
||||
)
|
||||
for cpt in range(10):
|
||||
token = StaticToken.random_token()
|
||||
|
||||
@@ -32,12 +32,12 @@ def create_static_tokens(request):
|
||||
device = request.user.staticdevice_set.first()
|
||||
if device is None:
|
||||
device = StaticDevice.objects.create(
|
||||
user=request.user, name="{} static device".format(request.user)
|
||||
user=request.user, name=f"{request.user} static device"
|
||||
)
|
||||
elif device is not None:
|
||||
return None
|
||||
tokens = []
|
||||
for cpt in range(10):
|
||||
for _cpt in range(10):
|
||||
token = StaticToken.random_token()
|
||||
device.token_set.create(token=token)
|
||||
tokens.append(token)
|
||||
|
||||
@@ -7,7 +7,7 @@ from django.template import Context, Template
|
||||
from django.utils.encoding import smart_str
|
||||
|
||||
|
||||
class Command(object):
|
||||
class Command:
|
||||
"""Base command class.
|
||||
|
||||
A valid administrative command must inherit from this class.
|
||||
|
||||
@@ -117,7 +117,7 @@ class DeployCommand(Command):
|
||||
"\n".join([line.decode() for line in output if line is not None]),
|
||||
file=sys.stderr,
|
||||
)
|
||||
print("%s failed, check your configuration" % cmd, file=sys.stderr)
|
||||
print(f"{cmd} failed, check your configuration", file=sys.stderr)
|
||||
|
||||
def ask_db_info(self, name="default"):
|
||||
"""Prompt the user for database information
|
||||
@@ -127,7 +127,7 @@ class DeployCommand(Command):
|
||||
|
||||
:param name: the connection name
|
||||
"""
|
||||
print("Configuring database connection: %s" % name)
|
||||
print(f"Configuring database connection: {name}")
|
||||
info = {
|
||||
"conn_name": name,
|
||||
"ENGINE": input("Database type (mysql, postgres or sqlite3): "),
|
||||
@@ -137,7 +137,7 @@ class DeployCommand(Command):
|
||||
|
||||
if info["ENGINE"] == "sqlite3":
|
||||
info["ENGINE"] = "django.db.backends.sqlite3"
|
||||
info["NAME"] = "%s.db" % name
|
||||
info["NAME"] = f"{name}.db"
|
||||
return info
|
||||
if info["ENGINE"] == "postgres":
|
||||
info["ENGINE"] = "django.db.backends.postgresql"
|
||||
@@ -146,7 +146,7 @@ class DeployCommand(Command):
|
||||
info["ENGINE"] = "django.db.backends.mysql"
|
||||
default_port = 3306
|
||||
info["HOST"] = input("Database host (default: 'localhost'): ")
|
||||
info["PORT"] = input("Database port (default: '%s'): " % default_port)
|
||||
info["PORT"] = input(f"Database port (default: '{default_port}'): ")
|
||||
# leave port setting empty, if default value is supplied and
|
||||
# leave it to django
|
||||
if info["PORT"] == default_port:
|
||||
@@ -181,7 +181,7 @@ class DeployCommand(Command):
|
||||
for extension in extensions:
|
||||
module = __import__(extension[1], locals(), globals(), [])
|
||||
basedir = os.path.dirname(module.__file__)
|
||||
if not os.path.exists("{0}/settings.py".format(basedir)):
|
||||
if not os.path.exists(f"{basedir}/settings.py"):
|
||||
continue
|
||||
extra_settings.append(extension[1])
|
||||
return extra_settings
|
||||
@@ -189,7 +189,7 @@ class DeployCommand(Command):
|
||||
def handle(self, parsed_args):
|
||||
django.setup()
|
||||
management.call_command("startproject", parsed_args.name, verbosity=False)
|
||||
path = "%(name)s/%(name)s" % {"name": parsed_args.name}
|
||||
path = f"{parsed_args.name}/{parsed_args.name}"
|
||||
sys.path.append(parsed_args.name)
|
||||
|
||||
conn_tpl = Template(DBCONN_TPL)
|
||||
@@ -240,7 +240,7 @@ class DeployCommand(Command):
|
||||
|
||||
mod = __import__(parsed_args.name, globals(), locals(), [smart_str("settings")])
|
||||
tpl = self._render_template(
|
||||
"%s/settings.py.tpl" % self._templates_dir,
|
||||
f"{self._templates_dir}/settings.py.tpl",
|
||||
{
|
||||
"db_connections": connections,
|
||||
"secret_key": management.utils.get_random_secret_key(),
|
||||
@@ -254,15 +254,15 @@ class DeployCommand(Command):
|
||||
"extra_settings": extra_settings,
|
||||
},
|
||||
)
|
||||
with open("%s/settings.py" % path, "w") as fp:
|
||||
with open(f"{path}/settings.py", "w") as fp:
|
||||
fp.write(tpl)
|
||||
generate_rsa_private_key(parsed_args.name)
|
||||
|
||||
shutil.copyfile("%s/urls.py.tpl" % self._templates_dir, "%s/urls.py" % path)
|
||||
os.mkdir("%s/media" % parsed_args.name)
|
||||
shutil.copyfile(f"{self._templates_dir}/urls.py.tpl", f"{path}/urls.py")
|
||||
os.mkdir(f"{parsed_args.name}/media")
|
||||
|
||||
if isfile("%s/settings.pyc" % path):
|
||||
os.unlink("%s/settings.pyc" % path)
|
||||
if isfile(f"{path}/settings.pyc"):
|
||||
os.unlink(f"{path}/settings.pyc")
|
||||
self._exec_django_command("migrate", parsed_args.name, "--noinput")
|
||||
self._exec_django_command(
|
||||
"load_initial_data",
|
||||
|
||||
@@ -124,7 +124,7 @@ class ExtensionsPool:
|
||||
root = ""
|
||||
pattern = "{}.urls_api"
|
||||
else:
|
||||
root = r"^{}/".format(ext.get_url())
|
||||
root = rf"^{ext.get_url()}/"
|
||||
pattern = "{}.urls"
|
||||
try:
|
||||
result.append(re_path(root, include(pattern.format(ext_name))))
|
||||
|
||||
@@ -10,7 +10,7 @@ from . import models
|
||||
class PermissionFactory(factory.django.DjangoModelFactory):
|
||||
"""A base factory to handle permissions."""
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@factory.post_generation
|
||||
@@ -23,7 +23,7 @@ class PermissionFactory(factory.django.DjangoModelFactory):
|
||||
class GroupFactory(factory.django.DjangoModelFactory):
|
||||
"""A factory to create Group instances."""
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = Group
|
||||
|
||||
name = "DefaultGroup"
|
||||
@@ -32,7 +32,7 @@ class GroupFactory(factory.django.DjangoModelFactory):
|
||||
class UserFactory(PermissionFactory):
|
||||
"""A factory to create User instances."""
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = models.User
|
||||
django_get_or_create = ("username",)
|
||||
|
||||
@@ -52,7 +52,7 @@ class UserFactory(PermissionFactory):
|
||||
class LogFactory(factory.django.DjangoModelFactory):
|
||||
"""Factory for Log."""
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = models.Log
|
||||
|
||||
logger = "modoboa.admin"
|
||||
|
||||
@@ -72,7 +72,7 @@ class Command(BaseCommand):
|
||||
return
|
||||
action = "delete" if options["delete"] else "disable"
|
||||
if not options["silent"]:
|
||||
answer = input("Do you want to {} those accounts? (y/N) ".format(action))
|
||||
answer = input(f"Do you want to {action} those accounts? (y/N) ")
|
||||
if not answer.lower().startswith("y"):
|
||||
return
|
||||
if action == "disable":
|
||||
|
||||
@@ -126,9 +126,7 @@ query = {{ query|safe }}
|
||||
if os.path.exists(fullpath) and not force_overwrite:
|
||||
if not self.__check_file(fullpath):
|
||||
print(
|
||||
"Cannot upgrade '{}' map because it has been modified.".format(
|
||||
mapobject.filename
|
||||
)
|
||||
f"Cannot upgrade '{mapobject.filename}' map because it has been modified."
|
||||
)
|
||||
return self.__checksums[mapobject.filename]
|
||||
mapcontent = utils.parse_map_file(fullpath)
|
||||
|
||||
@@ -91,7 +91,7 @@ class Command(BaseCommand):
|
||||
extension.load_initial_data()
|
||||
except Exception as e:
|
||||
self.stderr.write(
|
||||
"Unable to load initial data for '{}' ({}).".format(extname, str(e))
|
||||
f"Unable to load initial data for '{extname}' ({str(e)})."
|
||||
)
|
||||
else:
|
||||
signals.initial_data_loaded.send(sender=self.__class__, extname=extname)
|
||||
@@ -141,7 +141,7 @@ class Command(BaseCommand):
|
||||
base_frontend_dir = os.path.join(
|
||||
os.path.dirname(__file__), "../../../frontend_dist/"
|
||||
)
|
||||
frontend_target_dir = "{}/frontend".format(settings.BASE_DIR)
|
||||
frontend_target_dir = f"{settings.BASE_DIR}/frontend"
|
||||
if os.path.exists(base_frontend_dir):
|
||||
shutil.rmtree(frontend_target_dir, ignore_errors=True)
|
||||
shutil.copytree(base_frontend_dir, frontend_target_dir)
|
||||
|
||||
@@ -517,4 +517,4 @@ class ExtensionUpdateHistory(models.Model):
|
||||
unique_together = [("extension", "version")]
|
||||
|
||||
def __str__(self):
|
||||
return "{}: {}".format(self.extension, self.name)
|
||||
return f"{self.extension}: {self.name}"
|
||||
|
||||
@@ -58,7 +58,7 @@ def get_dovecot_schemes():
|
||||
internal_name=DOVEADM_PASS_SCHEME_ALARM,
|
||||
)
|
||||
|
||||
return ["{{{}}}".format(smart_str(scheme)) for scheme in schemes.split()], status
|
||||
return [f"{{{smart_str(scheme)}}}" for scheme in schemes.split()], status
|
||||
|
||||
|
||||
def cache_available_password_hasher(bypass_cache=False):
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext as _, ngettext
|
||||
|
||||
|
||||
class ComplexityValidator(object):
|
||||
class ComplexityValidator:
|
||||
"""Check password contains at least a few things."""
|
||||
|
||||
def __init__(self, upper=1, lower=1, digits=1, specials=1):
|
||||
|
||||
@@ -24,9 +24,9 @@ class SMSBackend:
|
||||
|
||||
def get_backend_class(name):
|
||||
"""Return class for given backend."""
|
||||
backend_module = import_module("modoboa.core.sms_backends.{}".format(name))
|
||||
backend_module = import_module(f"modoboa.core.sms_backends.{name}")
|
||||
try:
|
||||
backend_class = getattr(backend_module, "{}Backend".format(name.upper()))
|
||||
backend_class = getattr(backend_module, f"{name.upper()}Backend")
|
||||
except AttributeError:
|
||||
return None
|
||||
else:
|
||||
|
||||
@@ -148,7 +148,7 @@ class OVHBackend(SMSBackend):
|
||||
"""Send a new SMS to given recipients."""
|
||||
services = self.client.get("/sms")
|
||||
result = self.client.post(
|
||||
"/sms/{}/jobs".format(services[0]),
|
||||
f"/sms/{services[0]}/jobs",
|
||||
message=text,
|
||||
receivers=recipients,
|
||||
priority="high",
|
||||
|
||||
@@ -295,5 +295,5 @@ def currencyfmt(amount):
|
||||
"""Simple temp. filter to replace babel."""
|
||||
lang = get_language()
|
||||
if lang == "fr":
|
||||
return "{} €".format(amount)
|
||||
return "€{}".format(amount)
|
||||
return f"{amount} €"
|
||||
return f"€{amount}"
|
||||
|
||||
@@ -19,7 +19,7 @@ from modoboa.core.password_hashers.utils import get_dovecot_schemes
|
||||
from modoboa.lib.tests import NO_SMTP, ModoTestCase
|
||||
from .. import factories, models
|
||||
|
||||
DOVEADM_TEST_PATH = "{}/doveadm".format(os.path.dirname(__file__))
|
||||
DOVEADM_TEST_PATH = f"{os.path.dirname(__file__)}/doveadm"
|
||||
|
||||
|
||||
class MockedAttestedCredentialData:
|
||||
@@ -38,7 +38,7 @@ class AuthenticationTestCase(ModoTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # NOQA:N802
|
||||
"""Create test data."""
|
||||
super(AuthenticationTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
from modoboa.admin.factories import MailboxFactory
|
||||
|
||||
mailbox = MailboxFactory(
|
||||
@@ -322,7 +322,7 @@ class PasswordResetTestCase(ModoTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # NOQA:N802
|
||||
"""Create test data."""
|
||||
super(PasswordResetTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
cls.account_ok = factories.UserFactory(
|
||||
username="user@test.com",
|
||||
secondary_email="test@ext.com",
|
||||
|
||||
@@ -13,7 +13,7 @@ from .. import factories, models
|
||||
|
||||
|
||||
@skipIf(NO_LDAP, "No ldap module installed")
|
||||
class LDAPTestCaseMixin(object):
|
||||
class LDAPTestCaseMixin:
|
||||
"""Set of methods used to test LDAP features."""
|
||||
|
||||
@cached_property
|
||||
|
||||
@@ -236,7 +236,7 @@ class SettingsTestCase(ModoTestCase):
|
||||
url = reverse("core:parameters")
|
||||
response = self.ajax_get(url)
|
||||
for app in ["core", "admin", "limits"]:
|
||||
self.assertIn('data-app="{}"'.format(app), response["content"])
|
||||
self.assertIn(f'data-app="{app}"', response["content"])
|
||||
|
||||
def test_save_settings(self):
|
||||
"""Test settings save."""
|
||||
|
||||
@@ -2,4 +2,4 @@ import os
|
||||
|
||||
|
||||
def get_doveadm_test_path():
|
||||
return "{}/doveadm".format(os.path.dirname(__file__))
|
||||
return f"{os.path.dirname(__file__)}/doveadm"
|
||||
|
||||
@@ -37,7 +37,7 @@ class DashboardView(auth_mixins.AccessMixin, generic.TemplateView):
|
||||
lang = "en"
|
||||
context.update({"selection": "dashboard"})
|
||||
|
||||
feed_url = "{}{}/weblog/feeds/".format(MODOBOA_WEBSITE_URL, lang)
|
||||
feed_url = f"{MODOBOA_WEBSITE_URL}{lang}/weblog/feeds/"
|
||||
if self.request.user.role != "SuperAdmins":
|
||||
custom_feed_url = self.request.localconfig.parameters.get_value(
|
||||
"rss_feed_url"
|
||||
@@ -57,7 +57,7 @@ class DashboardView(auth_mixins.AccessMixin, generic.TemplateView):
|
||||
"hide_features_widget"
|
||||
)
|
||||
if self.request.user.is_superuser or not hide_features_widget:
|
||||
url = "{}{}/api/projects/?featured=true".format(MODOBOA_WEBSITE_URL, lang)
|
||||
url = f"{MODOBOA_WEBSITE_URL}{lang}/api/projects/?featured=true"
|
||||
features = []
|
||||
if not settings.DISABLE_DASHBOARD_EXTERNAL_QUERIES:
|
||||
try:
|
||||
|
||||
@@ -129,7 +129,7 @@ def import_report(content):
|
||||
else:
|
||||
print(f"Report skipped because of malformed data (empty {attr})")
|
||||
return
|
||||
setattr(report, "policy_{}".format(attr), node.text)
|
||||
setattr(report, f"policy_{attr}", node.text)
|
||||
report.save()
|
||||
for record in root.findall("record"):
|
||||
import_record(record, report)
|
||||
@@ -174,7 +174,7 @@ def import_report_from_email(content):
|
||||
fpo.seek(0)
|
||||
if file_type in FILE_TYPES:
|
||||
import_archive(fpo, content_type=part.get_content_type())
|
||||
except (OSError, IOError):
|
||||
except OSError:
|
||||
print("Error: the attachment does not match the mimetype")
|
||||
err = True
|
||||
else:
|
||||
@@ -219,8 +219,8 @@ def week_range(year, weeknumber):
|
||||
"""Return start and end dates of a given week."""
|
||||
tz = timezone.get_current_timezone()
|
||||
fmt = "%Y-%W-%w"
|
||||
start_week = datetime.datetime.strptime("{}-{}-{}".format(year, weeknumber, 1), fmt)
|
||||
end_week = datetime.datetime.strptime("{}-{}-{}".format(year, weeknumber, 0), fmt)
|
||||
start_week = datetime.datetime.strptime(f"{year}-{weeknumber}-{1}", fmt)
|
||||
end_week = datetime.datetime.strptime(f"{year}-{weeknumber}-{0}", fmt)
|
||||
return start_week.replace(tzinfo=tz), end_week.replace(tzinfo=tz)
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ def insert_record(target: dict, record, name: str) -> None:
|
||||
}
|
||||
target[name][record.source_ip]["total"] += record.count
|
||||
for typ in ["spf", "dkim"]:
|
||||
result = getattr(record, "{}_result".format(typ))
|
||||
result = getattr(record, f"{typ}_result")
|
||||
key = "success" if result == "pass" else "failure"
|
||||
target[name][record.source_ip][typ][key] += record.count
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Import a DMARC aggregated report."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from ... import lib
|
||||
|
||||
@@ -39,7 +39,7 @@ class Reporter(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
"""Return name and email."""
|
||||
return "{} <{}>".format(self.org_name, self.email)
|
||||
return f"{self.org_name} <{self.email}>"
|
||||
|
||||
|
||||
class Report(models.Model):
|
||||
@@ -63,7 +63,7 @@ class Report(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
"""Display provider and dates."""
|
||||
return "{}: {} -> {}".format(self.reporter, self.start_date, self.end_date)
|
||||
return f"{self.reporter}: {self.start_date} -> {self.end_date}"
|
||||
|
||||
|
||||
class Record(models.Model):
|
||||
|
||||
@@ -16,20 +16,16 @@ def next_period(period):
|
||||
"""Return next period."""
|
||||
current = period.split("-")
|
||||
if int(current[1]) == 52:
|
||||
week0 = datetime.datetime.strptime(
|
||||
"{}-0-1".format(int(current[0]) + 1), "%Y-%W-%w"
|
||||
)
|
||||
week1 = datetime.datetime.strptime(
|
||||
"{}-1-1".format(int(current[0]) + 1), "%Y-%W-%w"
|
||||
)
|
||||
week52 = datetime.datetime.strptime("{}-52-1".format(current[0]), "%Y-%W-%w")
|
||||
week0 = datetime.datetime.strptime(f"{int(current[0]) + 1}-0-1", "%Y-%W-%w")
|
||||
week1 = datetime.datetime.strptime(f"{int(current[0]) + 1}-1-1", "%Y-%W-%w")
|
||||
week52 = datetime.datetime.strptime(f"{current[0]}-52-1", "%Y-%W-%w")
|
||||
if week0 == week52 or week0 == week1:
|
||||
parts = (int(current[0]) + 1, 1)
|
||||
else:
|
||||
parts = (int(current[0]) + 1, 0)
|
||||
else:
|
||||
parts = (current[0], int(current[1]) + 1)
|
||||
return mark_safe("{}-{}".format(parts[0], parts[1]))
|
||||
return mark_safe(f"{parts[0]}-{parts[1]}")
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
@@ -37,24 +33,22 @@ def previous_period(period):
|
||||
"""Return previous period."""
|
||||
current = period.split("-")
|
||||
if int(current[1]) == 1:
|
||||
week0 = datetime.datetime.strptime("{}-0-1".format(current[0]), "%Y-%W-%w")
|
||||
week1 = datetime.datetime.strptime("{}-1-1".format(current[0]), "%Y-%W-%w")
|
||||
week0 = datetime.datetime.strptime(f"{current[0]}-0-1", "%Y-%W-%w")
|
||||
week1 = datetime.datetime.strptime(f"{current[0]}-1-1", "%Y-%W-%w")
|
||||
if week0 == week1:
|
||||
parts = (int(current[0]) - 1, 52)
|
||||
else:
|
||||
parts = (current[0], 0)
|
||||
elif int(current[1]) == 0:
|
||||
week0 = datetime.datetime.strptime("{}-0-1".format(current[0]), "%Y-%W-%w")
|
||||
week52 = datetime.datetime.strptime(
|
||||
"{}-52-1".format(int(current[0]) - 1), "%Y-%W-%w"
|
||||
)
|
||||
week0 = datetime.datetime.strptime(f"{current[0]}-0-1", "%Y-%W-%w")
|
||||
week52 = datetime.datetime.strptime(f"{int(current[0]) - 1}-52-1", "%Y-%W-%w")
|
||||
if week0 == week52:
|
||||
parts = (int(current[0]) - 1, 51)
|
||||
else:
|
||||
parts = (int(current[0]) - 1, 52)
|
||||
else:
|
||||
parts = (current[0], int(current[1]) - 1)
|
||||
return mark_safe("{}-{}".format(parts[0], parts[1]))
|
||||
return mark_safe(f"{parts[0]}-{parts[1]}")
|
||||
|
||||
|
||||
@register.filter
|
||||
|
||||
@@ -7,12 +7,12 @@ import sys
|
||||
from django.core.management import call_command
|
||||
|
||||
|
||||
class CallCommandMixin(object):
|
||||
class CallCommandMixin:
|
||||
"""A mixin to provide command execution shortcuts."""
|
||||
|
||||
def setUp(self):
|
||||
def setUp(self, *args, **kwargs):
|
||||
"""Replace stdin"""
|
||||
super(CallCommandMixin, self).setUp()
|
||||
super().setUp(*args, **kwargs)
|
||||
self.stdin = sys.stdin
|
||||
|
||||
def tearDown(self):
|
||||
|
||||
@@ -15,7 +15,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
super(DMARCViewsTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
cls.domain = admin_factories.DomainFactory(name="ngyn.org")
|
||||
|
||||
def test_domainlist_view(self):
|
||||
@@ -34,7 +34,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
|
||||
user = core_models.User.objects.get(username="admin")
|
||||
self.client.force_login(user)
|
||||
url = reverse("dmarc:domain_report", args=[self.domain.pk])
|
||||
response = self.client.get("{}?period=2015-26".format(url))
|
||||
response = self.client.get(f"{url}?period=2015-26")
|
||||
self.assertContains(response, "'Failed', 100.0")
|
||||
|
||||
def test_domainreport_view_week0(self):
|
||||
@@ -43,7 +43,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
|
||||
user = core_models.User.objects.get(username="admin")
|
||||
self.client.force_login(user)
|
||||
url = reverse("dmarc:domain_report", args=[self.domain.pk])
|
||||
response = self.client.get("{}?period=2019-0".format(url))
|
||||
response = self.client.get(f"{url}?period=2019-0")
|
||||
self.assertContains(response, "Dec. 31, 2018")
|
||||
|
||||
def test_domainreport_view_week1(self):
|
||||
@@ -52,7 +52,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
|
||||
user = core_models.User.objects.get(username="admin")
|
||||
self.client.force_login(user)
|
||||
url = reverse("dmarc:domain_report", args=[self.domain.pk])
|
||||
response = self.client.get("{}?period=2019-1".format(url))
|
||||
response = self.client.get(f"{url}?period=2019-1")
|
||||
self.assertContains(response, "Jan. 7, 2019")
|
||||
|
||||
def test_domainreport_view_week52(self):
|
||||
@@ -61,7 +61,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
|
||||
user = core_models.User.objects.get(username="admin")
|
||||
self.client.force_login(user)
|
||||
url = reverse("dmarc:domain_report", args=[self.domain.pk])
|
||||
response = self.client.get("{}?period=2018-52".format(url))
|
||||
response = self.client.get(f"{url}?period=2018-52")
|
||||
self.assertContains(response, "Dec. 30, 2018")
|
||||
|
||||
def test_domainreport_view_arc(self):
|
||||
@@ -70,7 +70,7 @@ class DMARCViewsTestCase(mixins.CallCommandMixin, ModoTestCase):
|
||||
user = core_models.User.objects.get(username="admin")
|
||||
self.client.force_login(user)
|
||||
url = reverse("dmarc:domain_report", args=[self.domain.pk])
|
||||
response = self.client.get("{}?period=2021-22".format(url))
|
||||
response = self.client.get(f"{url}?period=2021-22")
|
||||
self.assertContains(response, "'Fully aligned', 86.0")
|
||||
self.assertContains(response, "'Partially aligned', 8.0")
|
||||
self.assertContains(response, "'Forwarded', 4.0")
|
||||
|
||||
@@ -33,7 +33,7 @@ def insert_record(target, record, name):
|
||||
}
|
||||
target[name][record.source_ip]["total"] += record.count
|
||||
for typ in ["spf", "dkim"]:
|
||||
result = getattr(record, "{}_result".format(typ))
|
||||
result = getattr(record, f"{typ}_result")
|
||||
target[name][record.source_ip][typ][result] += record.count
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ def week_range(year, weeknumber):
|
||||
"""Return start and end dates of a given week."""
|
||||
tz = timezone.get_current_timezone()
|
||||
fmt = "%Y-%W-%w"
|
||||
start_week = datetime.datetime.strptime("{}-{}-{}".format(year, weeknumber, 1), fmt)
|
||||
end_week = datetime.datetime.strptime("{}-{}-{}".format(year, weeknumber, 0), fmt)
|
||||
start_week = datetime.datetime.strptime(f"{year}-{weeknumber}-{1}", fmt)
|
||||
end_week = datetime.datetime.strptime(f"{year}-{weeknumber}-{0}", fmt)
|
||||
return start_week.replace(tzinfo=tz), end_week.replace(tzinfo=tz)
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class DomainReportView(auth_mixins.PermissionRequiredMixin, generic.TemplateView
|
||||
if not self.period:
|
||||
year, week, day = timezone.now().isocalendar()
|
||||
week -= 1
|
||||
self.period = "{}-{}".format(year, week)
|
||||
self.period = f"{year}-{week}"
|
||||
else:
|
||||
year, week = self.period.split("-")
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ def _get_record_type_value(records, rr_type):
|
||||
# Multiple strings are separated by a space as described in:
|
||||
# https://tools.ietf.org/html/rfc4408#section-3.1.3
|
||||
value = str(record).replace('" "', "").strip('"')
|
||||
if value.startswith("v={}".format(rr_type)):
|
||||
if value.startswith(f"v={rr_type}"):
|
||||
return value
|
||||
return None
|
||||
|
||||
@@ -30,14 +30,14 @@ def get_spf_record(domain):
|
||||
|
||||
def get_dkim_record(domain, selector):
|
||||
"""Return DKIM records form domain (if any)."""
|
||||
name = "{}._domainkey.{}".format(selector, domain)
|
||||
name = f"{selector}._domainkey.{domain}"
|
||||
records = admin_lib.get_dns_records(name, "TXT")
|
||||
return _get_record_type_value(records, "DKIM1")
|
||||
|
||||
|
||||
def get_dmarc_record(domain):
|
||||
"""Return DMARC record for domain (if any)."""
|
||||
name = "_dmarc.{}".format(domain)
|
||||
name = f"_dmarc.{domain}"
|
||||
records = admin_lib.get_dns_records(name, "TXT")
|
||||
return _get_record_type_value(records, "DMARC1")
|
||||
|
||||
@@ -61,12 +61,12 @@ def _get_simple_record(name):
|
||||
|
||||
def get_autoconfig_record(domain):
|
||||
"""Return autoconfig record for domain (if any)."""
|
||||
return _get_simple_record("autoconfig.{}".format(domain))
|
||||
return _get_simple_record(f"autoconfig.{domain}")
|
||||
|
||||
|
||||
def get_autodiscover_record(domain):
|
||||
"""Return autodiscover record for domain (if any)."""
|
||||
return _get_simple_record("autodiscover.{}".format(domain))
|
||||
return _get_simple_record(f"autodiscover.{domain}")
|
||||
|
||||
|
||||
class DNSSyntaxError(Exception):
|
||||
@@ -167,7 +167,7 @@ def check_spf_syntax(record):
|
||||
continue
|
||||
for mechanism in constants.SPF_MECHANISMS:
|
||||
if part.startswith(mechanism):
|
||||
globals()["check_spf_{}".format(mechanism)](part)
|
||||
globals()[f"check_spf_{mechanism}"](part)
|
||||
mechanisms.append(mechanism)
|
||||
break
|
||||
else:
|
||||
|
||||
@@ -50,7 +50,7 @@ class DNSRecord(models.Model):
|
||||
objects = DNSRecordManager()
|
||||
|
||||
def __str__(self):
|
||||
return "{} ({}): {}".format(self.domain, self.type, self.value)
|
||||
return f"{self.domain} ({self.type}): {self.value}"
|
||||
|
||||
def get_dns_record(self):
|
||||
"""Retrieve corresponding DNS record."""
|
||||
@@ -59,13 +59,13 @@ class DNSRecord(models.Model):
|
||||
self.domain.name, self.domain.dkim_key_selector
|
||||
)
|
||||
else:
|
||||
func = getattr(lib, "get_{}_record".format(self.type))
|
||||
func = getattr(lib, f"get_{self.type}_record")
|
||||
self.value = func(self.domain.name)
|
||||
|
||||
def check_syntax(self, ttl=7200):
|
||||
"""Check record syntax."""
|
||||
try:
|
||||
func = getattr(lib, "check_{}_syntax".format(self.type))
|
||||
func = getattr(lib, f"check_{self.type}_syntax")
|
||||
result = func(self.value)
|
||||
except lib.DNSSyntaxError as err:
|
||||
self.error = str(err)[:50]
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""A plugin to migrate mailboxes using IMAP."""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from pkg_resources import get_distribution, DistributionNotFound
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Viewsets for API V2 for imap_migration."""
|
||||
|
||||
import imaplib
|
||||
import socket
|
||||
import ssl
|
||||
|
||||
from rest_framework import response, filters, mixins, permissions, viewsets
|
||||
@@ -38,7 +37,7 @@ class EmailProviderViewSet(GetThrottleViewsetMixin, viewsets.ModelViewSet):
|
||||
imaplib.IMAP4_SSL(address, port)
|
||||
else:
|
||||
imaplib.IMAP4(address, port)
|
||||
except (socket.error, imaplib.IMAP4.error, ssl.SSLError):
|
||||
except (OSError, imaplib.IMAP4.error, ssl.SSLError):
|
||||
return response.Response({"status": "failed"}, 400)
|
||||
return response.Response(status=200)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""IMAP authentication backend for Django."""
|
||||
|
||||
import imaplib
|
||||
import socket
|
||||
import ssl
|
||||
|
||||
from django.utils.encoding import smart_bytes, smart_str
|
||||
@@ -45,8 +44,10 @@ class IMAPBackend:
|
||||
conn = imaplib.IMAP4_SSL(address, port)
|
||||
else:
|
||||
conn = imaplib.IMAP4(address, port)
|
||||
except (socket.error, imaplib.IMAP4.error, ssl.SSLError) as error:
|
||||
raise ModoboaException(_("Connection to IMAP server failed: %s") % error)
|
||||
except (OSError, imaplib.IMAP4.error, ssl.SSLError) as error:
|
||||
raise ModoboaException(
|
||||
_(f"Connection to IMAP server failed: {error}")
|
||||
) from None
|
||||
|
||||
try:
|
||||
typ, data = conn.login(smart_bytes(username), smart_str(password))
|
||||
@@ -68,9 +69,7 @@ class IMAPBackend:
|
||||
orig_username = username
|
||||
# Check if old addresses must be converted
|
||||
if self.provider_domain.new_domain:
|
||||
username = "{}@{}".format(
|
||||
self.address, self.provider_domain.new_domain.name
|
||||
)
|
||||
username = f"{self.address}@{self.provider_domain.new_domain.name}"
|
||||
user, created = core_models.User.objects.get_or_create(
|
||||
username__iexact=username,
|
||||
defaults={"username": username.lower(), "email": username.lower()},
|
||||
|
||||
@@ -13,7 +13,7 @@ class EmailProviderFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = models.EmailProvider
|
||||
|
||||
name = factory.Sequence(lambda n: "Provider {}".format(n))
|
||||
name = factory.Sequence(lambda n: f"Provider {n}")
|
||||
address = "127.0.0.1"
|
||||
port = 143
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class Command(BaseCommand):
|
||||
"imap_create_folders": conf["create_folders"],
|
||||
"imap_folder_filter_exclude": conf["folder_filter_exclude"],
|
||||
"imap_folder_filter_include": ", ".join(
|
||||
"'{0}'".format(w) for w in conf["folder_filter_include"].split(",")
|
||||
f"'{w}'" for w in conf["folder_filter_include"].split(",")
|
||||
),
|
||||
"migrations": Migration.objects.select_related(
|
||||
"provider", "mailbox__domain"
|
||||
|
||||
@@ -21,7 +21,7 @@ from . import factories
|
||||
from . import models
|
||||
|
||||
|
||||
class DataMixin(object):
|
||||
class DataMixin:
|
||||
"""A mixin to provide test data."""
|
||||
|
||||
@classmethod
|
||||
|
||||
+8
-15
@@ -14,7 +14,7 @@ from modoboa.lib.exceptions import InternalError
|
||||
|
||||
def create_connection(srv_address, srv_port, config, username, password):
|
||||
"""Create a new connection with given server."""
|
||||
uri = "{}:{}".format(srv_address, srv_port)
|
||||
uri = f"{srv_address}:{srv_port}"
|
||||
uri = "{}://{}".format("ldaps" if config["ldap_secured"] == "ssl" else "ldap", uri)
|
||||
conn = ldap.initialize(uri)
|
||||
conn.protocol_version = 3
|
||||
@@ -152,7 +152,7 @@ def find_user_groups(conn, config, dn, entry):
|
||||
or config["ldap_group_type"] == "groupofnames"
|
||||
)
|
||||
if condition:
|
||||
flt = "(member={})".format(dn)
|
||||
flt = f"(member={dn})"
|
||||
elif config["ldap_group_type"] == "posixgroup":
|
||||
flt = "(memberUid={})".format(force_str(entry["uid"][0]))
|
||||
|
||||
@@ -204,7 +204,7 @@ def import_accounts_from_ldap(config):
|
||||
break
|
||||
if email is None:
|
||||
if grp == "SimpleUsers":
|
||||
print("Skipping {} because no email found".format(dn))
|
||||
print(f"Skipping {dn} because no email found")
|
||||
continue
|
||||
else:
|
||||
username = email
|
||||
@@ -235,8 +235,8 @@ def build_ldap_uri(config, node=""):
|
||||
node += "_"
|
||||
return "{}://{}:{}".format(
|
||||
"ldaps" if config["ldap_secured"] == "ssl" else "ldap",
|
||||
config["ldap_{}server_address".format(node)],
|
||||
config["ldap_{}server_port".format(node)],
|
||||
config[f"ldap_{node}server_address"],
|
||||
config[f"ldap_{node}server_port"],
|
||||
)
|
||||
|
||||
|
||||
@@ -259,18 +259,11 @@ def update_dovecot_config_file(config):
|
||||
|
||||
with open(conf_file, "w") as fp:
|
||||
fp.write(
|
||||
"""uris = {uris}
|
||||
f"""uris = {uris}
|
||||
dn = "{bind_dn}"
|
||||
dnpass = '{bind_pwd}'
|
||||
base = {base}
|
||||
user_filter = {user_filter}
|
||||
pass_filter = {pass_filter}
|
||||
""".format(
|
||||
uris=uris,
|
||||
bind_dn=bind_dn,
|
||||
bind_pwd=bind_pwd,
|
||||
base=base,
|
||||
user_filter=user_filter,
|
||||
pass_filter=user_filter,
|
||||
)
|
||||
pass_filter = {user_filter}
|
||||
"""
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ from django.utils.translation import gettext as _
|
||||
logger = logging.getLogger("modoboa.admin")
|
||||
|
||||
|
||||
class ModoAPIClient(object):
|
||||
class ModoAPIClient:
|
||||
"""A simple client for the public API."""
|
||||
|
||||
def __init__(self, api_url=None):
|
||||
@@ -57,10 +57,10 @@ class ModoAPIClient(object):
|
||||
|
||||
def register_instance(self, hostname):
|
||||
"""Register this instance."""
|
||||
url = "{}instances/search/?hostname={}".format(self._api_url, hostname)
|
||||
url = f"{self._api_url}instances/search/?hostname={hostname}"
|
||||
instance = self.__send_request(url)
|
||||
if instance is None:
|
||||
url = "{}instances/".format(self._api_url)
|
||||
url = f"{self._api_url}instances/"
|
||||
data = {"hostname": hostname, "known_version": self.local_core_version}
|
||||
try:
|
||||
response = requests.post(url, data=data)
|
||||
@@ -76,7 +76,7 @@ class ModoAPIClient(object):
|
||||
|
||||
def update_instance(self, pk, data):
|
||||
"""Update instance and send stats."""
|
||||
url = "{}instances/{}/".format(self._api_url, pk)
|
||||
url = f"{self._api_url}instances/{pk}/"
|
||||
try:
|
||||
response = requests.put(url, data=data)
|
||||
except RequestException as err:
|
||||
@@ -89,5 +89,5 @@ class ModoAPIClient(object):
|
||||
|
||||
def versions(self):
|
||||
"""Fetch core and extension versions."""
|
||||
url = "{}versions/".format(self._api_url)
|
||||
url = f"{self._api_url}versions/"
|
||||
return self.__send_request(url)
|
||||
|
||||
@@ -9,7 +9,7 @@ from modoboa.lib.email_utils import split_mailbox
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
|
||||
class SMTPBackend(object):
|
||||
class SMTPBackend:
|
||||
"""A backend to authenticate against an SMTP server."""
|
||||
|
||||
def authenticate(self, request, username=None, password=None):
|
||||
@@ -126,7 +126,7 @@ try:
|
||||
@classmethod
|
||||
def setting_fullname(cls, setting):
|
||||
"""Return fullname for given setting."""
|
||||
return "{}{}".format(cls.settings_prefix, setting)
|
||||
return f"{cls.settings_prefix}{setting}"
|
||||
|
||||
class LDAPBackend(LDAPBackendBase):
|
||||
"""Primary LDAP backend."""
|
||||
|
||||
@@ -31,7 +31,7 @@ class EmailAddress:
|
||||
def __init__(self, address):
|
||||
self.name, self.address = u2u_decode.decode_address(address)
|
||||
if self.name:
|
||||
self.fulladdress = "{} <{}>".format(self.name, self.address)
|
||||
self.fulladdress = f"{self.name} <{self.address}>"
|
||||
else:
|
||||
self.fulladdress = self.address
|
||||
|
||||
@@ -446,7 +446,7 @@ def sendmail_fromfile(sender, rcpt, fname):
|
||||
"""
|
||||
try:
|
||||
fp = open(fname)
|
||||
except IOError as e:
|
||||
except OSError as e:
|
||||
return False, str(e)
|
||||
|
||||
content = """From: %s
|
||||
|
||||
@@ -17,7 +17,7 @@ from modoboa.lib.web_utils import render_to_json_response
|
||||
ABC = abc.ABCMeta(force_str("ABC"), (object,), {})
|
||||
|
||||
|
||||
class WizardStep(object):
|
||||
class WizardStep:
|
||||
"""A wizard step."""
|
||||
|
||||
def __init__(self, uid, formclass, title, formtpl=None, new_args=None):
|
||||
@@ -188,7 +188,7 @@ class WizardForm(ABC):
|
||||
"""
|
||||
|
||||
|
||||
class DynamicForm(object):
|
||||
class DynamicForm:
|
||||
"""
|
||||
A form which accepts dynamic fields.
|
||||
|
||||
@@ -234,7 +234,7 @@ class DynamicForm(object):
|
||||
self.data = ndata
|
||||
|
||||
|
||||
class TabForms(object):
|
||||
class TabForms:
|
||||
"""
|
||||
Simple forms container.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Custom pagination tools.
|
||||
"""
|
||||
|
||||
|
||||
class Page(object):
|
||||
class Page:
|
||||
"""Class to represent a page."""
|
||||
|
||||
def __init__(self, paginator, pageid, id_start, id_stop, baseurl=None):
|
||||
@@ -66,7 +66,7 @@ class Page(object):
|
||||
return lid
|
||||
|
||||
|
||||
class Paginator(object):
|
||||
class Paginator:
|
||||
"""Pagination class."""
|
||||
|
||||
def __init__(self, total, elems_per_page):
|
||||
|
||||
@@ -75,7 +75,7 @@ def doveadm_cmd(params, pinput=None, capture_output=True, **kwargs):
|
||||
sudo_user = dovecot_user if curuser != dovecot_user else None
|
||||
if dpath:
|
||||
return exec_cmd(
|
||||
"{} {}".format(dpath, params),
|
||||
f"{dpath} {params}",
|
||||
sudo_user=sudo_user,
|
||||
pinput=pinput,
|
||||
capture_output=capture_output,
|
||||
|
||||
@@ -94,4 +94,4 @@ def render_field_width(field):
|
||||
width = form.field_widths[field.name]
|
||||
else:
|
||||
width = 5
|
||||
return "col-sm-{0}".format(width)
|
||||
return f"col-sm-{width}"
|
||||
|
||||
@@ -7,7 +7,7 @@ import tempfile
|
||||
from django.core.management import call_command
|
||||
|
||||
|
||||
class MapFilesTestCaseMixin(object):
|
||||
class MapFilesTestCaseMixin:
|
||||
"""A generic test case to check map files generation."""
|
||||
|
||||
MAP_FILES = None
|
||||
@@ -21,13 +21,13 @@ class MapFilesTestCaseMixin(object):
|
||||
shutil.rmtree(self.workdir)
|
||||
|
||||
def _test_maps_generation(self, engine):
|
||||
dburl = "{0}://user:password@localhost/testdb".format(engine)
|
||||
dburl = f"{engine}://user:password@localhost/testdb"
|
||||
call_command(
|
||||
"generate_postfix_maps", "--dburl", dburl, "--destdir", self.workdir
|
||||
)
|
||||
|
||||
for mapfile in self.MAP_FILES:
|
||||
path = "{0}/{1}".format(self.workdir, mapfile)
|
||||
path = f"{self.workdir}/{mapfile}"
|
||||
self.assertTrue(os.path.exists(path))
|
||||
with open(path) as fpo:
|
||||
content = fpo.read()
|
||||
|
||||
@@ -19,7 +19,7 @@ try:
|
||||
s = socket.create_connection(("127.0.0.1", 25))
|
||||
s.close()
|
||||
NO_SMTP = False
|
||||
except socket.error:
|
||||
except OSError:
|
||||
NO_SMTP = True
|
||||
|
||||
try:
|
||||
@@ -36,7 +36,7 @@ class ParametersMixin:
|
||||
@classmethod
|
||||
def setUpTestData(cls): # noqa
|
||||
"""Set LocalConfig instance."""
|
||||
super(ParametersMixin, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
cls.localconfig = core_models.LocalConfig.objects.first()
|
||||
|
||||
def set_global_parameter(self, name, value, app=None):
|
||||
@@ -64,7 +64,7 @@ class ModoTestCase(ParametersMixin, TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # noqa
|
||||
"""Create a default user."""
|
||||
super(ModoTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
management.call_command("load_initial_data", "--no-frontend")
|
||||
|
||||
def setUp(self, username="admin", password="password"):
|
||||
@@ -101,7 +101,7 @@ class ModoAPITestCase(ParametersMixin, APITestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # noqa
|
||||
"""Create a default user."""
|
||||
super(ModoAPITestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
management.call_command("load_initial_data")
|
||||
cls.token = Token.objects.create(
|
||||
user=core_models.User.objects.get(username="admin")
|
||||
@@ -109,7 +109,7 @@ class ModoAPITestCase(ParametersMixin, APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""Setup."""
|
||||
super(ModoAPITestCase, self).setUp()
|
||||
super().setUp()
|
||||
self.client.credentials(HTTP_AUTHORIZATION="Token " + self.token.key)
|
||||
self.workdir = tempfile.mkdtemp()
|
||||
self.set_global_parameter("storage_dir", self.workdir, app="pdfcredentials")
|
||||
|
||||
@@ -7,7 +7,7 @@ from django.core.validators import EmailValidator, URLValidator
|
||||
from django.utils.translation import gettext_lazy
|
||||
|
||||
|
||||
class HostnameValidator(object):
|
||||
class HostnameValidator:
|
||||
"""Validator for fqdn."""
|
||||
|
||||
message = gettext_lazy("Enter a valid domain name")
|
||||
|
||||
@@ -138,11 +138,11 @@ def size2integer(value, output_unit="B"):
|
||||
if m.group(2)[0] in ["G", "g"]:
|
||||
return int(m.group(1)) * 2**10
|
||||
else:
|
||||
raise ValueError("Unsupported output unit {}".format(output_unit))
|
||||
raise ValueError(f"Unsupported output unit {output_unit}")
|
||||
return 0
|
||||
|
||||
|
||||
class NavigationParameters(object):
|
||||
class NavigationParameters:
|
||||
"""
|
||||
Just a simple object to manipulate navigation parameters.
|
||||
"""
|
||||
|
||||
@@ -22,7 +22,7 @@ class APIAdminLimitsTestCase(lib_tests.ModoAPITestCase):
|
||||
"""Create test data."""
|
||||
super(APIAdminLimitsTestCase, cls).setUpTestData()
|
||||
for name, _definition in utils.get_user_limit_templates():
|
||||
cls.localconfig.parameters.set_value("deflt_user_{0}_limit".format(name), 2)
|
||||
cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
|
||||
cls.localconfig.save()
|
||||
populate_database()
|
||||
cls.user = User.objects.get(username="admin@test.com")
|
||||
@@ -192,9 +192,7 @@ class APIDomainLimitsTestCase(lib_tests.ModoAPITestCase):
|
||||
super(APIDomainLimitsTestCase, cls).setUpTestData()
|
||||
cls.localconfig.parameters.set_value("enable_domain_limits", True)
|
||||
for name, _definition in utils.get_domain_limit_templates():
|
||||
cls.localconfig.parameters.set_value(
|
||||
"deflt_domain_{0}_limit".format(name), 2
|
||||
)
|
||||
cls.localconfig.parameters.set_value(f"deflt_domain_{name}_limit", 2)
|
||||
cls.localconfig.save()
|
||||
populate_database()
|
||||
|
||||
@@ -255,7 +253,7 @@ class ResourcesAPITestCase(lib_tests.ModoAPITestCase):
|
||||
"""Create test data."""
|
||||
super(ResourcesAPITestCase, cls).setUpTestData()
|
||||
for name, _definition in utils.get_user_limit_templates():
|
||||
cls.localconfig.parameters.set_value("deflt_user_{0}_limit".format(name), 2)
|
||||
cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
|
||||
cls.localconfig.save()
|
||||
populate_database()
|
||||
cls.user = User.objects.get(username="admin@test.com")
|
||||
@@ -282,12 +280,12 @@ class ResourcesAPITestCase(lib_tests.ModoAPITestCase):
|
||||
compare(expected, response.data)
|
||||
|
||||
# As reseller => fails
|
||||
self.client.credentials(HTTP_AUTHORIZATION="Token {}".format(self.r_token.key))
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.r_token.key}")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
# As domain admin => fails
|
||||
self.client.credentials(HTTP_AUTHORIZATION="Token {}".format(self.da_token.key))
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.da_token.key}")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
@@ -304,7 +302,7 @@ class ResourcesAPITestCase(lib_tests.ModoAPITestCase):
|
||||
)
|
||||
|
||||
# As domain admin => fails
|
||||
self.client.credentials(HTTP_AUTHORIZATION="Token {}".format(self.da_token.key))
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.da_token.key}")
|
||||
resources.update({"domains": 2, "mailboxes": 2})
|
||||
url = reverse("v1:resources-detail", args=[self.user.pk])
|
||||
response = self.client.put(url, resources)
|
||||
@@ -312,7 +310,7 @@ class ResourcesAPITestCase(lib_tests.ModoAPITestCase):
|
||||
|
||||
# As reseller => ok
|
||||
permissions.grant_access_to_object(self.reseller, self.user, True)
|
||||
self.client.credentials(HTTP_AUTHORIZATION="Token {}".format(self.r_token.key))
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.r_token.key}")
|
||||
resources.update({"domains": 500, "mailboxes": 500})
|
||||
url = reverse("v1:resources-detail", args=[self.user.pk])
|
||||
response = self.client.put(url, resources)
|
||||
|
||||
@@ -22,7 +22,7 @@ class ResourcePoolForm(forms.Form):
|
||||
)
|
||||
if condition:
|
||||
continue
|
||||
self.fields["{}_limit".format(name)] = forms.IntegerField(
|
||||
self.fields[f"{name}_limit"] = forms.IntegerField(
|
||||
label=tpl["label"], help_text=tpl["help"]
|
||||
)
|
||||
if self.account:
|
||||
@@ -49,7 +49,7 @@ class ResourcePoolForm(forms.Form):
|
||||
def save(self):
|
||||
owner = get_object_owner(self.account)
|
||||
for name, _definition in utils.get_user_limit_templates():
|
||||
fieldname = "{}_limit".format(name)
|
||||
fieldname = f"{name}_limit"
|
||||
if fieldname not in self.cleaned_data:
|
||||
continue
|
||||
limit = self.account.userobjectlimit_set.get(name=name)
|
||||
@@ -67,7 +67,7 @@ class DomainLimitsForm(forms.Form):
|
||||
self.domain = kwargs.pop("instance", None)
|
||||
super(DomainLimitsForm, self).__init__(*args, **kwargs)
|
||||
for name, tpl in utils.get_domain_limit_templates():
|
||||
self.fields["{}_limit".format(name)] = forms.IntegerField(
|
||||
self.fields[f"{name}_limit"] = forms.IntegerField(
|
||||
label=tpl["label"], help_text=tpl["help"]
|
||||
)
|
||||
if not self.domain:
|
||||
@@ -91,7 +91,7 @@ class DomainLimitsForm(forms.Form):
|
||||
def save(self, user, **kwargs):
|
||||
"""Set limits."""
|
||||
for name, _definition in utils.get_domain_limit_templates():
|
||||
fieldname = "{}_limit".format(name)
|
||||
fieldname = f"{name}_limit"
|
||||
if fieldname not in self.cleaned_data:
|
||||
continue
|
||||
limit = self.domain.domainobjectlimit_set.get(name=name)
|
||||
|
||||
@@ -51,7 +51,7 @@ def create_user_limits(sender, instance, **kwargs):
|
||||
max_value = 0
|
||||
# creator can be None if user was created by a factory
|
||||
if not creator or creator.is_superuser:
|
||||
max_value = global_params["deflt_user_{0}_limit".format(name)]
|
||||
max_value = global_params[f"deflt_user_{name}_limit"]
|
||||
models.UserObjectLimit.objects.create(
|
||||
user=instance, name=name, content_type=ct, max_value=max_value
|
||||
)
|
||||
@@ -64,7 +64,7 @@ def create_domain_limits(sender, instance, **kwargs):
|
||||
return
|
||||
global_params = dict(param_tools.get_global_parameters("limits"))
|
||||
for name, _definition in utils.get_domain_limit_templates():
|
||||
max_value = global_params["deflt_domain_{0}_limit".format(name)]
|
||||
max_value = global_params[f"deflt_domain_{name}_limit"]
|
||||
models.DomainObjectLimit.objects.create(
|
||||
domain=instance, name=name, max_value=max_value
|
||||
)
|
||||
|
||||
@@ -9,7 +9,7 @@ from modoboa.core import models as core_models
|
||||
from . import lib, utils
|
||||
|
||||
|
||||
class ObjectLimitMixin(object):
|
||||
class ObjectLimitMixin:
|
||||
"""Common methods."""
|
||||
|
||||
@property
|
||||
@@ -54,7 +54,7 @@ class ObjectLimitMixin(object):
|
||||
"""Display current usage."""
|
||||
if self.max_value == -1:
|
||||
return _("unlimited")
|
||||
return "{}%".format(self.usage)
|
||||
return f"{self.usage}%"
|
||||
|
||||
|
||||
class UserObjectLimit(ObjectLimitMixin, models.Model):
|
||||
@@ -67,7 +67,7 @@ class UserObjectLimit(ObjectLimitMixin, models.Model):
|
||||
)
|
||||
max_value = models.IntegerField(default=0)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
unique_together = (("user", "name"),)
|
||||
|
||||
@property
|
||||
@@ -117,7 +117,7 @@ class UserObjectLimit(ObjectLimitMixin, models.Model):
|
||||
"""Display current usage."""
|
||||
if self.max_value == -1:
|
||||
return _("unlimited")
|
||||
return "{}%".format(self.usage)
|
||||
return f"{self.usage}%"
|
||||
|
||||
|
||||
class DomainObjectLimit(ObjectLimitMixin, models.Model):
|
||||
@@ -127,7 +127,7 @@ class DomainObjectLimit(ObjectLimitMixin, models.Model):
|
||||
name = models.CharField(max_length=254)
|
||||
max_value = models.IntegerField(default=0)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
unique_together = (("domain", "name"),)
|
||||
|
||||
@property
|
||||
@@ -143,7 +143,7 @@ class DomainObjectLimit(ObjectLimitMixin, models.Model):
|
||||
"""Return the current number of objects."""
|
||||
definition = self.definition
|
||||
if not definition:
|
||||
raise RuntimeError("Bad limit {}".format(self.name))
|
||||
raise RuntimeError(f"Bad limit {self.name}")
|
||||
relation = getattr(self.domain, definition["relation"])
|
||||
if "extra_filters" in definition:
|
||||
relation = relation.filter(**definition["extra_filters"])
|
||||
|
||||
@@ -16,14 +16,12 @@ class DomainLimitsTestCase(lib_tests.ModoTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls): # NOQA:N802
|
||||
"""Create test data."""
|
||||
super(DomainLimitsTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
cls.localconfig.parameters.set_values(
|
||||
{"enable_admin_limits": False, "enable_domain_limits": True}
|
||||
)
|
||||
for name, _definition in utils.get_domain_limit_templates():
|
||||
cls.localconfig.parameters.set_value(
|
||||
"deflt_domain_{0}_limit".format(name), 2
|
||||
)
|
||||
cls.localconfig.parameters.set_value(f"deflt_domain_{name}_limit", 2)
|
||||
cls.localconfig.save()
|
||||
admin_factories.populate_database()
|
||||
|
||||
|
||||
@@ -27,18 +27,16 @@ class LimitImportTestCase(ModoTestCase):
|
||||
self.client.force_login(self.reseller)
|
||||
self.assertFalse(limit.is_exceeded())
|
||||
f = ContentFile(
|
||||
"""domainalias; domalias1.com; {domain}; True
|
||||
domainalias; domalias2.com; {domain}; True
|
||||
""".format(
|
||||
domain=self.domain
|
||||
),
|
||||
f"""domainalias; domalias1.com; {self.domain}; True
|
||||
domainalias; domalias2.com; {self.domain}; True
|
||||
""",
|
||||
name="domains.csv",
|
||||
)
|
||||
self.client.post(reverse("admin:domain_import"), {"sourcefile": f})
|
||||
self.assertTrue(limit.is_exceeded())
|
||||
|
||||
f = ContentFile(
|
||||
"domainalias; domalias3.com; {}; True".format(self.domain),
|
||||
f"domainalias; domalias3.com; {self.domain}; True",
|
||||
name="domains.csv",
|
||||
)
|
||||
response = self.client.post(reverse("admin:domain_import"), {"sourcefile": f})
|
||||
@@ -51,20 +49,16 @@ domainalias; domalias2.com; {domain}; True
|
||||
content = ""
|
||||
for cpt in range(initial_count, 2):
|
||||
content += (
|
||||
"account; admin{cpt}@{domain}; toto; User; One; True; "
|
||||
"DomainAdmins; user{cpt}@{domain}; 5; {domain}\n".format(
|
||||
domain=self.domain, cpt=cpt
|
||||
)
|
||||
f"account; admin{cpt}@{self.domain}; toto; User; One; True; "
|
||||
f"DomainAdmins; user{cpt}@{self.domain}; 5; {self.domain}\n"
|
||||
)
|
||||
f = ContentFile(content, name="domain_admins.csv")
|
||||
response = self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
|
||||
self.assertTrue(limit.is_exceeded())
|
||||
|
||||
f = ContentFile(
|
||||
"""account; admin3@{domain}; toto; User; One; True; DomainAdmins; admin3@{domain}; 5; {domain}
|
||||
""".format(
|
||||
domain=self.domain
|
||||
),
|
||||
f"""account; admin3@{self.domain}; toto; User; One; True; DomainAdmins; admin3@{self.domain}; 5; {self.domain}
|
||||
""",
|
||||
name="domain_admins.csv",
|
||||
) # NOQA:E501
|
||||
response = self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
|
||||
@@ -75,21 +69,19 @@ domainalias; domalias2.com; {domain}; True
|
||||
self.client.force_login(self.dadmin)
|
||||
self.assertFalse(limit.is_exceeded())
|
||||
f = ContentFile(
|
||||
"account; user1@{domain}; toto; User; One; True; SimpleUsers; "
|
||||
"user1@{domain}; 5\r\n"
|
||||
"account; truc@{domain}; toto; René; Truc; True; SimpleUsers; "
|
||||
"truc@{domain}; 5\r\n".format(domain=self.domain),
|
||||
f"account; user1@{self.domain}; toto; User; One; True; SimpleUsers; "
|
||||
f"user1@{self.domain}; 5\r\n"
|
||||
f"account; truc@{self.domain}; toto; René; Truc; True; SimpleUsers; "
|
||||
f"truc@{self.domain}; 5\r\n",
|
||||
name="domains.csv",
|
||||
)
|
||||
response = self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
|
||||
self.assertTrue(limit.is_exceeded())
|
||||
|
||||
f = ContentFile(
|
||||
"""
|
||||
account; user3@{domain}; toto; User; One; True; SimpleUsers; user3@{domain}; 5
|
||||
""".format(
|
||||
domain=self.domain
|
||||
),
|
||||
f"""
|
||||
account; user3@{self.domain}; toto; User; One; True; SimpleUsers; user3@{self.domain}; 5
|
||||
""",
|
||||
name="domains.csv",
|
||||
)
|
||||
response = self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
|
||||
@@ -100,22 +92,18 @@ account; user3@{domain}; toto; User; One; True; SimpleUsers; user3@{domain}; 5
|
||||
self.client.force_login(self.dadmin)
|
||||
self.assertFalse(limit.is_exceeded())
|
||||
f = ContentFile(
|
||||
"""alias; alias1@{domain}; True; user@{domain}
|
||||
alias; alias2@{domain}; True; user@{domain}
|
||||
""".format(
|
||||
domain=self.domain
|
||||
),
|
||||
f"""alias; alias1@{self.domain}; True; user@{self.domain}
|
||||
alias; alias2@{self.domain}; True; user@{self.domain}
|
||||
""",
|
||||
name="aliases.csv",
|
||||
)
|
||||
self.client.post(reverse("admin:identity_import"), {"sourcefile": f})
|
||||
self.assertTrue(limit.is_exceeded())
|
||||
|
||||
f = ContentFile(
|
||||
"""
|
||||
alias; alias3@{domain}; True; user@{domain}
|
||||
""".format(
|
||||
domain=self.domain
|
||||
),
|
||||
f"""
|
||||
alias; alias3@{self.domain}; True; user@{self.domain}
|
||||
""",
|
||||
name="aliases.csv",
|
||||
)
|
||||
with self.assertRaises(exceptions.ValidationError):
|
||||
@@ -129,7 +117,7 @@ class UserLimitImportTestCase(LimitImportTestCase):
|
||||
"""Create test data."""
|
||||
localconfig = core_models.LocalConfig.objects.first()
|
||||
for name, _definition in utils.get_user_limit_templates():
|
||||
localconfig.parameters.set_value("deflt_user_{0}_limit".format(name), 2)
|
||||
localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
|
||||
localconfig.save()
|
||||
super(UserLimitImportTestCase, cls).setUpTestData()
|
||||
|
||||
@@ -191,7 +179,7 @@ class DomainLimitImportTestCase(LimitImportTestCase):
|
||||
{"enable_domain_limits": True, "enable_admin_limits": False}
|
||||
)
|
||||
for name, _definition in utils.get_domain_limit_templates():
|
||||
localconfig.parameters.set_value("deflt_domain_{0}_limit".format(name), 2)
|
||||
localconfig.parameters.set_value(f"deflt_domain_{name}_limit", 2)
|
||||
localconfig.save()
|
||||
super(DomainLimitImportTestCase, cls).setUpTestData()
|
||||
mb = admin_factories.MailboxFactory(
|
||||
|
||||
@@ -54,7 +54,7 @@ class ResourceTestCase(lib_tests.ModoTestCase):
|
||||
{"enable_admin_limits": True, "enable_domain_limits": False}
|
||||
)
|
||||
for name, _definition in utils.get_user_limit_templates():
|
||||
cls.localconfig.parameters.set_value("deflt_user_{0}_limit".format(name), 2)
|
||||
cls.localconfig.parameters.set_value(f"deflt_user_{name}_limit", 2)
|
||||
cls.localconfig.save()
|
||||
populate_database()
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class StatisticsViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
|
||||
end = int(time.mktime(serializer.validated_data["end"].timetuple()))
|
||||
else:
|
||||
end = int(time.mktime(time.localtime()))
|
||||
start = "-1{}".format(period)
|
||||
start = f"-1{period}"
|
||||
graphs = graph_sets[gset].export(
|
||||
fname, start, end, serializer.validated_data.get("graphic")
|
||||
)
|
||||
|
||||
@@ -15,10 +15,10 @@ class MaillogFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = models.Maillog
|
||||
|
||||
queue_id = factory.Sequence(lambda n: "ID{}".format(n))
|
||||
queue_id = factory.Sequence(lambda n: f"ID{n}")
|
||||
date = timezone.now()
|
||||
sender = factory.Sequence(lambda n: "sender{}@example.test".format(n))
|
||||
rcpt = factory.Sequence(lambda n: "rcpt{}@example2.test".format(n))
|
||||
sender = factory.Sequence(lambda n: f"sender{n}@example.test")
|
||||
rcpt = factory.Sequence(lambda n: f"rcpt{n}@example2.test")
|
||||
size = 100
|
||||
status = "sent"
|
||||
from_domain = factory.SubFactory(admin_factories.DomainFactory)
|
||||
|
||||
@@ -102,9 +102,7 @@ class Graphic:
|
||||
cmdargs += curve.to_rrd_command_args(rrdfile)
|
||||
code = 0
|
||||
|
||||
cmd = "{} xport --json -t --start {} --end {} ".format(
|
||||
self.rrdtool_binary, str(start), str(end)
|
||||
)
|
||||
cmd = f"{self.rrdtool_binary} xport --json -t --start {str(start)} --end {str(end)} "
|
||||
cmd += " ".join(cmdargs)
|
||||
code, output = exec_cmd(smart_bytes(cmd))
|
||||
if code:
|
||||
@@ -122,7 +120,7 @@ class Graphic:
|
||||
return result
|
||||
|
||||
|
||||
class GraphicSet(object):
|
||||
class GraphicSet:
|
||||
"""A set of graphics."""
|
||||
|
||||
domain_selector = False
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# coding: utf-8
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Postfix log parser.
|
||||
|
||||
@@ -230,7 +228,7 @@ class LogParser:
|
||||
def add_point_to_rrd(self, fname, tpl, values, ts=None):
|
||||
"""Try to add a new point to RRD file."""
|
||||
if ts:
|
||||
values = "{}:{}".format(ts, values)
|
||||
values = f"{ts}:{values}"
|
||||
if self.verbose:
|
||||
print("[rrd] VERBOSE update -t %s %s" % (tpl, values))
|
||||
try:
|
||||
@@ -564,11 +562,11 @@ class LogParser:
|
||||
host, prog, subprog, pid, log = m.groups()
|
||||
|
||||
try:
|
||||
parser = getattr(self, "_parse_{}".format(prog))
|
||||
parser = getattr(self, f"_parse_{prog}")
|
||||
if not parser(log, host, pid, subprog):
|
||||
self._dprint("[parser] ignoring %r log: %r" % (prog, log))
|
||||
except AttributeError:
|
||||
self._dprint('[parser] no log handler for "{}": {}'.format(prog, log))
|
||||
self._dprint(f'[parser] no log handler for "{prog}": {log}')
|
||||
|
||||
def process(self):
|
||||
"""Process the log file.
|
||||
@@ -580,7 +578,7 @@ class LogParser:
|
||||
with open(self.logfile, encoding="utf-8", errors="ignore") as fp:
|
||||
for line in fp:
|
||||
self._parse_line(line)
|
||||
except IOError as errno:
|
||||
except OSError as errno:
|
||||
self._dprint("%s" % errno)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class Command(BaseCommand):
|
||||
"""Create RRD file."""
|
||||
step = 3600
|
||||
ds_name = "new_accounts"
|
||||
params = ["DS:{}:ABSOLUTE:{}:0:U".format(ds_name, step * 2)]
|
||||
params = [f"DS:{ds_name}:ABSOLUTE:{step * 2}:0:U"]
|
||||
params += [
|
||||
"RRA:AVERAGE:0.5:1:48", # 48 hours with a 1h granularity
|
||||
"RRA:AVERAGE:0.5:24:31", # 31 days with a 1d granularity
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user