improving reviewers UI
This commit is contained in:
parent
eb0cf5010a
commit
3523db76c7
|
@ -11,3 +11,8 @@ class OrgaRequiredMixin(UserPassesTestMixin):
|
|||
class StaffRequiredMixin(UserPassesTestMixin):
|
||||
def test_func(self):
|
||||
return is_staff(self.request, self.request.user)
|
||||
|
||||
|
||||
class SuperuserRequiredMixin(UserPassesTestMixin):
|
||||
def test_func(self):
|
||||
return self.request.user.is_superuser
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 35805d82545cdc2aea0acc6f480f866119a8bb52
|
||||
Subproject commit c74943cba752ff6d40a505321ac58bdacbc41857
|
|
@ -23,3 +23,4 @@
|
|||
- [x] note sur un speaker
|
||||
- [ ] ouverture conf
|
||||
- [ ] mail de bienvenu autre que activation si le mec est inscrit par le staff
|
||||
- [ ] do not notify speakers of modified talk
|
||||
|
|
|
@ -146,6 +146,7 @@ BOWER_COMPONENTS_ROOT = os.path.join(BASE_DIR, 'components')
|
|||
BOWER_INSTALLED_APPS = (
|
||||
'bootstrap',
|
||||
'jquery',
|
||||
'jquery-ui',
|
||||
)
|
||||
|
||||
LOGIN_REDIRECT_URL = 'home'
|
||||
|
|
|
@ -3,12 +3,10 @@ from django.forms.models import modelform_factory
|
|||
|
||||
from proposals.models import Talk, Topic
|
||||
|
||||
__all__ = ['TalkForm', 'TopicForm', 'TopicOrgaForm']
|
||||
__all__ = ['TalkForm', 'TopicForm']
|
||||
|
||||
|
||||
TalkForm = modelform_factory(Talk, fields=['title', 'description', 'topics', 'event', 'speakers'],
|
||||
widgets={'topics': CheckboxSelectMultiple()})
|
||||
|
||||
TopicForm = modelform_factory(Topic, fields=['name'])
|
||||
|
||||
TopicOrgaForm = modelform_factory(Topic, fields=['name', 'reviewers'])
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.7 on 2016-07-06 14:46
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.contrib.sites.managers
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.db.models.manager
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sites', '0002_alter_domain_unique'),
|
||||
('proposals', '0003_talk_accepted'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelManagers(
|
||||
name='topic',
|
||||
managers=[
|
||||
('objects', django.db.models.manager.Manager()),
|
||||
('on_site', django.contrib.sites.managers.CurrentSiteManager()),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='topic',
|
||||
name='site',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='sites.Site'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -20,16 +20,25 @@ __all__ = ['Topic', 'Talk']
|
|||
|
||||
class Topic(PonyConfModel):
|
||||
|
||||
site = models.ForeignKey(Site, on_delete=models.CASCADE)
|
||||
|
||||
name = models.CharField(max_length=128, verbose_name='Name', unique=True)
|
||||
slug = AutoSlugField(populate_from='name', unique=True)
|
||||
|
||||
reviewers = models.ManyToManyField(Participation, blank=True)
|
||||
|
||||
objects = models.Manager()
|
||||
on_site = CurrentSiteManager()
|
||||
|
||||
@property
|
||||
def talks(self):
|
||||
return Talk.objects.filter(topics=self).all()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('list-talks-by-topic', kwargs={'topic': self.slug})
|
||||
return reverse('show-topic', kwargs={'slug': self.slug})
|
||||
|
||||
|
||||
class Talk(PonyConfModel):
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% load staticfiles accounts_tags %}
|
||||
|
||||
{% block topictab %} class="active"{% endblock %}
|
||||
|
||||
{% block css %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" href="{% static 'jquery-ui/themes/base/jquery-ui.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'jquery-ui/themes/base/autocomplete.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{{ topic }}</h1>
|
||||
|
||||
{% if request|orga %}
|
||||
<form class="form-inline" method="post" action="{% url 'add-reviewer' topic.slug %}" role="form" id="add-reviewer-form">
|
||||
{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<div class="input-group ui-widget">
|
||||
<input type="text" class="form-control" name="user" placeholder="add reviewer" value="">
|
||||
<div class="input-group-addon">
|
||||
<a href="javascript:void(0);" onclick="$('#add-reviewer-form').submit();"><span class="glyphicon glyphicon-plus"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<ul>
|
||||
{% for reviewer in topic.reviewers.all %}
|
||||
<li>
|
||||
{{ reviewer }}
|
||||
{% if request|orga %} - <a href="{% url 'remove-reviewer' topic.slug reviewer.user.username %}">remove</a>{% endif %}
|
||||
</li>
|
||||
{% empty %}
|
||||
<li><i>No reviewers.</i></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block js_end %}
|
||||
{{ block.super }}
|
||||
<script src="{% static 'jquery-ui/ui/minified/core.min.js' %}"></script>
|
||||
<script src="{% static 'jquery-ui/ui/minified/widget.min.js' %}"></script>
|
||||
<script src="{% static 'jquery-ui/ui/minified/position.min.js' %}"></script>
|
||||
<script src="{% static 'jquery-ui/ui/minified/menu.min.js' %}"></script>
|
||||
<script src="{% static 'jquery-ui/ui/minified/autocomplete.min.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
$('input[name="user"]').autocomplete({
|
||||
source: "{% url 'add-reviewer' topic.slug %}",
|
||||
onSelect: function (data) {
|
||||
$('input[name="user"]').val(data.data);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -11,18 +11,21 @@
|
|||
<ul>
|
||||
{% for topic in topic_list %}
|
||||
<li>
|
||||
{{ topic.get_link }}
|
||||
{{ topic }}:
|
||||
{% if request|staff %}
|
||||
{% if topic.reviewers.exists %} ({% for reviewer in topic.reviewers.all %} {{ reviewer.get_link }} {% if not forloop.last %}, {% endif %}{% endfor %}) {% endif %}
|
||||
{% if request|orga %} - <a href="{% url 'edit-topic' topic.slug %}">edit</a>{% endif %}
|
||||
<a href="{{ topic.get_absolute_url }}">{{ topic.reviewers.count }} reviewer{{ topic.reviewers.count|pluralize }}</a>
|
||||
and
|
||||
{% comment %}{% if topic.reviewers.exists %} ({% for reviewer in topic.reviewers.all %} {{ reviewer.get_link }} {% if not forloop.last %}, {% endif %}{% endfor %}) {% endif %}{% endcomment %}
|
||||
{% endif %}
|
||||
<a href="{% url 'list-talks-by-topic' topic.slug %}">{{ topic.talks.count }} talk{{ topic.talks.count|pluralize }}</a>.
|
||||
{% if request.user.is_superuser %} - <a href="{% url 'edit-topic' topic.slug %}">edit</a>{% endif %}
|
||||
</li>
|
||||
{% empty %}
|
||||
<li><i>No topic.</i></li>
|
||||
<li><i>No topics.</i></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% if request|staff %}
|
||||
{% if request|orga %}
|
||||
<a href="{% url 'add-topic' %}" class="btn btn-success">Add a topic</a>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -12,7 +12,10 @@ urlpatterns = [
|
|||
url(r'^talk/by-topic/(?P<topic>[-\w]+)$', views.talk_list_by_topic, name='list-talks-by-topic'),
|
||||
url(r'^topic/$', views.TopicList.as_view(), name='list-topics'),
|
||||
url(r'^topic/add/$', views.TopicCreate.as_view(), name='add-topic'),
|
||||
url(r'^topic/edit/(?P<slug>[-\w]+)/$', views.TopicUpdate.as_view(), name='edit-topic'),
|
||||
url(r'^topic/(?P<slug>[-\w]+)/$', views.TopicDetail.as_view(), name='show-topic'),
|
||||
url(r'^topic/(?P<slug>[-\w]+)/edit/$', views.TopicUpdate.as_view(), name='edit-topic'),
|
||||
url(r'^topic/(?P<slug>[-\w]+)/add-reviewer/$', views.topic_add_reviewer, name='add-reviewer'),
|
||||
url(r'^topic/(?P<slug>[-\w]+)/remove-reviewer/(?P<username>[\w.@+-]+)/$', views.topic_remove_reviewer, name='remove-reviewer'),
|
||||
url(r'^speakers/$', views.SpeakerList.as_view(), name='list-speakers'),
|
||||
url(r'^speaker/(?P<username>[\w.@+-]+)$', views.user_details, name='show-speaker'),
|
||||
]
|
||||
|
|
|
@ -8,12 +8,14 @@ from django.core.urlresolvers import reverse
|
|||
from django.db.models import Q
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.views.generic import CreateView, DetailView, ListView, UpdateView
|
||||
from django.http import JsonResponse
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
from accounts.mixins import OrgaRequiredMixin, StaffRequiredMixin
|
||||
from accounts.mixins import OrgaRequiredMixin, StaffRequiredMixin, SuperuserRequiredMixin
|
||||
from accounts.models import Participation
|
||||
from accounts.utils import is_orga
|
||||
|
||||
from .forms import TalkForm, TopicForm, TopicOrgaForm
|
||||
from .forms import TalkForm, TopicForm
|
||||
from .models import Talk, Topic, Vote
|
||||
from .utils import allowed_talks
|
||||
from .signals import *
|
||||
|
@ -81,25 +83,81 @@ class TalkDetail(LoginRequiredMixin, DetailView):
|
|||
return super().get_context_data(**ctx)
|
||||
|
||||
|
||||
class TopicList(LoginRequiredMixin, ListView):
|
||||
model = Topic
|
||||
|
||||
|
||||
class TopicMixin(object):
|
||||
model = Topic
|
||||
|
||||
def get_form_class(self):
|
||||
return TopicOrgaForm if is_orga(self.request, self.request.user) else TopicForm
|
||||
queryset = Topic.on_site.all()
|
||||
form_class = TopicForm
|
||||
|
||||
|
||||
class TopicCreate(StaffRequiredMixin, TopicMixin, CreateView):
|
||||
class TopicList(LoginRequiredMixin, TopicMixin, ListView):
|
||||
pass
|
||||
|
||||
|
||||
class TopicUpdate(OrgaRequiredMixin, TopicMixin, UpdateView):
|
||||
class TopicCreate(OrgaRequiredMixin, TopicMixin, CreateView):
|
||||
def form_valid(self, form):
|
||||
form.instance.site = get_current_site(self.request)
|
||||
return super(TopicCreate, self).form_valid(form)
|
||||
|
||||
|
||||
class TopicUpdate(SuperuserRequiredMixin, TopicMixin, UpdateView):
|
||||
pass
|
||||
|
||||
|
||||
class TopicDetail(StaffRequiredMixin, TopicMixin, DetailView):
|
||||
pass
|
||||
|
||||
|
||||
@login_required
|
||||
def topic_add_reviewer(request, slug):
|
||||
if not Participation.objects.get(user=request.user).is_orga():
|
||||
raise PermissionDenied()
|
||||
|
||||
topic = get_object_or_404(Topic, slug=slug)
|
||||
|
||||
if request.method == 'POST':
|
||||
user = request.POST.get('user')
|
||||
try:
|
||||
user = User.objects.get(username=user)
|
||||
except ObjectDoesNotExist:
|
||||
messages.error(request, 'User not found.')
|
||||
else:
|
||||
participation, created = Participation.on_site.get_or_create(user=user, site=get_current_site(request))
|
||||
if participation in topic.reviewers.all():
|
||||
messages.info(request, 'User is already a reviewer of this topic.')
|
||||
else:
|
||||
topic.reviewers.add(participation)
|
||||
topic.save()
|
||||
messages.success(request, 'User add to reviewer of this topic successfully.')
|
||||
return redirect(topic.get_absolute_url())
|
||||
else:
|
||||
term = request.GET.get('term')
|
||||
if not term:
|
||||
raise Http404()
|
||||
query = Q(username__icontains=term) \
|
||||
| Q(first_name__icontains=term) \
|
||||
| Q(last_name__icontains=term)
|
||||
users = User.objects \
|
||||
.exclude(id__in=topic.reviewers.values('user__id')) \
|
||||
.filter(query)[:10]
|
||||
response = []
|
||||
for user in users:
|
||||
response += [{
|
||||
'label': str(user.profile),
|
||||
'value': user.username,
|
||||
}]
|
||||
return JsonResponse(response, safe=False)
|
||||
|
||||
|
||||
@login_required
|
||||
def topic_remove_reviewer(request, slug, username):
|
||||
if not Participation.objects.get(user=request.user).is_orga():
|
||||
raise PermissionDenied()
|
||||
topic = get_object_or_404(Topic, slug=slug)
|
||||
participation = get_object_or_404(Participation, user__username=username)
|
||||
topic.reviewers.remove(participation)
|
||||
return redirect(topic.get_absolute_url())
|
||||
|
||||
|
||||
class SpeakerList(StaffRequiredMixin, ListView):
|
||||
queryset = Participation.on_site.filter(user__talk__in=Talk.on_site.all()).distinct()
|
||||
template_name = 'proposals/speaker_list.html'
|
||||
|
|
Loading…
Reference in New Issue