models.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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, m2m_changed
  7. from django.dispatch import receiver
  8. from coin.members.models import Member
  9. class MaillingList(models.Model):
  10. short_name = models.CharField(
  11. 'identifiant technique', max_length=50,
  12. help_text=(
  13. "c'est l'identifiant qui servira à "
  14. "communiquer avec le système de mailling-list"
  15. "(typiquement, la partie avant le \"@\" dans l'adress )"
  16. )
  17. )
  18. email = models.EmailField("adresse mail d'envoi")
  19. verbose_name = models.CharField(
  20. 'nom complet', max_length=130,
  21. help_text="Nom affiché dans l'interface membre"
  22. )
  23. description = models.TextField()
  24. subscribers = models.ManyToManyField(
  25. Member, related_name='subscribed_maillinglists',
  26. verbose_name='abonné·e·s', blank=True)
  27. class Meta:
  28. verbose_name = 'liste mail'
  29. verbose_name_plural = 'listes mail'
  30. def __unicode__(self):
  31. return '{} ({})'.format(self.verbose_name, self.email)
  32. def as_text_listing(self):
  33. """ One subscriber email per line
  34. """
  35. return '\n'.join(
  36. self.subscribers.values_list('email', flat=True))
  37. def sync_to_list_server(self, force_clear=False):
  38. if not settings.MAILLIST_SYNC_COMMAND:
  39. raise ValueError('You should define MAILLIST_SYNC_COMMAND'
  40. ' setting to use maillist module')
  41. else:
  42. cmd = settings.MAILLIST_SYNC_COMMAND.format(
  43. email=self.email,
  44. short_name=self.short_name,
  45. )
  46. p = subprocess.Popen(
  47. cmd, shell=True,
  48. stdin=subprocess.PIPE, stderr=subprocess.PIPE)
  49. if force_clear:
  50. text_listing = ''
  51. else:
  52. text_listing = self.as_text_listing()
  53. out_stdout, out_stderr = p.communicate(text_listing)
  54. if p.returncode != 0:
  55. raise SystemError(
  56. "Erreur à l'appel de la commande : \"{}\"".format(
  57. out_stderr.decode('utf-8')))
  58. @receiver(m2m_changed, sender=MaillingList.subscribers.through)
  59. def push_updated_list(sender, instance, action, reverse, model, pk_set, **kwargs):
  60. if action in ('post_add', 'post_remove'):
  61. if reverse:
  62. impacted_mls = MaillingList.objects.filter(pk__in=pk_set)
  63. else:
  64. impacted_mls = [instance]
  65. elif action == 'post_clear' and not reverse:
  66. impacted_mls = [instance]
  67. # cannot be handled at post_clear (we would have lost the information on
  68. # what has been removed)
  69. elif action == 'pre_clear' and reverse:
  70. impacted_mls = instance.subscribed_maillinglists.all()
  71. else:
  72. return
  73. for ml in impacted_mls:
  74. if action == 'pre_clear':
  75. # We have to force it because it has not yet been reflected in DB.
  76. ml.sync_to_list_server(force_clear=True)
  77. else:
  78. ml.sync_to_list_server()
  79. @receiver(pre_save, sender=Member)
  80. def store_previous_email(sender, instance, *args, **kwargs):
  81. """Record the email address for post_save handler
  82. update_an_email_address needs the old email address for comparison, but
  83. this information is not available at post_save stage.
  84. """
  85. member = instance
  86. # if not, this is a user creation, nothing to do
  87. if member.pk:
  88. old_member = Member.objects.get(pk=member.pk)
  89. member._previous_email = old_member.email
  90. @receiver(post_save, sender=Member)
  91. def update_an_email_address(sender, instance, *args, **kwargs):
  92. """Check if the member email has changed and sync mail lists if so.
  93. We do that at post_save stage because we need the new information to be
  94. recorded in database, otherwise, sync_list_to_server() would use the old
  95. email.
  96. """
  97. member = instance
  98. old_email = getattr(member, '_previous_email', None)
  99. if old_email and (old_email != member.email):
  100. for maillist in member.subscribed_maillinglists.all():
  101. maillist.sync_to_list_server()
  102. # Error handling