diff --git a/modoboa/extensions/amavis/management/commands/qcleanup.py b/modoboa/extensions/amavis/management/commands/qcleanup.py index c57aee2ef..c31d348f5 100644 --- a/modoboa/extensions/amavis/management/commands/qcleanup.py +++ b/modoboa/extensions/amavis/management/commands/qcleanup.py @@ -50,7 +50,7 @@ class Command(BaseCommand): flags += ['R'] self.__vprint("Deleting marked messages...") - ids = Msgrcpt.objects.filter(rs__in=flags).values("mail_id") + ids = Msgrcpt.objects.filter(rs__in=flags).values("mail_id").distinct() for msg in Msgs.objects.filter(mail_id__in=ids): if not msg.msgrcpt_set.exclude(rs__in=flags).count(): msg.delete() diff --git a/modoboa/extensions/amavis/models.py b/modoboa/extensions/amavis/models.py index 50f29305b..7a711fdd8 100644 --- a/modoboa/extensions/amavis/models.py +++ b/modoboa/extensions/amavis/models.py @@ -65,7 +65,7 @@ class Msgs(models.Model): class Msgrcpt(models.Model): partition_tag = models.IntegerField(null=True, blank=True) mail = models.ForeignKey(Msgs, primary_key=True) - rid = models.ForeignKey(Maddr, db_column='rid', primary_key=True) + rid = models.ForeignKey(Maddr, db_column='rid') rseqnum = models.IntegerField(default=0) is_local = models.CharField(max_length=3) content = models.CharField(max_length=3) diff --git a/modoboa/extensions/amavis/sql_listing.py b/modoboa/extensions/amavis/sql_listing.py index 71c2cd5aa..b7842eddb 100644 --- a/modoboa/extensions/amavis/sql_listing.py +++ b/modoboa/extensions/amavis/sql_listing.py @@ -9,7 +9,7 @@ from modoboa.lib.webutils import static_url from modoboa.lib.email_listing import MBconnector, EmailListing from modoboa.lib.emailutils import Email from modoboa.lib.dbutils import db_type -from modoboa.extensions.admin.models import Domain, Alias +from modoboa.extensions.admin.models import Domain from .models import Quarantine, Msgrcpt @@ -46,9 +46,10 @@ class Qtable(tables.Table): "date", label=ugettext_lazy("Date"), sort_order="date", cssclass="openable" ) - + cols_order = [ - 'selection', 'type', 'rstatus', 'score', 'to', 'from_', 'subject', 'time' + 'selection', 'type', 'rstatus', 'score', 'to', 'from_', 'subject', + 'time' ] def parse_type(self, value): @@ -101,7 +102,7 @@ class SQLconnector(MBconnector): order = self.order_translation_table[order] self.messages = self.messages.order_by(sign + order) - self.count = len(self.messages) + self.count = self.messages.count() return self.count def fetch(self, start=None, stop=None, **kwargs): @@ -115,7 +116,9 @@ class SQLconnector(MBconnector): "type": qm["mail__msgrcpt__content"], "score": qm["mail__msgrcpt__bspam_level"]} rs = qm["mail__msgrcpt__rs"] - if rs == '': + if rs == 'D': + continue + elif rs == '': m["class"] = "unseen" elif rs == 'R': m["img_rstatus"] = static_url("pics/release.png") @@ -138,6 +141,18 @@ class SQLWrapper(object): See ``PgWrapper`` for the real mess... """ + def _exec(self, query, args): + """Execute a raw SQL query. + + :param string query: query to execute + :param list args: a list of arguments to replace in :kw:`query` + """ + from django.db import connections, transaction + + cursor = connections['amavis'].cursor() + cursor.execute(query, args) + transaction.commit_unless_managed(using='amavis') + def get_mails(self, request, rcptfilter=None): """Retrieve all messages visible by a user. @@ -163,6 +178,19 @@ class SQLWrapper(object): def get_recipient_message(self, address, mailid): return Msgrcpt.objects.get(mail=mailid, rid__email=address) + def set_msgrcpt_status(self, address, mailid, status): + """Change the status (rs field) of a message recipient. + + :param string status: status + """ + from modoboa.extensions.amavis.models import Maddr + + addr = Maddr.objects.get(email=address) + self._exec( + "UPDATE msgrcpt SET rs=%s WHERE mail_id=%s AND rid=%s", + [status, mailid, addr.id] + ) + def get_recipient_messages(self, address, mailids): return Msgrcpt.objects.filter(mail__in=mailids, rid__email=address) diff --git a/modoboa/extensions/amavis/views.py b/modoboa/extensions/amavis/views.py index 91b3d52fb..c4d7f51d1 100644 --- a/modoboa/extensions/amavis/views.py +++ b/modoboa/extensions/amavis/views.py @@ -57,7 +57,6 @@ def _listing(request): else: raise BadRequest("unsupported search criteria %s" % c) flt = nfilter if flt is None else flt | nfilter - msgtype = navparams.get('msgtype', None) if msgtype is not None: nfilter = Q(mail__msgrcpt__content=msgtype) @@ -140,9 +139,7 @@ def viewmail(request, mail_id): if request.user.mailbox_set.count(): mb = Mailbox.objects.get(user=request.user) if rcpt in mb.alias_addresses: - msgrcpt = get_wrapper().get_recipient_message(rcpt, mail_id) - msgrcpt.rs = 'V' - msgrcpt.save() + get_wrapper().set_msgrcpt_status(rcpt, mail_id, 'V') content = Template(""" @@ -178,9 +175,7 @@ def delete_selfservice(request, mail_id): if rcpt is None: raise BadRequest(_("Invalid request")) try: - msgrcpt = get_wrapper().get_recipient_message(rcpt, mail_id) - msgrcpt.rs = 'D' - msgrcpt.save() + get_wrapper().set_msgrcpt_status(rcpt, mail_id, 'D') except Msgrcpt.DoesNotExist: raise BadRequest(_("Invalid request")) return render_to_json_response(_("Message deleted")) @@ -201,9 +196,7 @@ def delete(request, mail_id): if mb is not None and r != mb.full_address \ and not r in mb.alias_addresses: continue - msgrcpt = wrapper.get_recipient_message(r, i) - msgrcpt.rs = 'D' - msgrcpt.save() + wrapper.set_msgrcpt_status(r, i, 'D') message = ungettext("%(count)d message deleted successfully", "%(count)d messages deleted successfully", len(mail_id)) % {"count": len(mail_id)} @@ -218,24 +211,24 @@ def release_selfservice(request, mail_id): secret_id = request.GET.get("secret_id", None) if rcpt is None or secret_id is None: raise BadRequest(_("Invalid request")) + wrapper = get_wrapper() try: - msgrcpt = get_wrapper().get_recipient_message(rcpt, mail_id) + msgrcpt = wrapper.get_recipient_message(rcpt, mail_id) except Msgrcpt.DoesNotExist: raise BadRequest(_("Invalid request")) if secret_id != msgrcpt.mail.secret_id: raise BadRequest(_("Invalid request")) if parameters.get_admin("USER_CAN_RELEASE") == "no": - msgrcpt.rs = 'p' + wrapper.set_msgrcpt_status(rcpt, mail_id, 'p') msg = _("Request sent") else: amr = AMrelease() result = amr.sendreq(mail_id, secret_id, rcpt) if result: - rcpt.rs = 'R' + wrapper.set_msgrcpt_status(rcpt, mail_id, 'R') msg = _("Message released") else: raise BadRequest(result) - msgrcpt.save() return render_to_json_response(msg) @@ -257,14 +250,10 @@ def release(request, mail_id): continue msgrcpts += [wrapper.get_recipient_message(r, i)] if mb is not None and parameters.get_admin("USER_CAN_RELEASE") == "no": - # FIXME : can't use this syntax because extra SQL (using - # .extra() for postgres) is not propagated (the 'tables' - # parameter is lost somewhere...) - # - # msgrcpts.update(rs='p') for msgrcpt in msgrcpts: - msgrcpt.rs = 'p' - msgrcpt.save() + wrapper.set_msgrcpt_status( + msgrcpt.rid.email, msgrcpt.mail.mail_id, 'p' + ) message = ungettext("%(count)d request sent", "%(count)d requests sent", len(mail_id)) % {"count": len(mail_id)} @@ -276,10 +265,11 @@ def release(request, mail_id): amr = AMrelease() error = None for rcpt in msgrcpts: - result = amr.sendreq(rcpt.mail.mail_id, rcpt.mail.secret_id, rcpt.rid.email) + result = amr.sendreq( + rcpt.mail.mail_id, rcpt.mail.secret_id, rcpt.rid.email + ) if result: - rcpt.rs = 'R' - rcpt.save() + wrapper.set_msgrcpt_status(rcpt.rid.email, rcpt.mail.mail_id, 'R') else: error = result break