Compare commits

...

19 Commits
main ... main

Author SHA1 Message Date
9b629cb80b feat: Language added for talks. Closing #28 2024-02-26 08:56:14 +00:00
9a06cd81a1 fix: Hide actual votes when voting in order not to reveal too much; visibility can be toggled. Closes #24 2024-02-26 08:55:03 +00:00
16534bf41f Separation of status and score in order to be able to sort on score. Closes #22 2024-02-26 08:54:39 +00:00
11a5738731 fix: Added talk's score in CSV export. Closes #23 2024-02-26 08:54:10 +00:00
035a1a16c3
Bump to Django 4. 2023-03-30 14:55:40 +02:00
016a762e83 fix: Added participant name on page title. Closes #12 2023-03-19 17:36:20 +00:00
4b34081457 fix: Separate accepted talks from canceled ones. Help #13 2023-03-19 17:32:19 +00:00
1085cd497c fix: Add talk status on speaker page. Closes #14 2023-03-19 14:18:21 +00:00
c88cc3e2a2 Give hint in error message. 2023-03-16 09:47:14 +00:00
d7bb1d77f5 Validate phone numbers 2023-03-16 09:47:14 +00:00
787bc67794 Merge pull request 'Correction d'erreurs de langue' (#6) from fleborne/PonyConf:fix-spelling into main
Reviewed-on: AFPy/PonyConf#6
2023-01-30 21:47:16 +00:00
François Leborne
5d1af61b0a Update .mo file 2023-01-30 14:54:45 +01:00
François Leborne
713c777e81 Make messages: fix messages 2023-01-30 14:50:05 +01:00
François Leborne
975e5d4784 Make messages: update line numbers 2023-01-30 14:46:20 +01:00
3fa8a61656
fix: 'NoneType' object has no attribute 'get_full_name'. Closes #10 2023-01-29 22:16:58 +01:00
François Leborne
d80bafc0c6 Fix grammar 2023-01-22 18:53:19 +01:00
François Leborne
c33260c31f Fix grammar 2023-01-22 18:31:49 +01:00
François Leborne
b4bf7d182a Remove 's' at the end of 'information' 2023-01-22 18:11:14 +01:00
François Leborne
2b05eb3c69 Fix title spelling 2023-01-22 17:43:00 +01:00
29 changed files with 354 additions and 183 deletions

View File

@ -1,5 +1,5 @@
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.forms.models import modelform_factory from django.forms.models import modelform_factory

View File

@ -1,7 +1,7 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.urls import reverse from django.urls import reverse
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class Profile(models.Model): class Profile(models.Model):

View File

@ -4,8 +4,8 @@ from django.contrib.auth.signals import user_logged_in, user_logged_out
#from django.contrib.sites.shortcuts import get_current_site #from django.contrib.sites.shortcuts import get_current_site
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
#from django.utils.translation import ugettext_noop #from django.utils.translation import gettext_noop
from ponyconf.decorators import disable_for_loaddata from ponyconf.decorators import disable_for_loaddata

View File

@ -1,6 +1,6 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import LoginView from django.contrib.auth.views import LoginView
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.contrib import messages from django.contrib import messages

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from django.utils.html import escape from django.utils.html import escape
from pprint import pformat from pprint import pformat

View File

@ -3,7 +3,7 @@ from django.forms.models import modelform_factory
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.forms import UsernameField from django.contrib.auth.forms import UsernameField
from django.utils.translation import ugettext_lazy as _, pgettext_lazy from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
@ -86,7 +86,7 @@ class TalkForm(forms.ModelForm):
class Meta: class Meta:
model = Talk model = Talk
fields = ('category', 'title', 'description', 'notes', 'materials',) fields = ('category', 'title', 'language', 'description', 'notes', 'materials',)
class TalkStaffForm(forms.ModelForm): class TalkStaffForm(forms.ModelForm):

View File

@ -0,0 +1,19 @@
# Generated by Django 4.1.7 on 2024-02-22 12:43
import cfp.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cfp', '0027_auto_20200809_1530'),
]
operations = [
migrations.AlterField(
model_name='volunteer',
name='phone_number',
field=models.CharField(blank=True, default='', max_length=64, validators=[cfp.models.validate_phone_number], verbose_name='Phone number'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.7 on 2024-02-22 12:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cfp', '0028_alter_volunteer_phone_number'),
]
operations = [
migrations.AddField(
model_name='talk',
name='language',
field=models.CharField(blank=True, max_length=10),
),
]

View File

@ -1,5 +1,6 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.conf import settings
from django.urls import reverse from django.urls import reverse
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -7,13 +8,14 @@ from django.db import models
from django.db.models import Q, Count, Avg, Case, When from django.db.models import Q, Count, Avg, Case, When
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import gettext, gettext_lazy as _
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.html import escape, format_html from django.utils.html import escape, format_html
from autoslug import AutoSlugField from autoslug import AutoSlugField
from colorful.fields import RGBColorField from colorful.fields import RGBColorField
from functools import partial from functools import partial
import phonenumbers
import uuid import uuid
from datetime import timedelta from datetime import timedelta
@ -98,9 +100,10 @@ class ParticipantManager(models.Manager):
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
qs = qs.annotate( qs = qs.annotate(
accepted_talk_count=Count(Case(When(talk__accepted=True, then='talk__pk'), output_field=models.IntegerField()), distinct=True), accepted_talk_count=Count(Case(When(Q(talk__accepted=True) & (Q(talk__confirmed=True) | Q(talk__confirmed__isnull=True)), then='talk__pk'), output_field=models.IntegerField()), distinct=True),
pending_talk_count=Count(Case(When(talk__accepted=None, then='talk__pk'), output_field=models.IntegerField()), distinct=True), pending_talk_count=Count(Case(When(talk__accepted=None, then='talk__pk'), output_field=models.IntegerField()), distinct=True),
refused_talk_count=Count(Case(When(talk__accepted=False, then='talk__pk'), output_field=models.IntegerField()), distinct=True), refused_talk_count=Count(Case(When(talk__accepted=False, then='talk__pk'), output_field=models.IntegerField()), distinct=True),
canceled_talk_count=Count(Case(When(talk__confirmed=False, then='talk__pk'), output_field=models.IntegerField()), distinct=True),
) )
return qs return qs
@ -152,7 +155,10 @@ class Participant(PonyConfModel):
@property @property
def accepted_talk_set(self): def accepted_talk_set(self):
return self.talk_set.filter(accepted=True) return self.talk_set.filter(accepted=True).exclude(confirmed=False)
@property
def canceled_talk_set(self):
return self.talk_set.filter(confirmed=False)
@property @property
def pending_talk_set(self): def pending_talk_set(self):
return self.talk_set.filter(accepted=None) return self.talk_set.filter(accepted=None)
@ -275,7 +281,7 @@ class TalkCategory(models.Model): # type of talk (conf 30min, 1h, stand, …)
verbose_name_plural = "categories" verbose_name_plural = "categories"
def __str__(self): def __str__(self):
return ugettext(self.name) return gettext(self.name)
def get_absolute_url(self): def get_absolute_url(self):
return reverse('category-list') return reverse('category-list')
@ -311,7 +317,7 @@ class TalkCategory(models.Model): # type of talk (conf 30min, 1h, stand, …)
class TalkManager(models.Manager): class TalkManager(models.Manager):
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
qs = qs.annotate(score=Coalesce(Avg('vote__vote'), 0)) qs = qs.annotate(score=Coalesce(Avg('vote__vote'), 0.0))
return qs return qs
@ -358,6 +364,7 @@ class Talk(PonyConfModel):
video = models.URLField(max_length=1000, blank=True, default='', verbose_name='Video URL') video = models.URLField(max_length=1000, blank=True, default='', verbose_name='Video URL')
token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
conversation = models.OneToOneField(MessageThread, on_delete=models.PROTECT) conversation = models.OneToOneField(MessageThread, on_delete=models.PROTECT)
language = models.CharField(max_length=10, blank=True)
objects = TalkManager() objects = TalkManager()
@ -387,7 +394,7 @@ class Talk(PonyConfModel):
elif self.accepted is False: elif self.accepted is False:
return _('Refused') return _('Refused')
else: else:
return _('Pending decision, score: %(score).1f') % {'score': self.score} return _('Pending decision')
def get_status_color(self): def get_status_color(self):
if self.accepted is True: if self.accepted is True:
@ -423,6 +430,7 @@ class Talk(PonyConfModel):
1 if self.plenary else 0, 1 if self.plenary else 0,
self.materials, self.materials,
self.video, self.video,
self.score,
] ]
@property @property
@ -483,12 +491,22 @@ class Activity(models.Model):
return self.name return self.name
def validate_phone_number(phone_number: str):
try:
number = phonenumbers.parse(phone_number, region=settings.DEFAULT_PHONE_REGION)
except phonenumbers.phonenumberutil.NumberParseException as err:
raise ValidationError(str(err))
else:
if not phonenumbers.is_valid_number(number):
raise ValidationError(_("Invalid phone number, try using the country code (like +33 for France)"))
class Volunteer(PonyConfModel): class Volunteer(PonyConfModel):
site = models.ForeignKey(Site, on_delete=models.CASCADE) 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=_('Your Name'))
email = models.EmailField(verbose_name=_('Email')) email = models.EmailField(verbose_name=_('Email'))
token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
phone_number = models.CharField(max_length=64, blank=True, default='', verbose_name=_('Phone number')) phone_number = models.CharField(max_length=64, blank=True, default='', verbose_name=_('Phone number'), validators=[validate_phone_number])
sms_prefered = models.BooleanField(default=False, verbose_name=_('SMS prefered')) sms_prefered = models.BooleanField(default=False, verbose_name=_('SMS prefered'))
language = models.CharField(max_length=10, blank=True) language = models.CharField(max_length=10, blank=True)
notes = models.TextField(default='', blank=True, verbose_name=_('Notes'), notes = models.TextField(default='', blank=True, verbose_name=_('Notes'),

View File

@ -2,7 +2,7 @@ from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.urls import reverse from django.urls import reverse
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model

View File

@ -15,7 +15,7 @@
</h1> </h1>
</div> </div>
<h3>{% trans "Your informations" %}</h3> <h3>{% trans "Your information" %}</h3>
<p> <p>
<ul> <ul>

View File

@ -1,6 +1,8 @@
{% extends 'cfp/staff/base.html' %} {% extends 'cfp/staff/base.html' %}
{% load i18n %} {% load i18n %}
{% block title %}{{ participant.name }} - {{ conference.name }}{% endblock %}
{% block speakerstab %} class="active"{% endblock %} {% block speakerstab %} class="active"{% endblock %}
{% block content %} {% block content %}
@ -25,7 +27,7 @@
<p>{{ participant.notes|linebreaksbr }}</p> <p>{{ participant.notes|linebreaksbr }}</p>
{% endif %} {% endif %}
<h2>{% trans "Informations" %}</h2> <h2>{% trans "Information" %}</h2>
<ul> <ul>
{% if participant.vip %}<li><b>{% trans "Invited speaker" %}</b></li>{% endif %} {% if participant.vip %}<li><b>{% trans "Invited speaker" %}</b></li>{% endif %}
<li><b>{% trans "E-mail:" %}</b> <a href="mailto:{{ participant.email }}">{{ participant.email }}</a></li> <li><b>{% trans "E-mail:" %}</b> <a href="mailto:{{ participant.email }}">{{ participant.email }}</a></li>
@ -56,6 +58,7 @@
<i>{% trans "in" %}</i> <i>{% trans "in" %}</i>
{{ talk.track }} {{ talk.track }}
{% endif %} {% endif %}
<span class="label label-{{ talk.get_status_color }}">{{ talk.get_status_str }}</span>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -81,6 +81,8 @@
<span class="text-warning">{% blocktrans count pending=participant.pending_talk_count %}pending: {{ pending }}{% plural %}pending: {{ pending }}{% endblocktrans %}</span> <span class="text-warning">{% blocktrans count pending=participant.pending_talk_count %}pending: {{ pending }}{% plural %}pending: {{ pending }}{% endblocktrans %}</span>
<span class="text-danger">{% blocktrans count refused=participant.refused_talk_count %}refused: {{ refused }}{% plural %}refused: {{ refused }}{% endblocktrans %}</span> <span class="text-danger">{% blocktrans count refused=participant.refused_talk_count %}refused: {{ refused }}{% plural %}refused: {{ refused }}{% endblocktrans %}</span>
<span class="text-danger">{% blocktrans count canceled=participant.canceled_talk_count %}canceled: {{ canceled }}{% plural %}canceled: {{ canceled }}{% endblocktrans %}</span>
</td> </td>
</tr> </tr>
{% if forloop.last %} {% if forloop.last %}

View File

@ -95,7 +95,20 @@
<a class="btn {% if vote == 2 %} active {% endif %}btn-success" href="{% url 'talk-vote' talk.pk 2 %}">+2</a> <a class="btn {% if vote == 2 %} active {% endif %}btn-success" href="{% url 'talk-vote' talk.pk 2 %}">+2</a>
</div> </div>
</p> </p>
<p>{{ talk.vote_set.count }} {% trans "vote" %}{{ talk.vote_set.count|pluralize }}, {% trans "average:" %} {{ talk.score|floatformat:1 }}</p>
<p><button class="btn btn-info" onclick="toggle_votes()">{% trans "Toggle actual votes" %}</button> <span id="actual_votes" class="invisible">{{ talk.vote_set.count }} {% trans "vote" %}{{ talk.vote_set.count|pluralize }}, {% trans "average:" %} {{ talk.score|floatformat:1 }}</span></p>
<script>
function toggle_votes(){
let votes = document.getElementById('actual_votes');
if (votes != null) {
if (votes.className == "invisible") {
votes.className = "visible";
} else {
votes.className = "invisible";
}
}
}
</script>
<a href="{% url 'talk-accept' talk.pk %}" class="btn btn-success">{% trans "Accept" %}</a> <a href="{% url 'talk-accept' talk.pk %}" class="btn btn-success">{% trans "Accept" %}</a>
<a href="{% url 'talk-decline' talk.pk %}" class="btn btn-danger">{% trans "Decline" %}</a> <a href="{% url 'talk-decline' talk.pk %}" class="btn btn-danger">{% trans "Decline" %}</a>

View File

@ -55,9 +55,11 @@
<th class="text-center">{% trans "Title" %} <a href="?{{ sort_urls.title }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.title }} pull-right"></span></a></th> <th class="text-center">{% trans "Title" %} <a href="?{{ sort_urls.title }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.title }} pull-right"></span></a></th>
<th class="text-center">{% trans "Intervention kind" %} <a href="?{{ sort_urls.category }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.category }} pull-right"></span></a></th> <th class="text-center">{% trans "Intervention kind" %} <a href="?{{ sort_urls.category }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.category }} pull-right"></span></a></th>
<th class="text-center">{% trans "Speakers" %}</th> <th class="text-center">{% trans "Speakers" %}</th>
<th class="text-center">{% trans "Language" %}</th>
<th class="text-center">{% trans "Track" %}</th> <th class="text-center">{% trans "Track" %}</th>
<th class="text-center">{% trans "Tags" %}</th> <th class="text-center">{% trans "Tags" %}</th>
<th class="text-center">{% trans "Status" %} <a href="?{{ sort_urls.status }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.status }} pull-right"></span></a></th> <th class="text-center">{% trans "Status" %} <a href="?{{ sort_urls.status }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.status }} pull-right"></span></a></th>
<th class="text-center">{% trans "Score" %} <a href="?{{ sort_urls.score }}"><span class="glyphicon glyphicon-{{ sort_glyphicons.score }} pull-right"></span></a></th>
</tr> </tr>
</thead> </thead>
<tfoot> <tfoot>
@ -82,11 +84,11 @@
{% empty %} {% empty %}
{% endfor %} {% endfor %}
</td> </td>
<td>{{ talk.language }}</td>
<td>{{ talk.track|default:"" }}</td> <td>{{ talk.track|default:"" }}</td>
<td>{{ talk.get_tags_html }}</td> <td>{{ talk.get_tags_html }}</td>
<td> <td>{{ talk.get_status_str }}</td>
{{ talk.get_status_str }} <td>{{ talk.score }}</td>
</td>
</tr> </tr>
{% if forloop.last%} {% if forloop.last%}
</tbody> </tbody>

View File

@ -28,7 +28,7 @@
{% for activity in volunteer.activities.all %} {% for activity in volunteer.activities.all %}
{% if forloop.first %} {% if forloop.first %}
{% trans "The volunteer applied for following activities:" %} {% trans "This volunteer applied for the following activities:" %}
<ul> <ul>
{% endif %} {% endif %}
<li> <li>
@ -38,7 +38,7 @@
</ul> </ul>
{% endif %} {% endif %}
{% empty %} {% empty %}
{% trans "The volunteer does not applied for any activities." %} {% trans "This volunteer hasn't applied for any activities." %}
{% endfor %} {% endfor %}

View File

@ -17,7 +17,7 @@
</div> </div>
<h2>{% trans "Your informations" %}</h2> <h2>{% trans "Your information" %}</h2>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">

View File

@ -9,7 +9,7 @@
<div class="page-header"> <div class="page-header">
<h1> <h1>
{% trans "Become a volunteers!" %} {% trans "Become a volunteer!" %}
</h1> </h1>
{% blocktrans %}We need you! To participate, please enter your name and e-mail and select the activities you are interested in.{% endblocktrans %} {% blocktrans %}We need you! To participate, please enter your name and e-mail and select the activities you are interested in.{% endblocktrans %}
</div> </div>

View File

@ -2,7 +2,7 @@ from django.core.mail import send_mail
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.urls import reverse, reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.generic import DeleteView, FormView, TemplateView from django.views.generic import DeleteView, FormView, TemplateView
from django.contrib import messages from django.contrib import messages
from django.db.models import Q, Count, Sum from django.db.models import Q, Count, Sum
@ -120,7 +120,7 @@ def volunteer_mail_token(request):
subject=_("[%(conference)s] Someone asked to access your profil") % {'conference': request.conference}, subject=_("[%(conference)s] Someone asked to access your profil") % {'conference': request.conference},
content=body, content=body,
) )
messages.success(request, _('A email have been sent with a link to access to your profil.')) messages.success(request, _('An email has been sent with a link to access to your profil.'))
return redirect(reverse('volunteer-mail-token')) return redirect(reverse('volunteer-mail-token'))
return render(request, 'cfp/volunteer_mail_token.html', { return render(request, 'cfp/volunteer_mail_token.html', {
'form': form, 'form': form,
@ -331,7 +331,7 @@ Thanks!
}, },
content=body, content=body,
) )
messages.success(request, _('Your proposition have been successfully submitted!')) messages.success(request, _('Your proposition has been successfully submitted!'))
return redirect(reverse('proposal-talk-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)))
return render(request, 'cfp/proposal_home.html', { return render(request, 'cfp/proposal_home.html', {
'speaker_form': speaker_form, 'speaker_form': speaker_form,
@ -372,7 +372,7 @@ Sincerely,
}, },
content=body, content=body,
) )
messages.success(request, _('A email have been sent with a link to access to your profil.')) messages.success(request, _('An email has been sent with a link to access to your profil.'))
return redirect(reverse('proposal-mail-token')) return redirect(reverse('proposal-mail-token'))
return render(request, 'cfp/proposal_mail_token.html', { return render(request, 'cfp/proposal_mail_token.html', {
'form': form, 'form': form,
@ -414,7 +414,7 @@ def proposal_talk_edit(request, speaker, talk_id=None):
else: else:
# TODO: it could be great to receive the proposition by mail # 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 # but this is not crucial as the speaker already have a link in its mailbox
messages.success(request, _('Your proposition have been successfully submitted!')) messages.success(request, _('Your proposition has been successfully submitted!'))
return redirect(reverse('proposal-talk-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)))
return render(request, 'cfp/proposal_talk_form.html', { return render(request, 'cfp/proposal_talk_form.html', {
'speaker': speaker, 'speaker': speaker,
@ -584,17 +584,17 @@ def talk_acknowledgment(request, talk_id, confirm):
talk.confirmed = confirm talk.confirmed = confirm
talk.save() talk.save()
if confirm: if confirm:
confirmation_message= _('The speaker confirmation have been noted.') confirmation_message= _('The speaker confirmation has been noted.')
action = _('confirmed') action = _('confirmed')
thread_note = _('The talk have been confirmed.') thread_note = _('The talk has been confirmed.')
else: else:
confirmation_message = _('The speaker unavailability have been noted.') confirmation_message = _('The speaker unavailability has been noted.')
action = _('cancelled') action = _('cancelled')
thread_note = _('The talk have been %(action)s.') % {'action': action} thread_note = _('The talk has been %(action)s.') % {'action': action}
send_message( send_message(
thread=talk.conversation, thread=talk.conversation,
author=request.user, author=request.user,
subject=_("[%(conference)s] The talk '%(talk)s' have been %(action)s.") % { subject=_("[%(conference)s] The talk '%(talk)s' has been %(action)s.") % {
'conference': request.conference, 'conference': request.conference,
'talk': talk, 'talk': talk,
'action': action, 'action': action,
@ -695,7 +695,7 @@ def talk_list(request):
send_message( send_message(
thread=talk.conversation, thread=talk.conversation,
author=request.user, author=request.user,
subject=_("[%(conference)s] The talk '%(talk)s' have been %(action)s") % { subject=_("[%(conference)s] The talk '%(talk)s' has been %(action)s") % {
'conference': request.conference, 'conference': request.conference,
'talk': talk, 'talk': talk,
'action': action, 'action': action,
@ -730,6 +730,7 @@ def talk_list(request):
'title': 'title', 'title': 'title',
'category': 'category', 'category': 'category',
'status': 'accepted', 'status': 'accepted',
'score': 'score',
} }
sort = request.GET.get('sort') sort = request.GET.get('sort')
if sort in SORT_MAPPING.keys(): if sort in SORT_MAPPING.keys():
@ -830,7 +831,7 @@ def talk_decide(request, talk_id, accept):
send_message( send_message(
thread=participant.conversation, thread=participant.conversation,
author=request.conference, author=request.conference,
subject=_("[%(conference)s] Your talk '%(talk)s' have been %(action)s") % { subject=_("[%(conference)s] Your talk '%(talk)s' has been %(action)s") % {
'conference': request.conference, 'conference': request.conference,
'talk': talk, 'talk': talk,
'action': action, 'action': action,
@ -841,7 +842,7 @@ def talk_decide(request, talk_id, accept):
send_message( send_message(
thread=talk.conversation, thread=talk.conversation,
author=request.user, author=request.user,
subject=_("[%(conference)s] The talk '%(talk)s' have been %(action)s") % { subject=_("[%(conference)s] The talk '%(talk)s' has been %(action)s") % {
'conference': request.conference, 'conference': request.conference,
'talk': talk, 'talk': talk,
'action': action, 'action': action,

View File

@ -12,7 +12,7 @@ Commands starting with ``$`` must be run as ``ponyconf`` user.
Requirements Requirements
------------ ------------
PonyConf have been tested with python 3.5 and 3.6. PonyConf has been tested with python 3.5 and 3.6.
Preparation Preparation

Binary file not shown.

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-11-02 12:00+0000\n" "POT-Creation-Date: 2023-01-30 13:44+0000\n"
"PO-Revision-Date: 2019-11-02 13:06+0100\n" "PO-Revision-Date: 2019-11-02 13:06+0100\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
@ -22,11 +22,11 @@ msgstr ""
msgid "Email address" msgid "Email address"
msgstr "Adresse e-mail" msgstr "Adresse e-mail"
#: accounts/models.py:10 cfp/models.py:120 cfp/models.py:493 #: accounts/models.py:10 cfp/models.py:120 cfp/models.py:491
msgid "Phone number" msgid "Phone number"
msgstr "Numéro de téléphone" msgstr "Numéro de téléphone"
#: accounts/models.py:11 cfp/models.py:494 #: accounts/models.py:11 cfp/models.py:492
msgid "SMS prefered" msgid "SMS prefered"
msgstr "SMS préférés" msgstr "SMS préférés"
@ -136,15 +136,15 @@ msgstr "Décliné"
msgid "Waiting" msgid "Waiting"
msgstr "En attente" msgstr "En attente"
#: cfp/forms.py:30 cfp/forms.py:132 cfp/forms.py:258 cfp/models.py:384 #: cfp/forms.py:30 cfp/forms.py:132 cfp/forms.py:258 cfp/models.py:382
msgid "Confirmed" msgid "Confirmed"
msgstr "Confirmé" msgstr "Confirmé"
#: cfp/forms.py:31 cfp/models.py:386 #: cfp/forms.py:31 cfp/models.py:384
msgid "Cancelled" msgid "Cancelled"
msgstr "Annulé" msgstr "Annulé"
#: cfp/forms.py:63 cfp/models.py:475 #: cfp/forms.py:63 cfp/models.py:473
msgid "Activity" msgid "Activity"
msgstr "Activité" msgstr "Activité"
@ -167,13 +167,13 @@ msgstr "Catégorie"
msgid "Title" msgid "Title"
msgstr "Titre" msgstr "Titre"
#: cfp/forms.py:110 cfp/models.py:168 cfp/models.py:471 #: cfp/forms.py:110 cfp/models.py:168 cfp/models.py:469
#: cfp/templates/cfp/proposal_talk_details.html:75 #: cfp/templates/cfp/proposal_talk_details.html:75
#: cfp/templates/cfp/staff/talk_details.html:64 #: cfp/templates/cfp/staff/talk_details.html:64
msgid "Description" msgid "Description"
msgstr "Description" msgstr "Description"
#: cfp/forms.py:111 cfp/models.py:122 cfp/models.py:496 #: cfp/forms.py:111 cfp/models.py:122 cfp/models.py:494
#: cfp/templates/cfp/staff/participant_details.html:24 #: cfp/templates/cfp/staff/participant_details.html:24
#: cfp/templates/cfp/staff/talk_details.html:83 #: cfp/templates/cfp/staff/talk_details.html:83
#: cfp/templates/cfp/staff/volunteer_details.html:22 #: cfp/templates/cfp/staff/volunteer_details.html:22
@ -184,7 +184,7 @@ msgstr "Notes"
msgid "Visible by speakers" msgid "Visible by speakers"
msgstr "Visible par les orateurs" msgstr "Visible par les orateurs"
#: cfp/forms.py:138 cfp/forms.py:264 cfp/models.py:340 #: cfp/forms.py:138 cfp/forms.py:264 cfp/models.py:338
#: cfp/templates/cfp/staff/talk_details.html:21 #: cfp/templates/cfp/staff/talk_details.html:21
#: cfp/templates/cfp/staff/talk_list.html:58 #: cfp/templates/cfp/staff/talk_list.html:58
#: cfp/templates/cfp/staff/track_form.html:14 #: cfp/templates/cfp/staff/track_form.html:14
@ -222,7 +222,7 @@ msgstr "Programmé"
msgid "Filter talks already / not yet scheduled" msgid "Filter talks already / not yet scheduled"
msgstr "Filtrer les exposés déjà / pas encore planifiées" msgstr "Filtrer les exposés déjà / pas encore planifiées"
#: cfp/forms.py:162 cfp/models.py:358 #: cfp/forms.py:162 cfp/models.py:356
#: cfp/templates/cfp/proposal_talk_details.html:89 #: cfp/templates/cfp/proposal_talk_details.html:89
#: cfp/templates/cfp/staff/talk_details.html:54 #: cfp/templates/cfp/staff/talk_details.html:54
msgid "Materials" msgid "Materials"
@ -277,7 +277,7 @@ msgstr "Envoyer un e-mail"
msgid "Notify by mail?" msgid "Notify by mail?"
msgstr "Notifier par e-mail ?" msgstr "Notifier par e-mail ?"
#: cfp/forms.py:280 cfp/models.py:491 #: cfp/forms.py:280 cfp/models.py:489
#: cfp/templates/cfp/staff/volunteer_list.html:43 #: cfp/templates/cfp/staff/volunteer_list.html:43
msgid "Email" msgid "Email"
msgstr "E-mail" msgstr "E-mail"
@ -403,8 +403,8 @@ msgstr ""
"Ladresse de réponse doit être une chaine de texte formatable avec un " "Ladresse de réponse doit être une chaine de texte formatable avec un "
"argument « token » (e.g. ponyconf+{token}@exemple.com)." "argument « token » (e.g. ponyconf+{token}@exemple.com)."
#: cfp/models.py:110 cfp/models.py:166 cfp/models.py:188 cfp/models.py:218 #: cfp/models.py:110 cfp/models.py:166 cfp/models.py:186 cfp/models.py:216
#: cfp/models.py:469 cfp/templates/cfp/staff/participant_list.html:54 #: cfp/models.py:467 cfp/templates/cfp/staff/participant_list.html:54
#: cfp/templates/cfp/staff/volunteer_list.html:42 #: cfp/templates/cfp/staff/volunteer_list.html:42
msgid "Name" msgid "Name"
msgstr "Nom" msgstr "Nom"
@ -417,39 +417,39 @@ msgstr "Ce champ est uniquement visible par les organisateurs."
msgid "Invited speaker" msgid "Invited speaker"
msgstr "Orateur invité" msgstr "Orateur invité"
#: cfp/models.py:190 #: cfp/models.py:188
msgid "Label" msgid "Label"
msgstr "Étiquette" msgstr "Étiquette"
#: cfp/models.py:191 #: cfp/models.py:189
msgid "Capacity" msgid "Capacity"
msgstr "Capacité" msgstr "Capacité"
#: cfp/models.py:220 #: cfp/models.py:218
msgid "Color" msgid "Color"
msgstr "Couleur" msgstr "Couleur"
#: cfp/models.py:222 #: cfp/models.py:220
msgid "Show the tag on the public program" msgid "Show the tag on the public program"
msgstr "Afficher létiquette sur le programme public" msgstr "Afficher létiquette sur le programme public"
#: cfp/models.py:223 #: cfp/models.py:221
msgid "Show the tag on the staff program" msgid "Show the tag on the staff program"
msgstr "Afficher létiquette sur le programme organisateur" msgstr "Afficher létiquette sur le programme organisateur"
#: cfp/models.py:259 #: cfp/models.py:257
msgid "Default duration (min)" msgid "Default duration (min)"
msgstr "Durée par défaut (min)" msgstr "Durée par défaut (min)"
#: cfp/models.py:260 #: cfp/models.py:258
msgid "Color on program" msgid "Color on program"
msgstr "Couleur sur le programme" msgstr "Couleur sur le programme"
#: cfp/models.py:261 #: cfp/models.py:259
msgid "Label on program" msgid "Label on program"
msgstr "Label dans le xml du programme" msgstr "Label dans le xml du programme"
#: cfp/models.py:335 cfp/templates/cfp/proposal_talk_details.html:53 #: cfp/models.py:333 cfp/templates/cfp/proposal_talk_details.html:53
#: cfp/templates/cfp/staff/base.html:10 #: cfp/templates/cfp/staff/base.html:10
#: cfp/templates/cfp/staff/participant_list.html:16 #: cfp/templates/cfp/staff/participant_list.html:16
#: cfp/templates/cfp/staff/talk_details.html:68 #: cfp/templates/cfp/staff/talk_details.html:68
@ -457,23 +457,23 @@ msgstr "Label dans le xml du programme"
msgid "Speakers" msgid "Speakers"
msgstr "Orateurs" msgstr "Orateurs"
#: cfp/models.py:336 #: cfp/models.py:334
msgid "Talk Title" msgid "Talk Title"
msgstr "Titre de la proposition" msgstr "Titre de la proposition"
#: cfp/models.py:338 #: cfp/models.py:336
msgid "Description of your talk" msgid "Description of your talk"
msgstr "Description de votre proposition" msgstr "Description de votre proposition"
#: cfp/models.py:339 #: cfp/models.py:337
msgid "This description will be visible on the program." msgid "This description will be visible on the program."
msgstr "Cette description sera visible sur le programme." msgstr "Cette description sera visible sur le programme."
#: cfp/models.py:342 cfp/templates/cfp/proposal_talk_details.html:99 #: cfp/models.py:340 cfp/templates/cfp/proposal_talk_details.html:99
msgid "Message to organizers" msgid "Message to organizers"
msgstr "Message aux organisateurs" msgstr "Message aux organisateurs"
#: cfp/models.py:343 #: cfp/models.py:341
msgid "" msgid ""
"If you have any constraint or if you have anything that may help you to " "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 " "select your talk, like a video or slides of your talk, please write it down "
@ -483,51 +483,51 @@ msgstr ""
"votre proposition, comme une vidéo, des slides, n'hésitez pas à les ajouter " "votre proposition, comme une vidéo, des slides, n'hésitez pas à les ajouter "
"ici. Ce champ ne sera visible que par les organisateurs." "ici. Ce champ ne sera visible que par les organisateurs."
#: cfp/models.py:347 #: cfp/models.py:345
msgid "Talk Category" msgid "Talk Category"
msgstr "Catégorie de proposition" msgstr "Catégorie de proposition"
#: cfp/models.py:348 #: cfp/models.py:346
msgid "I'm ok to be recorded on video" msgid "I'm ok to be recorded on video"
msgstr "Jaccepte dêtre enregistré en vidéo" msgstr "Jaccepte dêtre enregistré en vidéo"
#: cfp/models.py:350 #: cfp/models.py:348
msgid "Video licence" msgid "Video licence"
msgstr "Licence vidéo" msgstr "Licence vidéo"
#: cfp/models.py:351 #: cfp/models.py:349
msgid "I need sound" msgid "I need sound"
msgstr "Jai besoin de son" msgstr "Jai besoin de son"
#: cfp/models.py:354 #: cfp/models.py:352
msgid "Beginning date and time" msgid "Beginning date and time"
msgstr "Date et heure de début" msgstr "Date et heure de début"
#: cfp/models.py:355 #: cfp/models.py:353
msgid "Duration (min)" msgid "Duration (min)"
msgstr "Durée (min)" msgstr "Durée (min)"
#: cfp/models.py:359 #: cfp/models.py:357
msgid "" msgid ""
"You can use this field to share some materials related to your intervention." "You can use this field to share some materials related to your intervention."
msgstr "" msgstr ""
"Vous pouvez utiliser ce champ pour partager les supports de votre " "Vous pouvez utiliser ce champ pour partager les supports de votre "
"intervention." "intervention."
#: cfp/models.py:388 #: cfp/models.py:386
msgid "Waiting confirmation" msgid "Waiting confirmation"
msgstr "En attente de confirmation" msgstr "En attente de confirmation"
#: cfp/models.py:390 #: cfp/models.py:388
msgid "Refused" msgid "Refused"
msgstr "Refusé" msgstr "Refusé"
#: cfp/models.py:392 #: cfp/models.py:390
#, python-format #, python-format
msgid "Pending decision, score: %(score).1f" msgid "Pending decision, score: %(score).1f"
msgstr "En cours, score : %(score).1f" msgstr "En cours, score : %(score).1f"
#: cfp/models.py:476 cfp/models.py:498 #: cfp/models.py:474 cfp/models.py:496
#: cfp/templates/cfp/admin/activity_list.html:9 #: cfp/templates/cfp/admin/activity_list.html:9
#: cfp/templates/cfp/admin/base.html:14 #: cfp/templates/cfp/admin/base.html:14
#: cfp/templates/cfp/staff/volunteer_details.html:27 #: cfp/templates/cfp/staff/volunteer_details.html:27
@ -536,11 +536,11 @@ msgstr "En cours, score : %(score).1f"
msgid "Activities" msgid "Activities"
msgstr "Activités" msgstr "Activités"
#: cfp/models.py:490 #: cfp/models.py:488
msgid "Your Name" msgid "Your Name"
msgstr "Votre Nom" msgstr "Votre Nom"
#: cfp/models.py:497 #: cfp/models.py:495
msgid "If you have some constraints, you can indicate them here." msgid "If you have some constraints, you can indicate them here."
msgstr "Si vous avez des contraintes, vous pouvez les indiquer ici." msgstr "Si vous avez des contraintes, vous pouvez les indiquer ici."
@ -734,7 +734,7 @@ msgstr "Éditer votre profil"
#: cfp/templates/cfp/proposal_dashboard.html:18 #: cfp/templates/cfp/proposal_dashboard.html:18
#: cfp/templates/cfp/volunteer_dashboard.html:20 #: cfp/templates/cfp/volunteer_dashboard.html:20
msgid "Your informations" msgid "Your information"
msgstr "Vos informations" msgstr "Vos informations"
#: cfp/templates/cfp/proposal_dashboard.html:22 #: cfp/templates/cfp/proposal_dashboard.html:22
@ -976,7 +976,8 @@ msgid "Remove"
msgstr "Supprimer" msgstr "Supprimer"
#: cfp/templates/cfp/staff/participant_details.html:28 #: cfp/templates/cfp/staff/participant_details.html:28
msgid "Informations" #: cfp/templates/cfp/staff/talk_details.html:12
msgid "Information"
msgstr "Informations" msgstr "Informations"
#: cfp/templates/cfp/staff/participant_details.html:39 #: cfp/templates/cfp/staff/participant_details.html:39
@ -1222,10 +1223,6 @@ msgstr "Accepter la proposition"
msgid "Decline the proposal" msgid "Decline the proposal"
msgstr "Décliner la proposition" msgstr "Décliner la proposition"
#: cfp/templates/cfp/staff/talk_details.html:12
msgid "Information"
msgstr "Informations"
#: cfp/templates/cfp/staff/talk_details.html:25 #: cfp/templates/cfp/staff/talk_details.html:25
msgctxt "session" msgctxt "session"
msgid "not defined" msgid "not defined"
@ -1341,11 +1338,11 @@ msgid "Email:"
msgstr "E-mail :" msgstr "E-mail :"
#: cfp/templates/cfp/staff/volunteer_details.html:31 #: cfp/templates/cfp/staff/volunteer_details.html:31
msgid "The volunteer applied for following activities:" msgid "This volunteer applied for the following activities:"
msgstr "Le bénévole s'est proposé pour les activités suivantes :" msgstr "Le bénévole s'est proposé pour les activités suivantes :"
#: cfp/templates/cfp/staff/volunteer_details.html:41 #: cfp/templates/cfp/staff/volunteer_details.html:41
msgid "The volunteer does not applied for any activities." msgid "This volunteer hasn't applied for any activities."
msgstr "Le bénévole ne sest proposé pour aucune activité." msgstr "Le bénévole ne sest proposé pour aucune activité."
#: cfp/templates/cfp/staff/volunteer_details.html:49 #: cfp/templates/cfp/staff/volunteer_details.html:49
@ -1392,7 +1389,7 @@ msgid "Sorry, I have a setback"
msgstr "Désolé, jai un contretemps" msgstr "Désolé, jai un contretemps"
#: cfp/templates/cfp/volunteer_enrole.html:12 #: cfp/templates/cfp/volunteer_enrole.html:12
msgid "Become a volunteers!" msgid "Become a volunteer!"
msgstr "Devenez un bénévole !" msgstr "Devenez un bénévole !"
#: cfp/templates/cfp/volunteer_enrole.html:14 #: cfp/templates/cfp/volunteer_enrole.html:14
@ -1463,7 +1460,7 @@ msgid "[%(conference)s] Someone asked to access your profil"
msgstr "[%(conference)s] Quelquun a demandé à accéder à votre profil" msgstr "[%(conference)s] Quelquun a demandé à accéder à votre profil"
#: cfp/views.py:123 cfp/views.py:375 #: cfp/views.py:123 cfp/views.py:375
msgid "A email have been sent with a link to access to your profil." msgid "An email has 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." msgstr "Un e-mail vous a été envoyé avec un lien pour accéder à votre profil."
#: cfp/views.py:143 cfp/views.py:413 cfp/views.py:500 #: cfp/views.py:143 cfp/views.py:413 cfp/views.py:500
@ -1542,7 +1539,7 @@ msgid "[%(conference)s] Thank you for your proposition '%(talk)s'"
msgstr "[%(conference)s] Merci pour votre proposition « %(talk)s »" msgstr "[%(conference)s] Merci pour votre proposition « %(talk)s »"
#: cfp/views.py:334 cfp/views.py:417 #: cfp/views.py:334 cfp/views.py:417
msgid "Your proposition have been successfully submitted!" msgid "Your proposition has been successfully submitted!"
msgstr "Votre proposition a été transmise avec succès !" msgstr "Votre proposition a été transmise avec succès !"
#: cfp/views.py:353 #: cfp/views.py:353
@ -1661,39 +1658,32 @@ msgid "Co-speaker successfully removed from the talk."
msgstr "Co-intervenant supprimé de lexposé avec succès." msgstr "Co-intervenant supprimé de lexposé avec succès."
#: cfp/views.py:587 #: cfp/views.py:587
msgid "The speaker confirmation have been noted." msgid "The speaker confirmation has been noted."
msgstr "La confirmation de lorateur a été notée." msgstr "La confirmation de lorateur a été notée."
#: cfp/views.py:589 #: cfp/views.py:589
msgid "The talk have been confirmed." msgid "The talk has been confirmed."
msgstr "Lexposé a été confirmé." msgstr "Lexposé a été confirmé."
#: cfp/views.py:591 #: cfp/views.py:591
msgid "The speaker unavailability have been noted." msgid "The speaker unavailability has been noted."
msgstr "Lindisponibilité de lintervenant a été notée." msgstr "Lindisponibilité de lintervenant a été notée."
#: cfp/views.py:593 #: cfp/views.py:593 cfp/views.py:694 cfp/views.py:849
#, python-format #, python-format
msgid "The talk have been %(action)s." msgid "The talk has been %(action)s."
msgstr "Lexposé a été %(action)s." msgstr "Lexposé a été %(action)s."
#: cfp/views.py:597 #: cfp/views.py:597
#, python-format msgid "[%(conference)s] The talk '%(talk)s' has been %(action)s."
msgid "[%(conference)s] The talk '%(talk)s' have been %(action)s."
msgstr "[%(conference)s] Lexposé « %(talk)s » a été %(action)s." msgstr "[%(conference)s] Lexposé « %(talk)s » a été %(action)s."
#: cfp/views.py:693 cfp/views.py:825 #: cfp/views.py:693 cfp/views.py:825
msgid "declined" msgid "declined"
msgstr "décliné" msgstr "décliné"
#: cfp/views.py:694 cfp/views.py:849
#, python-format
msgid "The talk has been %(action)s."
msgstr "Lexposé a été %(action)s."
#: cfp/views.py:698 cfp/views.py:844 #: cfp/views.py:698 cfp/views.py:844
#, python-format msgid "[%(conference)s] The talk '%(talk)s' has been %(action)s"
msgid "[%(conference)s] The talk '%(talk)s' have been %(action)s"
msgstr "[%(conference)s] Lexposé « %(talk)s » a été %(action)s" msgstr "[%(conference)s] Lexposé « %(talk)s » a été %(action)s"
#: cfp/views.py:782 #: cfp/views.py:782
@ -1710,8 +1700,7 @@ msgid "Vote successfully updated"
msgstr "Vote mis à jour" msgstr "Vote mis à jour"
#: cfp/views.py:833 #: cfp/views.py:833
#, python-format msgid "[%(conference)s] Your talk '%(talk)s' has been %(action)s"
msgid "[%(conference)s] Your talk '%(talk)s' have been %(action)s"
msgstr "[%(conference)s] Votre exposé « %(talk)s » a été %(action)s" msgstr "[%(conference)s] Votre exposé « %(talk)s » a été %(action)s"
#: cfp/views.py:851 #: cfp/views.py:851
@ -1784,11 +1773,11 @@ msgstr "Envoyer"
msgid "No messages." msgid "No messages."
msgstr "Aucun message." msgstr "Aucun message."
#: ponyconf/settings.py:141 #: ponyconf/settings.py:126
msgid "English" msgid "English"
msgstr "Anglais" msgstr "Anglais"
#: ponyconf/settings.py:142 #: ponyconf/settings.py:127
msgid "French" msgid "French"
msgstr "Français" msgstr "Français"
@ -1840,6 +1829,12 @@ msgstr "Mot de passe oublié ?"
msgid "Password Change" msgid "Password Change"
msgstr "Changement de mot de passe" msgstr "Changement de mot de passe"
#~ msgid "Informations"
#~ msgstr "Informations"
#~ msgid "The talk have been %(action)s."
#~ msgstr "Lexposé a été %(action)s."
#~ msgid "Messages" #~ msgid "Messages"
#~ msgstr "Messages" #~ msgstr "Messages"

View File

@ -4,7 +4,7 @@ from django.core.mail import EmailMessage, get_connection
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
import hashlib import hashlib
@ -35,7 +35,7 @@ class MessageAuthor(models.Model):
def __str__(self): def __str__(self):
author_class = self.author_type.model_class() author_class = self.author_type.model_class()
if author_class == get_user_model(): if self.author is not None and author_class == get_user_model():
return self.author.get_full_name() return self.author.get_full_name()
else: else:
return str(self.author) return str(self.author)

View File

@ -2,6 +2,8 @@ from ponyconf.settings import *
SECRET_KEY = 'CHANGE ME' SECRET_KEY = 'CHANGE ME'
DEFAULT_PHONE_REGION = "FR"
DEBUG = False DEBUG = False
LOGGING = { LOGGING = {

View File

@ -2,7 +2,7 @@
Django settings for ponyconf project. Django settings for ponyconf project.
""" """
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
import os import os
@ -34,6 +34,7 @@ INSTALLED_APPS = [
'bootstrap3', 'bootstrap3',
'django_select2', 'django_select2',
'crispy_forms', 'crispy_forms',
'crispy_bootstrap3',
# build-in apps # build-in apps
'django.contrib.admin', 'django.contrib.admin',
@ -91,6 +92,8 @@ DATABASES = {
} }
} }
DEFAULT_AUTO_FIELD='django.db.models.AutoField'
# Password validation # Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
@ -128,7 +131,7 @@ LANGUAGES = [
] ]
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'en-us'
DEFAULT_PHONE_REGION = "US"
LOCALE_PATHS = [ LOCALE_PATHS = [
os.path.join(BASE_DIR, 'locale'), os.path.join(BASE_DIR, 'locale'),
] ]
@ -171,4 +174,3 @@ SERVER_EMAIL = 'ponyconf@example.com'
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'localhost' EMAIL_HOST = 'localhost'
EMAIL_PORT = 25 EMAIL_PORT = 25

View File

@ -16,6 +16,6 @@ class PonyConfModel(models.Model):
def markdown_to_html(md): def markdown_to_html(md):
html = markdown(md) html = markdown(md)
allowed_tags = bleach.ALLOWED_TAGS + ['p', 'pre', 'span' ] + ['h%d' % i for i in range(1, 7) ] allowed_tags = bleach.ALLOWED_TAGS | {'p', 'pre', 'span' } | {'h%d' % i for i in range(1, 7)}
html = bleach.clean(html, tags=allowed_tags) html = bleach.clean(html, tags=allowed_tags)
return mark_safe(html) return mark_safe(html)

View File

@ -1,47 +1,107 @@
# #
# This file is autogenerated by pip-compile # This file is autogenerated by pip-compile with Python 3.9
# To update, run: # by the following command:
# #
# pip-compile requirements-dev.in # pip-compile requirements-dev.in
# #
asgiref==3.2.10 # via django asgiref==3.6.0
backcall==0.2.0 # via ipython # via django
bleach==3.1.5 # via -r requirements.in asttokens==2.2.1
chardet==3.0.4 # via -r requirements.in # via stack-data
decorator==4.4.2 # via ipython, traitlets backcall==0.2.0
django-appconf==1.0.4 # via django-select2 # via ipython
django-autoslug==1.9.8 # via -r requirements.in bleach==6.0.0
django-bootstrap3==14.1.0 # via -r requirements.in # via -r requirements.in
django-colorful==1.3 # via -r requirements.in chardet==5.1.0
django-crispy-forms==1.9.2 # via -r requirements.in # via -r requirements.in
django-debug-toolbar==2.2 # via -r requirements-dev.in crispy-bootstrap3==2022.1
django-extensions==3.0.4 # via -r requirements-dev.in # via -r requirements.in
django-select2==7.4.2 # via -r requirements.in decorator==5.1.1
django==3.1 # via -r requirements.in, django-appconf, django-bootstrap3, django-colorful, django-debug-toolbar, django-select2 # via ipython
icalendar==4.0.6 # via -r requirements.in django==4.1.7
importlib-metadata==1.7.0 # via django-bootstrap3, markdown # via
ipython-genutils==0.2.0 # via traitlets # -r requirements.in
ipython==7.16.1 # via -r requirements-dev.in # crispy-bootstrap3
jedi==0.17.2 # via ipython # django-appconf
jinja2==2.11.2 # via -r requirements.in # django-bootstrap3
markdown==3.2.2 # via -r requirements.in # django-colorful
markupsafe==1.1.1 # via jinja2 # django-crispy-forms
packaging==20.4 # via bleach # django-debug-toolbar
parso==0.7.1 # via jedi # django-extensions
pexpect==4.8.0 # via ipython # django-select2
pickleshare==0.7.5 # via ipython django-appconf==1.0.5
prompt-toolkit==3.0.5 # via ipython # via django-select2
ptyprocess==0.6.0 # via pexpect django-autoslug==1.9.8
pygments==2.6.1 # via ipython # via -r requirements.in
pyparsing==2.4.7 # via packaging django-bootstrap3==22.2
python-dateutil==2.8.1 # via icalendar # via -r requirements.in
pytz==2020.1 # via django, icalendar django-colorful==1.3
six==1.15.0 # via bleach, packaging, python-dateutil, traitlets # via -r requirements.in
sqlparse==0.3.1 # via django, django-debug-toolbar django-crispy-forms==2.0
traitlets==4.3.3 # via ipython # via
wcwidth==0.2.5 # via prompt-toolkit # -r requirements.in
webencodings==0.5.1 # via bleach # crispy-bootstrap3
zipp==3.1.0 # via importlib-metadata django-debug-toolbar==3.8.1
# via -r requirements-dev.in
# The following packages are considered to be unsafe in a requirements file: django-extensions==3.2.1
# setuptools # via -r requirements-dev.in
django-select2==8.1.1
# via -r requirements.in
executing==1.2.0
# via stack-data
icalendar==5.0.4
# via -r requirements.in
importlib-metadata==6.0.0
# via markdown
ipython==8.11.0
# via -r requirements-dev.in
jedi==0.18.2
# via ipython
jinja2==3.1.2
# via -r requirements.in
markdown==3.4.1
# via -r requirements.in
markupsafe==2.1.2
# via jinja2
matplotlib-inline==0.1.6
# via ipython
parso==0.8.3
# via jedi
pexpect==4.8.0
# via ipython
phonenumbers==8.13.7
# via -r requirements.in
pickleshare==0.7.5
# via ipython
prompt-toolkit==3.0.38
# via ipython
ptyprocess==0.7.0
# via pexpect
pure-eval==0.2.2
# via stack-data
pygments==2.14.0
# via ipython
python-dateutil==2.8.2
# via icalendar
pytz==2022.7.1
# via icalendar
six==1.16.0
# via
# bleach
# python-dateutil
sqlparse==0.4.3
# via
# django
# django-debug-toolbar
stack-data==0.6.2
# via ipython
traitlets==5.9.0
# via
# ipython
# matplotlib-inline
wcwidth==0.2.6
# via prompt-toolkit
webencodings==0.5.1
# via bleach
zipp==3.15.0
# via importlib-metadata

View File

@ -2,6 +2,7 @@ django
django-bootstrap3 django-bootstrap3
django-crispy-forms django-crispy-forms
crispy-bootstrap3
django-select2 django-select2
django-colorful django-colorful
django-autoslug django-autoslug
@ -11,3 +12,4 @@ bleach
chardet chardet
icalendar icalendar
jinja2 jinja2
phonenumbers

View File

@ -1,29 +1,63 @@
# #
# This file is autogenerated by pip-compile # This file is autogenerated by pip-compile with Python 3.9
# To update, run: # by the following command:
# #
# pip-compile # pip-compile
# #
asgiref==3.2.10 # via django asgiref==3.6.0
bleach==3.1.5 # via -r requirements.in # via django
chardet==3.0.4 # via -r requirements.in bleach==6.0.0
django-appconf==1.0.4 # via django-select2 # via -r requirements.in
django-autoslug==1.9.8 # via -r requirements.in chardet==5.1.0
django-bootstrap3==14.1.0 # via -r requirements.in # via -r requirements.in
django-colorful==1.3 # via -r requirements.in crispy-bootstrap3==2022.1
django-crispy-forms==1.9.2 # via -r requirements.in # via -r requirements.in
django-select2==7.4.2 # via -r requirements.in django==4.1.7
django==3.1 # via -r requirements.in, django-appconf, django-bootstrap3, django-colorful, django-select2 # via
icalendar==4.0.6 # via -r requirements.in # -r requirements.in
importlib-metadata==1.7.0 # via django-bootstrap3, markdown # crispy-bootstrap3
jinja2==2.11.2 # via -r requirements.in # django-appconf
markdown==3.2.2 # via -r requirements.in # django-bootstrap3
markupsafe==1.1.1 # via jinja2 # django-colorful
packaging==20.4 # via bleach # django-crispy-forms
pyparsing==2.4.7 # via packaging # django-select2
python-dateutil==2.8.1 # via icalendar django-appconf==1.0.5
pytz==2020.1 # via django, icalendar # via django-select2
six==1.15.0 # via bleach, packaging, python-dateutil django-autoslug==1.9.8
sqlparse==0.3.1 # via django # via -r requirements.in
webencodings==0.5.1 # via bleach django-bootstrap3==22.2
zipp==3.1.0 # via importlib-metadata # via -r requirements.in
django-colorful==1.3
# via -r requirements.in
django-crispy-forms==2.0
# via
# -r requirements.in
# crispy-bootstrap3
django-select2==8.1.1
# via -r requirements.in
icalendar==5.0.4
# via -r requirements.in
importlib-metadata==6.0.0
# via markdown
jinja2==3.1.2
# via -r requirements.in
markdown==3.4.1
# via -r requirements.in
markupsafe==2.1.2
# via jinja2
phonenumbers==8.13.7
# via -r requirements.in
python-dateutil==2.8.2
# via icalendar
pytz==2022.7.1
# via icalendar
six==1.16.0
# via
# bleach
# python-dateutil
sqlparse==0.4.3
# via django
webencodings==0.5.1
# via bleach
zipp==3.15.0
# via importlib-metadata