diff --git a/modoboa/core/app_settings.py b/modoboa/core/app_settings.py
index f05e4028c..63c009777 100644
--- a/modoboa/core/app_settings.py
+++ b/modoboa/core/app_settings.py
@@ -2,9 +2,10 @@
from django import forms
from django.conf import settings
from django.utils.translation import ugettext as _, ugettext_lazy
-from modoboa.lib.formutils import YesNoField, SeparatorField, InlineRadioSelect
-from modoboa.lib.cryptutils import random_key
+
from modoboa.lib import parameters
+from modoboa.lib.cryptutils import random_key
+from modoboa.lib.form_utils import YesNoField, SeparatorField, InlineRadioSelect
def enabled_applications():
@@ -18,7 +19,7 @@ def enabled_applications():
"""
from modoboa.core.models import Extension
from modoboa.core.extensions import exts_pool
- from modoboa.lib.dbutils import db_table_exists
+ from modoboa.lib.db_utils import db_table_exists
result = [("user", "user")]
if db_table_exists("core_extension"):
@@ -304,8 +305,9 @@ class GeneralParametersForm(parameters.AdminParametersForm):
required_fields = ["ldap_user_dn_template"]
for f in required_fields:
- if not f in cleaned_data or cleaned_data[f] == u'':
- self._errors[f] = self.error_class([_("This field is required")])
+ if f not in cleaned_data or cleaned_data[f] == u'':
+ self._errors[f] = self.error_class(
+ [_("This field is required")])
return cleaned_data
@@ -341,14 +343,20 @@ class GeneralParametersForm(parameters.AdminParametersForm):
))
if values["ldap_auth_method"] == "searchbind":
setattr(settings, "AUTH_LDAP_BIND_DN", values["ldap_bind_dn"])
- setattr(settings, "AUTH_LDAP_BIND_PASSWORD", values["ldap_bind_password"])
+ setattr(
+ settings, "AUTH_LDAP_BIND_PASSWORD",
+ values["ldap_bind_password"]
+ )
search = LDAPSearch(
values["ldap_search_base"], ldap.SCOPE_SUBTREE,
values["ldap_search_filter"]
)
setattr(settings, "AUTH_LDAP_USER_SEARCH", search)
else:
- setattr(settings, "AUTH_LDAP_USER_DN_TEMPLATE", values["ldap_user_dn_template"])
+ setattr(
+ settings, "AUTH_LDAP_USER_DN_TEMPLATE",
+ values["ldap_user_dn_template"]
+ )
if values["ldap_is_active_directory"] == "yes":
if not hasattr(settings, "AUTH_LDAP_GLOBAL_OPTIONS"):
setattr(settings, "AUTH_LDAP_GLOBAL_OPTIONS", {
diff --git a/modoboa/core/commands/__init__.py b/modoboa/core/commands/__init__.py
index 9b2b1c9f2..b1b2cf62e 100644
--- a/modoboa/core/commands/__init__.py
+++ b/modoboa/core/commands/__init__.py
@@ -1,12 +1,15 @@
# coding: utf-8
+
+import argparse
import os
import sys
-import argparse
+
from django.conf import settings
from django.template import Context, Template
class Command(object):
+
"""Base command class
A valid administrative command must inherit from this class.
@@ -71,6 +74,7 @@ def scan_for_commands(dirname=""):
def handle_command_line():
+ """Parse the command line."""
commands = scan_for_commands()
parser = argparse.ArgumentParser(
description="A set of utilities to ease the installation of Modoboa.",
@@ -83,7 +87,7 @@ def handle_command_line():
help='A valid command name')
(args, remaining) = parser.parse_known_args()
- if not args.command in commands:
+ if args.command not in commands:
print >> sys.stderr, "Unknown command '%s'" % args.command
sys.exit(1)
diff --git a/modoboa/core/commands/help.py b/modoboa/core/commands/help.py
index dfe46307c..ab6960d00 100644
--- a/modoboa/core/commands/help.py
+++ b/modoboa/core/commands/help.py
@@ -1,9 +1,12 @@
# coding: utf-8
+
import sys
+
from . import Command
class HelpCommand(Command):
+
help = "Display the help message associated to a specific command"
def __init__(self, *args, **kwargs):
@@ -12,7 +15,7 @@ class HelpCommand(Command):
help='A command name')
def handle(self, parsed_args):
- if not parsed_args.name in self._commands:
+ if parsed_args.name not in self._commands:
print >>sys.stderr, "Unknown command: %s" % parsed_args.name
sys.exit(1)
cmd = self._commands[parsed_args.name](self._commands)
diff --git a/modoboa/core/commands/postfix_maps.py b/modoboa/core/commands/postfix_maps.py
index 0cf9e9fb2..c663841fe 100644
--- a/modoboa/core/commands/postfix_maps.py
+++ b/modoboa/core/commands/postfix_maps.py
@@ -1,18 +1,22 @@
# coding: utf-8
+
+"""postfix_maps command."""
+
+import datetime
import getpass
import inspect
import os
import sys
-import datetime
+
from django.template import Context, Template
+
from . import Command
class MapFilesGenerator(object):
def __init__(self, dbinfo=None):
- """Constructor.
- """
+ """Constructor."""
self.template = Template("""user = {{ dbuser }}
password = {{ dbpass }}
dbname = {{ dbname }}
@@ -69,7 +73,7 @@ query = {{ query|safe }}
continue
if v is MapFile:
continue
- if not v.category in args.categories:
+ if v.category not in args.categories:
continue
self.__render_map(args, v)
diff --git a/modoboa/core/extensions.py b/modoboa/core/extensions.py
index 03997c150..96d3096cf 100644
--- a/modoboa/core/extensions.py
+++ b/modoboa/core/extensions.py
@@ -1,8 +1,11 @@
+"""Extension management."""
+
from django.conf import settings
from django.conf.urls import include
class ModoExtension(object):
+
"""Base extension class.
Each Modoboa extension must inherit from this class to be
@@ -35,6 +38,7 @@ class ModoExtension(object):
class ExtensionsPool(object):
+
"""The extensions manager"""
def __init__(self):
@@ -44,9 +48,9 @@ class ExtensionsPool(object):
self.extensions[ext.name] = dict(cls=ext, show=show)
def get_extension(self, name):
- if not name in self.extensions:
+ if name not in self.extensions:
return None
- if not "instance" in self.extensions[name]:
+ if "instance" not in self.extensions[name]:
self.extensions[name]["instance"] = self.extensions[name]["cls"]()
return self.extensions[name]["instance"]
@@ -54,7 +58,7 @@ class ExtensionsPool(object):
"""Check if an extension is enabled or not."""
from modoboa.core.models import Extension
- if not name in self.extensions:
+ if name not in self.extensions:
return False
try:
state = Extension.objects.get(name=name).enabled
diff --git a/modoboa/core/forms.py b/modoboa/core/forms.py
index 12c361797..a72f13d44 100644
--- a/modoboa/core/forms.py
+++ b/modoboa/core/forms.py
@@ -1,6 +1,10 @@
# coding: utf-8
+
+"""Core forms."""
+
from django import forms
from django.utils.translation import ugettext as _, ugettext_lazy
+
from modoboa.core.models import User
from modoboa.lib import parameters
@@ -38,9 +42,9 @@ class ProfileForm(forms.ModelForm):
model = User
fields = ("first_name", "last_name")
widgets = {
- 'first_name': forms.TextInput(attrs={'class': 'form-control'}),
- 'last_name': forms.TextInput(attrs={'class': 'form-control'})
- }
+ 'first_name': forms.TextInput(attrs={'class': 'form-control'}),
+ 'last_name': forms.TextInput(attrs={'class': 'form-control'})
+ }
def __init__(self, update_password, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
@@ -61,15 +65,19 @@ class ProfileForm(forms.ModelForm):
return self.cleaned_data["oldpassword"]
def clean_confirmation(self):
- if self.cleaned_data["newpassword"] != self.cleaned_data["confirmation"]:
+ newpassword = self.cleaned_data["newpassword"]
+ confirmation = self.cleaned_data["confirmation"]
+ if newpassword != confirmation:
raise forms.ValidationError(_("Passwords mismatch"))
return self.cleaned_data["confirmation"]
def save(self, commit=True):
user = super(ProfileForm, self).save(commit=False)
if commit:
- if self.cleaned_data.has_key("confirmation") and \
- self.cleaned_data["confirmation"] != "":
- user.set_password(self.cleaned_data["confirmation"], self.cleaned_data["oldpassword"])
+ if self.cleaned_data.get("confirmation", "") != "":
+ user.set_password(
+ self.cleaned_data["confirmation"],
+ self.cleaned_data["oldpassword"]
+ )
user.save()
return user
diff --git a/modoboa/core/management/commands/clean_unknown_extensions.py b/modoboa/core/management/commands/clean_unknown_extensions.py
index f22c13852..d0a297fea 100644
--- a/modoboa/core/management/commands/clean_unknown_extensions.py
+++ b/modoboa/core/management/commands/clean_unknown_extensions.py
@@ -21,6 +21,6 @@ class Command(BaseCommand):
"""Entry point."""
for extension in Extension.objects.all():
fullname = "modoboa.extensions.{0}".format(extension.name)
- if not fullname in settings.MODOBOA_APPS:
+ if fullname not in settings.MODOBOA_APPS:
extension.delete()
connection.close()
diff --git a/modoboa/core/management/commands/manage_extensions.py b/modoboa/core/management/commands/manage_extensions.py
index 24133a1e0..af90b1302 100644
--- a/modoboa/core/management/commands/manage_extensions.py
+++ b/modoboa/core/management/commands/manage_extensions.py
@@ -53,6 +53,6 @@ class Command(BaseCommand, CloseConnectionMixin):
for extname in args:
fullname = "modoboa.extensions.{0}".format(extname)
- if not fullname in settings.MODOBOA_APPS:
+ if fullname not in settings.MODOBOA_APPS:
continue
self._change_extension_state(extname, options)
diff --git a/modoboa/core/models.py b/modoboa/core/models.py
index 57faece90..8d4aed543 100644
--- a/modoboa/core/models.py
+++ b/modoboa/core/models.py
@@ -1,36 +1,43 @@
-import sys
+"""Core models."""
+
+import logging
import os
import re
-import logging
-import reversion
-from django.db import models
-from django.db.models.signals import post_delete
+import sys
+
from django.conf import settings
-from django.dispatch import receiver
-from django.utils import timezone
-from django.utils.translation import ugettext as _, ugettext_lazy
-from django.contrib.contenttypes.models import ContentType
-from django.contrib.contenttypes import generic
+from django.contrib.auth.hashers import make_password, is_password_usable
from django.contrib.auth.models import (
UserManager, Group, PermissionsMixin
)
-from django.contrib.auth.hashers import make_password, is_password_usable
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
+from django.db import models
+from django.db.models.signals import post_delete
+from django.dispatch import receiver
+from django.utils import timezone
+from django.utils.translation import ugettext as _, ugettext_lazy
+
+import reversion
+
+from modoboa.core.extensions import exts_pool
+from modoboa.core.password_hashers import get_password_hasher
from modoboa.lib import events, parameters
from modoboa.lib.exceptions import (
PermDeniedException, InternalError, BadRequest, Conflict
)
from modoboa.lib.sysutils import exec_cmd
-from modoboa.core.extensions import exts_pool
-from modoboa.core.password_hashers import get_password_hasher
+
try:
- from modoboa.lib.ldaputils import LDAPAuthBackend
+ from modoboa.lib.ldap_utils import LDAPAuthBackend
ldap_available = True
except ImportError:
ldap_available = False
class User(PermissionsMixin):
+
"""Custom User model.
It overloads the way passwords are stored into the database. The
@@ -108,7 +115,7 @@ class User(PermissionsMixin):
load_core_settings()
scheme = parameters.get_admin("PASSWORD_SCHEME")
- if type(raw_value) is unicode:
+ if isinstance(raw_value, unicode):
raw_value = raw_value.encode("utf-8")
return get_password_hasher(scheme.upper())().encrypt(raw_value)
@@ -140,7 +147,7 @@ class User(PermissionsMixin):
match = self.password_expr.match(self.password)
if match is None:
return False
- if type(raw_value) is unicode:
+ if isinstance(raw_value, unicode):
raw_value = raw_value.encode("utf-8")
scheme = match.group(1)
val2 = match.group(2)
@@ -242,7 +249,8 @@ class User(PermissionsMixin):
"""
ct = ContentType.objects.get_for_model(obj)
try:
- ooentry = self.objectaccess_set.get(content_type=ct, object_id=obj.id)
+ ooentry = self.objectaccess_set.get(
+ content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
return False
return ooentry.is_owner
@@ -263,7 +271,8 @@ class User(PermissionsMixin):
ct = ContentType.objects.get_for_model(obj)
try:
- ooentry = self.objectaccess_set.get(content_type=ct, object_id=obj.id)
+ ooentry = self.objectaccess_set.get(
+ content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
pass
else:
@@ -333,7 +342,8 @@ class User(PermissionsMixin):
if len(row) < 7:
raise BadRequest(_("Invalid line"))
role = row[6].strip()
- if not user.is_superuser and not role in ["SimpleUsers", "DomainAdmins"]:
+ allowed_roles = ["SimpleUsers", "DomainAdmins"]
+ if not user.is_superuser and role not in allowed_roles:
raise PermDeniedException(
_("You can't import an account with a role greater than yours")
)
@@ -345,13 +355,15 @@ class User(PermissionsMixin):
else:
raise Conflict
if role == "SimpleUsers":
- if (len(row) < 8 or not row[7].strip()):
+ if len(row) < 8 or not row[7].strip():
raise BadRequest(
- _("The simple user '%s' must have a valid email address" % self.username)
+ _("The simple user '%s' must have a valid email address"
+ % self.username)
)
if self.username != row[7].strip():
raise BadRequest(
- _("username and email fields must not differ for '%s'" % self.username)
+ _("username and email fields must not differ for '%s'"
+ % self.username)
)
if crypt_password:
@@ -374,9 +386,16 @@ class User(PermissionsMixin):
:param csvwriter: csv object
"""
- row = ["account", self.username.encode("utf-8"), self.password.encode("utf-8"),
- self.first_name.encode("utf-8"), self.last_name.encode("utf-8"),
- self.is_active, self.group, self.email.encode("utf-8")]
+ row = [
+ "account",
+ self.username.encode("utf-8"),
+ self.password.encode("utf-8"),
+ self.first_name.encode("utf-8"),
+ self.last_name.encode("utf-8"),
+ self.is_active,
+ self.group,
+ self.email.encode("utf-8")
+ ]
row += events.raiseQueryEvent("AccountExported", self)
csvwriter.writerow(row)
@@ -414,7 +433,9 @@ class ObjectAccess(models.Model):
unique_together = (("user", "content_type", "object_id"),)
def __unicode__(self):
- return "%s => %s (%s)" % (self.user, self.content_object, self.content_type)
+ return "%s => %s (%s)" % (
+ self.user, self.content_object, self.content_type
+ )
class Extension(models.Model):
@@ -501,7 +522,7 @@ def post_revision_commit(sender, **kwargs):
"""
from modoboa.lib.signals import get_request
- current_user = get_request().user.username
+ current_user = get_request().user.username
logger = logging.getLogger("modoboa.admin")
for version in kwargs["versions"]:
if version.object is None:
diff --git a/modoboa/core/password_hashers/advanced.py b/modoboa/core/password_hashers/advanced.py
index 3bb632ad2..73b5ef493 100644
--- a/modoboa/core/password_hashers/advanced.py
+++ b/modoboa/core/password_hashers/advanced.py
@@ -9,6 +9,7 @@ from modoboa.lib import parameters
class MD5CRYPTHasher(PasswordHasher):
+
"""
MD5-CRYPT password hasher.
@@ -29,6 +30,7 @@ class MD5CRYPTHasher(PasswordHasher):
class SHA256CRYPTHasher(PasswordHasher):
+
"""
SHA256-CRYPT password hasher.
@@ -51,6 +53,7 @@ class SHA256CRYPTHasher(PasswordHasher):
class SHA512CRYPTHasher(PasswordHasher):
+
"""
SHA512-CRYPT password hasher.
diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py
index 156a35616..b0902c926 100644
--- a/modoboa/core/password_hashers/base.py
+++ b/modoboa/core/password_hashers/base.py
@@ -12,9 +12,11 @@ from django.utils.crypto import constant_time_compare
class PasswordHasher(object):
+
"""
Base class of all hashers.
"""
+
def __init__(self, target='local'):
self._target = target
@@ -61,6 +63,7 @@ class PasswordHasher(object):
class PLAINHasher(PasswordHasher):
+
"""
Plain (ie. clear) password hasher.
"""
@@ -73,6 +76,7 @@ class PLAINHasher(PasswordHasher):
class CRYPTHasher(PasswordHasher):
+
"""
crypt password hasher.
@@ -89,6 +93,7 @@ class CRYPTHasher(PasswordHasher):
class MD5Hasher(PasswordHasher):
+
"""
MD5 password hasher.
@@ -104,6 +109,7 @@ class MD5Hasher(PasswordHasher):
class SHA256Hasher(PasswordHasher):
+
"""
SHA256 password hasher.
diff --git a/modoboa/core/templatetags/core_tags.py b/modoboa/core/templatetags/core_tags.py
index 9cbed9c08..bd50c7eb7 100644
--- a/modoboa/core/templatetags/core_tags.py
+++ b/modoboa/core/templatetags/core_tags.py
@@ -1,10 +1,14 @@
+"""Custom tags for Core application."""
+
import re
+
from django import template
+from django.contrib.sessions.models import Session
+from django.core.urlresolvers import reverse
from django.template.loader import render_to_string
from django.utils import timezone
from django.utils.translation import ugettext as _
-from django.core.urlresolvers import reverse
-from django.contrib.sessions.models import Session
+
from modoboa.lib import events
@@ -125,7 +129,7 @@ def colorize_level(level):
"WARNING": "text-warning",
"CRITICAL": "text-danger"
}
- if not level in classes:
+ if level not in classes:
return level
return "
%s
" % (classes[level], level)
@@ -140,7 +144,7 @@ def tohtml(message):
def visirule(field):
if not hasattr(field, 'form') or \
not hasattr(field.form, "visirules") or \
- not field.html_name in field.form.visirules:
+ field.html_name not in field.form.visirules:
return ""
rule = field.form.visirules[field.html_name]
return " data-visibility-field='%s' data-visibility-value='%s' " \
@@ -154,6 +158,7 @@ def get_version():
class ConnectedUsers(template.Node):
+
def __init__(self, varname):
self.varname = varname
diff --git a/modoboa/core/tests.py b/modoboa/core/tests.py
index 56ed41f2e..8c3608dae 100644
--- a/modoboa/core/tests.py
+++ b/modoboa/core/tests.py
@@ -74,7 +74,8 @@ class ModoboaAdminCommandTestCase(TestCase):
categories = ["std", "autoreply", "relaydomains"]
dburl = "{0}://user:password@localhost/testdb".format(engine)
code, output = exec_cmd(
- "modoboa-admin.py postfix_maps --categories {0} --dburl {1} {2}".format(
+ "modoboa-admin.py postfix_maps --categories {0}"
+ " --dburl {1} {2}".format(
" ".join(categories), dburl, self.workdir
)
)
diff --git a/modoboa/core/views/admin.py b/modoboa/core/views/admin.py
index c45ea3042..b4b36e056 100644
--- a/modoboa/core/views/admin.py
+++ b/modoboa/core/views/admin.py
@@ -1,21 +1,18 @@
"""
Views available to super administrators only.
"""
-
-from django.shortcuts import render
-from django.template.loader import render_to_string
-from django.utils.translation import ugettext as _
-
from django.contrib.auth.decorators import (
login_required, user_passes_test
)
-
+from django.shortcuts import render
+from django.template.loader import render_to_string
+from django.utils.translation import ugettext as _
from modoboa.core.models import Extension, Log
from modoboa.core.utils import new_version_available
from modoboa.lib import events, parameters
from modoboa.lib.listing import get_sort_order, get_listing_page
-from modoboa.lib.webutils import (
+from modoboa.lib.web_utils import (
_render_to_string, render_to_json_response
)
@@ -88,12 +85,12 @@ def saveextensions(request):
if k.startswith("select_"):
parts = k.split("_", 1)
dbext = Extension.objects.get(name=parts[1])
- if not dbext in actived_exts:
+ if dbext not in actived_exts:
dbext.on()
else:
found += [dbext]
for ext in actived_exts:
- if not ext in found:
+ if ext not in found:
ext.off()
return render_to_json_response(_("Modifications applied."))
diff --git a/modoboa/core/views/auth.py b/modoboa/core/views/auth.py
index d49e77211..44325a366 100644
--- a/modoboa/core/views/auth.py
+++ b/modoboa/core/views/auth.py
@@ -1,16 +1,19 @@
# coding: utf-8
import logging
-from django.views.decorators.cache import never_cache
+
from django.contrib.auth import authenticate, login, logout
-from django.http import HttpResponse, HttpResponseRedirect
from django.core.urlresolvers import reverse
+from django.http import HttpResponse, HttpResponseRedirect
from django.utils.translation import ugettext as _
-from modoboa.lib import events, parameters
-from modoboa.lib.webutils import _render_to_string
+from django.views.decorators.cache import never_cache
+
from modoboa.core.forms import LoginForm
+from modoboa.lib import events, parameters
+from modoboa.lib.web_utils import _render_to_string
def dologin(request):
+ """Try to authenticate."""
error = None
if request.method == "POST":
form = LoginForm(request.POST)
@@ -43,7 +46,7 @@ def dologin(request):
error = _(
"Your username and password didn't match. Please try again.")
logger.warning(
- "Failed connection attempt from '%(addr)s' as user '%(user)s'" \
+ "Failed connection attempt from '%(addr)s' as user '%(user)s'"
% {"addr": request.META["REMOTE_ADDR"],
"user": form.cleaned_data["username"]}
)
diff --git a/modoboa/core/views/user.py b/modoboa/core/views/user.py
index d3cb2e8ac..7c444bbd6 100644
--- a/modoboa/core/views/user.py
+++ b/modoboa/core/views/user.py
@@ -1,12 +1,15 @@
+"""Simple user views."""
+
+from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from django.utils.translation import ugettext as _
-from django.contrib.auth.decorators import login_required
+
+from modoboa.core.forms import ProfileForm
from modoboa.lib import events, parameters
-from modoboa.lib.webutils import (
+from modoboa.lib.cryptutils import encrypt
+from modoboa.lib.web_utils import (
_render_to_string, render_to_json_response
)
-from modoboa.lib.cryptutils import encrypt
-from modoboa.core.forms import ProfileForm
@login_required
@@ -31,7 +34,8 @@ def profile(request, tplname='core/user_profile.html'):
if form.is_valid():
form.save()
if update_password and form.cleaned_data["confirmation"] != "":
- request.session["password"] = encrypt(form.cleaned_data["confirmation"])
+ request.session["password"] = encrypt(
+ form.cleaned_data["confirmation"])
return render_to_json_response(_("Profile updated"))
return render_to_json_response({'form_errors': form.errors}, status=400)
diff --git a/modoboa/demo/__init__.py b/modoboa/demo/__init__.py
index a9fca9b36..ce86f9830 100755
--- a/modoboa/demo/__init__.py
+++ b/modoboa/demo/__init__.py
@@ -3,7 +3,7 @@ from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.template.loader import render_to_string
from modoboa.lib import events
-from modoboa.lib.webutils import static_url
+from modoboa.lib.web_utils import static_url
@events.observe("UserMenuDisplay")
diff --git a/modoboa/demo/views.py b/modoboa/demo/views.py
index 90c5e5dc1..57f266d85 100755
--- a/modoboa/demo/views.py
+++ b/modoboa/demo/views.py
@@ -3,8 +3,8 @@ import os
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.utils.translation import ugettext as _
-from modoboa.lib.webutils import render_to_json_response
-from modoboa.lib.emailutils import sendmail_simple, sendmail_fromfile
+from modoboa.lib.web_utils import render_to_json_response
+from modoboa.lib.email_utils import sendmail_simple, sendmail_fromfile
from modoboa.extensions.admin.lib import needs_mailbox
diff --git a/modoboa/extensions/admin/__init__.py b/modoboa/extensions/admin/__init__.py
index 22979bb6f..1c23ae3d7 100644
--- a/modoboa/extensions/admin/__init__.py
+++ b/modoboa/extensions/admin/__init__.py
@@ -1,13 +1,15 @@
# coding: utf-8
-from django.utils.translation import ugettext as _, ugettext_lazy
from django.core.urlresolvers import reverse
+from django.utils.translation import ugettext as _, ugettext_lazy
+
from modoboa.core.extensions import ModoExtension, exts_pool
-from modoboa.lib import parameters, events
-from modoboa.lib.exceptions import PermDeniedException, BadRequest, Conflict
-from modoboa.lib.emailutils import split_mailbox
from modoboa.extensions.admin.models import (
Domain, DomainAlias, Mailbox, Alias
)
+from modoboa.lib import parameters, events
+from modoboa.lib.email_utils import split_mailbox
+from modoboa.lib.exceptions import PermDeniedException, BadRequest, Conflict
+
admin_events = [
"DomainCreated",
@@ -54,7 +56,8 @@ class AdminConsole(ModoExtension):
def load(self):
from modoboa.extensions.admin.app_settings import AdminParametersForm
- parameters.register(AdminParametersForm, ugettext_lazy("Administration"))
+ parameters.register(
+ AdminParametersForm, ugettext_lazy("Administration"))
events.declare(admin_events)
def destroy(self):
@@ -80,15 +83,15 @@ def admin_menu(target, user):
entries = []
if user.has_perm("admin.view_domains"):
entries += [
- {"name" : "domains",
- "url" : reverse("admin:domain_list"),
- "label" : _("Domains")}
+ {"name": "domains",
+ "url": reverse("admin:domain_list"),
+ "label": _("Domains")}
]
if user.has_perm("core.add_user") or user.has_perm("admin.add_alias"):
entries += [
- {"name" : "identities",
- "url" : reverse("admin:identity_list"),
- "label" : _("Identities")},
+ {"name": "identities",
+ "url": reverse("admin:identity_list"),
+ "label": _("Identities")},
]
return entries
@@ -166,7 +169,8 @@ def import_account_mailbox(user, account, row):
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise BadRequest(
- _("Account import failed (%s): domain does not exist" % account.username)
+ _("Account import failed (%s): domain does not exist"
+ % account.username)
)
if not user.can_access(domain):
raise PermDeniedException
@@ -175,7 +179,7 @@ def import_account_mailbox(user, account, row):
except Mailbox.DoesNotExist:
pass
else:
- raise Conflict(_("Mailbox %s already exists" % account.email))
+ raise Conflict(_("Mailbox %s already exists" % account.email))
if len(row) == 1:
quota = None
else:
@@ -183,7 +187,8 @@ def import_account_mailbox(user, account, row):
quota = int(row[1].strip())
except ValueError:
raise BadRequest(
- _("Account import failed (%s): wrong quota value" % account.username)
+ _("Account import failed (%s): wrong quota value"
+ % account.username)
)
use_domain_quota = True if not quota else False
mb = Mailbox(address=mailbox, domain=domain,
diff --git a/modoboa/extensions/admin/app_settings.py b/modoboa/extensions/admin/app_settings.py
index 3f27d114f..8638bce9e 100644
--- a/modoboa/extensions/admin/app_settings.py
+++ b/modoboa/extensions/admin/app_settings.py
@@ -1,7 +1,7 @@
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy
-from modoboa.lib.formutils import YesNoField, SeparatorField
+from modoboa.lib.form_utils import YesNoField, SeparatorField
from modoboa.lib.sysutils import exec_cmd
from modoboa.lib import parameters
import os
diff --git a/modoboa/extensions/admin/factories.py b/modoboa/extensions/admin/factories.py
index 9097f1b2c..b4399e0cd 100644
--- a/modoboa/extensions/admin/factories.py
+++ b/modoboa/extensions/admin/factories.py
@@ -37,7 +37,8 @@ def populate_database():
"""
dom = DomainFactory.create(name="test.com")
admin = UserFactory(
- username="admin@test.com", groups=('DomainAdmins', ), password='{PLAIN}toto'
+ username="admin@test.com", groups=('DomainAdmins', ),
+ password='{PLAIN}toto'
)
MailboxFactory(address='admin', domain=dom, user=admin)
account = UserFactory.create(
@@ -59,7 +60,8 @@ def populate_database():
dom2 = DomainFactory.create(name='test2.com', quota=0)
admin = UserFactory.create(
- username='admin@test2.com', groups=('DomainAdmins',), password='{PLAIN}toto'
+ username='admin@test2.com', groups=('DomainAdmins',),
+ password='{PLAIN}toto'
)
MailboxFactory.create(address='admin', domain=dom2, user=admin)
u = UserFactory.create(
diff --git a/modoboa/extensions/admin/forms/account.py b/modoboa/extensions/admin/forms/account.py
index 24aca55ca..dcda56951 100644
--- a/modoboa/extensions/admin/forms/account.py
+++ b/modoboa/extensions/admin/forms/account.py
@@ -1,19 +1,22 @@
+"""Forms related to accounts management."""
+
from django import forms
-from django.utils.translation import ugettext as _, ugettext_lazy
-from django.http import QueryDict
from django.core.urlresolvers import reverse
-from modoboa.lib import events, parameters
-from modoboa.lib.exceptions import PermDeniedException, Conflict, NotFound
-from modoboa.lib.permissions import get_account_roles
-from modoboa.lib.emailutils import split_mailbox
-from modoboa.lib.formutils import (
- DomainNameField, DynamicForm, TabForms, WizardForm
-)
-from modoboa.lib.webutils import render_to_json_response
+from django.http import QueryDict
+from django.utils.translation import ugettext as _, ugettext_lazy
+
from modoboa.core.models import User
from modoboa.extensions.admin.models import (
Domain, Mailbox, Alias
)
+from modoboa.lib import events, parameters
+from modoboa.lib.email_utils import split_mailbox
+from modoboa.lib.exceptions import PermDeniedException, Conflict, NotFound
+from modoboa.lib.form_utils import (
+ DomainNameField, DynamicForm, TabForms, WizardForm
+)
+from modoboa.lib.permissions import get_account_roles
+from modoboa.lib.web_utils import render_to_json_response
class AccountFormGeneral(forms.ModelForm):
@@ -77,7 +80,8 @@ class AccountFormGeneral(forms.ModelForm):
del self.fields["is_active"]
self.fields["role"].initial = account.group
if not account.is_local \
- and parameters.get_admin("LDAP_AUTH_METHOD", app="core") == "directbind":
+ and parameters.get_admin(
+ "LDAP_AUTH_METHOD", app="core") == "directbind":
del self.fields["password1"]
del self.fields["password2"]
@@ -90,7 +94,7 @@ class AccountFormGeneral(forms.ModelForm):
"""
if not self.instance.mailbox_set.count():
return False
- return self.instance.mailbox_set.all()[0].domain.enabled == False
+ return not self.instance.mailbox_set.all()[0].domain.enabled
def clean_role(self):
if self.user.group == "DomainAdmins":
@@ -101,7 +105,7 @@ class AccountFormGeneral(forms.ModelForm):
def clean_username(self):
from django.core.validators import validate_email
- if not "role" in self.cleaned_data:
+ if "role" not in self.cleaned_data:
return self.cleaned_data["username"]
if self.cleaned_data["role"] != "SimpleUsers":
return self.cleaned_data["username"]
@@ -113,7 +117,8 @@ class AccountFormGeneral(forms.ModelForm):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
- raise forms.ValidationError(_("The two password fields didn't match."))
+ raise forms.ValidationError(
+ _("The two password fields didn't match."))
return password2
def save(self, commit=True):
@@ -165,7 +170,8 @@ class AccountFormMail(forms.Form, DynamicForm):
"quota": 3
}
self.extra_fields = []
- for fname, field in events.raiseQueryEvent('ExtraFormFields', 'mailform', self.mb):
+ result = events.raiseQueryEvent('ExtraFormFields', 'mailform', self.mb)
+ for fname, field in result:
self.fields[fname] = field
self.extra_fields.append(fname)
if self.mb is not None:
@@ -227,7 +233,7 @@ class AccountFormMail(forms.Form, DynamicForm):
events.raiseEvent("CanCreate", user, "mailboxes")
self.mb = Mailbox(address=locpart, domain=domain, user=account,
use_domain_quota=self.cleaned_data["quota_act"])
- self.mb.set_quota(self.cleaned_data["quota"],
+ self.mb.set_quota(self.cleaned_data["quota"],
user.has_perm("admin.add_domain"))
self.mb.save(creator=user)
@@ -235,7 +241,8 @@ class AccountFormMail(forms.Form, DynamicForm):
newaddress = None
if self.cleaned_data["email"] != self.mb.full_address:
newaddress = self.cleaned_data["email"]
- elif account.group == "SimpleUsers" and account.username != self.mb.full_address:
+ elif (account.group == "SimpleUsers" and
+ account.username != self.mb.full_address):
newaddress = account.username
if newaddress is not None:
self.mb.old_full_address = self.mb.full_address
@@ -267,7 +274,7 @@ class AccountFormMail(forms.Form, DynamicForm):
aliases.append(value)
for alias in self.mb.alias_set.all():
- if not alias.full_address in aliases:
+ if alias.full_address not in aliases:
if len(alias.get_recipients()) >= 2:
continue
alias.delete()
@@ -331,9 +338,9 @@ class AccountPermissionsForm(forms.Form, DynamicForm):
if not hasattr(self, "account") or self.account is None:
return
- for pos, domain in enumerate(Domain.objects.get_for_admin(self.account)):
+ for pos, dom in enumerate(Domain.objects.get_for_admin(self.account)):
name = "domains_%d" % (pos + 1)
- self._create_field(DomainNameField, name, domain.name)
+ self._create_field(DomainNameField, name, dom.name)
if len(args) and isinstance(args[0], QueryDict):
self._load_from_qdict(args[0], "domains", DomainNameField)
@@ -346,20 +353,20 @@ class AccountPermissionsForm(forms.Form, DynamicForm):
continue
if value in ["", None]:
continue
- if not value in current_domains:
+ if value not in current_domains:
domain = Domain.objects.get(name=value)
domain.add_admin(self.account)
for domain in Domain.objects.get_for_admin(self.account):
- if not len(filter(lambda name: self.cleaned_data[name] == domain.name,
- self.cleaned_data.keys())):
+ if not filter(lambda name: self.cleaned_data[name] == domain.name,
+ self.cleaned_data.keys()):
domain.remove_admin(self.account)
class AccountForm(TabForms):
- """Account edition form.
- """
+ """Account edition form."""
+
def __init__(self, request, *args, **kwargs):
self.user = request.user
self.forms = [
@@ -369,8 +376,11 @@ class AccountForm(TabForms):
new_args=[self.user], mandatory=True),
dict(id="mail", title=_("Mail"), formtpl="admin/mailform.html",
cls=AccountFormMail),
- dict(id="perms", title=_("Permissions"), formtpl="admin/permsform.html",
- cls=AccountPermissionsForm)
+ dict(
+ id="perms", title=_("Permissions"),
+ formtpl="admin/permsform.html",
+ cls=AccountPermissionsForm
+ )
]
cbargs = [self.user]
if "instances" in kwargs:
@@ -402,7 +412,9 @@ class AccountForm(TabForms):
return False
return True
- if False in events.raiseQueryEvent("CheckExtraAccountForm", self.account, form):
+ extra_forms = events.raiseQueryEvent(
+ "CheckExtraAccountForm", self.account, form)
+ if False in extra_forms:
return False
return True
@@ -435,8 +447,10 @@ class AccountForm(TabForms):
class AccountWizard(WizardForm):
+
"""Account creation wizard.
"""
+
def __init__(self, request):
super(AccountWizard, self).__init__(request)
self.add_step(
@@ -454,7 +468,7 @@ class AccountWizard(WizardForm):
})
def done(self):
- from modoboa.lib.webutils import render_to_json_response
+ from modoboa.lib.web_utils import render_to_json_response
account = self.first_step.form.save()
account.post_create(self.request.user)
diff --git a/modoboa/extensions/admin/forms/alias.py b/modoboa/extensions/admin/forms/alias.py
index 9de56daed..d54dad929 100644
--- a/modoboa/extensions/admin/forms/alias.py
+++ b/modoboa/extensions/admin/forms/alias.py
@@ -1,14 +1,17 @@
+"""Forms related to aliases management."""
+
from django import forms
-from django.utils.translation import ugettext as _, ugettext_lazy
from django.http import QueryDict
-from modoboa.lib.exceptions import BadRequest, NotFound, Conflict
-from modoboa.lib.emailutils import split_mailbox
-from modoboa.lib.formutils import (
- DynamicForm
-)
+from django.utils.translation import ugettext as _, ugettext_lazy
+
from modoboa.extensions.admin.models import (
Domain, Mailbox, Alias
)
+from modoboa.lib.email_utils import split_mailbox
+from modoboa.lib.exceptions import BadRequest, NotFound, Conflict
+from modoboa.lib.form_utils import (
+ DynamicForm
+)
class AliasForm(forms.ModelForm, DynamicForm):
@@ -109,7 +112,8 @@ class AliasForm(forms.ModelForm, DynamicForm):
rcpt = None
if rcpt is None:
try:
- rcpt = Mailbox.objects.get(domain=domain, address=local_part)
+ rcpt = Mailbox.objects.get(
+ domain=domain, address=local_part)
except Mailbox.DoesNotExist:
raise NotFound(
_("Local recipient %s not found" % v)
diff --git a/modoboa/extensions/admin/forms/domain.py b/modoboa/extensions/admin/forms/domain.py
index e9efb0c78..45bf1149e 100644
--- a/modoboa/extensions/admin/forms/domain.py
+++ b/modoboa/extensions/admin/forms/domain.py
@@ -1,19 +1,21 @@
+"""Forms related to domains management."""
+
from django import forms
+from django.core.urlresolvers import reverse
from django.http import QueryDict
from django.utils.translation import ugettext as _, ugettext_lazy
-from django.core.urlresolvers import reverse
-from modoboa.lib import events, parameters
-from modoboa.lib.exceptions import ModoboaException, Conflict
-from modoboa.lib.formutils import (
- DomainNameField, YesNoField, WizardForm, DynamicForm, TabForms
-)
-from modoboa.lib.webutils import render_to_json_response
from modoboa.core.models import User
from modoboa.extensions.admin.lib import check_if_domain_exists
from modoboa.extensions.admin.models import (
Domain, DomainAlias, Mailbox, Alias, Quota
)
+from modoboa.lib import events, parameters
+from modoboa.lib.exceptions import ModoboaException, Conflict
+from modoboa.lib.form_utils import (
+ DomainNameField, YesNoField, WizardForm, DynamicForm, TabForms
+)
+from modoboa.lib.web_utils import render_to_json_response
class DomainFormGeneral(forms.ModelForm, DynamicForm):
@@ -153,12 +155,13 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
continue
aliases.append(v)
for dalias in d.domainalias_set.all():
- if not dalias.name in aliases:
+ if dalias.name not in aliases:
dalias.delete()
else:
aliases.remove(dalias.name)
if aliases:
- events.raiseEvent("CanCreate", user, "domain_aliases", len(aliases))
+ events.raiseEvent(
+ "CanCreate", user, "domain_aliases", len(aliases))
for alias in aliases:
try:
d.domainalias_set.get(name=alias)
@@ -207,7 +210,8 @@ class DomainFormOptions(forms.Form):
def __init__(self, user, *args, **kwargs):
super(DomainFormOptions, self).__init__(*args, **kwargs)
- if False in events.raiseQueryEvent('UserCanSetRole', user, 'DomainAdmins'):
+ result = events.raiseQueryEvent('UserCanSetRole', user, 'DomainAdmins')
+ if False in result:
self.fields = {}
return
if args:
@@ -324,8 +328,10 @@ class DomainForm(TabForms):
class DomainWizard(WizardForm):
+
"""Domain creation wizard.
"""
+
def __init__(self, request):
super(DomainWizard, self).__init__(request)
self.add_step(
diff --git a/modoboa/extensions/admin/forms/export.py b/modoboa/extensions/admin/forms/export.py
index bee201157..f1f8a868b 100644
--- a/modoboa/extensions/admin/forms/export.py
+++ b/modoboa/extensions/admin/forms/export.py
@@ -32,12 +32,14 @@ class ExportDataForm(forms.Form):
class ExportDomainsForm(ExportDataForm):
+
def __init__(self, *args, **kwargs):
super(ExportDomainsForm, self).__init__(*args, **kwargs)
self.fields["filename"].initial = "modoboa-domains.csv"
class ExportIdentitiesForm(ExportDataForm):
+
def __init__(self, *args, **kwargs):
super(ExportIdentitiesForm, self).__init__(*args, **kwargs)
self.fields["filename"].initial = "modoboa-identities.csv"
diff --git a/modoboa/extensions/admin/forms/forward.py b/modoboa/extensions/admin/forms/forward.py
index 63de1ee5c..c4d1e4a7b 100644
--- a/modoboa/extensions/admin/forms/forward.py
+++ b/modoboa/extensions/admin/forms/forward.py
@@ -1,10 +1,13 @@
+"""Forms related to forwards management."""
+
from django import forms
from django.utils.translation import ugettext as _, ugettext_lazy
-from modoboa.lib.exceptions import BadRequest, PermDeniedException
-from modoboa.lib.emailutils import split_mailbox
+
from modoboa.extensions.admin.models import (
Domain
)
+from modoboa.lib.email_utils import split_mailbox
+from modoboa.lib.exceptions import BadRequest, PermDeniedException
class ForwardForm(forms.Form):
@@ -12,12 +15,14 @@ class ForwardForm(forms.Form):
label=ugettext_lazy("Recipient(s)"),
widget=forms.Textarea(attrs={"class": "form-control"}),
required=False,
- help_text=ugettext_lazy("Indicate one or more recipients separated by a ','")
+ help_text=ugettext_lazy(
+ "Indicate one or more recipients separated by a ','")
)
keepcopies = forms.BooleanField(
label=ugettext_lazy("Keep local copies"),
required=False,
- help_text=ugettext_lazy("Forward messages and store copies into your local mailbox")
+ help_text=ugettext_lazy(
+ "Forward messages and store copies into your local mailbox")
)
def parse_dest(self):
diff --git a/modoboa/extensions/admin/forms/import_.py b/modoboa/extensions/admin/forms/import_.py
index f63017037..0091e4ba8 100644
--- a/modoboa/extensions/admin/forms/import_.py
+++ b/modoboa/extensions/admin/forms/import_.py
@@ -1,8 +1,13 @@
+"""Forms related to import operations."""
+
from django import forms
from django.utils.translation import ugettext_lazy
class ImportDataForm(forms.Form):
+
+ """Base form to import objects."""
+
sourcefile = forms.FileField(label=ugettext_lazy("Select a file"))
sepchar = forms.CharField(
label=ugettext_lazy("Separator"),
@@ -26,7 +31,13 @@ class ImportDataForm(forms.Form):
class ImportIdentitiesForm(ImportDataForm):
+
+ """A form to import identities."""
+
crypt_password = forms.BooleanField(
label=ugettext_lazy("Crypt passwords"), required=False,
- help_text=ugettext_lazy("Check this option if passwords contained in your file are not crypted")
+ help_text=ugettext_lazy(
+ "Check this option if passwords contained in your file "
+ "are not crypted"
+ )
)
diff --git a/modoboa/extensions/admin/lib.py b/modoboa/extensions/admin/lib.py
index 5b68e1273..decb4a804 100644
--- a/modoboa/extensions/admin/lib.py
+++ b/modoboa/extensions/admin/lib.py
@@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _
from modoboa.core.models import User
from modoboa.lib import events
from modoboa.lib.exceptions import PermDeniedException
-from modoboa.lib.emailutils import split_mailbox
+from modoboa.lib.email_utils import split_mailbox
from modoboa.extensions.admin.models import (
Domain, Alias
)
diff --git a/modoboa/extensions/admin/migrations/0015_move_passwords.py b/modoboa/extensions/admin/migrations/0015_move_passwords.py
index ebf626a2e..8b32209ed 100644
--- a/modoboa/extensions/admin/migrations/0015_move_passwords.py
+++ b/modoboa/extensions/admin/migrations/0015_move_passwords.py
@@ -4,7 +4,7 @@ from south.db import db
from south.v2 import DataMigration
from django.db import models
from modoboa.lib.models import Parameter
-from modoboa.lib.dbutils import db_table_exists
+from modoboa.lib.db_utils import db_table_exists
class Migration(DataMigration):
diff --git a/modoboa/extensions/admin/models/alias.py b/modoboa/extensions/admin/models/alias.py
index 4ced8b994..04c154603 100644
--- a/modoboa/extensions/admin/models/alias.py
+++ b/modoboa/extensions/admin/models/alias.py
@@ -1,16 +1,21 @@
-import reversion
+"""Models related to aliases management."""
+
from django.db import models
from django.utils.translation import ugettext as _, ugettext_lazy
-from modoboa.lib.emailutils import split_mailbox
-from modoboa.lib.exceptions import (
- PermDeniedException, BadRequest, Conflict
-)
+
+import reversion
+
from .base import AdminObject
from .domain import Domain
from .mailbox import Mailbox
+from modoboa.lib.email_utils import split_mailbox
+from modoboa.lib.exceptions import (
+ PermDeniedException, BadRequest, Conflict
+)
class Alias(AdminObject):
+
"""
Mailbox alias.
"""
@@ -108,16 +113,16 @@ class Alias(AdminObject):
curmboxes = self.mboxes.all()
for rcpt in int_rcpts:
if isinstance(rcpt, Alias):
- if not rcpt in curaliases:
+ if rcpt not in curaliases:
self.aliases.add(rcpt)
continue
- if not rcpt in curmboxes:
+ if rcpt not in curmboxes:
self.mboxes.add(rcpt)
for rcpt in curaliases:
- if not rcpt in int_rcpts:
+ if rcpt not in int_rcpts:
self.aliases.remove(rcpt)
for rcpt in curmboxes:
- if not rcpt in int_rcpts:
+ if rcpt not in int_rcpts:
self.mboxes.remove(rcpt)
def get_recipients(self, with_external=True):
diff --git a/modoboa/extensions/admin/models/base.py b/modoboa/extensions/admin/models/base.py
index 5f3fd61a3..c576bdcad 100644
--- a/modoboa/extensions/admin/models/base.py
+++ b/modoboa/extensions/admin/models/base.py
@@ -6,6 +6,7 @@ from modoboa.lib.permissions import (
class ObjectDates(models.Model):
+
"""Dates recording for admin objects
This table keeps creation and last modification dates for Domains,
@@ -32,6 +33,7 @@ class ObjectDates(models.Model):
class AdminObject(models.Model):
+
"""Abstract model to support dates
Inherit from this model to automatically add the "dates" feature
diff --git a/modoboa/extensions/admin/models/domain.py b/modoboa/extensions/admin/models/domain.py
index 5d8566d62..20b5d0c1e 100644
--- a/modoboa/extensions/admin/models/domain.py
+++ b/modoboa/extensions/admin/models/domain.py
@@ -1,12 +1,16 @@
-import reversion
+"""Models related to domains management."""
+
+from django.contrib.contenttypes import generic
from django.db import models
from django.db.models.manager import Manager
from django.utils.translation import ugettext as _, ugettext_lazy
-from django.contrib.contenttypes import generic
+
+import reversion
+
+from .base import AdminObject
+from modoboa.core.models import User, ObjectAccess
from modoboa.lib import events, parameters
from modoboa.lib.exceptions import BadRequest, Conflict
-from modoboa.core.models import User, ObjectAccess
-from .base import AdminObject
class DomainManager(Manager):
@@ -23,6 +27,7 @@ class DomainManager(Manager):
class Domain(AdminObject):
+
"""Mail domain.
"""
name = models.CharField(ugettext_lazy('name'), max_length=100, unique=True,
@@ -109,7 +114,7 @@ class Domain(AdminObject):
ungrant_access_to_object(mb, account)
ungrant_access_to_object(mb.user, account)
for al in self.alias_set.all():
- ungrant_access_to_object(al, account)
+ ungrant_access_to_object(al, account)
def delete(self, fromuser, keepdir=False):
"""Custom delete method.
@@ -157,7 +162,8 @@ class Domain(AdminObject):
try:
self.quota = int(row[2].strip())
except ValueError:
- raise BadRequest(_("Invalid quota value for domain '%s'" % self.name))
+ raise BadRequest(
+ _("Invalid quota value for domain '%s'" % self.name))
self.enabled = (row[3].strip() in ["True", "1", "yes", "y"])
self.save(creator=user)
diff --git a/modoboa/extensions/admin/models/domain_alias.py b/modoboa/extensions/admin/models/domain_alias.py
index 51b542dd0..a0ab23bfc 100644
--- a/modoboa/extensions/admin/models/domain_alias.py
+++ b/modoboa/extensions/admin/models/domain_alias.py
@@ -1,9 +1,13 @@
-import reversion
+"""Models related to domain aliases management."""
+
from django.db import models
from django.utils.translation import ugettext as _, ugettext_lazy
-from modoboa.lib.exceptions import BadRequest, Conflict
+
+import reversion
+
from .base import AdminObject
from .domain import Domain
+from modoboa.lib.exceptions import BadRequest, Conflict
class DomainAlias(AdminObject):
diff --git a/modoboa/extensions/admin/models/mailbox.py b/modoboa/extensions/admin/models/mailbox.py
index 631dbb2ff..fcc46e52c 100644
--- a/modoboa/extensions/admin/models/mailbox.py
+++ b/modoboa/extensions/admin/models/mailbox.py
@@ -1,19 +1,24 @@
+"""Models related to mailboxes management."""
+
import os
import pwd
-import reversion
+
+from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Q
from django.db.models.manager import Manager
-from django.contrib.contenttypes.models import ContentType
-from django.utils.translation import ugettext as _, ugettext_lazy
from django.db.models.signals import pre_delete
from django.dispatch import receiver
+from django.utils.translation import ugettext as _, ugettext_lazy
+
+import reversion
+
+from .base import AdminObject
+from .domain import Domain
+from modoboa.core.models import User
from modoboa.lib import parameters, events
from modoboa.lib.exceptions import BadRequest, InternalError
from modoboa.lib.sysutils import exec_cmd
-from modoboa.core.models import User
-from .base import AdminObject
-from .domain import Domain
class Quota(models.Model):
@@ -72,7 +77,8 @@ class Mailbox(AdminObject):
address = models.CharField(
ugettext_lazy('address'), max_length=252,
- help_text=ugettext_lazy("Mailbox address (without the @domain.tld part)")
+ help_text=ugettext_lazy(
+ "Mailbox address (without the @domain.tld part)")
)
quota = models.PositiveIntegerField()
use_domain_quota = models.BooleanField(default=False)
@@ -112,7 +118,7 @@ class Mailbox(AdminObject):
@property
def mail_home(self):
"""Retrieve the home directory of this mailbox.
-
+
The home directory refers to the place on the file system
where the mailbox data is stored.
@@ -131,9 +137,10 @@ class Mailbox(AdminObject):
options['sudo_user'] = mbowner
code, output = exec_cmd(
"doveadm user %s -f home" % self.full_address, **options
- )
+ )
if code:
- raise InternalError(_("Failed to retrieve mailbox location (%s)" % output))
+ raise InternalError(
+ _("Failed to retrieve mailbox location (%s)" % output))
self.__mail_home = output.strip()
return self.__mail_home
@@ -273,7 +280,9 @@ class Mailbox(AdminObject):
We try to delete the associated quota in the same time (it may
has already been removed if we're deleting a domain).
- :param bool keepdir: delete the mailbox home dir on the filesystem or not
+ :param bool keepdir: delete the mailbox home dir on the
+ filesystem or not
+
"""
try:
q = Quota.objects.get(username=self.full_address)
@@ -322,4 +331,4 @@ class MailboxOperation(models.Model):
def __str__(self):
if self.type == 'rename':
return 'Rename %s -> %s' % (self.argument, self.mailbox.mail_home)
- return 'Delete %s' % self.argument
+ return 'Delete %s' % self.argument
diff --git a/modoboa/extensions/admin/templatetags/admin_tags.py b/modoboa/extensions/admin/templatetags/admin_tags.py
index abc4d2451..d05b8ad9c 100644
--- a/modoboa/extensions/admin/templatetags/admin_tags.py
+++ b/modoboa/extensions/admin/templatetags/admin_tags.py
@@ -9,7 +9,7 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.lib import events
from modoboa.lib.templatetags.lib_tags import render_link
-from modoboa.lib.webutils import render_actions
+from modoboa.lib.web_utils import render_actions
register = template.Library()
@@ -147,7 +147,8 @@ def domain_actions(user, domain):
if domain.__class__.__name__ == 'Domain':
actions = [
{"name": "listidentities",
- "url": reverse("admin:identity_list") + "#list/?searchquery=@%s" % domain.name,
+ "url": "{0}#list/?searchquery=@{1}".format(
+ reverse("admin:identity_list"), domain.name),
"title": _("View the domain's identities"),
"img": "fa fa-user"}
]
@@ -277,5 +278,3 @@ def get_extra_admin_content(user, target, currentpage):
"ExtraAdminContent", user, target, currentpage
)
return "".join(res)
-
-
diff --git a/modoboa/extensions/admin/tests/account.py b/modoboa/extensions/admin/tests/account.py
index 12b16fee8..6881056a4 100644
--- a/modoboa/extensions/admin/tests/account.py
+++ b/modoboa/extensions/admin/tests/account.py
@@ -1,10 +1,11 @@
from django.core.urlresolvers import reverse
+
from modoboa.core.models import User
-from modoboa.lib.tests import ModoTestCase
+from modoboa.extensions.admin import factories
from modoboa.extensions.admin.models import (
Domain, Mailbox
)
-from modoboa.extensions.admin import factories
+from modoboa.lib.tests import ModoTestCase
class AccountTestCase(ModoTestCase):
@@ -62,10 +63,14 @@ class AccountTestCase(ModoTestCase):
def test_set_nul_quota_as_domainadmin(self):
self.clt.logout()
- self.assertTrue(self.clt.login(username="admin@test.com", password="toto"))
+ self.assertTrue(
+ self.clt.login(username="admin@test.com", password="toto")
+ )
self._set_quota("user@test.com", 0, 400)
self.clt.logout()
- self.assertTrue(self.clt.login(username="admin@test2.com", password="toto"))
+ self.assertTrue(
+ self.clt.login(username="admin@test2.com", password="toto")
+ )
self._set_quota("user@test2.com", 0)
@@ -119,8 +124,9 @@ class PermissionsTestCase(ModoTestCase):
def test_self_modif(self):
self.clt.logout()
- self.assertEqual(self.clt.login(username="admin@test.com", password="toto"),
- True)
+ self.assertTrue(
+ self.clt.login(username="admin@test.com", password="toto")
+ )
admin = User.objects.get(username="admin@test.com")
values = dict(
username="admin@test.com", first_name="Admin",
@@ -132,7 +138,7 @@ class PermissionsTestCase(ModoTestCase):
values
)
self.assertEqual(admin.group, "DomainAdmins")
- self.assertEqual(admin.can_access(Domain.objects.get(name="test.com")), True)
+ self.assertTrue(admin.can_access(Domain.objects.get(name="test.com")))
values["role"] = "SuperAdmins"
self.ajax_post(
diff --git a/modoboa/extensions/admin/tests/alias.py b/modoboa/extensions/admin/tests/alias.py
index 5dbac1a8d..e470b8d49 100644
--- a/modoboa/extensions/admin/tests/alias.py
+++ b/modoboa/extensions/admin/tests/alias.py
@@ -1,10 +1,11 @@
from django.core.urlresolvers import reverse
+
from modoboa.core.models import User
-from modoboa.lib.tests import ModoTestCase
+from modoboa.extensions.admin import factories
from modoboa.extensions.admin.models import (
Alias
)
-from modoboa.extensions.admin import factories
+from modoboa.lib.tests import ModoTestCase
class AliasTestCase(ModoTestCase):
@@ -80,8 +81,8 @@ class AliasTestCase(ModoTestCase):
self.assertEqual(fwd.get_recipients_count(), 1)
self.ajax_post(
- reverse("admin:alias_delete") + "?selection=%d" \
- % fwd.id, {}
+ reverse("admin:alias_delete") + "?selection=%d"
+ % fwd.id, {}
)
self.assertRaises(Alias.DoesNotExist, Alias.objects.get,
address="forward2", domain__name="test.com")
diff --git a/modoboa/extensions/admin/tests/export.py b/modoboa/extensions/admin/tests/export.py
index 19df335a1..ca95c4b7d 100644
--- a/modoboa/extensions/admin/tests/export.py
+++ b/modoboa/extensions/admin/tests/export.py
@@ -1,6 +1,7 @@
from django.core.urlresolvers import reverse
-from modoboa.lib.tests import ModoTestCase
+
from modoboa.extensions.admin import factories
+from modoboa.lib.tests import ModoTestCase
class ExportTestCase(ModoTestCase):
@@ -12,8 +13,8 @@ class ExportTestCase(ModoTestCase):
def __export_identities(self, idtfilter="", grpfilter=""):
self.clt.get(
- reverse("admin:_identity_list") \
- + "?grpfilter=%s&idtfilter=%s" % (grpfilter, idtfilter)
+ reverse("admin:_identity_list")
+ + "?grpfilter=%s&idtfilter=%s" % (grpfilter, idtfilter)
)
return self.clt.post(
reverse("admin:identity_export"),
@@ -39,7 +40,7 @@ class ExportTestCase(ModoTestCase):
idtfilter="account", grpfilter="SimpleUsers"
)
self.assertListEqual(
- "account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10",
+ "account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10",
response.content.strip()
)
diff --git a/modoboa/extensions/admin/tests/import_.py b/modoboa/extensions/admin/tests/import_.py
index 5195ad0e0..dee11d995 100644
--- a/modoboa/extensions/admin/tests/import_.py
+++ b/modoboa/extensions/admin/tests/import_.py
@@ -1,12 +1,13 @@
# coding: utf-8
-from django.core.urlresolvers import reverse
from django.core.files.base import ContentFile
+from django.core.urlresolvers import reverse
+
from modoboa.core.models import User
-from modoboa.lib.tests import ModoTestCase
+from modoboa.extensions.admin import factories
from modoboa.extensions.admin.models import (
Domain, Alias, DomainAlias
)
-from modoboa.extensions.admin import factories
+from modoboa.lib.tests import ModoTestCase
class ImportTestCase(ModoTestCase):
@@ -76,7 +77,9 @@ dlist; dlist@test.com; True; user1@test.com; user@extdomain.com
self.assertEqual(mb1.quota, 0)
self.assertTrue(admin.is_owner(mb1))
self.assertEqual(mb1.full_address, "user1@test.com")
- self.assertTrue(self.clt.login(username="user1@test.com", password="toto"))
+ self.assertTrue(
+ self.clt.login(username="user1@test.com", password="toto")
+ )
da = User.objects.get(username="truc@test.com")
damb = da.mailbox_set.all()[0]
@@ -159,7 +162,9 @@ account; truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 0;
Expected result: no
"""
self.clt.logout()
- self.assertTrue(self.clt.login(username="admin@test.com", password="toto"))
+ self.assertTrue(
+ self.clt.login(username="admin@test.com", password="toto")
+ )
f = ContentFile(b"""
account; sa@test.com; toto; Super; Admin; True; SuperAdmins; superadmin@test.com; 50
""", name="identities.csv")
diff --git a/modoboa/extensions/admin/views/alias.py b/modoboa/extensions/admin/views/alias.py
index 4d73a4372..0104b667a 100644
--- a/modoboa/extensions/admin/views/alias.py
+++ b/modoboa/extensions/admin/views/alias.py
@@ -7,7 +7,7 @@ from django.contrib.auth.decorators import (
login_required, permission_required
)
from modoboa.lib import events
-from modoboa.lib.webutils import render_to_json_response
+from modoboa.lib.web_utils import render_to_json_response
from modoboa.lib.exceptions import PermDeniedException, Conflict
from modoboa.extensions.admin.forms import AliasForm
from modoboa.extensions.admin.models import Alias
diff --git a/modoboa/extensions/admin/views/domain.py b/modoboa/extensions/admin/views/domain.py
index 3cabdf935..35ef6a68d 100644
--- a/modoboa/extensions/admin/views/domain.py
+++ b/modoboa/extensions/admin/views/domain.py
@@ -15,7 +15,7 @@ import reversion
from modoboa.core.models import User
from modoboa.lib import parameters, events
-from modoboa.lib.webutils import (
+from modoboa.lib.web_utils import (
_render_to_string, render_to_json_response
)
from modoboa.lib.exceptions import (
diff --git a/modoboa/extensions/admin/views/export.py b/modoboa/extensions/admin/views/export.py
index 865371746..38fe6494f 100644
--- a/modoboa/extensions/admin/views/export.py
+++ b/modoboa/extensions/admin/views/export.py
@@ -1,17 +1,22 @@
-import csv
+"""Export related views."""
+
import cStringIO
+import csv
+
from rfc6266 import build_header
-from django.http import HttpResponse
-from django.shortcuts import render
-from django.core.urlresolvers import reverse
-from django.utils.translation import ugettext as _
+
from django.contrib.auth.decorators import (
login_required, permission_required, user_passes_test
)
-from modoboa.extensions.admin.lib import get_domains, get_identities
+from django.core.urlresolvers import reverse
+from django.http import HttpResponse
+from django.shortcuts import render
+from django.utils.translation import ugettext as _
+
from modoboa.extensions.admin.forms import (
ExportIdentitiesForm, ExportDomainsForm
)
+from modoboa.extensions.admin.lib import get_domains, get_identities
def _export(content, filename):
@@ -46,7 +51,9 @@ def export_identities(request):
form.is_valid()
fp = cStringIO.StringIO()
csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"])
- for ident in get_identities(request.user, **request.session['identities_filters']):
+ identities = get_identities(
+ request.user, **request.session['identities_filters'])
+ for ident in identities:
ident.to_csv(csvwriter)
content = fp.getvalue()
fp.close()
diff --git a/modoboa/extensions/admin/views/identity.py b/modoboa/extensions/admin/views/identity.py
index 0c0a29b20..58b93cebb 100644
--- a/modoboa/extensions/admin/views/identity.py
+++ b/modoboa/extensions/admin/views/identity.py
@@ -1,30 +1,29 @@
-"""
-Identity related views.
-"""
-from django.shortcuts import render
-from django.utils.translation import ugettext as _, ungettext
+"""Identity related views."""
+
from django.contrib.auth.decorators import (
login_required, permission_required, user_passes_test
)
+from django.shortcuts import render
+from django.utils.translation import ugettext as _, ungettext
from django.views.decorators.csrf import ensure_csrf_cookie
import reversion
+from modoboa.core.models import User
+from modoboa.extensions.admin.forms import (
+ AccountForm
+)
+from modoboa.extensions.admin.lib import get_identities
+from modoboa.extensions.admin.models import Mailbox, Domain
from modoboa.lib import parameters, events
from modoboa.lib.exceptions import (
PermDeniedException, BadRequest
)
-from modoboa.lib.webutils import (
- _render_to_string, render_to_json_response
-)
-from modoboa.core.models import User
-from modoboa.extensions.admin.models import Mailbox, Domain
from modoboa.lib.listing import (
get_sort_order, get_listing_page
)
-from modoboa.extensions.admin.lib import get_identities
-from modoboa.extensions.admin.forms import (
- AccountForm
+from modoboa.lib.web_utils import (
+ _render_to_string, render_to_json_response
)
@@ -67,7 +66,7 @@ def _identities(request):
@login_required
@permission_required("admin.add_mailbox")
def list_quotas(request):
- from modoboa.lib.dbutils import db_type
+ from modoboa.lib.db_utils import db_type
sort_order, sort_dir = get_sort_order(request.GET, "address")
mboxes = Mailbox.objects.get_for_admin(
@@ -80,9 +79,15 @@ def list_quotas(request):
where = "admin_mailbox.address||'@'||admin_domain.name"
db_type = db_type()
if db_type == "postgres":
- select = '(admin_quota.bytes::float / (CAST(admin_mailbox.quota AS BIGINT) * 1048576)) * 100'
+ select = (
+ "(admin_quota.bytes::float / (CAST(admin_mailbox.quota "
+ "AS BIGINT) * 1048576)) * 100"
+ )
else:
- select = 'admin_quota.bytes / (admin_mailbox.quota * 1048576) * 100'
+ select = (
+ "admin_quota.bytes / (admin_mailbox.quota "
+ "* 1048576) * 100"
+ )
if db_type == "mysql":
where = "CONCAT(admin_mailbox.address,'@',admin_domain.name)"
mboxes = mboxes.extra(
@@ -191,7 +196,8 @@ def remove_permission(request):
domain = Domain.objects.get(pk=domid)
except (User.DoesNotExist, Domain.DoesNotExist):
raise BadRequest(_("Invalid request"))
- if not request.user.can_access(account) or not request.user.can_access(domain):
+ if not request.user.can_access(account) or \
+ not request.user.can_access(domain):
raise PermDeniedException
domain.remove_admin(account)
return render_to_json_response({})
diff --git a/modoboa/extensions/admin/views/import.py b/modoboa/extensions/admin/views/import.py
index fb1e6918f..015db07d2 100644
--- a/modoboa/extensions/admin/views/import.py
+++ b/modoboa/extensions/admin/views/import.py
@@ -1,18 +1,23 @@
+"""Import related views."""
+
import csv
-import reversion
-from django.utils.translation import ugettext as _
-from django.shortcuts import render
-from django.core.urlresolvers import reverse
+
from django.contrib.auth.decorators import (
login_required, permission_required, user_passes_test
)
-from modoboa.lib import events
-from modoboa.lib.exceptions import ModoboaException, Conflict
+from django.core.urlresolvers import reverse
+from django.shortcuts import render
+from django.utils.translation import ugettext as _
+
+import reversion
+
from modoboa.core.models import User
+from modoboa.extensions.admin.forms import ImportIdentitiesForm, ImportDataForm
from modoboa.extensions.admin.models import (
Domain, DomainAlias, Alias
)
-from modoboa.extensions.admin.forms import ImportIdentitiesForm, ImportDataForm
+from modoboa.lib import events
+from modoboa.lib.exceptions import ModoboaException, Conflict
def import_domain(user, row, formopts):
@@ -41,7 +46,8 @@ def import_domains(request):
The first element of each line is mandatory and must be equal to one of the previous values.
You can use a different character as separator.
-""" % ''.join([unicode(hlp) for hlp in events.raiseQueryEvent('ExtraDomainImportHelp')]))
+""" % ''.join([unicode(hlp) for hlp in
+ events.raiseQueryEvent('ExtraDomainImportHelp')]))
ctx = dict(
title=_("Import domains"),
@@ -130,7 +136,7 @@ def importdata(request, formclass=ImportDataForm):
return render(request, "admin/import_done.html", {
"status": "ok", "msg": msg
})
- except (ModoboaException), e:
+ except (ModoboaException) as e:
error = str(e)
return render(request, "admin/import_done.html", {
diff --git a/modoboa/extensions/admin/views/user.py b/modoboa/extensions/admin/views/user.py
index 15e0a9b6c..55166166c 100644
--- a/modoboa/extensions/admin/views/user.py
+++ b/modoboa/extensions/admin/views/user.py
@@ -1,7 +1,7 @@
import reversion
from django.contrib.auth.decorators import login_required
from django.utils.translation import ugettext as _
-from modoboa.lib.webutils import (
+from modoboa.lib.web_utils import (
render_to_json_response, _render_to_string
)
from modoboa.extensions.admin.lib import needs_mailbox
diff --git a/modoboa/extensions/amavis/app_settings.py b/modoboa/extensions/amavis/app_settings.py
index 2fa1957cf..572d31d99 100644
--- a/modoboa/extensions/amavis/app_settings.py
+++ b/modoboa/extensions/amavis/app_settings.py
@@ -3,7 +3,7 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
-from modoboa.lib.formutils import SeparatorField, YesNoField, InlineRadioSelect
+from modoboa.lib.form_utils import SeparatorField, YesNoField, InlineRadioSelect
from modoboa.lib.parameters import AdminParametersForm, UserParametersForm
diff --git a/modoboa/extensions/amavis/forms.py b/modoboa/extensions/amavis/forms.py
index 72be441e9..7af8c2b52 100644
--- a/modoboa/extensions/amavis/forms.py
+++ b/modoboa/extensions/amavis/forms.py
@@ -7,7 +7,7 @@ from django import forms
from django.utils.translation import ugettext as _
from modoboa.lib import parameters
-from modoboa.lib.formutils import InlineRadioSelect
+from modoboa.lib.form_utils import InlineRadioSelect
from .models import Policy, Users
@@ -48,7 +48,7 @@ class DomainPolicyForm(forms.ModelForm):
def save(self, user, commit=True):
policy = super(DomainPolicyForm, self).save(commit=False)
for field in ['bypass_spam_checks', 'bypass_virus_checks',
- 'bypass_banned_checks']:
+ 'bypass_banned_checks']:
if getattr(policy, field) == '':
setattr(policy, field, None)
diff --git a/modoboa/extensions/amavis/lib.py b/modoboa/extensions/amavis/lib.py
index 646ca46c8..543913b8d 100644
--- a/modoboa/extensions/amavis/lib.py
+++ b/modoboa/extensions/amavis/lib.py
@@ -11,10 +11,10 @@ from django.core.urlresolvers import reverse
from modoboa.extensions.admin.models import Mailbox, Alias
from modoboa.lib import parameters
-from modoboa.lib.emailutils import split_mailbox
+from modoboa.lib.email_utils import split_mailbox
from modoboa.lib.exceptions import InternalError
from modoboa.lib.sysutils import exec_cmd
-from modoboa.lib.webutils import NavigationParameters
+from modoboa.lib.web_utils import NavigationParameters
from .models import Users, Policy
diff --git a/modoboa/extensions/amavis/management/commands/amnotify.py b/modoboa/extensions/amavis/management/commands/amnotify.py
index a0ff57f57..82c5f62f0 100644
--- a/modoboa/extensions/amavis/management/commands/amnotify.py
+++ b/modoboa/extensions/amavis/management/commands/amnotify.py
@@ -10,7 +10,7 @@ from django.utils.translation import ugettext as _
from modoboa.core.management.commands import CloseConnectionMixin
from modoboa.core.models import User
from modoboa.lib import parameters
-from modoboa.lib.emailutils import sendmail_simple
+from modoboa.lib.email_utils import sendmail_simple
from modoboa.extensions.admin.models import Domain
from modoboa.extensions.amavis import Amavis
diff --git a/modoboa/extensions/amavis/models.py b/modoboa/extensions/amavis/models.py
index 7a711fdd8..b2df0cbb1 100644
--- a/modoboa/extensions/amavis/models.py
+++ b/modoboa/extensions/amavis/models.py
@@ -2,10 +2,11 @@
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
-# Feel free to rename the models, but don't rename db_table values or field names.
+# Feel free to rename the models, but don't rename db_table values
+# or field names.
#
-# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'
-# into your database.
+# Also note: You'll have to insert the output of
+# 'django-admin.py sqlcustom [appname]'into your database.
#
# Original Amavis version : 2.6.2
@@ -95,42 +96,81 @@ class Policy(models.Model):
('Y', ugettext_lazy('no')),
('', ugettext_lazy('default'))),
max_length=3,
- help_text=ugettext_lazy("Bypass virus checks or not. Choose 'default' to use global settings.")
+ help_text=ugettext_lazy(
+ "Bypass virus checks or not. Choose 'default' to use global "
+ "settings."
)
+ )
bypass_spam_checks = models.CharField(
ugettext_lazy("Spam filter"), default='', null=True,
choices=(('N', ugettext_lazy('yes')),
('Y', ugettext_lazy('no')),
('', ugettext_lazy('default'))),
max_length=3,
- help_text=ugettext_lazy("Bypass spam checks or not. Choose 'default' to use global settings.")
+ help_text=ugettext_lazy(
+ "Bypass spam checks or not. Choose 'default' to use global "
+ "settings."
)
+ )
bypass_banned_checks = models.CharField(
ugettext_lazy("Banned filter"), default='', null=True,
choices=(('N', ugettext_lazy('yes')),
('Y', ugettext_lazy('no')),
('', ugettext_lazy('default'))),
max_length=3,
- help_text=ugettext_lazy("Bypass banned checks or not. Choose 'default' to use global settings.")
+ help_text=ugettext_lazy(
+ "Bypass banned checks or not. Choose 'default' to use global "
+ "settings."
)
+ )
bypass_header_checks = models.CharField(max_length=3, blank=True, null=True)
- virus_quarantine_to = models.CharField(max_length=192, blank=True, null=True)
+ virus_quarantine_to = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
spam_quarantine_to = models.CharField(max_length=192, blank=True, null=True)
- banned_quarantine_to = models.CharField(max_length=192, blank=True, null=True)
- unchecked_quarantine_to = models.CharField(max_length=192, blank=True, null=True)
- bad_header_quarantine_to = models.CharField(max_length=192, blank=True, null=True)
- clean_quarantine_to = models.CharField(max_length=192, blank=True, null=True)
- archive_quarantine_to = models.CharField(max_length=192, blank=True, null=True)
+ banned_quarantine_to = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
+ unchecked_quarantine_to = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
+ bad_header_quarantine_to = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
+ clean_quarantine_to = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
+ archive_quarantine_to = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
spam_tag_level = models.FloatField(null=True, blank=True)
spam_tag2_level = models.FloatField(null=True, blank=True)
spam_tag3_level = models.FloatField(null=True, blank=True)
spam_kill_level = models.FloatField(null=True, blank=True)
spam_dsn_cutoff_level = models.FloatField(null=True, blank=True)
spam_quarantine_cutoff_level = models.FloatField(null=True, blank=True)
- addr_extension_virus = models.CharField(max_length=192, blank=True, null=True)
- addr_extension_spam = models.CharField(max_length=192, blank=True, null=True)
- addr_extension_banned = models.CharField(max_length=192, blank=True, null=True)
- addr_extension_bad_header = models.CharField(max_length=192, blank=True, null=True)
+ addr_extension_virus = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
+ addr_extension_spam = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
+ addr_extension_banned = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
+ addr_extension_bad_header = models.CharField(
+ max_length=192,
+ blank=True,
+ null=True)
warnvirusrecip = models.CharField(max_length=3, blank=True, null=True)
warnbannedrecip = models.CharField(max_length=3, blank=True, null=True)
warnbadhrecip = models.CharField(max_length=3, blank=True, null=True)
@@ -143,8 +183,11 @@ class Policy(models.Model):
spam_subject_tag2 = models.CharField(
ugettext_lazy("Spam marker"), default=None,
max_length=192, blank=True, null=True,
- help_text=ugettext_lazy("Modify spam subject using the specified text. Choose 'default' to use global settings.")
+ help_text=ugettext_lazy(
+ "Modify spam subject using the specified text. Choose 'default' to "
+ "use global settings."
)
+ )
spam_subject_tag3 = models.CharField(max_length=192, blank=True, null=True)
message_size_limit = models.IntegerField(null=True, blank=True)
banned_rulenames = models.CharField(max_length=192, blank=True, null=True)
diff --git a/modoboa/extensions/amavis/sql_connector.py b/modoboa/extensions/amavis/sql_connector.py
index 46f0075ae..daa6ed411 100644
--- a/modoboa/extensions/amavis/sql_connector.py
+++ b/modoboa/extensions/amavis/sql_connector.py
@@ -6,7 +6,7 @@ import datetime
from django.db.models import Q
from modoboa.extensions.admin.models import Domain
-from modoboa.lib.dbutils import db_type
+from modoboa.lib.db_utils import db_type
from .models import Quarantine, Msgrcpt, Maddr
@@ -65,7 +65,7 @@ class SQLconnector(object):
rcpts = [self.user.email]
if self.user.mailbox_set.count():
rcpts += self.user.mailbox_set.all()[0].alias_addresses
- return flt & Q(rid__email__in=rcpts)
+ return flt & Q(rid__email__in=rcpts)
elif not self.user.is_superuser:
doms = Domain.objects.get_for_admin(self.user)
regexp = "(%s)" % '|'.join([dom.name for dom in doms])
@@ -238,7 +238,7 @@ class PgSQLconnector(SQLconnector):
if self.user.mailbox_set.count():
rcpts += self.user.mailbox_set.all()[0].alias_addresses
self._where.append(
- "convert_from(maddr.email, 'UTF8') IN (%s)" \
+ "convert_from(maddr.email, 'UTF8') IN (%s)"
% (','.join(["'%s'" % rcpt for rcpt in rcpts])))
elif not self.user.is_superuser:
doms = Domain.objects.get_for_admin(self.user)
diff --git a/modoboa/extensions/amavis/sql_email.py b/modoboa/extensions/amavis/sql_email.py
index 2722f09a8..59a462932 100644
--- a/modoboa/extensions/amavis/sql_email.py
+++ b/modoboa/extensions/amavis/sql_email.py
@@ -5,7 +5,7 @@ An email representation based on a database record.
from django.template.loader import render_to_string
-from modoboa.lib.emailutils import Email
+from modoboa.lib.email_utils import Email
from .sql_connector import get_connector
diff --git a/modoboa/extensions/amavis/templatetags/amavis_tags.py b/modoboa/extensions/amavis/templatetags/amavis_tags.py
index 79b8ac9b3..f5d1a2c9a 100644
--- a/modoboa/extensions/amavis/templatetags/amavis_tags.py
+++ b/modoboa/extensions/amavis/templatetags/amavis_tags.py
@@ -80,20 +80,23 @@ def viewm_menu(user, mail_id, rcpt):
@register.simple_tag
def viewm_menu_simple(user, mail_id, rcpt, secret_id=""):
+ release_url = "{0}?rcpt={1}".format(
+ reverse("amavis:mail_release", args=[mail_id]), rcpt)
+ delete_url = "{0}?rcpt={1}".format(
+ reverse("amavis:mail_delete", args=[mail_id]), rcpt)
+ if secret_id:
+ release_url += "&secret_id={0}".format(secret_id)
+ delete_url += "&secret_id={0}".format(secret_id)
entries = [
{"name": "release",
"img": "fa fa-check",
"class": "btn-success",
- "url": reverse('amavis:mail_release', args=[mail_id]) \
- + ("?rcpt=%s" % rcpt \
- + (("&secret_id=%s" % secret_id) if secret_id != "" else "")),
+ "url": release_url,
"label": _("Release")},
{"name": "delete",
"img": "fa fa-trash",
"class": "btn-danger",
- "url": reverse('amavis:mail_delete', args=[mail_id]) \
- + "?rcpt=%s" % rcpt \
- + ("&secret_id=%s" % secret_id if secret_id != "" else ""),
+ "url": delete_url,
"label": _("Delete")},
]
diff --git a/modoboa/extensions/amavis/views.py b/modoboa/extensions/amavis/views.py
index 7a69b6528..4b75d11cd 100644
--- a/modoboa/extensions/amavis/views.py
+++ b/modoboa/extensions/amavis/views.py
@@ -16,7 +16,7 @@ from django.contrib.auth.decorators import login_required
from modoboa.lib import parameters
from modoboa.lib.exceptions import BadRequest
from modoboa.lib.paginator import Paginator
-from modoboa.lib.webutils import (
+from modoboa.lib.web_utils import (
getctx, render_to_json_response, _render_to_string
)
from modoboa.extensions.admin.models import Mailbox, Domain
@@ -179,7 +179,7 @@ def viewheaders(request, mail_id):
content = ""
for qm in get_connector().get_mail_content(mail_id):
content += qm.mail_text
- if type(content) is unicode:
+ if isinstance(content, unicode):
content = content.encode("utf-8")
msg = email.message_from_string(content)
headers = []
diff --git a/modoboa/extensions/limits/app_settings.py b/modoboa/extensions/limits/app_settings.py
index 8b149a75f..2d759a3cd 100644
--- a/modoboa/extensions/limits/app_settings.py
+++ b/modoboa/extensions/limits/app_settings.py
@@ -1,42 +1,52 @@
+"""Custom settings."""
+
from django import forms
from django.utils.translation import ugettext_lazy as _
+
+from modoboa.lib.form_utils import SeparatorField
from modoboa.lib.parameters import AdminParametersForm
-from modoboa.lib.formutils import SeparatorField
class ParametersForm(AdminParametersForm):
app = "limits"
defv_sep = SeparatorField(label=_("Default limits"))
-
+
deflt_domain_admins_limit = forms.IntegerField(
label=_("Domain admins"),
initial=0,
- help_text=_("Maximum number of allowed domain administrators for a new administrator"),
+ help_text=_(
+ "Maximum number of allowed domain administrators for a new "
+ "administrator"
+ ),
widget=forms.widgets.TextInput(attrs={"class": "col-md-1 form-control"})
)
deflt_domains_limit = forms.IntegerField(
label=_("Domains"),
initial=0,
- help_text=_("Maximum number of allowed domains for a new administrator"),
+ help_text=_(
+ "Maximum number of allowed domains for a new administrator"),
widget=forms.widgets.TextInput(attrs={"class": "col-md-1 form-control"})
)
deflt_domain_aliases_limit = forms.IntegerField(
label=_("Domain aliases"),
initial=0,
- help_text=_("Maximum number of allowed domain aliases for a new administrator"),
+ help_text=_(
+ "Maximum number of allowed domain aliases for a new administrator"),
widget=forms.widgets.TextInput(attrs={"class": "col-md-1 form-control"})
)
deflt_mailboxes_limit = forms.IntegerField(
label=_("Mailboxes"),
initial=0,
- help_text=_("Maximum number of allowed mailboxes for a new administrator"),
+ help_text=_(
+ "Maximum number of allowed mailboxes for a new administrator"),
widget=forms.widgets.TextInput(attrs={"class": "col-md-1 form-control"})
)
deflt_mailbox_aliases_limit = forms.IntegerField(
label=_("Mailbox aliases"),
initial=0,
- help_text=_("Maximum number of allowed aliases for a new administrator"),
+ help_text=_(
+ "Maximum number of allowed aliases for a new administrator"),
widget=forms.widgets.TextInput(attrs={"class": "col-md-1 form-control"})
)
diff --git a/modoboa/extensions/limits/controls.py b/modoboa/extensions/limits/controls.py
index 34ff09159..f5d00b4ac 100644
--- a/modoboa/extensions/limits/controls.py
+++ b/modoboa/extensions/limits/controls.py
@@ -4,10 +4,10 @@
----------------------------------------------------------------------------
"""
-from modoboa.lib import events
-from modoboa.lib.permissions import get_object_owner
from .lib import LimitReached, inc_limit_usage, dec_limit_usage
from .models import LimitTemplates, LimitsPool
+from modoboa.lib import events
+from modoboa.lib.permissions import get_object_owner
def check_limit(user, lname, count=1):
@@ -124,7 +124,7 @@ def can_create_new_object(user, objtype, count=1):
@events.observe("AccountCreated")
def create_pool(user):
owner = get_object_owner(user)
- if not owner.group in ['SuperAdmins', 'Resellers']:
+ if owner.group not in ['SuperAdmins', 'Resellers']:
return
if user.group == 'DomainAdmins':
@@ -177,7 +177,7 @@ def on_account_modified(old, new):
p.save()
p.create_limits(owner)
- if not new.group in ["DomainAdmins", "Resellers"]:
+ if new.group not in ["DomainAdmins", "Resellers"]:
move_pool_resource(owner, new)
if old.oldgroup == "DomainAdmins":
@@ -193,7 +193,7 @@ def on_account_modified(old, new):
@events.observe("AccountDeleted")
def on_account_deleted(account, byuser, **kwargs):
owner = get_object_owner(account)
- if not owner.group in ["SuperAdmins", "Resellers"]:
+ if owner.group not in ["SuperAdmins", "Resellers"]:
return
move_pool_resource(owner, account)
diff --git a/modoboa/extensions/limits/forms.py b/modoboa/extensions/limits/forms.py
index a763284e2..0f0b27f9a 100644
--- a/modoboa/extensions/limits/forms.py
+++ b/modoboa/extensions/limits/forms.py
@@ -1,9 +1,13 @@
# coding: utf-8
+
+"""Custom forms."""
+
from django import forms
-from django.utils.translation import ugettext as _, ugettext_lazy
-from modoboa.lib import parameters
-from .models import LimitTemplates
+from django.utils.translation import ugettext as _
+
from .lib import BadLimitValue, UnsufficientResource
+from .models import LimitTemplates
+from modoboa.lib import parameters
class ResourcePoolForm(forms.Form):
@@ -15,8 +19,9 @@ class ResourcePoolForm(forms.Form):
del kwargs["instance"]
super(ResourcePoolForm, self).__init__(*args, **kwargs)
for tpl in LimitTemplates().templates:
- if len(tpl) > 3 and self.account is not None and self.account.group != tpl[3]:
- continue
+ if len(tpl) > 3:
+ if self.account is not None and self.account.group != tpl[3]:
+ continue
self.fields[tpl[0]] = forms.IntegerField(
label=tpl[1], help_text=tpl[2]
)
@@ -31,7 +36,7 @@ class ResourcePoolForm(forms.Form):
def clean(self):
cleaned_data = super(ResourcePoolForm, self).clean()
for lname in self.fields.keys():
- if not lname in self._errors and cleaned_data[lname] < -1:
+ if lname not in self._errors and cleaned_data[lname] < -1:
self._errors[lname] = self.error_class([_('Invalid limit')])
del cleaned_data[lname]
return cleaned_data
@@ -42,7 +47,9 @@ class ResourcePoolForm(forms.Form):
# The following lines will become useless in a near
# future.
if self.fields[lname].initial == -2:
- self.fields[lname].initial = parameters.get_admin("DEFLT_%s" % lname.upper())
+ self.fields[lname].initial = parameters.get_admin(
+ "DEFLT_%s" %
+ lname.upper())
def allocate_from_pool(self, limit, pool):
"""Allocate resource using an existing pool.
@@ -76,7 +83,7 @@ class ResourcePoolForm(forms.Form):
owner = get_object_owner(self.account)
for ltpl in LimitTemplates().templates:
- if not ltpl[0] in self.cleaned_data:
+ if ltpl[0] not in self.cleaned_data:
continue
l = self.account.limitspool.limit_set.get(name=ltpl[0])
if not owner.is_superuser:
diff --git a/modoboa/extensions/limits/general_callbacks.py b/modoboa/extensions/limits/general_callbacks.py
index d06fea86b..66561368d 100644
--- a/modoboa/extensions/limits/general_callbacks.py
+++ b/modoboa/extensions/limits/general_callbacks.py
@@ -1,7 +1,10 @@
+"""General event callbacks."""
+
from django.utils.translation import ugettext as _
-from modoboa.lib import events
-from modoboa.extensions.limits.models import LimitTemplates
+
from modoboa.extensions.limits.forms import ResourcePoolForm
+from modoboa.extensions.limits.models import LimitTemplates
+from modoboa.lib import events
@events.observe("GetExtraRoles")
@@ -37,10 +40,10 @@ def display_pool_usage(user, target, currentpage):
@events.observe("ExtraAccountForm")
def extra_account_form(user, account=None):
- if not user.group in ["SuperAdmins", "Resellers"]:
+ if user.group not in ["SuperAdmins", "Resellers"]:
return []
if account is not None and \
- not account.group in ["Resellers", "DomainAdmins"]:
+ account.group not in ["Resellers", "DomainAdmins"]:
return []
return [
diff --git a/modoboa/extensions/limits/lib.py b/modoboa/extensions/limits/lib.py
index c190b26d7..c0135a234 100644
--- a/modoboa/extensions/limits/lib.py
+++ b/modoboa/extensions/limits/lib.py
@@ -5,8 +5,9 @@
"""
from django.utils.translation import ugettext as _
-from modoboa.lib.exceptions import ModoboaException
+
from modoboa.extensions.limits.models import LimitsPool
+from modoboa.lib.exceptions import ModoboaException
class LimitReached(ModoboaException):
diff --git a/modoboa/extensions/limits/models.py b/modoboa/extensions/limits/models.py
index 21f4e7b32..60ced9325 100644
--- a/modoboa/extensions/limits/models.py
+++ b/modoboa/extensions/limits/models.py
@@ -1,10 +1,13 @@
# coding: utf-8
+
+"""Models for the limits extensions."""
+
+from django.conf import settings
from django.db import models
from django.utils.translation import ugettext as _, ugettext_lazy
-from modoboa.lib import parameters
+
+from modoboa.lib import parameters, events
from modoboa.lib.singleton import Singleton
-from django.conf import settings
-from modoboa.lib import events
class LimitTemplates(Singleton):
@@ -12,23 +15,27 @@ class LimitTemplates(Singleton):
def __init__(self):
self.__templates = [
("domain_admins_limit", ugettext_lazy("Domain admins"),
- ugettext_lazy("Maximum number of domain administrators this user can create"),
+ ugettext_lazy("Maximum number of domain administrators this user "
+ "can create"),
'Resellers'),
("domains_limit", ugettext_lazy("Domains"),
ugettext_lazy("Maximum number of domains this user can create"),
'Resellers'),
("domain_aliases_limit", ugettext_lazy("Domain aliases"),
- ugettext_lazy("Maximum number of domain aliases this user can create"),
+ ugettext_lazy(
+ "Maximum number of domain aliases this user can create"),
'Resellers'),
("mailboxes_limit", ugettext_lazy("Mailboxes"),
ugettext_lazy("Maximum number of mailboxes this user can create")),
("mailbox_aliases_limit", ugettext_lazy("Mailbox aliases"),
- ugettext_lazy("Maximum number of mailbox aliases this user can create"))
+ ugettext_lazy("Maximum number of mailbox aliases this user "
+ "can create"))
]
-
+
@property
def templates(self):
- return self.__templates + events.raiseQueryEvent('GetExtraLimitTemplates')
+ return self.__templates + \
+ events.raiseQueryEvent('GetExtraLimitTemplates')
class LimitsPool(models.Model):
@@ -47,8 +54,10 @@ class LimitsPool(models.Model):
try:
Limit.objects.get(name=ltpl[0], pool=self)
except Limit.DoesNotExist:
- maxvalue = int(parameters.get_admin("DEFLT_%s" % ltpl[0].upper())) \
+ maxvalue = (
+ int(parameters.get_admin("DEFLT_%s" % ltpl[0].upper()))
if creator.is_superuser else 0
+ )
Limit.objects.create(
name=ltpl[0], pool=self, maxvalue=maxvalue
)
diff --git a/modoboa/extensions/limits/tests.py b/modoboa/extensions/limits/tests.py
index 4db5078df..893417ea0 100644
--- a/modoboa/extensions/limits/tests.py
+++ b/modoboa/extensions/limits/tests.py
@@ -1,13 +1,16 @@
+"""Test cases for the limits extension."""
+
from django.core.urlresolvers import reverse
-from modoboa.lib.tests import ExtTestCase
-from modoboa.lib import parameters
-from modoboa.core.models import User
+
from modoboa.core.factories import UserFactory
+from modoboa.core.models import User
from modoboa.extensions.admin.factories import (
populate_database
)
from modoboa.extensions.admin.models import Alias, Domain
from modoboa.extensions.limits.models import LimitTemplates
+from modoboa.lib import parameters
+from modoboa.lib.tests import ExtTestCase
class PermissionsTestCase(ExtTestCase):
@@ -142,8 +145,8 @@ class DomainAdminTestCase(ResourceTestCase):
resp = self._create_alias('alias3@test.com', status=403)
self._check_limit('mailbox_aliases', 2, 2)
self.ajax_post(
- reverse('admin:alias_delete') + '?selection=%d' \
- % Alias.objects.get(address='alias2', domain__name='test.com').id,
+ reverse('admin:alias_delete') + '?selection=%d'
+ % Alias.objects.get(address='alias2', domain__name='test.com').id,
{}
)
self._check_limit('mailbox_aliases', 1, 2)
@@ -206,10 +209,14 @@ class ResellerTestCase(ResourceTestCase):
self._check_limit('domain_admins', 1, 2)
self._create_account('admin2@domain.tld', role='DomainAdmins')
self._check_limit('domain_admins', 2, 2)
- resp = self._create_account('admin3@domain.tld', role='DomainAdmins', status=400)
+ resp = self._create_account(
+ 'admin3@domain.tld',
+ role='DomainAdmins',
+ status=400)
self.assertEqual(
resp['form_errors']['role'][0],
- 'Select a valid choice. DomainAdmins is not one of the available choices.'
+ 'Select a valid choice. DomainAdmins is not one of the available '
+ 'choices.'
)
self._check_limit('domain_admins', 2, 2)
@@ -227,7 +234,8 @@ class ResellerTestCase(ResourceTestCase):
)
self.assertEqual(
resp['form_errors']['role'][0],
- 'Select a valid choice. DomainAdmins is not one of the available choices.'
+ 'Select a valid choice. DomainAdmins is not one of the available '
+ 'choices.'
)
self._check_limit('domain_admins', 2, 2)
diff --git a/modoboa/extensions/limits/urls.py b/modoboa/extensions/limits/urls.py
index fa7b22416..9adc5e04a 100644
--- a/modoboa/extensions/limits/urls.py
+++ b/modoboa/extensions/limits/urls.py
@@ -3,4 +3,4 @@ from django.conf.urls import patterns
urlpatterns = patterns(
'modoboa.extensions.limits.views',
- )
+)
diff --git a/modoboa/extensions/postfix_autoreply/__init__.py b/modoboa/extensions/postfix_autoreply/__init__.py
index 2dee81f87..10578ca40 100644
--- a/modoboa/extensions/postfix_autoreply/__init__.py
+++ b/modoboa/extensions/postfix_autoreply/__init__.py
@@ -7,23 +7,27 @@ functionality into Postfix.
"""
import sys
+
from django.utils.translation import ugettext_lazy
-from modoboa.lib import events, parameters
+
from modoboa.core.extensions import ModoExtension, exts_pool
from modoboa.extensions.postfix_autoreply.models import (
Transport, Alias
)
+from modoboa.lib import events, parameters
class PostfixAutoreply(ModoExtension):
+
"""
Auto-reply (vacation) functionality using Postfix.
-
+
"""
name = "postfix_autoreply"
label = "Postfix autoreply"
version = "1.0"
- description = ugettext_lazy("Auto-reply (vacation) functionality using Postfix")
+ description = ugettext_lazy(
+ "Auto-reply (vacation) functionality using Postfix")
def init(self):
from modoboa.extensions.admin.models import Domain
@@ -45,10 +49,14 @@ class PostfixAutoreply(ModoExtension):
onMailboxCreated(None, mb)
def load(self):
- from modoboa.extensions.postfix_autoreply.app_settings import ParametersForm
- parameters.register(ParametersForm, ugettext_lazy("Automatic replies"))
+ from modoboa.extensions.postfix_autoreply.app_settings import (
+ ParametersForm
+ )
+ parameters.register(
+ ParametersForm, ugettext_lazy("Automatic replies"))
from modoboa.extensions.postfix_autoreply import general_callbacks
- if 'modoboa.extensions.postfix_autoreply.general_callbacks' in sys.modules:
+ modname = "modoboa.extensions.postfix_autoreply.general_callbacks"
+ if modname in sys.modules:
reload(general_callbacks)
def destroy(self):
diff --git a/modoboa/extensions/postfix_autoreply/app_settings.py b/modoboa/extensions/postfix_autoreply/app_settings.py
index b5d74d99c..005f29029 100644
--- a/modoboa/extensions/postfix_autoreply/app_settings.py
+++ b/modoboa/extensions/postfix_autoreply/app_settings.py
@@ -1,8 +1,12 @@
# coding: utf-8
-from django.utils.translation import ugettext_lazy
+
+"""Custom settings."""
+
from django import forms
+from django.utils.translation import ugettext_lazy
+
+from modoboa.lib.form_utils import SeparatorField
from modoboa.lib.parameters import AdminParametersForm
-from modoboa.lib.formutils import SeparatorField
class ParametersForm(AdminParametersForm):
@@ -13,7 +17,9 @@ class ParametersForm(AdminParametersForm):
autoreplies_timeout = forms.IntegerField(
label=ugettext_lazy("Automatic reply timeout"),
initial=86400,
- help_text=ugettext_lazy("Timeout in seconds between two auto-replies to the same recipient"),
+ help_text=ugettext_lazy(
+ "Timeout in seconds between two auto-replies to the same recipient"
+ ),
widget=forms.TextInput(attrs={"class": "form-control"})
)
@@ -21,7 +27,8 @@ class ParametersForm(AdminParametersForm):
label=ugettext_lazy("Default subject"),
initial=ugettext_lazy("I'm off"),
help_text=ugettext_lazy(
- "Default subject used when an auto-reply message is created automatically"
+ "Default subject used when an auto-reply message is created "
+ "automatically"
),
widget=forms.TextInput(attrs={"class": "form-control"})
)
diff --git a/modoboa/extensions/postfix_autoreply/forms.py b/modoboa/extensions/postfix_autoreply/forms.py
index 0344a8069..9e159343e 100644
--- a/modoboa/extensions/postfix_autoreply/forms.py
+++ b/modoboa/extensions/postfix_autoreply/forms.py
@@ -1,11 +1,16 @@
# coding: utf-8
+
+"""Custom forms."""
+
from django import forms
-from django.utils.translation import ugettext as _, ugettext_lazy
from django.utils import timezone
+from django.utils.translation import ugettext as _, ugettext_lazy
+
from .models import ARmessage
class ARmessageForm(forms.ModelForm):
+
fromdate = forms.DateTimeField(
label=ugettext_lazy('From'),
required=False,
@@ -23,15 +28,15 @@ class ARmessageForm(forms.ModelForm):
)
)
subject = forms.CharField(
- widget=forms.TextInput(
- attrs={'class': 'form-control'}
- )
+ widget=forms.TextInput(
+ attrs={'class': 'form-control'}
+ )
)
content = forms.CharField(
- widget=forms.Textarea(
- attrs={'class': 'form-control'}
- )
+ widget=forms.Textarea(
+ attrs={'class': 'form-control'}
)
+ )
class Meta:
model = ARmessage
@@ -67,13 +72,14 @@ class ARmessageForm(forms.ModelForm):
else:
self.cleaned_data['fromdate'] = timezone.now()
if self.cleaned_data["untildate"] is not None:
- if self.cleaned_data["untildate"] < timezone.now():
+ untildate = self.cleaned_data["untildate"]
+ if untildate < timezone.now():
self._errors["untildate"] = self.error_class(
[_("This date is over")])
- del self.cleaned_data['untildate']
- elif 'fromdate' in self.cleaned_data and \
- self.cleaned_data['untildate'] < self.cleaned_data['fromdate']:
+ del self.cleaned_data["untildate"]
+ elif "fromdate" in self.cleaned_data and \
+ untildate < self.cleaned_data["fromdate"]:
self._errors["untildate"] = \
self.error_class([_("Must be greater than start date")])
- del self.cleaned_data['untildate']
+ del self.cleaned_data["untildate"]
return self.cleaned_data
diff --git a/modoboa/extensions/postfix_autoreply/general_callbacks.py b/modoboa/extensions/postfix_autoreply/general_callbacks.py
index eb1417088..caff6b845 100644
--- a/modoboa/extensions/postfix_autoreply/general_callbacks.py
+++ b/modoboa/extensions/postfix_autoreply/general_callbacks.py
@@ -1,8 +1,11 @@
+"""Event callbacks."""
+
from django.utils import timezone
from django.utils.translation import ugettext_lazy
-from modoboa.lib import events, parameters
-from modoboa.extensions.postfix_autoreply.models import Transport, Alias
+
from .models import ARmessage
+from modoboa.extensions.postfix_autoreply.models import Transport, Alias
+from modoboa.lib import events, parameters
@events.observe("ExtraUprefsJS")
@@ -45,8 +48,11 @@ def onDomainModified(domain):
return
Transport.objects.filter(domain="autoreply.%s" % domain.oldname) \
.update(domain="autoreply.%s" % domain.name)
- for al in Alias.objects.filter(full_address__contains="@%s" % domain.oldname):
- new_address = al.full_address.replace("@%s" % domain.oldname, "@%s" % domain.name)
+ for al in Alias.objects.filter(
+ full_address__contains="@%s" % domain.oldname):
+ new_address = al.full_address.replace(
+ "@%s" % domain.oldname,
+ "@%s" % domain.name)
al.full_address = new_address
al.autoreply_address = "%s@autoreply.%s" % (new_address, domain.name)
al.save()
@@ -103,7 +109,7 @@ def extra_mailform_fields(form_name, mailbox=None):
:param str form_name: form name (must be 'mailform')
:param Mailbox mailbox: mailbox
"""
- from modoboa.lib.formutils import YesNoField
+ from modoboa.lib.form_utils import YesNoField
if form_name != "mailform":
return []
diff --git a/modoboa/extensions/postfix_autoreply/management/commands/autoreply.py b/modoboa/extensions/postfix_autoreply/management/commands/autoreply.py
index 00e9c20c4..82f0c3064 100644
--- a/modoboa/extensions/postfix_autoreply/management/commands/autoreply.py
+++ b/modoboa/extensions/postfix_autoreply/management/commands/autoreply.py
@@ -8,7 +8,7 @@ from django.utils import timezone
from modoboa.core.management.commands import CloseConnectionMixin
from modoboa.lib import parameters
-from modoboa.lib.emailutils import split_mailbox, sendmail_simple
+from modoboa.lib.email_utils import split_mailbox, sendmail_simple
from modoboa.extensions.admin.models import Mailbox
from modoboa.extensions.postfix_autoreply import PostfixAutoreply
from modoboa.extensions.postfix_autoreply.models import ARmessage, ARhistoric
@@ -18,7 +18,7 @@ def send_autoreply(sender, mailbox, armessage):
if armessage.fromdate > timezone.now():
return
if armessage.untildate is not None \
- and armessage.untildate < timezone.now():
+ and armessage.untildate < timezone.now():
armessage.enabled = False
armessage.save()
return
@@ -51,7 +51,8 @@ class Command(BaseCommand, CloseConnectionMixin):
def handle(self, *args, **options):
if len(args) < 2:
- raise CommandError("usage: ./manage.py autoreply ")
+ raise CommandError(
+ "usage: ./manage.py autoreply ")
sender = args[0]
for fulladdress in args[1:]:
diff --git a/modoboa/extensions/postfix_autoreply/migrations/0002_auto__add_armessage__add_arhistoric.py b/modoboa/extensions/postfix_autoreply/migrations/0002_auto__add_armessage__add_arhistoric.py
index 0f816e834..408cb8087 100644
--- a/modoboa/extensions/postfix_autoreply/migrations/0002_auto__add_armessage__add_arhistoric.py
+++ b/modoboa/extensions/postfix_autoreply/migrations/0002_auto__add_armessage__add_arhistoric.py
@@ -3,7 +3,7 @@ import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
-from modoboa.lib.dbutils import db_table_exists
+from modoboa.lib.db_utils import db_table_exists
class Migration(SchemaMigration):
diff --git a/modoboa/extensions/postfix_autoreply/tests.py b/modoboa/extensions/postfix_autoreply/tests.py
index 4c65b4032..e021e2929 100644
--- a/modoboa/extensions/postfix_autoreply/tests.py
+++ b/modoboa/extensions/postfix_autoreply/tests.py
@@ -82,7 +82,6 @@ class EventsTestCase(ExtTestCase):
ARmessage.objects.get(
mbox__address='user', mbox__domain__name='test.com')
-
def test_modify_mailbox_event(self):
values = {
'username': "leon@test.com",
diff --git a/modoboa/extensions/postfix_autoreply/views.py b/modoboa/extensions/postfix_autoreply/views.py
index 0351a8c18..cbf7befe0 100644
--- a/modoboa/extensions/postfix_autoreply/views.py
+++ b/modoboa/extensions/postfix_autoreply/views.py
@@ -2,7 +2,7 @@
from datetime import date
from django.contrib.auth.decorators import login_required
from django.utils.translation import ugettext as _
-from modoboa.lib.webutils import (
+from modoboa.lib.web_utils import (
render_to_json_response, _render_to_string
)
from modoboa.extensions.admin.lib import needs_mailbox
diff --git a/modoboa/extensions/postfix_relay_domains/__init__.py b/modoboa/extensions/postfix_relay_domains/__init__.py
index d3451781e..4c4ceac6b 100644
--- a/modoboa/extensions/postfix_relay_domains/__init__.py
+++ b/modoboa/extensions/postfix_relay_domains/__init__.py
@@ -1,13 +1,16 @@
# coding: utf-8
import sys
+
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy
-from modoboa.lib import events, parameters
-from modoboa.core.extensions import ModoExtension, exts_pool
-from .models import RelayDomain, RelayDomainAlias, Service
-extension_events = [
+from .models import RelayDomain, RelayDomainAlias, Service
+from modoboa.core.extensions import ModoExtension, exts_pool
+from modoboa.lib import events, parameters
+
+
+EXTENSION_EVENTS = [
"RelayDomainCreated",
"RelayDomainDeleted",
"RelayDomainModified",
@@ -66,6 +69,9 @@ def init_amavis_dependant_features():
class PostfixRelayDomains(ModoExtension):
+
+ """Extension declaration."""
+
name = "postfix_relay_domains"
label = "Postfix relay domains"
version = "1.0"
@@ -88,17 +94,18 @@ class PostfixRelayDomains(ModoExtension):
parameters.register(
AdminParametersForm, ugettext_lazy("Relay domains")
)
- events.declare(extension_events)
+ events.declare(EXTENSION_EVENTS)
from modoboa.extensions.postfix_relay_domains import general_callbacks
- if 'modoboa.extensions.postfix_relay_domains.general_callbacks' in sys.modules:
+ prefix = "modoboa.extensions.postfix_relay_domains."
+ if prefix + "general_callbacks" in sys.modules:
reload(general_callbacks)
if exts_pool.is_extension_enabled('limits'):
import limits_callbacks
- if 'modoboa.extensions.postfix_relay_domains.limits_callbacks' in sys.modules:
+ if prefix + "limits_callbacks" in sys.modules:
reload(limits_callbacks)
if exts_pool.is_extension_enabled('amavis'):
import amavis_callbacks
- if 'modoboa.extensions.postfix_relay_domains.amavis_callbacks' in sys.modules:
+ if prefix + "amavis_callbacks" in sys.modules:
reload(amavis_callbacks)
def destroy(self):
diff --git a/modoboa/extensions/postfix_relay_domains/app_settings.py b/modoboa/extensions/postfix_relay_domains/app_settings.py
index 107a16979..80cfc6a4d 100644
--- a/modoboa/extensions/postfix_relay_domains/app_settings.py
+++ b/modoboa/extensions/postfix_relay_domains/app_settings.py
@@ -6,7 +6,7 @@ from django import forms
from django.utils.translation import ugettext_lazy
from modoboa.lib import parameters
-from modoboa.lib.formutils import SeparatorField
+from modoboa.lib.form_utils import SeparatorField
class AdminParametersForm(parameters.AdminParametersForm):
diff --git a/modoboa/extensions/postfix_relay_domains/forms.py b/modoboa/extensions/postfix_relay_domains/forms.py
index 001992539..0fcd2535f 100644
--- a/modoboa/extensions/postfix_relay_domains/forms.py
+++ b/modoboa/extensions/postfix_relay_domains/forms.py
@@ -2,14 +2,15 @@
Postfix relay domains extension forms.
"""
from django import forms
+from django.core.urlresolvers import reverse
from django.http import QueryDict
from django.utils.translation import ugettext as _, ugettext_lazy
-from django.core.urlresolvers import reverse
-from modoboa.lib import events
-from modoboa.lib.formutils import DynamicForm, DomainNameField, TabForms
-from modoboa.lib.webutils import render_to_json_response
-from modoboa.extensions.admin.models import Domain, DomainAlias
+
from .models import RelayDomain, RelayDomainAlias
+from modoboa.extensions.admin.models import Domain, DomainAlias
+from modoboa.lib import events
+from modoboa.lib.form_utils import DynamicForm, DomainNameField, TabForms
+from modoboa.lib.web_utils import render_to_json_response
class RelayDomainFormGeneral(forms.ModelForm, DynamicForm):
@@ -113,7 +114,7 @@ class RelayDomainFormGeneral(forms.ModelForm, DynamicForm):
continue
aliases.append(v)
for rdalias in rd.relaydomainalias_set.all():
- if not rdalias.name in aliases:
+ if rdalias.name not in aliases:
rdalias.delete()
else:
aliases.remove(rdalias.name)
@@ -137,11 +138,13 @@ class RelayDomainFormGeneral(forms.ModelForm, DynamicForm):
class RelayDomainForm(TabForms):
+
"""Specific edition form for relay domains.
We use a *tabs* compatible form because extensions can add their
own tab. (ex: amavis)
"""
+
def __init__(self, request, *args, **kwargs):
self.user = request.user
self.forms = []
diff --git a/modoboa/extensions/postfix_relay_domains/general_callbacks.py b/modoboa/extensions/postfix_relay_domains/general_callbacks.py
index 43b25312d..765b32fcf 100644
--- a/modoboa/extensions/postfix_relay_domains/general_callbacks.py
+++ b/modoboa/extensions/postfix_relay_domains/general_callbacks.py
@@ -1,10 +1,13 @@
-from django.core.urlresolvers import reverse
-from django.template import Template, Context
+"""General event callbacks."""
+
from django.conf import settings
-from django.utils.translation import ugettext_lazy
+from django.core.urlresolvers import reverse
from django.db.models import Q
-from modoboa.lib import events
+from django.template import Template, Context
+from django.utils.translation import ugettext_lazy
+
from .models import RelayDomain, RelayDomainAlias
+from modoboa.lib import events
@events.observe('GetStaticContent')
@@ -101,7 +104,8 @@ def extra_limit_templates():
ugettext_lazy('Maximum number of relay domains this user can create'),
'Resellers'),
("relay_domain_aliases_limit", ugettext_lazy("Relay domain aliases"),
- ugettext_lazy('Maximum number of relay domain aliases this user can create'),
+ ugettext_lazy(
+ 'Maximum number of relay domain aliases this user can create'),
'Resellers'),
]
diff --git a/modoboa/extensions/postfix_relay_domains/limits_callbacks.py b/modoboa/extensions/postfix_relay_domains/limits_callbacks.py
index 18911e159..8b1ce5d81 100644
--- a/modoboa/extensions/postfix_relay_domains/limits_callbacks.py
+++ b/modoboa/extensions/postfix_relay_domains/limits_callbacks.py
@@ -1,8 +1,11 @@
+"""Callbacks related to limits extension."""
+
from django.utils.translation import ugettext_lazy
+
+from .models import RelayDomainAlias
+from modoboa.extensions.limits.lib import inc_limit_usage, dec_limit_usage
from modoboa.lib import events
from modoboa.lib.permissions import get_object_owner
-from modoboa.extensions.limits.lib import inc_limit_usage, dec_limit_usage
-from .models import RelayDomainAlias
@events.observe('GetExtraParameters')
@@ -16,17 +19,23 @@ def extra_parameters(app, level):
label=ugettext_lazy("Relay domains"),
initial=0,
help_text=ugettext_lazy(
- "Maximum number of allowed relay domains for a new administrator"
+ "Maximum number of allowed relay domains for a new "
+ "administrator"
),
- widget=forms.widgets.TextInput(attrs={"class": "col-md-1 form-control"})
+ widget=forms.widgets.TextInput(
+ attrs={
+ "class": "col-md-1 form-control"})
),
'deflt_relay_domain_aliases_limit': forms.IntegerField(
label=ugettext_lazy("Relay domain aliases"),
initial=0,
help_text=ugettext_lazy(
- "Maximum number of allowed relay domain aliases for a new administrator"
+ "Maximum number of allowed relay domain aliases for a new "
+ "administrator"
),
- widget=forms.widgets.TextInput(attrs={"class": "col-md-1 form-control"})
+ widget=forms.widgets.TextInput(
+ attrs={
+ "class": "col-md-1 form-control"})
)
}
diff --git a/modoboa/extensions/postfix_relay_domains/models.py b/modoboa/extensions/postfix_relay_domains/models.py
index 088874c2e..5e96959ac 100644
--- a/modoboa/extensions/postfix_relay_domains/models.py
+++ b/modoboa/extensions/postfix_relay_domains/models.py
@@ -1,12 +1,16 @@
-import reversion
+"""Relay domain related models."""
+
+from django.contrib.contenttypes import generic
from django.db import models
from django.db.models.manager import Manager
-from django.contrib.contenttypes import generic
from django.utils.translation import ugettext as _, ugettext_lazy
+
+import reversion
+
from modoboa.core.models import ObjectAccess
+from modoboa.extensions.admin.models import AdminObject
from modoboa.lib import parameters
from modoboa.lib.exceptions import BadRequest, NotFound
-from modoboa.extensions.admin.models import AdminObject
class RelayDomainManager(Manager):
@@ -46,12 +50,13 @@ class ServiceManager(Manager):
services.append(parts[0])
to_delete = []
for service in self.all():
- if not service.name in services:
+ if service.name not in services:
to_delete.append(service.name)
Service.objects.filter(name__in=to_delete).delete()
class Service(models.Model):
+
"""Postfix service.
"""
name = models.CharField(
@@ -66,6 +71,7 @@ class Service(models.Model):
class RelayDomain(AdminObject):
+
"""Relay domain.
A relay domain differs from a usual domaine because its final
@@ -101,7 +107,7 @@ class RelayDomain(AdminObject):
def tags(self):
return [
{"name": "relaydomain", "label": _("Relay Domain"), "type": "dom"},
- {"name": self.service.name, "label": "%s:" % self.service.name,
+ {"name": self.service.name, "label": "%s:" % self.service.name,
"type": "srv", "color": "info"}
]
@@ -118,7 +124,7 @@ class RelayDomain(AdminObject):
:param csvwriter:
"""
csvwriter.writerow(
- ["relaydomain", self.name, self.target_host,
+ ["relaydomain", self.name, self.target_host,
self.service.name, self.enabled, self.verify_recipients]
)
for rdalias in self.relaydomainalias_set.all():
@@ -134,7 +140,8 @@ class RelayDomain(AdminObject):
raise BadRequest(_("Invalid line"))
self.name = row[1].strip()
self.target_host = row[2].strip()
- self.service, created = Service.objects.get_or_create(name=row[3].strip())
+ self.service, created = Service.objects.get_or_create(
+ name=row[3].strip())
self.enabled = (row[4].strip() == 'True')
self.verify_recipients = (row[5].strip() == 'True')
self.save(creator=user)
@@ -152,6 +159,7 @@ reversion.register(RelayDomain)
class RelayDomainAlias(AdminObject):
+
"""Relay domain alias.
"""
diff --git a/modoboa/extensions/postfix_relay_domains/views.py b/modoboa/extensions/postfix_relay_domains/views.py
index 987efa4da..e95ec849b 100644
--- a/modoboa/extensions/postfix_relay_domains/views.py
+++ b/modoboa/extensions/postfix_relay_domains/views.py
@@ -9,7 +9,7 @@ from django.contrib.auth.decorators import (
)
from modoboa.lib import events
from modoboa.lib.exceptions import PermDeniedException
-from modoboa.lib.webutils import render_to_json_response
+from modoboa.lib.web_utils import render_to_json_response
from .models import RelayDomain, Service
from .forms import RelayDomainForm, RelayDomainFormGeneral
diff --git a/modoboa/extensions/radicale/__init__.py b/modoboa/extensions/radicale/__init__.py
index 5e28f04a5..d7d884cf3 100644
--- a/modoboa/extensions/radicale/__init__.py
+++ b/modoboa/extensions/radicale/__init__.py
@@ -3,9 +3,11 @@ Radicale management frontend.
"""
import sys
+
from django.contrib.auth.models import Permission, Group
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy
+
from modoboa.lib import events, parameters
from modoboa.lib.permissions import add_permissions_to_group
from modoboa.core.extensions import ModoExtension, exts_pool
@@ -28,14 +30,16 @@ def init_limits_dependant_features():
class Radicale(ModoExtension):
- """
- """
+
+ """Radicale extension declaration."""
+
name = "radicale"
label = ugettext_lazy("Radicale management")
version = "1.0"
url = "calendars"
description = ugettext_lazy(
- "Management frontend for Radicale, a simple calendar and contact server."
+ "Management frontend for Radicale, a simple calendar and contact "
+ "server."
)
def init(self):
diff --git a/modoboa/extensions/radicale/app_settings.py b/modoboa/extensions/radicale/app_settings.py
index 2e7991f2a..80a6f10f8 100644
--- a/modoboa/extensions/radicale/app_settings.py
+++ b/modoboa/extensions/radicale/app_settings.py
@@ -1,7 +1,7 @@
from django.utils.translation import ugettext_lazy
from django import forms
from modoboa.lib.parameters import AdminParametersForm
-from modoboa.lib.formutils import SeparatorField, YesNoField
+from modoboa.lib.form_utils import SeparatorField, YesNoField
class ParametersForm(AdminParametersForm):
diff --git a/modoboa/extensions/radicale/forms.py b/modoboa/extensions/radicale/forms.py
index 4e84979a1..14472ded1 100644
--- a/modoboa/extensions/radicale/forms.py
+++ b/modoboa/extensions/radicale/forms.py
@@ -1,13 +1,15 @@
"""
Radicale extension forms.
"""
+
from django import forms
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _, ugettext_lazy
-from modoboa.lib.webutils import render_to_json_response
-from modoboa.lib.formutils import WizardForm, TabForms, DynamicForm
+
from modoboa.extensions.admin.models import Domain, Mailbox
from modoboa.extensions.radicale.models import UserCalendar, SharedCalendar
+from modoboa.lib.form_utils import WizardForm, TabForms, DynamicForm
+from modoboa.lib.web_utils import render_to_json_response
class UserCalendarForm(forms.ModelForm):
@@ -109,7 +111,7 @@ class RightsForm(forms.Form, DynamicForm):
def save(self):
"""Custom save method."""
import re
- from modoboa.lib.emailutils import split_mailbox
+ from modoboa.lib.email_utils import split_mailbox
from modoboa.lib.exceptions import BadRequest
from modoboa.extensions.admin.models import Mailbox
from .models import AccessRule
@@ -122,7 +124,7 @@ class RightsForm(forms.Form, DynamicForm):
pos = int(res.group(1)) if res else None
usernames[value] = pos
for rule in self.calendar.rules.select_related().all():
- if not rule.mailbox.full_address in usernames:
+ if rule.mailbox.full_address not in usernames:
rule.delete()
for username, pos in usernames.iteritems():
local_part, domname = split_mailbox(username)
diff --git a/modoboa/extensions/radicale/templatetags/radicale_tags.py b/modoboa/extensions/radicale/templatetags/radicale_tags.py
index e655be253..83bc07994 100644
--- a/modoboa/extensions/radicale/templatetags/radicale_tags.py
+++ b/modoboa/extensions/radicale/templatetags/radicale_tags.py
@@ -2,15 +2,17 @@
Custom template tags.
"""
from django import template
-from django.template.loader import render_to_string
from django.core.urlresolvers import reverse
+from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
-from modoboa.lib.webutils import render_actions
+
from modoboa.lib.templatetags.lib_tags import render_link
+from modoboa.lib.web_utils import render_actions
register = template.Library()
+
@register.simple_tag
def radicale_left_menu(user):
"""Menu located inside the left column.
@@ -96,7 +98,7 @@ def render_rule_fields(form):
result = ""
while True:
fname = "username_%d" % cpt
- if not fname in form.fields:
+ if fname not in form.fields:
break
rfieldname = "read_access_%d" % cpt
wfieldname = "write_access_%d" % cpt
diff --git a/modoboa/extensions/radicale/views.py b/modoboa/extensions/radicale/views.py
index 281e7a719..619c41dde 100644
--- a/modoboa/extensions/radicale/views.py
+++ b/modoboa/extensions/radicale/views.py
@@ -11,7 +11,7 @@ from django.contrib.auth.decorators import (
from django.utils.translation import ugettext as _
from modoboa.lib.listing import get_sort_order, get_listing_page
-from modoboa.lib.webutils import (
+from modoboa.lib.web_utils import (
_render_to_string, render_to_json_response
)
from modoboa.lib.exceptions import NotFound, PermDeniedException
diff --git a/modoboa/extensions/sievefilters/app_settings.py b/modoboa/extensions/sievefilters/app_settings.py
index 0d6547559..c3d1cbb4d 100644
--- a/modoboa/extensions/sievefilters/app_settings.py
+++ b/modoboa/extensions/sievefilters/app_settings.py
@@ -3,7 +3,7 @@ from django.utils.translation import ugettext_lazy as _
from django import forms
from sievelib.managesieve import SUPPORTED_AUTH_MECHS
from modoboa.lib.parameters import AdminParametersForm, UserParametersForm
-from modoboa.lib.formutils import SeparatorField, YesNoField, InlineRadioSelect
+from modoboa.lib.form_utils import SeparatorField, YesNoField, InlineRadioSelect
def supported_auth_mechs():
diff --git a/modoboa/extensions/sievefilters/forms.py b/modoboa/extensions/sievefilters/forms.py
index 3db5cf368..01e91e98c 100644
--- a/modoboa/extensions/sievefilters/forms.py
+++ b/modoboa/extensions/sievefilters/forms.py
@@ -1,10 +1,13 @@
+"""Custom forms."""
+
from django import forms
-from django.http import QueryDict
-from django.utils.translation import ugettext as _, ugettext_lazy
from django.forms.widgets import RadioFieldRenderer, RadioSelect, RadioInput
-from django.utils.safestring import mark_safe
-from django.utils.html import conditional_escape
+from django.http import QueryDict
from django.utils.encoding import force_unicode
+from django.utils.html import conditional_escape
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext as _, ugettext_lazy
+
from modoboa.extensions.admin.templatetags.admin_tags import gender
@@ -20,6 +23,7 @@ class FiltersSetForm(forms.Form):
class CustomRadioInput(RadioInput):
+
def __unicode__(self):
return self.render()
@@ -39,13 +43,16 @@ class CustomRadioInput(RadioInput):
class CustomRadioFieldRenderer(RadioFieldRenderer):
+
def __iter__(self):
for i, choice in enumerate(self.choices):
- yield CustomRadioInput(self.name, self.value, self.attrs.copy(), choice, i)
+ yield CustomRadioInput(
+ self.name, self.value, self.attrs.copy(), choice, i)
def __getitem__(self, idx):
choice = self.choices[idx]
- return CustomRadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
+ return CustomRadioInput(
+ self.name, self.value, self.attrs.copy(), choice, idx)
def render(self):
return mark_safe(u'\n'.join([force_unicode(w) for w in self]))
@@ -56,6 +63,7 @@ class CustomRadioSelect(RadioSelect):
class FilterForm(forms.Form):
+
def __init__(self, conditions, actions, request, *args, **kwargs):
super(FilterForm, self).__init__(*args, **kwargs)
@@ -80,10 +88,18 @@ class FilterForm(forms.Form):
]
self.cond_templates = [
- {"name": "Subject", "label": _("Subject"), "operators": self.header_operators},
- {"name": "From", "label": _("Sender"), "operators": self.header_operators},
- {"name": "To", "label": _("Recipient"), "operators": self.header_operators},
- {"name": "Cc", "label": _("Cc"), "operators": self.header_operators},
+ {"name": "Subject",
+ "label": _("Subject"),
+ "operators": self.header_operators},
+ {"name": "From",
+ "label": _("Sender"),
+ "operators": self.header_operators},
+ {"name": "To",
+ "label": _("Recipient"),
+ "operators": self.header_operators},
+ {"name": "Cc",
+ "label": _("Cc"),
+ "operators": self.header_operators},
{"name": "size", "label": _("Size"),
"operators": [("over", _("is greater than"), "number"),
("under", _("is less than"), "number")]},
@@ -160,10 +176,14 @@ class FilterForm(forms.Form):
arg = args[cnt]
aname = "action_arg_%d_%d" % (self.actions_cnt, cnt)
if arg["type"] == "string":
- self.fields[aname] = forms.CharField(max_length=255, initial=value)
+ self.fields[aname] = forms.CharField(
+ max_length=255,
+ initial=value)
elif arg["type"] == "list":
choices = getattr(self, arg["vloader"])(request)
- self.fields[aname] = forms.ChoiceField(initial=value, choices=choices)
+ self.fields[aname] = forms.ChoiceField(
+ initial=value,
+ choices=choices)
self.actions_cnt += 1
def _build_redirect_field(self, request, value):
@@ -177,7 +197,10 @@ class FilterForm(forms.Form):
for fd in folders:
value = fd["path"] if "path" in fd else fd["name"]
if parentmb:
- ret += [(value, fd["name"].replace("%s%s" % (parentmb, imapc.hdelimiter), ""))]
+ ret += [
+ (value, fd["name"].replace(
+ "%s%s" % (parentmb, imapc.hdelimiter), ""))
+ ]
else:
ret += [(value, fd["name"])]
if "sub" in fd:
@@ -206,7 +229,10 @@ class FilterForm(forms.Form):
argcpt = 0
while True:
try:
- naction += (self.cleaned_data["action_arg_%d_%d" % (cpt, argcpt)],)
+ naction += (
+ self.cleaned_data[
+ "action_arg_%d_%d" %
+ (cpt, argcpt)],)
except KeyError:
break
argcpt += 1
@@ -228,8 +254,12 @@ def build_filter_form_from_qdict(request):
if cpt == int(request.POST["conds"]):
break
if "cond_target_%d" % i in request.POST:
- qdict["cond_target_%d" % cpt] = request.POST["cond_target_%d" % i]
- qdict["cond_operator_%d" % cpt] = request.POST["cond_operator_%d" % i]
+ qdict["cond_target_%d" % cpt] = (
+ request.POST["cond_target_%d" % i]
+ )
+ qdict["cond_operator_%d" % cpt] = (
+ request.POST["cond_operator_%d" % i]
+ )
qdict["cond_value_%d" % cpt] = request.POST["cond_value_%d" % i]
condtarget = request.POST["cond_target_%d" % i]
condop = request.POST["cond_operator_%d" % i]
diff --git a/modoboa/extensions/sievefilters/lib.py b/modoboa/extensions/sievefilters/lib.py
index b980bc1d4..26cf8a2d1 100644
--- a/modoboa/extensions/sievefilters/lib.py
+++ b/modoboa/extensions/sievefilters/lib.py
@@ -1,12 +1,16 @@
# coding: utf-8
-from django.utils.translation import ugettext as _
+"""Internal tools."""
+
+from sievelib.factory import FiltersSet
from sievelib.managesieve import Client, Error
from sievelib.parser import Parser
-from sievelib.factory import FiltersSet
+
+from django.utils.translation import ugettext as _
+
from modoboa.lib import parameters
-from modoboa.lib.exceptions import ModoboaException
from modoboa.lib.connections import ConnectionsManager, ConnectionError
+from modoboa.lib.exceptions import ModoboaException
class SieveClientError(ModoboaException):
@@ -19,7 +23,7 @@ class SieveClient(object):
def __init__(self, user=None, password=None):
try:
ret, msg = self.login(user, password)
- except Error, e:
+ except Error as e:
raise ConnectionError(str(e))
if not ret:
raise ConnectionError(msg)
@@ -38,7 +42,10 @@ class SieveClient(object):
except Error:
ret = False
if not ret:
- return False, _("Connection to MANAGESIEVE server failed, check your configuration")
+ return False, _(
+ "Connection to MANAGESIEVE server failed, check your "
+ "configuration"
+ )
return True, None
def logout(self):
@@ -51,13 +58,13 @@ class SieveClient(object):
if self.msc is not None:
try:
self.msc.capability()
- except Error, e:
+ except Error as e:
pass
else:
return
try:
ret, msg = self.login(user, password)
- except (Error, ssl.SSLError), e:
+ except (Error, ssl.SSLError) as e:
raise ConnectionError(e)
if not ret:
raise ConnectionError(msg)
@@ -80,10 +87,11 @@ class SieveClient(object):
return fs
def pushscript(self, name, content, active=False):
- if type(content) is unicode:
+ if isinstance(content, unicode):
content = content.encode("utf-8")
if not self.msc.havespace(name, len(content)):
- error = "%s (%s)" % (_("Not enough space on server"), self.msc.errmsg)
+ error = "%s (%s)" % (
+ _("Not enough space on server"), self.msc.errmsg)
raise SieveClientError(error)
if not self.msc.putscript(name, content):
raise SieveClientError(self.msc.errmsg)
diff --git a/modoboa/extensions/sievefilters/templatetags/sfilters_tags.py b/modoboa/extensions/sievefilters/templatetags/sfilters_tags.py
index 0f977fee7..3bc0f937f 100644
--- a/modoboa/extensions/sievefilters/templatetags/sfilters_tags.py
+++ b/modoboa/extensions/sievefilters/templatetags/sfilters_tags.py
@@ -3,7 +3,7 @@ from django.core.urlresolvers import reverse
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _
-from modoboa.lib.webutils import render_actions
+from modoboa.lib.web_utils import render_actions
from modoboa.lib.templatetags.form_tags import configure_field_classes
register = template.Library()
@@ -34,7 +34,7 @@ def fset_menu(mode, setname):
"img": "fa fa-plus",
"label": _("New filter"),
"url": reverse("sievefilters:filter_add",
- args=[setname]),
+ args=[setname]),
"modal": True,
"autowidth": True,
"modalcb": "filterform_cb"}
@@ -45,7 +45,7 @@ def fset_menu(mode, setname):
"img": "fa fa-download-alt",
"label": _("Save filters set"),
"url": reverse("sievefilters:fs_save",
- args=[setname])},
+ args=[setname])},
]
entries += [
@@ -53,7 +53,7 @@ def fset_menu(mode, setname):
"img": "fa fa-check",
"label": _("Activate filters set"),
"url": reverse("sievefilters:fs_activate",
- args=[setname])},
+ args=[setname])},
{"name": "removefs",
"img": "fa fa-trash",
"label": _("Remove filters set"),
diff --git a/modoboa/extensions/sievefilters/test.py b/modoboa/extensions/sievefilters/test.py
index 2555f22bc..9d35832dc 100644
--- a/modoboa/extensions/sievefilters/test.py
+++ b/modoboa/extensions/sievefilters/test.py
@@ -2,7 +2,7 @@ if __name__ == "__main__":
clt = ManageSieveClient("mail.koalabs.org", 2000)
if clt.connect("tonio@ngyn.org", "aka/12;c", starttls=True):
print clt.get_sasl_mechanisms()
- print clt.get_sieve_capabilities()
+ print clt.get_sieve_capabilities()
print clt.havespace("test", 45)
if not clt.putscript("myscript", """require ["fileinto", "envelope"];
@@ -40,4 +40,3 @@ InvalidSieveCommand
else:
print clt.errmsg
clt.logout()
-
diff --git a/modoboa/extensions/sievefilters/views.py b/modoboa/extensions/sievefilters/views.py
index ae5783db8..2a97b479b 100644
--- a/modoboa/extensions/sievefilters/views.py
+++ b/modoboa/extensions/sievefilters/views.py
@@ -1,25 +1,31 @@
# coding: utf-8
-from sievelib.managesieve import Error
-from sievelib.commands import BadArgument, BadValue
+
+"""Custom views."""
+
from rfc6266 import build_header
-from django.shortcuts import render
-from django.http import HttpResponse
-from django.template.loader import render_to_string
+from sievelib.commands import BadArgument, BadValue
+from sievelib.managesieve import Error
+
from django.contrib.auth.decorators import login_required
-from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
-from modoboa.lib import parameters
-from modoboa.lib.webutils import _render_error, \
- ajax_response, render_to_json_response
-from modoboa.lib.connections import ConnectionError
-from modoboa.lib.exceptions import BadRequest
-from modoboa.extensions.admin.lib import needs_mailbox
-from .lib import SieveClient, SieveClientError
+from django.http import HttpResponse
+from django.shortcuts import render
+from django.template.loader import render_to_string
+from django.utils.translation import ugettext as _
+
from .forms import (
FilterForm, build_filter_form_from_qdict, build_filter_form_from_filter,
FiltersSetForm
)
+from .lib import SieveClient, SieveClientError
from .templatetags.sfilters_tags import fset_menu
+from modoboa.extensions.admin.lib import needs_mailbox
+from modoboa.lib import parameters
+from modoboa.lib.connections import ConnectionError
+from modoboa.lib.exceptions import BadRequest
+from modoboa.lib.web_utils import (
+ _render_error, ajax_response, render_to_json_response
+)
@login_required
@@ -30,12 +36,12 @@ def index(request, tplname="sievefilters/index.html"):
try:
sc = SieveClient(user=request.user.username,
password=request.session["password"])
- except ConnectionError, e:
+ except ConnectionError as e:
return _render_error(request, user_context={"error": e})
try:
active_script, scripts = sc.listscripts()
- except Error, e:
+ except Error as e:
return _render_error(request, user_context={"error": e})
if active_script is None:
@@ -73,7 +79,7 @@ def getfs(request, name):
error = None
try:
content = sc.getscript(name, format=editormode)
- except SieveClientError, e:
+ except SieveClientError as e:
error = str(e)
else:
if content is None:
@@ -104,7 +110,8 @@ def build_filter_ctx(ctx, form):
return ctx
-def submitfilter(request, setname, okmsg, tplname, tplctx, update=False, sc=None):
+def submitfilter(
+ request, setname, okmsg, tplname, tplctx, update=False, sc=None):
form = build_filter_form_from_qdict(request)
if form.is_valid():
if sc is None:
@@ -169,11 +176,13 @@ def editfilter(request, setname, fname, tplname="sievefilters/filter.html"):
sc = SieveClient(user=request.user.username,
password=request.session["password"])
if request.method == "POST":
- return submitfilter(request, setname, _("Filter modified"), tplname, ctx,
- update=True, sc=sc)
+ return submitfilter(
+ request, setname, _("Filter modified"), tplname, ctx,
+ update=True, sc=sc
+ )
fset = sc.getscript(setname, format="fset")
- if type(fname) is unicode:
+ if isinstance(fname, unicode):
fname = fname.encode("utf-8")
f = fset.getfilter(fname)
form = build_filter_form_from_filter(request, fname, f)
@@ -199,13 +208,13 @@ def removefilter(request, setname, fname):
@login_required
@needs_mailbox()
def savefs(request, name):
- if not "scriptcontent" in request.POST:
+ if "scriptcontent" not in request.POST:
return
sc = SieveClient(user=request.user.username,
password=request.session["password"])
try:
sc.pushscript(name, request.POST["scriptcontent"])
- except SieveClientError, e:
+ except SieveClientError as e:
error = str(e)
return ajax_response(request, "ko", respmsg=error)
return ajax_response(request, respmsg=_("Filters set saved"))
@@ -258,7 +267,7 @@ def activate_filters_set(request, name):
password=request.session["password"])
try:
sc.activatescript(name)
- except SieveClientError, e:
+ except SieveClientError as e:
return ajax_response(request, "ko", respmsg=str(e))
return ajax_response(request, respmsg=_("Filters set activated"))
@@ -270,7 +279,7 @@ def download_filters_set(request, name):
password=request.session["password"])
try:
script = sc.getscript(name)
- except SieveClientError, e:
+ except SieveClientError as e:
return ajax_response(request, "ko", respmsg=str(e))
resp = HttpResponse(script)
@@ -285,7 +294,7 @@ def download_filters_set(request, name):
def toggle_filter_state(request, setname, fname):
sc = SieveClient(user=request.user.username,
password=request.session["password"])
- if type(fname) is unicode:
+ if isinstance(fname, unicode):
fname = fname.encode("utf-8")
fset = sc.getscript(setname, format="fset")
if fset.is_filter_disabled(fname):
diff --git a/modoboa/extensions/stats/app_settings.py b/modoboa/extensions/stats/app_settings.py
index 84afb8f3b..9c48d156e 100644
--- a/modoboa/extensions/stats/app_settings.py
+++ b/modoboa/extensions/stats/app_settings.py
@@ -2,7 +2,7 @@
from django.utils.translation import ugettext_lazy
from django import forms
from modoboa.lib.parameters import AdminParametersForm
-from modoboa.lib.formutils import SeparatorField
+from modoboa.lib.form_utils import SeparatorField
class ParametersForm(AdminParametersForm):
diff --git a/modoboa/extensions/stats/graphics.py b/modoboa/extensions/stats/graphics.py
index d7f657ba9..80ca0af88 100644
--- a/modoboa/extensions/stats/graphics.py
+++ b/modoboa/extensions/stats/graphics.py
@@ -7,10 +7,12 @@ from modoboa.lib.sysutils import exec_cmd
class Curve(object):
+
"""Graphic curve.
Simple way to represent a graphic curve.
"""
+
def __init__(self, dsname, color, legend, cfunc="AVERAGE"):
"""Constructor.
"""
@@ -37,9 +39,11 @@ class Curve(object):
class Graphic(object):
+
"""Graphic.
"""
+
def __init__(self):
"""Constructor.
"""
@@ -76,7 +80,7 @@ class Graphic(object):
cmdargs += curve.to_rrd_command_args(rrdfile)
cmd = "rrdtool xport --start %s --end %s " % (str(start), str(end))
cmd += " ".join(cmdargs)
- if type(cmd) is unicode:
+ if isinstance(cmd, unicode):
cmd = cmd.encode("utf-8")
code, output = exec_cmd(cmd)
if code:
@@ -95,6 +99,7 @@ class Graphic(object):
class AverageTraffic(Graphic):
+
"""Average traffic.
"""
title = ugettext_lazy('Average traffic (msgs/min)')
@@ -109,6 +114,7 @@ class AverageTraffic(Graphic):
class AverageTrafficSize(Graphic):
+
"""Average traffic size.
"""
title = ugettext_lazy('Average normal traffic size (bytes/min)')
diff --git a/modoboa/extensions/stats/lib.py b/modoboa/extensions/stats/lib.py
index c71e5b59d..9d78c039f 100644
--- a/modoboa/extensions/stats/lib.py
+++ b/modoboa/extensions/stats/lib.py
@@ -12,7 +12,7 @@ def date_to_timestamp(timetuple):
:return: an integer
"""
date = " ".join(
- [("%d" % elem) if type(elem) is int else elem for elem in timetuple]
+ [("%d" % elem) if isinstance(elem, int) else elem for elem in timetuple]
)
fmt = "%Y %m %d %H %M %S" \
if timetuple[1].isdigit() else "%Y %b %d %H %M %S"
diff --git a/modoboa/extensions/stats/management/commands/logparser.py b/modoboa/extensions/stats/management/commands/logparser.py
index c622c5e05..3613f056b 100644
--- a/modoboa/extensions/stats/management/commands/logparser.py
+++ b/modoboa/extensions/stats/management/commands/logparser.py
@@ -21,10 +21,11 @@ import time
import sys
import os
import re
-import rrdtool
import string
from optparse import make_option
+import rrdtool
+
from django.core.management.base import BaseCommand
from modoboa.core.extensions import exts_pool
@@ -71,9 +72,11 @@ class LogParser(object):
self.workdict = {}
self.lupdates = {}
self._s_date_expr = \
- re.compile(r"(?P\w+)\s+(?P\d+)\s+(?P\d+):(?P\d+):(?P\d+)(?P.*)")
+ re.compile(
+ r"(?P\w+)\s+(?P\d+)\s+(?P\d+):(?P\d+):(?P\d+)(?P.*)")
self._hp_date_expr = \
- re.compile(r"(?P\d+)-(?P\d+)-(?P\d+)T(?P\d+):(?P\d+):(?P\d+)\.\d+\+\d+:\d+(?P.*)")
+ re.compile(
+ r"(?P\d+)-(?P\d+)-(?P\d+)T(?P\d+):(?P\d+):(?P\d+)\.\d+\+\d+:\d+(?P.*)")
self.date_expr = None
self.line_expr = \
re.compile(r"\s+([-\w]+)\s+(\w+)/?\w*[[](\d+)[]]:\s+(.*)")
@@ -200,7 +203,7 @@ class LogParser(object):
self.lupdates[fname] = self.init_rrd(fname, m)
self._dprint("[rrd] create new RRD file %s" % fname)
else:
- if not fname in self.lupdates:
+ if fname not in self.lupdates:
self.lupdates[fname] = rrdtool.last(str(fname))
if m <= self.lupdates[fname]:
@@ -250,11 +253,11 @@ class LogParser(object):
def inc_counter(self, dom, counter, val=1):
if dom is not None and dom in self.domains:
- if not self.cur_t in self.data[dom]:
+ if self.cur_t not in self.data[dom]:
self.initcounters(dom)
self.data[dom][self.cur_t][counter] += val
- if not self.cur_t in self.data["global"]:
+ if self.cur_t not in self.data["global"]:
self.initcounters("global")
self.data["global"][self.cur_t][counter] += val
@@ -299,7 +302,9 @@ class LogParser(object):
return
(line_id, line_log) = m.groups()
if line_id == "NOQUEUE":
- addrto = re.match("reject: .*from=<.*> to=<[^@]+@([^>]+)>", line_log)
+ addrto = re.match(
+ "reject: .*from=<.*> to=<[^@]+@([^>]+)>",
+ line_log)
if addrto and addrto.group(1) in self.domains:
self.inc_counter(addrto.group(1), 'reject')
return
@@ -316,9 +321,9 @@ class LogParser(object):
m = re.search("to=<([^>]*)>.*status=(\S+)", line_log)
if m is not None:
- if not line_id in self.workdict:
- self._dprint("Inconsistent mail (%s: %s), skipping" \
- % (line_id, m.group(1)))
+ if line_id not in self.workdict:
+ self._dprint("Inconsistent mail (%s: %s), skipping"
+ % (line_id, m.group(1)))
return
if not m.group(2) in variables:
self._dprint("Unsupported status %s, skipping" % m.group(2))
diff --git a/modoboa/extensions/stats/views.py b/modoboa/extensions/stats/views.py
index 2e2b3b83c..b16756408 100644
--- a/modoboa/extensions/stats/views.py
+++ b/modoboa/extensions/stats/views.py
@@ -1,4 +1,7 @@
# coding: utf-8
+
+"""Custom views."""
+
import re
import time
from django.shortcuts import render
@@ -10,7 +13,7 @@ from django.contrib.auth.decorators import (
from modoboa.core.extensions import exts_pool
from modoboa.lib import events
from modoboa.lib.exceptions import BadRequest, PermDeniedException, NotFound
-from modoboa.lib.webutils import (
+from modoboa.lib.web_utils import (
render_to_json_response
)
from modoboa.extensions.admin.models import (
@@ -83,7 +86,7 @@ def check_domain_access(user, pattern):
def graphs(request):
gset = request.GET.get("gset", None)
gsets = events.raiseDictEvent("GetGraphSets")
- if not gset in gsets:
+ if gset not in gsets:
raise NotFound(_("Unknown graphic set"))
searchq = request.GET.get("searchquery", None)
period = request.GET.get("period", "day")
@@ -94,7 +97,7 @@ def graphs(request):
tplvars["domain"] = domain
if period == "custom":
- if not "start" in request.GET or not "end" in request.GET:
+ if "start" not in request.GET or "end" not in request.GET:
raise BadRequest(_("Bad custom period"))
start = request.GET["start"]
end = request.GET["end"]
diff --git a/modoboa/extensions/webmail/app_settings.py b/modoboa/extensions/webmail/app_settings.py
index eca4f4570..70e371332 100644
--- a/modoboa/extensions/webmail/app_settings.py
+++ b/modoboa/extensions/webmail/app_settings.py
@@ -2,7 +2,7 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
from modoboa.lib.parameters import AdminParametersForm, UserParametersForm
-from modoboa.lib.formutils import SeparatorField, YesNoField, InlineRadioSelect
+from modoboa.lib.form_utils import SeparatorField, YesNoField, InlineRadioSelect
class ParametersForm(AdminParametersForm):
@@ -13,7 +13,8 @@ class ParametersForm(AdminParametersForm):
max_attachment_size = forms.CharField(
label=_("Maximum attachment size"),
initial="2048",
- help_text=_("Maximum attachment size in bytes (or KB, MB, GB if specified)")
+ help_text=_(
+ "Maximum attachment size in bytes (or KB, MB, GB if specified)")
)
sep1 = SeparatorField(label=_("IMAP settings"))
diff --git a/modoboa/extensions/webmail/exceptions.py b/modoboa/extensions/webmail/exceptions.py
index 51a42634a..e5012ab8a 100644
--- a/modoboa/extensions/webmail/exceptions.py
+++ b/modoboa/extensions/webmail/exceptions.py
@@ -25,6 +25,7 @@ class WebmailInternalError(InternalError):
class UnknownAction(ModoboaException):
+
"""
Use this exception when the webmail encounter an unknown action.
"""
@@ -35,6 +36,7 @@ class UnknownAction(ModoboaException):
class ImapError(ModoboaException):
+
def __init__(self, reason):
self.reason = reason
diff --git a/modoboa/extensions/webmail/forms.py b/modoboa/extensions/webmail/forms.py
index fa14d5216..c377fc91b 100644
--- a/modoboa/extensions/webmail/forms.py
+++ b/modoboa/extensions/webmail/forms.py
@@ -9,7 +9,7 @@ from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from modoboa.lib import parameters
-from modoboa.lib.emailutils import set_email_headers
+from modoboa.lib.email_utils import set_email_headers
from .lib import (
ImapEmail, create_mail_attachment
)
@@ -169,8 +169,8 @@ class ComposeMailForm(forms.Form):
class ForwardMailForm(ComposeMailForm):
- """Forward mail form.
- """
+
+ """Forward mail form."""
def _build_msg(self, request):
"""Convert form's content to a MIME message.
@@ -203,7 +203,8 @@ class FolderForm(forms.Form):
widget=forms.HiddenInput(attrs={"class": "form-control"}),
required=False
)
- name = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"}))
+ name = forms.CharField(
+ widget=forms.TextInput(attrs={"class": "form-control"}))
class AttachmentForm(forms.Form):
diff --git a/modoboa/extensions/webmail/lib/attachments.py b/modoboa/extensions/webmail/lib/attachments.py
index 11ab08f93..7da2ed898 100644
--- a/modoboa/extensions/webmail/lib/attachments.py
+++ b/modoboa/extensions/webmail/lib/attachments.py
@@ -4,7 +4,7 @@ from django.conf import settings
from django.core.files.uploadhandler import FileUploadHandler, SkipFile
from modoboa.lib import parameters
from modoboa.lib.exceptions import InternalError
-from modoboa.lib.webutils import size2integer
+from modoboa.lib.web_utils import size2integer
def set_compose_session(request):
@@ -80,18 +80,19 @@ def create_mail_attachment(attdef, payload=None):
res = MIMEBase(maintype, subtype)
if payload is None:
with open(os.path.join(
- settings.MEDIA_ROOT, "webmail", attdef["tmpname"]), "rb") as fp:
+ settings.MEDIA_ROOT, "webmail", attdef["tmpname"]), "rb") as fp:
res.set_payload(fp.read())
else:
res.set_payload(payload)
Encoders.encode_base64(res)
- if type(attdef['fname']) is str:
+ if isinstance(attdef['fname'], str):
attdef['fname'] = attdef['fname'].decode('utf-8')
res['Content-Disposition'] = build_header(attdef['fname'])
return res
class AttachmentUploadHandler(FileUploadHandler):
+
"""
Simple upload handler to limit the size of the attachments users
can upload.
diff --git a/modoboa/extensions/webmail/lib/fetch_parser.py b/modoboa/extensions/webmail/lib/fetch_parser.py
index 97110d753..8d83cdf2d 100644
--- a/modoboa/extensions/webmail/lib/fetch_parser.py
+++ b/modoboa/extensions/webmail/lib/fetch_parser.py
@@ -10,16 +10,19 @@ retrieve multiple attributes of a message.
This simple module tries to fix that *problem*.
"""
+
class ParseError(Exception):
pass
class Token(object):
+
def __init__(self, value):
self.value = value
class Literal(Token):
+
def __init__(self, value):
super(Literal, self).__init__(value)
self.next_token_len = int(self.value[1:-1])
@@ -50,7 +53,8 @@ def parse_next_token(buf):
end = pos - 1
break
if end == -1:
- raise ParseError("End of buffer reached while looking for a token end")
+ raise ParseError(
+ "End of buffer reached while looking for a token end")
end += 1
token = klass(buf[:end])
return token, end
@@ -94,7 +98,7 @@ def parse_bodystructure(buf, depth=0, prefix=""):
# make a difference between a content-disposition with
# multiple args beeing parsed and a mime part! (see
# the last example at the end of the file)
- if type(ret[0]) == list or len(ret) >= 7:
+ if isinstance(ret[0], list) or len(ret) >= 7:
partnum = prefix
if partnum is not None:
return [{"partnum": partnum, "struct": ret}], pos
@@ -111,7 +115,8 @@ def parse_bodystructure(buf, depth=0, prefix=""):
else:
ret.append(token.value.strip('"'))
- raise ParseError("End of buffer reached while looking for a BODY/BODYSTRUCTURE end")
+ raise ParseError(
+ "End of buffer reached while looking for a BODY/BODYSTRUCTURE end")
def parse_fetch_response(data):
@@ -128,7 +133,7 @@ def parse_fetch_response(data):
while cpt < len(data):
content = ()
while cpt < len(data) and data[cpt] != ')':
- if type(data[cpt]) == str:
+ if isinstance(data[cpt], str):
# FIXME : probably an unsolicited response
cpt += 1
continue
@@ -192,13 +197,13 @@ def parse_fetch_response(data):
def dump_bodystructure(bs, depth=0):
if depth:
print " " * (depth * 4),
- if type(bs[0]) == dict:
+ if isinstance(bs[0], dict):
print "%s :" % bs[0]["partnum"],
struct = bs[0]["struct"]
else:
struct = bs
- if type(struct[0]) == list:
+ if isinstance(struct[0], list):
print "multipart/%s" % struct[1]
for part in struct[0]:
dump_bodystructure(part, depth + 1)
@@ -209,9 +214,12 @@ def dump_bodystructure(bs, depth=0):
if __name__ == "__main__":
# resp = [('855 (UID 46931 BODYSTRUCTURE ((("text" "plain" ("charset" "iso-8859-1") NIL NIL "quoted-printable" 886 32 NIL NIL NIL NIL)("text" "html" ("charset" "us-ascii") NIL NIL "quoted-printable" 1208 16 NIL NIL NIL NIL) "alternative" ("boundary" "----=_NextPart_001_0003_01CCC564.B2F64FF0") NIL NIL NIL)("application" "octet-stream" ("name" "Carte Verte_2.pdf") NIL NIL "base64" 285610 NIL ("attachment" ("filename" "Carte Verte_2.pdf")) NIL NIL) "mixed" ("boundary" "----=_NextPart_000_0002_01CCC564.B2F64FF0") NIL NIL NIL) BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)] {153}', 'From: \r\nTo: \r\nCc: \r\nSubject: Notre contact du 28/12/2011 - 192175092\r\nDate: Wed, 28 Dec 2011 13:29:17 +0100\r\n\r\n'), ')']
- # #resp = [('856 (UID 46936 BODYSTRUCTURE (("text" "plain" ("charset" "ISO-8859-1") NIL NIL "quoted-printable" 724 22 NIL NIL NIL NIL)("text" "html" ("charset" "ISO-8859-1") NIL NIL "quoted-printable" 2662 48 NIL NIL NIL NIL) "alternative" ("boundary" "----=_Part_1326887_254624357.1325083973970") NIL NIL NIL) BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)] {258}', 'Date: Wed, 28 Dec 2011 15:52:53 +0100 (CET)\r\nFrom: =?ISO-8859-1?Q?Malakoff_M=E9d=E9ric?= \r\nTo: Antoine Nguyen \r\nSubject: =?ISO-8859-1?Q?Votre_inscription_au_grand_Jeu_Malakoff_M=E9d=E9ric?=\r\n\r\n'), ')']
+# resp = [('856 (UID 46936 BODYSTRUCTURE (("text" "plain" ("charset" "ISO-8859-1") NIL NIL "quoted-printable" 724 22 NIL NIL NIL NIL)("text" "html" ("charset" "ISO-8859-1") NIL NIL "quoted-printable" 2662 48 NIL NIL NIL NIL) "alternative" ("boundary" "----=_Part_1326887_254624357.1325083973970") NIL NIL NIL) BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)] {258}', 'Date: Wed, 28 Dec 2011 15:52:53 +0100 (CET)\r\nFrom: =?ISO-8859-1?Q?Malakoff_M=E9d=E9ric?= \r\nTo: Antoine Nguyen \r\nSubject: =?ISO-8859-1?Q?Votre_inscription_au_grand_Jeu_Malakoff_M=E9d=E9ric?=\r\n\r\n'), ')']
- resp = [('856 (UID 11111 BODYSTRUCTURE ((("text" "plain" ("charset" "UTF-8") NIL NIL "7bit" 0 0 NIL NIL NIL NIL) "mixed" ("boundary" "----=_Part_407172_3159001.1321948277321") NIL NIL NIL)("application" "octet-stream" ("name" "26274308.pdf") NIL NIL "base64" 14906 NIL ("attachment" ("filename" "26274308.pdf")) NIL NIL) "mixed" ("boundary" "----=_Part_407171_9686991.1321948277321") NIL NIL NIL)',), ')']
+ resp = [
+ ('856 (UID 11111 BODYSTRUCTURE ((("text" "plain" ("charset" "UTF-8") NIL NIL "7bit" 0 0 NIL NIL NIL NIL) "mixed" ("boundary" "----=_Part_407172_3159001.1321948277321") NIL NIL NIL)("application" "octet-stream" ("name" "26274308.pdf") NIL NIL "base64" 14906 NIL ("attachment" ("filename" "26274308.pdf")) NIL NIL) "mixed" ("boundary" "----=_Part_407171_9686991.1321948277321") NIL NIL NIL)',
+ ),
+ ')']
# resp = [('19 (UID 19 FLAGS (\\Seen) BODYSTRUCTURE (("text" "plain" ("charset" "ISO-8859-1" "format" "flowed") NIL NIL "7bit" 2 1 NIL NIL NIL NIL)("message" "rfc822" ("name*" "ISO-8859-1\'\'%5B%49%4E%53%43%52%49%50%54%49%4F%4E%5D%20%52%E9%63%E9%70%74%69%6F%6E%20%64%65%20%76%6F%74%72%65%20%64%6F%73%73%69%65%72%20%64%27%69%6E%73%63%72%69%70%74%69%6F%6E%20%46%72%65%65%20%48%61%75%74%20%44%E9%62%69%74") NIL NIL "8bit" 3632 ("Wed, 13 Dec 2006 20:30:02 +0100" {70}',
# "[INSCRIPTION] R\xe9c\xe9ption de votre dossier d'inscription Free Haut D\xe9bit"),
diff --git a/modoboa/extensions/webmail/lib/imapemail.py b/modoboa/extensions/webmail/lib/imapemail.py
index dcdb34b9e..0b68b31e2 100644
--- a/modoboa/extensions/webmail/lib/imapemail.py
+++ b/modoboa/extensions/webmail/lib/imapemail.py
@@ -12,7 +12,7 @@ from django.conf import settings
from django.utils.translation import ugettext as _
from modoboa.lib import parameters, u2u_decode
-from modoboa.lib.emailutils import Email, EmailAddress
+from modoboa.lib.email_utils import Email, EmailAddress
from .imaputils import (
get_imapconnector, BodyStructure
)
@@ -82,7 +82,7 @@ class ImapEmail(Email):
)
self.bs = BodyStructure(self._msg['BODYSTRUCTURE'])
self._find_attachments()
- if not self.dformat in ["plain", "html"]:
+ if self.dformat not in ["plain", "html"]:
self.dformat = parameters.get_user(
self.request.user, self.dformat
)
@@ -312,7 +312,7 @@ class ForwardModifier(Modifier):
return "----- %s -----" % _("Original message")
def _header_begin_html(self):
- return "----- %s -----" % _("Original message")
+ return "----- %s -----" % _("Original message")
def _header_line_plain(self, key, value):
return "%s: %s\n" % (key, value)
diff --git a/modoboa/extensions/webmail/lib/imapheader.py b/modoboa/extensions/webmail/lib/imapheader.py
index 1e1f4dac9..4d6d9628d 100644
--- a/modoboa/extensions/webmail/lib/imapheader.py
+++ b/modoboa/extensions/webmail/lib/imapheader.py
@@ -2,7 +2,7 @@
Set of functions used to parse and transform email headers.
"""
import chardet
-from modoboa.lib.emailutils import EmailAddress
+from modoboa.lib.email_utils import EmailAddress
__all__ = [
'parse_from', 'parse_to', 'parse_message_id', 'parse_date',
@@ -13,7 +13,7 @@ __all__ = [
def to_unicode(value):
"""Try to convert a string to unicode.
"""
- if value is None or type(value) is unicode:
+ if value is None or isinstance(value, unicode):
return value
try:
value = value.decode('utf-8')
@@ -111,4 +111,3 @@ def parse_subject(value, **kwargs):
except UnicodeDecodeError:
subject = value
return to_unicode(subject)
-
diff --git a/modoboa/extensions/webmail/lib/imaputils.py b/modoboa/extensions/webmail/lib/imaputils.py
index 8305f9fef..7d34a75c8 100644
--- a/modoboa/extensions/webmail/lib/imaputils.py
+++ b/modoboa/extensions/webmail/lib/imaputils.py
@@ -3,24 +3,27 @@
:mod:`imaputils` --- Extra IMAPv4 utilities
-------------------------------------------
"""
-import imaplib
-import ssl
import email
+import imaplib
import re
+import ssl
import time
from functools import wraps
+
from django.utils.translation import ugettext as _
-from modoboa.lib import parameters, imap_utf7
-from modoboa.lib.exceptions import InternalError
-from modoboa.lib.connections import ConnectionsManager
-from modoboa.lib.webutils import static_url
+
from ..exceptions import ImapError, WebmailInternalError
from .fetch_parser import parse_fetch_response
+from modoboa.lib import parameters
+from modoboa.lib.connections import ConnectionsManager
+from modoboa.lib.exceptions import InternalError
-#imaplib.Debug = 4
+
+# imaplib.Debug = 4
class capability(object):
+
"""
Simple decorator to check if the server presents the required
capability. If not, a fallback method is called instead.
@@ -28,6 +31,7 @@ class capability(object):
:param name: the capability name (upper case)
:param fallback_method: a method's name
"""
+
def __init__(self, name, fallback_method):
self.name = name
self.fallback_method = fallback_method
@@ -43,12 +47,14 @@ class capability(object):
class BodyStructure(object):
+
"""
BODYSTRUCTURE response parser.
Just a simple class that tries to distinguish content parts from
attachments.
"""
+
def __init__(self, definition=None):
self.is_multipart = False
self.contents = {}
@@ -71,7 +77,8 @@ class BodyStructure(object):
* If the MIME type is text/plain or text/html:
- * If no previous part of this type has already been seen, it's a content
+ * If no previous part of this type has already been seen,
+ it's a content
* Otherwise it's an attachment
* Else, if the multipart subtype is related, we consider this
@@ -82,6 +89,7 @@ class BodyStructure(object):
:param definition: a part definition (list)
:param pnum: the part's number
:param multisubtype: the multipart subtype
+
"""
pnum = "1" if pnum is None else pnum
params = dict(pnum=pnum, params=definition[2], cid=definition[3],
@@ -91,7 +99,7 @@ class BodyStructure(object):
subtype = definition[1].lower()
ftype = "%s/%s" % (definition[0].lower(), subtype)
if ftype in ("text/plain", "text/html"):
- if not subtype in self.contents:
+ if subtype not in self.contents:
self.contents[subtype] = [params]
else:
self.contents[subtype].append(params)
@@ -106,24 +114,27 @@ class BodyStructure(object):
if mtype == "text":
extensions = ["textlines"] + extensions
elif ftype == "message/rfc822":
- extensions = ["envelopestruct", "bodystruct", "textlines"] + extensions
+ extensions = [
+ "envelopestruct",
+ "bodystruct",
+ "textlines"] + extensions
for idx, value in enumerate(definition[7:]):
params[extensions[idx]] = value
self.attachments += [params]
def load_from_definition(self, definition, multisubtype=None):
- if type(definition) == dict:
+ if isinstance(definition, dict):
struct = definition["struct"]
pnum = definition["partnum"]
- elif type(definition[0]) == dict:
+ elif isinstance(definition[0], dict):
struct = definition[0]["struct"]
pnum = definition[0]["partnum"]
else:
struct = definition
pnum = None
- if type(struct[0]) == list:
+ if isinstance(struct[0], list):
for part in struct[0]:
self.load_from_definition(part, struct[1])
return
@@ -146,8 +157,11 @@ class IMAPconnector(object):
__metaclass__ = ConnectionsManager
- list_base_pattern = r'\((?P.*?)\) "(?P.*)" "?(?P[^"]*)"?'
- list_response_pattern_literal = re.compile(r'\((?P.*?)\) "(?P.*)" \{(?P\d+)\}')
+ list_base_pattern = (
+ r'\((?P.*?)\) "(?P.*)" "?(?P[^"]*)"?'
+ )
+ list_response_pattern_literal = re.compile(
+ r'\((?P.*?)\) "(?P.*)" \{(?P\d+)\}')
list_response_pattern = re.compile(list_base_pattern)
listextended_response_pattern = \
re.compile(list_base_pattern + r'\s*(?P.*)')
@@ -178,7 +192,7 @@ class IMAPconnector(object):
if name in ['FETCH', 'SORT', 'STORE', 'COPY', 'SEARCH']:
try:
typ, data = self.m.uid(name, *args)
- except imaplib.IMAP4.error, e:
+ except imaplib.IMAP4.error as e:
raise ImapError(e)
if typ == "NO":
raise ImapError(data)
@@ -188,17 +202,17 @@ class IMAPconnector(object):
try:
typ, data = self.m._simple_command(name, *args)
- except imaplib.IMAP4.error, e:
+ except imaplib.IMAP4.error as e:
raise ImapError(e)
if typ == "NO":
raise ImapError(data)
- if not 'responses' in kwargs:
- if not name in self.m.untagged_responses:
+ if 'responses' not in kwargs:
+ if name not in self.m.untagged_responses:
return None
return self.m.untagged_responses.pop(name)
res = []
for r in kwargs['responses']:
- if not r in self.m.untagged_responses:
+ if r not in self.m.untagged_responses:
return None
res.append(self.m.untagged_responses.pop(r))
return res
@@ -218,14 +232,20 @@ class IMAPconnector(object):
if type(bodystruct[0]) in [list, tuple]:
cpt = 1
for part in bodystruct[0]:
- nprefix = "%s" % cpt if prefix == "" else "%s.%d" % (prefix, cpt)
+ nprefix = "%s" % cpt if prefix == "" else "%s.%d" % (
+ prefix, cpt)
index, encoding, size = \
- self.__find_content_in_bodystruct(part, mtype, stype, nprefix)
+ self.__find_content_in_bodystruct(
+ part,
+ mtype,
+ stype,
+ nprefix)
if index is not None:
return (index, encoding, size)
cpt += 1
else:
- if bodystruct[0].lower() == mtype and bodystruct[1].lower() == stype:
+ if bodystruct[0].lower() == mtype and \
+ bodystruct[1].lower() == stype:
return ("1" if not len(prefix) else prefix,
bodystruct[5], int(bodystruct[6]))
return (None, None, 0)
@@ -275,9 +295,9 @@ class IMAPconnector(object):
"""
import socket
- if type(user) is unicode:
+ if isinstance(user, unicode):
user = user.encode("utf-8")
- if type(passwd) is unicode:
+ if isinstance(passwd, unicode):
passwd = passwd.encode("utf-8")
try:
secured = parameters.get_admin("IMAP_SECURED")
@@ -285,7 +305,7 @@ class IMAPconnector(object):
self.m = imaplib.IMAP4_SSL(self.address, self.port)
else:
self.m = imaplib.IMAP4(self.address, self.port)
- except (socket.error, imaplib.IMAP4.error, ssl.SSLError), error:
+ except (socket.error, imaplib.IMAP4.error, ssl.SSLError) as error:
raise ImapError(_("Connection to IMAP server failed: %s" % error))
data = self._cmd("LOGIN", user, passwd)
@@ -320,9 +340,9 @@ class IMAPconnector(object):
if criterion == u"both":
criterion = u"from_addr, subject"
criterions = ""
- if type(pattern) is unicode:
+ if isinstance(pattern, unicode):
pattern = pattern.encode("utf-8")
- if type(criterion) is unicode:
+ if isinstance(criterion, unicode):
criterion = criterion.encode("utf-8")
for c in criterion.split(','):
if c == "from_addr":
@@ -418,12 +438,14 @@ class IMAPconnector(object):
return True
def _listmboxes_simple(self, topmailbox='INBOX', mailboxes=None, **kwargs):
- #data = self._cmd("LIST", "", "*")
- if not mailboxes: mailboxes = []
+ # data = self._cmd("LIST", "", "*")
+ if not mailboxes:
+ mailboxes = []
(status, data) = self.m.list()
newmboxes = []
for mb in data:
- flags, delimiter, name = self.list_response_pattern.match(mb).groups()
+ flags, delimiter, name = self.list_response_pattern.match(
+ mb).groups()
name = name.strip('"').decode("imap4-utf-7")
mdm_found = False
for idx, mdm in enumerate(mailboxes):
@@ -437,7 +459,7 @@ class IMAPconnector(object):
if re.search("\%s" % delimiter, name):
parts = name.split(delimiter)
- if not "path" in descr:
+ if "path" not in descr:
descr["path"] = parts[0]
descr["sub"] = []
if self._parse_mailbox_name(descr["sub"], parts[0], delimiter,
@@ -480,7 +502,7 @@ class IMAPconnector(object):
descr = dict(name=name)
newmboxes += [descr]
- if r'\Marked' in flags or not r'\UnMarked' in flags:
+ if r'\Marked' in flags or r'\UnMarked' not in flags:
descr["send_status"] = True
if r'\HasChildren' in flags:
if r'\NonExistent' in flags:
@@ -529,7 +551,7 @@ class IMAPconnector(object):
if unseen_messages:
for mb in md_mailboxes:
- if not "send_status" in mb:
+ if "send_status" not in mb:
continue
del mb["send_status"]
key = "path" if "path" in mb else "name"
@@ -631,7 +653,7 @@ class IMAPconnector(object):
We also compute the current usage.
"""
- if not "QUOTA" in self.capabilities:
+ if "QUOTA" not in self.capabilities:
self.quota_limit = self.quota_current = None
return
@@ -700,7 +722,7 @@ class IMAPconnector(object):
data[int(uid)]['BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)]']
)
msg['imapid'] = uid
- if not r'\Seen' in data[int(uid)]['FLAGS']:
+ if r'\Seen' not in data[int(uid)]['FLAGS']:
msg['style'] = 'unseen'
if r'\Answered' in data[int(uid)]['FLAGS']:
msg['answered'] = True
diff --git a/modoboa/extensions/webmail/lib/sendmail.py b/modoboa/extensions/webmail/lib/sendmail.py
index ba654cc02..14b4eaa19 100644
--- a/modoboa/extensions/webmail/lib/sendmail.py
+++ b/modoboa/extensions/webmail/lib/sendmail.py
@@ -1,7 +1,7 @@
import smtplib
from modoboa.lib import parameters
-from modoboa.lib.emailutils import prepare_addresses
-from modoboa.lib.webutils import _render_to_string
+from modoboa.lib.email_utils import prepare_addresses
+from modoboa.lib.web_utils import _render_to_string
from modoboa.lib.cryptutils import get_password
from modoboa.extensions.webmail.exceptions import WebmailInternalError
from . import get_imapconnector, clean_attachments
diff --git a/modoboa/extensions/webmail/lib/signature.py b/modoboa/extensions/webmail/lib/signature.py
index c90a00fa5..2b24507fa 100644
--- a/modoboa/extensions/webmail/lib/signature.py
+++ b/modoboa/extensions/webmail/lib/signature.py
@@ -3,10 +3,12 @@ from modoboa.lib import parameters
class EmailSignature(object):
+
"""User signature
:param user: User object
"""
+
def __init__(self, user):
self._sig = u""
dformat = parameters.get_user(user, "EDITOR")
diff --git a/modoboa/extensions/webmail/lib/utils.py b/modoboa/extensions/webmail/lib/utils.py
index d48dd20ab..366384de0 100644
--- a/modoboa/extensions/webmail/lib/utils.py
+++ b/modoboa/extensions/webmail/lib/utils.py
@@ -1,7 +1,7 @@
"""
Misc. utilities.
"""
-from modoboa.lib.webutils import NavigationParameters
+from modoboa.lib.web_utils import NavigationParameters
def decode_payload(encoding, payload):
@@ -24,9 +24,11 @@ def decode_payload(encoding, payload):
class WebmailNavigationParameters(NavigationParameters):
+
"""
Specific NavigationParameters subclass for the webmail.
"""
+
def __init__(self, request, defmailbox=None):
super(WebmailNavigationParameters, self).__init__(
request, 'webmail_navparams'
diff --git a/modoboa/extensions/webmail/templatetags/webmail_tags.py b/modoboa/extensions/webmail/templatetags/webmail_tags.py
index 2bf34d190..83b2f3d95 100644
--- a/modoboa/extensions/webmail/templatetags/webmail_tags.py
+++ b/modoboa/extensions/webmail/templatetags/webmail_tags.py
@@ -36,14 +36,14 @@ def viewmail_menu(selection, folder, user, mail_id=None):
"url": "action=forward&mbox=%s&mailid=%s" % (folder, mail_id),
"img": "fa fa-mail-forward",
"label": _("Forward")},
- ]},
+ ]},
{"name": "delete",
"img": "fa fa-trash",
"class": "btn-danger",
"url": "{0}?mbox={1}&selection[]={2}".format(
reverse("webmail:mail_delete"), folder, mail_id),
"title": _("Delete")
- },
+ },
{"name": "display_options",
"title": _("Display options"),
"img": "fa fa-cog",
@@ -150,7 +150,8 @@ def print_mailboxes(
extra_attrs = ' data-toggle="%d"' % mbox["unseen"]
if "sub" in mbox:
- if selected is not None and selected != name and selected.count(name):
+ if selected is not None and selected != name and selected.count(
+ name):
ul_state = "visible"
div_state = "expanded"
else:
@@ -171,7 +172,7 @@ def print_mailboxes(
result += "" % (
mbox["path"], ul_state) + print_mailboxes(
mbox["sub"], selected, withunseen, selectonly, hdelimiter
- ) + "
\n"
+ ) + "\n"
result += "\n"
return result
diff --git a/modoboa/extensions/webmail/views.py b/modoboa/extensions/webmail/views.py
index a307192c9..230ddc230 100644
--- a/modoboa/extensions/webmail/views.py
+++ b/modoboa/extensions/webmail/views.py
@@ -20,7 +20,7 @@ from django.views.decorators.gzip import gzip_page
from modoboa.lib import parameters
from modoboa.lib.exceptions import ModoboaException, BadRequest
from modoboa.lib.paginator import Paginator
-from modoboa.lib.webutils import (
+from modoboa.lib.web_utils import (
_render_to_string, ajax_response, render_to_json_response
)
from modoboa.extensions.admin.lib import needs_mailbox
@@ -71,7 +71,7 @@ def getattachment(request):
@needs_mailbox()
def move(request):
for arg in ["msgset", "to"]:
- if not arg in request.GET:
+ if arg not in request.GET:
raise BadRequest(_("Invalid request"))
mbc = get_imapconnector(request)
navparams = WebmailNavigationParameters(request)
@@ -291,8 +291,8 @@ def attachments(request, tplname="webmail/attachments.html"):
@login_required
@needs_mailbox()
def delattachment(request):
- if not "compose_mail" in request.session \
- or not "name" in request.GET \
+ if "compose_mail" not in request.session \
+ or "name" not in request.GET \
or not request.GET["name"]:
return ajax_response(request, "ko", respmsg=_("Bad query"))
@@ -305,7 +305,7 @@ def delattachment(request):
)
try:
os.remove(fullpath)
- except OSError, e:
+ except OSError as e:
error = _("Failed to remove attachment: ") + str(e)
break
request.session.modified = True
@@ -390,11 +390,11 @@ def render_compose(request, form, posturl, email=None, insert_signature=False):
signature = EmailSignature(request.user)
body += unicode(signature)
randid = None
- if not "id" in request.GET:
+ if "id" not in request.GET:
if "compose_mail" in request.session:
clean_attachments(request.session["compose_mail"]["attachments"])
randid = set_compose_session(request)
- elif not "compose_mail" in request.session \
+ elif "compose_mail" not in request.session \
or request.session["compose_mail"]["id"] != request.GET["id"]:
randid = set_compose_session(request)
@@ -567,7 +567,7 @@ def index(request):
"""
action = request.GET.get("action", None)
if action is not None:
- if not action in globals():
+ if action not in globals():
raise UnknownAction
response = globals()[action](request)
else:
diff --git a/modoboa/lib/authbackends.py b/modoboa/lib/authbackends.py
index c08555a4e..92509fd28 100644
--- a/modoboa/lib/authbackends.py
+++ b/modoboa/lib/authbackends.py
@@ -1,11 +1,16 @@
+"""Custom authentication backends."""
+
from django.contrib.auth.backends import ModelBackend
+
from modoboa.core.models import User
from modoboa.lib import parameters
-from modoboa.lib.emailutils import split_mailbox
+from modoboa.lib.email_utils import split_mailbox
class SimpleBackend(ModelBackend):
+ """Simple authentication backend."""
+
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(username=username)
diff --git a/modoboa/lib/connections.py b/modoboa/lib/connections.py
index adbe58b94..3cb4f175a 100644
--- a/modoboa/lib/connections.py
+++ b/modoboa/lib/connections.py
@@ -2,10 +2,13 @@ from modoboa.lib.cryptutils import decrypt
class ConnectionsManager(type):
- """Singleton pattern implementation
+
+ """
+ Singleton pattern implementation.
This class is specialized in connection management.
"""
+
def __init__(cls, name, bases, ctx):
super(ConnectionsManager, cls).__init__(name, bases, ctx)
cls.instances = {}
@@ -16,7 +19,7 @@ class ConnectionsManager(type):
key = kwargs["user"]
else:
return None
- if not key in cls.instances:
+ if key not in cls.instances:
cls.instances[key] = None
if "password" in kwargs:
kwargs["password"] = decrypt(kwargs["password"])
diff --git a/modoboa/lib/dbutils.py b/modoboa/lib/db_utils.py
similarity index 88%
rename from modoboa/lib/dbutils.py
rename to modoboa/lib/db_utils.py
index 1bcd03fcb..156fc5077 100644
--- a/modoboa/lib/dbutils.py
+++ b/modoboa/lib/db_utils.py
@@ -1,7 +1,9 @@
# coding: utf-8
-from django.db import connection
+
from django.conf import settings
+from django.db import connection
from django.utils.translation import ugettext as _
+
from modoboa.lib.exceptions import InternalError
@@ -32,8 +34,9 @@ def db_type(cname="default"):
:param str cname: connection name
:return: a string or None
"""
- if not cname in settings.DATABASES:
- raise InternalError(_("Connection to database %s not configured" % cname))
+ if cname not in settings.DATABASES:
+ raise InternalError(
+ _("Connection to database %s not configured" % cname))
for t in ['postgres', 'mysql', 'sqlite']:
if settings.DATABASES[cname]['ENGINE'].find(t) != -1:
return t
diff --git a/modoboa/lib/emailutils.py b/modoboa/lib/email_utils.py
similarity index 95%
rename from modoboa/lib/emailutils.py
rename to modoboa/lib/email_utils.py
index 425c898d8..1ed4b6377 100644
--- a/modoboa/lib/emailutils.py
+++ b/modoboa/lib/email_utils.py
@@ -1,16 +1,20 @@
# coding: utf-8
-import time
+
import re
import smtplib
+import time
from email.header import Header, decode_header
from email.mime.text import MIMEText
from email.utils import make_msgid, formatdate, parseaddr
-from django.template.loader import render_to_string
+
from django.conf import settings
+from django.template.loader import render_to_string
+
from modoboa.lib import u2u_decode
class EmailAddress(object):
+
def __init__(self, address):
self.fulladdress = u2u_decode.u2u_decode(address).strip("\r\t\n")
(self.name, self.address) = parseaddr(self.fulladdress)
@@ -22,6 +26,7 @@ class EmailAddress(object):
class Email(object):
+
def __init__(self, mailid, mformat="plain", dformat="plain", links=0):
self.attached_map = {}
self.contents = {"html": "", "plain": ""}
@@ -42,11 +47,12 @@ class Email(object):
@property
def body(self):
- """Return email's body.
- """
+ """Return email's body."""
if self._body is None:
- self._body = getattr(self, "viewmail_%s" % self.mformat) \
+ self._body = (
+ getattr(self, "viewmail_%s" % self.mformat)
(self.contents[self.mformat], links=self.links)
+ )
return self._body
def get_header(self, msg, hdrname):
@@ -231,19 +237,22 @@ def split_mailbox(mailbox):
return (address, domain)
-def decode(s, encodings=('utf8', 'latin1', 'windows-1252', 'ascii'), charset=None):
+def decode(string, encodings=None, charset=None):
+ """Try to decode the given string."""
+ if encodings is None:
+ encodings = ('utf8', 'latin1', 'windows-1252', 'ascii')
if charset is not None:
try:
- return s.decode(charset, 'ignore')
+ return string.decode(charset, 'ignore')
except LookupError:
pass
for encoding in encodings:
try:
- return s.decode(encoding)
+ return string.decode(encoding)
except UnicodeDecodeError:
pass
- return s.decode('ascii', 'ignore')
+ return string.decode('ascii', 'ignore')
def prepare_addresses(addresses, usage="header"):
@@ -306,7 +315,8 @@ def __sendmail(sender, rcpt, msgstring, server='localhost', port=25):
return True, None
-def sendmail_simple(sender, rcpt, subject="Sample message", content="", **kwargs):
+def sendmail_simple(
+ sender, rcpt, subject="Sample message", content="", **kwargs):
"""Simple way to send a text message
Send a text/plain message with basic headers (msg-id, date).
diff --git a/modoboa/lib/events.py b/modoboa/lib/events.py
index cd574b412..19bbe0b20 100644
--- a/modoboa/lib/events.py
+++ b/modoboa/lib/events.py
@@ -8,12 +8,15 @@ This module provides a simple way of managing events between Modoboa
core application and additional components.
"""
-from functools import wraps
import inspect
import re
+from functools import wraps
+
from django.conf import settings
+
from modoboa.lib.sysutils import guess_extension_name
+
events = []
callbacks = {}
@@ -24,7 +27,7 @@ def declare(nevents):
:param list nevents: a list of event names
"""
for evt in nevents:
- if not evt in events:
+ if evt not in events:
events.append(evt)
@@ -39,10 +42,10 @@ def register(event, callback):
# if not event in events:
# print "Event %s not registered" % event
# return 0
- if not event in callbacks.keys():
+ if event not in callbacks.keys():
callbacks[event] = {}
fullname = "%s.%s" % (callback.__module__, callback.__name__)
- if not fullname in callbacks[event]:
+ if fullname not in callbacks[event]:
callbacks[event][fullname] = callback
return 1
@@ -93,7 +96,7 @@ class observe(object):
else:
if not ext.enabled:
return None
- elif not modname in settings.MODOBOA_APPS:
+ elif modname not in settings.MODOBOA_APPS:
return []
return f(*args, **kwargs)
for evt in self.evtnames:
@@ -107,9 +110,9 @@ def unregister(event, callback):
:param event: the targeted event
:param callback: the callback to remove
"""
- if not event in events:
+ if event not in events:
return False
- if not event in callbacks:
+ if event not in callbacks:
return False
fullname = "%s.%s" % (callback.__module__, callback.__name__)
try:
@@ -139,7 +142,7 @@ def raiseEvent(event, *args, **kwargs):
:param event: the event to raise
"""
- if not event in events or not event in callbacks:
+ if event not in events or event not in callbacks:
return 0
for callback in callbacks[event].values():
callback(*args, **kwargs)
@@ -155,7 +158,7 @@ def raiseQueryEvent(event, *args, **kwargs):
:param event: the event to raise
"""
result = []
- if not event in events or not event in callbacks:
+ if event not in events or event not in callbacks:
return result
for callback in callbacks[event].values():
tmp = callback(*args, **kwargs)
@@ -177,7 +180,7 @@ def raiseDictEvent(event, *args):
:return: a dictionnary
"""
result = {}
- if not event in events or not event in callbacks:
+ if event not in events or event not in callbacks:
return result
for callback in callbacks[event].values():
tmp = callback(*args)
diff --git a/modoboa/lib/formutils.py b/modoboa/lib/form_utils.py
similarity index 93%
rename from modoboa/lib/formutils.py
rename to modoboa/lib/form_utils.py
index 9c75761b2..ef90c7ff4 100644
--- a/modoboa/lib/formutils.py
+++ b/modoboa/lib/form_utils.py
@@ -1,24 +1,28 @@
# coding: utf-8
-import re
-import abc
-from django.shortcuts import render
-from django.utils.translation import ugettext as _, ugettext_lazy
-from django.forms import ChoiceField
-from django.forms.widgets import RadioSelect, RadioInput
-from django.forms.fields import CharField, Field
+"""Form management utilities."""
+
+import abc
+import re
+
from django.core.exceptions import ValidationError
+from django.forms import ChoiceField
+from django.forms.fields import CharField, Field
+from django.forms.widgets import RadioSelect, RadioInput
+from django.shortcuts import render
from django.utils.encoding import force_unicode
-from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext as _, ugettext_lazy
from modoboa.lib.exceptions import BadRequest
-from modoboa.lib.webutils import render_to_json_response
+from modoboa.lib.web_utils import render_to_json_response
class WizardStep(object):
- """A wizard step.
- """
+
+ """A wizard step."""
+
def __init__(self, cls, title, formtpl=None, new_args=None):
"""Constructor.
@@ -65,8 +69,9 @@ class WizardStep(object):
class WizardForm(object):
- """Custom wizard.
- """
+
+ """Custom wizard."""
+
__metaclass__ = abc.ABCMeta
template_name = "common/wizard_forms.html"
@@ -159,12 +164,15 @@ class WizardForm(object):
class DynamicForm(object):
- """A form which accepts dynamic fields.
+
+ """
+ A form which accepts dynamic fields.
We consider a field to be dynamic when it can appear multiple
times within the same request.
"""
+
def _create_field(self, typ, name, value=None, pos=None):
"""Create a new form field.
"""
@@ -222,8 +230,9 @@ class TabForms(object):
args.append(request.POST)
if instances is not None:
self.instances = instances
- if hasattr(self, "check_%s" % fd["id"]):
- if not getattr(self, "check_%s" % fd["id"])(instances[fd["id"]]):
+ mname = "check_%s" % fd["id"]
+ if hasattr(self, mname):
+ if not getattr(self, mname)(instances[fd["id"]]):
to_remove += [fd]
continue
kwargs["instance"] = instances[fd["id"]]
@@ -231,7 +240,7 @@ class TabForms(object):
fd["instance"] = classes[fd["id"]](*args, **kwargs)
else:
fd["instance"] = fd["cls"](*args, **kwargs)
- self.forms = [form for form in self.forms if not form in to_remove]
+ self.forms = [form for form in self.forms if form not in to_remove]
if self.forms:
self.active_id = self.forms[0]["id"]
@@ -263,10 +272,9 @@ class TabForms(object):
to_remove = []
for f in self.forms:
if mandatory_only and \
- (not 'mandatory' in f or not f["mandatory"]):
+ ('mandatory' not in f or not f["mandatory"]):
continue
- elif optional_only and \
- ('mandatory' in f and f["mandatory"]):
+ elif optional_only and ('mandatory' in f and f["mandatory"]):
continue
if not self._before_is_valid(f):
to_remove.append(f)
@@ -274,7 +282,7 @@ class TabForms(object):
if not f["instance"].is_valid():
self.active_id = f["id"]
return False
- self.forms = [f for f in self.forms if not f in to_remove]
+ self.forms = [f for f in self.forms if f not in to_remove]
return True
@abc.abstractmethod
@@ -334,11 +342,11 @@ class TabForms(object):
def is_valid_host(host):
- """IDN compatible domain validator
- """
+ """IDN compatible domain validator."""
host = host.encode('idna').lower()
if not hasattr(is_valid_host, '_re'):
- is_valid_host._re = re.compile(r'^([0-9a-z][-\w]*[0-9a-z]\.)+[a-z0-9\-]{2,15}$')
+ is_valid_host._re = re.compile(
+ r'^([0-9a-z][-\w]*[0-9a-z]\.)+[a-z0-9\-]{2,15}$')
return bool(is_valid_host._re.match(host))
diff --git a/modoboa/lib/imap_utf7.py b/modoboa/lib/imap_utf7.py
index bceddc5ee..2da57f0e9 100644
--- a/modoboa/lib/imap_utf7.py
+++ b/modoboa/lib/imap_utf7.py
@@ -1,8 +1,8 @@
# -*- coding: iso-8859-1 -*-
"""
-Imap folder names are encoded using a special version of utf-7 as defined in RFC
-2060 section 5.1.3.
+Imap folder names are encoded using a special version of utf-7 as
+defined in RFC 2060 section 5.1.3.
5.1.3. Mailbox International Naming Convention
@@ -56,15 +56,18 @@ import codecs
# encoding
+
def modified_base64(s):
s = s.encode('utf-16be')
return binascii.b2a_base64(s).rstrip('\n=').replace('/', ',')
+
def doB64(_in, r):
if _in:
r.append('&%s-' % modified_base64(''.join(_in)))
del _in[:]
+
def encoder(s):
r = []
_in = []
@@ -127,12 +130,13 @@ def imap4_utf_7(name):
codecs.register(imap4_utf_7)
-## testing methods
+# testing methods
def imapUTF7Encode(ust):
"Returns imap utf-7 encoded version of string"
return ust.encode('imap4-utf-7')
+
def imapUTF7EncodeSequence(seq):
"Returns imap utf-7 encoded version of strings in sequence"
return [imapUTF7Encode(itm) for itm in seq]
@@ -142,6 +146,7 @@ def imapUTF7Decode(st):
"Returns utf7 encoded version of imap utf-7 string"
return st.decode('imap4-utf-7')
+
def imapUTF7DecodeSequence(seq):
"Returns utf7 encoded version of imap utf-7 strings in sequence"
return [imapUTF7Decode(itm) for itm in seq]
@@ -159,14 +164,14 @@ def utf7SequenceToUTF8(seq):
__all__ = ['imapUTF7Encode', 'imapUTF7Decode', ]
-if __name__=='__main__':
+if __name__ == '__main__':
+
+ # print u'bøx'.encode('imap4-utf-7')
+ # print 'expected b&APg-x'
+
+ # print u'båx'.encode('imap4-utf-7')
+ # print 'expected b&AOU-x'
-## print u'bøx'.encode('imap4-utf-7')
-## print 'expected b&APg-x'
-##
-## print u'båx'.encode('imap4-utf-7')
-## print 'expected b&AOU-x'
-##
print '#######'
print 'bøx'
e = imapUTF7Encode(u'bøx')
@@ -191,12 +196,12 @@ if __name__=='__main__':
print e
print imapUTF7Decode(e).encode('latin-1')
-## e = imapUTF7Decode('b&AOU-x')
-## print e.encode('latin-1')
-##
-## e = imapUTF7Decode('b&APg-x')
-## print e.encode('latin-1')
-##
+ # e = imapUTF7Decode('b&AOU-x')
+ # print e.encode('latin-1')
+
+ # e = imapUTF7Decode('b&APg-x')
+ # print e.encode('latin-1')
+
print '#######'
print '~/Følder/mailbåx & stuff + more'
n = u'~/Følder/mailbåx & stuff + more'
@@ -207,4 +212,3 @@ if __name__=='__main__':
print '#######'
print '~peter/mail/&ZeVnLIqe-/&U,BTFw-'
print imapUTF7Decode('~peter/mail/&ZeVnLIqe-/&U,BTFw-').encode('utf-8')
-
diff --git a/modoboa/lib/ldaputils.py b/modoboa/lib/ldap_utils.py
similarity index 96%
rename from modoboa/lib/ldaputils.py
rename to modoboa/lib/ldap_utils.py
index b83e0971c..3ba247dac 100644
--- a/modoboa/lib/ldaputils.py
+++ b/modoboa/lib/ldap_utils.py
@@ -34,14 +34,17 @@ Extracted from `this blog
"""
import ldap
+
from django.conf import settings
from django.utils.translation import ugettext as _
+
from modoboa.core.password_hashers import get_password_hasher
from modoboa.lib import parameters
from modoboa.lib.exceptions import InternalError
class LDAPAuthBackend(object):
+
def __init__(self):
self.server_uri = self._setting(
"AUTH_LDAP_SERVER_URI", "ldap://localhost"
@@ -102,7 +105,9 @@ class LDAPAuthBackend(object):
try:
self.connect_to_server(user, password)
if self.ldap_ad:
- newpassword = ('"%s"' % newpassword).encode('utf-16').lstrip('\377\376')
+ newpassword = (
+ ('"%s"' % newpassword).encode('utf-16').lstrip('\377\376')
+ )
ldif = [(ldap.MOD_REPLACE,
self.pwd_attr,
self._crypt_password(newpassword))]
diff --git a/modoboa/lib/listing.py b/modoboa/lib/listing.py
index 858b85b31..3ae82d832 100644
--- a/modoboa/lib/listing.py
+++ b/modoboa/lib/listing.py
@@ -1,4 +1,5 @@
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
+
from modoboa.lib import parameters
@@ -16,7 +17,7 @@ def get_sort_order(qdict, default, allowed_values=None):
sort_order = sort_order[1:]
else:
sort_dir = ""
- if allowed_values is not None and not sort_order in allowed_values:
+ if allowed_values is not None and sort_order not in allowed_values:
return (default, "")
return (sort_order, sort_dir)
diff --git a/modoboa/lib/middleware.py b/modoboa/lib/middleware.py
index d32e3037a..31b7df90a 100644
--- a/modoboa/lib/middleware.py
+++ b/modoboa/lib/middleware.py
@@ -1,21 +1,24 @@
# coding: utf-8
-"""
-Custom middlewares.
-"""
+
+"""Custom middlewares."""
+
import re
+
from django.http import Http404, HttpResponseRedirect
-from modoboa.core.models import Extension
+
from modoboa.core.extensions import exts_pool
-from modoboa.lib.webutils import (
- _render_error, ajax_response, render_to_json_response
-)
+from modoboa.core.models import Extension
from modoboa.lib.exceptions import ModoboaException
from modoboa.lib.signals import request_accessor
+from modoboa.lib.web_utils import (
+ _render_error, ajax_response, render_to_json_response
+)
class ExtControlMiddleware(object):
+
def process_view(self, request, view, args, kwargs):
- m = re.match("modoboa\.extensions\.(\w+)", view.__module__)
+ m = re.match(r"modoboa\.extensions\.(\w+)", view.__module__)
if m is None:
return None
try:
@@ -31,6 +34,7 @@ class ExtControlMiddleware(object):
class AjaxLoginRedirect(object):
+
def process_response(self, request, response):
if request.is_ajax():
if type(response) == HttpResponseRedirect:
@@ -39,6 +43,7 @@ class AjaxLoginRedirect(object):
class CommonExceptionCatcher(object):
+
def process_exception(self, request, exception):
if not isinstance(exception, ModoboaException):
return None
@@ -57,6 +62,7 @@ class CommonExceptionCatcher(object):
class RequestCatcherMiddleware(object):
+
"""
Simple middleware to store the current request.
"""
diff --git a/modoboa/lib/parameters.py b/modoboa/lib/parameters.py
index b682501bf..e430f02de 100644
--- a/modoboa/lib/parameters.py
+++ b/modoboa/lib/parameters.py
@@ -1,4 +1,5 @@
# coding: utf-8
+
"""
This interface provides a simple way to declare and store parameters
in Modoboa's database.
@@ -8,10 +9,13 @@ will be available and modifiable directly from the web interface.
Only super users will be able to access this part of the web interface.
"""
+
from django import forms
+
from modoboa.lib import events
-from modoboa.lib.sysutils import guess_extension_name
from modoboa.lib.exceptions import ModoboaException
+from modoboa.lib.sysutils import guess_extension_name
+
_params = {'A': {}, 'U': {}}
@@ -83,16 +87,19 @@ class GenericParametersForm(forms.Form):
class AdminParametersForm(GenericParametersForm):
+
def _load_initial_values(self):
from .models import Parameter
- names = ["%s.%s" % (self.app, name.upper()) for name in self.fields.keys()]
+ names = [
+ "%s.%s" % (self.app, name.upper()) for name in self.fields.keys()
+ ]
for p in Parameter.objects.filter(name__in=names):
self.fields[p.shortname].initial = self._decode_value(p.value)
def save(self):
from .models import Parameter
- from modoboa.lib.formutils import SeparatorField
+ from modoboa.lib.form_utils import SeparatorField
for name, value in self.cleaned_data.items():
if type(self.fields[name]) is SeparatorField:
@@ -119,6 +126,7 @@ class AdminParametersForm(GenericParametersForm):
class UserParametersForm(GenericParametersForm):
+
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user") if "user" in kwargs else None
super(UserParametersForm, self).__init__(*args, **kwargs)
@@ -128,7 +136,9 @@ class UserParametersForm(GenericParametersForm):
return
from .models import UserParameter
- names = ["%s.%s" % (self.app, name.upper()) for name in self.fields.keys()]
+ names = [
+ "%s.%s" % (self.app, name.upper()) for name in self.fields.keys()
+ ]
for p in UserParameter.objects.filter(user=self.user, name__in=names):
self.fields[p.shortname].initial = self._decode_value(p.value)
@@ -138,7 +148,7 @@ class UserParametersForm(GenericParametersForm):
def save(self):
from .models import UserParameter
- from modoboa.lib.formutils import SeparatorField
+ from modoboa.lib.form_utils import SeparatorField
for name, value in self.cleaned_data.items():
if type(self.fields[name]) is SeparatorField:
@@ -162,7 +172,7 @@ def register(formclass, label):
:param formclass: a form class
:param string label: the label to display in parameters or settings pages
"""
- from modoboa.lib.formutils import SeparatorField
+ from modoboa.lib.form_utils import SeparatorField
if issubclass(formclass, AdminParametersForm):
level = 'A'
@@ -196,9 +206,12 @@ def unregister(app=None):
def __is_defined(app, level, name):
- if not level in ['A', 'U'] \
- or not app in _params[level] \
- or not name in _params[level][app]["defaults"]:
+ not_defined = (
+ level not in ['A', 'U']
+ or app not in _params[level]
+ or name not in _params[level][app]["defaults"]
+ )
+ if not_defined:
raise NotDefined(app, name)
diff --git a/modoboa/lib/permissions.py b/modoboa/lib/permissions.py
index a4b3487d3..b1a136b99 100644
--- a/modoboa/lib/permissions.py
+++ b/modoboa/lib/permissions.py
@@ -81,7 +81,8 @@ def grant_access_to_objects(user, objects, ct):
"""
for obj in objects:
try:
- ObjectAccess.objects.create(user=user, content_type=ct, object_id=obj.id)
+ ObjectAccess.objects.create(
+ user=user, content_type=ct, object_id=obj.id)
except IntegrityError:
pass
diff --git a/modoboa/lib/signals.py b/modoboa/lib/signals.py
index fbd5baad8..551898a40 100644
--- a/modoboa/lib/signals.py
+++ b/modoboa/lib/signals.py
@@ -1,11 +1,10 @@
-"""
-Custom signals.
-"""
+"""Custom signals."""
+
from django.dispatch import Signal
request_accessor = Signal()
+
def get_request():
- """Get the current request from anywhere.
- """
+ """Get the current request from anywhere."""
return request_accessor.send(None)[0][1]
diff --git a/modoboa/lib/templatetags/form_tags.py b/modoboa/lib/templatetags/form_tags.py
index e29091907..4a2ae2034 100644
--- a/modoboa/lib/templatetags/form_tags.py
+++ b/modoboa/lib/templatetags/form_tags.py
@@ -6,7 +6,7 @@ from django import forms
from django import template
from django.template.loader import render_to_string
-from modoboa.lib.formutils import SeparatorField
+from modoboa.lib.form_utils import SeparatorField
register = template.Library()
@@ -63,7 +63,7 @@ def render_fields_group(form, pattern):
haserror = len(first.errors) != 0
while True:
fname = "%s_%d" % (pattern, cpt)
- if not fname in form.fields:
+ if fname not in form.fields:
break
bfield = forms.forms.BoundField(form, form.fields[fname], fname)
if len(bfield.errors):
diff --git a/modoboa/lib/tests.py b/modoboa/lib/tests.py
index cdf66b3db..5eee3edbd 100644
--- a/modoboa/lib/tests.py
+++ b/modoboa/lib/tests.py
@@ -11,13 +11,14 @@ class ModoTestCase(TestCase):
def setUp(self, username="admin", password="password"):
self.clt = Client()
- self.assertEqual(self.clt.login(username=username, password=password), True)
+ self.assertEqual(
+ self.clt.login(username=username, password=password), True)
def ajax_request(self, method, url, params=None, status=200):
if params is None:
params = {}
- response = getattr(self.clt, method) \
- (url, params, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
+ response = getattr(self.clt, method)(
+ url, params, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, status)
return json.loads(response.content)
diff --git a/modoboa/lib/webutils.py b/modoboa/lib/web_utils.py
similarity index 97%
rename from modoboa/lib/webutils.py
rename to modoboa/lib/web_utils.py
index 9d2b37c8a..947c32503 100644
--- a/modoboa/lib/webutils.py
+++ b/modoboa/lib/web_utils.py
@@ -3,16 +3,18 @@
"""
This module contains extra functions/shortcuts used to render HTML.
"""
-import sys
-import re
+
import json
-from django.core.urlresolvers import reverse
-from django.http import HttpResponse, HttpResponseRedirect
-from django.contrib.auth.decorators import login_required
-from django.shortcuts import render
-from django.template.loader import render_to_string
+import re
+import sys
+
from django import template
from django.conf import settings
+from django.contrib.auth.decorators import login_required
+from django.core.urlresolvers import reverse
+from django.http import HttpResponse, HttpResponseRedirect
+from django.shortcuts import render
+from django.template.loader import render_to_string
def _render_to_string(request, tpl, user_context):
@@ -52,7 +54,7 @@ def getctx(status, level=1, callback=None, **kwargs):
def ajax_response(request, status="ok", respmsg=None,
- url=None, ajaxnav=False, norefresh=False,
+ url=None, ajaxnav=False, norefresh=False,
template=None, **kwargs):
"""Ajax response shortcut
@@ -74,7 +76,7 @@ def ajax_response(request, status="ok", respmsg=None,
ctx[k] = v
if template is not None:
content = _render_to_string(request, template, ctx)
- elif kwargs.has_key("content"):
+ elif "content" in kwargs:
content = kwargs["content"]
else:
content = ""
@@ -198,7 +200,7 @@ class NavigationParameters(object):
def store(self):
"""Store navigation parameters into session.
"""
- if not self.sessionkey in self.request.session:
+ if self.sessionkey not in self.request.session:
self.request.session[self.sessionkey] = {}
self._store_page()
navparams = self.request.session[self.sessionkey]
@@ -222,7 +224,7 @@ class NavigationParameters(object):
:param defaultvalue: default value if none is found
:return: parameter's value
"""
- if not self.sessionkey in self.request.session:
+ if self.sessionkey not in self.request.session:
return default_value
return self.request.session[self.sessionkey].get(param, default_value)
diff --git a/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py b/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py
index 7b40d5461..4f22bf79b 100644
--- a/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py
+++ b/modoboa/tools/pfxadmin_migrate/management/commands/migrate_from_postfixadmin.py
@@ -41,7 +41,7 @@ from django.db import transaction
import modoboa.core.models as core_models
import modoboa.extensions.admin.models as admin_models
-from modoboa.lib.emailutils import split_mailbox
+from modoboa.lib.email_utils import split_mailbox
from modoboa.extensions.admin import grant_access_to_all_objects
from modoboa.tools.pfxadmin_migrate import models as pf_models
diff --git a/modoboa/tools/pfxadmin_migrate/models.py b/modoboa/tools/pfxadmin_migrate/models.py
index c95f46216..f94e2f775 100755
--- a/modoboa/tools/pfxadmin_migrate/models.py
+++ b/modoboa/tools/pfxadmin_migrate/models.py
@@ -1,15 +1,20 @@
# coding: utf-8
+
+"""postfixadmin models."""
+
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
-# Feel free to rename the models, but don't rename db_table values or field names.
+# Feel free to rename the models, but don't rename db_table values or
+# field names.
#
-# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'
-# into your database.
+# Also note: You'll have to insert the output of 'django-admin.py
+# sqlcustom [appname]' into your database.
from django.db import models
+
class Admin(models.Model):
username = models.CharField(max_length=255, primary_key=True)
password = models.CharField(max_length=255)
diff --git a/modoboa/urls.py b/modoboa/urls.py
index 11c2bd5e9..2a4e3eef5 100644
--- a/modoboa/urls.py
+++ b/modoboa/urls.py
@@ -1,14 +1,16 @@
-from django.conf.urls import patterns, include, url
from django.conf import settings
+from django.conf.urls import patterns, include, url
+
+from modoboa.core import load_core_settings
from modoboa.core.extensions import exts_pool
from modoboa.lib import parameters, events
-from modoboa.core import load_core_settings
+
load_core_settings()
urlpatterns = patterns(
'',
- url(r'^$', 'modoboa.lib.webutils.topredirection', name="topredirection"),
+ url(r'^$', 'modoboa.lib.web_utils.topredirection', name="topredirection"),
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog',
{'packages': ('modoboa', ), }),
('', include('modoboa.core.urls', namespace="core")),
diff --git a/pylintrc b/pylintrc
new file mode 100644
index 000000000..7191ca562
--- /dev/null
+++ b/pylintrc
@@ -0,0 +1,329 @@
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS,migrations
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+[MESSAGES CONTROL]
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time. See also the "--disable" option for examples.
+#enable=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once).You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use"--disable=all --enable=classes
+# --disable=W"
+disable=no-init,no-member,locally-disabled,too-few-public-methods,too-many-public-methods,too-many-ancestors
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html. You can also give a reporter class, eg
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells whether to display a full report or only the messages
+reports=yes
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (RP0004).
+comment=no
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details
+#msg-template=
+
+
+[LOGGING]
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format
+logging-modules=logging
+
+
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )??$
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+# List of optional constructs for which whitespace checking is disabled
+no-space-check=trailing-comma,dict-separator
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input,file
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_,tz,logger,pk,qs
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Include a hint for the correct naming format with invalid-name
+include-naming-hint=no
+
+# Regular expression matching correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for function names
+function-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct variable names
+variable-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for variable names
+variable-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct constant names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Naming hint for constant names
+const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression matching correct attribute names
+attr-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for attribute names
+attr-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct argument names
+argument-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for argument names
+argument-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct class attribute names
+class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Naming hint for class attribute names
+class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Regular expression matching correct inline iteration names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Naming hint for inline iteration names
+inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
+
+# Regular expression matching correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Naming hint for class names
+class-name-hint=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression matching correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Naming hint for module names
+module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression matching correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for method names
+method-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=__.*__
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+
+[TYPECHECK]
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis
+ignored-modules=
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamically set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed. Python regular
+# expressions are accepted.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+dummy-variables-rgx=_$|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore
+ignored-argument-names=_.*
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branches=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=Exception
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 000000000..a7854adaf
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,3 @@
+[pep8]
+max-line-length = 80
+exclude = migrations