From 4e72d70917b43971cd9d42b1a1fe13cb7a2dbbfb Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Tue, 12 Nov 2013 21:48:48 +0100 Subject: [PATCH] Compatibility with reversion >= 1.8. fixes #497 --- .../core/commands/templates/settings.py.tpl | 3 +- modoboa/core/extensions.py | 8 +++-- modoboa/core/models.py | 36 +++++++++++++++---- modoboa/core/templatetags/core_tags.py | 2 +- modoboa/extensions/admin/forms/domain.py | 7 ++-- modoboa/extensions/admin/models/domain.py | 9 +++++ .../extensions/admin/static/admin/js/admin.js | 2 +- modoboa/extensions/admin/views/identity.py | 21 +++++------ .../extensions/amavis/general_callbacks.py | 11 +++--- .../extensions/postfix_relay_domains/forms.py | 8 +++-- .../general_callbacks.py | 26 -------------- .../postfix_relay_domains/limits_callbacks.py | 27 ++++++++++++++ .../postfix_relay_domains/models.py | 9 +++++ .../extensions/postfix_relay_domains/views.py | 4 +-- modoboa/lib/parameters.py | 2 ++ requirements.txt | 2 +- 16 files changed, 114 insertions(+), 63 deletions(-) diff --git a/modoboa/core/commands/templates/settings.py.tpl b/modoboa/core/commands/templates/settings.py.tpl index 710c6c96a..b7f36115f 100644 --- a/modoboa/core/commands/templates/settings.py.tpl +++ b/modoboa/core/commands/templates/settings.py.tpl @@ -106,6 +106,7 @@ TEMPLATE_LOADERS = ( MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.transaction.TransactionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', @@ -154,8 +155,8 @@ MODOBOA_APPS = ( 'modoboa.lib', # Modoboa extensions here. 'modoboa.extensions.admin', - 'modoboa.extensions.limits', 'modoboa.extensions.postfix_relay_domains', + 'modoboa.extensions.limits', 'modoboa.extensions.postfix_autoreply', 'modoboa.extensions.webmail', 'modoboa.extensions.stats', diff --git a/modoboa/core/extensions.py b/modoboa/core/extensions.py index fe11435c7..07073a9d2 100644 --- a/modoboa/core/extensions.py +++ b/modoboa/core/extensions.py @@ -76,11 +76,13 @@ class ExtensionsPool(object): """ from modoboa.core.models import Extension + result = [] for ext in settings.MODOBOA_APPS: __import__(ext) - result = [] - for extname in self.extensions.keys(): + extname = ext.split('.')[-1] extinstance = self.get_extension(extname) + if extinstance is None: + continue try: baseurl = extinstance.url \ if extinstance.url is not None else extname @@ -106,7 +108,7 @@ class ExtensionsPool(object): for extname, extdef in self.extensions.iteritems(): if not extdef["show"]: continue - infos = extdef["instance"].infos() + infos = self.get_extension_infos(extname) infos["id"] = extname result += [infos] return sorted(result, key=lambda i: i["name"]) diff --git a/modoboa/core/models.py b/modoboa/core/models.py index db0050044..68ac4bead 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -4,9 +4,11 @@ import re import hashlib import crypt import base64 +import logging from random import Random import reversion 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 @@ -463,24 +465,44 @@ class Log(models.Model): @receiver(reversion.post_revision_commit) def post_revision_commit(sender, **kwargs): - import logging - if kwargs["revision"].user is None: return logger = logging.getLogger("modoboa.admin") for version in kwargs["versions"]: - if version.type == reversion.models.VERSION_ADD: + prev_revisions = reversion.get_for_object(version.object) + if prev_revisions.count() == 1: action = _("added") level = "info" - elif version.type == reversion.models.VERSION_CHANGE: + else: action = _("modified") level = "warning" - else: - action = _("deleted") - level = "critical" message = _("%(object)s '%(name)s' %(action)s by user %(user)s") % { "object": unicode(version.content_type).capitalize(), "name": version.object_repr, "action": action, "user": kwargs["revision"].user.username } getattr(logger, level)(message) + + +@receiver(post_delete) +def post_delete_hook(sender, instance, **kwargs): + """Custom post-delete hook. + + We want to know who was responsible for an object deletion. + """ + from reversion.models import Version + + if not reversion.is_registered(sender): + return + del_list = reversion.get_deleted(sender) + try: + version = del_list.get(object_id=instance.id) + except Version.DoesNotExist: + return + logger = logging.getLogger("modoboa.admin") + msg = _("%(object)s '%(name)s' %(action)s by user %(user)s") % { + "object": unicode(version.content_type).capitalize(), + "name": version.object_repr, "action": _("deleted"), + "user": version.revision.user.username + } + logger.critical(msg) diff --git a/modoboa/core/templatetags/core_tags.py b/modoboa/core/templatetags/core_tags.py index b424c1f9f..7b9a08930 100644 --- a/modoboa/core/templatetags/core_tags.py +++ b/modoboa/core/templatetags/core_tags.py @@ -45,7 +45,7 @@ def settings_menu(selection, user): "label": _("Information")}, {"name": "logs", "class": "ajaxlink", - "url": "logs/", + "url": "logs/&sort_order=-date_created", "label": _("Logs")}, {"name": "parameters", "class": "ajaxlink", diff --git a/modoboa/extensions/admin/forms/domain.py b/modoboa/extensions/admin/forms/domain.py index c3686fdc9..c19b60d68 100644 --- a/modoboa/extensions/admin/forms/domain.py +++ b/modoboa/extensions/admin/forms/domain.py @@ -107,7 +107,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm): username = q['username'].replace('@%s' % self.oldname, '@%s' % domain.name) Quota.objects.filter(username=q['username']).update(username=username) - def save(self, user, commit=True): + def save(self, user, commit=True, domalias_post_create=False): """Custom save method Updating a domain may have consequences on other objects @@ -148,7 +148,7 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm): else: continue al = DomainAlias(name=alias, target=d, enabled=d.enabled) - al.save(creator=user) + al.save(creator=user) if domalias_post_create else al.save() if old_mail_homes is not None: self.update_mailbox_quotas(d) @@ -253,5 +253,6 @@ class DomainForm(TabForms): As forms interact with each other, it is easier to make custom code to save them. """ - for f in self.forms: + self.forms[0]['instance'].save(user, domalias_post_create=True) + for f in self.forms[1:]: f["instance"].save(user) diff --git a/modoboa/extensions/admin/models/domain.py b/modoboa/extensions/admin/models/domain.py index 95940dda6..e7467d592 100644 --- a/modoboa/extensions/admin/models/domain.py +++ b/modoboa/extensions/admin/models/domain.py @@ -148,4 +148,13 @@ class Domain(AdminObject): for dalias in self.domainalias_set.all(): dalias.to_csv(csvwriter) + def post_create(self, creator): + """Post creation actions. + + :param ``User`` creator: user whos created this domain + """ + super(Domain, self).post_create(creator) + for domalias in self.domainalias_set.all(): + domalias.post_create(creator) + reversion.register(Domain) diff --git a/modoboa/extensions/admin/static/admin/js/admin.js b/modoboa/extensions/admin/static/admin/js/admin.js index d0a5ef387..0ed43c300 100644 --- a/modoboa/extensions/admin/static/admin/js/admin.js +++ b/modoboa/extensions/admin/static/admin/js/admin.js @@ -392,7 +392,7 @@ Identities.prototype = { $("#wizard").cwizard({ formid: "newaccount_form", transition_callbacks: { - 2: this.mailform_prefill + 1: this.mailform_prefill }, success_callback: $.proxy(this.reload_listing, this) }); diff --git a/modoboa/extensions/admin/views/identity.py b/modoboa/extensions/admin/views/identity.py index e44ac9d87..8e8fea69a 100644 --- a/modoboa/extensions/admin/views/identity.py +++ b/modoboa/extensions/admin/views/identity.py @@ -115,6 +115,14 @@ def list_quotas(request, tplname="admin/quotas.html"): @permission_required("core.add_user") @transaction.commit_on_success def newaccount(request, tplname='common/wizard_forms.html'): + """Create a new account. + + .. note:: An issue still remains int this code: if all validation + steps are successful but an error occurs after we call 'save', + the account will be created. It happens transaction management + doesn't work very well with nested functions. Need to wait for + django 1.6 and atomicity. + """ cwizard = CreationWizard() cwizard.add_step(AccountFormGeneral, _("General"), [dict(classes="btn-inverse next", label=_("Next"))], @@ -135,16 +143,9 @@ def newaccount(request, tplname='common/wizard_forms.html'): if retcode == 2: genform = cwizard.steps[0]["form"] account = genform.save() - try: - account.post_create(request.user) - mailform = cwizard.steps[1]["form"] - mailform.save(request.user, account) - except ModoboaException: - # A bit uggly: transaction management doesn't work very - # well with nested functions. Need to wait for django 1.6 - # and atomicity. - account.delete(request.user, False) - raise + account.post_create(request.user) + mailform = cwizard.steps[1]["form"] + mailform.save(request.user, account) return render_to_json_response(_("Account created")) return render_to_json_response({ 'stepid': data, 'form_errors': cwizard.errors diff --git a/modoboa/extensions/amavis/general_callbacks.py b/modoboa/extensions/amavis/general_callbacks.py index 558b2355b..8d9ad4ec6 100644 --- a/modoboa/extensions/amavis/general_callbacks.py +++ b/modoboa/extensions/amavis/general_callbacks.py @@ -1,8 +1,8 @@ from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ from django.template import Template, Context - from modoboa.lib import events, parameters +from modoboa.extensions.admin.models import DomainAlias from modoboa.extensions.amavis.lib import ( create_user_and_policy, update_user_and_policy, delete_user_and_policy, create_user_and_use_policy, delete_user @@ -36,13 +36,16 @@ def on_domain_deleted(domain): @events.observe("DomainAliasCreated") -def on_relay_domain_alias_created(user, domainalias): +def on_domain_alias_created(user, domainalias): create_user_and_use_policy(domainalias.name, domainalias.target.name) @events.observe("DomainAliasDeleted") -def on_relay_domain_alias_deleted(domainalias): - delete_user(domainalias.name) +def on_domain_alias_deleted(domainaliases): + if isinstance(domainaliases, DomainAlias): + domainaliases = [domainaliases] + for domainalias in domainaliases: + delete_user(domainalias.name) @events.observe("GetStaticContent") diff --git a/modoboa/extensions/postfix_relay_domains/forms.py b/modoboa/extensions/postfix_relay_domains/forms.py index 37a49d646..08cff2059 100644 --- a/modoboa/extensions/postfix_relay_domains/forms.py +++ b/modoboa/extensions/postfix_relay_domains/forms.py @@ -82,7 +82,7 @@ class RelayDomainFormGeneral(forms.ModelForm, DynamicForm): return cleaned_data - def save(self, user, commit=True): + def save(self, user, commit=True, rdomalias_post_create=False): """Custom save method. As relay domain aliases are defined using the same form as @@ -119,7 +119,8 @@ class RelayDomainFormGeneral(forms.ModelForm, DynamicForm): al = RelayDomainAlias( name=alias, target=rd, enabled=rd.enabled ) - al.save(creator=user) + al.save(creator=user) \ + if rdomalias_post_create else al.save() return rd @@ -151,5 +152,6 @@ class RelayDomainForm(TabForms): As forms interact with each other, it is easier to make custom code to save them. """ - for f in self.forms: + self.forms[0]['instance'].save(user, rdomalias_post_create=True) + for f in self.forms[1:]: f["instance"].save(user) diff --git a/modoboa/extensions/postfix_relay_domains/general_callbacks.py b/modoboa/extensions/postfix_relay_domains/general_callbacks.py index 6c9861d36..c51c54be4 100644 --- a/modoboa/extensions/postfix_relay_domains/general_callbacks.py +++ b/modoboa/extensions/postfix_relay_domains/general_callbacks.py @@ -106,32 +106,6 @@ def extra_limit_templates(): ] -@events.observe('GetExtraParameters') -def extra_parameters(app, level): - from django import forms - - if app != 'limits' or level != 'A': - return {} - return { - 'deflt_relay_domains_limit': forms.IntegerField( - label=ugettext_lazy("Relay domains"), - initial=0, - help_text=ugettext_lazy( - "Maximum number of allowed relay domains for a new administrator" - ), - widget=forms.widgets.TextInput(attrs={"class": "span1"}) - ), - '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" - ), - widget=forms.widgets.TextInput(attrs={"class": "span1"}) - ) - } - - @events.observe('ExtraDomainImportHelp') def extra_domain_import_help(): return [ugettext_lazy(""" diff --git a/modoboa/extensions/postfix_relay_domains/limits_callbacks.py b/modoboa/extensions/postfix_relay_domains/limits_callbacks.py index 9966945e7..5d50ea28a 100644 --- a/modoboa/extensions/postfix_relay_domains/limits_callbacks.py +++ b/modoboa/extensions/postfix_relay_domains/limits_callbacks.py @@ -1,9 +1,36 @@ +from django.utils.translation import ugettext_lazy 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') +def extra_parameters(app, level): + from django import forms + + if app != 'limits' or level != 'A': + return {} + return { + 'deflt_relay_domains_limit': forms.IntegerField( + label=ugettext_lazy("Relay domains"), + initial=0, + help_text=ugettext_lazy( + "Maximum number of allowed relay domains for a new administrator" + ), + widget=forms.widgets.TextInput(attrs={"class": "span1"}) + ), + '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" + ), + widget=forms.widgets.TextInput(attrs={"class": "span1"}) + ) + } + + @events.observe('RelayDomainCreated') def inc_relaydomains_count(user, rdomain): inc_limit_usage(user, 'relay_domains_limit') diff --git a/modoboa/extensions/postfix_relay_domains/models.py b/modoboa/extensions/postfix_relay_domains/models.py index 360810cb3..088874c2e 100644 --- a/modoboa/extensions/postfix_relay_domains/models.py +++ b/modoboa/extensions/postfix_relay_domains/models.py @@ -139,6 +139,15 @@ class RelayDomain(AdminObject): self.verify_recipients = (row[5].strip() == 'True') self.save(creator=user) + def post_create(self, creator): + """Post creation actions. + + :param ``User`` creator: user whos created this relay domain + """ + super(RelayDomain, self).post_create(creator) + for rdomalias in self.relaydomainalias_set.all(): + rdomalias.post_create(creator) + reversion.register(RelayDomain) diff --git a/modoboa/extensions/postfix_relay_domains/views.py b/modoboa/extensions/postfix_relay_domains/views.py index e59657891..e6df53621 100644 --- a/modoboa/extensions/postfix_relay_domains/views.py +++ b/modoboa/extensions/postfix_relay_domains/views.py @@ -23,9 +23,7 @@ def create(request, tplname="postfix_relay_domains/new_relaydomain_form.html"): if form.is_valid(): rdom = form.save(request.user) rdom.post_create(request.user) - return render_to_json_response( - {"respmsg": _("Relay domain created")} - ) + return render_to_json_response(_("Relay domain created")) return render_to_json_response( {'form_errors': form.errors}, status=400 ) diff --git a/modoboa/lib/parameters.py b/modoboa/lib/parameters.py index a5c7dfa89..42b8294db 100644 --- a/modoboa/lib/parameters.py +++ b/modoboa/lib/parameters.py @@ -17,6 +17,8 @@ _params = {'A': {}, 'U': {}} class NotDefined(ModoboaException): + http_code = 404 + def __init__(self, app, name): self.app = app self.name = name diff --git a/requirements.txt b/requirements.txt index ff9ceadfb..94613aa78 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ django -django-reversion==1.7.1 +django-reversion>=1.8.0 dj-database-url south lxml