Add Wagtail CMS for kfet app.

K-Fêt
- Integrate wagtail to serve "static" pages of old K-Fêt website
- Fixture "kfetcms/kfet_wagtail_17_05" contains a copy of old website
(as in May 2017).
- Media files can be got until end of June 17 at
http://partage.eleves.ens.fr//files/604e6dea2ceebc66b1936c6b3f911744/kfet_media.tar.gz

Login/logout
- Update package django_cas_ng to last version.
- Clean COFCASBackend.
- Change CAS version to 3 (version used on eleves.ens). This enables
the logout redirection (for CAS ofc).
- Add messages and clean existing ones on login/logout (for both
outsider and cas users).

Misc
- Update settings to bypass an incompability between debug-toolbar and
wagtailmenus packages.
- Better management of dev/test-specific urls (if debug-toolbar wasn't in
INSTALLED_APPS, media files were not served).
- UI improvements.
This commit is contained in:
Aurélien Delobelle 2017-05-30 20:44:30 +02:00
parent b13e992a30
commit 8c6d56b27c
67 changed files with 3038 additions and 618 deletions

1
kfet/cms/__init__.py Normal file
View file

@ -0,0 +1 @@
default_app_config = 'kfet.cms.apps.KFetCMSAppConfig'

7
kfet/cms/apps.py Normal file
View file

@ -0,0 +1,7 @@
from django.apps import AppConfig
class KFetCMSAppConfig(AppConfig):
name = 'kfet.cms'
label = 'kfetcms'
verbose_name = 'CMS K-Fêt'

View file

@ -0,0 +1,20 @@
from kfet.models import Article
def get_articles(request):
articles = (
Article.objects
.filter(is_sold=True, hidden=False)
.select_related('category')
.order_by('category__name', 'name')
)
pressions, others = [], []
for article in articles:
if article.category.name == 'Pression':
pressions.append(article)
else:
others.append(article)
return {
'pressions': pressions,
'articles': others,
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,35 @@
from django.contrib.auth.models import Group
from django.core.management import call_command
from django.core.management.base import BaseCommand
from wagtail.wagtailcore.models import Page, Site
class Command(BaseCommand):
help = "Importe des données pour Wagtail"
def add_arguments(self, parser):
parser.add_argument('--file', default='kfet_wagtail_17_05')
def handle(self, *args, **options):
self.stdout.write("Import des données wagtail")
# Nettoyage des données initiales posées par Wagtail dans la migration
# wagtailcore/0002
Group.objects.filter(name__in=('Moderators', 'Editors')).delete()
try:
homepage = Page.objects.get(
title="Welcome to your new Wagtail site!"
)
homepage.delete()
Site.objects.filter(root_page=homepage).delete()
except Page.DoesNotExist:
pass
# Import des données
# Par défaut, il s'agit d'une copie du site K-Fêt (17-05)
call_command('loaddata', options['file'])

View file

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import modelcluster.fields
import wagtail.wagtailcore.fields
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wagtailcore', '0033_remove_golive_expiry_help_text'),
('wagtailimages', '0019_delete_filter'),
]
operations = [
migrations.CreateModel(
name='GroupTeam',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('name', models.CharField(max_length=255, verbose_name='Nom')),
],
options={
'verbose_name': 'Groupe de K-Fêt-eux-ses',
'verbose_name_plural': 'Groupes de K-Fêt-eux-ses',
},
),
migrations.CreateModel(
name='KFetPage',
fields=[
('page_ptr', models.OneToOneField(primary_key=True, to='wagtailcore.Page', parent_link=True, auto_created=True, serialize=False)),
('no_header', models.BooleanField(verbose_name='Sans en-tête', help_text="Coché, l'en-tête (avec le titre) de la page n'est pas affiché.", default=False)),
('content', wagtail.wagtailcore.fields.RichTextField(verbose_name='Contenu')),
('custom_template', models.CharField(max_length=255, verbose_name='Template personnalisé', blank=True)),
],
options={
'verbose_name': 'page K-Fêt',
'verbose_name_plural': 'pages K-Fêt',
},
bases=('wagtailcore.page',),
),
migrations.CreateModel(
name='KFetPageGroupTeam',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('sort_order', models.IntegerField(editable=False, null=True, blank=True)),
('title', models.CharField(max_length=255, verbose_name='Titre du groupe', blank=True)),
('content', wagtail.wagtailcore.fields.RichTextField(verbose_name='Texte de présentation du groupe')),
('group', models.ForeignKey(related_name='+', verbose_name='Groupe de K-Fêt-eux-ses', to='kfetcms.GroupTeam')),
('page', modelcluster.fields.ParentalKey(related_name='team_groups', to='kfetcms.KFetPage')),
('show_only', models.IntegerField(default=None, verbose_name='Montrer seulement', blank=True, null=True, help_text='Nombre de membres du groupe affichés initialement. Laisser vide pour tou-te-s les afficher.')),
],
options={
'abstract': False,
'ordering': ['sort_order'],
},
),
migrations.CreateModel(
name='MemberTeam',
fields=[
('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)),
('sort_order', models.IntegerField(editable=False, null=True, blank=True)),
('first_name', models.CharField(max_length=255, verbose_name='Prénom', blank=True, default='')),
('last_name', models.CharField(max_length=255, verbose_name='Nom', blank=True, default='')),
('nick_name', models.CharField(max_length=255, verbose_name='Alias', blank=True, default='')),
('group', modelcluster.fields.ParentalKey(related_name='members', verbose_name='Groupe de K-Fêt-eux-ses', to='kfetcms.GroupTeam')),
('photo', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, blank=True, verbose_name='Photo', to='wagtailimages.Image', null=True)),
],
options={
'verbose_name': 'K-Fêt-eux-se',
},
),
]

View file

165
kfet/cms/models.py Normal file
View file

@ -0,0 +1,165 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from modelcluster.models import ClusterableModel, ParentalKey
from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailcore.models import Orderable, Page
from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
from wagtail.wagtailsnippets.models import register_snippet
from kfet.cms.context_processors import get_articles
class KFetPage(Page):
no_header = models.BooleanField(
verbose_name=_('Sans en-tête'),
default=False,
help_text=_(
"Coché, l'en-tête (avec le titre) de la page n'est pas affiché."
),
)
content = RichTextField(verbose_name=_('Contenu'))
custom_template = models.CharField(
verbose_name=_('Template personnalisé'),
max_length=255,
blank=True,
)
content_panels = Page.content_panels + [
FieldPanel('no_header'),
FieldPanel('content', classname='full'),
InlinePanel('team_groups', label=_("Groupes de K-Fêt-eux-ses")),
]
settings_panels = Page.settings_panels + [
FieldPanel('custom_template'),
]
class Meta:
verbose_name = _('page K-Fêt')
verbose_name_plural = _('pages K-Fêt')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.template = "kfetcms/base.html"
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
page = context['page']
if not page.seo_title:
page.seo_title = page.title
if self.slug == "carte":
context.update(get_articles(request))
return context
def get_template(self, request, *args, **kwargs):
return self.custom_template or (
super().get_template(request, *args, **kwargs))
class KFetPageGroupTeam(Orderable, models.Model):
page = ParentalKey(KFetPage, related_name='team_groups')
group = models.ForeignKey(
'kfetcms.GroupTeam',
verbose_name=_('Groupe de K-Fêt-eux-ses'),
related_name='+',
)
title = models.CharField(
verbose_name=_('Titre du groupe'),
max_length=255,
blank=True,
)
content = RichTextField(
verbose_name=_('Texte de présentation du groupe'),
)
show_only = models.IntegerField(
verbose_name=_('Montrer seulement'),
blank=True, null=True, default=None,
help_text=_(
'Nombre de membres du groupe affichés initialement. Laisser vide '
'pour tou-te-s les afficher.'
),
)
panels = [
FieldPanel('title', classname='full'),
FieldPanel('show_only', classname='full'),
FieldPanel('content', classname='full'),
SnippetChooserPanel('group'),
]
@register_snippet
class GroupTeam(ClusterableModel):
name = models.CharField(
verbose_name=_('Nom'),
max_length=255,
)
class Meta:
verbose_name = _('Groupe de K-Fêt-eux-ses')
verbose_name_plural = _('Groupes de K-Fêt-eux-ses')
def __str__(self):
return self.name
panels = [
FieldPanel('name', classname='full'),
InlinePanel('members', label=_('Membres du groupe')),
]
@register_snippet
class MemberTeam(Orderable, models.Model):
group = ParentalKey(
GroupTeam,
verbose_name=_("Groupe de K-Fêt-eux-ses"),
on_delete=models.CASCADE,
related_name='members',
)
first_name = models.CharField(
verbose_name=_('Prénom'),
max_length=255,
blank=True, default='',
)
last_name = models.CharField(
verbose_name=_('Nom'),
max_length=255,
blank=True, default='',
)
nick_name = models.CharField(
verbose_name=_('Alias'),
max_length=255,
blank=True, default='',
)
photo = models.ForeignKey(
'wagtailimages.Image',
verbose_name=_('Photo'),
on_delete=models.SET_NULL,
null=True, blank=True,
related_name='+',
)
class Meta:
verbose_name = _('K-Fêt-eux-se')
def __str__(self):
return self.get_full_name()
panels = [
FieldPanel('first_name'),
FieldPanel('last_name'),
FieldPanel('nick_name'),
FieldPanel('group'),
ImageChooserPanel('photo'),
]
def get_full_name(self):
full_name = '{} {}'.format(self.first_name, self.last_name)
return full_name.strip()

View file

@ -0,0 +1,155 @@
.cms-content {
text-align: justify;
font-size: 1.1em;
}
@media (min-width:768px) {
.cms-content {
font-size: 1.2em;
line-height: 1.6em;
}
}
.cms-column {
column-gap: 45px;
padding: 20px 15px;
background: white;
}
@media (min-width: 768px) {
.cms-column {
padding: 35px 30px;
}
}
@media (min-width: 992px) {
.cms-column {
margin: 0 15px;
}
}
/* Titles */
.cms-content h2, .cms-content h3 {
clear: both;
margin: 0 0 15px;
padding-bottom: 10px;
border-bottom: 1px solid #c8102e;
text-align: left;
font-weight: bold;
}
@media (min-width: 768px) {
.cms-content h2, .cms-content h3 {
padding-bottom: 15px;
}
}
/* Paragraphs */
.cms-content p {
margin-bottom: 20px;
text-indent: 2em;
}
.cms-content p + :not(h2):not(h3):not(div) {
margin-top: -10px;
}
@media (min-width: 768px) {
.cms-content p {
padding-bottom: 15px;
}
.cms-content p + :not(h2):not(h3):not(div) {
margin-top: -30px;
}
}
/* Lists */
.cms-content ol, .cms-content ul {
padding: 0 0 0 15px;
margin: 0 0 10px;
}
.cms-content ul {
list-style-type: square;
}
.cms-content ol > li, .cms-content ul > li {
padding-left: 5px;
}
/* Images */
.cms-content .richtext-image {
max-height: 100%;
margin: 5px 0 15px;
}
.cms-content .richtext-image.left {
float: left;
margin-right: 30px;
}
.cms-content .richtext-image.right {
float: right;
margin-left: 30px;
}
/* Team groups & members */
.team-group {
margin-bottom: 20px;
}
.team-group .col-btn {
margin-bottom: 20px;
}
.team-group .member-more {
display: none;
}
.team-member {
padding: 0;
margin-bottom: 20px;
min-height: 190px;
background-color: inherit;
border: 0;
}
.team-member img {
max-width: 100%;
max-height: 125px;
width: auto;
height: auto;
display: block;
}
.team-member .infos {
height: 50px;
margin-top: 15px;
}
@media (min-width: 768px) {
.team-group {
margin-left: 20px;
margin-right: 20px;
}
.team-member {
min-height: 215px;
}
.team-member img {
max-height: 150px;
}
}

View file

@ -0,0 +1,68 @@
{% extends "kfet/base.html" %}
{% load static %}
{% load wagtailuserbar %}
{% load wagtailcore_tags %}
{% block extra_head %}
<link rel="stylesheet" type="text/css" href="{% static "kfetcms/css/index.css" %}">
{% endblock %}
{% block title %}{{ page.seo_title }}{% endblock %}
{% block header-class %}text-center{% endblock %}
{% block header-title %}{{ page.title }}{% endblock %}
{% block content %}
<div class="row row-messages">
{% include "kfet/base_messages.html" %}
</div>
<div class="row column-row">
<div class="cms-column {% block col-size %}column-md-2{% endblock %}">
<div class="cms-content">
{% block block1-content %}
{% endblock %}
{% block block2-content %}
{{ page.content|richtext }}
{% endblock %}
{% block block3-content %}
{% endblock %}
</div>
</div>
</div>
{% wagtailuserbar %}
<script type="text/javascript">
$( function() {
// Titles and their following elements (until next title) are unbreakable.
// This workaround should not be necessary if we use StreamField instead of
// RichTextField in wagtail pages.
$('.cms-content h2, .cms-content h3').filter( function() {
return $(this).closest('.unbreakable').length == 0;
})
.each( function() {
let elt = $('<div>', {class: "unbreakable"});
$(this).before(elt);
let current = $(this);
while (current.length !== 0) {
let next = current.next(':not(h2, h3)');
current.appendTo(elt);
current = next;
}
});
});
</script>
{% endblock %}
{% block footer %}
{% include "kfet/base_footer.html" %}
{% endblock %}

View file

@ -0,0 +1,46 @@
{% extends "kfetcms/base.html" %}
{% load static %}
{% load kfet_tags %}
{% block extra_head %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static "kfet/css/home.css" %}">
{% endblock %}
{% block col-size %}column-sm-2 column-md-3{% endblock %}
{% block block3-content %}
{% if pressions %}
<div class="unbreakable carte carte-inverted">
<h3 class="carte-title">Pressions du moment</h3>
<ul class="carte-list">
{% for article in pressions %}
<li class="carte-item">
<div class="filler"></div>
<span class="carte-label">{{ article.name }}</span>
<span class="carte-ukf">{{ article.price | ukf:False}} UKF</span>
</li>
{% endfor %}
</ul>
</div><!-- endblock unbreakable -->
{% endif %}
{% regroup articles by category as categories %}
{% for category in categories %}
<div class="unbreakable carte">
<h3 class="carte-title">{{ category.grouper.name }}</h3>
<ul class="carte-list">
{% for article in category.list %}
<li class="carte-item">
<div class="filler"></div>
<span class="carte-label">{{ article.name }}</span>
<span class="carte-ukf">{{ article.price | ukf:False}} UKF</span>
</li>
{% endfor %}
</ul>
</div><!-- endblock unbreakable -->
{% endfor %}
{% endblock %}

View file

@ -0,0 +1,67 @@
{% extends "kfetcms/base.html" %}
{% load wagtailcore_tags %}
{% load wagtailimages_tags %}
{% block block1-content %}
{% for group_block in page.team_groups.all %}
<h3>{{ group_block.title }}</h3>
<div>
{{ group_block.content|richtext }}
</div>
{% with members=group_block.group.members.all %}
{% with len=members|length %}
{% if len > 0 %}
<div class="team-group row">
{% if len == 2 %}
<div class="visible-sm col-sm-3"></div>
{% endif %}
{% for member in members %}
<div class="col-xs-6 {% if len == 1 %}col-sm-4 col-sm-offset-4{% elif len == 3 %}col-sm-4{% elif len == 2%}col-sm-3 col-md-6{% else %}col-sm-3 col-md-4 col-lg-3{% endif %} {% if group_block.show_only != None and forloop.counter0 >= group_block.show_only %}member-more{% endif %}">
<div class="team-member thumbnail text-center">
{% image member.photo max-200x500 %}
<div class="infos">
<b>{{ member.get_full_name }}</b>
<br>
{% if member.nick_name %}
<i>alias</i> {{ member.nick_name }}
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% if group_block.show_only != None and len > group_block.show_only %}
<div class="col-xs-12 col-btn text-center">
<button class="btn btn-primary btn-lg more">
{% if group_block.show_only %}
Y'en a plus !
{% else %}
Les voir
{% endif %}
</button>
</div>
{% endif %}
</div>
{% endif %}
{% endwith %}
{% endwith %}
{% endfor %}
<script type="text/javascript">
$( function() {
$('.more').click( function() {
$(this).closest('.col-btn').hide();
$(this).closest('.team-group').children('.member-more').show();
});
});
</script>
{% endblock %}