diff --git a/admin/forms.py b/admin/forms.py index 8ed87d6a4..96ffa742b 100644 --- a/admin/forms.py +++ b/admin/forms.py @@ -1,3 +1,4 @@ +# coding: utf8 from django import forms from modoboa.admin.models import * from django.utils.translation import ugettext as _ @@ -14,12 +15,26 @@ class DomainForm(forms.ModelForm): for f in ['name', 'quota']: self.fields[f].widget.attrs['size'] = 14 -class DomainAliasForm(forms.ModelForm): +class ProxyForm(forms.ModelForm): + def __init__(self, user, *args, **kwargs): + super(ProxyForm, self).__init__(*args, **kwargs) + + if user.is_superuser: + self._domain = None + else: + self._domain = user.mailbox_set.all()[0].domain + +class DomainAliasForm(ProxyForm): class Meta: model = DomainAlias fields = ('name', 'target', 'enabled') -class MailboxForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + super(DomainAliasForm, self).__init__(*args, **kwargs) + if self._domain is not None: + self.fields["target"].queryset = Domain.objects.filter(pk=self._domain.id) + +class MailboxForm(ProxyForm): quota = forms.IntegerField(label=_("Quota"), required=False, help_text=_("Mailbox quota in MB (default to domain quota if blank)")) password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput, @@ -38,6 +53,15 @@ class MailboxForm(forms.ModelForm): super(MailboxForm, self).__init__(*args, **kwargs) for f in ['name', 'address', 'quota', 'password1', 'password2']: self.fields[f].widget.attrs['size'] = 14 + if self._domain is not None: + self.fields["domain"].queryset = Domain.objects.filter(pk=self._domain.id) + self.fields["domain"].initial = self._domain + + if kwargs.has_key("instance"): + self.fields['quota'].initial = kwargs["instance"].quota + self.fields['enabled'].initial = kwargs["instance"].user.is_active + self.fields['password1'].initial = "é" + self.fields['password2'].initial = "é" def clean_address(self): if self.cleaned_data["address"].find('@') != -1: @@ -57,17 +81,37 @@ class MailboxForm(forms.ModelForm): quota=self.cleaned_data["quota"]) return m -class AliasForm(forms.ModelForm): +class AliasForm(ProxyForm): + domain = forms.ModelChoiceField(queryset=Domain.objects.all(), label=_("Domain"), + required=True, + empty_label=_("Select a domain"), + help_text=_("Select a domain in the list")) + def __init__(self, *args, **kwargs): - domain = None - if kwargs.has_key("domain"): - domain = kwargs["domain"] - del kwargs["domain"] super(AliasForm, self).__init__(*args, **kwargs) self.fields['address'].widget.attrs['size'] = 14 self.fields['mboxes'].widget.attrs['size'] = 5 - if domain: - self.fields['mboxes'].queryset = Mailbox.objects.filter(domain=domain.id) + + # This part is a little bit tricky. As 'domain' is not a + # member of Alias, we need to fill it manually. if + # self._domain exists, it means a DomainAdmin is going to + # create a new alias, we know how to fill the fields. Else, + # leave them empty (super admin mode). + if self._domain is not None: + self.fields["domain"].queryset = Domain.objects.filter(pk=self._domain.id) + elif len(args) < 2 or args[1]["domain"] == '': + self.fields['mboxes'].queryset = Mailbox.objects.none() + + # if 'instance' exists, it means we are modifying an existing + # record, we need to select the right value for + # 'domain'. Finally, if super user is doing the job, we need + # to fill 'mboxes' because it's currently empty (see upper). + if kwargs.has_key("instance") or self._domain is not None: + domain = self._domain is None and kwargs["instance"].domain or self._domain + self.fields["domain"].initial = domain + self.fields['mboxes'].queryset = Mailbox.objects.filter(domain=domain) + + self.fields.keyOrder = ['domain', 'address', 'mboxes', 'enabled'] class Meta: model = Alias @@ -81,8 +125,7 @@ class AliasForm(forms.ModelForm): def save(self, force_insert=False, force_update=False, commit=True): a = super(AliasForm, self).save(commit=False) if commit: - domain = self.cleaned_data["mboxes"][0].domain - a.full_address = "%s@%s" % (a.address, domain.name) + domain = self.cleaned_data["domain"].name a.save() return a diff --git a/admin/migrations/0007_auto__del_field_alias_full_address__chg_field_alias_address__del_field.py b/admin/migrations/0007_auto__del_field_alias_full_address__chg_field_alias_address__del_field.py new file mode 100644 index 000000000..670e220cb --- /dev/null +++ b/admin/migrations/0007_auto__del_field_alias_full_address__chg_field_alias_address__del_field.py @@ -0,0 +1,112 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Deleting field 'Alias.full_address' + db.delete_column('admin_alias', 'full_address') + + # Changing field 'Alias.address' + db.alter_column('admin_alias', 'address', self.gf('django.db.models.fields.CharField')(max_length=254)) + + # Deleting field 'Mailbox.full_address' + db.delete_column('admin_mailbox', 'full_address') + + + def backwards(self, orm): + + # User chose to not deal with backwards NULL issues for 'Alias.full_address' + raise RuntimeError("Cannot reverse this migration. 'Alias.full_address' and its values cannot be restored.") + + # Changing field 'Alias.address' + db.alter_column('admin_alias', 'address', self.gf('django.db.models.fields.CharField')(max_length=100)) + + # User chose to not deal with backwards NULL issues for 'Mailbox.full_address' + raise RuntimeError("Cannot reverse this migration. 'Mailbox.full_address' and its values cannot be restored.") + + + models = { + 'admin.alias': { + 'Meta': {'object_name': 'Alias'}, + 'address': ('django.db.models.fields.CharField', [], {'max_length': '254'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mboxes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['admin.Mailbox']", 'symmetrical': 'False'}) + }, + 'admin.domain': { + 'Meta': {'object_name': 'Domain'}, + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'quota': ('django.db.models.fields.IntegerField', [], {}) + }, + 'admin.domainalias': { + 'Meta': {'object_name': 'DomainAlias'}, + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}) + }, + 'admin.extension': { + 'Meta': {'object_name': 'Extension'}, + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '150'}) + }, + 'admin.mailbox': { + 'Meta': {'object_name': 'Mailbox'}, + 'address': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}), + 'gid': ('django.db.models.fields.IntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'path': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'quota': ('django.db.models.fields.IntegerField', [], {}), + 'uid': ('django.db.models.fields.IntegerField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + } + } + + complete_apps = ['admin'] diff --git a/admin/migrations/0007_auto__alter_full_address.py b/admin/migrations/0008_update_mailbox_path.py similarity index 91% rename from admin/migrations/0007_auto__alter_full_address.py rename to admin/migrations/0008_update_mailbox_path.py index 0a48e2f73..6caa2096d 100644 --- a/admin/migrations/0007_auto__alter_full_address.py +++ b/admin/migrations/0008_update_mailbox_path.py @@ -1,25 +1,25 @@ # encoding: utf-8 import datetime from south.db import db -from south.v2 import SchemaMigration +from south.v2 import DataMigration from django.db import models -class Migration(SchemaMigration): +class Migration(DataMigration): def forwards(self, orm): - db.alter_column("admin_alias", "full_address", - models.CharField(unique=True, max_length=254)) + for mb in orm.Mailbox.objects.all(): + mb.path = mb.path.replace(mb.domain.name + "/", "", 1) + mb.save() + def backwards(self, orm): - db.alter_column("admin_alias", "full_address", - models.CharField(max_length=150)) + raise RuntimeError("Cannot revert this migration") models = { 'admin.alias': { 'Meta': {'object_name': 'Alias'}, - 'address': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'address': ('django.db.models.fields.CharField', [], {'max_length': '254'}), 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'full_address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '254'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'mboxes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['admin.Mailbox']", 'symmetrical': 'False'}) }, @@ -47,7 +47,6 @@ class Migration(SchemaMigration): 'Meta': {'object_name': 'Mailbox'}, 'address': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}), - 'full_address': ('django.db.models.fields.CharField', [], {'max_length': '150'}), 'gid': ('django.db.models.fields.IntegerField', [], {}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), diff --git a/admin/models.py b/admin/models.py index fa20b606e..c9d869223 100644 --- a/admin/models.py +++ b/admin/models.py @@ -86,7 +86,6 @@ class Mailbox(models.Model): help_text=_("First name and last name of mailbox owner")) address = models.CharField(_('address'), max_length=100, help_text=_("Mailbox address (without the @domain.tld part)")) - full_address = models.CharField(max_length=150) password = models.CharField(_('password'), max_length=100) quota = models.IntegerField() uid = models.IntegerField() @@ -106,7 +105,12 @@ class Mailbox(models.Model): self.mdirroot = parameters.get_admin("MAILDIR_ROOT") def __str__(self): - return "%s" % (self.full_address) + return self.__full_address() + + def __full_address(self): + return "%s@%s" % (self.address, self.domain.name) + + full_address = property(__full_address) def create_dir(self): relpath = "%s/%s" % (self.domain.name, self.address) @@ -172,11 +176,11 @@ class Mailbox(models.Model): self.password = crypt_password(kwargs["password"]) self.uid = pwd.getpwnam(parameters.get_admin("VIRTUAL_UID")).pw_uid self.gid = pwd.getpwnam(parameters.get_admin("VIRTUAL_GID")).pw_gid - if kwargs.has_key("quota") and int(kwargs["quota"]) < self.domain.quota: + if kwargs.has_key("quota") and kwargs["quota"] is not None \ + and int(kwargs["quota"]) < self.domain.quota: self.quota = kwargs["quota"] else: self.quota = self.domain.quota - self.full_address = self.user.email try: for kw in ["enabled", "password", "quota"]: del kwargs[kw] @@ -207,9 +211,8 @@ class Mailbox(models.Model): } class Alias(models.Model): - address = models.CharField(_('address'), max_length=100, + address = models.CharField(_('address'), max_length=254, help_text=_("The alias address (without the domain part)")) - full_address = models.CharField(max_length=254, unique=True) mboxes = models.ManyToManyField(Mailbox, verbose_name=_('mailboxes'), help_text=_("The mailboxes this alias points to")) enabled = models.BooleanField(_('enabled'), @@ -220,6 +223,11 @@ class Alias(models.Model): ("view_aliases", "View aliases"), ) + def __domain(self): + return self.mboxes.all()[0].domain + + domain = property(__domain) + class Extension(models.Model): name = models.CharField(max_length=150) enabled = models.BooleanField(_('enabled'), diff --git a/admin/permissions.py b/admin/permissions.py index 8892d4c4a..727249b2c 100644 --- a/admin/permissions.py +++ b/admin/permissions.py @@ -7,7 +7,7 @@ from forms import * class Permissions(object): @staticmethod - def is_auhorized(user): + def is_authorized(user): return True def get_add_form(self, request): @@ -27,7 +27,7 @@ class SuperAdminsPerms(Permissions): role = "super_admins" @staticmethod - def is_auhorized(user): + def is_authorized(user): return user.is_superuser def get_add_form(self, request): @@ -141,7 +141,7 @@ def get_perms_class(user, role): cname += p.capitalize() cname += "Perms" if globals().has_key(cname): - if globals()[cname].is_auhorized(user): + if globals()[cname].is_authorized(user): return globals()[cname] return None res = events.raiseQueryEvent("PermsGetClass", role=role) diff --git a/admin/templatetags/admin_extras.py b/admin/templatetags/admin_extras.py index 6f6c587d7..a44cb1963 100644 --- a/admin/templatetags/admin_extras.py +++ b/admin/templatetags/admin_extras.py @@ -199,7 +199,7 @@ def mbalias_actions(user, alias): "img" : static_url("pics/edit.png"), "title" : _("Edit mailbox alias"), "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:300,y:250}}"} + "rel" : "{handler:'iframe',size:{x:300,y:300}}"} ] return render_actions(actions) diff --git a/admin/urls.py b/admin/urls.py index 7afac93d8..81d8822cf 100644 --- a/admin/urls.py +++ b/admin/urls.py @@ -22,7 +22,8 @@ urlpatterns = patterns('modoboa.admin.views', (r'^mbaliases/edit/(?P\d+)/$', 'editmbalias'), (r'^mbaliases/delete/$', 'delmbalias'), - (r'^domains/(?P\d+)/raw/$', 'mailboxes_raw'), + (r'^domains/(?P\d+)/rawmboxes/$', 'mailboxes_raw'), + #url(r'^domains/(?P\d+)/aliases/$', "aliases", name="full-aliases"), #url(r'^domains/(?P\d+)/aliases/(?P\d+)/$', "aliases", name="mbox-aliases"), diff --git a/admin/views.py b/admin/views.py index 46bac3a90..7ef9a1068 100644 --- a/admin/views.py +++ b/admin/views.py @@ -14,8 +14,7 @@ from modoboa import admin, userprefs from models import * from admin.permissions import * from modoboa.lib import crypt_password -from modoboa.lib import _render, _render_to_string, _ctx_ok, \ - getctx, events, parameters +from modoboa.lib import _render, ajax_response, getctx, events, parameters from modoboa.lib.models import Parameter import copy @@ -69,7 +68,11 @@ def domains(request): @login_required @permission_required("admin.add_domain") -def newdomain(request): +def newdomain(request, tplname="admin/adminform.html"): + commonctx = {"title" : _("New domain"), + "submit_label" : _("Create"), + "action" : reverse(newdomain), + "formid" : "domform"} if request.method == "POST": form = DomainForm(request.POST) error = None @@ -81,26 +84,26 @@ def newdomain(request): if domain.create_dir(): form.save() events.raiseEvent("CreateDomain", dom=domain) - ctx = _ctx_ok(reverse(admin.views.domains)) messages.info(request, _("Domain created"), fail_silently=True) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") + return ajax_response(request, url=reverse(admin.views.domains)) + error = _("Failed to initialise domain, check permissions") - content = _render_to_string(request, "admin/newdomain.html", { - "form" : form, "error" : error - }) - ctx = getctx("ko", content=content) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + commonctx["form"] = form + commonctx["error"] = error + return ajax_response(request, status="ko", template=tplname, **commonctx) form = DomainForm() - return _render(request, 'admin/newdomain.html', { - "form" : form - }) + commonctx["form"] = form + return _render(request, tplname, commonctx) @login_required @permission_required("admin.change_domain") -def editdomain(request, dom_id): +def editdomain(request, dom_id, tplname="admin/adminform.html"): domain = Domain.objects.get(pk=dom_id) + commonctx = {"title" : _("Domain editing"), + "submit_label" : _("Update"), + "action" : reverse(editdomain, args=[dom_id]), + "formid" : "domform"} olddomain = copy.deepcopy(domain) if request.method == "POST": form = DomainForm(request.POST, instance=domain) @@ -114,19 +117,16 @@ def editdomain(request, dom_id): error = _("Failed to rename domain, check permissions") if not error: form.save() - ctx = _ctx_ok(reverse(admin.views.domains)) messages.info(request, _("Domain modified"), fail_silently=True) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") - content = _render_to_string(request, "admin/editdomain.html", { - "domain" : domain, "form" : form, "error" : error - }) - ctx = getctx("ko", content=content) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + return ajax_response(request, url=reverse(admin.views.domains)) - form = DomainForm(instance=domain) - return _render(request, 'admin/editdomain.html', { - "domain" : domain, "form" : form - }) + commonctx["form"] = form + commonctx["error"] = error + return ajax_response(request, status="ko", template=tplname, **commonctx) + + form = DomainForm(instance=domain) + commonctx["form"] = form + return _render(request, tplname, commonctx) @login_required @permission_required("admin.delete_domain") @@ -147,10 +147,8 @@ def deldomain(request): dom.delete(keepdir=keepdir) msg = ungettext("Domain deleted", "Domains deleted", len(selection)) messages.info(request, msg, fail_silently=True) - ctx = _ctx_ok("") - else: - ctx = getctx("ko", error=error) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + return ajax_response(request) + return ajax_response(request, status="ko", error=error) @login_required @permission_required("admin.view_domaliases") @@ -167,9 +165,13 @@ def domaliases(request): @login_required @good_domain @permission_required("admin.add_domainalias") -def newdomalias(request): +def newdomalias(request, tplname="admin/adminform.html"): + commonctx = {"title" : _("New domain alias"), + "submit_label" : _("Create"), + "action" : reverse(newdomalias), + "formid" : "domaliasform"} if request.method == "POST": - form = DomainAliasForm(request.POST) + form = DomainAliasForm(request.user, request.POST) error = None if form.is_valid(): domalias = form.save(commit=False) @@ -181,32 +183,30 @@ def newdomalias(request): except IntegrityError: error = _("Alias with this name already exists") else: - ctx = _ctx_ok(reverse(admin.views.domaliases)) messages.info(request, _("Domain alias created"), fail_silently=True) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") - content = _render_to_string(request, "admin/newdomalias.html", { - "form" : form, "error" : error - }) - ctx = getctx("ko", content=content) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") - form = DomainAliasForm() - if not request.user.is_superuser: - form.fields["target"].queryset = \ - Domain.objects.filter(mailbox__user__pk=request.user.id) + return ajax_response(request, url=reverse(admin.views.domaliases)) + + commonctx["form"] = form + commonctx["error"] = error + return ajax_response(request, status="ko", template=tplname, **commonctx) + + form = DomainAliasForm(request.user) if request.GET.has_key("domid"): form.fields["target"].initial = request.GET["domid"] - return _render(request, 'admin/newdomalias.html', { - "form" : form - }) + commonctx["form"] = form + return _render(request, tplname, commonctx) @login_required @good_domain @permission_required("admin.change_domainalias") -def editdomalias(request, alias_id): +def editdomalias(request, alias_id, tplname="admin/adminform.html"): domalias = DomainAlias.objects.get(pk=alias_id) + commonctx = {"title" : _("Domain alias editing"), + "submit_label" : _("Update"), + "action" : reverse(editdomalias, args=[alias_id]), + "formid" : "domaliasform"} if request.method == "POST": - form = DomainAliasForm(request.POST, instance=domalias) + form = DomainAliasForm(request.user, request.POST, instance=domalias) error = None if form.is_valid(): ndomalias = form.save(commit=False) @@ -218,20 +218,15 @@ def editdomalias(request, alias_id): except IntegrityError: error = _("Alias with this name already exists") else: - ctx = _ctx_ok(reverse(admin.views.domaliases)) messages.info(request, _("Domain alias updated"), fail_silently=True) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") - content = _render_to_string(request, "admin/editdomalias.html", { - "form" : form, "error" : error - }) - ctx = getctx("ko", content=content) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + return ajax_response(request, url=reverse(admin.views.domaliases)) + commonctx["form"] = form + commonctx["error"] = error + return ajax_response(request, status="ko", template=tplname, **commonctx) - form = DomainAliasForm(instance=domalias) - return _render(request, 'admin/editdomalias.html', { - "form" : form, "domalias" : domalias - }) + form = DomainAliasForm(request.user, instance=domalias) + commonctx["form"] = form + return _render(request, tplname, commonctx) @login_required @good_domain @@ -241,8 +236,7 @@ def deldomalias(request): DomainAlias.objects.filter(id__in=selection).delete() msg = ungettext("Domain alias deleted", "Domain aliases deleted", len(selection)) messages.info(request, msg, fail_silently=True) - return HttpResponse(simplejson.dumps(_ctx_ok("")), - mimetype="application/json") + return ajax_response(request) @login_required @good_domain @@ -262,17 +256,26 @@ def mailboxes(request): @good_domain @permission_required("admin.view_mailboxes") def mailboxes_raw(request, dom_id=None): - mailboxes = Mailbox.objects.filter(domain=dom_id).exclude(user__id=request.user.id) + target = request.GET.has_key("target") and request.GET["target"] or "permissions" + if target == "permissions": + mailboxes = Mailbox.objects.filter(domain=dom_id).exclude(user__id=request.user.id) + else: + mailboxes = Mailbox.objects.filter(domain=dom_id) return _render(request, 'admin/mailboxes_raw.html', { - "mailboxes" : mailboxes + "mailboxes" : mailboxes, + "target" : target }) @login_required @good_domain @permission_required("admin.add_mailbox") -def newmailbox(request): +def newmailbox(request, tplname="admin/adminform.html"): + commonctx = {"title" : _("New mailbox"), + "submit_label" : _("Create"), + "action" : reverse(newmailbox), + "formid" : "mboxform"} if request.method == "POST": - form = MailboxForm(request.POST) + form = MailboxForm(request.user, request.POST) error = None if form.is_valid(): try: @@ -283,34 +286,29 @@ def newmailbox(request): events.raiseEvent("CreateMailbox", mbox=mb) messages.info(request, _("Mailbox created"), fail_silently=True) - ctx = _ctx_ok(reverse(admin.views.mailboxes) + "?domid=%d" % mb.domain.id) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") - content = _render_to_string(request, "admin/newmailbox.html", { - "form" : form, "error" : error - }) - ctx = getctx("ko", content=content) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + return ajax_response(request, url=reverse(admin.views.mailboxes) + "?domid=%d" % mb.domain.id) - form = MailboxForm() - if not request.user.is_superuser: - form.fields["domain"].queryset = \ - Domain.objects.filter(mailbox__user__pk=request.user.id) - if request.GET.has_key("domid"): - form.fields["domain"].initial = request.GET["domid"] - return _render(request, "admin/newmailbox.html", { - "form" : form - }) + commonctx["form"] = form + commonctx["error"] = error + return ajax_response(request, status="ko", template=tplname, **commonctx) + + form = MailboxForm(request.user) + commonctx["form"] = form + return _render(request, tplname, commonctx) @login_required @good_domain @permission_required("admin.change_mailbox") @transaction.commit_manually -def editmailbox(request, mbox_id=None): +def editmailbox(request, mbox_id=None, tplname="admin/adminform.html"): mb = Mailbox.objects.get(pk=mbox_id) + commonctx = {"title" : _("Mailbox editing"), + "submit_label" : _("Create"), + "action" : reverse(editmailbox, args=[mbox_id]), + "formid" : "mboxform"} if request.method == "POST": oldmb = copy.deepcopy(mb) - form = MailboxForm(request.POST, instance=mb) + form = MailboxForm(request.user, request.POST, instance=mb) error = None if form.is_valid(): try: @@ -323,25 +321,17 @@ def editmailbox(request, mbox_id=None): events.raiseEvent("ModifyMailbox", mbox=mb, oldmbox=oldmb) messages.info(request, _("Mailbox modified"), fail_silently=True) - ctx = _ctx_ok(reverse(admin.views.mailboxes) + "?domid=%d" % mb.domain.id) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") + return ajax_response(request, url=reverse(admin.views.mailboxes) + "?domid=%d" % mb.domain.id) error = _("Failed to rename mailbox, check permissions") if error is not None: transaction.rollback() - ctx = getctx("ko", content=_render_to_string(request, "admin/editmailbox.html", { - "mbox" : mb, "form" : form, "error" : error - })) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + commonctx["form"] = form + commonctx["error"] = error + return ajax_response(request, status="ko", template=tplname, **commonctx) - form = MailboxForm(instance=mb) - form.fields['quota'].initial = mb.quota - form.fields['enabled'].initial = mb.user.is_active - form.fields['password1'].initial = "é" - form.fields['password2'].initial = "é" - return _render(request, 'admin/editmailbox.html', { - "form" : form, "mbox" : mb - }) + form = MailboxForm(request.user, instance=mb) + commonctx["form"] = form + return _render(request, tplname, commonctx) @login_required @good_domain @@ -364,11 +354,9 @@ def delmailbox(request): mb.delete(keepdir=keepdir) msg = ungettext("Mailbox deleted", "Mailboxes deleted", len(selection)) messages.info(request, msg, fail_silently=True) - ctx = _ctx_ok("") - else: - ctx = getctx("ko", error=error) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") + return ajax_response(request) + + return ajax_response(request, status="ko", error=error) @login_required @good_domain @@ -385,9 +373,13 @@ def mbaliases(request): @login_required @good_domain @permission_required("admin.add_alias") -def newmbalias(request): +def newmbalias(request, tplname="admin/mbaliasform.html"): + commonctx = {"title" : _("New mailbox alias"), + "submit_label" : _("Create"), + "action" : reverse(newmbalias), + "formid" : "mbaliasform"} if request.method == "POST": - form = AliasForm(request.POST) + form = AliasForm(request.user, request.POST) error = None if form.is_valid(): try: @@ -396,33 +388,31 @@ def newmbalias(request): error = _("Alias with this name already exists") else: form.save_m2m() - ctx = _ctx_ok(reverse(admin.views.mbaliases) ) messages.info(request, _("Mailbox alias created"), fail_silently=True) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") - content = _render_to_string(request, "admin/newmbalias.html", { - "form" : form, "error" : error - }) - ctx = getctx("ko", content=content) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + return ajax_response(request, url=reverse(admin.views.mbaliases)) - if request.GET.has_key("domid"): - form = AliasForm(domain=Domain.objects.get(pk=request.GET["domid"])) - else: - form = AliasForm() + commonctx["form"] = form + commonctx["error"] = error + return ajax_response(request, status="ko", template=tplname, **commonctx) + + form = AliasForm(request.user) if request.GET.has_key("mbid"): form.fields["mboxes"].initial = request.GET["mbid"] - return _render(request, 'admin/newmbalias.html', { - "form" : form, "noerrors" : True - }) + commonctx["form"] = form + commonctx["noerrors"] = True + return _render(request, tplname, commonctx) @login_required @good_domain @permission_required("admin.change_alias") -def editmbalias(request, alias_id): +def editmbalias(request, alias_id, tplname="admin/mbaliasform.html"): alias = Alias.objects.get(pk=alias_id) + commonctx = {"title" : _("Mailbox alias editing"), + "submit_label" : _("Update"), + "action" : reverse(editmbalias, args=[alias.id]), + "formid" : "mbaliasform"} if request.method == "POST": - form = AliasForm(request.POST, instance=alias) + form = AliasForm(request.user, request.POST, instance=alias) error = None if form.is_valid(): try: @@ -431,20 +421,16 @@ def editmbalias(request, alias_id): error = _("Alias with this name already exists") else: form.save_m2m() - ctx = _ctx_ok(reverse(admin.views.mbaliases)) messages.info(request, _("Mailbox alias modified"), fail_silently=True) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") - content = _render_to_string(request, "admin/editmbalias.html", { - "alias" : alias, "form" : form, "error" : error - }) - ctx = getctx("ko", content=content) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") - form = AliasForm(instance=alias) - return _render(request, 'admin/editmbalias.html', { - "form" : form, "alias" : alias - }) + return ajax_response(request, url=reverse(admin.views.mbaliases)) + commonctx["form"] = form + commonctx["error"] = error + return ajax_response(request, status="ko", template=tplname, **commonctx) + + form = AliasForm(request.user, instance=alias) + commonctx["form"] = form + return _render(request, tplname, commonctx) @login_required @good_domain @@ -454,12 +440,11 @@ def delmbalias(request): Alias.objects.filter(id__in=selection).delete() msg = ungettext("Alias deleted", "Aliases deleted", len(selection)) messages.info(request, msg, fail_silently=True) - return HttpResponse(simplejson.dumps(_ctx_ok("")), - mimetype="application/json") + return ajax_response(request) @login_required @permission_required("auth.view_permissions") -def permissions(request): +def permissions(request, tplname='admin/permissions.html'): permtables = [] if request.user.is_superuser: permtables += [ @@ -476,7 +461,7 @@ def permissions(request): "content" : DomainAdminsPerms().get(request)} ] - return _render(request, 'admin/permissions.html', { + return _render(request, tplname, { "permtables" : permtables }) @@ -497,9 +482,7 @@ def add_permission(request): return HttpResponse(simplejson.dumps(data), mimetype="application/json") messages.info(request, _("Permission added."), fail_silently=True) - ctx = _ctx_ok(reverse(admin.views.permissions)) - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") + return ajax_response(request, url=reverse(admin.views.permissions)) @login_required def delete_permissions(request): @@ -516,7 +499,7 @@ def delete_permissions(request): @login_required @user_passes_test(lambda u: u.is_superuser) -def viewparameters(request): +def viewparameters(request, tplname='admin/parameters.html'): apps = sorted(parameters._params.keys()) gparams = [] for app in apps: @@ -533,7 +516,7 @@ def viewparameters(request): tmp["params"] += [newdef] gparams += [tmp] - return _render(request, 'admin/parameters.html', { + return _render(request, tplname, { "gparams" : gparams }) @@ -545,12 +528,11 @@ def saveparameters(request): continue app, name = pname.split('.') parameters.save_admin(name, v, app=app) - ctx = getctx("ok") - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + return ajax_response(request) @login_required @user_passes_test(lambda u: u.is_superuser) -def viewextensions(request): +def viewextensions(request, tplname='admin/extensions.html'): from modoboa.extensions import list_extensions from modoboa.lib import tables @@ -574,7 +556,7 @@ def viewextensions(request): ext["selection"] = False tbl = ExtensionsTable(exts) - return _render(request, 'admin/extensions.html', { + return _render(request, tplname, { "extensions" : tbl.render(request) }) diff --git a/extensions/webmail/lib.py b/extensions/webmail/lib.py index 12b26bf5f..9c8820b42 100644 --- a/extensions/webmail/lib.py +++ b/extensions/webmail/lib.py @@ -40,10 +40,10 @@ class WMtable(tables.Table): class EmailAddress(object): addr_exp = re.compile("([^<\(]+)[<(]([^>)]+)[>)]") - name_exp = re.compile('"([^"]+)"') + name_exp = re.compile('"?([^"]+)"?') def __init__(self, address): - self.fulladdress = address + self.fulladdress = u2u_decode.u2u_decode(address).strip("\r\t\n") self.name = self.address = None m = EmailAddress.addr_exp.match(self.fulladdress) if m is None: @@ -75,7 +75,7 @@ class IMAPheader(object): addr = EmailAddress(value) if "full" in kwargs.keys() and kwargs["full"]: return addr.fulladdress - return addr.name and addr.name or value + return addr.name and addr.name or addr.fulladdress @staticmethod def parse_address_list(values, **kwargs): @@ -454,11 +454,12 @@ class ImapEmail(Email): def render_headers(self, **kwargs): from django.template.loader import render_to_string - return render_to_string("webmail/headers.html", { + res = render_to_string("webmail/headers.html", { "headers" : self.headers, "folder" : kwargs["folder"], "mail_id" : kwargs["mail_id"], "attachments" : self.attachments != {} and self.attachments or None }) + return res class ReplyModifier(ImapEmail): def __init__(self, msg, user, form, all=False, **kwargs): diff --git a/extensions/webmail/templates/webmail/index.html b/extensions/webmail/templates/webmail/index.html index b084d849e..fb709aae7 100644 --- a/extensions/webmail/templates/webmail/index.html +++ b/extensions/webmail/templates/webmail/index.html @@ -154,7 +154,6 @@ var rtimer = null; window.addEvent("pageRefresh", function() { - console.log("timer : ", rtimer); if (rtimer) { clearInterval(rtimer); rtimer = null; diff --git a/lib/__init__.py b/lib/__init__.py index 5d9aac7bf..87c36671e 100644 --- a/lib/__init__.py +++ b/lib/__init__.py @@ -4,11 +4,13 @@ import sys import time import hashlib, crypt, string from random import Random +from django.http import HttpResponse from django.conf import settings from django.template import RequestContext from django.shortcuts import render_to_response from django.template.loader import render_to_string from django.utils.translation import ugettext as _ +from django.utils import simplejson from modoboa.lib import parameters def exec_cmd(cmd, **kwargs): @@ -19,10 +21,19 @@ def exec_cmd(cmd, **kwargs): return p.returncode, output def _render(request, tpl, user_context): + """Custom rendering function + + Just a wrapper which automatically adds a RequestContext instance + (useful to use settings variables like MEDIA_URL inside templates) + """ return render_to_response(tpl, user_context, context_instance=RequestContext(request)) def _render_to_string(request, tpl, user_context): + """Custom rendering function + + Same a mrender_render. + """ return render_to_string(tpl, user_context, context_instance=RequestContext(request)) @@ -63,6 +74,31 @@ def getctx(status, level=1, callback=None, **kwargs): ctx[kw] = v return ctx +def ajax_response(request, status="ok", url=None, template=None, **kwargs): + """Ajax response shortcut + + Simple shortcut that sends an JSON response. If a template is + provided, a 'content' field will be added to the response, + containing the result of this template rendering. + + :param request: a request object + :param status: the response status ('ok' or 'ko) + :param nexturl: url to display after receiving this response + :param template: eventual template's path + :param kwargs: dict used for template rendering + """ + ctx = {} + for k, v in kwargs.iteritems(): + ctx[k] = v + if template is not None: + content = _render_to_string(request, template, ctx) + else: + content = "" + return HttpResponse(simplejson.dumps({"status" : status, + "content" : content, + "url" : url}), + mimetype="application/json") + def decode(s, encodings=('utf8', 'latin1', 'windows-1252', 'ascii'), charset=None): if charset is not None: return s.decode(charset, 'ignore') diff --git a/lib/authbackends.py b/lib/authbackends.py index 76cd8b022..288fb479f 100644 --- a/lib/authbackends.py +++ b/lib/authbackends.py @@ -9,7 +9,7 @@ class SimpleBackend: except User.DoesNotExist: return None try: - mb = Mailbox.objects.get(full_address=username) + mb = user.mailbox_set.all()[0] except Exception: return None if not _check_password(password, mb.password): diff --git a/static/js/form.js b/static/js/form.js index 0c1ef26b8..029f6472d 100644 --- a/static/js/form.js +++ b/static/js/form.js @@ -1,85 +1,59 @@ window.addEvent("domready", function() { - sqbsubmit = function(obj, dest, id, callback, onerror) { - obj.set('send', { - onSuccess: function(responseText) { - var decode = JSON.decode(responseText); - if (decode.status == "ko") { - $(dest).set("html", decode.content); - $(id).addEvent("submit", callback); - if ($defined(onerror)) { - onerror(); - } + ajax_submit = function(event, error_callbacks) { + var form = event.target; + var myid = form.get("id"); + + event.stop(); + form.set('send', { + onSuccess: function(responseText) { + var response = JSON.decode(responseText); + + if (response.status == "ko") { + $(document.body).set("html", response.content); + $(document.body).addEvent("submit", default_submit); + if ($defined(error_callbacks[myid])) { + error_callbacks[myid](); + } } else { - if (decode.url != "") - parent.location.href = decode.url; + if (response.url != "") { + parent.location.href = response.url; + } parent.SqueezeBox.close(); } }, onFailure: function(xhr) { parent.document.getElement('iframe').setStyles({width: 800, height: 600}); - $(dest).set("html", xhr.responseText); + $(document.body).set("html", xhr.responseText); } }); - obj.send(); + form.send(); }; - generic_submit = function(obj, event, id, callback, onerror) { - event.stop(); - sqbsubmit(obj, "content", id, callback, onerror); - }; - domain_submit = function(event) { - generic_submit(this, event, "domform", domain_submit); - }; - domalias_submit = function(event) { - generic_submit(this, event, "domaliasform", domalias_submit); - }; - mailbox_submit = function(event) { - generic_submit(this, event, "mboxform", mailbox_submit); - }; - alias_submit = function(event) { - generic_submit(this, event, "aliasform", alias_submit); - }; - permission_submit = function(event) { - generic_submit(this, event, "permform", permission_submit, function() { - if ($defined($("id_domain"))) { - $("id_domain").options[0].setProperty("selected", "selected"); - domadminform_init(); + default_submit = function(event) { + var error_callbacks = { + mbaliasform: function() { + if ($("id_domain").value == 0) { + $("id_mboxes").set("html", ""); + } + mbaliasform_init(); + }, + armform: function() { + new Calendar({id_untildate: "Y-m-d"}, { + tweak: {x: -100, y:-100} + }); + }, + permform: function() { + if ($defined($("id_domain"))) { + $("id_domain").options[0].setProperty("selected", "selected"); + domadminform_init(); + } } - }); - }; - chpassword_submit = function(event) { - generic_submit(this, event, "chpwdform", chpassword_submit); + }; + + ajax_submit(event, error_callbacks); }; - arm_onerror = function() { - new Calendar({id_untildate: "Y-m-d"}, { - tweak: {x: -100, y:-100} - }); - }; - arm_submit = function(event) { - generic_submit(this, event, "armform", arm_submit, arm_onerror); - }; - - if ($("domform")) - $("domform").addEvent("submit", domain_submit); - - if ($("domaliasform")) - $("domaliasform").addEvent("submit", domalias_submit); - - if ($("mboxform")) - $("mboxform").addEvent("submit", mailbox_submit); - - if ($("aliasform")) - $("aliasform").addEvent("submit", alias_submit); - - if ($("permform")) - $("permform").addEvent("submit", permission_submit); - - if ($("chpwdform")) - $("chpwdform").addEvent("submit", chpassword_submit); - - if ($("armform")) - $("armform").addEvent("submit", arm_submit); + $(document.body).addEvent("submit", default_submit); $$("input[type!=submit]", "select").each(function(elt) { if ($(elt.name + "_helptext")) { diff --git a/static/js/mootools-more.js b/static/js/mootools-more.js index 6da8f7872..5d7c5eb1c 100644 --- a/static/js/mootools-more.js +++ b/static/js/mootools-more.js @@ -1,6 +1,6 @@ // MooTools: the javascript framework. -// Load this file's selection again by visiting: http://mootools.net/more/25f2abaf6eb14bf4513d6443e1c77284 -// Or build this file again with packager using: packager build More/More More/URI More/Hash More/Element.Position More/Form.Request More/Fx.Slide More/Drag.Move More/HtmlTable.Select More/Tips +// Load this file's selection again by visiting: http://mootools.net/more/c11b92fc7fef906755509fc9c331de22 +// Or build this file again with packager using: packager build More/URI More/Hash More/Form.Request More/Fx.Accordion More/Fx.Slide More/Drag.Move More/HtmlTable.Select More/Tips /* --- copyrights: @@ -35,9 +35,13 @@ a.base=new a(Array.from(document.getElements("base[href]",true)).getLast(),{base },set:function(b,c){if(!this[b]||this.hasOwnProperty(b)){this[b]=c;}return this;},empty:function(){a.each(this,function(c,b){delete this[b];},this);return this; },include:function(b,c){if(this[b]==undefined){this[b]=c;}return this;},map:function(b,c){return new a(Object.map(this,b,c));},filter:function(b,c){return new a(Object.filter(this,b,c)); },every:function(b,c){return Object.every(this,b,c);},some:function(b,c){return Object.some(this,b,c);},getKeys:function(){return Object.keys(this);},getValues:function(){return Object.values(this); -},toQueryString:function(b){return Object.toQueryString(this,b);}});a.alias({indexOf:"keyOf",contains:"hasValue"});})();(function(){var a=function(d,c){var e=[]; -Object.each(c,function(f){Object.each(f,function(g){d.each(function(h){e.push(h+"-"+g+(h=="border"?"-width":""));});});});return e;};var b=function(e,d){var c=0; -Object.each(d,function(g,f){if(f.test(e)){c=c+g.toInt();}});return c;};Element.implement({measure:function(h){var d=function(j){return !!(!j||j.offsetHeight||j.offsetWidth); +},toQueryString:function(b){return Object.toQueryString(this,b);}});a.alias({indexOf:"keyOf",contains:"hasValue"});})();Class.Mutators.Binds=function(a){return a; +};Class.Mutators.initialize=function(a){return function(){Array.from(this.Binds).each(function(b){var c=this[b];if(c){this[b]=c.bind(this);}},this);return a.apply(this,arguments); +};};Class.Occlude=new Class({occlude:function(c,b){b=document.id(b||this.element);var a=b.retrieve(c||this.property);if(a&&this.occluded!=null){return this.occluded=a; +}this.occluded=false;b.store(c||this.property,this);return this.occluded;}});Class.refactor=function(b,a){Object.each(a,function(e,d){var c=b.prototype[d]; +if(c&&c.$origin){c=c.$origin;}if(c&&typeof e=="function"){b.implement(d,function(){var f=this.previous;this.previous=c;var g=e.apply(this,arguments);this.previous=f; +return g;});}else{b.implement(d,e);}});return b;};(function(){var a=function(d,c){var e=[];Object.each(c,function(f){Object.each(f,function(g){d.each(function(h){e.push(h+"-"+g+(h=="border"?"-width":"")); +});});});return e;};var b=function(e,d){var c=0;Object.each(d,function(g,f){if(f.test(e)){c=c+g.toInt();}});return c;};Element.implement({measure:function(h){var d=function(j){return !!(!j||j.offsetHeight||j.offsetWidth); };if(d(this)){return h.apply(this);}var g=this.getParent(),i=[],e=[];while(!d(g)&&g!=document.body){e.push(g.expose());g=g.getParent();}var f=this.expose(); var c=h.apply(this);f();e.each(function(j){j();});return c;},expose:function(){if(this.getStyle("display")!="none"){return function(){};}var c=this.style.cssText; this.setStyles({display:"block",position:"absolute",visibility:"hidden"});return function(){this.style.cssText=c;}.bind(this);},getDimensions:function(c){c=Object.merge({computeSize:false},c); @@ -62,11 +66,7 @@ var i={left:"x",top:"y"};["minimum","maximum"].each(function(t){["left","top"].e }});});if(f.getStyle("position")=="fixed"||g.relFixedPosition){var m=window.getScroll();j.top+=m.y;j.left+=m.x;}if(g.ignoreScroll){var p=f.getScroll(); j.top-=p.y;j.left-=p.x;}if(g.ignoreMargins){j.left+=(g.edge.x=="right"?n["margin-right"]:g.edge.x=="center"?-n["margin-left"]+((n["margin-right"]+n["margin-left"])/2):-n["margin-left"]); j.top+=(g.edge.y=="bottom"?n["margin-bottom"]:g.edge.y=="center"?-n["margin-top"]+((n["margin-bottom"]+n["margin-top"])/2):-n["margin-top"]);}j.left=Math.ceil(j.left); -j.top=Math.ceil(j.top);if(g.returnPos){return j;}else{this.setStyles(j);}return this;}});})();Class.Mutators.Binds=function(a){return a;};Class.Mutators.initialize=function(a){return function(){Array.from(this.Binds).each(function(b){var c=this[b]; -if(c){this[b]=c.bind(this);}},this);return a.apply(this,arguments);};};Class.Occlude=new Class({occlude:function(c,b){b=document.id(b||this.element);var a=b.retrieve(c||this.property); -if(a&&this.occluded!=null){return this.occluded=a;}this.occluded=false;b.store(c||this.property,this);return this.occluded;}});Class.refactor=function(b,a){Object.each(a,function(e,d){var c=b.prototype[d]; -if(c&&c.$origin){c=c.$origin;}if(c&&typeof e=="function"){b.implement(d,function(){var f=this.previous;this.previous=c;var g=e.apply(this,arguments);this.previous=f; -return g;});}else{b.implement(d,e);}});return b;};var IframeShim=new Class({Implements:[Options,Events,Class.Occlude],options:{className:"iframeShim",src:'javascript:false;document.write("");',display:false,zIndex:null,margin:0,offset:{x:0,y:0},browsers:((Browser.ie&&Browser.version==6)||(Browser.firefox&&Browser.version<3&&Browser.Platform.mac))},property:"IframeShim",initialize:function(b,a){this.element=document.id(b); +j.top=Math.ceil(j.top);if(g.returnPos){return j;}else{this.setStyles(j);}return this;}});})();var IframeShim=new Class({Implements:[Options,Events,Class.Occlude],options:{className:"iframeShim",src:'javascript:false;document.write("");',display:false,zIndex:null,margin:0,offset:{x:0,y:0},browsers:((Browser.ie&&Browser.version==6)||(Browser.firefox&&Browser.version<3&&Browser.Platform.mac))},property:"IframeShim",initialize:function(b,a){this.element=document.id(b); if(this.occlude()){return this.occluded;}this.setOptions(a);this.makeShim();return this;},makeShim:function(){if(this.options.browsers){var c=this.element.getStyle("zIndex").toInt(); if(!c){c=1;var b=this.element.getStyle("position");if(b=="static"||!b){this.element.setStyle("position","relative");}this.element.setStyle("zIndex",c); }c=((this.options.zIndex!=null||this.options.zIndex===0)&&c>this.options.zIndex)?this.options.zIndex:c-1;if(c<0){c=1;}this.shim=new Element("iframe",{src:this.options.src,scrolling:"no",frameborder:0,styles:{zIndex:c,position:"absolute",border:"none",filter:"progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)"},"class":this.options.className}).store("IframeShim",this); @@ -135,7 +135,34 @@ var c=a.update||a.updateId;var b=this.retrieve("form.request");if(c){if(b){b.upd }this.store("form.request:options",a.options);}return this;},get:function(){var a=Array.link(arguments,{options:Type.isObject,update:Type.isElement,updateId:Type.isString}); var b=a.update||a.updateId;if(a.options||b||!this.retrieve("form.request")){if(a.options||!this.retrieve("form.request:options")){this.set("form.request",a.options); }if(b){this.set("form.request",b);}this.store("form.request",new Form.Request(this,this.retrieve("form.request:update"),this.retrieve("form.request:options"))); -}return this.retrieve("form.request");}};Element.implement({formUpdate:function(b,a){this.get("formRequest",b,a).send();return this;}});})();Fx.Slide=new Class({Extends:Fx,options:{mode:"vertical",wrapper:false,hideOverflow:true,resetHeight:false},initialize:function(b,a){this.addEvent("complete",function(){this.open=(this.wrapper["offset"+this.layout.capitalize()]!=0); +}return this.retrieve("form.request");}};Element.implement({formUpdate:function(b,a){this.get("formRequest",b,a).send();return this;}});})();Fx.Elements=new Class({Extends:Fx.CSS,initialize:function(b,a){this.elements=this.subject=$$(b); +this.parent(a);},compute:function(g,h,j){var c={};for(var d in g){var a=g[d],e=h[d],f=c[d]={};for(var b in a){f[b]=this.parent(a[b],e[b],j);}}return c; +},set:function(b){for(var c in b){if(!this.elements[c]){continue;}var a=b[c];for(var d in a){this.render(this.elements[c],d,a[d],this.options.unit);}}return this; +},start:function(c){if(!this.check(c)){return this;}var h={},j={};for(var d in c){if(!this.elements[d]){continue;}var f=c[d],a=h[d]={},g=j[d]={};for(var b in f){var e=this.prepare(this.elements[d],b,f[b]); +a[b]=e.from;g[b]=e.to;}}return this.parent(h,j);}});Fx.Accordion=new Class({Extends:Fx.Elements,options:{fixedHeight:false,fixedWidth:false,display:0,show:false,height:true,width:false,opacity:true,alwaysHide:false,trigger:"click",initialDisplayFx:true,returnHeightToAuto:true},initialize:function(){var d=function(e){return e!=null; +};var c=Array.link(arguments,{container:Type.isElement,options:Type.isObject,togglers:d,elements:d});this.parent(c.elements,c.options);this.togglers=$$(c.togglers); +this.previous=-1;this.internalChain=new Chain();if(this.options.alwaysHide){this.options.wait=true;}if(this.options.show||this.options.show===0){this.options.display=false; +this.previous=this.options.show;}if(this.options.start){this.options.display=false;this.options.show=false;}this.effects={};if(this.options.opacity){this.effects.opacity="fullOpacity"; +}if(this.options.width){this.effects.width=this.options.fixedWidth?"fullWidth":"offsetWidth";}if(this.options.height){this.effects.height=this.options.fixedHeight?"fullHeight":"scrollHeight"; +}for(var b=0,a=this.togglers.length;b=0?a-1:0)).chain(d);}else{d();}return this;},detach:function(b){var a=function(c){c.removeEvent(this.options.trigger,c.retrieve("accordion:display")); +}.bind(this);if(!b){this.togglers.each(a);}else{a(b);}return this;},display:function(a,b){if(!this.check(a,b)){return this;}b=b!=null?b:true;a=(typeOf(a)=="element")?this.elements.indexOf(a):a; +if(a==this.previous&&!this.options.alwaysHide){return this;}if(this.options.returnHeightToAuto){var d=this.elements[this.previous];if(d&&!this.selfHidden){for(var c in this.effects){d.setStyle(c,d[this.effects[c]]); +}}}if((this.timer&&this.options.wait)||(a===this.previous&&!this.options.alwaysHide)){return this;}this.previous=a;var e={};this.elements.each(function(h,g){e[g]={}; +var f;if(g!=a){f=true;}else{if(this.options.alwaysHide&&((h.offsetHeight>0&&this.options.height)||h.offsetWidth>0&&this.options.width)){f=true;this.selfHidden=true; +}}this.fireEvent(f?"background":"active",[this.togglers[g],h]);for(var j in this.effects){e[g][j]=f?0:h[this.effects[j]];}},this);this.internalChain.clearChain(); +this.internalChain.chain(function(){if(this.options.returnHeightToAuto&&!this.selfHidden){var f=this.elements[a];if(f){f.setStyle("height","auto");}}}.bind(this)); +return b?this.start(e):this.set(e);}});var Accordion=new Class({Extends:Fx.Accordion,initialize:function(){this.parent.apply(this,arguments);var a=Array.link(arguments,{container:Type.isElement}); +this.container=a.container;},addSection:function(c,b,e){c=document.id(c);b=document.id(b);var d=this.togglers.contains(c);var a=this.togglers.length;if(a&&(!d||e)){e=e!=null?e:a-1; +c.inject(this.togglers[e],"before");b.inject(c,"after");}else{if(this.container&&!d){c.inject(this.container);b.inject(this.container);}}return this.parent.apply(this,arguments); +}});Fx.Slide=new Class({Extends:Fx,options:{mode:"vertical",wrapper:false,hideOverflow:true,resetHeight:false},initialize:function(b,a){this.addEvent("complete",function(){this.open=(this.wrapper["offset"+this.layout.capitalize()]!=0); if(this.open&&this.options.resetHeight){this.wrapper.setStyle("height","");}},true);this.element=this.subject=document.id(b);this.parent(a);var d=this.element.retrieve("wrapper"); var c=this.element.getStyles("margin","position","overflow");if(this.options.hideOverflow){c=Object.append(c,{overflow:"hidden"});}if(this.options.wrapper){d=document.id(this.options.wrapper).setStyles(c); }this.wrapper=d||new Element("div",{styles:c}).wraps(this.element);this.element.store("wrapper",this.wrapper).setStyle("margin",0);this.now=[];this.open=true; diff --git a/templates/admin/add_domain_admin.html b/templates/admin/add_domain_admin.html index d6217ca2c..17ab5d126 100644 --- a/templates/admin/add_domain_admin.html +++ b/templates/admin/add_domain_admin.html @@ -12,9 +12,10 @@ domadminform_init = function() { } {% url domains as doms_url %} new Request({ - url: "{{ doms_url }}" + domid + "/raw/", + url: "{% url domains %}" + domid + "/rawmboxes/?target=permissions", method: "get", onSuccess: function(data) { $("id_user").set("html", data); + $("id_user").options[0].setProperty("selected", "selected"); } }).send(); }); diff --git a/templates/admin/newmailbox.html b/templates/admin/adminform.html similarity index 57% rename from templates/admin/newmailbox.html rename to templates/admin/adminform.html index 8c5f5f487..52c3ccdc5 100644 --- a/templates/admin/newmailbox.html +++ b/templates/admin/adminform.html @@ -1,19 +1,15 @@ {% extends "layout_simple.html" %} -{% load i18n %} {% block html_head %} {% endblock %} {% block content %} -

{% trans "New mailbox" %}

- +

{{ title }}

{% if error %}
{{ error }}
{% endif %} -{% url admin.views.newmailbox as box_url %} -
+ {% include "common/genericform.html" %}
-
- +
{% endblock %} diff --git a/templates/admin/domains_header.html b/templates/admin/domains_header.html index 0b5baafe3..68054b53b 100644 --- a/templates/admin/domains_header.html +++ b/templates/admin/domains_header.html @@ -21,13 +21,13 @@ window.addEvent("domready", function() { $$("a[name=remove]").addEvent("click", function(evt) { evt.stop(); - if (objtable.selectedRows.length == 0) { + if (objtable._selectedRows.length == 0) { return; } var type = $("objects").get("name"); var selection = ""; - objtable.selectedRows.each(function(item) { + objtable._selectedRows.each(function(item) { if (selection != "") { selection += ","; } diff --git a/templates/admin/editdomain.html b/templates/admin/editdomain.html deleted file mode 100644 index 2277c0884..000000000 --- a/templates/admin/editdomain.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "layout_simple.html" %} -{% block html_head %} - -{% endblock %} -{% block content %} -{% load i18n %} -

{% trans "Domain" %} {{ domain.name }}

- -{% if error %}
{{ error }}
{% endif %} -{% url admin.views.editdomain domain.id as dom_url %} -
- {% include "common/genericform.html" %} - -
- -
-
-{% endblock %} diff --git a/templates/admin/editdomalias.html b/templates/admin/editdomalias.html deleted file mode 100644 index 1827d0fa2..000000000 --- a/templates/admin/editdomalias.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "layout_simple.html" %} -{% load i18n %} -{% block html_head %} - -{% endblock %} -{% block content %} -

{% trans "Edit domain alias" %}

-{% if error %}
{{ error }}
{% endif %} -{% url admin.views.editdomalias domalias.id as box_url %} -
- {% include "common/genericform.html" %} -
-
- -
-
-{% endblock %} diff --git a/templates/admin/editmailbox.html b/templates/admin/editmailbox.html deleted file mode 100644 index 97da28461..000000000 --- a/templates/admin/editmailbox.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "layout_simple.html" %} -{% load i18n %} -{% block html_head %} - -{% endblock %} -{% block content %} -

{% trans "Mailbox" %} {{ mbox.name }}

-{% if error %}
{{ error }}
{% endif %} -{% url admin.views.editmailbox mbox.id as box_url %} -
- {% include "common/genericform.html" %} -
- -
- -
-
-{% endblock %} diff --git a/templates/admin/editmbalias.html b/templates/admin/editmbalias.html deleted file mode 100644 index b6fae716b..000000000 --- a/templates/admin/editmbalias.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "layout_simple.html" %} -{% block html_head %} - -{% endblock %} -{% block content %} -{% load i18n %} -

Mailbox alias {{ alias.address }}

-{% if error %}
{{ error }}
{% endif %} -{% url admin.views.editmbalias alias.id as edit_url %} -
- {% include "common/genericform.html" %} -
-
- -
-
-{% endblock %} diff --git a/templates/admin/mailboxes.html b/templates/admin/mailboxes.html index b3324aeb6..09226d126 100644 --- a/templates/admin/mailboxes.html +++ b/templates/admin/mailboxes.html @@ -18,7 +18,7 @@ {% for mb in mailboxes %} {{ mb.name }} - {{ mb.address }}@{{ mb.domain.name }} + {{ mb.full_address }} {{ mb.alias_set.all|length }} {{ mb.quota }} {% trans "MB" %} {{ mb.user.is_active|yesno:_("yes,no") }} diff --git a/templates/admin/mailboxes_raw.html b/templates/admin/mailboxes_raw.html index 3c4d9974a..9723f4a20 100644 --- a/templates/admin/mailboxes_raw.html +++ b/templates/admin/mailboxes_raw.html @@ -1,5 +1,11 @@ {% load i18n %} +{% if target == "permissions" %} +{% endif %} {% for mb in mailboxes %} +{% if target == "permissions" %} +{% else %} + +{% endif %} {% endfor %} diff --git a/templates/admin/mbaliases.html b/templates/admin/mbaliases.html index f9fddf4b4..2ab5f8904 100644 --- a/templates/admin/mbaliases.html +++ b/templates/admin/mbaliases.html @@ -2,7 +2,7 @@ {% load i18n admin_extras %} {% block pagetitle %}{% trans "Mailbox aliases" %}{% endblock %} {% block subcontent %} -
+
{% if not aliases %}
{% trans "No mailbox alias defined." %}
{% else %} @@ -15,7 +15,7 @@ {% for alias in aliases %} - {{ alias.full_address }} + {{ alias.address }}@{{ alias.domain }} {% for mb in alias.mboxes.all %} {{ mb.full_address }}
diff --git a/templates/admin/mbaliasform.html b/templates/admin/mbaliasform.html new file mode 100644 index 000000000..c41a2cb45 --- /dev/null +++ b/templates/admin/mbaliasform.html @@ -0,0 +1,24 @@ +{% extends "admin/adminform.html" %} +{% block html_head %} +{{ block.super }} + +{% endblock %} diff --git a/templates/admin/newdomain.html b/templates/admin/newdomain.html deleted file mode 100644 index be753367f..000000000 --- a/templates/admin/newdomain.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "layout_simple.html" %} -{% block html_head %} - -{% endblock %} -{% block content %} -{% load i18n %} -

{% trans "New domain" %}

- -{% if error %}
{{ error }}
{% endif %} -{% url admin.views.newdomain as dom_url %} -
- {% include "common/genericform.html" %} - -
- -
-
-{% endblock %} - diff --git a/templates/admin/newdomalias.html b/templates/admin/newdomalias.html deleted file mode 100644 index f670c5a5b..000000000 --- a/templates/admin/newdomalias.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "layout_simple.html" %} -{% load i18n %} -{% block html_head %} - -{% endblock %} -{% block content %} -

{% trans "New domain alias" %}

-{% if error %}
{{ error }}
{% endif %} -{% url admin.views.newdomalias domain as box_url %} -
- {% include "common/genericform.html" %} -
-
- -
-
-{% endblock %} diff --git a/templates/admin/newmbalias.html b/templates/admin/newmbalias.html deleted file mode 100644 index 31c2fec03..000000000 --- a/templates/admin/newmbalias.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "layout_simple.html" %} -{% load i18n %} -{% block html_head %} - -{% endblock %} -{% block content %} -

{% trans "New mailbox alias" %}

-{% if error %}
{{ error }}
{% endif %} -{% url admin.views.newmbalias as box_url %} -
- {% include "common/genericform.html" %} -
-
- -
-
-{% endblock %} diff --git a/userprefs/templatetags/uprefs_extras.py b/userprefs/templatetags/uprefs_extras.py index 48c37b3ab..8e0268d52 100644 --- a/userprefs/templatetags/uprefs_extras.py +++ b/userprefs/templatetags/uprefs_extras.py @@ -20,7 +20,7 @@ def options_menu(user): "img" : static_url("pics/edit.png"), "label" : _("Change password"), "class" : "boxed", - "rel" : "{handler:'iframe',size:{x:350,y:220}}"}, + "rel" : "{handler:'iframe',size:{x:360,y:200}}"}, {"name" : "preferences", "img" : static_url("pics/user.png"), "label" : _("Preferences"), diff --git a/userprefs/views.py b/userprefs/views.py index af87f57d7..e1257783a 100644 --- a/userprefs/views.py +++ b/userprefs/views.py @@ -1,11 +1,10 @@ import copy from django.core.urlresolvers import reverse -from django.http import HttpResponse, HttpResponseRedirect +from django.http import HttpResponseRedirect from django.contrib.auth.decorators \ import login_required -from django.utils import simplejson from django.utils.translation import ugettext as _ -from modoboa.lib import _render, _ctx_ok, _ctx_ko, getctx, parameters +from modoboa.lib import _render, ajax_response, parameters from modoboa.lib import crypt_password from forms import ChangePasswordForm from modoboa.admin.models import Mailbox @@ -15,7 +14,7 @@ def index(request): return HttpResponseRedirect(reverse(preferences)) @login_required -def changepassword(request): +def changepassword(request, tplname="userprefs/chpassword.html"): if request.user.id == 1: target = request.user else: @@ -29,13 +28,8 @@ def changepassword(request): else: target.set_password(request.POST["confirmation"]) target.save() - ctx = _ctx_ok("") - return HttpResponse(simplejson.dumps(ctx), - mimetype="application/json") - ctx = _ctx_ko("userprefs/chpassword.html", { - "form" : form, "error" : error - }) - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + return ajax_response(request) + return ajax_response(request, status="ko", template=tplname, form=form, error=error) form = ChangePasswordForm(target) return _render(request, 'userprefs/chpassword.html', { @@ -70,6 +64,5 @@ def savepreferences(request): app, name = pname.split('.') parameters.save_user(request.user, name, v, app=app) - ctx = getctx("ok") - return HttpResponse(simplejson.dumps(ctx), mimetype="application/json") + return ajax_response(request)