models.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import subprocess
  4. from django.conf import settings
  5. from django.db import models
  6. from django.db.models.signals import pre_save, post_save, post_delete
  7. from django.dispatch import receiver
  8. from coin.members.models import Member
  9. class SyncCommandError(SystemError):
  10. pass
  11. class MaillingListSubscription(models.Model):
  12. member = models.ForeignKey(Member, verbose_name='membre')
  13. maillinglist = models.ForeignKey('MaillingList', verbose_name='liste mail')
  14. class Meta:
  15. verbose_name = 'abonnement à une liste mail'
  16. verbose_name_plural = 'abonnements à des listes mail'
  17. unique_together = ('member', 'maillinglist')
  18. class MaillingList(models.Model):
  19. short_name = models.CharField(
  20. 'identifiant technique', max_length=50,
  21. help_text=(
  22. "c'est l'identifiant qui servira à "
  23. "communiquer avec le système de mailling-list"
  24. "(typiquement, la partie avant le \"@\" dans l'adress )"
  25. )
  26. )
  27. email = models.EmailField("adresse mail d'envoi")
  28. verbose_name = models.CharField(
  29. 'nom complet', max_length=130,
  30. help_text="Nom affiché dans l'interface membre"
  31. )
  32. description = models.TextField()
  33. subscribers = models.ManyToManyField(
  34. Member, related_name='subscribed_maillinglists',
  35. through=MaillingListSubscription,
  36. verbose_name='abonné·e·s', blank=True)
  37. class Meta:
  38. verbose_name = 'liste mail'
  39. verbose_name_plural = 'listes mail'
  40. def __unicode__(self):
  41. return '{} ({})'.format(self.verbose_name, self.email)
  42. def as_text_listing(self):
  43. """ One subscriber email per line
  44. """
  45. return '\n'.join(
  46. self.subscribers.values_list('email', flat=True))
  47. def sync_to_list_server(self, force_clear=False):
  48. if not settings.MAILLIST_SYNC_COMMAND:
  49. raise ValueError('You should define MAILLIST_SYNC_COMMAND'
  50. ' setting to use maillist module')
  51. else:
  52. cmd = settings.MAILLIST_SYNC_COMMAND.format(
  53. email=self.email,
  54. short_name=self.short_name,
  55. )
  56. p = subprocess.Popen(
  57. cmd, shell=True,
  58. stdin=subprocess.PIPE, stderr=subprocess.PIPE)
  59. if force_clear:
  60. text_listing = ''
  61. else:
  62. text_listing = self.as_text_listing()
  63. out_stdout, out_stderr = p.communicate(text_listing)
  64. if p.returncode != 0:
  65. raise SyncCommandError(
  66. "Erreur à l'appel de la commande : \"{}\"".format(
  67. out_stderr.decode('utf-8')))
  68. @receiver(post_save, sender=MaillingListSubscription)
  69. def push_new_subscription(sender, instance, created, raw, *args, **kwargs):
  70. if raw:
  71. print("The synchronization of mailling list with Coin was not performed, please launch it by hand in the admin interface.")
  72. else:
  73. instance.maillinglist.sync_to_list_server()
  74. @receiver(post_delete, sender=MaillingListSubscription)
  75. def push_remove_subscription(sender, instance, *args, **kwargs):
  76. instance.maillinglist.sync_to_list_server()
  77. @receiver(pre_save, sender=Member)
  78. def store_previous_email(sender, instance, *args, **kwargs):
  79. """Record the email address for post_save handler
  80. update_an_email_address needs the old email address for comparison, but
  81. this information is not available at post_save stage.
  82. """
  83. member = instance
  84. # if not, this is a user creation, nothing to do
  85. if member.pk:
  86. old_member = Member.objects.get(pk=member.pk)
  87. member._previous_email = old_member.email
  88. @receiver(post_save, sender=Member)
  89. def update_an_email_address(sender, instance, *args, **kwargs):
  90. """Check if the member email has changed and sync mail lists if so.
  91. We do that at post_save stage because we need the new information to be
  92. recorded in database, otherwise, sync_list_to_server() would use the old
  93. email.
  94. """
  95. member = instance
  96. old_email = getattr(member, '_previous_email', None)
  97. if old_email and (old_email != member.email):
  98. for maillist in member.subscribed_maillinglists.all():
  99. maillist.sync_to_list_server()
  100. # Error handling