diff --git a/cfp/models.py b/cfp/models.py
index c7ed501..72c6284 100644
--- a/cfp/models.py
+++ b/cfp/models.py
@@ -86,7 +86,7 @@ class Conference(models.Model):
})
def __str__(self):
- return str(self.site)
+ return self.name
class ParticipantManager(models.Manager):
diff --git a/cfp/signals.py b/cfp/signals.py
index 6fbc901..b582901 100644
--- a/cfp/signals.py
+++ b/cfp/signals.py
@@ -5,9 +5,11 @@ from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
+from django.contrib.auth import get_user_model
from ponyconf.decorators import disable_for_loaddata
from mailing.models import MessageThread, Message
+from mailing.utils import send_message
from .models import Participant, Talk, Conference, Volunteer
@@ -25,80 +27,47 @@ pre_save.connect(create_conversation, sender=Talk)
pre_save.connect(create_conversation, sender=Volunteer)
-@receiver(pre_save, sender=Message, dispatch_uid="Set message author")
-def set_message_author(sender, instance, **kwargs):
- message = instance
- if message.author is None:
- # Try users
- try:
- instance.author = User.objects.get(email=message.from_email)
- except User.DoesNotExist:
- pass
- else:
- return
- # Try participants
- try:
- instance.author = Participant.objects.get(email=message.from_email)
- except User.DoesNotExist:
- pass
- else:
- return
- # Try conferences
- try:
- instance.author = Conference.objects.get(contact_email=message.from_email)
- except Conference.DoesNotExist:
- pass
- else:
- return
-
-
@receiver(post_save, sender=Message, dispatch_uid="Send message notifications")
def send_message_notifications(sender, instance, **kwargs):
message = instance
+ author = message.author.author
thread = message.thread
- first_message = thread.message_set.first()
- if message == first_message:
- reference = None
+ if message.in_reply_to:
+ reference = message.in_reply_to.token
else:
- reference = first_message.token
- subject_prefix = 'Re: ' if reference else ''
+ reference = None
if hasattr(thread, 'participant'):
conf = thread.participant.site.conference
elif hasattr(thread, 'talk'):
conf = thread.talk.site.conference
+ elif hasattr(thread, 'volunteer'):
+ conf = thread.volunteer.site.conference
message_id = '<{id}@%s>' % conf.site.domain
if conf.reply_email:
- reply_to = (conf.name, conf.reply_email)
+ reply_to = (str(conf), conf.reply_email)
else:
reply_to = None
- sender = (message.author_display, conf.contact_email)
- staff_dests = [ (user.get_full_name(), user.email) for user in conf.staff.all() ]
- if hasattr(thread, 'participant'):
- conf = thread.participant.site.conference
- participant = thread.participant
- participant_dests = [ (participant.name, participant.email) ]
- participant_subject = _('[%(prefix)s] Message from the staff') % {'prefix': conf.name}
- staff_subject = _('[%(prefix)s] Conversation with %(dest)s') % {'prefix': conf.name, 'dest': participant.name}
- proto = 'https' if conf.secure_domain else 'http'
- footer = '\n\n--\n%s://' % proto + conf.site.domain + reverse('participant-details', args=[participant.pk])
- if message.from_email == conf.contact_email: # this is a talk notification message
- # send it only to the participant
- message.send_notification(subject=subject_prefix+participant_subject, sender=sender, dests=participant_dests,
- reply_to=reply_to, message_id=message_id, reference=reference)
+ if type(author) == get_user_model():
+ sender = author.get_full_name()
+ else:
+ sender = str(author)
+ sender = (sender, conf.contact_email)
+ staff_dests = [ (user, user.get_full_name(), user.email) for user in conf.staff.all() ]
+ if hasattr(thread, 'participant') or hasattr(thread, 'volunteer'):
+ if hasattr(thread, 'participant'):
+ user = thread.participant
else:
- # this is a message between the staff and the participant
- message.send_notification(subject=subject_prefix+staff_subject, sender=sender, dests=staff_dests,
- reply_to=reply_to, message_id=message_id, reference=reference, footer=footer)
- if message.from_email != thread.participant.email: # message from staff: sent it to the participant too
- message.send_notification(subject=subject_prefix+participant_subject, sender=sender, dests=participant_dests,
- reply_to=reply_to, message_id=message_id, reference=reference)
+ user = thread.volunteer
+ dests = [ (user, user.name, user.email) ]
+ if author == user: # message from the user, notify the staff
+ message.send_notification(sender=sender, dests=staff_dests, reply_to=reply_to, message_id=message_id, reference=reference)
+ else: # message to the user, notify the user, and the staff if the message is not a conference notification
+ message.send_notification(sender=sender, dests=dests, reply_to=reply_to, message_id=message_id, reference=reference)
+ if author != conf:
+ message.send_notification(sender=sender, dests=staff_dests, reply_to=reply_to, message_id=message_id, reference=reference)
elif hasattr(thread, 'talk'):
- conf = thread.talk.site.conference
- subject = _('[%(prefix)s] Talk: %(talk)s') % {'prefix': conf.name, 'talk': thread.talk.title}
- proto = 'https' if conf.secure_domain else 'http'
- footer = '\n\n--\n%s://' % proto + conf.site.domain + reverse('talk-details', args=[thread.talk.pk])
- message.send_notification(subject=subject_prefix+subject, sender=sender, dests=staff_dests,
- reply_to=reply_to, message_id=message_id, reference=reference, footer=footer)
+ message.send_notification(sender=sender, dests=staff_dests,
+ reply_to=reply_to, message_id=message_id, reference=reference)
# connected in apps.py
diff --git a/cfp/templates/cfp/staff/participant_list.html b/cfp/templates/cfp/staff/participant_list.html
index bcd09fa..773fc2e 100644
--- a/cfp/templates/cfp/staff/participant_list.html
+++ b/cfp/templates/cfp/staff/participant_list.html
@@ -69,11 +69,6 @@
—
{% blocktrans count refused=participant.refused_talk_count %}refused: {{ refused }}{% plural %}refused: {{ refused }}{% endblocktrans %}
- {% comment %}
-
- {% trans "Contact" %}
- |
- {% endcomment %}
{% if forloop.last %}
diff --git a/cfp/views.py b/cfp/views.py
index 3b4ee2e..5f969b2 100644
--- a/cfp/views.py
+++ b/cfp/views.py
@@ -19,8 +19,8 @@ from django_select2.views import AutoResponseView
from functools import reduce
import csv
-from mailing.models import Message
from mailing.forms import MessageForm
+from mailing.utils import send_message
from .planning import Program
from .decorators import speaker_required, volunteer_required, staff_required
from .mixins import StaffRequiredMixin, OnSiteMixin, OnSiteFormMixin
@@ -75,17 +75,11 @@ Thanks!
{}
""").format(volunteer.name, request.conference.name, volunteer.get_secret_url(full=True), request.conference.name)
- #Message.objects.create(
- # thread=volunteer.conversation,
- # author=request.conference,
- # from_email=request.conference.contact_email,
- # content=body,
- #)
- send_mail(
- subject=_('Thank you for your help!'),
- message=body,
- from_email='%s <%s>' % (request.conference.name, request.conference.contact_email),
- recipient_list=['%s <%s>' % (volunteer.name, volunteer.email)],
+ send_message(
+ thread=volunteer.conversation,
+ author=request.conference,
+ subject=_('[%(conference)s] Thank you for your help!') % {'conference': request.conference},
+ content=body,
)
messages.success(request, _('Thank you for your participation! You can now subscribe to some activities.'))
return redirect(reverse('volunteer-dashboard', kwargs={'volunteer_token': volunteer.token}))
@@ -111,17 +105,11 @@ def volunteer_mail_token(request):
'url': url,
'conf': request.conference
})
- #Message.objects.create(
- # thread=volunteer.conversation,
- # author=request.conference,
- # from_email=request.conference.contact_email,
- # content=body,
- #)
- send_mail(
- subject=_('Thank you for your help!'),
- message=body,
- from_email='%s <%s>' % (request.conference.name, request.conference.contact_email),
- recipient_list=['%s <%s>' % (volunteer.name, volunteer.email)],
+ send_message(
+ thread=volunteer.conversation,
+ author=request.conference,
+ subject=_("[%(conference)s] Someone asked to access your profil") % {'conference': request.conference},
+ content=body,
)
messages.success(request, _('A email have been sent with a link to access to your profil.'))
return redirect(reverse('volunteer-mail-token'))
@@ -251,14 +239,17 @@ Thanks!
{}
""").format(
- speaker.name, request.conference.name,talk.title, talk.description,
+ speaker.name, request.conference.name, talk.title, talk.description,
url_dashboard, url_talk_details, url_speaker_add,
request.conference.name,
)
- Message.objects.create(
+ send_message(
thread=speaker.conversation,
author=request.conference,
- from_email=request.conference.contact_email,
+ subject=_("[%(conference)s] Thank you for your proposition '%(talk)s'") % {
+ 'conference': request.conference.name,
+ 'talk': talk,
+ },
content=body,
)
messages.success(request, _('You proposition have been successfully submitted!'))
@@ -282,7 +273,7 @@ def proposal_mail_token(request):
dashboard_url = base_url + reverse('proposal-dashboard', kwargs=dict(speaker_token=speaker.token))
body = _("""Hi {},
-Someone, probably you, ask to access your profile.
+Someone, probably you, asked to access your profile.
You can edit your talks or add new ones following this url:
{}
@@ -294,10 +285,12 @@ Sincerely,
{}
""").format(speaker.name, dashboard_url, request.conference.name)
- Message.objects.create(
+ send_message(
thread=speaker.conversation,
author=request.conference,
- from_email=request.conference.contact_email,
+ subject=_("[%(conference)s] Someone asked to access your profil") % {
+ 'conference': request.conference.name,
+ },
content=body,
)
messages.success(request, _('A email have been sent with a link to access to your profil.'))
@@ -367,11 +360,25 @@ def proposal_talk_acknowledgment(request, speaker, talk_id, confirm):
talk.save()
if confirm:
confirmation_message= _('Your participation has been taken into account, thank you!')
- thread_note = _('Speaker %(speaker)s confirmed his/her participation.' % {'speaker': speaker})
+ action = _('confirmed')
else:
confirmation_message = _('We have noted your unavailability.')
- thread_note = _('Speaker %(speaker)s CANCELLED his/her participation.' % {'speaker': speaker})
- Message.objects.create(thread=talk.conversation, author=speaker, content=thread_note)
+ action = _('cancelled')
+ content = _('Speaker %(speaker)s %(action)s his/her participation for %(talk)s.') % {
+ 'speaker': speaker,
+ 'action': action,
+ 'talk': talk,
+ }
+ send_message(
+ thread=talk.conversation,
+ author=speaker,
+ subject=_('[%(conference)s] %(speaker)s %(action)s his/her participation') % {
+ 'conference': request.conference,
+ 'speaker': speaker,
+ 'action': action,
+ },
+ content=content,
+ )
messages.success(request, confirmation_message)
return redirect(reverse('proposal-talk-details', kwargs={'speaker_token': speaker.token, 'talk_id': talk.pk}))
@@ -443,10 +450,13 @@ Thanks!
url_dashboard, url_talk_details, url_speaker_add,
request.conference.name,
)
- Message.objects.create(
+ send_message(
thread=edited_speaker.conversation,
author=request.conference,
- from_email=request.conference.contact_email,
+ subject=_("[%(conference)s] You have been added as co-speaker to '%(talk)s'") % {
+ 'conference': request.conference,
+ 'talk': talk,
+ },
content=body,
)
messages.success(request, _('Co-speaker successfully added to the talk.'))
@@ -496,11 +506,22 @@ def talk_acknowledgment(request, talk_id, confirm):
talk.save()
if confirm:
confirmation_message= _('The speaker confirmation have been noted.')
+ action = _('confirmed')
thread_note = _('The talk have been confirmed.')
else:
confirmation_message = _('The speaker unavailability have been noted.')
- thread_note = _('The talk have been cancelled.')
- Message.objects.create(thread=talk.conversation, author=request.user, content=thread_note)
+ action = _('cancelled')
+ thread_note = _('The talk have been %(action)s.') % {'action': action}
+ send_message(
+ thread=talk.conversation,
+ author=request.user,
+ subject=_("[%(conference)s] The talk '%(talk)s' have been %(action)s.") % {
+ 'conference': request.conference,
+ 'talk': talk,
+ 'action': action,
+ },
+ content=thread_note,
+ )
messages.success(request, confirmation_message)
return redirect(reverse('talk-details', kwargs=dict(talk_id=talk_id)))
@@ -588,10 +609,20 @@ def talk_list(request):
talk = Talk.objects.get(site=request.conference.site, pk=talk_id)
if data['decision'] != None and data['decision'] != talk.accepted:
if data['decision']:
- note = _("The talk has been accepted.")
+ action = _('accepted')
else:
- note = _("The talk has been declined.")
- Message.objects.create(thread=talk.conversation, author=request.user, content=note)
+ action = _('declined')
+ note = _('The talk has been %(action)s.') % {'action': action}
+ send_message(
+ thread=talk.conversation,
+ author=request.user,
+ subject=_("[%(conference)s] The talk '%(talk)s' have been %(action)s") % {
+ 'conference': conference,
+ 'talk': talk,
+ 'action': action,
+ },
+ content=note,
+ )
talk.accepted = data['decision']
if data['track']:
talk.track = Track.objects.get(site=request.conference.site, slug=data['track'])
@@ -657,11 +688,21 @@ def talk_details(request, talk_id):
vote = None
message_form = MessageForm(request.POST or None)
if request.method == 'POST' and message_form.is_valid():
- message = message_form.save(commit=False)
- message.author = request.user
- message.from_email = request.user.email
- message.thread = talk.conversation
- message.save()
+ in_reply_to = talk.conversation.message_set.last()
+ subject=_("[%(conference)s] New comment about '%(talk)s'") % {
+ 'conference': request.conference,
+ 'talk': talk,
+ }
+ if in_reply_to:
+ # Maybe use in_reply_to.subject?
+ subject = 'Re: ' + subject
+ send_message(
+ thread=talk.conversation,
+ author=request.user,
+ subject=subject,
+ content=message_form.cleaned_data['content'],
+ in_reply_to=in_reply_to,
+ )
messages.success(request, _('Message sent!'))
return redirect(reverse('talk-details', args=[talk.pk]))
return render(request, 'cfp/staff/talk_details.html', {
@@ -686,17 +727,35 @@ def talk_decide(request, talk_id, accept):
if request.method == 'POST':
talk.accepted = accept
talk.save()
+ if accept:
+ action = _('accepted')
+ else:
+ action = _('declined')
# Does we need to send a notification to the proposer?
m = request.POST.get('message', '').strip()
if m:
for participant in talk.speakers.all():
- Message.objects.create(thread=talk.conversation, author=request.user, content=m)
+ send_message(
+ thread=talk.conversation,
+ author=request.conference,
+ subject=_("[%(conference)s] Your talk '%(talk)s' have been %(action)s") % {
+ 'conference': request.conference,
+ 'talk': talk,
+ 'action': action,
+ },
+ content=m,
+ )
# Save the decision in the talk's conversation
- if accept:
- note = _("The talk has been accepted.")
- else:
- note = _("The talk has been declined.")
- Message.objects.create(thread=talk.conversation, author=request.user, content=note)
+ send_message(
+ thread=talk.conversation,
+ author=request.user,
+ subject=_("[%(conference)s] The talk '%(talk)s' have been %(action)s") % {
+ 'conference': request.conference,
+ 'talk': talk,
+ 'action': action,
+ },
+ content=_('The talk has been %(action)s.') % {'action': action},
+ )
messages.success(request, _('Decision taken in account'))
return redirect(talk.get_absolute_url())
return render(request, 'cfp/staff/talk_decide.html', {
diff --git a/locale/fr/LC_MESSAGES/django.mo b/locale/fr/LC_MESSAGES/django.mo
index 2a9270e..fe90efc 100644
Binary files a/locale/fr/LC_MESSAGES/django.mo and b/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 21beb1b..572a747 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-11-25 22:23+0000\n"
-"PO-Revision-Date: 2017-11-25 23:24+0100\n"
+"POT-Creation-Date: 2017-11-30 10:05+0000\n"
+"PO-Revision-Date: 2017-11-30 11:06+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
@@ -22,41 +22,41 @@ msgstr ""
msgid "Email address"
msgstr "Adresse e-mail"
-#: accounts/models.py:10 cfp/models.py:110 cfp/models.py:462
+#: accounts/models.py:10 cfp/models.py:115 cfp/models.py:467
msgid "Phone number"
msgstr "Numéro de téléphone"
-#: accounts/models.py:11 cfp/models.py:463
+#: accounts/models.py:11 cfp/models.py:468
msgid "SMS prefered"
msgstr "SMS préférés"
-#: accounts/models.py:12 cfp/models.py:102
+#: accounts/models.py:12 cfp/models.py:107
#: cfp/templates/cfp/proposal_dashboard.html:33
#: cfp/templates/cfp/staff/participant_details.html:15
msgid "Biography"
msgstr "Biographie"
-#: accounts/models.py:14 cfp/models.py:104
+#: accounts/models.py:14 cfp/models.py:109
msgid "Twitter"
msgstr "Twitter"
-#: accounts/models.py:15 cfp/models.py:105
+#: accounts/models.py:15 cfp/models.py:110
msgid "LinkedIn"
msgstr "LinkedIn"
-#: accounts/models.py:16 cfp/models.py:106
+#: accounts/models.py:16 cfp/models.py:111
msgid "Github"
msgstr "Github"
-#: accounts/models.py:17 cfp/models.py:107
+#: accounts/models.py:17 cfp/models.py:112
msgid "Website"
msgstr "Site web"
-#: accounts/models.py:18 cfp/models.py:108
+#: accounts/models.py:18 cfp/models.py:113
msgid "Facebook"
msgstr "Facebook"
-#: accounts/models.py:19 cfp/models.py:109
+#: accounts/models.py:19 cfp/models.py:114
msgid "Mastodon"
msgstr "Mastodon"
@@ -114,15 +114,15 @@ msgstr "Décliné"
msgid "Waiting"
msgstr "En attente"
-#: cfp/forms.py:29 cfp/forms.py:131 cfp/forms.py:228 cfp/models.py:374
+#: cfp/forms.py:29 cfp/forms.py:131 cfp/forms.py:228 cfp/models.py:379
msgid "Confirmed"
msgstr "Confirmé"
-#: cfp/forms.py:30 cfp/models.py:376
+#: cfp/forms.py:30 cfp/models.py:381
msgid "Cancelled"
msgstr "Annulé"
-#: cfp/forms.py:62 cfp/models.py:505
+#: cfp/forms.py:62 cfp/models.py:510
msgid "Activity"
msgstr "Activité"
@@ -145,13 +145,13 @@ msgstr "Catégorie"
msgid "Title"
msgstr "Titre"
-#: cfp/forms.py:109 cfp/models.py:158 cfp/models.py:500
+#: cfp/forms.py:109 cfp/models.py:163 cfp/models.py:505
#: cfp/templates/cfp/proposal_talk_details.html:75
#: cfp/templates/cfp/staff/talk_details.html:64
msgid "Description"
msgstr "Description"
-#: cfp/forms.py:110 cfp/models.py:112 cfp/models.py:465
+#: cfp/forms.py:110 cfp/models.py:117 cfp/models.py:470
#: cfp/templates/cfp/staff/participant_details.html:19
#: cfp/templates/cfp/staff/talk_details.html:82
#: cfp/templates/cfp/staff/volunteer_details.html:22
@@ -162,7 +162,7 @@ msgstr "Notes"
msgid "Visible by speakers"
msgstr "Visible par les orateurs"
-#: cfp/forms.py:137 cfp/forms.py:234 cfp/models.py:330
+#: cfp/forms.py:137 cfp/forms.py:234 cfp/models.py:335
#: cfp/templates/cfp/staff/talk_details.html:21
#: cfp/templates/cfp/staff/talk_list.html:46
#: cfp/templates/cfp/staff/track_form.html:14
@@ -200,7 +200,7 @@ msgstr "Programmé"
msgid "Filter talks already / not yet scheduled"
msgstr "Filtrer les exposés déjà / pas encore planifiées"
-#: cfp/forms.py:161 cfp/models.py:348
+#: cfp/forms.py:161 cfp/models.py:353
#: cfp/templates/cfp/proposal_talk_details.html:89
#: cfp/templates/cfp/staff/talk_details.html:54
msgid "Materials"
@@ -243,7 +243,7 @@ msgstr "Assigner à une salle"
msgid "Notify by mail?"
msgstr "Notifier par e-mail ?"
-#: cfp/forms.py:250 cfp/models.py:460
+#: cfp/forms.py:250 cfp/models.py:465
#: cfp/templates/cfp/staff/volunteer_list.html:30
msgid "Email"
msgstr "E-mail"
@@ -318,7 +318,11 @@ msgstr "Date d’ouverture de l’appel à bénévole"
msgid "Volunteers enrollment closing date"
msgstr "Date de fermeture de l’appel à bénévole"
-#: cfp/models.py:80
+#: cfp/models.py:44
+msgid "Video publishing date"
+msgstr "Date de publication des vidéos"
+
+#: cfp/models.py:85
#, python-brace-format
msgid ""
"The reply email should be a formatable string accepting a token argument (e."
@@ -327,53 +331,53 @@ msgstr ""
"L’adresse de réponse doit être une chaine de texte formatable avec un "
"argument « token » (e.g. ponyconf+{token}@exemple.com)."
-#: cfp/models.py:100 cfp/models.py:156 cfp/models.py:178 cfp/models.py:208
-#: cfp/models.py:498 cfp/templates/cfp/staff/participant_list.html:42
+#: cfp/models.py:105 cfp/models.py:161 cfp/models.py:183 cfp/models.py:213
+#: cfp/models.py:503 cfp/templates/cfp/staff/participant_list.html:42
#: cfp/templates/cfp/staff/volunteer_list.html:29
msgid "Name"
msgstr "Nom"
-#: cfp/models.py:113
+#: cfp/models.py:118
msgid "This field is only visible by organizers."
msgstr "Ce champ est uniquement visible par les organisateurs."
-#: cfp/models.py:114 cfp/templates/cfp/staff/participant_details.html:25
+#: cfp/models.py:119 cfp/templates/cfp/staff/participant_details.html:25
msgid "Invited speaker"
msgstr "Orateur invité"
-#: cfp/models.py:180
+#: cfp/models.py:185
msgid "Label"
msgstr "Étiquette"
-#: cfp/models.py:181
+#: cfp/models.py:186
msgid "Capacity"
msgstr "Capacité"
-#: cfp/models.py:210
+#: cfp/models.py:215
msgid "Color"
msgstr "Couleur"
-#: cfp/models.py:212
+#: cfp/models.py:217
msgid "Show the tag on the public program"
msgstr "Afficher l’étiquette sur le programme public"
-#: cfp/models.py:213
+#: cfp/models.py:218
msgid "Show the tag on the staff program"
msgstr "Afficher l’étiquette sur le programme organisateur"
-#: cfp/models.py:249
+#: cfp/models.py:254
msgid "Default duration (min)"
msgstr "Durée par défaut (min)"
-#: cfp/models.py:250
+#: cfp/models.py:255
msgid "Color on program"
msgstr "Couleur sur le programme"
-#: cfp/models.py:251
+#: cfp/models.py:256
msgid "Label on program"
msgstr "Label dans le xml du programme"
-#: cfp/models.py:325 cfp/templates/cfp/proposal_talk_details.html:53
+#: cfp/models.py:330 cfp/templates/cfp/proposal_talk_details.html:53
#: cfp/templates/cfp/staff/base.html:10
#: cfp/templates/cfp/staff/participant_list.html:8
#: cfp/templates/cfp/staff/talk_details.html:68
@@ -381,23 +385,23 @@ msgstr "Label dans le xml du programme"
msgid "Speakers"
msgstr "Orateurs"
-#: cfp/models.py:326
+#: cfp/models.py:331
msgid "Talk Title"
msgstr "Titre de la proposition"
-#: cfp/models.py:328
+#: cfp/models.py:333
msgid "Description of your talk"
msgstr "Description de votre proposition"
-#: cfp/models.py:329
+#: cfp/models.py:334
msgid "This description will be visible on the program."
msgstr "Cette description sera visible sur le programme."
-#: cfp/models.py:332 cfp/templates/cfp/proposal_talk_details.html:99
+#: cfp/models.py:337 cfp/templates/cfp/proposal_talk_details.html:99
msgid "Message to organizers"
msgstr "Message aux organisateurs"
-#: cfp/models.py:333
+#: cfp/models.py:338
msgid ""
"If you have any constraint or if you have anything that may help you to "
"select your talk, like a video or slides of your talk, please write it down "
@@ -407,84 +411,69 @@ msgstr ""
"votre proposition, comme une vidéo, des slides, n'hésitez pas à les ajouter "
"ici. Ce champ ne sera visible que par les organisateurs."
-#: cfp/models.py:337
+#: cfp/models.py:342
msgid "Talk Category"
msgstr "Catégorie de proposition"
-#: cfp/models.py:338
+#: cfp/models.py:343
msgid "I'm ok to be recorded on video"
msgstr "J’accepte d’être enregistré en vidéo"
-#: cfp/models.py:340
+#: cfp/models.py:345
msgid "Video licence"
msgstr "Licence vidéo"
-#: cfp/models.py:341
+#: cfp/models.py:346
msgid "I need sound"
msgstr "J’ai besoin de son"
-#: cfp/models.py:344
+#: cfp/models.py:349
msgid "Beginning date and time"
msgstr "Date et heure de début"
-#: cfp/models.py:345
+#: cfp/models.py:350
msgid "Duration (min)"
msgstr "Durée (min)"
-#: cfp/models.py:349
+#: cfp/models.py:354
msgid ""
"You can use this field to share some materials related to your intervention."
msgstr ""
"Vous pouvez utiliser ce champ pour partager les supports de votre "
"intervention."
-#: cfp/models.py:378
+#: cfp/models.py:383
msgid "Waiting confirmation"
msgstr "En attente de confirmation"
-#: cfp/models.py:380
+#: cfp/models.py:385
msgid "Refused"
msgstr "Refusé"
-#: cfp/models.py:382
+#: cfp/models.py:387
#, python-format
msgid "Pending decision, score: %(score).1f"
msgstr "En cours, score : %(score).1f"
-#: cfp/models.py:459
+#: cfp/models.py:464
msgid "Your Name"
msgstr "Votre Nom"
-#: cfp/models.py:466
+#: cfp/models.py:471
msgid "If you have some constraints, you can indicate them here."
msgstr "Si vous avez des contraintes, vous pouvez les indiquer ici."
-#: cfp/models.py:501 cfp/templates/cfp/staff/volunteer_details.html:8
+#: cfp/models.py:506 cfp/templates/cfp/staff/volunteer_details.html:8
msgid "Volunteer"
msgstr "Bénévole"
-#: cfp/models.py:506 cfp/templates/cfp/admin/activity_list.html:9
+#: cfp/models.py:511 cfp/templates/cfp/admin/activity_list.html:9
#: cfp/templates/cfp/admin/base.html:14
#: cfp/templates/cfp/staff/volunteer_details.html:27
#: cfp/templates/cfp/staff/volunteer_list.html:32
msgid "Activities"
msgstr "Activités"
-#: cfp/signals.py:80
-#, python-format
-msgid "[%(prefix)s] Message from the staff"
-msgstr "[%(prefix)s] Message du staff"
-
-#: cfp/signals.py:81
-#, python-format
-msgid "[%(prefix)s] Conversation with %(dest)s"
-msgstr "[%(prefix)s] Conversation avec %(dest)s"
-
-#: cfp/signals.py:97
-#, python-format
-msgid "[%(prefix)s] Talk: %(talk)s"
-msgstr "[%(prefix)s] Talk: %(talk)s"
-
#: cfp/templates/cfp/admin/activity_form.html:16
msgid "Edit activity"
msgstr "Édition d’une activité"
@@ -746,11 +735,13 @@ msgstr "et"
msgid "you must confirm you participation"
msgstr "vous devez confirmer votre participation"
-#: cfp/templates/cfp/proposal_dashboard.html:61
+#: cfp/templates/cfp/proposal_dashboard.html:61 cfp/views.py:612
+#: cfp/views.py:731
msgid "accepted"
msgstr "accepté"
-#: cfp/templates/cfp/proposal_dashboard.html:63
+#: cfp/templates/cfp/proposal_dashboard.html:63 cfp/views.py:366
+#: cfp/views.py:513
msgid "cancelled"
msgstr "annulé"
@@ -1271,34 +1262,40 @@ msgstr ""
"{}\n"
"\n"
-#: cfp/views.py:85 cfp/views.py:121
-msgid "Thank you for your help!"
-msgstr "Merci pour votre aide !"
+#: cfp/views.py:81
+#, python-format
+msgid "[%(conference)s] Thank you for your help!"
+msgstr "[%(conference)s] Merci pour votre aide !"
-#: cfp/views.py:90
+#: cfp/views.py:84
msgid ""
"Thank you for your participation! You can now subscribe to some activities."
msgstr ""
"Merci pour votre participation ! Vous pouvez maintenant vous inscrire à une "
"ou plusieurs activités."
-#: cfp/views.py:104 cfp/views.py:278
+#: cfp/views.py:98 cfp/views.py:269
msgid "Sorry, we do not know this email."
msgstr "Désolé, nous ne connaissons pas cette e-mail."
-#: cfp/views.py:126 cfp/views.py:303
+#: cfp/views.py:111 cfp/views.py:291
+#, python-format
+msgid "[%(conference)s] Someone asked to access your profil"
+msgstr "[%(conference)s] Quelqu’un a demandé à accéder à votre profil"
+
+#: cfp/views.py:114 cfp/views.py:296
msgid "A email have been sent with a link to access to your profil."
msgstr "Un e-mail vous a été envoyé avec un lien pour accéder à votre profil."
-#: cfp/views.py:147
+#: cfp/views.py:135
msgid "Thank you for your participation!"
msgstr "Merci pour votre participation !"
-#: cfp/views.py:151
+#: cfp/views.py:139
msgid "Okay, no problem!"
msgstr "Ok, pas de soucis !"
-#: cfp/views.py:234
+#: cfp/views.py:222
msgid ""
"Hi {},\n"
"\n"
@@ -1338,15 +1335,20 @@ msgstr ""
"{}\n"
"\n"
-#: cfp/views.py:264 cfp/views.py:345
+#: cfp/views.py:249
+#, python-format
+msgid "[%(conference)s] Thank you for your proposition '%(talk)s'"
+msgstr "[%(conference)s] Merci pour votre proposition « %(talk)s »"
+
+#: cfp/views.py:255 cfp/views.py:338
msgid "You proposition have been successfully submitted!"
msgstr "Votre proposition a été transmise avec succès !"
-#: cfp/views.py:283
+#: cfp/views.py:274
msgid ""
"Hi {},\n"
"\n"
-"Someone, probably you, ask to access your profile.\n"
+"Someone, probably you, asked to access your profile.\n"
"You can edit your talks or add new ones following this url:\n"
"\n"
" {}\n"
@@ -1373,37 +1375,41 @@ msgstr ""
"{}\n"
"\n"
-#: cfp/views.py:341 cfp/views.py:414
+#: cfp/views.py:334 cfp/views.py:421
msgid "Changes saved."
msgstr "Modifications sauvegardées."
-#: cfp/views.py:362
+#: cfp/views.py:355
msgid "You already confirmed your participation to this talk."
msgstr "Vous avez déjà confirmé votre participation à cet exposé."
-#: cfp/views.py:364
+#: cfp/views.py:357
msgid "You already cancelled your participation to this talk."
msgstr "Vous avez déjà annulé votre participation à cet exposé."
-#: cfp/views.py:369
+#: cfp/views.py:362
msgid "Your participation has been taken into account, thank you!"
msgstr "Votre participation a été prise en compte, merci !"
-#: cfp/views.py:370
-#, python-format
-msgid "Speaker %(speaker)s confirmed his/her participation."
-msgstr "L’intervenant %(speaker)s a confirmé sa participation."
+#: cfp/views.py:363 cfp/views.py:509
+msgid "confirmed"
+msgstr "confirmé"
-#: cfp/views.py:372
+#: cfp/views.py:365
msgid "We have noted your unavailability."
msgstr "Nous avons enregistré votre indisponibilité."
-#: cfp/views.py:373
+#: cfp/views.py:367
#, python-format
-msgid "Speaker %(speaker)s CANCELLED his/her participation."
-msgstr "L’intervenant %(speaker)s a ANNULÉ sa participation."
+msgid "Speaker %(speaker)s %(action)s his/her participation for %(talk)s."
+msgstr "L’orateur %(speaker)s a %(action) sa participation pour %(talk)s."
-#: cfp/views.py:421
+#: cfp/views.py:375
+#, python-format
+msgid "[%(conference)s] %(speaker)s %(action)s his/her participation"
+msgstr "[%(conference)s] %(speaker)s a %(action) sa participation"
+
+#: cfp/views.py:428
msgid ""
"Hi {},\n"
"\n"
@@ -1443,63 +1449,91 @@ msgstr ""
"{}\n"
"\n"
-#: cfp/views.py:452 cfp/views.py:472
+#: cfp/views.py:456
+#, python-format
+msgid "[%(conference)s] You have been added as co-speaker to '%(talk)s'"
+msgstr ""
+"[%(conference)s] Vous avez été ajouté comme co-intervenant pour « %(talk)s »"
+
+#: cfp/views.py:462 cfp/views.py:482
msgid "Co-speaker successfully added to the talk."
msgstr "Co-intervenant ajouté à l’exposé avec succès."
-#: cfp/views.py:485
+#: cfp/views.py:495
msgid "Co-speaker successfully removed from the talk."
msgstr "Co-intervenant supprimé de l’exposé avec succès."
-#: cfp/views.py:498
+#: cfp/views.py:508
msgid "The speaker confirmation have been noted."
msgstr "La confirmation de l’orateur a été notée."
-#: cfp/views.py:499
+#: cfp/views.py:510
msgid "The talk have been confirmed."
msgstr "L’exposé a été confirmé."
-#: cfp/views.py:501
+#: cfp/views.py:512
msgid "The speaker unavailability have been noted."
msgstr "L’indisponibilité de l’intervenant a été notée."
-#: cfp/views.py:502
-msgid "The talk have been cancelled."
-msgstr "L’exposé a été annulé."
+#: cfp/views.py:514
+#, python-format
+msgid "The talk have been %(action)s."
+msgstr "L’exposé a été %(action)s."
-#: cfp/views.py:591 cfp/views.py:696
-msgid "The talk has been accepted."
-msgstr "L’exposé a été accepté."
+#: cfp/views.py:518
+#, python-format
+msgid "[%(conference)s] The talk '%(talk)s' have been %(action)s."
+msgstr "[%(conference)s] L’exposé « %(talk)s » a été %(action)s."
-#: cfp/views.py:593 cfp/views.py:698
-msgid "The talk has been declined."
-msgstr "L’exposé a été décliné."
+#: cfp/views.py:614 cfp/views.py:733
+msgid "declined"
+msgstr "décliné"
-#: cfp/views.py:665 cfp/views.py:784
+#: cfp/views.py:615 cfp/views.py:757
+#, python-format
+msgid "The talk has been %(action)s."
+msgstr "L’exposé a été %(action)s."
+
+#: cfp/views.py:619 cfp/views.py:752
+#, python-format
+msgid "[%(conference)s] The talk '%(talk)s' have been %(action)s"
+msgstr "[%(conference)s] L’exposé « %(talk)s » a été %(action)s"
+
+#: cfp/views.py:692
+#, python-format
+msgid "[%(conference)s] New comment about '%(talk)s'"
+msgstr "[%(conference)s] Nouveau commentaire sur « %(talk)s »"
+
+#: cfp/views.py:706 cfp/views.py:843
msgid "Message sent!"
msgstr "Message envoyé !"
-#: cfp/views.py:679
+#: cfp/views.py:720
msgid "Vote successfully created"
msgstr "A voté !"
-#: cfp/views.py:679
+#: cfp/views.py:720
msgid "Vote successfully updated"
msgstr "Vote mis à jour"
-#: cfp/views.py:700
+#: cfp/views.py:741
+#, python-format
+msgid "[%(conference)s] Your talk '%(talk)s' have been %(action)s"
+msgstr "[%(conference)s] Votre exposé « %(talk)s » a été %(action)s"
+
+#: cfp/views.py:759
msgid "Decision taken in account"
msgstr "Décision enregistrée"
-#: cfp/views.py:718
+#: cfp/views.py:777
msgid "Speaker removed from this talk"
msgstr "Intervenant supprimé de l’exposé avec succès"
-#: cfp/views.py:843
+#: cfp/views.py:904
msgid "[{}] You have been added to the staff team"
msgstr "[{}] Vous avez été ajouté aux membres du staff"
-#: cfp/views.py:844
+#: cfp/views.py:905
msgid ""
"Hi {},\n"
"\n"
@@ -1523,20 +1557,20 @@ msgstr ""
"{}\n"
"\n"
-#: cfp/views.py:865 cfp/views.py:877
+#: cfp/views.py:926 cfp/views.py:938
msgid "Modifications successfully saved."
msgstr "Modification enregistrée avec succès."
-#: cfp/views.py:1038
+#: cfp/views.py:1101
msgid "User created successfully."
msgstr "Utilisateur créé avec succès."
-#: cfp/views.py:1059
+#: cfp/views.py:1122
#, python-format
msgid "Format '%s' not available"
msgstr "Format '%s' non disponible"
-#: mailing/models.py:103
+#: mailing/models.py:106
#, python-format
msgid "Message from %(author)s"
msgstr "Message de %(author)s"
@@ -1605,6 +1639,24 @@ msgstr "Mot de passe oublié ?"
msgid "Password Change"
msgstr "Changement de mot de passe"
+#~ msgid "[%(prefix)s] Message from the staff"
+#~ msgstr "[%(prefix)s] Message du staff"
+
+#~ msgid "[%(prefix)s] Conversation with %(dest)s"
+#~ msgstr "[%(prefix)s] Conversation avec %(dest)s"
+
+#~ msgid "[%(prefix)s] Talk: %(talk)s"
+#~ msgstr "[%(prefix)s] Talk: %(talk)s"
+
+#~ msgid "Speaker %(speaker)s CANCELLED his/her participation."
+#~ msgstr "L’intervenant %(speaker)s a ANNULÉ sa participation."
+
+#~ msgid "The talk have been cancelled."
+#~ msgstr "L’exposé a été annulé."
+
+#~ msgid "The talk has been declined."
+#~ msgstr "L’exposé a été décliné."
+
#~ msgid "Contact:"
#~ msgstr "Contacter :"
diff --git a/mailing/forms.py b/mailing/forms.py
index 80eab2f..38bb35e 100644
--- a/mailing/forms.py
+++ b/mailing/forms.py
@@ -3,4 +3,4 @@ from django.forms.models import modelform_factory
from .models import Message
-MessageForm = modelform_factory(Message, fields=['content'])
+MessageForm = modelform_factory(Message, fields=['subject', 'content'])
diff --git a/mailing/migrations/0003_auto_20171129_2155.py b/mailing/migrations/0003_auto_20171129_2155.py
new file mode 100644
index 0000000..9d7ae61
--- /dev/null
+++ b/mailing/migrations/0003_auto_20171129_2155.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.7 on 2017-11-29 21:55
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import mailing.models
+
+
+def forward(apps, schema_editor):
+ db_alias = schema_editor.connection.alias
+ Message = apps.get_model("mailing", "Message")
+ MessageAuthor = apps.get_model("mailing", "MessageAuthor")
+ for message in Message.objects.using(db_alias).all():
+ message.new_author, _ = MessageAuthor.objects.using(db_alias).get_or_create(author_type=message.author_type, author_id=message.author_id)
+ message.save()
+
+
+def backward(apps, schema_editor):
+ db_alias = schema_editor.connection.alias
+ Message = apps.get_model("mailing", "Message")
+ ContentType = apps.get_model("contenttypes", "ContentType")
+ for message in Message.objects.using(db_alias).all():
+ author_type = message.new_author.author_type
+ message.author_type = message.new_author.author_type
+ message.author_id = message.new_author.author_id
+ AuthorType = apps.get_model(author_type.app_label, author_type.model)
+ author = AuthorType.objects.get(pk=message.author_id)
+ if author_type.model == 'conference':
+ message.from_email = author.contact_email
+ else:
+ message.from_email = author.email
+ message.save()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('contenttypes', '0002_remove_content_type_name'),
+ ('mailing', '0002_message_author'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='MessageAuthor',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('author_id', models.PositiveIntegerField(blank=True, null=True)),
+ ('token', models.CharField(default=mailing.models.generate_message_token, max_length=64, unique=True)),
+ ('author_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
+ ],
+ ),
+ migrations.AddField(
+ model_name='message',
+ name='new_author',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='mailing.MessageAuthor'),
+ preserve_default=False,
+ ),
+ migrations.AlterField(
+ model_name='message',
+ name='from_email',
+ field=models.EmailField(blank=True, null=True),
+ ),
+ migrations.RunPython(forward, backward),
+ migrations.RemoveField(
+ model_name='message',
+ name='author_id',
+ ),
+ migrations.RemoveField(
+ model_name='message',
+ name='author_type',
+ ),
+ migrations.RemoveField(
+ model_name='message',
+ name='from_email',
+ ),
+ migrations.RenameField(
+ model_name='message',
+ old_name='new_author',
+ new_name='author',
+ ),
+ migrations.AlterField(
+ model_name='message',
+ name='author',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mailing.MessageAuthor'),
+ ),
+ migrations.AddField(
+ model_name='message',
+ name='in_reply_to',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mailing.Message'),
+ ),
+ migrations.AddField(
+ model_name='message',
+ name='subject',
+ field=models.CharField(blank=True, max_length=1000),
+ ),
+ ]
diff --git a/mailing/models.py b/mailing/models.py
index 2e039d9..82a6455 100644
--- a/mailing/models.py
+++ b/mailing/models.py
@@ -27,6 +27,20 @@ class MessageCorrespondent(models.Model):
token = models.CharField(max_length=64, default=generate_message_token, unique=True)
+class MessageAuthor(models.Model):
+ author_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
+ author_id = models.PositiveIntegerField(null=True, blank=True)
+ author = GenericForeignKey('author_type', 'author_id')
+ token = models.CharField(max_length=64, default=generate_message_token, unique=True)
+
+ def __str__(self):
+ author_class = self.author_type.model_class()
+ if author_class == get_user_model():
+ return self.author.get_full_name()
+ else:
+ return str(self.author)
+
+
class MessageThread(models.Model):
created = models.DateTimeField(auto_now_add=True)
token = models.CharField(max_length=64, default=generate_message_token, unique=True)
@@ -36,17 +50,16 @@ class MessageManager(models.Manager):
def get_queyset(self):
qs = super().get_queryset()
# Does not work so well as prefetch_related is limited to one content type for generic foreign keys
- qs = qs.prefetch_related('author')
+ qs = qs.prefetch_related('author__author')
return qs
class Message(models.Model):
created = models.DateTimeField(auto_now_add=True)
thread = models.ForeignKey(MessageThread)
- author_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
- author_id = models.PositiveIntegerField(null=True, blank=True)
- author = GenericForeignKey('author_type', 'author_id')
- from_email = models.EmailField()
+ author = models.ForeignKey(MessageAuthor)
+ in_reply_to = models.ForeignKey('self', null=True, blank=True)
+ subject = models.CharField(max_length=1000, blank=True)
content = models.TextField(blank=True)
token = models.CharField(max_length=64, default=generate_message_token, unique=True)
@@ -55,11 +68,12 @@ class Message(models.Model):
class Meta:
ordering = ['created']
- def send_notification(self, subject, sender, dests, reply_to=None, message_id=None, reference=None, footer=None):
+ def send_notification(self, sender, dests, reply_to=None, message_id=None, reference=None, footer=None):
messages = []
- for dest_name, dest_email in dests:
- correspondent, created = MessageCorrespondent.objects.get_or_create(email=dest_email)
- token = self.thread.token + correspondent.token + hexdigest_sha256(settings.SECRET_KEY, self.thread.token, correspondent.token)[:16]
+ for dest, dest_name, dest_email in dests:
+ dest_type = ContentType.objects.get_for_model(dest)
+ dest, _ = MessageAuthor.objects.get_or_create(author_type=dest_type, author_id=dest.pk)
+ token = self.token + dest.token + hexdigest_sha256(settings.SECRET_KEY, self.token, dest.token)[:16]
if reply_to:
reply_to_name, reply_to_email = reply_to
reply_to_list = ['%s <%s>' % (reply_to_name, reply_to_email.format(token=token))]
@@ -78,7 +92,7 @@ class Message(models.Model):
if footer is not None:
body += footer
messages.append(EmailMessage(
- subject=subject,
+ subject=self.subject,
body=body,
from_email='%s <%s>' % sender,
to=['%s <%s>' % (dest_name, dest_email)],
@@ -88,16 +102,5 @@ class Message(models.Model):
connection = get_connection()
connection.send_messages(messages)
- @property
- def author_display(self):
- if self.author:
- author_class = ContentType.objects.get_for_model(self.author).model_class()
- if author_class == get_user_model():
- return self.author.get_full_name()
- else:
- return str(self.author)
- else:
- return self.from_email
-
def __str__(self):
- return _("Message from %(author)s") % {'author': self.author_display}
+ return _("Message from %(author)s") % {'author': str(self.author)}
diff --git a/mailing/templates/mailing/_message_list.html b/mailing/templates/mailing/_message_list.html
index f79d128..8bc49a9 100644
--- a/mailing/templates/mailing/_message_list.html
+++ b/mailing/templates/mailing/_message_list.html
@@ -1,9 +1,9 @@
{% load i18n %}
{% for message in messages %}
-
+
- {{ message.created }} | {{ message.author_display }}
+ {{ message.created }} | {{ message.author }} | {{ message.subject }}
{{ message.content|linebreaksbr }}
diff --git a/mailing/utils.py b/mailing/utils.py
index 4317e89..683ae95 100644
--- a/mailing/utils.py
+++ b/mailing/utils.py
@@ -1,5 +1,6 @@
from django.conf import settings
from django.db import models
+from django.contrib.contenttypes.models import ContentType
import imaplib
import ssl
@@ -9,7 +10,7 @@ from email.parser import BytesParser
import chardet
import re
-from .models import MessageThread, MessageCorrespondent, Message, hexdigest_sha256
+from .models import MessageThread, MessageAuthor, Message, hexdigest_sha256
class NoTokenFoundException(Exception):
@@ -22,12 +23,24 @@ class InvalidKeyException(Exception):
pass
-def fetch_imap_box(user, password, host, port=993, ssl=True, inbox='INBOX', trash='Trash'):
+def send_message(thread, author, subject, content, in_reply_to=None):
+ author_type = ContentType.objects.get_for_model(author)
+ author, _ = MessageAuthor.objects.get_or_create(author_type=author_type, author_id=author.pk)
+ Message.objects.create(
+ thread=thread,
+ author=author,
+ subject=subject,
+ content=content,
+ in_reply_to=in_reply_to,
+ )
+
+
+def fetch_imap_box(user, password, host, port=993, use_ssl=True, inbox='INBOX', trash='Trash'):
logging.basicConfig(level=logging.DEBUG)
context = ssl.create_default_context()
success, failure = 0, 0
kwargs = {'host': host, 'port': port}
- if ssl:
+ if use_ssl:
IMAP4 = imaplib.IMAP4_SSL
kwargs.update({'ssl_context': ssl.create_default_context()})
else:
@@ -116,7 +129,32 @@ def process_email(raw_email):
raise NoTokenFoundException
token = m.group('token')
+
+ try:
+ in_reply_to, author = process_new_token(token)
+ except InvalidTokenException:
+ in_reply_to, author = process_old_token(token)
+
+ subject = msg.get('Subject', '')
+
+ Message.objects.create(thread=in_reply_to.thread, in_reply_to=in_reply_to, author=author, subject=subject, content=content)
+
+
+def process_new_token(token):
key = token[64:]
+ try:
+ in_reply_to = Message.objects.get(token__iexact=token[:32])
+ author = MessageAuthor.objects.get(token__iexact=token[32:64])
+ except models.ObjectDoesNotExist:
+ raise InvalidTokenException
+
+ if key.lower() != hexdigest_sha256(settings.SECRET_KEY, in_reply_to.token, author.token)[:16]:
+ raise InvalidKeyException
+
+ return in_reply_to, author
+
+
+def process_old_token(token):
try:
thread = MessageThread.objects.get(token__iexact=token[:32])
sender = MessageCorrespondent.objects.get(token__iexact=token[32:64])
@@ -126,4 +164,25 @@ def process_email(raw_email):
if key.lower() != hexdigest_sha256(settings.SECRET_KEY, thread.token, sender.token)[:16]:
raise InvalidKeyException
- Message.objects.create(thread=thread, from_email=sender.email, content=content)
+ in_reply_to = thread.message_set.last()
+ author = None
+
+ if author is None:
+ try:
+ author = User.objects.get(email=sender.email)
+ except User.DoesNotExist:
+ pass
+ if author is None:
+ try:
+ author = Participant.objects.get(email=message.from_email)
+ except Participant.DoesNotExist:
+ pass
+ if author is None:
+ try:
+ author = Conference.objects.get(contact_email=message.from_email)
+ except Conference.DoesNotExist:
+ raise # this was last hope...
+
+ author = MessageAuthor.objects.get_or_create(author=author)
+
+ return in_reply_to, author