Merge branch 'dev'

This commit is contained in:
Élie Bouttier 2016-11-23 18:46:25 +01:00
commit 8582f68c77
58 changed files with 788 additions and 688 deletions

View File

@ -9,7 +9,7 @@ from .models import Participation, Profile
UserForm = modelform_factory(User, fields=['first_name', 'last_name', 'email', 'username']) UserForm = modelform_factory(User, fields=['first_name', 'last_name', 'email', 'username'])
ProfileForm = modelform_factory(Profile, fields=['biography']) ProfileForm = modelform_factory(Profile, fields=['phone_number', 'biography'])
ParticipationForm = modelform_factory(Participation, ParticipationForm = modelform_factory(Participation,
fields=['need_transport', 'transport', 'transport_city_outward', 'transport_city_return', fields=['need_transport', 'transport', 'transport_city_outward', 'transport_city_return',

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-11-13 19:08
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0015_auto_20161013_1922'),
]
operations = [
migrations.AddField(
model_name='profile',
name='phone_number',
field=models.CharField(blank=True, default='', max_length=16, verbose_name='Phone number'),
),
]

View File

@ -15,6 +15,7 @@ from .utils import generate_user_uid
class Profile(PonyConfModel): class Profile(PonyConfModel):
user = models.OneToOneField(User) user = models.OneToOneField(User)
phone_number = models.CharField(max_length=16, blank=True, default='', verbose_name=_('Phone number'))
biography = models.TextField(blank=True, verbose_name=_('Biography')) biography = models.TextField(blank=True, verbose_name=_('Biography'))
email_token = models.CharField(max_length=12, default=generate_user_uid, unique=True) email_token = models.CharField(max_length=12, default=generate_user_uid, unique=True)
@ -88,7 +89,7 @@ class Participation(PonyConfModel):
return str(self.user.profile) return str(self.user.profile)
def get_absolute_url(self): def get_absolute_url(self):
return reverse('show-speaker', kwargs={'username': self.user.username}) return reverse('show-participant', kwargs={'username': self.user.username})
def is_orga(self): def is_orga(self):
return self.orga return self.orga

View File

@ -1,12 +1,15 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load accounts_tags i18n avatar_tags %} {% load accounts_tags i18n avatar_tags %}
{% block participantstab %} class="active"{% endblock %}
{% block content %} {% block content %}
<h1>{{ profile }}</h1> <h1>{{ profile }}</h1>
{% if request|staff %} {% if request|staff %}
<a href="{% url 'conversation' profile.user.username %}" class="btn btn-primary ">{% trans "Contact" %}</a> <a href="{% url 'user-conversation' profile.user.username %}" class="btn btn-primary ">{% trans "Contact" %}</a>
{% endif %} {% endif %}
{% if request|edit_profile:profile %} {% if request|edit_profile:profile %}
<a href="{% url 'edit-participant' profile.user.username %}" class="btn btn-success">{% trans "Edit" %}</a> <a href="{% url 'edit-participant' profile.user.username %}" class="btn btn-success">{% trans "Edit" %}</a>

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load i18n %} {% load i18n %}
{% block speakertab %} class="active"{% endblock %} {% block participantstab %} class="active"{% endblock %}
{% block content %} {% block content %}

View File

@ -1,14 +1,12 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 accounts_tags i18n %} {% load bootstrap3 accounts_tags i18n %}
{% block admintab %} active{% endblock %} {% block participantstab %} class="active"{% endblock %}
{% block content %} {% block content %}
<div class="page-header"> <h1>{% trans "Participants" %}</h1>
<h1>Participants</h1>
</div>
<table class="table table-striped"> <table class="table table-striped">
<tr> <tr>
@ -26,14 +24,14 @@
<td>{% for topic in participation.topic_set.all %}{{ topic.get_link }}{% if not forloop.last %}, <td>{% for topic in participation.topic_set.all %}{{ topic.get_link }}{% if not forloop.last %},
{% endif %}{% endfor %}</td> {% endif %}{% endfor %}</td>
<td> <td>
<a href="{% url 'conversation' participation.user.username %}" data-toggle="tooltip" data-placement="bottom" <a href="{% url 'user-conversation' participation.user.username %}" data-toggle="tooltip" data-placement="bottom"
title="{% trans "View conversation" %}"><span class="glyphicon glyphicon-envelope"></span></a> title="{% trans "View conversation" %}"><span class="glyphicon glyphicon-envelope"></span></a>
{% if request.user in participation.conversation.subscribers.all %} {% if request.user in participation.conversation.subscribers.all %}
<a href="{% url 'unsubscribe-conversation' participation.user.username %}?next={% url 'list-participant' %}" <a href="{% url 'unsubscribe-conversation' participation.user.username %}?next={% url 'list-participants' %}"
data-toggle="tooltip" data-placement="bottom" title="{% trans "Unsubscribe from the conversation" %}"> data-toggle="tooltip" data-placement="bottom" title="{% trans "Unsubscribe from the conversation" %}">
<span class="glyphicon glyphicon-star"></span></a> <span class="glyphicon glyphicon-star"></span></a>
{% else %} {% else %}
<a href="{% url 'subscribe-conversation' participation.user.username %}?next={% url 'list-participant' %}" <a href="{% url 'subscribe-conversation' participation.user.username %}?next={% url 'list-participants' %}"
data-toggle="tooltip" data-placement="bottom" title="{% trans "Subscribe to the conversation" %}"> data-toggle="tooltip" data-placement="bottom" title="{% trans "Subscribe to the conversation" %}">
<span class="glyphicon glyphicon-star-empty"></span></a> <span class="glyphicon glyphicon-star-empty"></span></a>
{% endif %} {% endif %}

View File

@ -47,7 +47,7 @@ class AccountTests(TestCase):
def test_participant_views(self): def test_participant_views(self):
self.assertEqual(self.client.get(reverse('registration_register')).status_code, 200) self.assertEqual(self.client.get(reverse('registration_register')).status_code, 200)
self.client.login(username='b', password='b') self.client.login(username='b', password='b')
self.assertEqual(self.client.get(reverse('list-participant')).status_code, 403) self.assertEqual(self.client.get(reverse('list-participants')).status_code, 403)
self.assertEqual(self.client.post(reverse('edit-participant', kwargs={'username': 'a'}), self.assertEqual(self.client.post(reverse('edit-participant', kwargs={'username': 'a'}),
{'biography': 'foo'}).status_code, 403) {'biography': 'foo'}).status_code, 403)
b = User.objects.get(username='b') b = User.objects.get(username='b')
@ -55,12 +55,12 @@ class AccountTests(TestCase):
b.save() b.save()
p = Participation.objects.get(user=b) p = Participation.objects.get(user=b)
self.assertFalse(p.orga) self.assertFalse(p.orga)
self.assertEqual(self.client.get(reverse('list-participant')).status_code, 403) self.assertEqual(self.client.get(reverse('list-participants')).status_code, 403)
# login signal should set orga to True due to superuser status # login signal should set orga to True due to superuser status
self.client.login(username='b', password='b') self.client.login(username='b', password='b')
p = Participation.objects.get(user=b) p = Participation.objects.get(user=b)
self.assertTrue(p.orga) self.assertTrue(p.orga)
self.assertEqual(self.client.get(reverse('list-participant')).status_code, 200) self.assertEqual(self.client.get(reverse('list-participants')).status_code, 200)
self.assertEqual(self.client.post(reverse('edit-participant', kwargs={'username': 'a'}), self.assertEqual(self.client.post(reverse('edit-participant', kwargs={'username': 'a'}),
{'biography': 'foo', 'nootes': 'bar'}).status_code, 200) {'biography': 'foo', 'nootes': 'bar'}).status_code, 200)
self.assertEqual(User.objects.get(username='a').profile.biography, '') self.assertEqual(User.objects.get(username='a').profile.biography, '')

View File

@ -8,8 +8,9 @@ urlpatterns = [
url(r'^profile/$', views.profile, name='profile'), url(r'^profile/$', views.profile, name='profile'),
url(r'^login/$', auth_views.login, {'extra_context': {'buttons': [views.RESET_PASSWORD_BUTTON]}}, name='login'), url(r'^login/$', auth_views.login, {'extra_context': {'buttons': [views.RESET_PASSWORD_BUTTON]}}, name='login'),
url(r'^logout/$', auth_views.logout, {'next_page': settings.LOGOUT_REDIRECT_URL}, name='logout'), url(r'^logout/$', auth_views.logout, {'next_page': settings.LOGOUT_REDIRECT_URL}, name='logout'),
url(r'^admin/participants/$', views.participation_list, name='list-participant'), url(r'^participant/$', views.participation_list, name='list-participants'),
url(r'^admin/participant/(?P<username>[\w.@+-]+)$', views.edit, name='edit-participant'), url(r'^participant/(?P<username>[\w.@+-]+)$', views.participant_details, name='show-participant'),
url(r'^participant/(?P<username>[\w.@+-]+)/edit/$', views.participant_edit, name='edit-participant'),
url(r'^avatar/', include('avatar.urls')), url(r'^avatar/', include('avatar.urls')),
url(r'', include('django.contrib.auth.urls')), url(r'', include('django.contrib.auth.urls')),
url(r'', include('registration.backends.default.urls')), url(r'', include('registration.backends.default.urls')),

View File

@ -12,6 +12,9 @@ from .forms import (NewParticipationForm, ParticipationForm,
from .models import Participation, Profile, User from .models import Participation, Profile, User
from .utils import can_edit_profile, is_orga from .utils import can_edit_profile, is_orga
from proposals.models import Talk
from proposals.utils import allowed_talks
RESET_PASSWORD_BUTTON = ('password_reset', 'warning', _('Reset your password')) RESET_PASSWORD_BUTTON = ('password_reset', 'warning', _('Reset your password'))
CHANGE_PASSWORD_BUTTON = ('password_change', 'warning', _('Change password')) CHANGE_PASSWORD_BUTTON = ('password_change', 'warning', _('Change password'))
CHANGE_AVATAR_BUTTON = ('avatar_change', 'default', _('Change avatar')) CHANGE_AVATAR_BUTTON = ('avatar_change', 'default', _('Change avatar'))
@ -30,9 +33,9 @@ def profile(request):
if all(form.is_valid() for form in forms): if all(form.is_valid() for form in forms):
for form in forms: for form in forms:
form.save() form.save()
messages.success(request, 'Profile updated successfully.') messages.success(request, _('Profile updated successfully.'))
else: else:
messages.error(request, 'Please correct those errors.') messages.error(request, _('Please correct those errors.'))
return render(request, 'accounts/profile.html', { return render(request, 'accounts/profile.html', {
'user_form': user_form, 'user_form': user_form,
@ -42,30 +45,8 @@ def profile(request):
}) })
@staff_required
def participation_list(request):
participation_list = Participation.objects.filter(site=get_current_site(request)).all()
form = NewParticipationForm(request.POST or None, site=get_current_site(request))
if request.method == 'POST' and form.is_valid():
if not Participation.objects.get(user=request.user, site=get_current_site(request)).is_orga():
raise PermissionDenied()
user = User.objects.get(username=form.cleaned_data['participant'])
participation, created = Participation.objects.get_or_create(user=user, site=get_current_site(request))
if created:
messages.success(request, "%s added to participant" % user.profile)
else:
messages.info(request, "%s is already a participant" % user.profile)
return redirect(reverse('list-participant'))
return render(request, 'accounts/participation_list.html', {
'participation_list': participation_list,
'form': form,
})
@login_required @login_required
def edit(request, username): def participant_edit(request, username):
profile = get_object_or_404(Profile, user__username=username) profile = get_object_or_404(Profile, user__username=username)
if not can_edit_profile(request, profile): if not can_edit_profile(request, profile):
@ -81,8 +62,41 @@ def edit(request, username):
if all(form.is_valid() for form in forms): if all(form.is_valid() for form in forms):
for form in forms: for form in forms:
form.save() form.save()
messages.success(request, 'Profile updated successfully.') messages.success(request, _('Profile updated successfully.'))
else: else:
messages.error(request, 'Please correct those errors.') messages.error(request, _('Please correct those errors.'))
return render(request, 'accounts/edit_profile.html', {'forms': forms, 'profile': profile}) return render(request, 'accounts/participant_edit.html', {'forms': forms, 'profile': profile})
@staff_required
def participation_list(request):
participation_list = Participation.objects.filter(site=get_current_site(request)).all()
form = NewParticipationForm(request.POST or None, site=get_current_site(request))
if request.method == 'POST' and form.is_valid():
if not Participation.objects.get(user=request.user, site=get_current_site(request)).is_orga():
raise PermissionDenied()
user = User.objects.get(username=form.cleaned_data['participant'])
participation, created = Participation.objects.get_or_create(user=user, site=get_current_site(request))
if created:
messages.success(request, _("%(name)s added to participants") % {'name': user.profile})
else:
messages.info(request, _("%(name)s is already a participant") % {'name': user.profile})
return redirect(reverse('list-participants'))
return render(request, 'accounts/participant_list.html', {
'participation_list': participation_list,
'form': form,
})
@login_required
def participant_details(request, username):
user = get_object_or_404(User, username=username)
participation = get_object_or_404(Participation, user=user, site=get_current_site(request))
return render(request, 'accounts/participant_details.html', {
'profile': user.profile,
'participation': participation,
'talk_list': allowed_talks(Talk.objects.filter(site=get_current_site(request), speakers=user), request),
})

View File

@ -53,7 +53,7 @@ class ConversationWithParticipant(Conversation):
return "Conversation with %s" % self.participation.user return "Conversation with %s" % self.participation.user
def get_absolute_url(self): def get_absolute_url(self):
return reverse('conversation', kwargs={'username': self.participation.user.username}) return reverse('user-conversation', kwargs={'username': self.participation.user.username})
def get_site(self): def get_site(self):
return self.participation.site return self.participation.site
@ -67,7 +67,7 @@ class ConversationWithParticipant(Conversation):
self.subscribers.add(message.author) self.subscribers.add(message.author)
data = { data = {
'content': message.content, 'content': message.content,
'uri': site.domain + reverse('conversation', args=[self.participation.user.username]), 'uri': site.domain + reverse('user-conversation', args=[self.participation.user.username]),
} }
first = self.messages.first() first = self.messages.first()
if first != message: if first != message:
@ -117,7 +117,7 @@ class ConversationAboutTalk(Conversation):
data.update({ data.update({
'talk': self.talk, 'talk': self.talk,
'proposer': message.author, 'proposer': message.author,
'proposer_uri': site.domain + reverse('show-speaker', args=[message.author.username]) 'proposer_uri': site.domain + reverse('show-participant', args=[message.author.username])
}) })
else: else:
subject = 'Re: [%s] Talk: %s' % (site.name, self.talk.title) subject = 'Re: [%s] Talk: %s' % (site.name, self.talk.title)

View File

@ -1,6 +1,6 @@
<div class="panel panel-{% block panelstyleblock %}default{% endblock %}"> <div class="panel panel-{% if message.author == message.conversation.participation.user %}info{% else %}default{% endif %}">
<div class="panel-heading"> <div class="panel-heading">
{{ message.created }} | <a href="{% url 'show-speaker' message.author.username %}">{{ message.author.profile }}</a> {{ message.created }} | <a href="{% url 'show-participant' message.author.username %}">{{ message.author.profile }}</a>
</div> </div>
<div class="panel-body"> <div class="panel-body">
{{ message.content|linebreaksbr }} {{ message.content|linebreaksbr }}

View File

@ -1,17 +1,12 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load i18n %} {% load i18n %}
{% block admintab %} active{% endblock %} {% block correspondentstab %} class="active"{% endblock %}
{% block content %} {% block content %}
<div class="page-header"> <h1>{% blocktrans with correspondent=correspondent.profile %}Conversation with {{ correspondent }}{% endblocktrans %}</h1>
<h1>{% trans "Messaging" %}</h1>
{% block heading %}
<a href="{% url 'correspondents' %}" class="btn btn-primary"><span class="glyphicon glyphicon-arrow-left"></span>&nbsp;{% trans "Go back to correspondents list" %}</a>
{% endblock %}
</div>
{% for message in message_list %} {% for message in message_list %}
{% include 'conversations/_message_detail.html' %} {% include 'conversations/_message_detail.html' %}

View File

@ -0,0 +1,36 @@
{% extends 'staff.html' %}
{% load bootstrap3 i18n %}
{% block correspondentstab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Correspondents" %}</h1>
<p>{% trans "This is the list of participants that you follow." %}</p>
<table class="table table-striped">
<tr>
<th>#</th>
<th>Username</th>
<th>Full name</th>
<th>Administration</th>
</tr>
{% for correspondent in correspondent_list %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ correspondent.user.username }}</td>
<td>{{ correspondent.user.get_full_name }}</td>
<td>
<a href="{% url 'user-conversation' correspondent.user.username %}"><span class="glyphicon glyphicon-envelope"></span></a>
{% if request.user in correspondent.conversation.subscribers.all %}
<a href="{% url 'unsubscribe-conversation' correspondent.user.username %}?next={% url 'list-correspondents' %}" data-toggle="tooltip" data-placement="bottom" title="{% trans "Unsubscribe from the conversation" %}"><span class="glyphicon glyphicon-star"></span></a>
{% else %}
<a href="{% url 'subscribe-conversation' correspondent.user.username %}?next={% url 'list-correspondents' %}" data-toggle="tooltip" data-placement="bottom" title="{% trans "Subscribe to the conversation" %}"><span class="glyphicon glyphicon-star-empty"></span></a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -1,38 +0,0 @@
{% extends 'base.html' %}
{% load bootstrap3 i18n %}
{% block admintab %} active{% endblock %}
{% block content %}
<div class="page-header">
<h1>{% trans "Correspondents" %}</h1>
{% trans "This is the list of participants that you follow." %}
</div>
<table class="table table-striped">
<tr>
<th>#</th>
<th>Username</th>
<th>Full name</th>
<th>Administration</th>
</tr>
{% for correspondent in correspondent_list %}
<tr>
<th>{{ forloop.counter }}</th>
<td>{{ correspondent.user.username }}</td>
<td>{{ correspondent.user.get_full_name }}</td>
<td>
<a href="{% url 'conversation' correspondent.user.username %}"><span class="glyphicon glyphicon-envelope"></span></a>
{% if request.user in correspondent.conversation.subscribers.all %}
<a href="{% url 'unsubscribe-conversation' correspondent.user.username %}?next={% url 'correspondents' %}" data-toggle="tooltip" data-placement="bottom" title="{% trans "Unsubscribe from the conversation" %}"><span class="glyphicon glyphicon-star"></span></a>
{% else %}
<a href="{% url 'subscribe-conversation' correspondent.user.username %}?next={% url 'correspondents' %}" data-toggle="tooltip" data-placement="bottom" title="{% trans "Subscribe to the conversation" %}"><span class="glyphicon glyphicon-star-empty"></span></a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -1,12 +1,18 @@
{% extends 'conversations/conversation.html' %} {% extends 'base.html' %}
{% load i18n %} {% load i18n %}
{% block inboxtab %} class="active"{% endblock %} {% block inboxtab %} class="active"{% endblock %}
{% block admintab %}{% endblock %}
{% block heading %} {% block content %}
<h1>{% trans "Messaging" %}</h1>
<p>{% trans "You can use this page to communicate with the staff." %}</p> <p>{% trans "You can use this page to communicate with the staff." %}</p>
{% endblock %}
{% block panelstyleblock %}{% if message.author == message.conversation.participation.user %}info{% else %}success{% endif %}{% endblock %} {% for message in message_list %}
{% include 'conversations/_message_detail.html' %}
{% endfor %}
{% include 'conversations/_message_form.html' %}
{% endblock %}

View File

@ -37,12 +37,14 @@ class ConversationTests(TestCase):
self.assertEqual(self.client.get(url).status_code, 302) self.assertEqual(self.client.get(url).status_code, 302)
self.client.login(username='c', password='c') self.client.login(username='c', password='c')
self.assertEqual(self.client.get(url).status_code, 403) self.assertEqual(self.client.get(url).status_code, 403)
self.assertEqual(self.client.get(reverse('correspondents')).status_code, 200) self.assertEqual(self.client.get(reverse('list-correspondents')).status_code, 403) # c is not staff
self.assertEqual(self.client.get(reverse('inbox')).status_code, 200) self.assertEqual(self.client.get(reverse('inbox')).status_code, 200)
self.client.post(reverse('inbox'), {'content': 'coucou'}) self.client.post(reverse('inbox'), {'content': 'coucou'})
self.client.login(username='d', password='d') self.client.login(username='d', password='d')
self.client.post(url, {'content': 'im superuser'}) self.client.post(url, {'content': 'im superuser'})
self.assertEqual(Message.objects.last().content, 'im superuser') self.assertEqual(Message.objects.last().content, 'im superuser')
self.client.login(username='d', password='d')
self.assertEqual(self.client.get(reverse('list-correspondents')).status_code, 200)
@override_settings(DEFAULT_FROM_EMAIL='noreply@example.org', @override_settings(DEFAULT_FROM_EMAIL='noreply@example.org',

View File

@ -3,10 +3,10 @@ from django.conf.urls import url
from conversations import emails, views from conversations import emails, views
urlpatterns = [ urlpatterns = [
url(r'^recv/$', emails.email_recv), url(r'^recv/$', emails.email_recv), # API
url(r'^inbox/$', views.user_conversation, name='inbox'), url(r'^inbox/$', views.user_conversation, name='inbox'),
url(r'^$', views.correspondents, name='correspondents'), url(r'^$', views.correspondent_list, name='list-correspondents'),
url(r'^with/(?P<username>[\w.@+-]+)/$', views.user_conversation, name='conversation'), url(r'^with/(?P<username>[\w.@+-]+)/$', views.user_conversation, name='user-conversation'),
url(r'^about/(?P<talk>[\w.@+-]+)/$', views.talk_conversation, name='talk-conversation'), url(r'^about/(?P<talk>[\w.@+-]+)/$', views.talk_conversation, name='talk-conversation'),
url(r'^subscribe/(?P<username>[\w.@+-]+)/$', views.subscribe, name='subscribe-conversation'), url(r'^subscribe/(?P<username>[\w.@+-]+)/$', views.subscribe, name='subscribe-conversation'),
url(r'^unsubscribe/(?P<username>[\w.@+-]+)/$', views.unsubscribe, name='unsubscribe-conversation'), url(r'^unsubscribe/(?P<username>[\w.@+-]+)/$', views.unsubscribe, name='unsubscribe-conversation'),

View File

@ -5,6 +5,7 @@ from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import ugettext_lazy as _
from accounts.decorators import staff_required from accounts.decorators import staff_required
from accounts.models import Participation from accounts.models import Participation
@ -36,13 +37,14 @@ def user_conversation(request, username=None):
form.instance.conversation = conversation form.instance.conversation = conversation
form.instance.author = request.user form.instance.author = request.user
form.save() form.save()
messages.success(request, 'Message sent!') messages.success(request, _('Message sent!'))
if username: if username:
return redirect(reverse('conversation', args=[username])) return redirect(reverse('user-conversation', args=[username]))
else: else:
return redirect('inbox') return redirect('inbox')
return render(request, template, { return render(request, template, {
'correspondent': user,
'message_list': message_list, 'message_list': message_list,
'form': form, 'form': form,
}) })
@ -63,13 +65,13 @@ def talk_conversation(request, talk):
return redirect(talk.get_absolute_url()) return redirect(talk.get_absolute_url())
@login_required @staff_required
def correspondents(request): def correspondent_list(request):
correspondent_list = Participation.objects.filter(site=get_current_site(request), correspondent_list = Participation.objects.filter(site=get_current_site(request),
conversation__subscribers=request.user) conversation__subscribers=request.user)
return render(request, 'conversations/correspondents.html', { return render(request, 'conversations/correspondent_list.html', {
'correspondent_list': correspondent_list, 'correspondent_list': correspondent_list,
}) })
@ -81,7 +83,7 @@ def subscribe(request, username):
participation.conversation.subscribers.add(request.user) participation.conversation.subscribers.add(request.user)
messages.success(request, 'Subscribed.') messages.success(request, 'Subscribed.')
next_url = request.GET.get('next') or reverse('conversation', args=[username]) next_url = request.GET.get('next') or reverse('user-conversation', args=[username])
return redirect(next_url) return redirect(next_url)
@ -93,6 +95,6 @@ def unsubscribe(request, username):
participation.conversation.subscribers.remove(request.user) participation.conversation.subscribers.remove(request.user)
messages.success(request, 'Unsubscribed.') messages.success(request, 'Unsubscribed.')
next_url = request.GET.get('next') or reverse('conversation', args=[username]) next_url = request.GET.get('next') or reverse('user-conversation', args=[username])
return redirect(next_url) return redirect(next_url)

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
{% extends 'base.html' %}
{% load i18n %}
{% block planningtab %} active{% endblock %}
{% block content %}
<h1>{% trans "Program" %}</h1>
{{ program }}
{% endblock %}

View File

@ -1,10 +1,14 @@
{% extends '_base.html' %} {% extends '_base.html' %}
{% load i18n %} {% load i18n %}
{% block content %} {% block body %}
<h1>{{ site.name }} {% trans "Program" %}</h1> <div class="container">
<h1>{{ site.name }} {% trans "Schedule" %}</h1>
{{ program }} {{ program }}
</div>
{% endblock %} {% endblock %}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load i18n %} {% load i18n %}
{% block planningtab %} active{% endblock %} {% block roomstab %} class="active"{% endblock %}
{% block content %} {% block content %}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load i18n %} {% load i18n %}
{% block planningtab %} active{% endblock %} {% block roomstab %} class="active"{% endblock %}
{% block css %} {% block css %}
{{ block.super }} {{ block.super }}
@ -11,7 +11,7 @@
{% block content %} {% block content %}
<h1>{% trans "Room" %}</h1> <h1>{% if room %}{% trans "Modify the room" %}{% else %}{% trans "Add a room" %}{% endif %}</h1>
{% include "_form.html" %} {% include "_form.html" %}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 accounts_tags i18n %} {% load bootstrap3 accounts_tags i18n %}
{% block planningtab %} active{% endblock %} {% block roomstab %} class="active"{% endblock %}
{% block content %} {% block content %}
@ -21,7 +21,7 @@
{{ room.capacity }} {% trans "place" %}{{ room.capacity|pluralize }} {{ room.capacity }} {% trans "place" %}{{ room.capacity|pluralize }}
{% if request|staff %} {% if request|staff %}
| |
<span{% if room.unscheduled_talks %} class="text-warning" data-toggle="tooltip" data-placement="bottom" title="{% trans "Some talks are not scheduled yet." %}"{% endif %}> <span{% if room.unscheduled_talks %} class="text-danger" data-toggle="tooltip" data-placement="bottom" title="{% trans "Some talks are not scheduled yet." %}"{% endif %}>
{{ room.talks.count }} {% trans "talk" %}{{ room.talks.count|pluralize }} {{ room.talks.count }} {% trans "talk" %}{{ room.talks.count|pluralize }}
</span> </span>
{% if request|orga %} {% if request|orga %}

View File

@ -0,0 +1,13 @@
{% extends 'staff.html' %}
{% load i18n %}
{% block scheduletab %} class="active"{% endblock %}
{% block content %}
<h1>{% trans "Schedule" %}</h1>
{{ program }}
{% endblock %}

View File

@ -8,6 +8,7 @@ urlpatterns = [
url(r'^room/add/$', views.RoomCreate.as_view(), name='add-room'), url(r'^room/add/$', views.RoomCreate.as_view(), name='add-room'),
url(r'^room/(?P<slug>[-\w]+)/$', views.RoomDetail.as_view(), name='show-room'), url(r'^room/(?P<slug>[-\w]+)/$', views.RoomDetail.as_view(), name='show-room'),
url(r'^room/(?P<slug>[-\w]+)/edit/$', views.RoomUpdate.as_view(), name='edit-room'), url(r'^room/(?P<slug>[-\w]+)/edit/$', views.RoomUpdate.as_view(), name='edit-room'),
url(r'^program/$', views.program_pending, name='pending-program'), url(r'^schedule/$', views.program_pending, name='show-schedule'),
url(r'^program/public/$', views.program_public, name='public-program'), url(r'^schedule/public/$', views.program_public, name='public-schedule'),
url(r'^planning/program/public/$', views.program_public),
] ]

View File

@ -226,7 +226,7 @@ class Program:
if talk.registration_required and self.conference.subscriptions_open: if talk.registration_required and self.conference.subscriptions_open:
links += mark_safe(""" links += mark_safe("""
<link tag="registration">%(link)s</link>""" % { <link tag="registration">%(link)s</link>""" % {
'link': reverse('subscribe-to-talk', args=[talk.slug]), 'link': reverse('register-for-a-talk', args=[talk.slug]),
}) })
registration = """ registration = """
<attendees_max>%(max)s</attendees_max> <attendees_max>%(max)s</attendees_max>

View File

@ -41,7 +41,7 @@ class RoomDetail(StaffRequiredMixin, RoomMixin, DetailView):
@staff_required @staff_required
def program_pending(request): def program_pending(request):
output = request.GET.get('format', 'html') output = request.GET.get('format', 'html')
return program(request, pending=True, output=output, html_template='pending-program.html', cache=False) return program(request, pending=True, output=output, html_template='schedule.html', cache=False)
def program_public(request): def program_public(request):
@ -52,7 +52,7 @@ def program_public(request):
def program(request, pending=False, output='html', html_template='public-program.html', cache=True): def program(request, pending=False, output='html', html_template='public-program.html', cache=True):
program = Program(site=get_current_site(request), pending=pending, cache=cache) program = Program(site=get_current_site(request), pending=pending, cache=cache)
if output == 'html': if output == 'html':
return render(request, 'planning/' + html_template, {'program': program}) return render(request, 'planning/' + html_template, {'program': program.render('html')})
elif output == 'xml': elif output == 'xml':
return HttpResponse(program.render('xml'), content_type="application/xml") return HttpResponse(program.render('xml'), content_type="application/xml")
elif output == 'ics': elif output == 'ics':

View File

@ -1,3 +1,3 @@
tr.clickable-row { .subnav {
cursor: pointer; padding-bottom: 20px;
} }

View File

@ -5,16 +5,8 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="shortcut icon" type="image/png" href="{% static 'img/favicon.ico' %}"/>
{% comment %}<link rel="icon" href="{% static 'favicon.ico' %}">{% endcomment %}
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> {% comment %}<link rel="icon" href="{% static 'favicon.ico' %}">{% endcomment %}
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<title>{% block title %}{{ site.name }}{% endblock %}</title> <title>{% block title %}{{ site.name }}{% endblock %}</title>
@ -23,31 +15,16 @@
{% block css %}{% endblock %} {% block css %}{% endblock %}
{% block js %}{% endblock %} {% block js %}{% endblock %}
</head>
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body> <body>
{% block pageheader %} {% block body %}{% endblock %}
{% endblock %}
<div class="container">
<div class="row">
<div class="col-md-12">
{% bootstrap_messages %}
{% block content %}{% endblock %}
</div> <!-- /col -->
</div> <!-- /row -->
{% block pagefooter %}
{% endblock %}
</div> <!-- /container -->
<script src="{% bootstrap_jquery_url %}"></script> <script src="{% bootstrap_jquery_url %}"></script>
{% bootstrap_javascript %} {% bootstrap_javascript %}

View File

@ -1,105 +1,62 @@
{% extends '_base.html' %} {% extends '_base.html' %}
{% load accounts_tags i18n %} {% load accounts_tags i18n bootstrap3 %}
{% block pageheader %} {% block body %}
<!-- Static navbar -->
<div class="navbar navbar-default navbar-static-top" role="navigation"> {% block navbar %}
<nav class="navbar navbar-inverse" role="navigation">
<div class="container"> <div class="container">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span> <span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="#">{{ site.name }}</a> <a class="navbar-brand" href="{% url 'home' %}">{{ site.name }}</a>
</div> </div>
<div class="navbar-collapse collapse"> <div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li{% block hometab %}{% endblock %}><a href="{% url 'home' %}">{% trans "Home" %}</a></li> <li{% block hometab %}{% endblock %}><a href="{% url 'home' %}"><span class="glyphicon glyphicon-home"></span>&nbsp;{% trans "Home" %}</a></li>
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<li class="dropdown{% block participatetab %}{% endblock %}"> <li{% block exhibitortab %}{% endblock %}><a href="{% url 'participate-as-speaker' %}"><span class="glyphicon glyphicon-bullhorn"></span>&nbsp;{% trans "Exhibitor" %}</a></li>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-th"></span>&nbsp;{% trans "Participate" %}&nbsp;<span class="caret"></span></a> <li{% block volunteertab %}{% endblock %}><a href="{% url 'enrole-as-volunteer' %}"><span class="glyphicon glyphicon-thumbs-up"></span>&nbsp;{% trans "Volunteer" %}</a></li>
<ul class="dropdown-menu" role="menu">
<li role="presentation">
<a role="menuitem" tabindex="-1" href="{% url 'participate-as-speaker' %}"><span class="glyphicon glyphicon-bullhorn"></span>&nbsp;{% trans "Propose an intervention" %}</a>
</li>
<li role="presentation">
<a role="menuitem" tabindex="-1" href="{% url 'participate-as-volunteer' %}"><span class="glyphicon glyphicon-thumbs-up"></span>&nbsp;{% trans "Enrole as volunteer" %}</a>
</li>
</ul>
</li>
{% endif %} {% endif %}
{% if conference.subscriptions_open %} <li{% block wsregtab %}{% endblock %}><a href="{% url 'list-registrable-talks' %}"><span class="glyphicon glyphicon-edit"></span>&nbsp;{% trans "Workshop registration" %}</a></li>
<li{% block subscriptiontab %}{% endblock %}><a href="{% url 'subscriptions-list' %}"><span class="glyphicon glyphicon-check"></span>&nbsp;{% trans "Subscribe to a workshop" %}</a></li>
{% endif %}
{% if request.user.is_authenticated %}
{% if not request|staff %}
<li{% block topictab %}{% endblock %}><a href="{% url 'list-topics' %}">{% trans "Topics" %}</a></li>
{% endif %}
{% endif %}
{% block navbar-left %}{% endblock %}
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
{% if request|staff %} {% if request|staff %}
<li class="dropdown{% block listingtab %}{% endblock %}"> <li{% block stafftab %}{% endblock %}><a href="{% url 'staff' %}"><span class="glyphicon glyphicon-cog"></span>&nbsp;{% trans "Staff" %}</a></li>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-th-list"></span>&nbsp;{% trans "Listing" %}&nbsp;<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li role="presentation">
<a role="menuitem" tabindex="-1" href="{% url 'list-talks' %}"><span class="glyphicon glyphicon-bullhorn"></span>&nbsp;{% trans "Talks" %}</a>
</li>
<li role="presentation">
<a role="menuitem" tabindex="-1" href="{% url 'list-speakers' %}"><span class="glyphicon glyphicon-user"></span>&nbsp;{% trans "Speakers" %}</a>
</li>
<li role="presentation">
<a role="menuitem" tabindex="-1" href="{% url 'list-volunteers' %}"><span class="glyphicon glyphicon-thumbs-up"></span>&nbsp;{% trans "Volunteers" %}</a>
</li>
<li role="presentation">
<a role="menuitem" tabindex="-1" href="{% url 'list-topics' %}"><span class="glyphicon glyphicon-tag"></span>&nbsp;{% trans "Topics" %}</a></li>
</li>
<li role="presentation">
<a role="menuitem" tabindex="-1" href="{% url 'list-tracks' %}"><span class="glyphicon glyphicon-screenshot"></span>&nbsp;{% trans "Tracks" %}</a></li>
</li>
<li role="presentation">
<a role="menuitem" tabindex="-1" href="{% url 'list-rooms' %}"><span class="glyphicon glyphicon-tent"></span>&nbsp;{% trans "Rooms" %}</a>
</li>
</ul>
</li>
<li{% block planningtab %}{% endblock %}><a href="{% url 'pending-program' %}"><span class="glyphicon glyphicon-calendar"></span>&nbsp;{% trans "Program" %}</a></li>
<li class="dropdown{% block admintab %}{% endblock %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-cog"></span>&nbsp;{% trans "Administration" %}&nbsp;<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li role="presentation">
{% if request.user.is_staff %}
<a role="menuitem" tabindex="-1" href="{% url 'admin:index' %}"><span class="glyphicon glyphicon-dashboard"></span>&nbsp;Django Admin</a>
{% endif %}
{% if request|orga %}
<a role="menuitem" tabindex="-1" href="{% url 'conference' %}"><span class="glyphicon glyphicon-blackboard"></span>&nbsp;{% trans "Conference" %}</a>
{% endif %}
<a role="menuitem" tabindex="-1" href="{% url 'list-participant' %}"><span class="glyphicon glyphicon-user"></span>&nbsp;{% trans "Participants" %}</a>
<a role="menuitem" tabindex="-1" href="{% url 'correspondents' %}"><span class="glyphicon glyphicon-envelope"></span>&nbsp;{% trans "Correspondence" %}</a>
</li>
</ul>
</li>
{% endif %} {% endif %}
<li{% block inboxtab %}{% endblock %}><a href="{% url 'inbox' %}" data-toggle="tooltip" data-placement="bottom" title="Inbox"><span class="glyphicon glyphicon-envelope"></span>&nbsp;Inbox</a></li> <li{% block inboxtab %}{% endblock %}><a href="{% url 'inbox' %}"><span class="glyphicon glyphicon-envelope"></span>&nbsp;Inbox</a></li>
<li{% block profiletab %}{% endblock %}><a href="{% url 'profile' %}" data-toggle="tooltip" data-placement="bottom" title="Profile"><span class="glyphicon glyphicon-user"></span>&nbsp;{{ request.user.username }}</a></li> <li{% block profiletab %}{% endblock %}><a href="{% url 'profile' %}"><span class="glyphicon glyphicon-user"></span>&nbsp;{% trans "Profile" %}</a></li>
<li><a href="{% url 'logout' %}" data-toggle="tooltip" data-placement="bottom" title="Logout"><span class="glyphicon glyphicon-log-out"></span></a></li> <li><a href="{% url 'logout' %}" data-toggle="tooltip" data-placement="bottom" title="Logout"><span class="glyphicon glyphicon-log-out"></span></a></li>
{% else %} {% else %}
<li{% block registrationtab %}{% endblock %}><a href="{% url 'registration_register' %}"><span class="glyphicon glyphicon-edit"></span>&nbsp;{% trans "Register" %}</a></li> <li{% block registrationtab %}{% endblock %}><a href="{% url 'registration_register' %}"><span class="glyphicon glyphicon-edit"></span>&nbsp;{% trans "Register" %}</a></li>
<li{% block logintab %}{% endblock %}><a href="{% url 'login' %}"><span class="glyphicon glyphicon-log-in"></span>&nbsp;{% trans "Login" %}</a></li> <li{% block logintab %}{% endblock %}><a href="{% url 'login' %}"><span class="glyphicon glyphicon-log-in"></span>&nbsp;{% trans "Login" %}</a></li>
{% endif %} {% endif %}
</ul> </ul>
{% block navbar-right %}{% endblock %} </div>
</div><!--/.nav-collapse --> </div>
</div><!--/.container-fluid --> </nav>
</div>
{% endblock %} {% endblock %}
{% block container %}
<div class="container">
<div class="row">
<div id="content" class="col-md-12">
{% bootstrap_messages %}
{% block content %}{% endblock %}
{% block pagefooter %} {% block pagefooter %}
<hr> <hr>
<footer> <footer>
<p class="text-muted">{% trans "Powered by" %} <a href="https://github.com/toulibre/ponyconf">PonyConf</a></p> <p class="text-muted">{% trans "Powered by" %} <a href="https://github.com/toulibre/ponyconf">PonyConf</a></p>
</footer> </footer>
{% endblock %}
</div>
</div>
</div>
{% endblock %}
{% endblock %} {% endblock %}

View File

@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% load staticfiles i18n %}
{% block stafftab %} class="active"{% endblock %}
{% block navbar %}
{{ block.super }}
<ul class="nav nav-tabs nav-justified subnav">
<li{% block topicstab %}{% endblock %}><a href="{% url 'list-topics' %}"><span class="glyphicon glyphicon-tag"></span>&nbsp;{% trans "Topics" %}</a></li>
<li{% block trackstab %}{% endblock %}><a href="{% url 'list-tracks' %}"><span class="glyphicon glyphicon-screenshot"></span>&nbsp;{% trans "Tracks" %}</a></li>
<li{% block talkstab %}{% endblock %}><a href="{% url 'list-talks' %}"><span class="glyphicon glyphicon-blackboard"></span>&nbsp;{% trans "Talks" %}</a></li>
<li{% block speakerstab %}{% endblock %}><a href="{% url 'list-speakers' %}"><span class="glyphicon glyphicon-bullhorn"></span>&nbsp;{% trans "Speakers" %}</a></li>
<li{% block volunteerstab %}{% endblock %}><a href="{% url 'list-volunteers' %}"><span class="glyphicon glyphicon-thumbs-up"></span>&nbsp;{% trans "Volunteers" %}</a></li>
<li{% block roomstab %}{% endblock %}><a href="{% url 'list-rooms' %}"><span class="glyphicon glyphicon-tent"></span>&nbsp;{% trans "Rooms" %}</a></li>
<li{% block scheduletab %}{% endblock %}><a href="{% url 'show-schedule' %}"><span class="glyphicon glyphicon-calendar"></span>&nbsp;{% trans "Schedule" %}</a></li>
<li{% block participantstab %}{% endblock %}><a href="{% url 'list-participants' %}"><span class="glyphicon glyphicon-user"></span>&nbsp;{% trans "Participants" %}</a></li>
<li{% block correspondentstab %}{% endblock %}><a href="{% url 'list-correspondents' %}"><span class="glyphicon glyphicon-envelope"></span>&nbsp;{% trans "Correspondents" %}</a></li>
<li{% block conferencetab %}{% endblock %}><a href="{% url 'edit-conference' %}"><span class="glyphicon glyphicon-cog"></span>&nbsp;{% trans "Conference" %}</a></li>
<li><a href="{% url 'admin:index' %}"><span class="glyphicon glyphicon-dashboard"></span>&nbsp;Django-Admin</a></li>
</ul>
{% endblock %}

View File

@ -22,8 +22,8 @@ urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^accounts/', include('accounts.urls')), url(r'^accounts/', include('accounts.urls')),
url(r'', include('proposals.urls')), url(r'', include('proposals.urls')),
url(r'^conversations/', include('conversations.urls')), url(r'', include('planning.urls')),
url(r'^planning/', include('planning.urls')),
url(r'^volunteers/', include('volunteers.urls')), url(r'^volunteers/', include('volunteers.urls')),
url(r'^conversations/', include('conversations.urls')),
url(r'^select2/', include('django_select2.urls')), url(r'^select2/', include('django_select2.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -24,10 +24,18 @@ STATUS_VALUES = [
class TalkForm(forms.ModelForm): class TalkForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
site = kwargs.pop('site') site = kwargs.pop('site')
staff = kwargs.pop('staff')
super(TalkForm, self).__init__(*args, **kwargs) super(TalkForm, self).__init__(*args, **kwargs)
self.fields['topics'].queryset = Topic.objects.filter(site=site) self.fields['topics'].queryset = Topic.objects.filter(site=site)
self.fields['track'].queryset = Track.objects.filter(site=site) self.fields['track'].queryset = Track.objects.filter(site=site)
self.fields['event'].queryset = Event.objects.filter(site=site) if staff:
self.fields['event'].queryset = Event.objects.filter(site=site)
else:
self.fields['event'].queryset = Conference.objects.get(site=site).opened_events
for field in ['track', 'duration', 'start_date', 'room', 'registration_required', 'attendees_limit']:
self.fields.pop(field)
if self.instance.pk is not None:
self.fields['title'].disabled = True
class Meta: class Meta:
model = Talk model = Talk
@ -192,9 +200,7 @@ class SubscribeForm(forms.Form):
ConferenceForm = modelform_factory(Conference, ConferenceForm = modelform_factory(Conference,
fields=['cfp_opening_date', 'cfp_closing_date', 'subscriptions_open', 'venue', 'city', 'home'], fields=['subscriptions_open', 'venue', 'city', 'home'],
widgets={ widgets={
'cfp_opening_date': forms.TextInput(),
'cfp_closing_date': forms.TextInput(),
'venue': forms.Textarea(attrs={'rows': 4}), 'venue': forms.Textarea(attrs={'rows': 4}),
}) })

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-11-13 22:04
from __future__ import unicode_literals
from django.db import migrations, models
def migrate_opening_closing_dates(apps, schema_editor):
db_alias = schema_editor.connection.alias
Conference = apps.get_model('proposals', 'Conference')
Event = apps.get_model('proposals', 'Event')
for conf in Conference.objects.all():
for event in Event.objects.filter(site=conf.site).all():
event.opening_date = conf.cfp_opening_date
event.closing_date = conf.cfp_closing_date
event.save()
class Migration(migrations.Migration):
dependencies = [
('proposals', '0027_conference_subscriptions_open'),
]
operations = [
migrations.AddField(
model_name='event',
name='closing_date',
field=models.DateTimeField(blank=True, default=None, null=True),
),
migrations.AddField(
model_name='event',
name='opening_date',
field=models.DateTimeField(blank=True, default=None, null=True),
),
migrations.RunPython(migrate_opening_closing_dates),
migrations.RemoveField(
model_name='conference',
name='cfp_closing_date',
),
migrations.RemoveField(
model_name='conference',
name='cfp_opening_date',
),
]

View File

@ -6,6 +6,7 @@ from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models from django.db import models
from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext from django.utils.translation import ugettext
from django.utils import timezone from django.utils import timezone
@ -26,17 +27,18 @@ class Conference(models.Model):
home = models.TextField(blank=True, default="") home = models.TextField(blank=True, default="")
venue = models.TextField(blank=True, default="") venue = models.TextField(blank=True, default="")
city = models.CharField(max_length=64, blank=True, default="") city = models.CharField(max_length=64, blank=True, default="")
cfp_opening_date = models.DateTimeField(null=True, blank=True, default=None) subscriptions_open = models.BooleanField(default=False) # workshop subscription
cfp_closing_date = models.DateTimeField(null=True, blank=True, default=None)
subscriptions_open = models.BooleanField(default=False)
def cfp_is_open(self): def cfp_is_open(self):
events = Event.objects.filter(site=self.site)
return any(map(lambda x: x.is_open(), events))
@property
def opened_events(self):
now = timezone.now() now = timezone.now()
if self.cfp_opening_date and now < self.cfp_opening_date: return Event.objects.filter(site=self.site)\
return False .filter(Q(opening_date__isnull=True) | Q(opening_date__lte=now))\
if self.cfp_closing_date and now > self.cfp_closing_date: .filter(Q(closing_date__isnull=True) | Q(closing_date__gte=now))
return False
return True
def __str__(self): def __str__(self):
return str(self.site) return str(self.site)
@ -93,6 +95,16 @@ class Event(models.Model):
duration = models.PositiveIntegerField(default=0, verbose_name=_('Default duration (min)')) duration = models.PositiveIntegerField(default=0, verbose_name=_('Default duration (min)'))
color = RGBColorField(default='#ffffff', verbose_name=_("Color on program")) color = RGBColorField(default='#ffffff', verbose_name=_("Color on program"))
label = models.CharField(max_length=64, verbose_name=_("Label on program"), blank=True, default="") label = models.CharField(max_length=64, verbose_name=_("Label on program"), blank=True, default="")
opening_date = models.DateTimeField(null=True, blank=True, default=None)
closing_date = models.DateTimeField(null=True, blank=True, default=None)
def is_open(self):
now = timezone.now()
if self.opening_date and now < self.opening_date:
return False
if self.closing_date and now > self.closing_date:
return False
return True
class Meta: class Meta:
unique_together = ('site', 'name') unique_together = ('site', 'name')

View File

@ -7,7 +7,7 @@
{{ talk.get_link }} {{ talk.get_link }}
<i>{% trans "by" %}</i> <i>{% trans "by" %}</i>
{% for speaker in talk.speakers.all %} {% for speaker in talk.speakers.all %}
<a href="{% url 'show-speaker' speaker.username %}">{{ speaker }}</a> <a href="{% url 'show-participant' speaker.username %}">{{ speaker }}</a>
{% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %} {% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %}
{% endfor %} {% endfor %}
{% if talk.topics.exists %} {% if talk.topics.exists %}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 staticfiles i18n %} {% load bootstrap3 staticfiles i18n %}
{% block admintab %} class=active{% endblock %} {% block conferencetab %} class=active{% endblock %}
{% block content %} {% block content %}
@ -13,9 +13,7 @@
<div class="panel-body"> <div class="panel-body">
<form method="post" role="form"> <form method="post" role="form">
{% csrf_token %} {% csrf_token %}
{% bootstrap_field form.cfp_opening_date addon_after='<span class="glyphicon glyphicon-calendar"></span>' %} {% bootstrap_form form exclude='home' %}
{% bootstrap_field form.cfp_closing_date addon_after='<span class="glyphicon glyphicon-calendar"></span>' %}
{% bootstrap_form form exclude='cfp_opening_date,cfp_closing_date,home' %}
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li class="active"><a href="#editor" role="tab" data-toggle="tab">Editor</a></li> <li class="active"><a href="#editor" role="tab" data-toggle="tab">Editor</a></li>
<li><a href="#preview" role="tab" data-toggle="tab">Preview</a></li> <li><a href="#preview" role="tab" data-toggle="tab">Preview</a></li>
@ -43,7 +41,6 @@
{% block css %} {% block css %}
{{ block.super }} {{ block.super }}
{{ form.media.css }} {{ form.media.css }}
<link rel="stylesheet" href="{% static 'eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css' %}" />
{% endblock css %} {% endblock css %}
{% block js_end %} {% block js_end %}
@ -52,28 +49,5 @@
<script type="text/javascript"> <script type="text/javascript">
var markdown_preview_url = "{% url 'markdown' %}"; var markdown_preview_url = "{% url 'markdown' %}";
</script> </script>
<script src="{% static 'moment/min/moment.min.js' %}"></script>
<script src="{% static 'eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js' %}"></script>
<script src="{% static 'jquery.cookie/jquery.cookie.js' %}"></script>
<script src="{% static 'js/markdown-preview.js' %}"></script> <script src="{% static 'js/markdown-preview.js' %}"></script>
<script type="text/javascript">
$(function() {
$("#id_cfp_opening_date").datetimepicker({
"format": "YYYY-MM-DD HH:mm",
"widgetPositioning": {
"horizontal": "right",
"vertical": "auto"
},
"showClear": true
});
$("#id_cfp_closing_date").datetimepicker({
"format": "YYYY-MM-DD HH:mm",
"widgetPositioning": {
"horizontal": "right",
"vertical": "auto"
},
"showClear": true
});
});
</script>
{% endblock js_end %} {% endblock js_end %}

View File

@ -2,7 +2,7 @@
{% load accounts_tags i18n %} {% load accounts_tags i18n %}
{% block participatetab %} class="active"{% endblock %} {% block exhibitortab %} class="active"{% endblock %}
{% block content %} {% block content %}
@ -19,8 +19,15 @@
{% endif %} {% endif %}
<br /> <br />
{% if conf.cfp_is_open or request|orga %} {% if conference.cfp_is_open %}
<a class="btn btn-{% if conf.cfp_is_open %}success{% else %}danger{% endif %}" href="{% url 'add-talk' %}">{% trans "Propose a talk" %}</a> {% trans "The Call for Participation is currently open for following categories:" %}
{% for event in conference.opened_events.all %}
{% if forloop.first %}<ul>{% endif %}
<li>{{ event }}{% if event.closing_date %} ({% blocktrans with closing_date=event.closing_date|date:"DATETIME_FORMAT" %}until {{ closing_date }}{% endblocktrans %}){% endif %}</li>
{% if forloop.last %}</ul>{% endif %}
{% endfor %}
<br />
<a class="btn btn-{% if conference.cfp_is_open %}success{% else %}danger{% endif %}" href="{% url 'add-talk' %}">{% trans "Propose a talk" %}</a>
{% else %} {% else %}
{% trans "Sorry, the Call for Participation is closed." %} {% trans "Sorry, the Call for Participation is closed." %}
{% endif %} {% endif %}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 i18n %} {% load bootstrap3 i18n %}
{% block listingtab %} active{% endblock %} {% block speakerstab %} class="active"{% endblock %}
{% block content %} {% block content %}
@ -56,15 +56,15 @@
</thead> </thead>
<tfoot> <tfoot>
<tr> <tr>
<td colspan="7">{% trans "Contact:" %}<a href="{{ contact_link }}">lien</a></td> <td colspan="7">{% trans "Contact:" %} <a href="{{ contact_link }}">{% trans "link" %}</a></td>
</tr> </tr>
</tfoot> </tfoot>
{% for speaker in speaker_list %} {% for speaker in speaker_list %}
{% if forloop.first %} {% if forloop.first %}
<tbody> <tbody>
{% endif %} {% endif %}
<tr class="clickable-row" data-href="{% url 'show-speaker' username=speaker.user.username %}"> <tr>
<td>{{ speaker.user.username }}</td> <td><a href="{% url 'show-participant' username=speaker.user.username %}">{{ speaker.user.username }}</a></td>
<td>{{ speaker.user.get_full_name }}</td> <td>{{ speaker.user.get_full_name }}</td>
<td class="text-right">{{ speaker.not_refused_talk_set.count }}{% if speaker.pending_talk_set.count %} ({{ speaker.pending_talk_set.count }} pending){% endif %}</td> <td class="text-right">{{ speaker.not_refused_talk_set.count }}{% if speaker.pending_talk_set.count %} ({{ speaker.pending_talk_set.count }} pending){% endif %}</td>
{% if speaker.need_transport %} {% if speaker.need_transport %}
@ -94,10 +94,9 @@
<td>No</td> <td>No</td>
{% endif %} {% endif %}
<td> <td>
<a class="btn btn-{% if speaker.conversation.messages.last.author == speaker.user %}primary{% else %}default{% endif %}" href="{% url 'conversation' speaker.user.username %}">{% trans "Contact" %}</a> <a class="btn btn-{% if speaker.conversation.messages.last.author == speaker.user %}primary{% else %}default{% endif %}" href="{% url 'user-conversation' speaker.user.username %}">{% trans "Contact" %}</a>
</td> </td>
</tr> </tr>
</li>
{% if forloop.last %} {% if forloop.last %}
</tbody> </tbody>
{% endif %} {% endif %}
@ -109,10 +108,6 @@
{% block js_end %} {% block js_end %}
<script type="text/javascript"> <script type="text/javascript">
jQuery(document).ready(function($) { jQuery(document).ready(function($) {
$(".clickable-row").click(function() {
window.location = $(this).data("href");
});
var anchor = window.location.hash.replace("#", ""); var anchor = window.location.hash.replace("#", "");
if (anchor == "filter") { if (anchor == "filter") {
$("#filter").collapse('show'); $("#filter").collapse('show');

View File

@ -1,9 +1,13 @@
{% extends 'base.html' %} {% extends base_template %}
{% if staff %}
{% block talkstab %} class="active"{% endblock %}
{% else %}
{% block exhibitortab %} class="active"{% endblock %}
{% endif %}
{% load i18n %} {% load i18n %}
{% block listingtab %} active{% endblock %}
{% block content %} {% block content %}
<h1>{{ talk.title }}</h1> <h1>{{ talk.title }}</h1>
@ -65,7 +69,7 @@
{% for speaker in talk.speakers.all %} {% for speaker in talk.speakers.all %}
{% if forloop.first %}<ul>{% endif %} {% if forloop.first %}<ul>{% endif %}
<li><a href="{% url 'show-speaker' speaker.username %}">{{ speaker.profile }}</a></li> <li><a href="{% url 'show-participant' speaker.username %}">{{ speaker.profile }}</a></li>
{% if forloop.last %}</ul>{% endif %} {% if forloop.last %}</ul>{% endif %}
{% empty %} {% empty %}
<i>{% trans "No speakers." %}</i> <i>{% trans "No speakers." %}</i>

View File

@ -1,12 +1,13 @@
{% extends 'base.html' %} {% extends base_template %}
{% load bootstrap3 staticfiles i18n %} {% load i18n %}
{% block listingtab %} active{% endblock %} {% block talkstab %}{% if base_template == 'staff.html' %} class="active"{% endif %}{% endblock %}
{% block exhibitortab %}{% if base_template == 'base.html' %} class="active"{% endif %}{% endblock %}
{% block content %} {% block content %}
<h1>{% trans "Propose a talk" %}</h1> <h1>{% if talk %}{% trans "Edit a talk" %}{% else %}{% trans "Propose a talk" %}{% endif %}</h1>
{% include "_form.html" %} {% include "_form.html" %}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 i18n accounts_tags %} {% load bootstrap3 i18n accounts_tags %}
{% block listingtab %} active{% endblock %} {% block talkstab %} class="active"{% endblock %}
{% block content %} {% block content %}
@ -60,7 +60,7 @@
<td>{{ talk.event }}</td> <td>{{ talk.event }}</td>
<td> <td>
{% for speaker in talk.speakers.all %} {% for speaker in talk.speakers.all %}
<a href="{% url 'show-speaker' speaker.username %}">{{ speaker.profile }}</a> <a href="{% url 'show-participant' speaker.username %}">{{ speaker.profile }}</a>
{% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %} {% if forloop.revcounter == 2 %} {% trans "and" %} {% elif not forloop.last %}, {% endif %}
{% empty %} {% empty %}
{% endfor %} {% endfor %}

View File

@ -2,7 +2,7 @@
{% load bootstrap3 i18n %} {% load bootstrap3 i18n %}
{% block subscriptiontab %} class="active"{% endblock %} {% block wsregtab %} class="active"{% endblock %}
{% block css %} {% block css %}
{{ block.super }} {{ block.super }}
@ -11,12 +11,14 @@
{% block content %} {% block content %}
<h1>{% trans "Subscribe to a workshop" %}<br /><small>{{ talk.title }}</small></h1> <h1>{% trans "Register for a workshop" %}<br /><small>{{ talk.title }}</small></h1>
<hr>
{% include "_form.html" %} {% include "_form.html" %}
<p class="text-center"> <p class="text-center">
<a href="{% url "login" %}?next={% url "subscribe-to-talk" talk.slug %}">{% trans "Login to register with you're account" %}</a> <a href="{% url "login" %}?next={% url "register-for-a-talk" talk.slug %}">{% trans "Login to register with your account" %}</a>
</p> </p>
{% endblock %} {% endblock %}

View File

@ -2,11 +2,11 @@
{% load accounts_tags i18n %} {% load accounts_tags i18n %}
{% block subscriptiontab %} class="active"{% endblock %} {% block wsregtab %} class="active"{% endblock %}
{% block content %} {% block content %}
<h1>{% trans "Subscribe to a workshop" %}</h1> <h1>{% trans "Register to a workshop" %}</h1>
{% for talk in talks %} {% for talk in talks %}
{% if forloop.first %}<div class="list-group">{% endif %} {% if forloop.first %}<div class="list-group">{% endif %}
@ -20,9 +20,9 @@
{% if talk.remaining_attendees != 0 or attendee in talk.attendees.all %} {% if talk.remaining_attendees != 0 or attendee in talk.attendees.all %}
<p> <p>
{% if attendee in talk.attendees.all %} {% if attendee in talk.attendees.all %}
<a class="btn btn-danger" href="{% url 'subscribe-to-talk' talk=talk.slug %}">{% trans "Unregister" %}</a> <a class="btn btn-danger" href="{% url 'register-for-a-talk' talk=talk.slug %}">{% trans "Unregister" %}</a>
{% else %} {% else %}
<a class="btn btn-primary" href="{% url 'subscribe-to-talk' talk=talk.slug %}">{% trans "Register" %}</a> <a class="btn btn-primary" href="{% url 'register-for-a-talk' talk=talk.slug %}">{% trans "Register" %}</a>
{% endif %} {% endif %}
</p> </p>
{% endif %} {% endif %}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 i18n %} {% load bootstrap3 i18n %}
{% block topictab %} class="active"{% endblock %} {% block topicstab %} class="active"{% endblock %}
{% block css %} {% block css %}
{{ block.super }} {{ block.super }}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 accounts_tags i18n %} {% load bootstrap3 accounts_tags i18n %}
{% block topictab %} class="active"{% endblock %} {% block topicstab %} class="active"{% endblock %}
{% block content %} {% block content %}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 i18n %} {% load bootstrap3 i18n %}
{% block tracktab %} class="active"{% endblock %} {% block trackstab %} class="active"{% endblock %}
{% block css %} {% block css %}
{{ block.super }} {{ block.super }}

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 accounts_tags proposals_tags i18n %} {% load bootstrap3 accounts_tags proposals_tags i18n %}
{% block tracktab %} class="active"{% endblock %} {% block trackstab %} class="active"{% endblock %}
{% block content %} {% block content %}

View File

@ -47,7 +47,7 @@ class ProposalsTests(TestCase):
self.assertEqual(self.client.get(reverse('list-speakers')).status_code, 403) self.assertEqual(self.client.get(reverse('list-speakers')).status_code, 403)
self.assertEqual(self.client.get(reverse('edit-talk', kwargs={'talk': talk.slug})).status_code, 200) self.assertEqual(self.client.get(reverse('edit-talk', kwargs={'talk': talk.slug})).status_code, 200)
self.assertEqual(self.client.get(reverse('show-talk', kwargs={'slug': talk.slug})).status_code, 200) self.assertEqual(self.client.get(reverse('show-talk', kwargs={'slug': talk.slug})).status_code, 200)
self.assertEqual(self.client.get(reverse('show-speaker', kwargs={'username': 'a'})).status_code, 200) self.assertEqual(self.client.get(reverse('show-participant', kwargs={'username': 'a'})).status_code, 200)
self.client.login(username='b', password='b') self.client.login(username='b', password='b')
self.assertEqual(self.client.post(reverse('edit-talk', kwargs={'talk': 'super-talk'}), self.assertEqual(self.client.post(reverse('edit-talk', kwargs={'talk': 'super-talk'}),

View File

@ -5,7 +5,8 @@ from proposals import views
urlpatterns = [ urlpatterns = [
url(r'^markdown/$', views.markdown_preview, name='markdown'), url(r'^markdown/$', views.markdown_preview, name='markdown'),
url(r'^$', views.home, name='home'), url(r'^$', views.home, name='home'),
url(r'^conference/$', views.conference, name='conference'), url(r'^staff/$', views.staff, name='staff'),
url(r'^conference/$', views.conference, name='edit-conference'),
url(r'^talk/propose/$', views.participate, name='participate-as-speaker'), url(r'^talk/propose/$', views.participate, name='participate-as-speaker'),
url(r'^talk/$', views.talk_list, name='list-talks'), url(r'^talk/$', views.talk_list, name='list-talks'),
url(r'^talk/add/$', views.talk_edit, name='add-talk'), url(r'^talk/add/$', views.talk_edit, name='add-talk'),
@ -22,7 +23,6 @@ urlpatterns = [
url(r'^track/add/$', views.TrackCreate.as_view(), name='add-track'), url(r'^track/add/$', views.TrackCreate.as_view(), name='add-track'),
url(r'^track/(?P<slug>[-\w]+)/edit/$', views.TrackUpdate.as_view(), name='edit-track'), url(r'^track/(?P<slug>[-\w]+)/edit/$', views.TrackUpdate.as_view(), name='edit-track'),
url(r'^speakers/$', views.speaker_list, name='list-speakers'), url(r'^speakers/$', views.speaker_list, name='list-speakers'),
url(r'^speaker/(?P<username>[\w.@+-]+)$', views.user_details, name='show-speaker'), url(r'^register/$', views.talk_registrable_list, name='list-registrable-talks'),
url(r'^subscribe/$', views.talk_subscriptions, name='subscriptions-list'), url(r'^register/(?P<talk>[-\w]+)$', views.talk_register, name='register-for-a-talk'),
url(r'^subscribe/(?P<talk>[-\w]+)$', views.talk_subscribe, name='subscribe-to-talk'),
] ]

View File

@ -29,7 +29,7 @@ from planning.models import Room
from .forms import TalkForm, TopicForm, TrackForm, ConferenceForm, TalkFilterForm, STATUS_VALUES, SpeakerFilterForm, TalkActionForm, SubscribeForm from .forms import TalkForm, TopicForm, TrackForm, ConferenceForm, TalkFilterForm, STATUS_VALUES, SpeakerFilterForm, TalkActionForm, SubscribeForm
from .models import Talk, Track, Topic, Vote, Conference, Attendee from .models import Talk, Track, Topic, Vote, Conference, Attendee
from .signals import talk_added, talk_edited from .signals import talk_added, talk_edited
from .utils import allowed_talks, markdown_to_html from .utils import markdown_to_html
@login_required @login_required
@ -43,6 +43,11 @@ def home(request):
return render(request, 'proposals/home.html') return render(request, 'proposals/home.html')
@staff_required
def staff(request):
return render(request, 'staff.html')
@orga_required @orga_required
def conference(request): def conference(request):
conference = Conference.objects.get(site=get_current_site(request)) conference = Conference.objects.get(site=get_current_site(request))
@ -50,7 +55,7 @@ def conference(request):
if request.method == 'POST' and form.is_valid(): if request.method == 'POST' and form.is_valid():
form.save() form.save()
messages.success(request, 'Conference updated!') messages.success(request, 'Conference updated!')
return redirect(reverse('conference')) return redirect(reverse('edit-conference'))
return render(request, 'proposals/conference.html', { return render(request, 'proposals/conference.html', {
'form': form, 'form': form,
}) })
@ -185,34 +190,19 @@ def talk_edit(request, talk=None):
conf = Conference.objects.get(site=site) conf = Conference.objects.get(site=site)
if not is_orga(request, request.user) and not conf.cfp_is_open(): if not is_orga(request, request.user) and not conf.cfp_is_open():
raise PermissionDenied raise PermissionDenied
form = TalkForm(request.POST or None, instance=talk, site=site) staff = talk.is_moderable_by(request.user) if talk else is_orga(request, request.user)
form = TalkForm(request.POST or None, instance=talk, site=site, staff=staff)
if talk: if talk:
form.fields['topics'].disabled = True form.fields['topics'].disabled = True
if talk.event.duration: if 'duration' in form.fields and talk.event.duration:
form.fields['duration'].help_text = 'Default value if zero: %d min' % talk.duration form.fields['duration'].help_text = 'Default value if zero: %d min' % talk.duration
if not talk.is_editable_by(request.user): if 'attendees_limit' in form.fields and talk.is_editable_by(request.user) and talk.room and talk.room.capacity:
form.fields.pop('track')
form.fields.pop('duration')
form.fields.pop('start_date')
form.fields.pop('room')
form.fields.pop('registration_required')
form.fields.pop('attendees_limit')
elif talk.room and talk.room.capacity:
form.fields['attendees_limit'].help_text=ungettext_lazy( form.fields['attendees_limit'].help_text=ungettext_lazy(
"Note: the room %(room)s has %(capacity)s seat.", "Note: the room %(room)s has %(capacity)s seat.",
"Note: the room %(room)s has %(capacity)s seats.", "Note: the room %(room)s has %(capacity)s seats.",
talk.room.capacity) % {'room': talk.room.name, 'capacity': talk.room.capacity} talk.room.capacity) % {'room': talk.room.name, 'capacity': talk.room.capacity}
if not talk.is_moderable_by(request.user):
form.fields['title'].disabled = True
else: else:
form.fields['speakers'].initial = [request.user] form.fields['speakers'].initial = [request.user]
if not is_orga(request, request.user):
form.fields.pop('track')
form.fields.pop('duration')
form.fields.pop('start_date')
form.fields.pop('room')
form.fields.pop('registration_required')
form.fields.pop('attendees_limit')
if request.method == 'POST' and form.is_valid(): if request.method == 'POST' and form.is_valid():
if hasattr(talk, 'id'): if hasattr(talk, 'id'):
talk = form.save() talk = form.save()
@ -227,6 +217,8 @@ def talk_edit(request, talk=None):
messages.success(request, _('Talk proposed successfully!')) messages.success(request, _('Talk proposed successfully!'))
return redirect(talk.get_absolute_url()) return redirect(talk.get_absolute_url())
return render(request, 'proposals/talk_edit.html', { return render(request, 'proposals/talk_edit.html', {
'base_template': 'staff.html' if staff else 'base.html',
'talk': talk,
'form': form, 'form': form,
}) })
@ -255,6 +247,10 @@ class TalkDetail(LoginRequiredMixin, DetailView):
form_url=reverse('talk-conversation', kwargs={'talk': self.object.slug})) form_url=reverse('talk-conversation', kwargs={'talk': self.object.slug}))
else: else:
ctx['edit_perm'] = self.object.is_editable_by(self.request.user) ctx['edit_perm'] = self.object.is_editable_by(self.request.user)
if is_staff(self.request, self.request.user):
ctx.update(base_template='staff.html')
else:
ctx.update(base_template='base.html')
return super().get_context_data(**ctx) return super().get_context_data(**ctx)
@ -404,18 +400,7 @@ def speaker_list(request):
}) })
@login_required def talk_registrable_list(request):
def user_details(request, username):
user = get_object_or_404(User, username=username)
participation = get_object_or_404(Participation, user=user, site=get_current_site(request))
return render(request, 'proposals/user_details.html', {
'profile': user.profile,
'participation': participation,
'talk_list': allowed_talks(Talk.objects.filter(site=get_current_site(request), speakers=user), request),
})
def talk_subscriptions(request):
site = get_current_site(request) site = get_current_site(request)
if not Conference.objects.get(site=site).subscriptions_open: if not Conference.objects.get(site=site).subscriptions_open:
raise Http404 raise Http404
@ -424,13 +409,13 @@ def talk_subscriptions(request):
attendee = Attendee.objects.filter(user=request.user).first() # None if it does not exists attendee = Attendee.objects.filter(user=request.user).first() # None if it does not exists
else: else:
attendee = None attendee = None
return render(request, 'proposals/subscriptions_list.html', { return render(request, 'proposals/talk_registrable_list.html', {
'talks': talks, 'talks': talks,
'attendee': attendee, 'attendee': attendee,
}) })
def talk_subscribe(request, talk): def talk_register(request, talk):
talk = get_object_or_404(Talk, site=get_current_site(request), registration_required=True, slug=talk) talk = get_object_or_404(Talk, site=get_current_site(request), registration_required=True, slug=talk)
form = SubscribeForm(request.POST or None) form = SubscribeForm(request.POST or None)
@ -452,9 +437,9 @@ def talk_subscribe(request, talk):
talk.attendees.add(attendee) talk.attendees.add(attendee)
messages.success(request, _("Registered!")) messages.success(request, _("Registered!"))
talk.save() talk.save()
return redirect('subscriptions-list') return redirect('list-registrable-talks')
return render(request, 'proposals/talk_subscribe.html', { return render(request, 'proposals/talk_register.html', {
'talk': talk, 'talk': talk,
'form': form, 'form': form,
}) })

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-11-13 19:08
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('volunteers', '0003_auto_20161024_1313'),
]
operations = [
migrations.AlterModelOptions(
name='activity',
options={'verbose_name': 'Activity', 'verbose_name_plural': 'Activities'},
),
]

View File

@ -2,7 +2,7 @@
{% load accounts_tags i18n %} {% load accounts_tags i18n %}
{% block participatetab %} class="active"{% endblock %} {% block volunteertab %} class="active"{% endblock %}
{% block content %} {% block content %}
@ -16,9 +16,9 @@
<p>{{ activity.description }}</p> <p>{{ activity.description }}</p>
<p> <p>
{% if request.user in activity.participants.all %} {% if request.user in activity.participants.all %}
<a class="btn btn-danger" href="{% url 'enrole-in-activity' slug=activity.slug %}">{% trans "Sorry, I have a setback" %}</a> <a class="btn btn-danger" href="{% url 'enrole-as-volunteer' slug=activity.slug %}">{% trans "Sorry, I have a setback" %}</a>
{% else %} {% else %}
<a class="btn btn-primary" href="{% url 'enrole-in-activity' slug=activity.slug %}">{% trans "I will be happy to help on that!" %}</a> <a class="btn btn-primary" href="{% url 'enrole-as-volunteer' slug=activity.slug %}">{% trans "I will be happy to help on that!" %}</a>
{% endif %} {% endif %}
</p> </p>
</p> </p>

View File

@ -1,8 +1,8 @@
{% extends 'base.html' %} {% extends 'staff.html' %}
{% load bootstrap3 i18n %} {% load bootstrap3 i18n %}
{% block listingtab %} active{% endblock %} {% block volunteerstab %} class="active"{% endblock %}
{% block content %} {% block content %}
@ -36,8 +36,8 @@
{% if forloop.first %} {% if forloop.first %}
<tbody> <tbody>
{% endif %} {% endif %}
<tr class="clickable-row" data-href="{% url 'show-speaker' username=volunteer.user.username %}"> <tr>
<td>{{ volunteer.user.username }}</td> <td><a href="{% url 'show-participant' username=volunteer.user.username %}">{{ volunteer.user.username }}</a></td>
<td>{{ volunteer.user.get_full_name }}</td> <td>{{ volunteer.user.get_full_name }}</td>
<td> <td>
{% for activity in volunteer.user.activities.all %} {% for activity in volunteer.user.activities.all %}
@ -45,10 +45,9 @@
{% endfor %} {% endfor %}
</td> </td>
<td> <td>
<a class="btn btn-{% if volunteer.conversation.messages.last.author == volunteer.user %}primary{% else %}default{% endif %}" href="{% url 'conversation' volunteer.user.username %}">{% trans "Contact" %}</a> <a class="btn btn-{% if volunteer.conversation.messages.last.author == volunteer.user %}primary{% else %}default{% endif %}" href="{% url 'user-conversation' volunteer.user.username %}">{% trans "Contact" %}</a>
</td> </td>
</tr> </tr>
</li>
{% if forloop.last %} {% if forloop.last %}
</tbody> </tbody>
{% endif %} {% endif %}

View File

@ -4,10 +4,10 @@ from . import views
urlpatterns = [ urlpatterns = [
url(r'^enrole/$', views.participate, name='participate-as-volunteer'), url(r'^enrole/$', views.enrole, name='enrole-as-volunteer'),
url(r'^enrole/(?P<slug>[-\w]+)/$', views.enrole, name='enrole-as-volunteer'),
url(r'^list/$', views.volunteer_list, name='list-volunteers'), url(r'^list/$', views.volunteer_list, name='list-volunteers'),
url(r'^activities/$', views.ActivityList.as_view(), name='list-activities'), url(r'^activities/$', views.ActivityList.as_view(), name='list-activities'),
url(r'^activities/add/$', views.ActivityCreate.as_view(), name='add-activity'), url(r'^activities/add/$', views.ActivityCreate.as_view(), name='add-activity'),
url(r'^activities/(?P<slug>[-\w]+)/edit/$', views.ActivityUpdate.as_view(), name='edit-activity'), url(r'^activities/(?P<slug>[-\w]+)/edit/$', views.ActivityUpdate.as_view(), name='edit-activity'),
url(r'^activities/(?P<slug>[-\w]+)/enrole/$', views.participate, name='enrole-in-activity'),
] ]

View File

@ -15,7 +15,7 @@ from .forms import ActivityForm, VolunteerFilterForm
@login_required @login_required
def participate(request, slug=None): def enrole(request, slug=None):
if slug: if slug:
# TODO: enrole action should be done on post (with bootstrap modal confirmation box?) # TODO: enrole action should be done on post (with bootstrap modal confirmation box?)
activity = get_object_or_404(Activity, site=get_current_site(request), slug=slug) activity = get_object_or_404(Activity, site=get_current_site(request), slug=slug)
@ -24,13 +24,32 @@ def participate(request, slug=None):
else: else:
activity.participants.add(request.user) activity.participants.add(request.user)
activity.save() activity.save()
return redirect('participate-as-volunteer') return redirect('enrole-as-volunteer')
activities = Activity.objects.filter(site=get_current_site(request)) activities = Activity.objects.filter(site=get_current_site(request))
return render(request, 'volunteers/participate.html', { return render(request, 'volunteers/volunteer_enrole.html', {
'activities': activities, 'activities': activities,
}) })
@staff_required
def volunteer_list(request):
show_filters = False
site = get_current_site(request)
filter_form = VolunteerFilterForm(request.GET or None, site=site)
# Filtering
volunteers = Participation.objects.filter(site=site,user__activities__isnull=False).order_by('pk').distinct()
if filter_form.is_valid():
data = filter_form.cleaned_data
if len(data['activity']):
show_filters = True
volunteers = volunteers.filter(user__activities__slug__in=data['activity'])
return render(request, 'volunteers/volunteer_list.html', {
'volunteer_list': volunteers,
'filter_form': filter_form,
'show_filters': show_filters,
})
class ActivityMixin(object): class ActivityMixin(object):
def get_queryset(self): def get_queryset(self):
return Activity.objects.filter(site=get_current_site(self.request)).all() return Activity.objects.filter(site=get_current_site(self.request)).all()
@ -54,22 +73,3 @@ class ActivityUpdate(OrgaRequiredMixin, ActivityMixin, ActivityFormMixin, Update
class ActivityDetail(StaffRequiredMixin, ActivityMixin, DetailView): class ActivityDetail(StaffRequiredMixin, ActivityMixin, DetailView):
pass pass
@staff_required
def volunteer_list(request):
show_filters = False
site = get_current_site(request)
filter_form = VolunteerFilterForm(request.GET or None, site=site)
# Filtering
volunteers = Participation.objects.filter(site=site,user__activities__isnull=False).order_by('pk').distinct()
if filter_form.is_valid():
data = filter_form.cleaned_data
if len(data['activity']):
show_filters = True
volunteers = volunteers.filter(user__activities__slug__in=data['activity'])
return render(request, 'volunteers/volunteer_list.html', {
'volunteer_list': volunteers,
'filter_form': filter_form,
'show_filters': show_filters,
})