implementing volunteers enrollment

This commit is contained in:
Élie Bouttier 2017-10-06 01:33:57 +02:00
parent 09a86a6c46
commit 20a67eddc2
12 changed files with 449 additions and 149 deletions

View File

@ -2,7 +2,7 @@ from django.contrib import admin
from django.contrib.sites.models import Site
from .mixins import OnSiteAdminMixin
from .models import Conference, Participant, Talk, TalkCategory, Track, Vote
from .models import Conference, Participant, Talk, TalkCategory, Track, Vote, Volunteer, Activity
class ConferenceAdmin(OnSiteAdminMixin, admin.ModelAdmin):
@ -41,8 +41,18 @@ class VoteAdmin(admin.ModelAdmin):
return super().get_queryset(request).filter(talk__site=request.conference.site)
class VolunteerAdmin(OnSiteAdminMixin, admin.ModelAdmin):
pass
class ActivityAdmin(OnSiteAdminMixin, admin.ModelAdmin):
pass
admin.site.register(Conference, ConferenceAdmin)
admin.site.register(Participant, ParticipantAdmin)
admin.site.register(Talk, TalkAdmin)
admin.site.register(TalkCategory, TalkCategoryAdmin)
admin.site.register(Vote, VoteAdmin)
admin.site.register(Volunteer, VolunteerAdmin)
admin.site.register(Activity, ActivityAdmin)

View File

@ -9,7 +9,7 @@ from django.utils.crypto import get_random_string
from django_select2.forms import ModelSelect2MultipleWidget
from .models import Participant, Talk, TalkCategory, Track, Conference, Room
from .models import Participant, Talk, TalkCategory, Track, Conference, Room, Volunteer
STATUS_CHOICES = [
@ -175,7 +175,10 @@ class UsersWidget(ModelSelect2MultipleWidget):
class ConferenceForm(forms.ModelForm):
class Meta:
model = Conference
fields = ['name', 'home', 'venue', 'city', 'contact_email', 'schedule_publishing_date', 'reply_email', 'secure_domain', 'staff',]
fields = [
'name', 'home', 'venue', 'city', 'contact_email', 'schedule_publishing_date',
'volunteers_opening_date', 'volunteers_closing_date', 'reply_email', 'secure_domain', 'staff',
]
widgets = {
'staff': UsersWidget(),
}
@ -248,3 +251,28 @@ class RoomForm(OnSiteNamedModelForm):
class Meta:
model = Room
fields = ['name', 'label', 'capacity']
class VolunteerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.conference = kwargs.pop('conference')
super().__init__(*args, **kwargs)
# we should manually check (site, email) uniqueness as the site is not part of the form
def clean_email(self):
email = self.cleaned_data['email']
if (not self.instance or self.instance.email != email) \
and self._meta.model.objects.filter(site=self.conference.site, email=email).exists():
raise self.instance.unique_error_message(self._meta.model, ['email'])
return email
def save(self, commit=True):
obj = super().save(commit=False)
obj.site = self.conference.site
if commit:
obj.save()
return obj
class Meta:
model = Volunteer
fields = ['name', 'email', 'phone_number', 'notes']

View File

@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2017-10-05 23:28
from __future__ import unicode_literals
import autoslug.fields
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('mailing', '0002_message_author'),
('sites', '0002_alter_domain_unique'),
('cfp', '0010_auto_20170927_1820'),
]
operations = [
migrations.CreateModel(
name='Activity',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=256, verbose_name='Name')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name')),
('description', models.TextField(blank=True, verbose_name='Description')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
options={
'verbose_name': 'Activity',
'verbose_name_plural': 'Activities',
},
),
migrations.CreateModel(
name='Volunteer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=128, verbose_name='Your Name')),
('email', models.EmailField(max_length=254)),
('token', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
('phone_number', models.CharField(blank=True, default='', max_length=64, verbose_name='Phone number')),
('language', models.CharField(blank=True, max_length=10)),
('notes', models.TextField(blank=True, default='', help_text='If you have some constraints, you can indicate them here.', verbose_name='Notes')),
('conversation', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='mailing.MessageThread')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.Site')),
],
),
migrations.AddField(
model_name='conference',
name='volunteers_closing_date',
field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Volunteers enrollment closing date'),
),
migrations.AddField(
model_name='conference',
name='volunteers_opening_date',
field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Volunteers enrollment opening date'),
),
migrations.AlterField(
model_name='talk',
name='description',
field=models.TextField(help_text='This field is only visible by organizers.', verbose_name='Description of your talk'),
),
migrations.AddField(
model_name='activity',
name='volunteers',
field=models.ManyToManyField(blank=True, related_name='activities', to='cfp.Volunteer', verbose_name='Volunteer'),
),
migrations.AlterUniqueTogether(
name='volunteer',
unique_together=set([('site', 'email')]),
),
migrations.AlterUniqueTogether(
name='activity',
unique_together=set([('site', 'name')]),
),
]

View File

@ -22,8 +22,8 @@ from mailing.models import MessageThread
class Conference(models.Model):
site = models.OneToOneField(Site, on_delete=models.CASCADE)
name = models.CharField(blank=True, max_length=100, verbose_name=_('Conference name'))
home = models.TextField(blank=True, default="", verbose_name=_('Homepage (markdown)'))
venue = models.TextField(blank=True, default="", verbose_name=_('Venue information'))
@ -33,6 +33,8 @@ class Conference(models.Model):
staff = models.ManyToManyField(User, blank=True, verbose_name=_('Staff members'))
secure_domain = models.BooleanField(default=True, verbose_name=_('Secure domain (HTTPS)'))
schedule_publishing_date = models.DateTimeField(null=True, blank=True, default=None, verbose_name=_('Schedule publishing date'))
volunteers_opening_date = models.DateTimeField(null=True, blank=True, default=None, verbose_name=_('Volunteers enrollment opening date'))
volunteers_closing_date = models.DateTimeField(null=True, blank=True, default=None, verbose_name=_('Volunteers enrollment closing date'))
custom_css = models.TextField(blank=True)
external_css_link = models.URLField(blank=True)
@ -43,6 +45,12 @@ class Conference(models.Model):
# events = Event.objects.filter(site=self.site)
# return any(map(lambda x: x.is_open(), events))
def volunteers_enrollment_is_open(self):
now = timezone.now()
opening = self.volunteers_opening_date
closing = self.volunteers_closing_date
return opening and opening < now and (not closing or closing > now)
@property
def opened_categories(self):
now = timezone.now()
@ -83,30 +91,22 @@ class ParticipantManager(models.Manager):
class Participant(PonyConfModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=128, verbose_name=_('Your Name'))
email = models.EmailField()
biography = models.TextField(verbose_name=_('Biography'))
token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
twitter = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Twitter'))
linkedin = models.CharField(max_length=100, blank=True, default='', verbose_name=_('LinkedIn'))
github = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Github'))
website = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Website'))
facebook = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Facebook'))
mastodon = models.CharField(max_length=100, blank=True, default='', verbose_name=_('Mastodon'))
phone_number = models.CharField(max_length=64, blank=True, default='', verbose_name=_('Phone number'))
language = models.CharField(max_length=10, blank=True)
notes = models.TextField(default='', blank=True, verbose_name=_("Notes"), help_text=_('This field is only visible by organizers.'))
notes = models.TextField(default='', blank=True, verbose_name=_("Notes"),
help_text=_('This field is only visible by organizers.'))
vip = models.BooleanField(default=False)
conversation = models.OneToOneField(MessageThread)
objects = ParticipantManager()
@ -133,9 +133,7 @@ class Participant(PonyConfModel):
class Track(PonyConfModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=128, verbose_name=_('Name'))
slug = AutoSlugField(populate_from='name')
description = models.TextField(blank=True, verbose_name=_('Description'))
@ -156,10 +154,9 @@ class Track(PonyConfModel):
class Room(models.Model):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
slug = AutoSlugField(populate_from='name')
name = models.CharField(max_length=256, blank=True, default="")
slug = AutoSlugField(populate_from='name')
label = models.CharField(max_length=256, blank=True, default="")
capacity = models.IntegerField(default=0)
@ -187,7 +184,6 @@ class Room(models.Model):
class TalkCategory(models.Model): # type of talk (conf 30min, 1h, stand, …)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
duration = models.PositiveIntegerField(default=0, verbose_name=_('Default duration (min)'))
@ -252,7 +248,6 @@ def talks_materials_destination(talk, filename):
class Talk(PonyConfModel):
LICENCES = (
('CC-Zero CC-BY', 'CC-Zero CC-BY'),
('CC-BY-SA', 'CC-BY-SA'),
@ -268,12 +263,17 @@ class Talk(PonyConfModel):
title = models.CharField(max_length=128, verbose_name=_('Talk Title'))
slug = AutoSlugField(populate_from='title', unique=True)
#abstract = models.CharField(max_length=255, blank=True, verbose_name=_('Abstract'))
description = models.TextField(verbose_name=_('Description of your talk'))
description = models.TextField(verbose_name=_('Description of your talk'),
help_text=_('This field is only visible by organizers.'))
track = models.ForeignKey(Track, blank=True, null=True, verbose_name=_('Track'))
notes = models.TextField(blank=True, verbose_name=_('Message to organizers'), help_text=_('If you have any constraint or if you have anything that may help you to select your talk, like a video or slides of your talk, please write it down here'))
notes = models.TextField(blank=True, verbose_name=_('Message to organizers'),
help_text=_('If you have any constraint or if you have anything that may '
'help you to select your talk, like a video or slides of your'
' talk, please write it down here'))
category = models.ForeignKey(TalkCategory, verbose_name=_('Talk Category'))
videotaped = models.BooleanField(_("I'm ok to be recorded on video"), default=True)
video_licence = models.CharField(choices=LICENCES, default='CC-BY-SA', max_length=10, verbose_name=_("Video licence"))
video_licence = models.CharField(choices=LICENCES, default='CC-BY-SA',
max_length=10, verbose_name=_("Video licence"))
sound = models.BooleanField(_("I need sound"), default=False)
accepted = models.NullBooleanField(default=None)
start_date = models.DateTimeField(null=True, blank=True, default=None, verbose_name=_('Beginning date and time'))
@ -330,7 +330,6 @@ class Talk(PonyConfModel):
class Vote(PonyConfModel):
talk = models.ForeignKey(Talk)
user = models.ForeignKey(User)
vote = models.IntegerField(validators=[MinValueValidator(-2), MaxValueValidator(2)], default=0)
@ -343,3 +342,41 @@ class Vote(PonyConfModel):
def get_absolute_url(self):
return self.talk.get_absolute_url()
class Volunteer(PonyConfModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=128, verbose_name=_('Your Name'))
email = models.EmailField()
token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
phone_number = models.CharField(max_length=64, blank=True, default='', verbose_name=_('Phone number'))
language = models.CharField(max_length=10, blank=True)
notes = models.TextField(default='', blank=True, verbose_name=_('Notes'),
help_text=_('If you have some constraints, you can indicate them here.'))
conversation = models.OneToOneField(MessageThread)
def get_absolute_url(self):
return reverse('volunteer-details', kwargs={'volunteer_id': self.token})
class Meta:
# A volunteer can participe only once to a Conference (= Site)
unique_together = ('site', 'email')
def __str__(self):
return str(self.name)
class Activity(models.Model):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
name = models.CharField(max_length=256, verbose_name=_('Name'))
slug = AutoSlugField(populate_from='name')
description = models.TextField(blank=True, verbose_name=_('Description'))
volunteers = models.ManyToManyField(Volunteer, blank=True, related_name='activities', verbose_name=_('Volunteer'))
class Meta:
unique_together = ('site', 'name')
verbose_name = _('Activity')
verbose_name_plural = _('Activities')
def __str__(self):
return self.name

View File

@ -8,7 +8,7 @@ from django.core.urlresolvers import reverse
from ponyconf.decorators import disable_for_loaddata
from mailing.models import MessageThread, Message
from .models import Participant, Talk, Conference
from .models import Participant, Talk, Conference, Volunteer
@receiver(post_save, sender=Site, dispatch_uid="Create Conference for Site")
@ -22,6 +22,7 @@ def create_conversation(sender, instance, **kwargs):
instance.conversation = MessageThread.objects.create()
pre_save.connect(create_conversation, sender=Participant)
pre_save.connect(create_conversation, sender=Talk)
pre_save.connect(create_conversation, sender=Volunteer)
@receiver(pre_save, sender=Message, dispatch_uid="Set message author")

View File

@ -0,0 +1,29 @@
{% extends 'base.html' %}
{% load i18n %}
{% block volunteerstab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Become a volunteers!" %}</h1>
{% for activity in activities %}
{% if forloop.first %}<div class="list-group">{% endif %}
<div class="list-group-item{% if request.user in activity.participants.all %} list-group-item-info{% endif %}">
<h4 clas="list-group-item-heading">{{ activity.name }}</h4>
<p class="list-group-item-text">
<p>{{ activity.description }}</p>
<p>
{% if volunteer not in activity.volunteers.all %}
<a class="btn btn-primary" href="{% url 'volunteer-join' volunteer_id=volunteer.token activity=activity.slug %}">{% trans "I will be happy to help on that!" %}</a>
{% else %}
<a class="btn btn-danger" href="{% url 'volunteer-quit' volunteer_id=volunteer.token activity=activity.slug %}">{% trans "Sorry, I have a setback" %}</a>
{% endif %}
</p>
</p>
</div>
{% if forloop.last %}</div>{% endif %}
{% endfor %}
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends 'base.html' %}
{% load i18n %}
{% block volunteerstab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Become a volunteers!" %}</h1>
<p>
{% blocktrans %}We need you! To participate, please enter your name and e-mail.{% endblocktrans %}
</p>
{% include '_form.html' %}
{% for activity in activities %}
{% if forloop.first %}
<div class="panel panel-default">
<div class="panel-heading">
{% blocktrans %}We are looking for help with the following activities:{% endblocktrans %}
</div>
<ul class="list-group">
{% endif %}
<li class="list-group-item">
<b>{{ activity.name }}</b>
{{ activity.description }}
</li>
{% if forloop.last %}
</ul>
</div>
{% endif %}
{% endfor %}
{% endblock %}

View File

@ -8,6 +8,10 @@ urlpatterns = [
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'),
url(r'^cfp/(?P<talk_id>[\w\-]+)/(?P<participant_id>[\w\-]+)/$', views.talk_proposal, name='talk-proposal-edit'),
url(r'^volunteer/$', views.volunteer_enrole, name='volunteer-enrole'),
url(r'^volunteer/(?P<volunteer_id>[\w\-]+)/$', views.volunteer, name='volunteer'),
url(r'^volunteer/(?P<volunteer_id>[\w\-]+)/join/(?P<activity>[\w\-]+)/$', views.volunteer_activity, {'join': True}, name='volunteer-join'),
url(r'^volunteer/(?P<volunteer_id>[\w\-]+)/quit/(?P<activity>[\w\-]+)/$', views.volunteer_activity, {'join': False}, name='volunteer-quit'),
url(r'^staff/$', views.staff, name='staff'),
url(r'^staff/conference/$', views.conference, name='conference'),
url(r'^staff/talks/$', views.talk_list, name='talk-list'),

View File

@ -22,10 +22,11 @@ from .planning import Program
from .decorators import staff_required
from .mixins import StaffRequiredMixin, OnSiteMixin, OnSiteFormMixin
from .utils import is_staff
from .models import Participant, Talk, TalkCategory, Vote, Track, Room
from .models import Participant, Talk, TalkCategory, Vote, Track, Room, Volunteer, Activity
from .forms import TalkForm, TalkStaffForm, TalkFilterForm, TalkActionForm, \
ParticipantForm, ParticipantStaffForm, ParticipantFilterForm, \
ConferenceForm, CreateUserForm, STATUS_VALUES, TrackForm, RoomForm
ConferenceForm, CreateUserForm, STATUS_VALUES, TrackForm, RoomForm, \
VolunteerForm
def home(request):
@ -35,6 +36,46 @@ def home(request):
return redirect(reverse('talk-proposal'))
def volunteer_enrole(request):
if not request.conference.volunteers_enrollment_is_open():
raise PermissionDenied
form = VolunteerForm(request.POST or None, conference=request.conference)
if request.method == 'POST' and form.is_valid():
volunteer = form.save(commit=False)
volunteer.language = request.LANGUAGE_CODE
volunteer.save()
messages.success(request, _('Thank you for your participation! You can now subscribe to some activities.'))
return redirect(reverse('volunteer', kwargs=dict(volunteer_id=volunteer.token)))
return render(request, 'cfp/volunteer_enrole.html', {
'activities': Activity.objects.filter(site=request.conference.site),
'form': form,
})
def volunteer(request, volunteer_id):
volunteer = get_object_or_404(Volunteer, token=volunteer_id, site=request.conference.site)
return render(request, 'cfp/volunteer.html', {
'activities': Activity.objects.filter(site=request.conference.site),
'volunteer': volunteer,
})
def volunteer_activity(request, volunteer_id, activity, join):
if not request.conference.volunteers_enrollment_is_open():
raise PermissionDenied
volunteer = get_object_or_404(Volunteer, token=volunteer_id, site=request.conference.site)
activity = get_object_or_404(Activity, slug=activity, site=request.conference.site)
if join:
activity.volunteers.add(volunteer)
activity.save()
messages.success(request, _('Thank you for your participation!'))
else:
activity.volunteers.remove(volunteer)
activity.save()
messages.success(request, _('Okay, no problem!'))
return redirect(reverse('volunteer', kwargs=dict(volunteer_id=volunteer.token)))
def talk_proposal(request, talk_id=None, participant_id=None):
conference = request.conference

Binary file not shown.

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-09-27 18:39+0000\n"
"PO-Revision-Date: 2017-08-30 01:14+0200\n"
"POT-Creation-Date: 2017-10-05 23:31+0000\n"
"PO-Revision-Date: 2017-10-06 01:31+0200\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: fr\n"
@ -22,11 +22,11 @@ msgstr ""
msgid "Pending decision"
msgstr "Décision en attente"
#: cfp/forms.py:17 cfp/templates/cfp/staff/talk_list.html:64
#: cfp/forms.py:17 cfp/templates/cfp/staff/talk_list.html:66
msgid "Accepted"
msgstr "Accepté"
#: cfp/forms.py:18 cfp/templates/cfp/staff/talk_list.html:66
#: cfp/forms.py:18 cfp/templates/cfp/staff/talk_list.html:68
msgid "Declined"
msgstr "Décliné"
@ -40,16 +40,16 @@ msgstr "Durée par défaut : %(duration)d min"
msgid "Category"
msgstr "Catégorie"
#: cfp/forms.py:56 cfp/templates/cfp/staff/talk_list.html:39
#: cfp/forms.py:56 cfp/templates/cfp/staff/talk_list.html:41
msgid "Title"
msgstr "Titre"
#: cfp/forms.py:57 cfp/models.py:141
#: cfp/templates/cfp/staff/talk_details.html:61 volunteers/models.py:14
#: cfp/forms.py:57 cfp/models.py:139 cfp/models.py:373
#: cfp/templates/cfp/staff/talk_details.html:61
msgid "Description"
msgstr "Description"
#: cfp/forms.py:58 cfp/models.py:106
#: cfp/forms.py:58 cfp/models.py:107 cfp/models.py:354
#: cfp/templates/cfp/staff/participant_details.html:16
#: cfp/templates/cfp/staff/talk_details.html:75
msgid "Notes"
@ -62,13 +62,13 @@ msgstr "Visible par les orateurs"
#: cfp/forms.py:73 cfp/forms.py:149
#: cfp/templates/cfp/staff/talk_details.html:18
#: cfp/templates/cfp/staff/talk_details.html:81
#: cfp/templates/cfp/staff/talk_list.html:43
#: cfp/templates/cfp/staff/talk_list.html:45
msgid "Status"
msgstr "Statut"
#: cfp/forms.py:79 cfp/forms.py:155 cfp/models.py:276
#: cfp/forms.py:79 cfp/forms.py:155 cfp/models.py:268
#: cfp/templates/cfp/staff/talk_details.html:21
#: cfp/templates/cfp/staff/talk_list.html:42
#: cfp/templates/cfp/staff/talk_list.html:44
#: cfp/templates/cfp/staff/track_form.html:14
msgid "Track"
msgstr "Session"
@ -100,7 +100,8 @@ msgstr "Programmé"
msgid "Filter talks already / not yet scheduled"
msgstr "Filtrer les exposés déjà / pas encore planifiées"
#: cfp/forms.py:97 cfp/models.py:287
#: cfp/forms.py:97 cfp/models.py:283
#: cfp/templates/cfp/staff/talk_details.html:51
msgid "Materials"
msgstr "Supports"
@ -108,7 +109,7 @@ msgstr "Supports"
msgid "Filter talks with / without materials"
msgstr "Filtrer les exposés avec / sans supports"
#: cfp/forms.py:101
#: cfp/forms.py:101 cfp/templates/cfp/staff/talk_details.html:55
msgid "Video"
msgstr "Vidéo"
@ -132,22 +133,22 @@ msgstr "Assigner à une session"
msgid "Put in a room"
msgstr "Assigner à une salle"
#: cfp/forms.py:137 cfp/models.py:139
#: cfp/templates/cfp/staff/participant_list.html:34 volunteers/models.py:12
#: cfp/forms.py:137 cfp/models.py:137 cfp/models.py:371
#: cfp/templates/cfp/staff/participant_list.html:34
msgid "Name"
msgstr "Nom"
#: cfp/forms.py:183
#: cfp/forms.py:186
msgid "New staff members will be informed of their new position by e-mail."
msgstr ""
"Les nouveaux membres du staff seront informés de leur nouveau rôle par "
"courrier électronique."
#: cfp/forms.py:203
#: cfp/forms.py:206
msgid "An user with that firstname and that lastname already exists."
msgstr "Un utilisateur avec ce prénom et ce nom existe déjà."
#: cfp/forms.py:208
#: cfp/forms.py:211
msgid "A user with that email already exists."
msgstr "Un utilisateur avec cet email existe déjà."
@ -187,7 +188,15 @@ msgstr "Domaine sécurisé (HTTPS)"
msgid "Schedule publishing date"
msgstr "Date de publication du programme"
#: cfp/models.py:67
#: cfp/models.py:36
msgid "Volunteers enrollment opening date"
msgstr "Date douverture de lappel à bénévole"
#: cfp/models.py:37
msgid "Volunteers enrollment closing date"
msgstr "Date de fermeture de lappel à bénévole"
#: cfp/models.py:75
#, python-brace-format
msgid ""
"The reply email should be a formatable string accepting a token argument (e."
@ -196,78 +205,78 @@ msgstr ""
"Ladresse de réponse doit être une chaine de texte formatable avec un "
"argument « token » (e.g. ponyconf+{token}@exemple.com)."
#: cfp/models.py:89
#: cfp/models.py:95 cfp/models.py:349
msgid "Your Name"
msgstr "Votre Nom"
#: cfp/models.py:92 cfp/templates/cfp/staff/participant_details.html:12
#: cfp/models.py:97 cfp/templates/cfp/staff/participant_details.html:12
msgid "Biography"
msgstr "Biographie"
#: cfp/models.py:95
#: cfp/models.py:99
msgid "Twitter"
msgstr "Twitter"
#: cfp/models.py:96
#: cfp/models.py:100
msgid "LinkedIn"
msgstr "LinkedIn"
#: cfp/models.py:97
#: cfp/models.py:101
msgid "Github"
msgstr "Github"
#: cfp/models.py:98
#: cfp/models.py:102
msgid "Website"
msgstr "Site web"
#: cfp/models.py:99
#: cfp/models.py:103
msgid "Facebook"
msgstr "Facebook"
#: cfp/models.py:100
#: cfp/models.py:104
msgid "Mastodon"
msgstr "Mastodon"
#: cfp/models.py:102
#: cfp/models.py:105 cfp/models.py:352
msgid "Phone number"
msgstr "Numéro de téléphone"
#: cfp/models.py:106
#: cfp/models.py:108 cfp/models.py:267
msgid "This field is only visible by organizers."
msgstr "Ce champs est uniquement visible par les organisateurs."
#: cfp/models.py:193
#: cfp/models.py:189
msgid "Default duration (min)"
msgstr "Durée par défaut (min)"
#: cfp/models.py:194
#: cfp/models.py:190
msgid "Color on program"
msgstr "Couleur sur le programme"
#: cfp/models.py:195
#: cfp/models.py:191
msgid "Label on program"
msgstr "Label dans le xml du programme"
#: cfp/models.py:271 cfp/templates/cfp/staff/base.html:17
#: cfp/models.py:262 cfp/templates/cfp/staff/base.html:17
#: cfp/templates/cfp/staff/participant_list.html:8
#: cfp/templates/cfp/staff/talk_details.html:65
#: cfp/templates/cfp/staff/talk_list.html:41
#: cfp/templates/cfp/staff/talk_list.html:43
msgid "Speakers"
msgstr "Orateurs"
#: cfp/models.py:272
#: cfp/models.py:263
msgid "Talk Title"
msgstr "Titre de la proposition"
#: cfp/models.py:275
#: cfp/models.py:266
msgid "Description of your talk"
msgstr "Description de votre proposition"
#: cfp/models.py:277
#: cfp/models.py:269
msgid "Message to organizers"
msgstr "Message aux organisateurs"
#: cfp/models.py:277
#: cfp/models.py:270
msgid ""
"If you have any constraint or if you have anything that may help you to "
"select your talk, like a video or slides of your talk, please write it down "
@ -277,48 +286,64 @@ msgstr ""
"votre proposition, comme une vidéo, des slides, n'hésitez pas à les ajouter "
"ici."
#: cfp/models.py:278
#: cfp/models.py:273
msgid "Talk Category"
msgstr "Catégorie de proposition"
#: cfp/models.py:279
#: cfp/models.py:274
msgid "I'm ok to be recorded on video"
msgstr "Jaccepte dêtre enregistré en vidéo"
#: cfp/models.py:280
#: cfp/models.py:276
msgid "Video licence"
msgstr "Licence vidéo"
#: cfp/models.py:281
#: cfp/models.py:277
msgid "I need sound"
msgstr "Jai besoin de son"
#: cfp/models.py:283
#: cfp/models.py:279
msgid "Beginning date and time"
msgstr "Date et heure de début"
#: cfp/models.py:284
#: cfp/models.py:280
msgid "Duration (min)"
msgstr "Durée (min)"
#: cfp/models.py:288
#: cfp/models.py:284
msgid ""
"You can use this field to share some materials related to your intervention."
msgstr ""
"Vous pouvez utiliser ce champs pour partager les supports de votre "
"intervention."
#: cfp/signals.py:79
#: cfp/models.py:355
msgid "If you have some constraints, you can indicate them here."
msgstr "Si vous avez des contraintes, vous pouvez les indiquer ici."
#: cfp/models.py:374
msgid "Volunteer"
msgstr "Bénévole"
#: cfp/models.py:378
msgid "Activity"
msgstr "Activité"
#: cfp/models.py:379
msgid "Activities"
msgstr "Activités"
#: cfp/signals.py:80
#, python-format
msgid "[%(prefix)s] Message from the staff"
msgstr "[%(prefix)s] Message du staff"
#: cfp/signals.py:80
#: cfp/signals.py:81
#, python-format
msgid "[%(prefix)s] Conversation with %(dest)s"
msgstr "[%(prefix)s] Conversation avec %(dest)s"
#: cfp/signals.py:96
#: cfp/signals.py:97
#, python-format
msgid "[%(prefix)s] Talk: %(talk)s"
msgstr "[%(prefix)s] Talk: %(talk)s"
@ -458,7 +483,7 @@ msgstr "par"
#: cfp/templates/cfp/staff/participant_details.html:43
#: cfp/templates/cfp/staff/room_details.html:21
#: cfp/templates/cfp/staff/room_details.html:39
#: cfp/templates/cfp/staff/talk_list.html:57
#: cfp/templates/cfp/staff/talk_list.html:59
msgid "and"
msgstr "et"
@ -489,19 +514,16 @@ msgstr "Éditer un orateur"
#: cfp/templates/cfp/staff/participant_list.html:10
#: cfp/templates/cfp/staff/talk_list.html:10
#: volunteers/templates/volunteers/volunteer_list.html:11
msgid "Show filtering options…"
msgstr "Afficher les options de filtrage…"
#: cfp/templates/cfp/staff/participant_list.html:24
#: cfp/templates/cfp/staff/talk_list.html:27
#: volunteers/templates/volunteers/volunteer_list.html:19
#: cfp/templates/cfp/staff/talk_list.html:29
msgid "Filter"
msgstr "Filtrer"
#: cfp/templates/cfp/staff/participant_list.html:30
#: cfp/templates/cfp/staff/talk_list.html:35
#: volunteers/templates/volunteers/volunteer_list.html:25
#: cfp/templates/cfp/staff/talk_list.html:37
msgid "Total:"
msgstr "Total :"
@ -568,7 +590,7 @@ msgid "Some talks are not scheduled yet."
msgstr "Certains exposés ne sont pas encore planifiés."
#: cfp/templates/cfp/staff/room_list.html:24
#: cfp/templates/cfp/staff/talk_list.html:35
#: cfp/templates/cfp/staff/talk_list.html:37
#: cfp/templates/cfp/staff/track_list.html:21
msgid "talk"
msgstr "exposé"
@ -636,6 +658,10 @@ msgstr "Créneau"
msgid "not defined"
msgstr "non défini"
#: cfp/templates/cfp/staff/talk_details.html:56
msgid "download"
msgstr "Télécharger"
#: cfp/templates/cfp/staff/talk_details.html:63
msgid "No description provided."
msgstr "Aucune description fournie."
@ -672,20 +698,20 @@ msgstr ""
msgid "Edit a talk"
msgstr "Éditer un exposé"
#: cfp/templates/cfp/staff/talk_list.html:40
#: cfp/templates/cfp/staff/talk_list.html:42
msgid "Intervention kind"
msgstr "Type dintervention"
#: cfp/templates/cfp/staff/talk_list.html:68
#: cfp/templates/cfp/staff/talk_list.html:70
#, python-format
msgid "Pending, score: %(score)s"
msgstr "En cours, score : %(score)s"
#: cfp/templates/cfp/staff/talk_list.html:80
#: cfp/templates/cfp/staff/talk_list.html:82
msgid "For selected talks:"
msgstr "Pour les exposés sélectionnés :"
#: cfp/templates/cfp/staff/talk_list.html:85
#: cfp/templates/cfp/staff/talk_list.html:87
msgid "Apply"
msgstr "Appliquer"
@ -701,7 +727,44 @@ msgstr "responsable"
msgid "No tracks."
msgstr "Aucune session."
#: cfp/views.py:76
#: cfp/templates/cfp/volunteer.html:9 cfp/templates/cfp/volunteer_enrole.html:9
msgid "Become a volunteers!"
msgstr "Devenez un bénévole !"
#: cfp/templates/cfp/volunteer.html:19
msgid "I will be happy to help on that!"
msgstr "Je serai heureux daider à cela !"
#: cfp/templates/cfp/volunteer.html:21
msgid "Sorry, I have a setback"
msgstr "Désolé, jai un contretemps"
#: cfp/templates/cfp/volunteer_enrole.html:12
msgid "We need you! To participate, please enter your name and e-mail."
msgstr ""
"Nous avons besoin de vous ! Pour participer, veuillez indiquer votre nom et "
"votre adresse e-mail."
#: cfp/templates/cfp/volunteer_enrole.html:22
msgid "We are looking for help with the following activities:"
msgstr "Nous cherchons de laide pour les activités suivantes :"
#: cfp/views.py:47
msgid ""
"Thank you for your participation! You can now subscribe to some activities."
msgstr ""
"Merci pour votre participation ! Vous pouvez maintenant vous inscrire à une "
"ou plusieurs activités."
#: cfp/views.py:71
msgid "Thank you for your participation!"
msgstr "Merci pour votre participation !"
#: cfp/views.py:75
msgid "Okay, no problem!"
msgstr "Ok, pas de soucis !"
#: cfp/views.py:117
msgid ""
"Hi {},\n"
"\n"
@ -741,35 +804,35 @@ msgstr ""
"{}\n"
"\n"
#: cfp/views.py:196 cfp/views.py:291
#: cfp/views.py:237 cfp/views.py:332
msgid "The talk has been accepted."
msgstr "Lexposé a été accepté."
#: cfp/views.py:198 cfp/views.py:293
#: cfp/views.py:239 cfp/views.py:334
msgid "The talk has been declined."
msgstr "Lexposé a été décliné."
#: cfp/views.py:261 cfp/views.py:349
#: cfp/views.py:302 cfp/views.py:390
msgid "Message sent!"
msgstr "Message envoyé !"
#: cfp/views.py:274
#: cfp/views.py:315
msgid "Vote successfully created"
msgstr "A voté !"
#: cfp/views.py:274
#: cfp/views.py:315
msgid "Vote successfully updated"
msgstr "Vote mis à jour"
#: cfp/views.py:295
#: cfp/views.py:336
msgid "Decision taken in account"
msgstr "Décision enregistrée"
#: cfp/views.py:377
#: cfp/views.py:418
msgid "[{}] You have been added to the staff team"
msgstr "[{}] Vous avez été ajouté aux membres du staff"
#: cfp/views.py:378
#: cfp/views.py:419
msgid ""
"Hi {},\n"
"\n"
@ -793,15 +856,15 @@ msgstr ""
"{}\n"
"\n"
#: cfp/views.py:399
#: cfp/views.py:440
msgid "Modifications successfully saved."
msgstr "Modification enregistrée avec succès."
#: cfp/views.py:476
#: cfp/views.py:517
msgid "User created successfully."
msgstr "Utilisateur créé avec succès."
#: cfp/views.py:497
#: cfp/views.py:538
#, python-format
msgid "Format '%s' not available"
msgstr "Format '%s' non disponible"
@ -839,15 +902,19 @@ msgstr "Accueil"
msgid "Call for participation"
msgstr "Appel à participation"
#: ponyconf/templates/base.html:39 ponyconf/templates/base.html:50
#: ponyconf/templates/base.html:28
msgid "Volunteers"
msgstr "Bénévoles"
#: ponyconf/templates/base.html:42 ponyconf/templates/base.html:53
msgid "Staff"
msgstr "Staff"
#: ponyconf/templates/base.html:48
#: ponyconf/templates/base.html:51
msgid "Logout"
msgstr "Déconnection"
#: ponyconf/templates/base.html:67
#: ponyconf/templates/base.html:70
msgid "Powered by"
msgstr "Propulsé par"
@ -875,56 +942,25 @@ msgstr "Changement de mot de passe"
msgid "Email address"
msgstr "Adresse e-mail"
#: volunteers/models.py:15
msgid "Participants"
msgstr "Participants"
#~ msgid "Participants"
#~ msgstr "Participants"
#: volunteers/models.py:19
msgid "Activity"
msgstr "Activité"
#~ msgid "We are not yet looking for volunteers … come back later!"
#~ msgstr ""
#~ "Nous ne sommes pas encore en recherche de bénévoles … mais revenez plus "
#~ "tard !"
#: volunteers/models.py:20
#: volunteers/templates/volunteers/volunteer_list.html:31
msgid "Activities"
msgstr "Activités"
#~ msgid "volunteer"
#~ msgstr "bénévole"
#: volunteers/templates/volunteers/volunteer_enrole.html:9
msgid "Enrole as volunteer"
msgstr "Devenir bénévole"
#~ msgid "Username"
#~ msgstr "Nom dutilisateur"
#: volunteers/templates/volunteers/volunteer_enrole.html:19
msgid "Sorry, I have a setback"
msgstr "Désolé, jai un contretemps"
#~ msgid "Fullname"
#~ msgstr "Prénom et nom"
#: volunteers/templates/volunteers/volunteer_enrole.html:21
msgid "I will be happy to help on that!"
msgstr "Je serai heureux daider à cela !"
#: volunteers/templates/volunteers/volunteer_enrole.html:28
msgid "We are not yet looking for volunteers … come back later!"
msgstr ""
"Nous ne sommes pas encore en recherche de bénévoles … mais revenez plus "
"tard !"
#: volunteers/templates/volunteers/volunteer_list.html:9
msgid "Volunteers"
msgstr "Bénévoles"
#: volunteers/templates/volunteers/volunteer_list.html:25
msgid "volunteer"
msgstr "bénévole"
#: volunteers/templates/volunteers/volunteer_list.html:29
msgid "Username"
msgstr "Nom dutilisateur"
#: volunteers/templates/volunteers/volunteer_list.html:30
msgid "Fullname"
msgstr "Prénom et nom"
#: volunteers/templates/volunteers/volunteer_list.html:48
msgid "Contact"
msgstr "Contacter"
#~ msgid "Contact"
#~ msgstr "Contacter"
#~ msgid ""
#~ "For example, you need to be back on saturday evening, you cannot eat meat."
@ -1185,9 +1221,6 @@ msgstr "Contacter"
#~ msgid "required but unlimited"
#~ msgstr "requis mais non limité"
#~ msgid "download"
#~ msgstr "Télécharger"
#~ msgid "Assign to"
#~ msgstr "Assigner à"

View File

@ -24,6 +24,9 @@
{% if conference.schedule_available %}
<li{% block publicscheduletab %}{% endblock %}><a href="{% url 'public-schedule' %}"><span class="glyphicon glyphicon-calendar"></span>&nbsp;{% trans "Schedule" %}</a></li>
{% endif %}
{% if conference.volunteers_enrolement_is_open %}
<li{% block volunteerstab %}{% endblock %}><a href="{% url 'volunteer-enrole' %}"><span class="glyphicon glyphicon-thumbs-up"></span>&nbsp;{% trans "Volunteers" %}</a></li>
{% endif %}
{% comment %}
{% if request.user.is_authenticated %}
<li{% block exhibitortab %}{% endblock %}><a href="{% url 'participate-as-speaker' %}"><span class="glyphicon glyphicon-bullhorn"></span>&nbsp;{% trans "Exhibitor" %}</a></li>