2017-06-05 23:12:47 +00:00
|
|
|
from django.core.mail import send_mail
|
|
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
2017-11-19 22:39:25 +00:00
|
|
|
from django.template.loader import render_to_string
|
2017-12-10 12:32:19 +00:00
|
|
|
from django.urls import reverse, reverse_lazy
|
2017-06-05 23:12:47 +00:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2017-11-28 00:10:28 +00:00
|
|
|
from django.views.generic import DeleteView, FormView, TemplateView
|
2017-07-30 14:57:38 +00:00
|
|
|
from django.contrib import messages
|
2017-12-02 01:34:23 +00:00
|
|
|
from django.db.models import Q, Count, Sum
|
2017-08-02 17:34:07 +00:00
|
|
|
from django.views.generic import CreateView, DetailView, ListView, UpdateView
|
2017-12-02 01:34:23 +00:00
|
|
|
from django.http import HttpResponse, Http404, HttpResponseServerError
|
2017-08-15 18:52:12 +00:00
|
|
|
from django.utils import timezone
|
|
|
|
from django.core.exceptions import PermissionDenied
|
2017-10-30 17:05:57 +00:00
|
|
|
from django.core.mail import send_mail
|
2017-11-05 20:37:34 +00:00
|
|
|
from django.forms import modelform_factory
|
2017-12-02 01:34:23 +00:00
|
|
|
from django import forms
|
|
|
|
from django.views.decorators.http import require_http_methods
|
2019-06-08 11:02:19 +00:00
|
|
|
from django.core.cache import cache
|
2017-07-30 14:57:38 +00:00
|
|
|
|
2017-07-30 18:11:13 +00:00
|
|
|
from django_select2.views import AutoResponseView
|
|
|
|
|
2017-08-01 12:25:29 +00:00
|
|
|
from functools import reduce
|
2017-11-08 19:17:56 +00:00
|
|
|
import csv
|
2017-08-01 12:25:29 +00:00
|
|
|
|
2017-08-01 23:45:38 +00:00
|
|
|
from mailing.forms import MessageForm
|
2017-11-30 19:34:12 +00:00
|
|
|
from mailing.utils import send_message
|
2017-08-15 11:26:15 +00:00
|
|
|
from .planning import Program
|
2017-11-06 20:47:31 +00:00
|
|
|
from .decorators import speaker_required, volunteer_required, staff_required
|
2017-08-13 15:03:20 +00:00
|
|
|
from .mixins import StaffRequiredMixin, OnSiteMixin, OnSiteFormMixin
|
2017-07-30 16:07:09 +00:00
|
|
|
from .utils import is_staff
|
2017-10-09 17:54:04 +00:00
|
|
|
from .models import Participant, Talk, TalkCategory, Vote, Track, Tag, Room, Volunteer, Activity
|
2017-12-17 12:04:17 +00:00
|
|
|
from .emails import talk_email_send, talk_email_render_preview, \
|
|
|
|
speaker_email_send, speaker_email_render_preview, \
|
|
|
|
volunteer_email_send, volunteer_email_render_preview
|
|
|
|
from .forms import TalkForm, TalkStaffForm, TalkFilterForm, get_talk_speaker_form_class, \
|
|
|
|
TalkActionForm, SpeakerActionForm, VolunteerActionForm, \
|
2017-11-05 20:37:34 +00:00
|
|
|
ParticipantForm, ParticipantFilterForm, NotifyForm, \
|
2017-11-25 22:19:24 +00:00
|
|
|
ConferenceForm, HomepageForm, CreateUserForm, TrackForm, RoomForm, \
|
2017-12-17 12:04:17 +00:00
|
|
|
VolunteerForm, VolunteerFilterForm, EmailForm, \
|
|
|
|
PreviewTalkMailForm, PreviewSpeakerMailForm, PreviewVolunteerMailForm, \
|
|
|
|
SendTalkMailForm, SendSpeakerMailForm, SendVolunteerMailForm, \
|
2017-11-07 23:18:46 +00:00
|
|
|
TagForm, TalkCategoryForm, ActivityForm, \
|
2017-10-06 16:45:58 +00:00
|
|
|
ACCEPTATION_VALUES, CONFIRMATION_VALUES
|
2017-07-30 14:57:38 +00:00
|
|
|
|
2017-06-05 23:12:47 +00:00
|
|
|
|
2017-08-12 12:17:00 +00:00
|
|
|
def home(request):
|
|
|
|
if request.conference.home:
|
2017-07-30 14:57:38 +00:00
|
|
|
return render(request, 'cfp/home.html')
|
|
|
|
else:
|
2017-11-04 14:30:00 +00:00
|
|
|
return redirect(reverse('proposal-home'))
|
2017-05-30 20:27:45 +00:00
|
|
|
|
|
|
|
|
2017-10-05 23:33:57 +00:00
|
|
|
def volunteer_enrole(request):
|
2017-12-10 12:32:19 +00:00
|
|
|
if request.user.is_authenticated and Volunteer.objects.filter(site=request.conference.site, email=request.user.email).exists():
|
2017-11-25 21:05:47 +00:00
|
|
|
return redirect(reverse('volunteer-dashboard'))
|
2017-10-05 23:33:57 +00:00
|
|
|
if not request.conference.volunteers_enrollment_is_open():
|
|
|
|
raise PermissionDenied
|
2017-11-19 20:41:03 +00:00
|
|
|
initial = {}
|
2017-12-10 12:32:19 +00:00
|
|
|
if request.user.is_authenticated and not request.POST:
|
2017-11-20 19:12:51 +00:00
|
|
|
initial.update({
|
|
|
|
'name': request.user.get_full_name(),
|
|
|
|
'phone_number': request.user.profile.phone_number,
|
|
|
|
'sms_prefered': request.user.profile.sms_prefered,
|
|
|
|
})
|
2017-11-19 20:41:03 +00:00
|
|
|
form = VolunteerForm(request.POST or None, initial=initial, conference=request.conference)
|
2017-12-10 12:32:19 +00:00
|
|
|
if request.user.is_authenticated:
|
2017-11-19 20:41:03 +00:00
|
|
|
form.fields.pop('email')
|
2017-10-05 23:33:57 +00:00
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
volunteer = form.save(commit=False)
|
|
|
|
volunteer.language = request.LANGUAGE_CODE
|
2017-12-10 12:32:19 +00:00
|
|
|
if request.user.is_authenticated:
|
2017-11-19 20:41:03 +00:00
|
|
|
volunteer.email = request.user.email
|
2017-10-05 23:33:57 +00:00
|
|
|
volunteer.save()
|
2017-12-01 18:54:41 +00:00
|
|
|
form.save_m2m()
|
2017-10-30 17:05:57 +00:00
|
|
|
body = _("""Hi {},
|
|
|
|
|
|
|
|
Thank your for your help in the organization of the conference {}!
|
|
|
|
|
|
|
|
You can update your availability at anytime:
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
Thanks!
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
2017-11-04 14:59:58 +00:00
|
|
|
""").format(volunteer.name, request.conference.name, volunteer.get_secret_url(full=True), request.conference.name)
|
2017-11-30 19:34:12 +00:00
|
|
|
send_message(
|
|
|
|
thread=volunteer.conversation,
|
|
|
|
author=request.conference,
|
|
|
|
subject=_('[%(conference)s] Thank you for your help!') % {'conference': request.conference},
|
|
|
|
content=body,
|
2017-10-30 17:05:57 +00:00
|
|
|
)
|
2017-10-05 23:33:57 +00:00
|
|
|
messages.success(request, _('Thank you for your participation! You can now subscribe to some activities.'))
|
2017-11-25 21:41:21 +00:00
|
|
|
return redirect(reverse('volunteer-dashboard', kwargs={'volunteer_token': volunteer.token}))
|
2017-10-05 23:33:57 +00:00
|
|
|
return render(request, 'cfp/volunteer_enrole.html', {
|
|
|
|
'activities': Activity.objects.filter(site=request.conference.site),
|
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-11-19 22:39:25 +00:00
|
|
|
def volunteer_mail_token(request):
|
2017-12-02 01:34:23 +00:00
|
|
|
form = EmailForm(request.POST or None)
|
2017-11-19 22:39:25 +00:00
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
try:
|
|
|
|
volunteer = Volunteer.objects.get(site=request.conference.site, email=form.cleaned_data['email'])
|
|
|
|
except Volunteer.DoesNotExist:
|
|
|
|
messages.error(request, _('Sorry, we do not know this email.'))
|
|
|
|
else:
|
|
|
|
|
|
|
|
base_url = ('https' if request.is_secure() else 'http') + '://' + request.conference.site.domain
|
2017-11-25 21:05:47 +00:00
|
|
|
url = base_url + reverse('volunteer-dashboard', kwargs=dict(volunteer_token=volunteer.token))
|
2017-11-19 22:39:25 +00:00
|
|
|
body = render_to_string('cfp/mails/volunteer_send_token.txt', {
|
|
|
|
'volunteer': volunteer,
|
|
|
|
'url': url,
|
|
|
|
'conf': request.conference
|
|
|
|
})
|
2017-11-30 19:34:12 +00:00
|
|
|
send_message(
|
|
|
|
thread=volunteer.conversation,
|
|
|
|
author=request.conference,
|
|
|
|
subject=_("[%(conference)s] Someone asked to access your profil") % {'conference': request.conference},
|
|
|
|
content=body,
|
2017-11-19 22:39:25 +00:00
|
|
|
)
|
2023-01-22 17:31:49 +00:00
|
|
|
messages.success(request, _('An email has been sent with a link to access to your profil.'))
|
2017-11-19 22:39:25 +00:00
|
|
|
return redirect(reverse('volunteer-mail-token'))
|
|
|
|
return render(request, 'cfp/volunteer_mail_token.html', {
|
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-11-06 20:47:31 +00:00
|
|
|
@volunteer_required
|
2017-11-25 21:05:47 +00:00
|
|
|
def volunteer_dashboard(request, volunteer):
|
2017-12-01 20:46:15 +00:00
|
|
|
return render(request, 'cfp/volunteer_dashboard.html', {
|
2017-10-05 23:33:57 +00:00
|
|
|
'activities': Activity.objects.filter(site=request.conference.site),
|
|
|
|
'volunteer': volunteer,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-12-01 20:37:40 +00:00
|
|
|
@volunteer_required
|
|
|
|
def volunteer_profile(request, volunteer):
|
|
|
|
form = VolunteerForm(request.POST or None, instance=volunteer, conference=request.conference)
|
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
form.save()
|
|
|
|
messages.success(request, _('Changes saved.'))
|
|
|
|
return redirect(reverse('volunteer-dashboard', kwargs={'volunteer_token': volunteer.token}))
|
|
|
|
return render(request, 'cfp/volunteer_profile.html', {
|
|
|
|
'volunteer': volunteer,
|
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-11-06 20:47:31 +00:00
|
|
|
@volunteer_required
|
|
|
|
def volunteer_update_activity(request, volunteer, activity, join):
|
2017-10-05 23:33:57 +00:00
|
|
|
activity = get_object_or_404(Activity, slug=activity, site=request.conference.site)
|
|
|
|
if join:
|
2017-12-01 18:54:41 +00:00
|
|
|
volunteer.activities.add(activity)
|
2017-10-05 23:33:57 +00:00
|
|
|
messages.success(request, _('Thank you for your participation!'))
|
|
|
|
else:
|
2017-12-01 18:54:41 +00:00
|
|
|
volunteer.activities.remove(activity)
|
2017-10-05 23:33:57 +00:00
|
|
|
messages.success(request, _('Okay, no problem!'))
|
2017-11-25 21:05:47 +00:00
|
|
|
return redirect(reverse('volunteer-dashboard', kwargs=dict(volunteer_token=volunteer.token)))
|
2017-10-05 23:33:57 +00:00
|
|
|
|
|
|
|
|
2017-10-30 13:06:08 +00:00
|
|
|
@staff_required
|
|
|
|
def volunteer_list(request):
|
|
|
|
site = request.conference.site
|
|
|
|
filter_form = VolunteerFilterForm(request.GET or None, site=site)
|
|
|
|
# Filtering
|
2017-11-08 19:17:56 +00:00
|
|
|
show_filters = False
|
2017-11-03 14:17:33 +00:00
|
|
|
volunteers = Volunteer.objects.filter(site=site).order_by('pk').distinct().prefetch_related('activities')
|
2017-10-30 13:06:08 +00:00
|
|
|
if filter_form.is_valid():
|
|
|
|
data = filter_form.cleaned_data
|
|
|
|
if len(data['activity']):
|
|
|
|
show_filters = True
|
2017-11-03 13:53:35 +00:00
|
|
|
q = Q()
|
|
|
|
if 'none' in data['activity']:
|
|
|
|
data['activity'].remove('none')
|
|
|
|
q |= Q(activities__isnull=True)
|
|
|
|
if len(data['activity']):
|
|
|
|
q |= Q(activities__slug__in=data['activity'])
|
|
|
|
volunteers = volunteers.filter(q)
|
2017-12-17 12:04:17 +00:00
|
|
|
# Action
|
|
|
|
action_form = VolunteerActionForm(request.POST or None, volunteers=volunteers)
|
|
|
|
if request.method == 'POST' and action_form.is_valid():
|
|
|
|
data = action_form.cleaned_data
|
|
|
|
if data['email']:
|
|
|
|
request.session['volunteer-email-list'] = data['volunteers']
|
|
|
|
return redirect(reverse('volunteer-email'))
|
|
|
|
return redirect(request.get_full_path())
|
2017-11-08 19:17:56 +00:00
|
|
|
if request.GET.get('format') == 'csv':
|
|
|
|
response = HttpResponse(content_type='text/csv')
|
|
|
|
response['Content-Disposition'] = 'attachment; filename="volunteers.csv"'
|
|
|
|
writer = csv.writer(response)
|
|
|
|
for volunteer in volunteers:
|
|
|
|
writer.writerow(volunteer.get_csv_row())
|
|
|
|
return response
|
|
|
|
else:
|
|
|
|
contact_link = 'mailto:' + ','.join([volunteer.email for volunteer in volunteers.all()])
|
|
|
|
csv_query_dict = request.GET.copy()
|
|
|
|
csv_query_dict['format'] = 'csv'
|
|
|
|
csv_link = '?' + csv_query_dict.urlencode()
|
|
|
|
return render(request, 'cfp/staff/volunteer_list.html', {
|
|
|
|
'volunteer_list': volunteers,
|
|
|
|
'filter_form': filter_form,
|
2017-12-17 12:04:17 +00:00
|
|
|
'action_form': action_form,
|
2017-11-08 19:17:56 +00:00
|
|
|
'show_filters': show_filters,
|
|
|
|
'contact_link': contact_link,
|
|
|
|
'csv_link': csv_link,
|
2017-12-17 12:04:17 +00:00
|
|
|
'pending_email': bool(request.session.get('volunteer-email-list', None)),
|
2017-11-08 19:17:56 +00:00
|
|
|
})
|
2017-05-30 20:27:45 +00:00
|
|
|
|
2017-10-30 13:06:08 +00:00
|
|
|
|
2017-10-30 17:05:57 +00:00
|
|
|
@staff_required
|
|
|
|
def volunteer_details(request, volunteer_id):
|
2017-11-03 13:53:35 +00:00
|
|
|
volunteer = get_object_or_404(Volunteer, site=request.conference.site, pk=volunteer_id)
|
2017-12-17 12:39:57 +00:00
|
|
|
message_form = MessageForm(request.POST or None)
|
|
|
|
if request.method == 'POST' and message_form.is_valid():
|
2017-12-17 12:57:32 +00:00
|
|
|
in_reply_to = volunteer.conversation.message_set.last()
|
|
|
|
send_message(
|
|
|
|
thread=volunteer.conversation,
|
|
|
|
author=request.user,
|
|
|
|
subject='',
|
|
|
|
content=message_form.cleaned_data['content'],
|
|
|
|
in_reply_to=in_reply_to,
|
|
|
|
)
|
2017-12-17 12:39:57 +00:00
|
|
|
messages.success(request, _('Message sent!'))
|
|
|
|
return redirect(reverse('volunteer-details', args=[volunteer.pk]))
|
2017-11-03 13:53:35 +00:00
|
|
|
return render(request, 'cfp/staff/volunteer_details.html', {
|
|
|
|
'volunteer': volunteer,
|
|
|
|
})
|
2017-10-30 17:05:57 +00:00
|
|
|
|
|
|
|
|
2017-12-17 12:04:17 +00:00
|
|
|
@staff_required
|
|
|
|
def volunteer_email(request):
|
|
|
|
volunteers = Volunteer.objects.filter(pk__in=request.session.get('volunteer-email-list', []))
|
|
|
|
if not volunteers.exists():
|
|
|
|
messages.error(request, _('Please select some volunteers.'))
|
|
|
|
return redirect('volunteer-list')
|
|
|
|
form = SendVolunteerMailForm(request.POST or None, initial=request.session.get('volunteer-email-stored'), volunteers=volunteers)
|
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
subject = form.cleaned_data['subject']
|
|
|
|
body = form.cleaned_data['body']
|
|
|
|
request.session['volunteer-email-stored'] = {'subject': subject, 'body': body}
|
|
|
|
if form.cleaned_data['confirm']:
|
|
|
|
sent = volunteer_email_send(volunteers, subject, body)
|
|
|
|
messages.success(request, _('%(count)d mails have been sent.') % {'count': sent})
|
|
|
|
del request.session['volunteer-email-list']
|
|
|
|
return redirect('volunteer-list')
|
|
|
|
else:
|
2019-11-02 09:18:54 +00:00
|
|
|
messages.info(request, _('You are ready to send %(count)d emails.') % {'count': volunteers.count()})
|
2017-12-17 12:04:17 +00:00
|
|
|
else:
|
|
|
|
form.fields.pop('confirm')
|
|
|
|
return render(request, 'cfp/staff/volunteer_email.html', {
|
|
|
|
'volunteers': volunteers,
|
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
@require_http_methods(['POST'])
|
|
|
|
@staff_required
|
|
|
|
def volunteer_email_preview(request):
|
|
|
|
form = PreviewVolunteerMailForm(request.POST or None)
|
|
|
|
if not form.is_valid():
|
|
|
|
return HttpResponseServerError()
|
|
|
|
volunteer = get_object_or_404(Volunteer, site=request.conference.site, pk=form.cleaned_data['volunteer'])
|
|
|
|
preview = volunteer_email_render_preview(volunteer, form.cleaned_data['subject'], form.cleaned_data['body'])
|
|
|
|
return HttpResponse(preview)
|
|
|
|
|
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
def proposal_home(request):
|
2017-11-05 20:37:34 +00:00
|
|
|
categories = request.conference.opened_categories
|
2017-11-04 20:37:53 +00:00
|
|
|
if not categories.exists():
|
|
|
|
return render(request, 'cfp/closed.html')
|
2017-11-19 20:41:52 +00:00
|
|
|
initial = {}
|
|
|
|
fields = ['name', 'email', 'biography']
|
2017-12-10 12:32:19 +00:00
|
|
|
if request.user.is_authenticated:
|
2017-11-05 20:37:34 +00:00
|
|
|
if Participant.objects.filter(site=request.conference.site, email=request.user.email).exists():
|
|
|
|
return redirect(reverse('proposal-dashboard'))
|
2017-11-19 20:41:52 +00:00
|
|
|
elif not request.POST:
|
|
|
|
initial.update({
|
|
|
|
'name': request.user.get_full_name(),
|
2017-11-19 20:42:45 +00:00
|
|
|
'biography': request.user.profile.biography,
|
2017-11-19 20:41:52 +00:00
|
|
|
})
|
|
|
|
fields.remove('email')
|
|
|
|
NewSpeakerForm = modelform_factory(Participant, form=ParticipantForm, fields=fields)
|
|
|
|
speaker_form = NewSpeakerForm(request.POST or None, initial=initial, conference=request.conference)
|
2017-11-04 14:30:00 +00:00
|
|
|
talk_form = TalkForm(request.POST or None, categories=categories)
|
|
|
|
if request.method == 'POST' and all(map(lambda f: f.is_valid(), [speaker_form, talk_form])):
|
|
|
|
speaker = speaker_form.save(commit=False)
|
|
|
|
speaker.site = request.conference.site
|
2017-12-10 12:32:19 +00:00
|
|
|
if request.user.is_authenticated:
|
2017-11-05 20:37:34 +00:00
|
|
|
speaker.email = request.user.email
|
2017-11-04 14:30:00 +00:00
|
|
|
speaker.save()
|
2017-06-03 11:17:05 +00:00
|
|
|
talk = talk_form.save(commit=False)
|
2017-11-04 14:30:00 +00:00
|
|
|
talk.site = request.conference.site
|
2017-06-03 11:17:05 +00:00
|
|
|
talk.save()
|
2017-11-04 14:30:00 +00:00
|
|
|
talk.speakers.add(speaker)
|
|
|
|
base_url = ('https' if request.is_secure() else 'http') + '://' + request.conference.site.domain
|
|
|
|
url_dashboard = base_url + reverse('proposal-dashboard', kwargs=dict(speaker_token=speaker.token))
|
|
|
|
url_talk_details = base_url + reverse('proposal-talk-details', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk))
|
|
|
|
url_speaker_add = base_url + reverse('proposal-speaker-add', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk))
|
2017-08-01 23:45:38 +00:00
|
|
|
body = _("""Hi {},
|
2017-06-05 23:12:47 +00:00
|
|
|
|
|
|
|
Your talk has been submitted for {}.
|
|
|
|
|
|
|
|
Here are the details of your talk:
|
|
|
|
Title: {}
|
2017-06-06 22:06:32 +00:00
|
|
|
Description: {}
|
2017-06-05 23:12:47 +00:00
|
|
|
|
2017-06-06 07:32:29 +00:00
|
|
|
You can at anytime:
|
2017-11-04 14:30:00 +00:00
|
|
|
- review and edit your profile: {}
|
|
|
|
- review and edit your talk: {}
|
2017-06-06 07:32:29 +00:00
|
|
|
- add a new co-speaker: {}
|
2017-06-05 23:12:47 +00:00
|
|
|
|
|
|
|
If you have any question, your can answer to this email.
|
|
|
|
|
|
|
|
Thanks!
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
""").format(
|
2017-11-30 19:34:12 +00:00
|
|
|
speaker.name, request.conference.name, talk.title, talk.description,
|
2017-11-04 14:30:00 +00:00
|
|
|
url_dashboard, url_talk_details, url_speaker_add,
|
|
|
|
request.conference.name,
|
|
|
|
)
|
2017-11-30 19:34:12 +00:00
|
|
|
send_message(
|
2017-11-04 14:30:00 +00:00
|
|
|
thread=speaker.conversation,
|
|
|
|
author=request.conference,
|
2017-11-30 19:34:12 +00:00
|
|
|
subject=_("[%(conference)s] Thank you for your proposition '%(talk)s'") % {
|
|
|
|
'conference': request.conference.name,
|
|
|
|
'talk': talk,
|
|
|
|
},
|
2017-08-01 23:45:38 +00:00
|
|
|
content=body,
|
2017-06-05 23:12:47 +00:00
|
|
|
)
|
2023-01-22 17:31:49 +00:00
|
|
|
messages.success(request, _('Your proposition has been successfully submitted!'))
|
2017-11-04 14:30:00 +00:00
|
|
|
return redirect(reverse('proposal-talk-details', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk)))
|
|
|
|
return render(request, 'cfp/proposal_home.html', {
|
|
|
|
'speaker_form': speaker_form,
|
2017-06-05 23:12:47 +00:00
|
|
|
'talk_form': talk_form,
|
2017-06-03 11:17:05 +00:00
|
|
|
})
|
|
|
|
|
2017-05-30 20:27:45 +00:00
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
def proposal_mail_token(request):
|
2017-12-02 01:34:23 +00:00
|
|
|
form = EmailForm(request.POST or None)
|
2017-11-04 14:30:00 +00:00
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
try:
|
|
|
|
speaker = Participant.objects.get(site=request.conference.site, email=form.cleaned_data['email'])
|
|
|
|
except Participant.DoesNotExist:
|
|
|
|
messages.error(request, _('Sorry, we do not know this email.'))
|
|
|
|
else:
|
2017-06-05 23:12:47 +00:00
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
base_url = ('https' if request.is_secure() else 'http') + '://' + request.conference.site.domain
|
|
|
|
dashboard_url = base_url + reverse('proposal-dashboard', kwargs=dict(speaker_token=speaker.token))
|
|
|
|
body = _("""Hi {},
|
2017-06-05 23:12:47 +00:00
|
|
|
|
2017-11-30 19:34:12 +00:00
|
|
|
Someone, probably you, asked to access your profile.
|
2017-11-04 14:30:00 +00:00
|
|
|
You can edit your talks or add new ones following this url:
|
2017-06-05 23:12:47 +00:00
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
{}
|
2017-06-05 23:12:47 +00:00
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
If you have any question, your can answer to this email.
|
2017-06-05 23:12:47 +00:00
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
Sincerely,
|
2017-06-05 23:12:47 +00:00
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
""").format(speaker.name, dashboard_url, request.conference.name)
|
2017-11-30 19:34:12 +00:00
|
|
|
send_message(
|
2017-11-04 14:30:00 +00:00
|
|
|
thread=speaker.conversation,
|
|
|
|
author=request.conference,
|
2017-11-30 19:34:12 +00:00
|
|
|
subject=_("[%(conference)s] Someone asked to access your profil") % {
|
|
|
|
'conference': request.conference.name,
|
|
|
|
},
|
2017-11-04 14:30:00 +00:00
|
|
|
content=body,
|
|
|
|
)
|
2023-01-22 17:31:49 +00:00
|
|
|
messages.success(request, _('An email has been sent with a link to access to your profil.'))
|
2017-11-04 14:30:00 +00:00
|
|
|
return redirect(reverse('proposal-mail-token'))
|
|
|
|
return render(request, 'cfp/proposal_mail_token.html', {
|
|
|
|
'form': form,
|
|
|
|
})
|
2017-06-05 23:12:47 +00:00
|
|
|
|
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
@speaker_required
|
|
|
|
def proposal_dashboard(request, speaker):
|
|
|
|
return render(request, 'cfp/proposal_dashboard.html', {
|
|
|
|
'speaker': speaker,
|
|
|
|
'talks': speaker.talk_set.all(),
|
2017-06-05 23:12:47 +00:00
|
|
|
})
|
|
|
|
|
2017-07-30 14:57:38 +00:00
|
|
|
|
2017-11-04 14:30:00 +00:00
|
|
|
@speaker_required
|
|
|
|
def proposal_talk_details(request, speaker, talk_id):
|
|
|
|
talk = get_object_or_404(Talk, site=request.conference.site, speakers__pk=speaker.pk, pk=talk_id)
|
|
|
|
return render(request, 'cfp/proposal_talk_details.html', {
|
|
|
|
'speaker': speaker,
|
|
|
|
'talk': talk,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
@speaker_required
|
|
|
|
def proposal_talk_edit(request, speaker, talk_id=None):
|
|
|
|
if talk_id:
|
|
|
|
talk = get_object_or_404(Talk, site=request.conference.site, speakers__pk=speaker.pk, pk=talk_id)
|
|
|
|
else:
|
|
|
|
talk = None
|
2017-11-21 20:28:55 +00:00
|
|
|
categories = request.conference.opened_categories
|
2017-11-14 23:03:13 +00:00
|
|
|
form = TalkForm(request.POST or None, request.FILES or None, categories=categories, instance=talk)
|
2017-11-04 14:30:00 +00:00
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
talk = form.save(commit=False)
|
|
|
|
talk.site = request.conference.site
|
|
|
|
talk.save()
|
|
|
|
talk.speakers.add(speaker)
|
|
|
|
if talk_id:
|
|
|
|
messages.success(request, _('Changes saved.'))
|
|
|
|
else:
|
|
|
|
# TODO: it could be great to receive the proposition by mail
|
|
|
|
# but this is not crucial as the speaker already have a link in its mailbox
|
2023-01-22 17:31:49 +00:00
|
|
|
messages.success(request, _('Your proposition has been successfully submitted!'))
|
2017-11-04 14:30:00 +00:00
|
|
|
return redirect(reverse('proposal-talk-details', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk)))
|
|
|
|
return render(request, 'cfp/proposal_talk_form.html', {
|
|
|
|
'speaker': speaker,
|
|
|
|
'talk': talk,
|
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2018-10-14 19:02:22 +00:00
|
|
|
@speaker_required
|
|
|
|
def proposal_talk_acknowledgment(request, speaker, talk_id, confirm):
|
2017-10-06 16:45:58 +00:00
|
|
|
# TODO: handle multiple speakers case
|
2018-10-14 19:02:22 +00:00
|
|
|
talk = get_object_or_404(Talk, site=request.conference.site, speakers__pk=speaker.pk, pk=talk_id)
|
2019-06-08 11:02:19 +00:00
|
|
|
if not request.conference.disclosed_acceptances or not talk.accepted or request.conference.completed:
|
2018-10-14 19:02:22 +00:00
|
|
|
raise PermissionDenied
|
2017-11-04 14:30:00 +00:00
|
|
|
if talk.confirmed == confirm:
|
|
|
|
if confirm:
|
|
|
|
messages.warning(request, _('You already confirmed your participation to this talk.'))
|
|
|
|
else:
|
|
|
|
messages.warning(request, _('You already cancelled your participation to this talk.'))
|
2017-10-06 16:45:58 +00:00
|
|
|
else:
|
|
|
|
talk.confirmed = confirm
|
|
|
|
talk.save()
|
|
|
|
if confirm:
|
|
|
|
confirmation_message= _('Your participation has been taken into account, thank you!')
|
2017-11-30 19:34:12 +00:00
|
|
|
action = _('confirmed')
|
2017-10-06 16:45:58 +00:00
|
|
|
else:
|
|
|
|
confirmation_message = _('We have noted your unavailability.')
|
2017-11-30 19:34:12 +00:00
|
|
|
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,
|
|
|
|
)
|
2017-10-06 16:45:58 +00:00
|
|
|
messages.success(request, confirmation_message)
|
2017-11-27 19:32:27 +00:00
|
|
|
return redirect(reverse('proposal-talk-details', kwargs={'speaker_token': speaker.token, 'talk_id': talk.pk}))
|
2017-11-04 14:30:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
# FIXME his this view really useful?
|
|
|
|
#@speaker_required
|
|
|
|
#def proposal_speaker_details(request, speaker, talk_id, co_speaker_id):
|
|
|
|
# talk = get_object_or_404(Talk, site=request.conference.site, speakers__pk=speaker.pk, pk=talk_id)
|
|
|
|
# co_speaker = get_object_or_404(Participant, site=request.conference.site, talk_set__pk=talk.pk, pk=co_speaker_id)
|
|
|
|
# return render(request, 'cfp/proposal_speaker_details.html', {
|
|
|
|
# 'speaker': speaker,
|
|
|
|
# 'talk': talk,
|
|
|
|
# 'co_speaker': co_speaker,
|
|
|
|
# })
|
|
|
|
|
|
|
|
|
|
|
|
@speaker_required
|
|
|
|
def proposal_speaker_edit(request, speaker, talk_id=None, co_speaker_id=None):
|
2017-11-04 19:11:50 +00:00
|
|
|
talk, co_speaker, co_speaker_candidates = None, None, None
|
2017-11-04 14:30:00 +00:00
|
|
|
if talk_id:
|
|
|
|
talk = get_object_or_404(Talk, site=request.conference.site, speakers__pk=speaker.pk, pk=talk_id)
|
|
|
|
if co_speaker_id:
|
|
|
|
co_speaker = get_object_or_404(Participant, site=request.conference.site, talk__pk=talk.pk, pk=co_speaker_id)
|
|
|
|
else:
|
2017-11-04 19:11:50 +00:00
|
|
|
co_speaker_candidates = speaker.co_speaker_set.exclude(pk__in=talk.speakers.values_list('pk'))
|
2017-11-05 20:37:34 +00:00
|
|
|
EditSpeakerForm = modelform_factory(Participant, form=ParticipantForm, fields=['name', 'email', 'biography'] + ParticipantForm.SOCIAL_FIELDS)
|
2017-11-05 21:06:31 +00:00
|
|
|
all_forms = []
|
2017-11-05 20:37:34 +00:00
|
|
|
speaker_form = EditSpeakerForm(request.POST or None, conference=request.conference, instance=co_speaker if talk else speaker)
|
2017-11-05 21:06:31 +00:00
|
|
|
all_forms.append(speaker_form)
|
2017-11-05 20:37:34 +00:00
|
|
|
if talk and not co_speaker_id:
|
|
|
|
notify_form = NotifyForm(request.POST or None)
|
2017-11-05 21:06:31 +00:00
|
|
|
all_forms.append(notify_form)
|
2017-11-05 20:37:34 +00:00
|
|
|
else:
|
|
|
|
notify_form = None
|
2017-11-05 21:06:31 +00:00
|
|
|
if request.method == 'POST' and all(map(lambda f: f.is_valid(), all_forms)):
|
2017-11-05 20:37:34 +00:00
|
|
|
edited_speaker = speaker_form.save()
|
2017-11-04 14:30:00 +00:00
|
|
|
if talk:
|
|
|
|
talk.speakers.add(edited_speaker)
|
2017-11-04 20:12:58 +00:00
|
|
|
if co_speaker_id:
|
|
|
|
messages.success(request, _('Changes saved.'))
|
|
|
|
else:
|
2017-11-05 20:37:34 +00:00
|
|
|
if notify_form.cleaned_data['notify']:
|
2017-11-04 20:12:58 +00:00
|
|
|
base_url = ('https' if request.is_secure() else 'http') + '://' + request.conference.site.domain
|
|
|
|
url_dashboard = base_url + reverse('proposal-dashboard', kwargs=dict(speaker_token=edited_speaker.token))
|
|
|
|
url_talk_details = base_url + reverse('proposal-talk-details', kwargs=dict(speaker_token=edited_speaker.token, talk_id=talk.pk))
|
|
|
|
url_speaker_add = base_url + reverse('proposal-speaker-add', kwargs=dict(speaker_token=edited_speaker.token, talk_id=talk.pk))
|
|
|
|
body = _("""Hi {},
|
|
|
|
|
|
|
|
{} add you as a co-speaker for the conference {}.
|
|
|
|
|
|
|
|
Here is a summary of the talk:
|
|
|
|
Title: {}
|
|
|
|
Description: {}
|
|
|
|
|
|
|
|
You can at anytime:
|
|
|
|
- review and edit your profile: {}
|
|
|
|
- review and edit the talk: {}
|
|
|
|
- add another co-speaker: {}
|
|
|
|
|
|
|
|
If you have any question, your can answer to this email.
|
|
|
|
|
|
|
|
Thanks!
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
""").format(
|
|
|
|
edited_speaker.name, speaker.name, request.conference.name,
|
|
|
|
talk.title, talk.description,
|
|
|
|
url_dashboard, url_talk_details, url_speaker_add,
|
|
|
|
request.conference.name,
|
|
|
|
)
|
2017-11-30 19:34:12 +00:00
|
|
|
send_message(
|
2017-11-04 20:12:58 +00:00
|
|
|
thread=edited_speaker.conversation,
|
|
|
|
author=request.conference,
|
2017-11-30 19:34:12 +00:00
|
|
|
subject=_("[%(conference)s] You have been added as co-speaker to '%(talk)s'") % {
|
|
|
|
'conference': request.conference,
|
|
|
|
'talk': talk,
|
|
|
|
},
|
2017-11-04 20:12:58 +00:00
|
|
|
content=body,
|
|
|
|
)
|
|
|
|
messages.success(request, _('Co-speaker successfully added to the talk.'))
|
2017-11-04 14:30:00 +00:00
|
|
|
#return redirect(reverse('proposal-speaker-details', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk)))
|
|
|
|
return redirect(reverse('proposal-talk-details', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk)))
|
|
|
|
else:
|
|
|
|
return redirect(reverse('proposal-dashboard', kwargs=dict(speaker_token=speaker.token)))
|
|
|
|
return render(request, 'cfp/proposal_speaker_form.html', {
|
|
|
|
'speaker': speaker,
|
|
|
|
'talk': talk,
|
|
|
|
'co_speaker': co_speaker,
|
2017-11-04 19:11:50 +00:00
|
|
|
'co_speaker_candidates': co_speaker_candidates,
|
2017-11-05 20:37:34 +00:00
|
|
|
'speaker_form': speaker_form,
|
|
|
|
'notify_form': notify_form,
|
2017-11-04 14:30:00 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-11-04 19:11:50 +00:00
|
|
|
@speaker_required
|
|
|
|
def proposal_speaker_add(request, speaker, talk_id, speaker_id):
|
|
|
|
talk = get_object_or_404(Talk, site=request.conference.site, speakers__pk=speaker.pk, pk=talk_id)
|
2017-11-22 13:12:38 +00:00
|
|
|
co_speaker = get_object_or_404(Participant, pk__in=speaker.co_speaker_set.values_list('pk'), pk=speaker_id)
|
2017-11-04 19:11:50 +00:00
|
|
|
talk.speakers.add(co_speaker)
|
|
|
|
messages.success(request, _('Co-speaker successfully added to the talk.'))
|
|
|
|
return redirect(reverse('proposal-talk-details', kwargs=dict(speaker_token=speaker.token, talk_id=talk_id)))
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: ask for confirmation (with POST request needed)
|
2017-11-04 14:30:00 +00:00
|
|
|
@speaker_required
|
|
|
|
def proposal_speaker_remove(request, speaker, talk_id, co_speaker_id):
|
|
|
|
talk = get_object_or_404(Talk, site=request.conference.site, speakers__pk=speaker.pk, pk=talk_id)
|
2017-11-04 19:11:50 +00:00
|
|
|
co_speaker = get_object_or_404(Participant, site=request.conference.site, talk__pk=talk.pk, pk=co_speaker_id)
|
|
|
|
# prevent speaker from removing his/her self
|
|
|
|
if co_speaker.pk == speaker.pk:
|
|
|
|
raise PermissionDenied
|
|
|
|
talk.speakers.remove(co_speaker)
|
|
|
|
messages.success(request, _('Co-speaker successfully removed from the talk.'))
|
|
|
|
return redirect(reverse('proposal-talk-details', kwargs=dict(speaker_token=speaker.token, talk_id=talk_id)))
|
2017-11-04 14:30:00 +00:00
|
|
|
|
|
|
|
|
2017-11-25 21:20:05 +00:00
|
|
|
@staff_required
|
|
|
|
def talk_acknowledgment(request, talk_id, confirm):
|
2017-11-25 21:41:21 +00:00
|
|
|
talk = get_object_or_404(Talk, pk=talk_id, site=request.conference.site)
|
2017-11-27 19:32:27 +00:00
|
|
|
if talk.accepted is not True or talk.confirmed == confirm:
|
2017-11-04 14:30:00 +00:00
|
|
|
raise PermissionDenied
|
|
|
|
# TODO: handle multiple speakers case
|
|
|
|
talk.confirmed = confirm
|
|
|
|
talk.save()
|
|
|
|
if confirm:
|
2023-01-22 17:31:49 +00:00
|
|
|
confirmation_message= _('The speaker confirmation has been noted.')
|
2017-11-30 19:34:12 +00:00
|
|
|
action = _('confirmed')
|
2023-01-22 17:31:49 +00:00
|
|
|
thread_note = _('The talk has been confirmed.')
|
2017-10-06 16:45:58 +00:00
|
|
|
else:
|
2023-01-22 17:31:49 +00:00
|
|
|
confirmation_message = _('The speaker unavailability has been noted.')
|
2017-11-30 19:34:12 +00:00
|
|
|
action = _('cancelled')
|
2023-01-22 17:31:49 +00:00
|
|
|
thread_note = _('The talk has been %(action)s.') % {'action': action}
|
2017-11-30 19:34:12 +00:00
|
|
|
send_message(
|
|
|
|
thread=talk.conversation,
|
|
|
|
author=request.user,
|
2023-01-22 17:31:49 +00:00
|
|
|
subject=_("[%(conference)s] The talk '%(talk)s' has been %(action)s.") % {
|
2017-11-30 19:34:12 +00:00
|
|
|
'conference': request.conference,
|
|
|
|
'talk': talk,
|
|
|
|
'action': action,
|
|
|
|
},
|
|
|
|
content=thread_note,
|
|
|
|
)
|
2017-11-04 14:30:00 +00:00
|
|
|
messages.success(request, confirmation_message)
|
|
|
|
return redirect(reverse('talk-details', kwargs=dict(talk_id=talk_id)))
|
2017-10-06 16:45:58 +00:00
|
|
|
|
|
|
|
|
2017-07-30 14:57:38 +00:00
|
|
|
@staff_required
|
2017-08-12 12:17:00 +00:00
|
|
|
def staff(request):
|
2017-07-30 14:57:38 +00:00
|
|
|
return render(request, 'cfp/staff/base.html')
|
|
|
|
|
|
|
|
|
2017-10-09 18:31:45 +00:00
|
|
|
@staff_required
|
|
|
|
def admin(request):
|
|
|
|
return render(request, 'cfp/admin/base.html')
|
|
|
|
|
|
|
|
|
2017-07-30 14:57:38 +00:00
|
|
|
@staff_required
|
2017-08-12 12:17:00 +00:00
|
|
|
def talk_list(request):
|
|
|
|
talks = Talk.objects.filter(site=request.conference.site)
|
2017-08-01 12:25:29 +00:00
|
|
|
# Filtering
|
2017-08-12 20:24:55 +00:00
|
|
|
show_filters = False
|
|
|
|
filter_form = TalkFilterForm(request.GET or None, site=request.conference.site)
|
2017-08-01 12:25:29 +00:00
|
|
|
if filter_form.is_valid():
|
|
|
|
data = filter_form.cleaned_data
|
|
|
|
if len(data['category']):
|
|
|
|
show_filters = True
|
|
|
|
talks = talks.filter(reduce(lambda x, y: x | y, [Q(category__pk=pk) for pk in data['category']]))
|
2017-10-06 16:45:58 +00:00
|
|
|
if len(data['accepted']):
|
2017-08-01 12:25:29 +00:00
|
|
|
show_filters = True
|
2017-10-06 16:45:58 +00:00
|
|
|
talks = talks.filter(reduce(lambda x, y: x | y, [Q(accepted=dict(ACCEPTATION_VALUES)[status]) for status in data['accepted']]))
|
|
|
|
if len(data['confirmed']):
|
|
|
|
show_filters = True
|
|
|
|
talks = talks.filter(accepted=True)
|
|
|
|
talks = talks.filter(reduce(lambda x, y: x | y, [Q(confirmed=dict(CONFIRMATION_VALUES)[status]) for status in data['confirmed']]))
|
2017-08-12 00:05:53 +00:00
|
|
|
if data['room'] != None:
|
|
|
|
show_filters = True
|
|
|
|
talks = talks.filter(room__isnull=not data['room'])
|
|
|
|
if data['scheduled'] != None:
|
|
|
|
show_filters = True
|
|
|
|
talks = talks.filter(start_date__isnull=not data['scheduled'])
|
2017-10-09 17:48:33 +00:00
|
|
|
if len(data['tag']):
|
|
|
|
show_filters = True
|
|
|
|
talks = talks.filter(tags__slug__in=data['tag'])
|
2017-08-01 12:25:29 +00:00
|
|
|
if len(data['track']):
|
|
|
|
show_filters = True
|
|
|
|
q = Q()
|
|
|
|
if 'none' in data['track']:
|
|
|
|
data['track'].remove('none')
|
|
|
|
q |= Q(track__isnull=True)
|
|
|
|
if len(data['track']):
|
|
|
|
q |= Q(track__slug__in=data['track'])
|
|
|
|
talks = talks.filter(q)
|
|
|
|
if data['vote'] != None:
|
|
|
|
show_filters = True
|
|
|
|
if data['vote']:
|
|
|
|
talks = talks.filter(vote__user=request.user)
|
|
|
|
else:
|
|
|
|
talks = talks.exclude(vote__user=request.user)
|
2017-09-27 18:39:29 +00:00
|
|
|
if data['materials'] != None:
|
|
|
|
show_filters = True
|
2017-11-22 14:49:36 +00:00
|
|
|
materials_filter = Q(materials__isnull=False) & ~Q(materials__exact='')
|
|
|
|
if data['materials']:
|
|
|
|
talks = talks.filter(materials_filter)
|
|
|
|
else:
|
|
|
|
talks = talks.filter(~materials_filter)
|
2017-09-27 18:39:29 +00:00
|
|
|
if data['video'] != None:
|
|
|
|
show_filters = True
|
|
|
|
if data['video']:
|
|
|
|
talks = talks.exclude(video__exact='')
|
|
|
|
else:
|
|
|
|
talks = talks.filter(video__exact='')
|
2017-11-08 19:17:56 +00:00
|
|
|
talks = talks.prefetch_related('category', 'speakers', 'track', 'tags')
|
|
|
|
|
|
|
|
if request.GET.get('format') == 'csv':
|
|
|
|
response = HttpResponse(content_type='text/csv')
|
|
|
|
response['Content-Disposition'] = 'attachment; filename="talks.csv"'
|
|
|
|
writer = csv.writer(response)
|
|
|
|
for talk in talks:
|
|
|
|
writer.writerow(talk.get_csv_row())
|
|
|
|
return response
|
|
|
|
|
2017-08-12 14:57:12 +00:00
|
|
|
# Action
|
|
|
|
action_form = TalkActionForm(request.POST or None, talks=talks, site=request.conference.site)
|
|
|
|
if request.method == 'POST' and action_form.is_valid():
|
|
|
|
data = action_form.cleaned_data
|
2017-11-25 21:41:21 +00:00
|
|
|
for talk_id in data['talks']:
|
|
|
|
talk = Talk.objects.get(site=request.conference.site, pk=talk_id)
|
2017-08-12 14:57:12 +00:00
|
|
|
if data['decision'] != None and data['decision'] != talk.accepted:
|
|
|
|
if data['decision']:
|
2017-11-30 19:34:12 +00:00
|
|
|
action = _('accepted')
|
2017-08-12 14:57:12 +00:00
|
|
|
else:
|
2017-11-30 19:34:12 +00:00
|
|
|
action = _('declined')
|
|
|
|
note = _('The talk has been %(action)s.') % {'action': action}
|
|
|
|
send_message(
|
|
|
|
thread=talk.conversation,
|
|
|
|
author=request.user,
|
2023-01-22 17:31:49 +00:00
|
|
|
subject=_("[%(conference)s] The talk '%(talk)s' has been %(action)s") % {
|
2018-01-24 10:09:25 +00:00
|
|
|
'conference': request.conference,
|
2017-11-30 19:34:12 +00:00
|
|
|
'talk': talk,
|
|
|
|
'action': action,
|
|
|
|
},
|
|
|
|
content=note,
|
|
|
|
)
|
2017-08-12 14:57:12 +00:00
|
|
|
talk.accepted = data['decision']
|
|
|
|
if data['track']:
|
|
|
|
talk.track = Track.objects.get(site=request.conference.site, slug=data['track'])
|
2017-10-09 17:54:04 +00:00
|
|
|
if data['tag']:
|
|
|
|
talk.tags.add(Tag.objects.get(site=request.conference.site, slug=data['tag']))
|
2017-08-12 14:57:12 +00:00
|
|
|
if data['room']:
|
|
|
|
talk.room = Room.objects.get(site=request.conference.site, slug=data['room'])
|
|
|
|
talk.save()
|
2018-07-12 17:27:05 +00:00
|
|
|
if data['email']:
|
|
|
|
email = int(data['email'])
|
2017-12-16 15:43:45 +00:00
|
|
|
if email == TalkActionForm.EMAIL_TALKS:
|
|
|
|
request.session['talk-email-list'] = data['talks']
|
|
|
|
return redirect(reverse('talk-email'))
|
|
|
|
elif email == TalkActionForm.EMAIL_SPEAKERS:
|
|
|
|
selected_talks = Talk.objects.filter(pk__in=data['talks'])
|
|
|
|
speakers = Participant.objects.filter(pk__in=selected_talks.values('speakers__pk')).distinct()
|
|
|
|
request.session['speaker-email-list'] = list(speakers.values_list('pk', flat=True))
|
|
|
|
return redirect(reverse('speaker-email'))
|
2017-08-12 14:57:12 +00:00
|
|
|
return redirect(request.get_full_path())
|
2017-08-01 12:00:31 +00:00
|
|
|
# Sorting
|
|
|
|
if request.GET.get('order') == 'desc':
|
2017-08-01 23:45:38 +00:00
|
|
|
sort_reverse = True
|
2017-08-01 12:00:31 +00:00
|
|
|
else:
|
2017-08-01 23:45:38 +00:00
|
|
|
sort_reverse = False
|
2017-08-01 12:00:31 +00:00
|
|
|
SORT_MAPPING = {
|
|
|
|
'title': 'title',
|
|
|
|
'category': 'category',
|
|
|
|
'status': 'accepted',
|
|
|
|
}
|
|
|
|
sort = request.GET.get('sort')
|
|
|
|
if sort in SORT_MAPPING.keys():
|
2017-08-01 23:45:38 +00:00
|
|
|
if sort_reverse:
|
2017-08-01 12:00:31 +00:00
|
|
|
talks = talks.order_by('-' + SORT_MAPPING[sort])
|
|
|
|
else:
|
|
|
|
talks = talks.order_by(SORT_MAPPING[sort])
|
|
|
|
# Sorting URLs
|
|
|
|
sort_urls = dict()
|
|
|
|
sort_glyphicons = dict()
|
|
|
|
for c in SORT_MAPPING.keys():
|
|
|
|
url = request.GET.copy()
|
|
|
|
url['sort'] = c
|
|
|
|
if c == sort:
|
2017-08-01 23:45:38 +00:00
|
|
|
if sort_reverse:
|
2017-08-01 12:00:31 +00:00
|
|
|
del url['order']
|
|
|
|
glyphicon = 'sort-by-attributes-alt'
|
|
|
|
else:
|
|
|
|
url['order'] = 'desc'
|
|
|
|
glyphicon = 'sort-by-attributes'
|
|
|
|
else:
|
|
|
|
glyphicon = 'sort'
|
|
|
|
sort_urls[c] = url.urlencode()
|
|
|
|
sort_glyphicons[c] = glyphicon
|
2017-11-08 19:17:56 +00:00
|
|
|
csv_query_dict = request.GET.copy()
|
|
|
|
csv_query_dict['format'] = 'csv'
|
|
|
|
csv_link = '?' + csv_query_dict.urlencode()
|
2017-07-30 14:57:38 +00:00
|
|
|
return render(request, 'cfp/staff/talk_list.html', {
|
2017-08-01 12:25:29 +00:00
|
|
|
'show_filters': show_filters,
|
2017-07-30 14:57:38 +00:00
|
|
|
'talk_list': talks,
|
2017-08-01 12:25:29 +00:00
|
|
|
'filter_form': filter_form,
|
2017-08-12 14:57:12 +00:00
|
|
|
'action_form': action_form,
|
2017-08-01 12:00:31 +00:00
|
|
|
'sort_urls': sort_urls,
|
|
|
|
'sort_glyphicons': sort_glyphicons,
|
2017-11-08 19:17:56 +00:00
|
|
|
'csv_link': csv_link,
|
2017-12-02 01:34:23 +00:00
|
|
|
'pending_email': bool(request.session.get('talk-email-list', None)),
|
2017-07-30 14:57:38 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
@staff_required
|
2017-08-12 12:17:00 +00:00
|
|
|
def talk_details(request, talk_id):
|
2017-11-25 21:41:21 +00:00
|
|
|
talk = get_object_or_404(Talk, pk=talk_id, site=request.conference.site)
|
2017-10-19 18:22:44 +00:00
|
|
|
try:
|
|
|
|
vote = talk.vote_set.get(user=request.user).vote
|
|
|
|
except Vote.DoesNotExist:
|
|
|
|
vote = None
|
2017-08-01 23:45:38 +00:00
|
|
|
message_form = MessageForm(request.POST or None)
|
|
|
|
if request.method == 'POST' and message_form.is_valid():
|
2017-11-30 19:34:12 +00:00
|
|
|
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,
|
|
|
|
)
|
2017-08-01 23:45:38 +00:00
|
|
|
messages.success(request, _('Message sent!'))
|
2017-11-25 21:41:21 +00:00
|
|
|
return redirect(reverse('talk-details', args=[talk.pk]))
|
2017-07-30 14:57:38 +00:00
|
|
|
return render(request, 'cfp/staff/talk_details.html', {
|
|
|
|
'talk': talk,
|
2017-10-19 18:22:44 +00:00
|
|
|
'vote': vote,
|
2017-07-30 14:57:38 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
@staff_required
|
2017-08-12 12:17:00 +00:00
|
|
|
def talk_vote(request, talk_id, score):
|
2017-12-10 12:32:19 +00:00
|
|
|
if score not in [-2, -1, 0, 1, 2]:
|
|
|
|
raise Http404
|
2017-11-25 21:41:21 +00:00
|
|
|
talk = get_object_or_404(Talk, pk=talk_id, site=request.conference.site)
|
2017-07-30 14:57:38 +00:00
|
|
|
vote, created = Vote.objects.get_or_create(talk=talk, user=request.user)
|
2017-12-10 12:32:19 +00:00
|
|
|
vote.vote = score
|
2017-07-30 14:57:38 +00:00
|
|
|
vote.save()
|
|
|
|
messages.success(request, _('Vote successfully created') if created else _('Vote successfully updated'))
|
|
|
|
return redirect(talk.get_absolute_url())
|
|
|
|
|
|
|
|
|
|
|
|
@staff_required
|
2017-08-12 12:17:00 +00:00
|
|
|
def talk_decide(request, talk_id, accept):
|
2017-11-25 21:41:21 +00:00
|
|
|
talk = get_object_or_404(Talk, pk=talk_id, site=request.conference.site)
|
2017-07-30 14:57:38 +00:00
|
|
|
if request.method == 'POST':
|
2017-08-12 14:57:12 +00:00
|
|
|
talk.accepted = accept
|
|
|
|
talk.save()
|
2017-11-30 19:34:12 +00:00
|
|
|
if accept:
|
|
|
|
action = _('accepted')
|
|
|
|
else:
|
|
|
|
action = _('declined')
|
2017-08-02 10:44:44 +00:00
|
|
|
# Does we need to send a notification to the proposer?
|
|
|
|
m = request.POST.get('message', '').strip()
|
|
|
|
if m:
|
|
|
|
for participant in talk.speakers.all():
|
2017-11-30 19:34:12 +00:00
|
|
|
send_message(
|
2018-01-24 14:51:54 +00:00
|
|
|
thread=participant.conversation,
|
2017-11-30 19:34:12 +00:00
|
|
|
author=request.conference,
|
2023-01-22 17:31:49 +00:00
|
|
|
subject=_("[%(conference)s] Your talk '%(talk)s' has been %(action)s") % {
|
2017-11-30 19:34:12 +00:00
|
|
|
'conference': request.conference,
|
|
|
|
'talk': talk,
|
|
|
|
'action': action,
|
|
|
|
},
|
|
|
|
content=m,
|
|
|
|
)
|
2017-08-02 10:44:44 +00:00
|
|
|
# Save the decision in the talk's conversation
|
2017-11-30 19:34:12 +00:00
|
|
|
send_message(
|
|
|
|
thread=talk.conversation,
|
|
|
|
author=request.user,
|
2023-01-22 17:31:49 +00:00
|
|
|
subject=_("[%(conference)s] The talk '%(talk)s' has been %(action)s") % {
|
2017-11-30 19:34:12 +00:00
|
|
|
'conference': request.conference,
|
|
|
|
'talk': talk,
|
|
|
|
'action': action,
|
|
|
|
},
|
|
|
|
content=_('The talk has been %(action)s.') % {'action': action},
|
|
|
|
)
|
2017-07-30 14:57:38 +00:00
|
|
|
messages.success(request, _('Decision taken in account'))
|
|
|
|
return redirect(talk.get_absolute_url())
|
|
|
|
return render(request, 'cfp/staff/talk_decide.html', {
|
|
|
|
'talk': talk,
|
|
|
|
'accept': accept,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-12-02 01:34:23 +00:00
|
|
|
@staff_required
|
|
|
|
def talk_email(request):
|
|
|
|
talks = Talk.objects.filter(pk__in=request.session.get('talk-email-list', []))
|
|
|
|
count = talks.annotate(speakers_count=Count('speakers', distinct=True)).aggregate(Sum('speakers_count'))['speakers_count__sum']
|
|
|
|
if not talks.exists():
|
|
|
|
messages.error(request, _('Please select some talks.'))
|
|
|
|
return redirect('talk-list')
|
2017-12-16 12:20:26 +00:00
|
|
|
form = SendTalkMailForm(request.POST or None, initial=request.session.get('talk-email-stored'), talks=talks)
|
2017-12-02 01:34:23 +00:00
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
subject = form.cleaned_data['subject']
|
|
|
|
body = form.cleaned_data['body']
|
|
|
|
request.session['talk-email-stored'] = {'subject': subject, 'body': body}
|
|
|
|
if form.cleaned_data['confirm']:
|
|
|
|
sent = talk_email_send(talks, subject, body)
|
|
|
|
messages.success(request, _('%(count)d mails have been sent.') % {'count': sent})
|
|
|
|
del request.session['talk-email-list']
|
|
|
|
return redirect('talk-list')
|
|
|
|
else:
|
2019-11-02 09:18:54 +00:00
|
|
|
messages.info(request, _('You are ready to send %(count)d emails.') % {'count': count})
|
2017-12-02 01:34:23 +00:00
|
|
|
else:
|
|
|
|
form.fields.pop('confirm')
|
|
|
|
return render(request, 'cfp/staff/talk_email.html', {
|
|
|
|
'talks': talks,
|
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
@require_http_methods(['POST'])
|
|
|
|
@staff_required
|
|
|
|
def talk_email_preview(request):
|
2017-12-16 12:20:26 +00:00
|
|
|
form = PreviewTalkMailForm(request.POST or None)
|
2017-12-02 01:34:23 +00:00
|
|
|
if not form.is_valid():
|
|
|
|
return HttpResponseServerError()
|
|
|
|
speaker = get_object_or_404(Participant, site=request.conference.site, pk=form.cleaned_data['speaker'])
|
|
|
|
talk = get_object_or_404(Talk, site=request.conference.site, pk=form.cleaned_data['talk'])
|
|
|
|
preview = talk_email_render_preview(talk, speaker, form.cleaned_data['subject'], form.cleaned_data['body'])
|
|
|
|
return HttpResponse(preview)
|
|
|
|
|
|
|
|
|
2017-07-30 14:57:38 +00:00
|
|
|
@staff_required
|
2017-08-12 12:17:00 +00:00
|
|
|
def participant_list(request):
|
|
|
|
participants = Participant.objects.filter(site=request.conference.site) \
|
2017-08-11 23:03:22 +00:00
|
|
|
.extra(select={'lower_name': 'lower(name)'}) \
|
|
|
|
.order_by('lower_name')
|
2017-08-12 20:24:55 +00:00
|
|
|
# Filtering
|
|
|
|
show_filters = False
|
|
|
|
filter_form = ParticipantFilterForm(request.GET or None, site=request.conference.site)
|
|
|
|
if filter_form.is_valid():
|
|
|
|
data = filter_form.cleaned_data
|
|
|
|
talks = Talk.objects.filter(site=request.conference.site)
|
|
|
|
if len(data['category']):
|
|
|
|
show_filters = True
|
|
|
|
talks = talks.filter(reduce(lambda x, y: x | y, [Q(category__pk=pk) for pk in data['category']]))
|
2017-10-06 16:45:58 +00:00
|
|
|
if len(data['accepted']):
|
|
|
|
show_filters = True
|
|
|
|
talks = talks.filter(reduce(lambda x, y: x | y, [Q(accepted=dict(ACCEPTATION_VALUES)[status]) for status in data['accepted']]))
|
|
|
|
if len(data['confirmed']):
|
2017-08-12 20:24:55 +00:00
|
|
|
show_filters = True
|
2017-10-06 16:45:58 +00:00
|
|
|
talks = talks.filter(accepted=True)
|
|
|
|
talks = talks.filter(reduce(lambda x, y: x | y, [Q(confirmed=dict(CONFIRMATION_VALUES)[status]) for status in data['confirmed']]))
|
2017-08-12 20:24:55 +00:00
|
|
|
if len(data['track']):
|
|
|
|
show_filters = True
|
|
|
|
q = Q()
|
|
|
|
if 'none' in data['track']:
|
|
|
|
data['track'].remove('none')
|
|
|
|
q |= Q(track__isnull=True)
|
|
|
|
if len(data['track']):
|
|
|
|
q |= Q(track__slug__in=data['track'])
|
|
|
|
talks = talks.filter(q)
|
|
|
|
participants = participants.filter(talk__in=talks)
|
2017-12-16 12:20:26 +00:00
|
|
|
# Action
|
|
|
|
action_form = SpeakerActionForm(request.POST or None, speakers=participants)
|
|
|
|
if request.method == 'POST' and action_form.is_valid():
|
|
|
|
data = action_form.cleaned_data
|
|
|
|
if data['email']:
|
|
|
|
request.session['speaker-email-list'] = data['speakers']
|
|
|
|
return redirect(reverse('speaker-email'))
|
|
|
|
return redirect(request.get_full_path())
|
2017-11-08 19:17:56 +00:00
|
|
|
if request.GET.get('format') == 'csv':
|
|
|
|
response = HttpResponse(content_type='text/csv')
|
|
|
|
response['Content-Disposition'] = 'attachment; filename="participants.csv"'
|
|
|
|
writer = csv.writer(response)
|
|
|
|
for participant in participants:
|
|
|
|
writer.writerow(participant.get_csv_row())
|
|
|
|
return response
|
|
|
|
else:
|
|
|
|
contact_link = 'mailto:' + ','.join([participant.email for participant in participants.all()])
|
|
|
|
csv_query_dict = request.GET.copy()
|
|
|
|
csv_query_dict['format'] = 'csv'
|
|
|
|
csv_link = '?' + csv_query_dict.urlencode()
|
|
|
|
return render(request, 'cfp/staff/participant_list.html', {
|
|
|
|
'filter_form': filter_form,
|
2017-12-16 12:20:26 +00:00
|
|
|
'action_form': action_form,
|
2017-11-08 19:17:56 +00:00
|
|
|
'participant_list': participants,
|
|
|
|
'show_filters': show_filters,
|
|
|
|
'contact_link': contact_link,
|
|
|
|
'csv_link': csv_link,
|
2017-12-16 12:20:26 +00:00
|
|
|
'pending_email': bool(request.session.get('speaker-email-list', None)),
|
2017-11-08 19:17:56 +00:00
|
|
|
})
|
2017-07-30 14:57:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
@staff_required
|
2017-08-12 12:17:00 +00:00
|
|
|
def participant_details(request, participant_id):
|
2017-11-25 21:41:21 +00:00
|
|
|
participant = get_object_or_404(Participant, pk=participant_id, site=request.conference.site)
|
2017-08-01 23:45:38 +00:00
|
|
|
message_form = MessageForm(request.POST or None)
|
|
|
|
if request.method == 'POST' and message_form.is_valid():
|
2017-12-17 12:57:32 +00:00
|
|
|
in_reply_to = participant.conversation.message_set.last()
|
|
|
|
send_message(
|
|
|
|
thread=participant.conversation,
|
|
|
|
author=request.user,
|
|
|
|
subject='',
|
|
|
|
content=message_form.cleaned_data['content'],
|
|
|
|
in_reply_to=in_reply_to,
|
|
|
|
)
|
2017-08-01 23:45:38 +00:00
|
|
|
messages.success(request, _('Message sent!'))
|
2017-11-25 21:41:21 +00:00
|
|
|
return redirect(reverse('participant-details', args=[participant.pk]))
|
2017-07-30 14:57:38 +00:00
|
|
|
return render(request, 'cfp/staff/participant_details.html', {
|
|
|
|
'participant': participant,
|
|
|
|
})
|
2017-07-30 18:11:13 +00:00
|
|
|
|
|
|
|
|
2017-11-09 22:45:10 +00:00
|
|
|
class ParticipantCreate(StaffRequiredMixin, OnSiteFormMixin, CreateView):
|
|
|
|
model = Participant
|
|
|
|
template_name = 'cfp/staff/participant_form.html'
|
|
|
|
|
|
|
|
def get_form_class(self):
|
|
|
|
return modelform_factory(
|
|
|
|
self.model,
|
|
|
|
form=ParticipantForm,
|
2017-11-10 14:40:50 +00:00
|
|
|
fields=['name', 'vip', 'email', 'phone_number', 'biography', 'notes'] + ParticipantForm.SOCIAL_FIELDS,
|
2017-11-09 22:45:10 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2017-11-05 20:37:34 +00:00
|
|
|
class ParticipantUpdate(StaffRequiredMixin, OnSiteFormMixin, UpdateView):
|
2017-08-11 22:50:42 +00:00
|
|
|
model = Participant
|
|
|
|
template_name = 'cfp/staff/participant_form.html'
|
2017-11-26 19:30:19 +00:00
|
|
|
slug_field = 'pk'
|
|
|
|
slug_url_kwarg = 'participant_id'
|
2017-08-11 22:50:42 +00:00
|
|
|
|
2017-11-05 20:37:34 +00:00
|
|
|
def get_form_class(self):
|
|
|
|
return modelform_factory(
|
|
|
|
self.model,
|
|
|
|
form=ParticipantForm,
|
2017-11-10 14:40:50 +00:00
|
|
|
fields=['name', 'vip', 'email', 'phone_number', 'biography', 'notes'] + ParticipantForm.SOCIAL_FIELDS,
|
2017-11-05 20:37:34 +00:00
|
|
|
)
|
|
|
|
|
2017-08-11 22:50:42 +00:00
|
|
|
|
2017-11-28 00:10:28 +00:00
|
|
|
class ParticipantRemove(StaffRequiredMixin, OnSiteFormMixin, DeleteView):
|
|
|
|
slug_field = 'pk'
|
|
|
|
slug_url_kwarg = 'participant_id'
|
|
|
|
success_url = reverse_lazy('participant-list')
|
|
|
|
|
|
|
|
def get_queryset(self):
|
2017-11-28 23:29:50 +00:00
|
|
|
return Participant.objects.filter(talk__isnull=True)
|
2017-11-28 00:10:28 +00:00
|
|
|
|
|
|
|
|
2017-11-09 22:45:10 +00:00
|
|
|
@staff_required
|
|
|
|
def participant_add_talk(request, participant_id):
|
|
|
|
participant = get_object_or_404(Participant, site=request.conference.site, pk=participant_id)
|
|
|
|
form = TalkForm(request.POST or None, categories=TalkCategory.objects.filter(site=request.conference.site))
|
|
|
|
if request.method == 'POST' and form.is_valid():
|
2017-11-10 14:39:32 +00:00
|
|
|
talk = form.save(commit=False)
|
|
|
|
talk.site = request.conference.site
|
|
|
|
talk.save()
|
2017-11-09 22:45:10 +00:00
|
|
|
talk.speakers.add(participant)
|
2017-11-25 21:41:21 +00:00
|
|
|
return redirect(reverse('talk-details', kwargs={'talk_id': talk.pk}))
|
2017-11-09 22:45:10 +00:00
|
|
|
return render(request, 'cfp/staff/talk_form.html', {
|
|
|
|
'form': form,
|
|
|
|
'participant': participant,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-12-16 12:20:26 +00:00
|
|
|
@staff_required
|
|
|
|
def speaker_email(request):
|
|
|
|
speakers = Participant.objects.filter(pk__in=request.session.get('speaker-email-list', []))
|
|
|
|
if not speakers.exists():
|
|
|
|
messages.error(request, _('Please select some speakers.'))
|
|
|
|
return redirect('participant-list')
|
|
|
|
form = SendSpeakerMailForm(request.POST or None, initial=request.session.get('speaker-email-stored'), speakers=speakers)
|
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
subject = form.cleaned_data['subject']
|
|
|
|
body = form.cleaned_data['body']
|
|
|
|
request.session['speaker-email-stored'] = {'subject': subject, 'body': body}
|
|
|
|
if form.cleaned_data['confirm']:
|
|
|
|
sent = speaker_email_send(speakers, subject, body)
|
|
|
|
messages.success(request, _('%(count)d mails have been sent.') % {'count': sent})
|
|
|
|
del request.session['speaker-email-list']
|
|
|
|
return redirect('participant-list')
|
|
|
|
else:
|
2019-11-02 09:18:54 +00:00
|
|
|
messages.info(request, _('You are ready to send %(count)d emails.') % {'count': speakers.count()})
|
2017-12-16 12:20:26 +00:00
|
|
|
else:
|
|
|
|
form.fields.pop('confirm')
|
|
|
|
return render(request, 'cfp/staff/speaker_email.html', {
|
|
|
|
'speakers': speakers,
|
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
@require_http_methods(['POST'])
|
|
|
|
@staff_required
|
|
|
|
def speaker_email_preview(request):
|
|
|
|
form = PreviewSpeakerMailForm(request.POST or None)
|
|
|
|
if not form.is_valid():
|
|
|
|
return HttpResponseServerError()
|
|
|
|
speaker = get_object_or_404(Participant, site=request.conference.site, pk=form.cleaned_data['speaker'])
|
|
|
|
preview = speaker_email_render_preview(speaker, form.cleaned_data['subject'], form.cleaned_data['body'])
|
|
|
|
return HttpResponse(preview)
|
|
|
|
|
|
|
|
|
2017-07-30 18:11:13 +00:00
|
|
|
@staff_required
|
2017-11-04 14:30:00 +00:00
|
|
|
def conference_edit(request):
|
2017-08-12 12:17:00 +00:00
|
|
|
form = ConferenceForm(request.POST or None, instance=request.conference)
|
2017-07-30 18:11:13 +00:00
|
|
|
if request.method == 'POST' and form.is_valid():
|
2017-08-12 12:17:00 +00:00
|
|
|
old_staff = set(request.conference.staff.all())
|
2017-07-31 23:11:58 +00:00
|
|
|
new_conference = form.save()
|
|
|
|
new_staff = set(new_conference.staff.all())
|
|
|
|
added_staff = new_staff - old_staff
|
2017-07-31 23:29:12 +00:00
|
|
|
protocol = 'https' if request.is_secure() else 'http'
|
2017-08-12 12:17:00 +00:00
|
|
|
base_url = protocol+'://'+request.conference.site.domain
|
2017-07-31 23:11:58 +00:00
|
|
|
url_login = base_url + reverse('login')
|
|
|
|
url_password_reset = base_url + reverse('password_reset')
|
2017-08-12 12:17:00 +00:00
|
|
|
msg_title = _('[{}] You have been added to the staff team').format(request.conference.name)
|
2017-07-31 23:11:58 +00:00
|
|
|
msg_body_template = _("""Hi {},
|
|
|
|
|
|
|
|
You have been added to the staff team.
|
|
|
|
|
|
|
|
You can now:
|
|
|
|
- login: {}
|
|
|
|
- reset your password: {}
|
|
|
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
""")
|
2017-08-01 23:45:38 +00:00
|
|
|
# TODO: send bulk emails
|
2017-07-31 23:11:58 +00:00
|
|
|
for user in added_staff:
|
2017-08-12 12:17:00 +00:00
|
|
|
msg_body = msg_body_template.format(user.get_full_name(), url_login, url_password_reset, request.conference.name)
|
2017-07-31 23:11:58 +00:00
|
|
|
send_mail(
|
|
|
|
msg_title,
|
|
|
|
msg_body,
|
2017-08-12 12:17:00 +00:00
|
|
|
request.conference.from_email(),
|
2017-07-31 23:11:58 +00:00
|
|
|
[user.email],
|
|
|
|
fail_silently=False,
|
|
|
|
)
|
2017-07-30 18:11:13 +00:00
|
|
|
messages.success(request, _('Modifications successfully saved.'))
|
2017-11-25 22:19:24 +00:00
|
|
|
return redirect(reverse('conference-edit'))
|
2017-10-09 18:31:45 +00:00
|
|
|
return render(request, 'cfp/admin/conference.html', {
|
2017-07-30 18:11:13 +00:00
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-11-25 22:19:24 +00:00
|
|
|
@staff_required
|
|
|
|
def homepage_edit(request):
|
|
|
|
form = HomepageForm(request.POST or None, instance=request.conference)
|
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
form.save()
|
|
|
|
messages.success(request, _('Modifications successfully saved.'))
|
|
|
|
return redirect(reverse('homepage-edit'))
|
|
|
|
return render(request, 'cfp/admin/homepage.html', {
|
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2017-08-13 15:03:20 +00:00
|
|
|
class TalkUpdate(StaffRequiredMixin, OnSiteMixin, OnSiteFormMixin, UpdateView):
|
2017-08-02 17:34:07 +00:00
|
|
|
model = Talk
|
|
|
|
template_name = 'cfp/staff/talk_form.html'
|
2017-11-29 23:01:39 +00:00
|
|
|
pk_url_kwarg = 'talk_id'
|
|
|
|
|
|
|
|
def get_form_class(self):
|
|
|
|
return get_talk_speaker_form_class(self.object.site)
|
2017-08-02 17:34:07 +00:00
|
|
|
|
|
|
|
|
2017-08-02 18:25:43 +00:00
|
|
|
class TrackMixin(OnSiteMixin):
|
|
|
|
model = Track
|
|
|
|
|
|
|
|
|
|
|
|
class TrackList(StaffRequiredMixin, TrackMixin, ListView):
|
|
|
|
template_name = 'cfp/staff/track_list.html'
|
|
|
|
|
|
|
|
|
2017-08-13 15:03:20 +00:00
|
|
|
class TrackFormMixin(OnSiteFormMixin, TrackMixin):
|
2017-08-02 18:25:43 +00:00
|
|
|
template_name = 'cfp/staff/track_form.html'
|
|
|
|
form_class = TrackForm
|
|
|
|
success_url = reverse_lazy('track-list')
|
|
|
|
|
|
|
|
|
|
|
|
class TrackCreate(StaffRequiredMixin, TrackFormMixin, CreateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class TrackUpdate(StaffRequiredMixin, TrackFormMixin, UpdateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2017-08-12 00:05:53 +00:00
|
|
|
class RoomMixin(OnSiteMixin):
|
|
|
|
model = Room
|
|
|
|
|
|
|
|
|
|
|
|
class RoomList(StaffRequiredMixin, RoomMixin, ListView):
|
|
|
|
template_name = 'cfp/staff/room_list.html'
|
|
|
|
|
|
|
|
|
|
|
|
class RoomDetail(StaffRequiredMixin, RoomMixin, DetailView):
|
|
|
|
template_name = 'cfp/staff/room_details.html'
|
|
|
|
|
|
|
|
|
|
|
|
class RoomFormMixin(RoomMixin):
|
|
|
|
template_name = 'cfp/staff/room_form.html'
|
|
|
|
form_class = RoomForm
|
|
|
|
success_url = reverse_lazy('room-list')
|
|
|
|
|
|
|
|
def get_form_kwargs(self):
|
|
|
|
kwargs = super().get_form_kwargs()
|
|
|
|
kwargs.update({
|
2017-08-12 12:17:00 +00:00
|
|
|
'conference': self.request.conference,
|
2017-08-12 00:05:53 +00:00
|
|
|
})
|
|
|
|
return kwargs
|
|
|
|
|
|
|
|
|
|
|
|
class RoomCreate(StaffRequiredMixin, RoomFormMixin, CreateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class RoomUpdate(StaffRequiredMixin, RoomFormMixin, UpdateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2017-11-07 22:54:09 +00:00
|
|
|
class TalkCategoryMixin(OnSiteMixin):
|
|
|
|
model = TalkCategory
|
|
|
|
|
|
|
|
|
|
|
|
class TalkCategoryList(StaffRequiredMixin, TalkCategoryMixin, ListView):
|
|
|
|
template_name = 'cfp/admin/category_list.html'
|
|
|
|
|
|
|
|
|
|
|
|
class TalkCategoryFormMixin(TalkCategoryMixin):
|
|
|
|
template_name = 'cfp/admin/category_form.html'
|
|
|
|
form_class = TalkCategoryForm
|
|
|
|
success_url = reverse_lazy('category-list')
|
|
|
|
|
|
|
|
def get_form_kwargs(self):
|
|
|
|
kwargs = super().get_form_kwargs()
|
|
|
|
kwargs.update({
|
|
|
|
'conference': self.request.conference,
|
|
|
|
})
|
|
|
|
return kwargs
|
|
|
|
|
|
|
|
|
|
|
|
class TalkCategoryCreate(StaffRequiredMixin, TalkCategoryFormMixin, CreateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class TalkCategoryUpdate(StaffRequiredMixin, TalkCategoryFormMixin, UpdateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class TagMixin(OnSiteMixin):
|
|
|
|
model = Tag
|
|
|
|
|
|
|
|
|
|
|
|
class TagList(StaffRequiredMixin, TagMixin, ListView):
|
|
|
|
template_name = 'cfp/admin/tag_list.html'
|
|
|
|
|
|
|
|
|
|
|
|
class TagFormMixin(TagMixin):
|
|
|
|
template_name = 'cfp/admin/tag_form.html'
|
|
|
|
form_class = TagForm
|
|
|
|
success_url = reverse_lazy('tag-list')
|
|
|
|
|
|
|
|
def get_form_kwargs(self):
|
|
|
|
kwargs = super().get_form_kwargs()
|
|
|
|
kwargs.update({
|
|
|
|
'conference': self.request.conference,
|
|
|
|
})
|
|
|
|
return kwargs
|
|
|
|
|
|
|
|
|
|
|
|
class TagCreate(StaffRequiredMixin, TagFormMixin, CreateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class TagUpdate(StaffRequiredMixin, TagFormMixin, UpdateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2017-11-07 23:18:46 +00:00
|
|
|
class ActivityMixin(OnSiteMixin):
|
|
|
|
model = Activity
|
|
|
|
|
|
|
|
|
|
|
|
class ActivityList(StaffRequiredMixin, ActivityMixin, ListView):
|
|
|
|
template_name = 'cfp/admin/activity_list.html'
|
|
|
|
|
|
|
|
|
|
|
|
class ActivityFormMixin(ActivityMixin):
|
|
|
|
template_name = 'cfp/admin/activity_form.html'
|
|
|
|
form_class = ActivityForm
|
|
|
|
success_url = reverse_lazy('activity-list')
|
|
|
|
|
|
|
|
def get_form_kwargs(self):
|
|
|
|
kwargs = super().get_form_kwargs()
|
|
|
|
kwargs.update({
|
|
|
|
'conference': self.request.conference,
|
|
|
|
})
|
|
|
|
return kwargs
|
|
|
|
|
|
|
|
|
|
|
|
class ActivityCreate(StaffRequiredMixin, ActivityFormMixin, CreateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
class ActivityUpdate(StaffRequiredMixin, ActivityFormMixin, UpdateView):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2017-07-30 19:52:14 +00:00
|
|
|
@staff_required
|
2017-08-12 12:17:00 +00:00
|
|
|
def create_user(request):
|
2017-07-30 19:52:14 +00:00
|
|
|
form = CreateUserForm(request.POST or None)
|
|
|
|
|
|
|
|
if request.method == 'POST' and form.is_valid():
|
|
|
|
form.save()
|
|
|
|
messages.success(request, _('User created successfully.'))
|
|
|
|
return redirect(reverse('create-user'))
|
|
|
|
|
2017-11-05 21:45:40 +00:00
|
|
|
return render(request, 'cfp/admin/create_user.html', {
|
2017-07-30 19:52:14 +00:00
|
|
|
'form': form,
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2019-10-06 20:08:12 +00:00
|
|
|
def schedule(request, program_format, pending, template, staff, cache=None):
|
|
|
|
program = Program(site=request.conference.site, pending=pending, staff=staff, cache=cache)
|
2017-08-15 18:52:12 +00:00
|
|
|
if program_format is None:
|
|
|
|
return render(request, template, {'program': program.render('html')})
|
|
|
|
elif program_format == 'html':
|
|
|
|
return HttpResponse(program.render('html'))
|
|
|
|
elif program_format == 'xml':
|
2017-08-15 11:26:15 +00:00
|
|
|
return HttpResponse(program.render('xml'), content_type="application/xml")
|
2017-11-19 20:56:15 +00:00
|
|
|
elif program_format in ['ics', 'citymeo']:
|
|
|
|
response = HttpResponse(program.render('ics', citymeo=bool(program_format == 'citymeo')), content_type='text/calendar')
|
2017-09-10 21:27:59 +00:00
|
|
|
response['Content-Disposition'] = 'attachment; filename="planning.ics"'
|
|
|
|
return response
|
2017-08-15 11:26:15 +00:00
|
|
|
else:
|
2017-08-15 18:52:12 +00:00
|
|
|
raise Http404(_("Format '%s' not available" % program_format))
|
|
|
|
|
|
|
|
|
2020-08-09 14:10:28 +00:00
|
|
|
def public_schedule(request, program_format=None):
|
2017-08-28 09:11:03 +00:00
|
|
|
if not request.conference.schedule_available and not is_staff(request, request.user):
|
2017-08-15 18:52:12 +00:00
|
|
|
raise PermissionDenied
|
2017-10-18 18:37:16 +00:00
|
|
|
if request.conference.schedule_redirection_url and program_format is None:
|
|
|
|
return redirect(request.conference.schedule_redirection_url)
|
|
|
|
else:
|
2019-10-06 20:08:12 +00:00
|
|
|
return schedule(request, program_format=program_format, pending=False, template='cfp/schedule.html', staff=False)
|
2017-08-15 18:52:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
@staff_required
|
2020-08-09 14:10:28 +00:00
|
|
|
def staff_schedule(request, program_format=None):
|
2019-10-06 20:08:12 +00:00
|
|
|
return schedule(request, program_format=program_format, pending=True, template='cfp/staff/schedule.html', staff=True, cache=False)
|
2017-08-15 11:26:15 +00:00
|
|
|
|
|
|
|
|
2019-06-08 11:02:19 +00:00
|
|
|
@staff_required
|
|
|
|
def schedule_evict(request):
|
|
|
|
cache.clear()
|
|
|
|
messages.success(request, _('Schedule evicted from cache.'))
|
|
|
|
return redirect('/')
|
|
|
|
|
|
|
|
|
2017-07-30 18:11:13 +00:00
|
|
|
class Select2View(StaffRequiredMixin, AutoResponseView):
|
|
|
|
pass
|