mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
@@ -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", {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+15
-7
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
+48
-27
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 "<p class='%s'>%s</p>" % (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
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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."))
|
||||
|
||||
@@ -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"]}
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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({})
|
||||
|
||||
@@ -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):
|
||||
</ul>
|
||||
<p>The first element of each line is mandatory and must be equal to one of the previous values.</p>
|
||||
<p>You can use a different character as separator.</p>
|
||||
""" % ''.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", {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")},
|
||||
]
|
||||
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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"})
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 [
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -3,4 +3,4 @@ from django.conf.urls import patterns
|
||||
urlpatterns = patterns(
|
||||
'modoboa.extensions.limits.views',
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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"})
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 []
|
||||
|
||||
@@ -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 <sender> <recipient ...>")
|
||||
raise CommandError(
|
||||
"usage: ./manage.py autoreply <sender> <recipient ...>")
|
||||
|
||||
sender = args[0]
|
||||
for fulladdress in args[1:]:
|
||||
|
||||
+1
-1
@@ -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):
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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'),
|
||||
]
|
||||
|
||||
|
||||
@@ -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"})
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)')
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<month>\w+)\s+(?P<day>\d+)\s+(?P<hour>\d+):(?P<min>\d+):(?P<sec>\d+)(?P<eol>.*)")
|
||||
re.compile(
|
||||
r"(?P<month>\w+)\s+(?P<day>\d+)\s+(?P<hour>\d+):(?P<min>\d+):(?P<sec>\d+)(?P<eol>.*)")
|
||||
self._hp_date_expr = \
|
||||
re.compile(r"(?P<year>\d+)-(?P<month>\d+)-(?P<day>\d+)T(?P<hour>\d+):(?P<min>\d+):(?P<sec>\d+)\.\d+\+\d+:\d+(?P<eol>.*)")
|
||||
re.compile(
|
||||
r"(?P<year>\d+)-(?P<month>\d+)-(?P<day>\d+)T(?P<hour>\d+):(?P<min>\d+):(?P<sec>\d+)\.\d+\+\d+:\d+(?P<eol>.*)")
|
||||
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))
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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"))
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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: <Service.client10@maaf.fr>\r\nTo: <TONIO@NGYN.ORG>\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?= <communication@communication.malakoffmederic.com>\r\nTo: Antoine Nguyen <tonio@ngyn.org>\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?= <communication@communication.malakoffmederic.com>\r\nTo: Antoine Nguyen <tonio@ngyn.org>\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"),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user