123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- # -*- coding: utf-8 -*-
- from __future__ import unicode_literals
- from contextlib import contextmanager
- 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 aux 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, unique=True,
- help_text=(
- "c'est l'identifiant qui servira à "
- "communiquer avec le serveur de liste mail "
- "(typiquement, la partie avant le \"@\" dans l'adress )"
- )
- )
- email = models.EmailField("adresse mail d'envoi", unique=True)
- 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')))
- 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.")
- elif not getattr(sender, 'skip_sync', False):
- instance.maillinglist.sync_to_list_server()
- def push_remove_subscription(sender, instance, *args, **kwargs):
- if not getattr(sender, 'skip_sync', False):
- instance.maillinglist.sync_to_list_server()
- 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 ???
- # try:
- # except SyncCommandError as e:
- # print("error", e)
- # we cannot send a message because we don't have the request
- SIGNALS = [
- (Member, pre_save, store_previous_email),
- (Member, post_save, update_an_email_address),
- (MaillingListSubscription, post_save, push_new_subscription),
- (MaillingListSubscription, post_delete, push_remove_subscription),
- ]
- def connect_signals():
- for sender, signal, receiver in SIGNALS:
- signal.connect(sender=sender, receiver=receiver)
- def disconnect_signals():
- for sender, signal, receiver in SIGNALS:
- signal.disconnect(sender=sender, receiver=receiver)
- # Do it once
- connect_signals()
- @contextmanager
- def skip_maillist_sync():
- """ Allows to skip temporary signals
- """
- disconnect_signals()
- try:
- yield
- finally:
- connect_signals()
|