link participation to user account

This commit is contained in:
Élie Bouttier 2017-11-05 21:37:34 +01:00
parent bac3953763
commit 0565a9f217
7 changed files with 69 additions and 48 deletions

View File

@ -11,9 +11,12 @@ from cfp.models import Participant
def speaker_required(view_func):
def wrapped_view(request, **kwargs):
speaker_token = kwargs.pop('speaker_token')
# TODO v3: if no speaker token is provided, we should check for a logged user, and if so,
# we should check if his/her participating at current conference
speaker = get_object_or_404(Participant, site=request.conference.site, token=speaker_token)
if speaker_token:
speaker = get_object_or_404(Participant, site=request.conference.site, token=speaker_token)
elif request.user.is_authenticated():
speaker = get_object_or_404(Participant, site=request.conference.site, email=request.user.email)
else:
raise PermissionDenied
kwargs['speaker'] = speaker
return view_func(request, **kwargs)
return wraps(view_func)(wrapped_view)

View File

@ -195,22 +195,12 @@ class TalkActionForm(forms.Form):
self.fields['room'].choices = [(None, "---------")] + list(rooms.values_list('slug', 'name'))
class ParticipantForm(OnSiteNamedModelForm):
class NotifyForm(forms.Form):
notify = forms.BooleanField(initial=True, required=False, label=_('Notify by mail?'))
def __init__(self, *args, **kwargs):
social = kwargs.pop('social', True)
ask_notify = kwargs.pop('ask_notify', False)
super().__init__(*args, **kwargs)
if not social:
for field in ['twitter', 'linkedin', 'github', 'website', 'facebook', 'mastodon']:
self.fields.pop(field)
if not ask_notify:
self.fields.pop('notify')
class Meta:
model = Participant
fields = ['name', 'email', 'biography', 'twitter', 'linkedin', 'github', 'website', 'facebook', 'mastodon']
class ParticipantForm(OnSiteNamedModelForm):
SOCIAL_FIELDS = ['twitter', 'linkedin', 'github', 'website', 'facebook', 'mastodon']
def clean_email(self):
email = self.cleaned_data['email']
@ -220,11 +210,6 @@ class ParticipantForm(OnSiteNamedModelForm):
return email
class ParticipantStaffForm(ParticipantForm):
class Meta(ParticipantForm.Meta):
fields = ['name', 'vip', 'email', 'phone_number', 'notes'] + ParticipantForm.Meta.fields[3:]
class ParticipantFilterForm(forms.Form):
category = forms.MultipleChoiceField(
label=_('Category'),

View File

@ -115,6 +115,10 @@ class Participant(PonyConfModel):
objects = ParticipantManager()
def get_absolute_url(self):
return reverse('participant-details', kwargs=dict(participant_id=self.token))
def get_secret_url(self, full=False):
url = reverse('proposal-dashboard', kwargs={'speaker_token': self.token})
if full:

View File

@ -15,11 +15,13 @@
<div class="row">
<div class="col-md-12">
{% if not request.user.is_authenticated %}
<div class="col-md-8 col-md-offset-2 alert alert-info">
<span class="glyphicon glyphicon-exclamation-sign"></span>
{% url 'proposal-mail-token' as mail_token_url %}
{% blocktrans %}If you already have submitted a talk and you want to edit it or submit another one, please click <a href="{{ mail_token_url }}">here</a>.{% endblocktrans %}
</div>
{% endif %}
<form method="POST" class="form-horizontal col-md-8 col-md-offset-2">
{% csrf_token %}
{{ speaker_form|crispy }}

View File

@ -42,7 +42,8 @@
{% endif %}
<form method="POST" class="form-horizontal col-md-8 col-md-offset-2">
{% csrf_token %}
{{ form|crispy }}
{{ speaker_form|crispy }}
{% if notify_form %}{{ notify_form|crispy }}{% endif %}
<div class="col-md-12 text-center">
<button type="submit" class="btn btn-primary text-center">{% trans "Save" %} <i class="fa fa-check"></i></button>
</div>

View File

@ -7,18 +7,19 @@ urlpatterns = [
# v1.1
url(r'^cfp/$', views.proposal_home, name='proposal-home'),
url(r'^cfp/token/$', views.proposal_mail_token, name='proposal-mail-token'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/$', views.proposal_dashboard, name='proposal-dashboard'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/profile/$', views.proposal_speaker_edit, name='proposal-profile-edit'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/add/$', views.proposal_talk_edit, name='proposal-talk-add'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/(?P<talk_id>[0-9]+)/$', views.proposal_talk_details, name='proposal-talk-details'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/(?P<talk_id>[0-9]+)/edit/$', views.proposal_talk_edit, name='proposal-talk-edit'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/(?P<talk_id>[0-9]+)/speaker/add/$', views.proposal_speaker_edit, name='proposal-speaker-add'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/(?P<talk_id>[0-9]+)/speaker/add/(?P<speaker_id>[0-9]+)/$', views.proposal_speaker_add, name='proposal-speaker-add-existing'),
#url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/(?P<talk_id>[0-9]+)/speaker/(?P<co_speaker_id>[0-9]+)/$', views.proposal_speaker_details, name='proposal-speaker-details'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/(?P<talk_id>[0-9]+)/speaker/(?P<co_speaker_id>[0-9]+)/edit/$', views.proposal_speaker_edit, name='proposal-speaker-edit'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/(?P<talk_id>[0-9]+)/speaker/(?P<co_speaker_id>[0-9]+)/remove/$', views.proposal_speaker_remove, name='proposal-speaker-remove'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/(?P<talk_id>[0-9]+)/confirm/$', views.proposal_talk_acknowledgment, {'confirm': True}, name='proposal-talk-confirm'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/talk/(?P<talk_id>[0-9]+)/desist/$', views.proposal_talk_acknowledgment, {'confirm': False}, name='proposal-talk-desist'),
url(r'^cfp/(?P<speaker_token>[\w\-]+)/$', views.proposal_dashboard), # backward compatibility
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?dashboard/$', views.proposal_dashboard, name='proposal-dashboard'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?profile/$', views.proposal_speaker_edit, name='proposal-profile-edit'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?talk/add/$', views.proposal_talk_edit, name='proposal-talk-add'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?talk/(?P<talk_id>[0-9]+)/$', views.proposal_talk_details, name='proposal-talk-details'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?talk/(?P<talk_id>[0-9]+)/edit/$', views.proposal_talk_edit, name='proposal-talk-edit'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?talk/(?P<talk_id>[0-9]+)/speaker/add/$', views.proposal_speaker_edit, name='proposal-speaker-add'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?talk/(?P<talk_id>[0-9]+)/speaker/add/(?P<speaker_id>[0-9]+)/$', views.proposal_speaker_add, name='proposal-speaker-add-existing'),
#url(r'^cfp(?:/(?P<speaker_token>[\w\-]+))?/talk/(?P<talk_id>[0-9]+)/speaker/(?P<co_speaker_id>[0-9]+)/$', views.proposal_speaker_details, name='proposal-speaker-details'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?talk/(?P<talk_id>[0-9]+)/speaker/(?P<co_speaker_id>[0-9]+)/edit/$', views.proposal_speaker_edit, name='proposal-speaker-edit'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?talk/(?P<talk_id>[0-9]+)/speaker/(?P<co_speaker_id>[0-9]+)/remove/$', views.proposal_speaker_remove, name='proposal-speaker-remove'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?talk/(?P<talk_id>[0-9]+)/confirm/$', views.proposal_talk_acknowledgment, {'confirm': True}, name='proposal-talk-confirm'),
url(r'^cfp/(?:(?P<speaker_token>[\w\-]+)/)?talk/(?P<talk_id>[0-9]+)/desist/$', views.proposal_talk_acknowledgment, {'confirm': False}, name='proposal-talk-desist'),
# Backward compatibility
url(r'^cfp/(?P<talk_id>[\w\-]+)/speaker/add/$', views.talk_proposal_speaker_edit, name='talk-proposal-speaker-add'),
url(r'^cfp/(?P<talk_id>[\w\-]+)/speaker/(?P<participant_id>[\w\-]+)/$', views.talk_proposal_speaker_edit, name='talk-proposal-speaker-edit'),

View File

@ -12,6 +12,7 @@ from django.http import HttpResponse, Http404
from django.utils import timezone
from django.core.exceptions import PermissionDenied
from django.core.mail import send_mail
from django.forms import modelform_factory
from django_select2.views import AutoResponseView
@ -25,7 +26,7 @@ from .mixins import StaffRequiredMixin, OnSiteMixin, OnSiteFormMixin
from .utils import is_staff
from .models import Participant, Talk, TalkCategory, Vote, Track, Tag, Room, Volunteer, Activity
from .forms import TalkForm, TalkStaffForm, TalkFilterForm, TalkActionForm, \
ParticipantForm, ParticipantStaffForm, ParticipantFilterForm, \
ParticipantForm, ParticipantFilterForm, NotifyForm, \
ConferenceForm, CreateUserForm, TrackForm, RoomForm, \
VolunteerForm, VolunteerFilterForm, MailForm, \
ACCEPTATION_VALUES, CONFIRMATION_VALUES
@ -139,17 +140,29 @@ def volunteer_details(request, volunteer_id):
def proposal_home(request):
if is_staff(request, request.user):
categories = TalkCategory.objects.filter(site=request.conference.site)
else:
categories = request.conference.opened_categories
categories = request.conference.opened_categories
if not categories.exists():
return render(request, 'cfp/closed.html')
speaker_form = ParticipantForm(request.POST or None, conference=request.conference, social=False)
if request.user.is_authenticated():
if Participant.objects.filter(site=request.conference.site, email=request.user.email).exists():
return redirect(reverse('proposal-dashboard'))
else:
NewSpeakerForm = modelform_factory(Participant, form=ParticipantForm, fields=['name', 'biography'])
if request.POST:
data = request.POST
else:
# TODO: import biography from User profile
data = dict(name=request.user.get_full_name())
else:
NewSpeakerForm = modelform_factory(Participant, form=ParticipantForm, fields=['name', 'email', 'biography'])
data = request.POST or None
speaker_form = NewSpeakerForm(data, conference=request.conference)
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
if request.user.is_authenticated():
speaker.email = request.user.email
speaker.save()
talk = talk_form.save(commit=False)
talk.site = request.conference.site
@ -328,16 +341,20 @@ def proposal_speaker_edit(request, speaker, talk_id=None, co_speaker_id=None):
co_speaker = get_object_or_404(Participant, site=request.conference.site, talk__pk=talk.pk, pk=co_speaker_id)
else:
co_speaker_candidates = speaker.co_speaker_set.exclude(pk__in=talk.speakers.values_list('pk'))
form = ParticipantForm(request.POST or None, conference=request.conference,
instance=co_speaker if talk else speaker, ask_notify=talk and not co_speaker)
if request.method == 'POST' and form.is_valid():
edited_speaker = form.save()
EditSpeakerForm = modelform_factory(Participant, form=ParticipantForm, fields=['name', 'email', 'biography'] + ParticipantForm.SOCIAL_FIELDS)
speaker_form = EditSpeakerForm(request.POST or None, conference=request.conference, instance=co_speaker if talk else speaker)
if talk and not co_speaker_id:
notify_form = NotifyForm(request.POST or None)
else:
notify_form = None
if request.method == 'POST' and all(map(lambda f: f.is_valid(), [speaker_form, notify_form])):
edited_speaker = speaker_form.save()
if talk:
talk.speakers.add(edited_speaker)
if co_speaker_id:
messages.success(request, _('Changes saved.'))
else:
if form.cleaned_data['notify']:
if notify_form.cleaned_data['notify']:
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))
@ -383,7 +400,8 @@ Thanks!
'talk': talk,
'co_speaker': co_speaker,
'co_speaker_candidates': co_speaker_candidates,
'form': form,
'speaker_form': speaker_form,
'notify_form': notify_form,
})
@ -697,13 +715,20 @@ def participant_details(request, participant_id):
})
class ParticipantUpdate(StaffRequiredMixin, OnSiteMixin, UpdateView):
class ParticipantUpdate(StaffRequiredMixin, OnSiteFormMixin, UpdateView):
model = Participant
slug_field = 'token'
slug_url_kwarg = 'participant_id'
form_class = ParticipantStaffForm
#form_class = ParticipantStaffForm
template_name = 'cfp/staff/participant_form.html'
def get_form_class(self):
return modelform_factory(
self.model,
form=ParticipantForm,
fields=['name', 'vip', 'email', 'phone_number', 'notes'] + ParticipantForm.SOCIAL_FIELDS,
)
@staff_required
def conference_edit(request):