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:
parent
eb807a6853
commit
24371b629a
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}))
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue