# -*- coding: utf-8 -*- from __future__ import unicode_literals import subprocess from django.conf import settings from django.db import models from django.db.models.signals import pre_save, post_save, post_delete from django.dispatch import receiver from coin.members.models import Member class SyncCommandError(SystemError): pass class MaillingListSubscription(models.Model): member = models.ForeignKey(Member, verbose_name='membre') maillinglist = models.ForeignKey('MaillingList', verbose_name='liste mail') class Meta: verbose_name = 'abonnement à une liste mail' verbose_name_plural = 'abonnements à des listes mail' unique_together = ('member', 'maillinglist') def __str__(self): return str(self.maillinglist) class MaillingList(models.Model): short_name = models.CharField( 'identifiant technique', max_length=50, help_text=( "c'est l'identifiant qui servira à " "communiquer avec le système de mailling-list" "(typiquement, la partie avant le \"@\" dans l'adress )" ) ) email = models.EmailField("adresse mail d'envoi") verbose_name = models.CharField( 'nom complet', max_length=130, help_text="Nom affiché dans l'interface membre" ) description = models.TextField() subscribers = models.ManyToManyField( Member, related_name='subscribed_maillinglists', through=MaillingListSubscription, verbose_name='abonné·e·s', blank=True) class Meta: verbose_name = 'liste mail' verbose_name_plural = 'listes mail' def __unicode__(self): return '{} ({})'.format(self.verbose_name, self.email) def as_text_listing(self): """ One subscriber email per line """ return '\n'.join( self.subscribers.values_list('email', flat=True)) def sync_to_list_server(self, force_clear=False): if not settings.MAILLIST_SYNC_COMMAND: raise ValueError('You should define MAILLIST_SYNC_COMMAND' ' setting to use maillist module') else: cmd = settings.MAILLIST_SYNC_COMMAND.format( email=self.email, short_name=self.short_name, ) p = subprocess.Popen( cmd, shell=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE) if force_clear: text_listing = '' else: text_listing = self.as_text_listing() out_stdout, out_stderr = p.communicate(text_listing) if p.returncode != 0: raise SyncCommandError( "Erreur à l'appel de la commande : \"{}\"".format( out_stderr.decode('utf-8'))) @receiver(post_save, sender=MaillingListSubscription) def push_new_subscription(sender, instance, created, raw, *args, **kwargs): if raw: print("The synchronization of mailling list with Coin was not performed, please launch it by hand in the admin interface.") else: instance.maillinglist.sync_to_list_server() @receiver(post_delete, sender=MaillingListSubscription) def push_remove_subscription(sender, instance, *args, **kwargs): instance.maillinglist.sync_to_list_server() @receiver(pre_save, sender=Member) def store_previous_email(sender, instance, *args, **kwargs): """Record the email address for post_save handler update_an_email_address needs the old email address for comparison, but this information is not available at post_save stage. """ member = instance # if not, this is a user creation, nothing to do if member.pk: old_member = Member.objects.get(pk=member.pk) member._previous_email = old_member.email @receiver(post_save, sender=Member) def update_an_email_address(sender, instance, *args, **kwargs): """Check if the member email has changed and sync mail lists if so. We do that at post_save stage because we need the new information to be recorded in database, otherwise, sync_list_to_server() would use the old email. """ member = instance old_email = getattr(member, '_previous_email', None) if old_email and (old_email != member.email): for maillist in member.subscribed_maillinglists.all(): maillist.sync_to_list_server() # Error handling