From 1f5377f38ddcbdd845c94284c4b1ecebf529d786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89lie=20Bouttier?= Date: Sat, 4 Nov 2017 15:30:00 +0100 Subject: [PATCH] major overhaul of proposal process --- cfp/decorators.py | 17 +- cfp/forms.py | 70 +- cfp/migrations/0018_auto_20171104_1227.py | 25 + cfp/models.py | 11 +- cfp/templates/cfp/closed.html | 15 - cfp/templates/cfp/complete.html | 27 - cfp/templates/cfp/proposal_dashboard.html | 77 +++ cfp/templates/cfp/proposal_home.html | 33 + cfp/templates/cfp/proposal_mail_token.html | 30 + cfp/templates/cfp/proposal_speaker_form.html | 42 ++ cfp/templates/cfp/proposal_talk_details.html | 86 +++ .../{propose.html => proposal_talk_form.html} | 12 +- cfp/templates/cfp/speaker.html | 26 - cfp/templatetags/cfp_tags.py | 5 + cfp/urls.py | 19 +- cfp/views.py | 319 ++++++--- locale/fr/LC_MESSAGES/django.mo | Bin 17631 -> 21304 bytes locale/fr/LC_MESSAGES/django.po | 604 ++++++++++++------ ponyconf/templates/base.html | 2 +- 19 files changed, 1022 insertions(+), 398 deletions(-) create mode 100644 cfp/migrations/0018_auto_20171104_1227.py delete mode 100644 cfp/templates/cfp/closed.html delete mode 100644 cfp/templates/cfp/complete.html create mode 100644 cfp/templates/cfp/proposal_dashboard.html create mode 100644 cfp/templates/cfp/proposal_home.html create mode 100644 cfp/templates/cfp/proposal_mail_token.html create mode 100644 cfp/templates/cfp/proposal_speaker_form.html create mode 100644 cfp/templates/cfp/proposal_talk_details.html rename cfp/templates/cfp/{propose.html => proposal_talk_form.html} (61%) delete mode 100644 cfp/templates/cfp/speaker.html diff --git a/cfp/decorators.py b/cfp/decorators.py index a5ca0ac..40964bc 100644 --- a/cfp/decorators.py +++ b/cfp/decorators.py @@ -1,9 +1,22 @@ -from functools import wraps - from django.core.exceptions import PermissionDenied from django.contrib.auth.decorators import login_required +from django.shortcuts import get_object_or_404 + +from functools import wraps from cfp.utils import is_staff +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) + kwargs['speaker'] = speaker + return view_func(request, **kwargs) + return wraps(view_func)(wrapped_view) def staff_required(view_func): diff --git a/cfp/forms.py b/cfp/forms.py index b0bef9e..a2bc667 100644 --- a/cfp/forms.py +++ b/cfp/forms.py @@ -36,6 +36,27 @@ CONFIRMATION_VALUES = [ ] +class OnSiteNamedModelForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + self.conference = kwargs.pop('conference') + super().__init__(*args, **kwargs) + + # we should manually check (site, name) uniqueness as the site is not part of the form + def clean_name(self): + name = self.cleaned_data['name'] + if (not self.instance or self.instance.name != name) \ + and self._meta.model.objects.filter(site=self.conference.site, name=name).exists(): + raise self.instance.unique_error_message(self._meta.model, ['name']) + return name + + def save(self, commit=True): + obj = super().save(commit=False) + obj.site = self.conference.site + if commit: + obj.save() + return obj + + class VolunteerFilterForm(forms.Form): activity = forms.MultipleChoiceField( label=_('Activity'), @@ -174,15 +195,29 @@ class TalkActionForm(forms.Form): self.fields['room'].choices = [(None, "---------")] + list(rooms.values_list('slug', 'name')) -ParticipantForm = modelform_factory(Participant, fields=('name', 'email', 'biography')) +class ParticipantForm(OnSiteNamedModelForm): + def __init__(self, *args, **kwargs): + social = kwargs.pop('social', True) + super().__init__(*args, **kwargs) + if not social: + for field in ['twitter', 'linkedin', 'github', 'website', 'facebook', 'mastodon']: + self.fields.pop(field) + + class Meta: + model = Participant + fields = ['name', 'email', 'biography', 'twitter', 'linkedin', 'github', 'website', 'facebook', 'mastodon'] + + 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 class ParticipantStaffForm(ParticipantForm): class Meta(ParticipantForm.Meta): - fields = ('name', 'vip', 'email', 'biography') - labels = { - 'name': _('Name'), - } + fields = ['name', 'vip', 'email', 'phone_number', 'notes'] + ParticipantForm.Meta.fields[3:] class ParticipantFilterForm(forms.Form): @@ -220,6 +255,10 @@ class ParticipantFilterForm(forms.Form): self.fields['track'].choices = [('none', _('Not assigned'))] + list(tracks.values_list('slug', 'name')) +class MailForm(forms.Form): + email = forms.EmailField(required=True, label=_('Email')) + + class UsersWidget(ModelSelect2MultipleWidget): model = User search_fields = [ '%s__icontains' % field for field in UserAdmin.search_fields ] @@ -273,27 +312,6 @@ class CreateUserForm(forms.ModelForm): return user -class OnSiteNamedModelForm(forms.ModelForm): - def __init__(self, *args, **kwargs): - self.conference = kwargs.pop('conference') - super().__init__(*args, **kwargs) - - # we should manually check (site, name) uniqueness as the site is not part of the form - def clean_name(self): - name = self.cleaned_data['name'] - if (not self.instance or self.instance.name != name) \ - and self._meta.model.objects.filter(site=self.conference.site, name=name).exists(): - raise self.instance.unique_error_message(self._meta.model, ['name']) - return name - - def save(self, commit=True): - obj = super().save(commit=False) - obj.site = self.conference.site - if commit: - obj.save() - return obj - - class TrackForm(OnSiteNamedModelForm): class Meta: model = Track diff --git a/cfp/migrations/0018_auto_20171104_1227.py b/cfp/migrations/0018_auto_20171104_1227.py new file mode 100644 index 0000000..7e9884b --- /dev/null +++ b/cfp/migrations/0018_auto_20171104_1227.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-11-04 12:27 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cfp', '0017_auto_20171103_1922'), + ] + + operations = [ + migrations.AddField( + model_name='conference', + name='acceptances_disclosure_date', + field=models.DateTimeField(blank=True, default=None, null=True, verbose_name='Acceptances disclosure date'), + ), + migrations.AlterField( + model_name='participant', + name='name', + field=models.CharField(max_length=128, verbose_name='Name'), + ), + ] diff --git a/cfp/models.py b/cfp/models.py index 09090eb..f9c8ff8 100644 --- a/cfp/models.py +++ b/cfp/models.py @@ -34,6 +34,7 @@ class Conference(models.Model): reply_email = models.CharField(max_length=100, blank=True, verbose_name=_('Reply email')) staff = models.ManyToManyField(User, blank=True, verbose_name=_('Staff members')) secure_domain = models.BooleanField(default=True, verbose_name=_('Secure domain (HTTPS)')) + acceptances_disclosure_date = models.DateTimeField(null=True, blank=True, default=None, verbose_name=_('Acceptances disclosure date')) schedule_publishing_date = models.DateTimeField(null=True, blank=True, default=None, verbose_name=_('Schedule publishing date')) schedule_redirection_url = models.URLField(blank=True, default='', verbose_name=_('Schedule redirection URL'), help_text=_('If specified, schedule tab will redirect to this URL.')) @@ -56,6 +57,11 @@ class Conference(models.Model): .filter(Q(opening_date__isnull=True) | Q(opening_date__lte=now))\ .filter(Q(closing_date__isnull=True) | Q(closing_date__gte=now)) + @property + def disclosed_acceptances(self): + # acceptances are automatically disclosed if the schedule is published + return self.acceptances_disclosure_date and self.acceptances_disclosure_date <= timezone.now() or self.schedule_available + @property def schedule_available(self): return self.schedule_publishing_date and self.schedule_publishing_date <= timezone.now() @@ -90,7 +96,7 @@ 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')) + name = models.CharField(max_length=128, verbose_name=_('Name')) email = models.EmailField() biography = models.TextField(verbose_name=_('Biography')) token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) @@ -110,10 +116,11 @@ class Participant(PonyConfModel): objects = ParticipantManager() def get_absolute_url(self): - return reverse('participant-details', kwargs={'participant_id': self.token}) + return reverse('proposal-dashboard', kwargs={'speaker_token': self.token}) class Meta: # A User can participe only once to a Conference (= Site) + unique_together = ('site', 'name') unique_together = ('site', 'email') def __str__(self): diff --git a/cfp/templates/cfp/closed.html b/cfp/templates/cfp/closed.html deleted file mode 100644 index 792703c..0000000 --- a/cfp/templates/cfp/closed.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends 'base.html' %} -{% load i18n %} - -{% block proposetab %} class="active"{% endblock %} - -{% block content %} - - -

{% trans "Sorry, the Call for Participation is closed!" %}

- -{% endblock %} diff --git a/cfp/templates/cfp/complete.html b/cfp/templates/cfp/complete.html deleted file mode 100644 index c25b4a2..0000000 --- a/cfp/templates/cfp/complete.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends 'base.html' %} - -{% load ponyconf_tags i18n %} - -{% block proposetab %} class="active"{% endblock %} - -{% block content %} - - -
- -
-{% endblock %} diff --git a/cfp/templates/cfp/proposal_dashboard.html b/cfp/templates/cfp/proposal_dashboard.html new file mode 100644 index 0000000..c1d3b28 --- /dev/null +++ b/cfp/templates/cfp/proposal_dashboard.html @@ -0,0 +1,77 @@ +{% extends 'base.html' %} +{% load i18n crispy_forms_tags cfp_tags %} + +{% load ponyconf_tags i18n %} + +{% block proposetab %} class="active"{% endblock %} + +{% block content %} + + +

{% trans "Your informations" %}

+ +

+

+

+ +

{% trans "Biography" %}

+ +

+ {% if speaker.biography %} + {{ speaker.biography|linebreaksbr }} + {% else %} + {% trans "No biography." %} + {% endif %} +

+ +

{% trans "Your proposals" %}

+ +

+ {% for talk in talks %} + {% if forloop.first %} +

+ {% endif %} + {% empty %} + {% trans "No proposals." %} + {% endfor %} +

+

+ {% trans "New proposal" %} +

+{% endblock %} diff --git a/cfp/templates/cfp/proposal_home.html b/cfp/templates/cfp/proposal_home.html new file mode 100644 index 0000000..6622729 --- /dev/null +++ b/cfp/templates/cfp/proposal_home.html @@ -0,0 +1,33 @@ +{% extends 'base.html' %} +{% load crispy_forms_tags %} + +{% load ponyconf_tags i18n %} + +{% block proposetab %} class="active"{% endblock %} + +{% block content %} + + + +
+
+
+ + {% 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 here.{% endblocktrans %} +
+
+ {% csrf_token %} + {{ speaker_form|crispy }} + {{ talk_form|crispy }} +
+ +
+
+
+
+{% endblock %} diff --git a/cfp/templates/cfp/proposal_mail_token.html b/cfp/templates/cfp/proposal_mail_token.html new file mode 100644 index 0000000..9ad8b31 --- /dev/null +++ b/cfp/templates/cfp/proposal_mail_token.html @@ -0,0 +1,30 @@ +{% extends 'base.html' %} +{% load crispy_forms_tags %} + +{% load ponyconf_tags i18n %} + +{% block proposetab %} class="active"{% endblock %} + +{% block content %} + + + +
+
+
+ {% csrf_token %} +
+ {% blocktrans %}To receive a email with a link to access your profile, please enter your email below.{% endblocktrans %} +
+ {{ form|crispy }} +
+ +
+
+
+
+{% endblock %} diff --git a/cfp/templates/cfp/proposal_speaker_form.html b/cfp/templates/cfp/proposal_speaker_form.html new file mode 100644 index 0000000..13e2a82 --- /dev/null +++ b/cfp/templates/cfp/proposal_speaker_form.html @@ -0,0 +1,42 @@ +{% extends 'base.html' %} +{% load crispy_forms_tags %} + +{% load ponyconf_tags i18n %} + +{% block proposetab %} class="active"{% endblock %} + +{% block content %} + + +
+
+
+ {% csrf_token %} + {{ form|crispy }} +
+ +
+
+
+
+{% endblock %} diff --git a/cfp/templates/cfp/proposal_talk_details.html b/cfp/templates/cfp/proposal_talk_details.html new file mode 100644 index 0000000..9c2878a --- /dev/null +++ b/cfp/templates/cfp/proposal_talk_details.html @@ -0,0 +1,86 @@ +{% extends 'base.html' %} +{% load i18n crispy_forms_tags %} + +{% load ponyconf_tags i18n %} + +{% block proposetab %} class="active"{% endblock %} + +{% block content %} + + +

{% trans "Status" %}

+ +{% if not conference.disclosed_acceptances or talk.accepted is None %} +

{% trans "Reviewing in progress, we will keep you informed by mail." %}

+{% elif talk.accepted %} +

{% trans "Accepted!" %}

+{% if talk.confirmed is None %} +

+ {% trans "Please confirm your participation:" %} + {% trans "I will be there!" %} + {% trans "Sorry, couldn't make it :-(" %} +

+{% elif talk.confirmed %} +

+ {% trans "Sorry, I have to cancel." %} +

+{% else %} +

+ {% trans "Good news, I finally could be there!" %} +

+{% endif %} +{% else %} +

{% trans "Sorry, refused :-(" %}

+{% endif %} + +

{% trans "Speakers" %}

+ +

+ {% for spkr in talk.speakers.all %} + {% if forloop.first %}

{% endif %} + {% endfor %} + +  {% trans "Add a co-speaker" %} + +

+ +

{% trans "Description" %}

+ +

+ {% if talk.description %} + {{ talk.description|linebreaksbr }} + {% else %} + {% trans "No description provided." %} + {% endif %} +

+ +

{% trans "Message to organizers" %}

+ +

+ {% if talk.notes %} + {{ talk.notes|linebreaksbr }} + {% else %} + {% trans "No description provided." %} + {% endif %} +

+{% endblock %} diff --git a/cfp/templates/cfp/propose.html b/cfp/templates/cfp/proposal_talk_form.html similarity index 61% rename from cfp/templates/cfp/propose.html rename to cfp/templates/cfp/proposal_talk_form.html index a59db31..b7ba544 100644 --- a/cfp/templates/cfp/propose.html +++ b/cfp/templates/cfp/proposal_talk_form.html @@ -8,7 +8,14 @@ {% block content %} @@ -16,8 +23,7 @@
{% csrf_token %} - {{ participant_form|crispy }} - {{ talk_form|crispy }} + {{ form|crispy }}
diff --git a/cfp/templates/cfp/speaker.html b/cfp/templates/cfp/speaker.html deleted file mode 100644 index 6a98b01..0000000 --- a/cfp/templates/cfp/speaker.html +++ /dev/null @@ -1,26 +0,0 @@ -{% extends 'base.html' %} -{% load crispy_forms_tags %} - -{% load ponyconf_tags i18n %} - -{% block proposetab %} class="active"{% endblock %} - -{% block content %} - - -
-
- - {% csrf_token %} - {{ participant_form|crispy }} -
- -
- -
-
-{% endblock %} diff --git a/cfp/templatetags/cfp_tags.py b/cfp/templatetags/cfp_tags.py index 11ae8a6..7128df5 100644 --- a/cfp/templatetags/cfp_tags.py +++ b/cfp/templatetags/cfp_tags.py @@ -17,3 +17,8 @@ def duration_format(value): hours = int(value/60) minutes = value%60 return '%d h %02d' % (hours, minutes) + + +@register.filter +def exclude(queryset, excluded): + return queryset.exclude(pk=excluded.pk) diff --git a/cfp/urls.py b/cfp/urls.py index 600de38..e15e192 100644 --- a/cfp/urls.py +++ b/cfp/urls.py @@ -4,12 +4,27 @@ from . import views urlpatterns = [ url(r'^$', views.home, name='home'), - url(r'^cfp/$', views.talk_proposal, name='talk-proposal'), +# 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[\w\-]+)/$', views.proposal_dashboard, name='proposal-dashboard'), + url(r'^cfp/(?P[\w\-]+)/profile/$', views.proposal_speaker_edit, name='proposal-profile-edit'), + url(r'^cfp/(?P[\w\-]+)/talk/add/$', views.proposal_talk_edit, name='proposal-talk-add'), + url(r'^cfp/(?P[\w\-]+)/talk/(?P[\w\-]+)/$', views.proposal_talk_details, name='proposal-talk-details'), + url(r'^cfp/(?P[\w\-]+)/talk/(?P[\w\-]+)/edit/$', views.proposal_talk_edit, name='proposal-talk-edit'), + url(r'^cfp/(?P[\w\-]+)/talk/(?P[\w\-]+)/speaker/add/$', views.proposal_speaker_edit, name='proposal-speaker-add'), + url(r'^cfp/(?P[\w\-]+)/talk/(?P[\w\-]+)/confirm/$', views.proposal_talk_acknowledgment, {'confirm': True}, name='proposal-talk-confirm'), + url(r'^cfp/(?P[\w\-]+)/talk/(?P[\w\-]+)/desist/$', views.proposal_talk_acknowledgment, {'confirm': False}, name='proposal-talk-desist'), + #url(r'^cfp/(?P[\w\-]+)/talk/(?P[\w\-]+)/speaker/(?P[\w\-]+)/$', views.proposal_speaker_details, name='proposal-speaker-details'), + url(r'^cfp/(?P[\w\-]+)/talk/(?P[\w\-]+)/speaker/(?P[\w\-]+)/edit/$', views.proposal_speaker_edit, name='proposal-speaker-edit'), + url(r'^cfp/(?P[\w\-]+)/talk/(?P[\w\-]+)/speaker/(?P[\w\-]+)/remove/$', views.proposal_speaker_remove, name='proposal-speaker-remove'), +# Backward compatibility url(r'^cfp/(?P[\w\-]+)/speaker/add/$', views.talk_proposal_speaker_edit, name='talk-proposal-speaker-add'), url(r'^cfp/(?P[\w\-]+)/speaker/(?P[\w\-]+)/$', views.talk_proposal_speaker_edit, name='talk-proposal-speaker-edit'), url(r'^cfp/(?P[\w\-]+)/(?P[\w\-]+)/$', views.talk_proposal, name='talk-proposal-edit'), url(r'^cfp/(?P[\w\-]+)/(?P[\w\-]+)/confirm/$', views.talk_acknowledgment, {'confirm': True}, name='talk-confirm'), url(r'^cfp/(?P[\w\-]+)/(?P[\w\-]+)/desist/$', views.talk_acknowledgment, {'confirm': False}, name='talk-desist'), +# End backward compatibility url(r'^volunteer/$', views.volunteer_enrole, name='volunteer-enrole'), url(r'^volunteer/(?P[\w\-]+)/$', views.volunteer_home, name='volunteer-home'), url(r'^volunteer/(?P[\w\-]+)/join/(?P[\w\-]+)/$', views.volunteer_update_activity, {'join': True}, name='volunteer-join'), @@ -41,7 +56,7 @@ urlpatterns = [ url(r'^staff/schedule/((?P[\w]+)/)?$', views.staff_schedule, name='staff-schedule'), url(r'^staff/select2/$', views.Select2View.as_view(), name='django_select2-json'), url(r'^admin/$', views.admin, name='admin'), - url(r'^admin/conference/$', views.conference, name='conference'), + url(r'^admin/conference/$', views.conference_edit, name='conference'), url(r'^schedule/((?P[\w]+)/)?$', views.public_schedule, name='public-schedule'), #url(r'^markdown/$', views.markdown_preview, name='markdown'), ] diff --git a/cfp/views.py b/cfp/views.py index 9943ef0..b82d475 100644 --- a/cfp/views.py +++ b/cfp/views.py @@ -20,14 +20,14 @@ from functools import reduce from mailing.models import Message from mailing.forms import MessageForm from .planning import Program -from .decorators import staff_required +from .decorators import speaker_required, staff_required 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, \ ConferenceForm, CreateUserForm, TrackForm, RoomForm, \ - VolunteerForm, VolunteerFilterForm, \ + VolunteerForm, VolunteerFilterForm, MailForm, \ ACCEPTATION_VALUES, CONFIRMATION_VALUES @@ -35,7 +35,7 @@ def home(request): if request.conference.home: return render(request, 'cfp/home.html') else: - return redirect(reverse('talk-proposal')) + return redirect(reverse('proposal-home')) def volunteer_enrole(request): @@ -139,43 +139,25 @@ def volunteer_details(request, volunteer_id): }) -def talk_proposal(request, talk_id=None, participant_id=None): - conference = request.conference - site = conference.site +def proposal_home(request): if is_staff(request, request.user): - categories = TalkCategory.objects.filter(site=site) + categories = TalkCategory.objects.filter(site=request.conference.site) else: - categories = conference.opened_categories - talk = None - participant = None - - if talk_id and participant_id: - talk = get_object_or_404(Talk, token=talk_id, site=site) - participant = get_object_or_404(Participant, token=participant_id, site=site) - elif not categories.exists(): - return render(request, 'cfp/closed.html') - - participant_form = ParticipantForm(request.POST or None, instance=participant) - talk_form = TalkForm(request.POST or None, categories=categories, instance=talk) - - if request.method == 'POST' and talk_form.is_valid() and participant_form.is_valid(): + categories = request.conference.opened_categories + speaker_form = ParticipantForm(request.POST or None, conference=request.conference, social=False) + 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 + speaker.save() talk = talk_form.save(commit=False) - talk.site = site - - participant, created = Participant.objects.get_or_create(email=participant_form.cleaned_data['email'], site=site) - participant_form = ParticipantForm(request.POST, instance=participant) - participant = participant_form.save() - participant.language = request.LANGUAGE_CODE - participant.save() - + talk.site = request.conference.site talk.save() - talk.speakers.add(participant) - - protocol = 'https' if request.is_secure() else 'http' - base_url = protocol+'://'+site.domain - url_talk_proposal_edit = base_url + reverse('talk-proposal-edit', args=[talk.token, participant.token]) - url_talk_proposal_speaker_add = base_url + reverse('talk-proposal-speaker-add', args=[talk.token]) - url_talk_proposal_speaker_edit = base_url + reverse('talk-proposal-speaker-edit', args=[talk.token, participant.token]) + 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)) body = _("""Hi {}, Your talk has been submitted for {}. @@ -185,9 +167,9 @@ Title: {} Description: {} You can at anytime: -- edit your talk: {} +- review and edit your profile: {} +- review and edit your talk: {} - add a new co-speaker: {} -- edit your profile: {} If you have any question, your can answer to this email. @@ -195,89 +177,232 @@ Thanks! {} -""").format(participant.name, conference.name, talk.title, talk.description, url_talk_proposal_edit, url_talk_proposal_speaker_add, url_talk_proposal_speaker_edit, conference.name) - +""").format( + speaker.name, request.conference.name,talk.title, talk.description, + url_dashboard, url_talk_details, url_speaker_add, + request.conference.name, + ) Message.objects.create( - thread=participant.conversation, - author=conference, - from_email=conference.contact_email, + thread=speaker.conversation, + author=request.conference, + from_email=request.conference.contact_email, content=body, ) - - return render(request, 'cfp/complete.html', {'talk': talk, 'participant': participant}) - - return render(request, 'cfp/propose.html', { - 'participant_form': participant_form, - 'site': site, + messages.success(request, _('You proposition have been successfully submitted!')) + 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, 'talk_form': talk_form, }) -def talk_proposal_speaker_edit(request, talk_id, participant_id=None): +def proposal_mail_token(request): + form = MailForm(request.POST or None) + 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: - talk = get_object_or_404(Talk, token=talk_id, site=request.conference.site) - participant = None + 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 {}, - if participant_id: - participant = get_object_or_404(Participant, token=participant_id, site=request.conference.site) +Someone, probably you, ask to access your profile. +You can edit your talks or add new ones following this url: - participant_form = ParticipantForm(request.POST or None, instance=participant) + {} - if request.method == 'POST' and participant_form.is_valid(): +If you have any question, your can answer to this email. - participant, created = Participant.objects.get_or_create(email=participant_form.cleaned_data['email'], site=request.conference.site) - participant_form = ParticipantForm(request.POST, instance=participant) - participant = participant_form.save() - participant.save() +Sincerely, - talk.speakers.add(participant) +{} - return render(request,'cfp/complete.html', {'talk': talk, 'participant': participant}) - - return render(request, 'cfp/speaker.html', { - 'participant_form': participant_form, +""").format(speaker.name, dashboard_url, request.conference.name) + Message.objects.create( + thread=speaker.conversation, + author=request.conference, + from_email=request.conference.contact_email, + content=body, + ) + messages.success(request, _('A email have been sent with a link to access to your profil.')) + return redirect(reverse('proposal-mail-token')) + return render(request, 'cfp/proposal_mail_token.html', { + 'form': form, }) -def talk_acknowledgment(request, talk_id, confirm, participant_id=None): - # TODO: handle multiple speakers case - talk = get_object_or_404(Talk, token=talk_id, site=request.conference.site) - if participant_id: - participant = get_object_or_404(Participant, token=participant_id, site=request.conference.site) - elif not is_staff(request, request.user): - raise PermissionDenied +@speaker_required +def proposal_dashboard(request, speaker): + return render(request, 'cfp/proposal_dashboard.html', { + 'speaker': speaker, + 'talks': speaker.talk_set.all(), + }) + + +@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: - participant = None - if not talk.accepted: - raise PermissionDenied - if talk.confirmed != confirm: - talk.confirmed = confirm + talk = None + if is_staff(request, request.user): + categories = TalkCategory.objects.filter(site=request.conference.site) + else: + categories = request.conference.opened_categories + form = TalkForm(request.POST or None, categories=categories, instance=talk) + if request.method == 'POST' and form.is_valid(): + talk = form.save(commit=False) + talk.site = request.conference.site talk.save() - if confirm: - confirmation_message= _('Your participation has been taken into account, thank you!') - if participant: - thread_note = _('Speaker %(speaker)s confirmed his/her participation.') - else: - thread_note = _('The talk have been confirmed.') + talk.speakers.add(speaker) + if talk_id: + messages.success(request, _('Changes saved.')) else: - confirmation_message = _('We have noted your unavailability.') - if participant: - thread_note = _('Speaker %(speaker)s CANCELLED his/her participation.') - else: - thread_note = _('The talk have been cancelled.') - if participant_id: - thread_note = thread_note % {'speaker': participant} - Message.objects.create(thread=talk.conversation, author=participant or request.user, content=thread_note) - messages.success(request, confirmation_message) - 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 + messages.success(request, _('You proposition have been successfully submitted!')) + 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, + }) + + +@speaker_required +def proposal_talk_acknowledgment(request, speaker, talk_id, confirm): + # TODO: handle multiple speakers case + talk = get_object_or_404(Talk, site=request.conference.site, speakers__pk=speaker.pk, pk=talk_id) + if not request.conference.disclosed_acceptances or not talk.accepted: + raise PermissionDenied + 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.')) - if participant: - return redirect(reverse('talk-proposal-edit', kwargs=dict(talk_id=talk_id, participant_id=participant_id))) else: - return redirect(reverse('talk-details', kwargs=dict(talk_id=talk_id))) + talk.confirmed = confirm + talk.save() + if confirm: + confirmation_message= _('Your participation has been taken into account, thank you!') + thread_note = _('Speaker %(speaker)s confirmed his/her participation.' % {'speaker': speaker}) + else: + confirmation_message = _('We have noted your unavailability.') + thread_note = _('Speaker %(speaker)s CANCELLED his/her participation.' % {'speaker': speaker}) + Message.objects.create(thread=talk.conversation, author=speaker, content=thread_note) + messages.success(request, confirmation_message) + return redirect(reverse('proposal-talk-details', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk))) + + +# 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): + 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: + co_speaker = None + else: + talk = None + co_speaker = None + form = ParticipantForm(request.POST or None, conference=request.conference, instance=co_speaker if talk else speaker) + if request.method == 'POST' and form.is_valid(): + # TODO: Allow to add a co-speaker which already exists. + # This should be automatically allowed if the speaker already have a talk in common with the co-speaker. + # Otherwise, we should send an speaker request to the other user OR allow the other user to join the talk with his token. + # This last requirements in planned for v3. + edited_speaker = form.save() + if talk: + talk.speakers.add(edited_speaker) + #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, + 'form': form, + }) + + +@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) + co_speaker = get_object_or_404(Participant, site=request.conference.site, talk_set__pk=talk.pk, pk=co_speaker_id) + return redirect(reverse('proposal-speaker-details', kwargs=dict())) + + +# BACKWARD COMPATIBILITY +def talk_proposal(request, talk_id=None, participant_id=None): + if talk_id and participant_id: + talk = get_object_or_404(Talk, token=talk_id, site=site) + speaker = get_object_or_404(Participant, token=participant_id, site=site) + return render(reverse('proposal-talk-edit', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk))) + else: + return render(reverse('proposal-home')) + + +# BACKWARD COMPATIBILITY +def talk_proposal_speaker_edit(request, talk_id, participant_id=None): + talk = get_object_or_404(Talk, token=talk_id, site=request.conference.site) + speaker = talk.speakers.first() # no other choice here… + if participant_id: + co_speaker = get_object_or_404(Participant, token=participant_id, site=request.conference.site) + return redirect(reverse('proposal-speaker-edit', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk, co_speaker_id=co_speaker.pk))) + else: + return redirect(reverse('proposal-speaker-add', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk))) + + +# TODO: add @staff_required decorator when dropping old links support +def talk_acknowledgment(request, talk_id, confirm, participant_id=None): + talk = get_object_or_404(Talk, token=talk_id, site=request.conference.site) + if participant_id: + speaker = get_object_or_404(Participant, token=participant_id, site=request.conference.site) + if confirm: + return redirect(reverse('proposal-talk-confirm', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk))) + else: + return redirect(reverse('proposal-talk-desist', kwargs=dict(speaker_token=speaker.token, talk_id=talk.pk))) + elif not is_staff(request, request.user): + raise PermissionDenied + if not talk.accepted or talk.confirmed == confirm: + raise PermissionDenied + # TODO: handle multiple speakers case + talk.confirmed = confirm + talk.save() + if confirm: + confirmation_message= _('The speaker confirmation have been noted.') + thread_note = _('The talk have been confirmed.') + else: + confirmation_message = _('The speaker unavailability have been noted.') + thread_note = _('The talk have been cancelled.') + Message.objects.create(thread=talk.conversation, author=request.user, content=thread_note) + messages.success(request, confirmation_message) + return redirect(reverse('talk-details', kwargs=dict(talk_id=talk_id))) @staff_required @@ -529,7 +654,7 @@ class ParticipantUpdate(StaffRequiredMixin, OnSiteMixin, UpdateView): @staff_required -def conference(request): +def conference_edit(request): form = ConferenceForm(request.POST or None, instance=request.conference) if request.method == 'POST' and form.is_valid(): diff --git a/locale/fr/LC_MESSAGES/django.mo b/locale/fr/LC_MESSAGES/django.mo index f0ac7bad1b12fa9cd84952a4cd33b01ed16ebfab..6e261cdf7e30ae5942d2eb2c7fc6d52ee3870781 100644 GIT binary patch delta 8688 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 diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 7fe0432..66bfd3d 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-11-03 19:20+0000\n" -"PO-Revision-Date: 2017-11-03 20:22+0100\n" +"POT-Creation-Date: 2017-11-04 14:28+0000\n" +"PO-Revision-Date: 2017-11-04 15:29+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: fr\n" @@ -22,7 +22,7 @@ msgstr "" msgid "Pending decision" msgstr "Décision en attente" -#: cfp/forms.py:18 cfp/forms.py:103 cfp/forms.py:196 +#: cfp/forms.py:18 cfp/forms.py:124 cfp/forms.py:231 msgid "Accepted" msgstr "Accepté" @@ -34,145 +34,146 @@ msgstr "Décliné" msgid "Waiting" msgstr "En attente" -#: cfp/forms.py:29 cfp/forms.py:109 cfp/forms.py:202 cfp/models.py:343 +#: cfp/forms.py:29 cfp/forms.py:130 cfp/forms.py:237 cfp/models.py:350 msgid "Confirmed" msgstr "Confirmé" -#: cfp/forms.py:30 cfp/models.py:345 +#: cfp/forms.py:30 cfp/models.py:352 msgid "Cancelled" msgstr "Annulé" -#: cfp/forms.py:41 cfp/models.py:438 +#: cfp/forms.py:62 cfp/models.py:445 msgid "Activity" msgstr "Activité" -#: cfp/forms.py:51 +#: cfp/forms.py:72 msgctxt "activity" msgid "None" msgstr "Aucune" -#: cfp/forms.py:77 +#: cfp/forms.py:98 #, python-format msgid "Default duration: %(duration)d min" msgstr "Durée par défaut : %(duration)d min" -#: cfp/forms.py:85 cfp/forms.py:97 cfp/forms.py:190 +#: cfp/forms.py:106 cfp/forms.py:118 cfp/forms.py:225 #: cfp/templates/cfp/staff/talk_details.html:15 msgid "Category" msgstr "Catégorie" -#: cfp/forms.py:86 cfp/templates/cfp/staff/talk_list.html:43 +#: cfp/forms.py:107 cfp/templates/cfp/staff/talk_list.html:43 msgid "Title" msgstr "Titre" -#: cfp/forms.py:87 cfp/models.py:137 cfp/models.py:433 +#: cfp/forms.py:108 cfp/models.py:144 cfp/models.py:440 +#: cfp/templates/cfp/proposal_talk_details.html:67 #: cfp/templates/cfp/staff/talk_details.html:64 msgid "Description" msgstr "Description" -#: cfp/forms.py:88 cfp/models.py:105 cfp/models.py:414 -#: cfp/templates/cfp/staff/participant_details.html:16 +#: cfp/forms.py:109 cfp/models.py:111 cfp/models.py:421 +#: cfp/templates/cfp/staff/participant_details.html:19 #: cfp/templates/cfp/staff/talk_details.html:78 #: cfp/templates/cfp/staff/volunteer_details.html:20 msgid "Notes" msgstr "Notes" -#: cfp/forms.py:91 +#: cfp/forms.py:112 msgid "Visible by speakers" msgstr "Visible par les orateurs" -#: cfp/forms.py:115 cfp/forms.py:208 cfp/models.py:300 +#: cfp/forms.py:136 cfp/forms.py:243 cfp/models.py:307 #: cfp/templates/cfp/staff/talk_details.html:21 #: cfp/templates/cfp/staff/talk_list.html:46 #: cfp/templates/cfp/staff/track_form.html:14 msgid "Track" msgstr "Session" -#: cfp/forms.py:121 +#: cfp/forms.py:142 msgid "Tag" msgstr "Étiquette" -#: cfp/forms.py:127 cfp/templates/cfp/staff/talk_details.html:83 +#: cfp/forms.py:148 cfp/templates/cfp/staff/talk_details.html:83 msgid "Vote" msgstr "Vote" -#: cfp/forms.py:128 +#: cfp/forms.py:149 msgid "Filter talks you already / not yet voted for" msgstr "" "Filtrer les propositions pour lesquelles vous avez déjà voté / pas encore " "voté" -#: cfp/forms.py:131 cfp/templates/cfp/staff/room_form.html:14 +#: cfp/forms.py:152 cfp/templates/cfp/staff/room_form.html:14 #: cfp/templates/cfp/staff/talk_details.html:38 msgid "Room" msgstr "Salle" -#: cfp/forms.py:132 +#: cfp/forms.py:153 msgid "Filter talks already / not yet affected to a room" msgstr "Filtrer les exposés déjà / pas encore affectées à une salle" -#: cfp/forms.py:135 +#: cfp/forms.py:156 msgid "Scheduled" msgstr "Programmé" -#: cfp/forms.py:136 +#: cfp/forms.py:157 msgid "Filter talks already / not yet scheduled" msgstr "Filtrer les exposés déjà / pas encore planifiées" -#: cfp/forms.py:139 cfp/models.py:317 +#: cfp/forms.py:160 cfp/models.py:324 #: cfp/templates/cfp/staff/talk_details.html:54 msgid "Materials" msgstr "Supports" -#: cfp/forms.py:140 +#: cfp/forms.py:161 msgid "Filter talks with / without materials" msgstr "Filtrer les exposés avec / sans supports" -#: cfp/forms.py:143 cfp/templates/cfp/staff/talk_details.html:58 +#: cfp/forms.py:164 cfp/templates/cfp/staff/talk_details.html:58 msgid "Video" msgstr "Vidéo" -#: cfp/forms.py:144 +#: cfp/forms.py:165 msgid "Filter talks with / without video" msgstr "Filtrer les exposés avec / sans vidéo" -#: cfp/forms.py:153 cfp/forms.py:220 +#: cfp/forms.py:174 cfp/forms.py:255 msgid "Not assigned" msgstr "Pas encore assignée" -#: cfp/forms.py:159 +#: cfp/forms.py:180 msgid "Accept talk?" msgstr "Accepter la proposition ?" -#: cfp/forms.py:160 +#: cfp/forms.py:181 msgid "Assign to a track" msgstr "Assigner à une session" -#: cfp/forms.py:161 +#: cfp/forms.py:182 msgid "Add a tag" msgstr "Ajouter une étiquette" -#: cfp/forms.py:162 +#: cfp/forms.py:183 msgid "Put in a room" msgstr "Assigner à une salle" -#: cfp/forms.py:184 cfp/models.py:135 cfp/models.py:187 cfp/models.py:431 -#: cfp/templates/cfp/staff/participant_list.html:35 -#: cfp/templates/cfp/staff/volunteer_list.html:29 -msgid "Name" -msgstr "Nom" +#: cfp/forms.py:259 cfp/models.py:416 +#: cfp/templates/cfp/staff/volunteer_details.html:11 +#: cfp/templates/cfp/staff/volunteer_list.html:30 +msgid "Email" +msgstr "E-mail" -#: cfp/forms.py:239 +#: cfp/forms.py:278 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:259 +#: cfp/forms.py:298 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:264 +#: cfp/forms.py:303 msgid "A user with that email already exists." msgstr "Un utilisateur avec cet email existe déjà." @@ -209,26 +210,30 @@ msgid "Secure domain (HTTPS)" msgstr "Domaine sécurisé (HTTPS)" #: cfp/models.py:37 +msgid "Acceptances disclosure date" +msgstr "Date de divulgation des acceptations" + +#: cfp/models.py:38 msgid "Schedule publishing date" msgstr "Date de publication du programme" -#: cfp/models.py:38 +#: cfp/models.py:39 msgid "Schedule redirection URL" msgstr "URL de redirection du programme" -#: cfp/models.py:39 +#: cfp/models.py:40 msgid "If specified, schedule tab will redirect to this URL." msgstr "Si spécifiée, l’onglet programme redirigera vers cette URL." -#: cfp/models.py:40 +#: cfp/models.py:41 msgid "Volunteers enrollment opening date" msgstr "Date d’ouverture de l’appel à bénévole" -#: cfp/models.py:41 +#: cfp/models.py:42 msgid "Volunteers enrollment closing date" msgstr "Date de fermeture de l’appel à bénévole" -#: cfp/models.py:73 +#: cfp/models.py:79 #, python-brace-format msgid "" "The reply email should be a formatable string accepting a token argument (e." @@ -237,87 +242,91 @@ msgstr "" "L’adresse de réponse doit être une chaine de texte formatable avec un " "argument « token » (e.g. ponyconf+{token}@exemple.com)." -#: cfp/models.py:93 cfp/models.py:408 -msgid "Your Name" -msgstr "Votre Nom" +#: cfp/models.py:99 cfp/models.py:142 cfp/models.py:194 cfp/models.py:438 +#: cfp/templates/cfp/staff/participant_list.html:35 +#: cfp/templates/cfp/staff/volunteer_list.html:29 +msgid "Name" +msgstr "Nom" -#: cfp/models.py:95 cfp/templates/cfp/staff/participant_details.html:12 +#: cfp/models.py:101 cfp/templates/cfp/proposal_dashboard.html:33 +#: cfp/templates/cfp/staff/participant_details.html:15 msgid "Biography" msgstr "Biographie" -#: cfp/models.py:97 +#: cfp/models.py:103 msgid "Twitter" msgstr "Twitter" -#: cfp/models.py:98 +#: cfp/models.py:104 msgid "LinkedIn" msgstr "LinkedIn" -#: cfp/models.py:99 +#: cfp/models.py:105 msgid "Github" msgstr "Github" -#: cfp/models.py:100 +#: cfp/models.py:106 msgid "Website" msgstr "Site web" -#: cfp/models.py:101 +#: cfp/models.py:107 msgid "Facebook" msgstr "Facebook" -#: cfp/models.py:102 +#: cfp/models.py:108 msgid "Mastodon" msgstr "Mastodon" -#: cfp/models.py:103 cfp/models.py:411 +#: cfp/models.py:109 cfp/models.py:418 #: cfp/templates/cfp/staff/volunteer_details.html:14 msgid "Phone number" msgstr "Numéro de téléphone" -#: cfp/models.py:106 cfp/models.py:299 +#: cfp/models.py:112 cfp/models.py:306 msgid "This field is only visible by organizers." msgstr "Ce champs est uniquement visible par les organisateurs." -#: cfp/models.py:107 +#: cfp/models.py:113 cfp/templates/cfp/staff/participant_details.html:25 msgid "Invited speaker" msgstr "Orateur invité" -#: cfp/models.py:189 +#: cfp/models.py:196 msgid "Color" msgstr "Couleur" -#: cfp/models.py:221 +#: cfp/models.py:228 msgid "Default duration (min)" msgstr "Durée par défaut (min)" -#: cfp/models.py:222 +#: cfp/models.py:229 msgid "Color on program" msgstr "Couleur sur le programme" -#: cfp/models.py:223 +#: cfp/models.py:230 msgid "Label on program" msgstr "Label dans le xml du programme" -#: cfp/models.py:294 cfp/templates/cfp/staff/base.html:11 +#: cfp/models.py:301 cfp/templates/cfp/proposal_talk_details.html:51 +#: cfp/templates/cfp/staff/base.html:11 #: cfp/templates/cfp/staff/participant_list.html:8 #: cfp/templates/cfp/staff/talk_details.html:68 #: cfp/templates/cfp/staff/talk_list.html:45 msgid "Speakers" msgstr "Orateurs" -#: cfp/models.py:295 +#: cfp/models.py:302 msgid "Talk Title" msgstr "Titre de la proposition" -#: cfp/models.py:298 +#: cfp/models.py:305 msgid "Description of your talk" msgstr "Description de votre proposition" -#: cfp/models.py:302 +#: cfp/models.py:309 cfp/templates/cfp/proposal_talk_details.html:77 msgid "Message to organizers" msgstr "Message aux organisateurs" -#: cfp/models.py:303 +#: cfp/models.py:310 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 " @@ -327,68 +336,67 @@ msgstr "" "votre proposition, comme une vidéo, des slides, n'hésitez pas à les ajouter " "ici." -#: cfp/models.py:306 +#: cfp/models.py:313 msgid "Talk Category" msgstr "Catégorie de proposition" -#: cfp/models.py:307 +#: cfp/models.py:314 msgid "I'm ok to be recorded on video" msgstr "J’accepte d’être enregistré en vidéo" -#: cfp/models.py:309 +#: cfp/models.py:316 msgid "Video licence" msgstr "Licence vidéo" -#: cfp/models.py:310 +#: cfp/models.py:317 msgid "I need sound" msgstr "J’ai besoin de son" -#: cfp/models.py:313 +#: cfp/models.py:320 msgid "Beginning date and time" msgstr "Date et heure de début" -#: cfp/models.py:314 +#: cfp/models.py:321 msgid "Duration (min)" msgstr "Durée (min)" -#: cfp/models.py:318 +#: cfp/models.py:325 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/models.py:347 +#: cfp/models.py:354 msgid "Waiting confirmation" msgstr "En attente de confirmation" -#: cfp/models.py:349 +#: cfp/models.py:356 msgid "Refused" msgstr "Refusé" -#: cfp/models.py:351 +#: cfp/models.py:358 #, python-format msgid "Pending decision, score: %(score).1f" msgstr "En cours, score : %(score).1f" -#: cfp/models.py:409 cfp/templates/cfp/staff/volunteer_details.html:11 -#: cfp/templates/cfp/staff/volunteer_list.html:30 -msgid "Email" -msgstr "E-mail" +#: cfp/models.py:415 +msgid "Your Name" +msgstr "Votre Nom" -#: cfp/models.py:412 +#: cfp/models.py:419 msgid "SMS prefered" msgstr "SMS préférés" -#: cfp/models.py:415 +#: cfp/models.py:422 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:434 cfp/templates/cfp/staff/volunteer_details.html:8 +#: cfp/models.py:441 cfp/templates/cfp/staff/volunteer_details.html:8 msgid "Volunteer" msgstr "Bénévole" -#: cfp/models.py:439 cfp/templates/cfp/staff/volunteer_details.html:25 +#: cfp/models.py:446 cfp/templates/cfp/staff/volunteer_details.html:25 #: cfp/templates/cfp/staff/volunteer_list.html:32 msgid "Activities" msgstr "Activités" @@ -422,14 +430,18 @@ msgstr "Veuillez sélectionner une catégorie." msgid "Add a new user" msgstr "Ajouter un nouvel utilisateur" -#: cfp/templates/cfp/admin/conference.html:14 cfp/templates/cfp/propose.html:22 -#: cfp/templates/cfp/speaker.html:21 +#: cfp/templates/cfp/admin/conference.html:14 +#: cfp/templates/cfp/proposal.html:28 +#: cfp/templates/cfp/proposal_mail_token.html:25 +#: cfp/templates/cfp/proposal_speaker_form.html:38 +#: cfp/templates/cfp/proposal_talk_form.html:28 +#: cfp/templates/cfp/propose.html:22 cfp/templates/cfp/speaker.html:21 #: cfp/templates/cfp/staff/create_user.html:13 msgid "Save" msgstr "Envoyer" -#: cfp/templates/cfp/closed.html:9 cfp/templates/cfp/propose.html:11 -#: cfp/templates/cfp/speaker.html:11 +#: cfp/templates/cfp/closed.html:9 cfp/templates/cfp/proposal.html:12 +#: cfp/templates/cfp/propose.html:11 cfp/templates/cfp/speaker.html:11 msgid "Participate" msgstr "Participer" @@ -465,13 +477,207 @@ msgstr "Éditer votre profil :" msgid "An email has been sent to you with those URLs" msgstr "Un mail vous a été envoyé avec toutes les URLs" +#: cfp/templates/cfp/proposal.html:21 +#, python-format +msgid "" +"If you already have submitted a talk and you want to edit it or submit " +"another one, please click here." +msgstr "" +"Si vous avez déjà soumis une proposition et que vous souhaitez l’éditer ou " +"en soumettre une autre, cliquez ici." + +#: cfp/templates/cfp/proposal_dashboard.html:11 +#, python-format +msgid "Welcome %(name)s!" +msgstr "Bienvenue %(name)s !" + +#: cfp/templates/cfp/proposal_dashboard.html:13 +#: cfp/templates/cfp/proposal_speaker_form.html:21 +msgid "Edit your profile" +msgstr "Éditer votre profil" + +#: cfp/templates/cfp/proposal_dashboard.html:18 +msgid "Your informations" +msgstr "Vos informations" + +#: cfp/templates/cfp/proposal_dashboard.html:22 +#: cfp/templates/cfp/staff/participant_details.html:26 +msgid "E-mail:" +msgstr "E-mail :" + +#: cfp/templates/cfp/proposal_dashboard.html:23 +#: cfp/templates/cfp/staff/participant_details.html:27 +msgid "Twitter:" +msgstr "Twitter :" + +#: cfp/templates/cfp/proposal_dashboard.html:24 +#: cfp/templates/cfp/staff/participant_details.html:28 +msgid "LinkedIn:" +msgstr "LinkedIn :" + +#: cfp/templates/cfp/proposal_dashboard.html:25 +#: cfp/templates/cfp/staff/participant_details.html:29 +msgid "Github:" +msgstr "Github :" + +#: cfp/templates/cfp/proposal_dashboard.html:26 +#: cfp/templates/cfp/staff/participant_details.html:30 +msgid "Website:" +msgstr "Website :" + +#: cfp/templates/cfp/proposal_dashboard.html:27 +#: cfp/templates/cfp/staff/participant_details.html:31 +msgid "Facebook:" +msgstr "Facebook :" + +#: cfp/templates/cfp/proposal_dashboard.html:28 +#: cfp/templates/cfp/staff/participant_details.html:32 +msgid "Mastodon:" +msgstr "Mastodon :" + +#: cfp/templates/cfp/proposal_dashboard.html:29 +#: cfp/templates/cfp/staff/participant_details.html:33 +msgid "Phone number:" +msgstr "Numéro de téléphone :" + +#: cfp/templates/cfp/proposal_dashboard.html:39 +msgid "No biography." +msgstr "Pas de bibliographie." + +#: cfp/templates/cfp/proposal_dashboard.html:43 +msgid "Your proposals" +msgstr "Vos propositions" + +#: cfp/templates/cfp/proposal_dashboard.html:53 +msgid "with" +msgstr "avec" + +#: cfp/templates/cfp/proposal_dashboard.html:55 +#: cfp/templates/cfp/staff/participant_details.html:47 +#: cfp/templates/cfp/staff/room_details.html:21 +#: cfp/templates/cfp/staff/room_details.html:39 +#: cfp/templates/cfp/staff/talk_list.html:62 +msgid "and" +msgstr "et" + +#: cfp/templates/cfp/proposal_dashboard.html:59 +msgid "you must confirm you participation" +msgstr "vous devez confirmer votre participation" + +#: cfp/templates/cfp/proposal_dashboard.html:61 +msgid "accepted" +msgstr "accepté" + +#: cfp/templates/cfp/proposal_dashboard.html:63 +msgid "cancelled" +msgstr "annulé" + +#: cfp/templates/cfp/proposal_dashboard.html:71 +msgid "No proposals." +msgstr "Aucune proposition." + +#: cfp/templates/cfp/proposal_dashboard.html:75 +#: cfp/templates/cfp/proposal_talk_details.html:17 +msgid "New proposal" +msgstr "Nouvelle proposition" + +#: cfp/templates/cfp/proposal_mail_token.html:12 +msgid "Access an existing profile" +msgstr "Accéder à un profil existant" + +#: cfp/templates/cfp/proposal_mail_token.html:21 +msgid "" +"To receive a email with a link to access your profile, please enter your " +"email below." +msgstr "" +"Pour recevoir un e-mail avec un lien pour accéder à votre profil, veuillez " +"entrer votre adresse mail ci-dessous." + +#: cfp/templates/cfp/proposal_speaker_form.html:13 +#: cfp/templates/cfp/staff/participant_form.html:8 +msgid "Edit a speaker" +msgstr "Éditer un orateur" + +#: cfp/templates/cfp/proposal_speaker_form.html:15 +#: cfp/templates/cfp/proposal_talk_details.html:63 +msgid "Add a co-speaker" +msgstr "Ajouter un co-orateur" + +#: cfp/templates/cfp/proposal_speaker_form.html:17 +msgid "Go back to the talk" +msgstr "Retourner à l’exposé" + +#: cfp/templates/cfp/proposal_talk_details.html:14 +msgid "My profile" +msgstr "Mon profil" + +#: cfp/templates/cfp/proposal_talk_details.html:20 +msgid "Edit this proposal" +msgstr "Éditer cette proposition" + +#: cfp/templates/cfp/proposal_talk_details.html:26 +#: cfp/templates/cfp/staff/talk_details.html:18 +#: cfp/templates/cfp/staff/talk_list.html:48 +msgid "Status" +msgstr "Statut" + +#: cfp/templates/cfp/proposal_talk_details.html:29 +msgid "Reviewing in progress, we will keep you informed by mail." +msgstr "Relecture en cours, nous vous tenons informé par e-mail." + +#: cfp/templates/cfp/proposal_talk_details.html:31 +msgid "Accepted!" +msgstr "Accepté !" + +#: cfp/templates/cfp/proposal_talk_details.html:34 +msgid "Please confirm your participation:" +msgstr "Merci de confirmer votre participation :" + +#: cfp/templates/cfp/proposal_talk_details.html:35 +msgid "I will be there!" +msgstr "Je serai là !" + +#: cfp/templates/cfp/proposal_talk_details.html:36 +msgid "Sorry, couldn't make it :-(" +msgstr "Désolé, j’ai un contre temps :-(" + +#: cfp/templates/cfp/proposal_talk_details.html:40 +msgid "Sorry, I have to cancel." +msgstr "Désolé, je dois annuler." + +#: cfp/templates/cfp/proposal_talk_details.html:44 +msgid "Good news, I finally could be there!" +msgstr "Bonne nouvelle, je peux finalement être présent !" + +#: cfp/templates/cfp/proposal_talk_details.html:48 +msgid "Sorry, refused :-(" +msgstr "Désolé, refusé :-(" + +#: cfp/templates/cfp/proposal_talk_details.html:58 +msgid "you!" +msgstr "vous !" + +#: cfp/templates/cfp/proposal_talk_details.html:73 +#: cfp/templates/cfp/proposal_talk_details.html:83 +#: cfp/templates/cfp/staff/talk_details.html:66 +msgid "No description provided." +msgstr "Aucune description fournie." + +#: cfp/templates/cfp/proposal_talk_form.html:14 +msgid "Submit a proposal" +msgstr "Soumettre une proposition" + +#: cfp/templates/cfp/proposal_talk_form.html:16 +msgid "Go back to proposals list" +msgstr "Retourner à la liste des propositions" + #: cfp/templates/cfp/schedule.html:9 cfp/templates/cfp/staff/base.html:15 #: cfp/templates/cfp/staff/schedule.html:9 ponyconf/templates/base.html:27 msgid "Schedule" msgstr "Programme" #: cfp/templates/cfp/staff/base.html:10 -#: cfp/templates/cfp/staff/participant_details.html:33 +#: cfp/templates/cfp/staff/participant_details.html:37 #: cfp/templates/cfp/staff/talk_list.html:8 msgid "Talks" msgstr "Exposés" @@ -498,75 +704,40 @@ msgid "Cancel" msgstr "Annuler" #: cfp/templates/cfp/staff/participant_details.html:10 +msgid "VIP" +msgstr "VIP" + +#: cfp/templates/cfp/staff/participant_details.html:13 #: cfp/templates/cfp/staff/talk_details.html:10 msgid "Edit" msgstr "Éditer" -#: cfp/templates/cfp/staff/participant_details.html:20 +#: cfp/templates/cfp/staff/participant_details.html:23 msgid "Informations" msgstr "Informations" -#: cfp/templates/cfp/staff/participant_details.html:22 -msgid "E-mail:" -msgstr "E-mail :" - -#: cfp/templates/cfp/staff/participant_details.html:23 -msgid "Twitter:" -msgstr "Twitter :" - -#: cfp/templates/cfp/staff/participant_details.html:24 -msgid "LinkedIn:" -msgstr "LinkedIn :" - -#: cfp/templates/cfp/staff/participant_details.html:25 -msgid "Github:" -msgstr "Github :" - -#: cfp/templates/cfp/staff/participant_details.html:26 -msgid "Website:" -msgstr "Website :" - -#: cfp/templates/cfp/staff/participant_details.html:27 -msgid "Facebook:" -msgstr "Facebook :" - -#: cfp/templates/cfp/staff/participant_details.html:28 -msgid "Mastodon:" -msgstr "Mastodon :" - -#: cfp/templates/cfp/staff/participant_details.html:29 -msgid "Phone number:" -msgstr "Numéro de téléphone :" - -#: cfp/templates/cfp/staff/participant_details.html:30 +#: cfp/templates/cfp/staff/participant_details.html:34 msgid "Language:" msgstr "Langue :" -#: cfp/templates/cfp/staff/participant_details.html:40 +#: cfp/templates/cfp/staff/participant_details.html:44 msgid "by" 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:62 -msgid "and" -msgstr "et" - -#: cfp/templates/cfp/staff/participant_details.html:46 +#: cfp/templates/cfp/staff/participant_details.html:50 msgid "in" msgstr "dans la session" -#: cfp/templates/cfp/staff/participant_details.html:52 +#: cfp/templates/cfp/staff/participant_details.html:56 msgid "No talks" msgstr "Aucun exposé" -#: cfp/templates/cfp/staff/participant_details.html:55 +#: cfp/templates/cfp/staff/participant_details.html:59 #: cfp/templates/cfp/staff/talk_details.html:120 msgid "Messaging" msgstr "Messagerie" -#: cfp/templates/cfp/staff/participant_details.html:59 +#: cfp/templates/cfp/staff/participant_details.html:63 msgid "" "Send a message – this message will be received by this participant and " "all the staff team" @@ -574,10 +745,6 @@ msgstr "" "Envoyer un message – ce message sera reçu par le participant et l’équipe " "d’organisation" -#: cfp/templates/cfp/staff/participant_form.html:8 -msgid "Edit a speaker" -msgstr "Éditer un orateur" - #: cfp/templates/cfp/staff/participant_list.html:10 #: cfp/templates/cfp/staff/talk_list.html:10 #: cfp/templates/cfp/staff/volunteer_list.html:11 @@ -614,21 +781,21 @@ msgstr "Contacter :" msgid "link" msgstr "lien" -#: cfp/templates/cfp/staff/participant_list.html:53 +#: cfp/templates/cfp/staff/participant_list.html:56 #, python-format msgid "accepted: %(accepted)s" msgid_plural "accepted: %(accepted)s" msgstr[0] "accepté : %(accepted)s" msgstr[1] "acceptés : %(accepted)s" -#: cfp/templates/cfp/staff/participant_list.html:55 +#: cfp/templates/cfp/staff/participant_list.html:58 #, python-format msgid "pending: %(pending)s" msgid_plural "pending: %(pending)s" msgstr[0] "en attente : %(pending)s" msgstr[1] "en attente : %(pending)s" -#: cfp/templates/cfp/staff/participant_list.html:57 +#: cfp/templates/cfp/staff/participant_list.html:60 #, python-format msgid "refused: %(refused)s" msgid_plural "refused: %(refused)s" @@ -715,11 +882,6 @@ msgstr "Décliner la proposition" msgid "Information" msgstr "Informations" -#: cfp/templates/cfp/staff/talk_details.html:18 -#: cfp/templates/cfp/staff/talk_list.html:48 -msgid "Status" -msgstr "Statut" - #: cfp/templates/cfp/staff/talk_details.html:25 msgctxt "session" msgid "not defined" @@ -748,10 +910,6 @@ msgstr "non défini" msgid "download" msgstr "télécharger" -#: cfp/templates/cfp/staff/talk_details.html:66 -msgid "No description provided." -msgstr "Aucune description fournie." - #: cfp/templates/cfp/staff/talk_details.html:75 msgid "No speakers." msgstr "Aucun orateur." @@ -913,7 +1071,7 @@ msgstr "Merci pour votre participation !" msgid "Okay, no problem!" msgstr "Ok, pas de soucis !" -#: cfp/views.py:179 +#: cfp/views.py:161 msgid "" "Hi {},\n" "\n" @@ -924,9 +1082,9 @@ msgid "" "Description: {}\n" "\n" "You can at anytime:\n" -"- edit your talk: {}\n" +"- review and edit your profile: {}\n" +"- review and edit your talk: {}\n" "- add a new co-speaker: {}\n" -"- edit your profile: {}\n" "\n" "If you have any question, your can answer to this email.\n" "\n" @@ -944,78 +1102,133 @@ msgstr "" "Description: {}\n" "\n" "Vous pouvez à tout moment:\n" -"- éditer votre proposition: {}\n" -"- ajouter un co-speaker: {}\n" -"- éditer votre profil: {}\n" +"- consulter et modifier votre profil : {}\n" +"- consulter et modifier votre proposition : {}\n" +"- ajouter un autre intervenant : {}\n" "\n" "Si vous avez une question, vous pouvez répondre à ce mail.\n" "\n" "{}\n" "\n" -#: cfp/views.py:257 +#: cfp/views.py:191 cfp/views.py:275 +msgid "You proposition have been successfully submitted!" +msgstr "Votre proposition a été transmise avec succès !" + +#: cfp/views.py:205 +msgid "Sorry, we do not know this email." +msgstr "Désolé, nous ne connaissons pas cette e-mail." + +#: cfp/views.py:210 +msgid "" +"Hi {},\n" +"\n" +"Someone, probably you, ask to access your profile.\n" +"You can edit your talks or add new ones following this url:\n" +"\n" +" {}\n" +"\n" +"If you have any question, your can answer to this email.\n" +"\n" +"Sincerely,\n" +"\n" +"{}\n" +"\n" +msgstr "" +"Bonjour {},\n" +"\n" +"Quelqu’un, sans doute vous, a demandé à accéder à votre profil.\n" +"Vous pouvez modifier vos propositions ou en soumettre de nouvelles à l’url " +"suivante :\n" +"\n" +" {}\n" +"\n" +"Si vous avez une question, vous pouvez répondre à ce mail.\n" +"\n" +"Sincèrement,\n" +"\n" +"{}\n" +"\n" + +#: cfp/views.py:230 +msgid "A email have been sent with a link to access to your profil." +msgstr "Un e-mail vous a été envoyé avec un lien pour accéder à votre profil." + +#: cfp/views.py:271 +msgid "Changes saved." +msgstr "Modifications sauvegardées." + +#: cfp/views.py:292 +msgid "You already confirmed your participation to this talk." +msgstr "Vous avez déjà confirmé votre participation à cet exposé." + +#: cfp/views.py:294 +msgid "You already cancelled your participation to this talk." +msgstr "Vous avez déjà annulé votre participation à cet exposé." + +#: cfp/views.py:299 msgid "Your participation has been taken into account, thank you!" msgstr "Votre participation a été prise en compte, merci !" -#: cfp/views.py:259 +#: cfp/views.py:300 #, python-format msgid "Speaker %(speaker)s confirmed his/her participation." msgstr "L’intervenant %(speaker)s a confirmé sa participation." -#: cfp/views.py:261 -msgid "The talk have been confirmed." -msgstr "L’exposé a été confirmé." - -#: cfp/views.py:263 +#: cfp/views.py:302 msgid "We have noted your unavailability." msgstr "Nous avons enregistré votre indisponibilité." -#: cfp/views.py:265 +#: cfp/views.py:303 #, python-format msgid "Speaker %(speaker)s CANCELLED his/her participation." msgstr "L’intervenant %(speaker)s a ANNULÉ sa participation." -#: cfp/views.py:267 +#: cfp/views.py:398 +msgid "The speaker confirmation have been noted." +msgstr "La confirmation de l’orateur a été notée." + +#: cfp/views.py:399 +msgid "The talk have been confirmed." +msgstr "L’exposé a été confirmé." + +#: cfp/views.py:401 +msgid "The speaker unavailability have been noted." +msgstr "L’indisponibilité de l’intervenant a été notée." + +#: cfp/views.py:402 msgid "The talk have been cancelled." msgstr "L’exposé a été annulé." -#: cfp/views.py:274 -msgid "You already confirmed your participation to this talk." -msgstr "Vous avez déjà confirmé votre participation à cet exposé." - -#: cfp/views.py:276 -msgid "You already cancelled your participation to this talk." -msgstr "Vous avez déjà annulé votre participation à cet exposé." - -#: cfp/views.py:352 cfp/views.py:454 +#: cfp/views.py:477 cfp/views.py:579 msgid "The talk has been accepted." msgstr "L’exposé a été accepté." -#: cfp/views.py:354 cfp/views.py:456 +#: cfp/views.py:479 cfp/views.py:581 msgid "The talk has been declined." msgstr "L’exposé a été décliné." -#: cfp/views.py:423 cfp/views.py:516 +#: cfp/views.py:548 cfp/views.py:641 msgid "Message sent!" msgstr "Message envoyé !" -#: cfp/views.py:437 +#: cfp/views.py:562 msgid "Vote successfully created" msgstr "A voté !" -#: cfp/views.py:437 +#: cfp/views.py:562 msgid "Vote successfully updated" msgstr "Vote mis à jour" -#: cfp/views.py:458 +#: cfp/views.py:583 msgid "Decision taken in account" msgstr "Décision enregistrée" -#: cfp/views.py:544 +#: cfp/views.py:669 msgid "[{}] You have been added to the staff team" msgstr "[{}] Vous avez été ajouté aux membres du staff" -#: cfp/views.py:545 +#: cfp/views.py:670 msgid "" "Hi {},\n" "\n" @@ -1039,15 +1252,15 @@ msgstr "" "{}\n" "\n" -#: cfp/views.py:566 +#: cfp/views.py:691 msgid "Modifications successfully saved." msgstr "Modification enregistrée avec succès." -#: cfp/views.py:643 +#: cfp/views.py:768 msgid "User created successfully." msgstr "Utilisateur créé avec succès." -#: cfp/views.py:664 +#: cfp/views.py:789 #, python-format msgid "Format '%s' not available" msgstr "Format '%s' non disponible" @@ -1129,6 +1342,15 @@ msgstr "Changement de mot de passe" msgid "Email address" msgstr "Adresse e-mail" +#~ msgid "My proposals" +#~ msgstr "Mes propositions" + +#~ msgid "Propose a talk" +#~ msgstr "Proposer un exposé" + +#~ msgid "My talks" +#~ msgstr "Mes exposés" + #~ msgid "Moderation" #~ msgstr "Modération" @@ -1249,12 +1471,6 @@ msgstr "Adresse e-mail" #~ msgid "Subscribe to the conversation" #~ msgstr "S’abonner à la discussion" -#~ msgid "Profile" -#~ msgstr "Profil" - -#~ msgid "Change avatar" -#~ msgstr "Changer d’avatar" - #~ msgid "Your current avatar: " #~ msgstr "Votre avatar actuel : " @@ -1368,9 +1584,6 @@ msgstr "Adresse e-mail" #~ msgid "Home page" #~ msgstr "Page d’accueil" -#~ msgid "My talks:" -#~ msgstr "Mes exposés :" - #~ msgid "Proposed talks for others speakers:" #~ msgstr "Exposés proposés pour un tier :" @@ -1383,9 +1596,6 @@ msgstr "Adresse e-mail" #~ msgid "until %(closing_date)s" #~ msgstr "jusqu’au %(closing_date)s" -#~ msgid "Propose a talk" -#~ msgstr "Proposer un exposé" - #~ msgid "Sorry, the Call for Participation is closed." #~ msgstr "Désolé, l’appel à participation est fermé." diff --git a/ponyconf/templates/base.html b/ponyconf/templates/base.html index 49150ff..67d421d 100644 --- a/ponyconf/templates/base.html +++ b/ponyconf/templates/base.html @@ -21,7 +21,7 @@  {% trans "Home" %} {% endif %} -  {% trans "Call for participation" %} +  {% trans "Call for participation" %} {% if conference.schedule_available %}  {% trans "Schedule" %}