Commit 10dfb2c1 authored by Skia's avatar Skia
Browse files

Merge branch 'nabos' into 'master'

Communication screens

See merge request !116
parents 5132eb49 a2ee0176
Pipeline #1446 passed with stage
in 6 minutes
......@@ -11,6 +11,7 @@
<li> <a href="{{ url('trombi:detail', trombi_id=object.trombi.id) }}">{% trans %}Edit Trombi{% endtrans %}</a></li>
{% else %}
<li> <a href="{{ url('trombi:create', club_id=object.id) }}">{% trans %}New Trombi{% endtrans %}</a></li>
<li> <a href="{{ url('club:poster_list', club_id=object.id) }}">{% trans %}Posters{% endtrans %}</a></li>
{% endif %}
</ul>
<h4>{% trans %}Counters:{% endtrans %}</h4>
......
......@@ -50,4 +50,8 @@ urlpatterns = [
url(r'^(?P<mailing_id>[0-9]+)/mailing/delete$', MailingDeleteView.as_view(), name='mailing_delete'),
url(r'^(?P<mailing_subscription_id>[0-9]+)/mailing/delete/subscription$', MailingSubscriptionDeleteView.as_view(), name='mailing_subscription_delete'),
url(r'^membership/(?P<membership_id>[0-9]+)/set_old$', MembershipSetOldView.as_view(), name='membership_set_old'),
url(r'^(?P<club_id>[0-9]+)/poster$', PosterListView.as_view(), name='poster_list'),
url(r'^(?P<club_id>[0-9]+)/poster/create$', PosterCreateView.as_view(), name='poster_create'),
url(r'^(?P<club_id>[0-9]+)/poster/(?P<poster_id>[0-9]+)/edit$', PosterEditView.as_view(), name='poster_edit'),
url(r'^(?P<club_id>[0-9]+)/poster/(?P<poster_id>[0-9]+)/delete$', PosterDeleteView.as_view(), name='poster_delete'),
]
......@@ -37,12 +37,14 @@ from ajax_select.fields import AutoCompleteSelectField
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404, redirect
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, PageEditViewBase
from core.views import CanCreateMixin, CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, PageEditViewBase
from core.views.forms import SelectDate, SelectDateTime
from club.models import Club, Membership, Mailing, MailingSubscription
from sith.settings import SITH_MAXIMUM_FREE_ROLE
from counter.models import Selling, Counter
from core.models import User, PageRev
from com.views import PosterListBaseView, PosterCreateBaseView, PosterEditBaseView, PosterDeleteBaseView
from com.models import Poster
from django.conf import settings
......@@ -86,8 +88,9 @@ class MailingSubscriptionForm(forms.ModelForm):
class ClubTabsMixin(TabedViewMixin):
def get_tabs_title(self):
if isinstance(self.object, PageRev):
self.object = self.object.page.club
obj = self.get_object()
if isinstance(obj, PageRev):
self.object = obj.page.club
return self.object.get_display_name()
def get_list_of_tabs(self):
......@@ -141,6 +144,11 @@ class ClubTabsMixin(TabedViewMixin):
'slug': 'mailing',
'name': _("Mailing list"),
})
tab_list.append({
'url': reverse('club:poster_list', kwargs={'club_id': self.object.id}),
'slug': 'posters',
'name': _("Posters list"),
})
if self.request.user.is_owner(self.object):
tab_list.append({
'url': reverse('club:club_prop', kwargs={'club_id': self.object.id}),
......@@ -592,3 +600,51 @@ class MailingAutoCleanView(View):
def get(self, request, *args, **kwargs):
self.mailing.subscriptions.all().delete()
return redirect('club:mailing', club_id=self.mailing.club.id)
class PosterListView(ClubTabsMixin, PosterListBaseView, CanViewMixin):
"""List communication posters"""
def get_object(self):
return self.club
def get_context_data(self, **kwargs):
kwargs = super(PosterListView, self).get_context_data(**kwargs)
kwargs['app'] = "club"
kwargs['club'] = self.club
return kwargs
class PosterCreateView(PosterCreateBaseView, CanCreateMixin):
"""Create communication poster"""
pk_url_kwarg = "club_id"
def get_object(self):
obj = super(PosterCreateView, self).get_object()
if not obj:
return self.club
return obj
def get_success_url(self, **kwargs):
return reverse_lazy('club:poster_list', kwargs={'club_id': self.club.id})
class PosterEditView(ClubTabsMixin, PosterEditBaseView, CanEditMixin):
"""Edit communication poster"""
def get_success_url(self):
return reverse_lazy('club:poster_list', kwargs={'club_id': self.club.id})
def get_context_data(self, **kwargs):
kwargs = super(PosterEditView, self).get_context_data(**kwargs)
kwargs['app'] = "club"
return kwargs
class PosterDeleteView(PosterDeleteBaseView, ClubTabsMixin, CanEditMixin):
"""Delete communication poster"""
def get_success_url(self):
return reverse_lazy('club:poster_list', kwargs={'club_id': self.club.id})
......@@ -39,4 +39,6 @@ class WeekmailAdmin(SearchModelAdmin):
admin.site.register(Sith)
admin.site.register(News, NewsAdmin)
admin.site.register(Weekmail, WeekmailAdmin)
admin.site.register(Screen)
admin.site.register(Poster)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('club', '0010_auto_20170912_2028'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('com', '0003_auto_20170115_2300'),
]
operations = [
migrations.CreateModel(
name='Poster',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('name', models.CharField(verbose_name='name', max_length=128, default='')),
('file', models.ImageField(verbose_name='file', upload_to='com/posters')),
('date_begin', models.DateTimeField(default=django.utils.timezone.now)),
('date_end', models.DateTimeField(blank=True, null=True)),
('display_time', models.IntegerField(verbose_name='display time', default=30)),
('is_moderated', models.BooleanField(verbose_name='is moderated', default=False)),
('club', models.ForeignKey(verbose_name='club', related_name='posters', to='club.Club')),
('moderator', models.ForeignKey(verbose_name='moderator', blank=True, null=True, related_name='moderated_posters', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Screen',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('name', models.CharField(verbose_name='name', max_length=128)),
],
),
migrations.AddField(
model_name='poster',
name='screens',
field=models.ManyToManyField(related_name='posters', to='com.Screen'),
),
]
......@@ -2,6 +2,7 @@
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
# - Sli <antoine@bartuccio.fr>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
......@@ -31,11 +32,16 @@ from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib.staticfiles.templatetags.staticfiles import static
from django.core.mail import EmailMultiAlternatives
from django.core.exceptions import ValidationError
from core.models import User, Preferences, RealGroup, Notification
from django.utils import timezone
from core.models import User, Preferences, RealGroup, Notification, SithFile
from club.models import Club
class Sith(models.Model):
"""A one instance class storing all the modifiable infos"""
alert_msg = models.TextField(_("alert message"), default="", blank=True)
......@@ -183,3 +189,54 @@ class WeekmailArticle(models.Model):
def __str__(self):
return "%s - %s (%s)" % (self.title, self.author, self.club)
class Screen(models.Model):
name = models.CharField(_("name"), max_length=128)
def active_posters(self):
now = timezone.now()
return self.posters.filter(is_moderated=True, date_begin__lte=now).filter(Q(date_end__isnull=True) | Q(date_end__gte=now))
def is_owned_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
def __str__(self):
return "%s" % (self.name)
class Poster(models.Model):
name = models.CharField(_("name"), blank=False, null=False, max_length=128, default="")
file = models.ImageField(_("file"), null=False, upload_to="com/posters")
club = models.ForeignKey(Club, related_name="posters", verbose_name=_("club"), null=False)
screens = models.ManyToManyField(Screen, related_name="posters")
date_begin = models.DateTimeField(blank=False, null=False, default=timezone.now)
date_end = models.DateTimeField(blank=True, null=True)
display_time = models.IntegerField(_("display time"), blank=False, null=False, default=30)
is_moderated = models.BooleanField(_("is moderated"), default=False)
moderator = models.ForeignKey(User, related_name="moderated_posters", verbose_name=_("moderator"), null=True, blank=True)
def save(self, *args, **kwargs):
if self.date_end and self.date_begin > self.date_end:
raise ValidationError(_("Begin date should be before end date"))
if not self.is_moderated:
for u in RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID).first().users.all():
Notification(user=u, url=reverse("com:poster_moderate_list"),
type="POSTER_MODERATION").save()
return super(Poster, self).save(*args, **kwargs)
def is_owned_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID) or Club.objects.filter(id__in=user.clubs_with_rights)
def can_be_moderated_by(self, user):
return user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
def get_display_name(self):
return self.club.get_display_name()
@property
def page(self):
return self.club.page
def __str__(self):
return self.name
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Poster{% endtrans %}
{% endblock %}
{% block content %}
<div id="poster_edit">
<div id="title">
<div id="links" class="left">
{% if app == "com" %}
<a id="list" class="link" href="{{ url(app + ":poster_list") }}">{% trans %}List{% endtrans %}</a>
{% elif app == "club" %}
<a id="list" class="link" href="{{ url(app + ":poster_list", club.id) }}">{% trans %}List{% endtrans %}</a>
{% endif %}
</div>
<h3>{% trans %}Posters - edit{% endtrans %}</h3>
<div id="links" class="right">
{% if app == "com" %}
<a class="link delete" href="{{ url(app + ":poster_delete", poster.id) }}">{% trans %}Delete{% endtrans %}</a>
{% elif app == "club" %}
<a class="link delete" href="{{ url(app + ":poster_delete", club.id, poster.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</div>
</div>
<div id="poster">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
</div>
</div>
{% endblock %}
{% extends "core/base.jinja" %}
{% block script %}
{{ super() }}
<script src="{{ static('com/js/poster_list.js') }}"></script>
{% endblock %}
{% block title %}
{% trans %}Poster{% endtrans %}
{% endblock %}
{% block content %}
<div id="poster_list">
<div id="title">
<h3>{% trans %}Posters{% endtrans %}</h3>
<div id="links" class="right">
{% if app == "com" %}
<a id="create" class="link" href="{{ url(app + ":poster_create") }}">{% trans %}Create{% endtrans %}</a>
<a id="moderation" class="link" href="{{ url("com:poster_moderate_list") }}">{% trans %}Moderation{% endtrans %}</a>
{% elif app == "club" %}
<a id="create" class="link" href="{{ url(app + ":poster_create", club.id) }}">{% trans %}Create{% endtrans %}</a>
{% endif %}
</div>
</div>
<div id="posters">
{% if poster_list.count() == 0 %}
<div id="no-posters">{% trans %}No posters{% endtrans %}</div>
{% else %}
{% for poster in poster_list %}
<div class="poster{% if not poster.is_moderated %} not_moderated{% endif %}">
<div class="name">{{ poster.name }}</div>
<div class="image"><img src="{{ poster.file.url }}"></img></div>
<div class="dates">
<div class="begin">{{ poster.date_begin | date("d/M/Y H:m") }}</div>
<div class="end">{{ poster.date_end | date("d/M/Y H:m") }}</div>
</div>
{% if app == "com" %}
<a class="edit" href="{{ url(app + ":poster_edit", poster.id) }}">{% trans %}Edit{% endtrans %}</a>
{% elif app == "club" %}
<a class="edit" href="{{ url(app + ":poster_edit", club.id, poster.id) }}">{% trans %}Edit{% endtrans %}</a>
{% endif %}
<div class="tooltip">
<ul>
{% for screen in poster.screens.all() %}
<li>{{ screen }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
{% endif %}
</div>
<div id="view"><div id="placeholder"></div></div>
</div>
{% endblock %}
{% extends "core/base.jinja" %}
{% block script %}
{{ super() }}
<script src="{{ static('com/js/poster_list.js') }}"></script>
{% endblock %}
{% block content %}
<div id="poster_list">
<div id="title">
<div id="links" class="left">
<a id="list" class="link" href="{{ url("com:poster_list") }}">{% trans %}List{% endtrans %}</a>
</div>
<h3>{% trans %}Posters - moderation{% endtrans %}</h3>
</div>
<div id="posters">
{% if object_list.count == 0 %}
<div id="no-posters">{% trans %}No objects{% endtrans %}</div>
{% else %}
{% for poster in object_list %}
<div class="poster{% if not poster.is_moderated %} not_moderated{% endif %}">
<div class="name"> {{ poster.name }} </div>
<div class="image"> <img src="{{ poster.file.url }}"></img> </div>
<a class="moderate" href="{{ url("com:poster_moderate", object_id=poster.id) }}">Moderate</a>
</div>
{% endfor %}
{% endif %}
</div>
<div id="view"><div id="placeholder"></div></div>
</div>
{% endblock %}
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Screen{% endtrans %}
{% endblock %}
{% block content %}
<div id="screen_edit">
<div id="title">
<div id="links" class="left">
<a id="list" class="link" href="{{ url("com:screen_list") }}">{% trans %}List{% endtrans %}</a>
</div>
<h3>{% trans %}Screen - edit{% endtrans %}</h3>
<div id="links" class="right">
<a class="link delete" href="{{ url("com:screen_delete", screen.id) }}">{% trans %}Delete{% endtrans %}</a>
</div>
</div>
<div id="screen">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
</div>
</div>
{% endblock %}
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Screens{% endtrans %}
{% endblock %}
{% block content %}
<div id="screen_list">
<div id="title">
<h3>{% trans %}Screens{% endtrans %}</h3>
<div id="links" class="right">
<a id="create" class="link" href="{{ url("com:screen_create") }}">{% trans %}Create{% endtrans %}</a>
</div>
</div>
<div id="screens">
{% if screen_list.count() == 0 %}
<div id="no-screens">{% trans %}No screens{% endtrans %}</div>
{% else %}
{% for screen in screen_list %}
<div class="screen">
<div class="name">{{ screen.name }}</div>
<a class="edit" href="{{ url("com:screen_edit", screen.id) }}">{% trans %}Edit{% endtrans %}</a>
<a class="slideshow" href="{{ url("com:screen_slideshow", screen.id) }}" target="_blank">{% trans %}Slideshow{% endtrans %}</a>
</div>
{% endfor %}
{% endif %}
</div>
</div>
{% endblock %}
<!DOCTYPE html>
<html lang="fr">
<head>
<title>{% trans %}Slideshow{% endtrans %}</title>
<link href="{{ scss('com/slideshow.scss') }}" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="slideshow">
<div id="slides">
{% for poster in posters %}
<div class="slide {% if loop.first %}center{% else %}right{% endif %}" display_time="{{ poster.display_time }}">
<img src="{{ poster.file.url }}"></img>
</div>
{% endfor %}
</div>
<div id="progress_bullets">
{% for poster in posters %}
<div class="bullet {% if loop.first %}active{% endif %}"></div>
{% endfor %}
</div>
<div id="progress_bar"></div>
</div>
<script src="{{ static('core/js/jquery-3.1.0.min.js') }}"></script>
<script src="{{ static('com/js/slideshow.js') }}"></script>
</body>
</html>
......@@ -47,5 +47,16 @@ urlpatterns = [
url(r'^mailings$', MailingListAdminView.as_view(), name='mailing_admin'),
url(r'^mailings/(?P<mailing_id>[0-9]+)/moderate$', MailingModerateView.as_view(), name='mailing_moderate'),
url(r'^mailings/(?P<mailing_id>[0-9]+)/delete$', MailingDeleteView.as_view(redirect_page='com:mailing_admin'), name='mailing_delete'),
url(r'^poster$', PosterListView.as_view(), name='poster_list'),
url(r'^poster/create$', PosterCreateView.as_view(), name='poster_create'),
url(r'^poster/(?P<poster_id>[0-9]+)/edit$', PosterEditView.as_view(), name='poster_edit'),
url(r'^poster/(?P<poster_id>[0-9]+)/delete$', PosterDeleteView.as_view(), name='poster_delete'),
url(r'^poster/moderate$', PosterModerateListView.as_view(), name='poster_moderate_list'),
url(r'^poster/(?P<object_id>[0-9]+)/moderate$', PosterModerateView.as_view(), name='poster_moderate'),
url(r'^screen$', ScreenListView.as_view(), name='screen_list'),
url(r'^screen/create$', ScreenCreateView.as_view(), name='screen_create'),
url(r'^screen/(?P<screen_id>[0-9]+)/slideshow$', ScreenSlideshowView.as_view(), name='screen_slideshow'),
url(r'^screen/(?P<screen_id>[0-9]+)/edit$', ScreenEditView.as_view(), name='screen_edit'),
url(r'^screen/(?P<screen_id>[0-9]+)/delete$', ScreenDeleteView.as_view(), name='screen_delete'),
]
......@@ -40,7 +40,7 @@ from django import forms
from datetime import timedelta
from com.models import Sith, News, NewsDate, Weekmail, WeekmailArticle
from com.models import Sith, News, NewsDate, Weekmail, WeekmailArticle, Screen, Poster
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, TabedViewMixin, CanCreateMixin, QuickNotifMixin
from core.views.forms import SelectDateTime
from core.models import Notification, RealGroup, User
......@@ -52,6 +52,23 @@ from club.models import Club, Mailing
sith = Sith.objects.first
class PosterForm(forms.ModelForm):
class Meta:
model = Poster
fields = ['name', 'file', 'club', 'screens', 'date_begin', 'date_end', 'display_time']
widgets = {
'screens': forms.CheckboxSelectMultiple,
'is_moderated': forms.HiddenInput()
}
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(PosterForm, self).__init__(*args, **kwargs)
if self.user:
if not self.user.is_com_admin:
self.fields['club'].queryset = Club.objects.filter(id__in=self.user.clubs_with_rights)
class ComTabsMixin(TabedViewMixin):
def get_tabs_title(self):
return _("Communication administration")
......@@ -88,9 +105,27 @@ class ComTabsMixin(TabedViewMixin):
'slug': 'mailings',
'name': _("Mailing lists administration"),
})
tab_list.append({
'url': reverse('com:poster_list'),
'slug': 'posters',
'name': _("Posters list"),
})
tab_list.append({
'url': reverse('com:screen_list'),
'slug': 'screens',
'name': _("Screens list"),
})
return tab_list
class IsComAdminMixin(View):
def dispatch(self, request, *args, **kwargs):
if not (request.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)):
raise PermissionDenied
return super(IsComAdminMixin, self).dispatch(request, *args, **kwargs)
class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
model = Sith
template_name = 'core/edit.jinja'
......@@ -465,3 +500,222 @@ class MailingModerateView(View):
return redirect('com:mailing_admin')
raise PermissionDenied
class PosterListBaseView(ListView):
"""List communication posters"""
current_tab = "posters"
model = Poster
template_name = 'com/poster_list.jinja'
def dispatch(self, request, *args, **kwargs):
club_id = kwargs.pop('club_id', None)
self.club = None
if club_id: