reenable mails to Content team when speaker/eventproposals are created/updated, change so proposal URLs are opened in new window, add a message in browser when proposals are approved in the admin

This commit is contained in:
Thomas Steen Rasmussen 2018-05-26 10:24:52 +02:00
parent eb807a6853
commit 24371b629a
7 changed files with 74 additions and 13 deletions

View file

@ -24,7 +24,7 @@ from .models import (
class SpeakerProposalAdmin(admin.ModelAdmin): class SpeakerProposalAdmin(admin.ModelAdmin):
def mark_speakerproposal_as_approved(self, request, queryset): def mark_speakerproposal_as_approved(self, request, queryset):
for sp in queryset: for sp in queryset:
sp.mark_as_approved() sp.mark_as_approved(request)
mark_speakerproposal_as_approved.description = 'Approve and create Speaker object(s)' mark_speakerproposal_as_approved.description = 'Approve and create Speaker object(s)'
actions = ['mark_speakerproposal_as_approved'] actions = ['mark_speakerproposal_as_approved']
@ -43,7 +43,7 @@ class EventProposalAdmin(admin.ModelAdmin):
return False return False
else: else:
try: try:
ep.mark_as_approved() ep.mark_as_approved(request)
except ValidationError as e: except ValidationError as e:
messages.error(request, e) messages.error(request, e)
return False return False

View file

@ -247,7 +247,7 @@ class SpeakerProposal(UserSubmittedModel):
def get_absolute_url(self): def get_absolute_url(self):
return reverse_lazy('program:speakerproposal_detail', kwargs={'camp_slug': self.camp.slug, 'pk': self.uuid}) return reverse_lazy('program:speakerproposal_detail', kwargs={'camp_slug': self.camp.slug, 'pk': self.uuid})
def mark_as_approved(self): def mark_as_approved(self, request):
speakermodel = apps.get_model('program', 'speaker') speakermodel = apps.get_model('program', 'speaker')
speakerproposalmodel = apps.get_model('program', 'speakerproposal') speakerproposalmodel = apps.get_model('program', 'speakerproposal')
speaker = speakermodel() speaker = speakermodel()
@ -261,6 +261,16 @@ class SpeakerProposal(UserSubmittedModel):
self.proposal_status = speakerproposalmodel.PROPOSAL_APPROVED self.proposal_status = speakerproposalmodel.PROPOSAL_APPROVED
self.save() self.save()
# copy all the URLs too
for url in self.urls.all():
Url.objects.create(
url=url.url,
urltype=url.urltype,
speaker=speaker
)
messages.success(request, "Speaker object %s has been created" % speaker)
class EventProposal(UserSubmittedModel): class EventProposal(UserSubmittedModel):
""" An event proposal """ """ An event proposal """
@ -336,11 +346,11 @@ class EventProposal(UserSubmittedModel):
user=self.user user=self.user
).exclude(uuid__in=self.speakers.all().values_list('uuid')) ).exclude(uuid__in=self.speakers.all().values_list('uuid'))
def mark_as_approved(self): def mark_as_approved(self, request):
eventmodel = apps.get_model('program', 'event') eventmodel = apps.get_model('program', 'event')
eventproposalmodel = apps.get_model('program', 'eventproposal') eventproposalmodel = apps.get_model('program', 'eventproposal')
event = eventmodel() event = eventmodel()
event.camp = self.camp event.track = self.track
event.title = self.title event.title = self.title
event.abstract = self.abstract event.abstract = self.abstract
event.event_type = self.event_type event.event_type = self.event_type
@ -358,6 +368,15 @@ class EventProposal(UserSubmittedModel):
self.proposal_status = eventproposalmodel.PROPOSAL_APPROVED self.proposal_status = eventproposalmodel.PROPOSAL_APPROVED
self.save() self.save()
# copy all the URLs too
for url in self.urls.all():
Url.objects.create(
url=url.url,
urltype=url.urltype,
event=event
)
messages.success(request, "Event object %s has been created" % event)
############################################################################### ###############################################################################

View file

@ -15,7 +15,7 @@
<tr> <tr>
<td><span class="h4">{{ eventproposal.title }}</span></td> <td><span class="h4">{{ eventproposal.title }}</span></td>
<td><i class="fas fa-{{ eventproposal.event_type.icon }} fa-lg" style="color: {{ eventproposal.event_type.color }};"></i><span class="h4"> {{ eventproposal.event_type }}</span></td> <td><i class="fas fa-{{ eventproposal.event_type.icon }} fa-lg" style="color: {{ eventproposal.event_type.color }};"></i><span class="h4"> {{ eventproposal.event_type }}</span></td>
<td><span class="h4">{% for url in eventproposal.urls.all %}<a href="{{ url.url }}"><i class="fas fa-{{ url.urltype.icon }}" data-toggle="tooltip" title="{{ url.urltype.name }}"></i></a> {% empty %}N/A{% endfor %}</span></td> <td><span class="h4">{% for url in eventproposal.urls.all %}<a href="{{ url.url }}" target="_blank"><i class="fas fa-{{ url.urltype.icon }}" data-toggle="tooltip" title="{{ url.urltype.name }}"></i></a> {% empty %}N/A{% endfor %}</span></td>
<td><span class="h4">{% for person in eventproposal.speakers.all %}<a href="{% url 'program:speakerproposal_detail' camp_slug=camp.slug pk=person.uuid %}"><i class="fas fa-user" data-toggle="tooltip" title="{{ person.name }}"></i></a> {% endfor %}</span></td> <td><span class="h4">{% for person in eventproposal.speakers.all %}<a href="{% url 'program:speakerproposal_detail' camp_slug=camp.slug pk=person.uuid %}"><i class="fas fa-user" data-toggle="tooltip" title="{{ person.name }}"></i></a> {% endfor %}</span></td>
<td><span class="h4">{{ eventproposal.track.name }}</span></td> <td><span class="h4">{{ eventproposal.track.name }}</span></td>
<td><span class="badge">{{ eventproposal.proposal_status }}</span></td> <td><span class="badge">{{ eventproposal.proposal_status }}</span></td>

View file

@ -10,7 +10,7 @@
{% for url in eventproposal.urls.all %} {% for url in eventproposal.urls.all %}
<tr> <tr>
<td><i class="fas fa-{{ url.urltype.icon }} fa-lg"></i><span class="h4"> {{ url.urltype.name }}</span></td> <td><i class="fas fa-{{ url.urltype.icon }} fa-lg"></i><span class="h4"> {{ url.urltype.name }}</span></td>
<td><span class="h4"><a href="{{ url.url }}">{{ url }}</a></span></td> <td><span class="h4"><a href="{{ url.url }}" target="_blank">{{ url }}</a></span></td>
<td class='text-right'> <td class='text-right'>
{% if not camp.read_only %} {% if not camp.read_only %}
<a href="{% url 'program:eventproposalurl_update' camp_slug=camp.slug event_uuid=eventproposal.uuid url_uuid=url.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-edit"></i><span class="h5"> Update</span></a> <a href="{% url 'program:eventproposalurl_update' camp_slug=camp.slug event_uuid=eventproposal.uuid url_uuid=url.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-edit"></i><span class="h5"> Update</span></a>

View file

@ -23,7 +23,7 @@
</td> </td>
<td class="text-center"> <td class="text-center">
{% for url in speakerproposal.urls.all %} {% for url in speakerproposal.urls.all %}
<a href="{{ url.url }}" data-toggle="tooltip" title="{{ url.urltype }}"><i class="fas fa-{{ url.urltype.icon }}"></i></a> <a href="{{ url.url }}" target="_blank" data-toggle="tooltip" title="{{ url.urltype }}"><i class="fas fa-{{ url.urltype.icon }}"></i></a>
{% empty %} {% empty %}
N/A N/A
{% endfor %} {% endfor %}

View file

@ -10,7 +10,7 @@
{% for url in speakerproposal.urls.all %} {% for url in speakerproposal.urls.all %}
<tr> <tr>
<td><i class="fas fa-{{ url.urltype.icon }} fa-lg"></i><span class="h4"> {{ url.urltype.name }}</span></td> <td><i class="fas fa-{{ url.urltype.icon }} fa-lg"></i><span class="h4"> {{ url.urltype.name }}</span></td>
<td><span class="h4"><a href="{{ url.url }}">{{ url }}</a></span></td> <td><span class="h4"><a href="{{ url.url }}" target="_blank">{{ url }}</a></span></td>
<td class='text-right'> <td class='text-right'>
{% if not camp.read_only %} {% if not camp.read_only %}
<a href="{% url 'program:speakerproposalurl_update' camp_slug=camp.slug speaker_uuid=speakerproposal.uuid url_uuid=url.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-edit"></i><span class="h5"> Update</span></a> <a href="{% url 'program:speakerproposalurl_update' camp_slug=camp.slug speaker_uuid=speakerproposal.uuid url_uuid=url.uuid %}" class="btn btn-success btn-sm"><i class="fas fa-edit"></i><span class="h5"> Update</span></a>

View file

@ -28,6 +28,8 @@ from .mixins import (
UrlViewMixin, UrlViewMixin,
) )
from .email import ( from .email import (
add_new_eventproposal_email,
add_new_speakerproposal_email,
add_speakerproposal_updated_email, add_speakerproposal_updated_email,
add_eventproposal_updated_email add_eventproposal_updated_email
) )
@ -151,6 +153,10 @@ class SpeakerProposalCreateView(LoginRequiredMixin, CampViewMixin, EnsureWritabl
# add speakerproposal to eventproposal # add speakerproposal to eventproposal
self.eventproposal.speakers.add(speakerproposal) self.eventproposal.speakers.add(speakerproposal)
# send mail to content team
if not add_new_speakerproposal_email(speakerproposal):
logger.error("Unable to send email to content team after new speakerproposal")
return redirect( return redirect(
reverse('program:proposal_list', kwargs={'camp_slug': self.camp.slug}) reverse('program:proposal_list', kwargs={'camp_slug': self.camp.slug})
) )
@ -163,8 +169,6 @@ class SpeakerProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureWritabl
model = models.SpeakerProposal model = models.SpeakerProposal
template_name = 'speakerproposal_form.html' template_name = 'speakerproposal_form.html'
def get_success_url(self):
return reverse('program:proposal_list', kwargs={'camp_slug': self.camp.slug})
def get_form_class(self): def get_form_class(self):
""" Get the appropriate form class based on the eventtype """ """ Get the appropriate form class based on the eventtype """
@ -181,6 +185,22 @@ class SpeakerProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureWritabl
# more than one type of event for this person, return the generic speakerproposal form # more than one type of event for this person, return the generic speakerproposal form
return BaseSpeakerProposalForm return BaseSpeakerProposalForm
def form_valid(self, form):
"""
Change the speakerproposal status to pending
"""
# set proposal status to pending
form.instance.proposal_status = models.SpeakerProposal.PROPOSAL_PENDING
speakerproposal = form.save()
# send mail to content team
if not add_speakerproposal_updated_email(speakerproposal):
logger.error("Unable to send email to content team after speakerproposal update")
# message user and redirect
messages.info(self.request, "Your proposal is now pending approval by the content team.")
return redirect(reverse('program:proposal_list', kwargs={'camp_slug': self.camp.slug}))
class SpeakerProposalDeleteView(LoginRequiredMixin, CampViewMixin, EnsureWritableCampMixin, EnsureUserOwnsProposalMixin, EnsureCFPOpenMixin, DeleteView): class SpeakerProposalDeleteView(LoginRequiredMixin, CampViewMixin, EnsureWritableCampMixin, EnsureUserOwnsProposalMixin, EnsureCFPOpenMixin, DeleteView):
""" """
@ -391,6 +411,10 @@ class EventProposalCreateView(LoginRequiredMixin, CampViewMixin, EnsureWritableC
# add the speakerproposal to the eventproposal # add the speakerproposal to the eventproposal
eventproposal.speakers.add(self.speakerproposal) eventproposal.speakers.add(self.speakerproposal)
# send mail to content team
if not add_new_eventproposal_email(eventproposal):
logger.error("Unable to send email to content team after new eventproposal")
# all good # all good
return redirect(self.get_success_url()) return redirect(self.get_success_url())
@ -406,8 +430,6 @@ class EventProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureWritableC
""" Get the appropriate form class based on the eventtype """ """ Get the appropriate form class based on the eventtype """
return get_eventproposal_form_class(self.get_object().event_type) return get_eventproposal_form_class(self.get_object().event_type)
def get_success_url(self):
return reverse('program:proposal_list', kwargs={'camp_slug': self.camp.slug})
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
""" Make speakerproposal and eventtype objects available in the template """ """ Make speakerproposal and eventtype objects available in the template """
@ -425,6 +447,19 @@ class EventProposalUpdateView(LoginRequiredMixin, CampViewMixin, EnsureWritableC
form.fields['track'].queryset = models.EventTrack.objects.filter(camp=self.camp) form.fields['track'].queryset = models.EventTrack.objects.filter(camp=self.camp)
return form return form
def form_valid(self, form):
# set status to pending and save eventproposal
form.instance.proposal_status = models.EventProposal.PROPOSAL_PENDING
eventproposal = form.save()
# send email to content team
if not add_eventproposal_updated_email(eventproposal):
logger.error("Unable to send email to content team after eventproposal update")
# message for the user and redirect
messages.info(self.request, "Your proposal is now pending approval by the content team.")
return redirect(reverse('program:proposal_list', kwargs={'camp_slug': self.camp.slug}))
class EventProposalDeleteView(LoginRequiredMixin, CampViewMixin, EnsureWritableCampMixin, EnsureUserOwnsProposalMixin, EnsureCFPOpenMixin, DeleteView): class EventProposalDeleteView(LoginRequiredMixin, CampViewMixin, EnsureWritableCampMixin, EnsureUserOwnsProposalMixin, EnsureCFPOpenMixin, DeleteView):
model = models.EventProposal model = models.EventProposal
@ -543,6 +578,13 @@ class CombinedProposalSubmitView(LoginRequiredMixin, CampViewMixin, CreateView):
# add the speakerproposal to the eventproposal # add the speakerproposal to the eventproposal
eventproposal.speakers.add(speakerproposal) eventproposal.speakers.add(speakerproposal)
# send mail(s) to content team
if not add_new_eventproposal_email(eventproposal):
logger.error("Unable to send email to content team after new eventproposal")
if not hasattr(self, 'speakerproposal'):
if not add_new_speakerproposal_email(speakerproposal):
logger.error("Unable to send email to content team after new speakerproposal")
# all good # all good
return redirect(reverse_lazy('program:proposal_list', kwargs={'camp_slug': self.camp.slug})) return redirect(reverse_lazy('program:proposal_list', kwargs={'camp_slug': self.camp.slug}))