Quota support improvements.

* A mailbox can now use the domain's default quota. (fixes #402)
* Possibility to define unlimited quotas (fixes #343)
This commit is contained in:
Antoine Nguyen
2013-04-24 10:08:21 +02:00
parent 66d684ed7e
commit eedaff6f6a
8 changed files with 50 additions and 42 deletions
+20 -8
View File
@@ -84,6 +84,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
q.save()
old_mail_homes = dict((mb.id, mb.mail_home) for mb in d.mailbox_set.all())
d.save()
Mailbox.objects.filter(domain=d, use_domain_quota=True).update(quota=d.quota)
if old_mail_homes is not None:
for mb in d.mailbox_set.all():
mb.rename_dir(old_mail_homes[mb.id])
@@ -511,9 +512,10 @@ class AccountFormMail(forms.Form, DynamicForm):
quota = forms.IntegerField(
label=ugettext_lazy("Quota"),
required=False,
help_text=_("Quota in MB for this mailbox. Leave empty to use the value defined at domain level."),
help_text=_("Quota in MB for this mailbox. Define a custom value or use domain's default one. Leave empty to define an unlimited value (not allowed for domain administrators)."),
widget=forms.widgets.TextInput(attrs={"class": "span1"})
)
quota_act = forms.BooleanField(required=False)
aliases = forms.EmailField(
label=ugettext_lazy("Alias(es)"),
required=False,
@@ -534,8 +536,12 @@ class AccountFormMail(forms.Form, DynamicForm):
name = "aliases_%d" % cpt
self._create_field(forms.EmailField, name, alias.full_address)
cpt += 1
self.fields["email"].initial = self.mb.full_address
self.fields["quota"].initial = self.mb.quota
self.fields["email"].initial = self.mb.full_address
self.fields["quota_act"].initial = self.mb.use_domain_quota
if not self.mb.use_domain_quota and self.mb.quota:
self.fields["quota"].initial = self.mb.quota
else:
self.fields["quota_act"].initial = True
if len(args) and isinstance(args[0], QueryDict):
self._load_from_qdict(args[0], "aliases", forms.EmailField)
@@ -557,15 +563,19 @@ class AccountFormMail(forms.Form, DynamicForm):
if not user.can_access(domain):
raise PermDeniedException
if self.cleaned_data["quota_act"]:
self.cleaned_data["quota"] = None
if not hasattr(self, "mb") or self.mb is None:
try:
self.mb = Mailbox.objects.get(address=locpart, domain=domain)
except Mailbox.DoesNotExist:
events.raiseEvent("CanCreate", user, "mailboxes")
self.mb = Mailbox()
self.mb.save_from_user(locpart, domain, account,
self.cleaned_data["quota"],
owner=user)
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"],
user.has_perm("admin.add_domain"))
self.mb.save()
grant_access_to_object(user, self.mb, is_owner=True)
events.raiseEvent("CreateMailbox", user, self.mb)
if user.is_superuser and not self.mb.user.has_perm("admin.add_domain"):
@@ -587,7 +597,9 @@ class AccountFormMail(forms.Form, DynamicForm):
self.mb.address = locpart
q.username = self.mb.full_address
q.save()
self.mb.set_quota(self.cleaned_data["quota"], user.has_perm("admin.add_domain"))
self.mb.use_domain_quota = self.cleaned_data["quota_act"]
override_rules = True if not self.mb.quota or user.has_perm("admin.add_domain") else False
self.mb.set_quota(self.cleaned_data["quota"], override_rules)
self.mb.save()
if old_mail_home is not None:
# Problem here: the mailbox has already been saved so,
+21 -23
View File
@@ -440,8 +440,9 @@ class User(DUser):
raise AdminError(_("Account import failed (%s): domain does not exist" % self.username))
if not user.can_access(domain):
raise PermDeniedException
mb = Mailbox()
mb.save_from_user(mailbox, domain, self, owner=user)
mb = Mailbox(address=mailbox, domain=domain, user=self, use_domain_quota=True)
mb.set_quota(override_rules=user.has_perm("admin.change_domain"))
mb.save()
self.save()
if grpname == "DomainAdmins":
for domname in row[8:]:
@@ -586,6 +587,7 @@ class Domain(DatesAware):
def to_csv(self, csvwriter):
csvwriter.writerow(["domain", self.name, self.quota, self.enabled])
class DomainAlias(DatesAware):
name = models.CharField(ugettext_lazy("name"), max_length=100, unique=True,
help_text=ugettext_lazy("The alias name"))
@@ -641,7 +643,8 @@ class Mailbox(DatesAware):
ugettext_lazy('address'), max_length=100,
help_text=ugettext_lazy("Mailbox address (without the @domain.tld part)")
)
quota = models.IntegerField()
quota = models.PositiveIntegerField()
use_domain_quota = models.BooleanField(default=False)
domain = models.ForeignKey(Domain)
user = models.ForeignKey(User)
@@ -718,20 +721,25 @@ class Mailbox(DatesAware):
if code:
raise AdminError(_("Failed to remove mailbox: %s" % output))
def set_quota(self, value, override_domain=False):
def set_quota(self, value=None, override_rules=False):
"""Set or update quota's value for this mailbox.
:param integer value: the quota's value
:param bool override_domain: allow to override domain's limit or not
:param bool override_rules: allow to override defined quota rules
"""
if value is None:
self.quota = self.domain.quota
elif int(value) > self.domain.quota and not override_domain:
if self.use_domain_quota:
self.quota = self.domain.quota
else:
self.quota = 0
elif int(value) > self.domain.quota and not override_rules:
raise AdminError(
_("Quota is greater than the allowed domain's limit (%dM)" % self.domain.quota)
)
else:
self.quota = value
if not self.quota and not override_rules:
raise AdminError(_("A quota is required"))
def get_quota(self):
q = Quota.objects.get(username=self.full_address)
@@ -743,20 +751,9 @@ class Mailbox(DatesAware):
q = Quota.objects.get(username=self.full_address)
return int(q.bytes / float(self.quota * 1048576) * 100)
def save_from_user(self, localpart, domain, user, quota=None, owner=None):
"""Simple save method called for automatic creations
:param localpart: the mailbox
:param domain: the associated Domain object
:param user: the associated User object
"""
self.address = localpart
self.domain = domain
self.user = user
self.set_quota(quota, True if (owner and owner.has_perm("admin.add_domain")) else False)
super(Mailbox, self).save()
Quota.objects.create(username=self.full_address)
def save(self, *args, **kwargs):
super(Mailbox, self).save(*args, **kwargs)
quota, created = Quota.objects.get_or_create(username=self.full_address)
def delete(self, keepdir=False):
"""Custom delete method
@@ -1030,8 +1027,9 @@ def populate_callback(user):
try:
mb = Mailbox.objects.get(domain=domain, address=localpart)
except Mailbox.DoesNotExist:
mb = Mailbox()
mb.save_from_user(localpart, domain, user)
mb = Mailbox(address=localpart, domain=domain, user=user, use_domain_quota=True)
mb.set_quota()
mb.save()
events.raiseEvent("CreateMailbox", sadmins[0], mb)
grant_access_to_object(sadmins[0], mb, True)
for su in sadmins[1:]:
+1 -1
View File
@@ -339,7 +339,7 @@ Identities.prototype = {
choices: get_domains_list
})
.dynamic_input();
activate_widget.call($("#id_quota_act"));
},
accountform_init: function() {
+1 -1
View File
@@ -1,4 +1,4 @@
{% load libextras %}
{% render_field form.email %}
{% render_field_appended form.quota _("MB") %}
{% include "common/generic_field.html" with field=form.quota help_display_mode="tooltip" appended_text=_("MB") activator=True activator_value=form.quota_act.value deactivate_if_empty=False activator_label=_("Use domain default value") %}
{% render_fields_group form "aliases" %}
+2 -1
View File
@@ -77,6 +77,7 @@ class AccountTestCase(ModoTestCase):
def test(self):
values = dict(username="tester@test.com", first_name="Tester", last_name="Toto",
password1="toto", password2="toto", role="SimpleUsers",
quota_act=True,
is_active=True, email="tester@test.com", stepid=2)
self.check_ajax_post(reverse("modoboa.admin.views.newaccount"), values)
@@ -236,7 +237,7 @@ class PermissionsTestCase(ModoTestCase):
True)
admin = User.objects.get(username="admin@test.com")
values = dict(username="admin@test.com", first_name="Admin", password1="", password2="",
is_active=True, email="admin@test.com")
quota=10, is_active=True, email="admin@test.com")
self.check_ajax_post(reverse("modoboa.admin.views.editaccount", args=[admin.id]), values)
self.assertEqual(admin.group, "DomainAdmins")
self.assertEqual(admin.can_access(Domain.objects.get(name="test.com")), True)
+1 -4
View File
@@ -118,10 +118,7 @@ $(document).ready(function() {
});
$(document).bind('domform_init', function() {
var checked = $('#id_spam_subject_tag2_act').attr('checked');
if (checked && checked == 'checked') {
$('#id_spam_subject_tag2').attr('disabled', true);
}
activate_widget.call($('#id_spam_subject_tag2_act'));
});
});
</script>""")
+3 -3
View File
@@ -107,9 +107,9 @@ def render_field_appended(field, text):
@register.simple_tag
def render_field_with_activator(field, activator_label=ugettext_lazy("activate")):
return render_to_string("common/generic_field.html", {
"field" : field, "help_display_mode" : "tooltip", "activator" : True,
"activator_label" : activator_label
})
"field": field, "help_display_mode": "tooltip", "activator": True,
"activator_label": activator_label, "deactivate_if_empty": True
})
@register.simple_tag
def render_and_hide_field(field):
+1 -1
View File
@@ -5,7 +5,7 @@
{% if appended_text %}<div class="input-append">{% endif %}
{{ field }}{% if appended_text %}<span class="add-on">{{ appended_text }}</span></div>{% endif %}{% if help_display_mode == "inline" and field.help_text %}<span class="help-inline">{{ field.help_text }}</span>{% endif %}
{% if activator %}<label class="checkbox inline" for="id_{{ field.name }}_act">
<input type="checkbox" class="activator" id="id_{{ field.name }}_act" name="{{ field.name }}_act" {% if field.value == None %}checked='checked'{% endif %}/>{{ activator_label }}
<input type="checkbox" class="activator" id="id_{{ field.name }}_act" name="{{ field.name }}_act" {% if activator_value %}checked='checked'{% else %}{% if deactivate_if_empty and field.value == None %}checked='checked'{% endif %}{% endif %}/>{{ activator_label }}
</label>{% endif %}
{% if field.errors %}<p class="help-block">{% for error in field.errors %}{{ error|safe }}{% endfor %}</p>{% endif %}
</div>