Style d'affichage sur les catégories
This commit is contained in:
parent
8cdef202c7
commit
47d96bbef9
30 changed files with 113 additions and 265 deletions
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-24 10:29
|
||||
# Generated by Django 2.0.1 on 2018-02-28 12:53
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
@ -17,7 +17,9 @@ class Migration(migrations.Migration):
|
|||
name='Category',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('Nom de la catégorie', models.CharField(max_length=255)),
|
||||
('name', models.CharField(max_length=255, verbose_name='Nom de la catégorie')),
|
||||
('description', models.TextField(default='', verbose_name='Descriton de la catégorie')),
|
||||
('image', models.ImageField(null=True, upload_to='', verbose_name='Illustration de la catégorie')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-24 10:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('content', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='category',
|
||||
name='Nom de la catégorie',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='name',
|
||||
field=models.CharField(default='Nom de la catégorie', max_length=255, verbose_name='Nom de la catégorie'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -1,6 +1,7 @@
|
|||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth.models import Group
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
|
@ -9,6 +10,14 @@ class Category(models.Model):
|
|||
max_length=255,
|
||||
verbose_name="Nom de la catégorie"
|
||||
)
|
||||
description = models.TextField(
|
||||
verbose_name="Descriton de la catégorie",
|
||||
default=""
|
||||
)
|
||||
image = models.ImageField(
|
||||
verbose_name="Illustration de la catégorie",
|
||||
null=True,
|
||||
)
|
||||
def get_absolute_url(self):
|
||||
return reverse('content:category-list', kwargs={'pk':self.pk})
|
||||
|
||||
|
|
|
@ -1,17 +1,42 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% if category %}
|
||||
<h1>{{category.name}}</h1>
|
||||
{% else %}
|
||||
<h1>Liste des contenus</h1>
|
||||
{% endif %}
|
||||
|
||||
{% for content in contents %}
|
||||
<div>
|
||||
<h2>{{content.name}}</h2>
|
||||
<h3>Contenu proposé par {{content.group_owner.name}}</h3>
|
||||
<a href="{{content.content_url}}">C'est ici que ça se passe</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% load staticfiles %}
|
||||
{% block style %}
|
||||
.page-title
|
||||
{
|
||||
background-image : url("{{category.image.url}}");
|
||||
background-attachment : fixed;
|
||||
background-position: center;
|
||||
}
|
||||
.title-block
|
||||
{
|
||||
background-color: rgba(248, 249, 250, 0.6);
|
||||
}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<script>
|
||||
function show_content () {
|
||||
$('html, body').animate({scrollTop: $('#category-content').offset().top}, 800);
|
||||
}
|
||||
</script>
|
||||
<div class="position-relative overflow-hidden p-3 p-md-5 m-md-3 text-center bg-light page-title">
|
||||
<div class="col-md-5 p-lg-5 mx-auto my-5 title-block">
|
||||
<h1 class="display-4 font-weight-normal">{{category.name}}</h1>
|
||||
<p class="lead font-weight-normal">{{category.description}}</p>
|
||||
<a class="btn btn-outline-secondary smooth-scroll" href="#category-content">Aller voir !</a>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<span id="category-content"></span>
|
||||
{% for content in contents %}
|
||||
<div class="bg-dark pt-3 px-3 pt-md-5 px-md-5 text-center text-white overflow-hidden">
|
||||
<div class="my-3 py-3">
|
||||
<h2 class="display-5">{{content.name}}</h2>
|
||||
<p class="lead">Contenu proposé par {{content.group_owner.name}}</p>
|
||||
</div>
|
||||
<video controls>
|
||||
<source src="{{content.content_url}}" type="video/mp4">
|
||||
</video>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -28,6 +28,12 @@ class CreateCategory(generic.CreateView):
|
|||
"""Création de catégorie."""
|
||||
model = Category
|
||||
fields = '__all__'
|
||||
template_name = "edit.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(generic.CreateView, self).get_context_data(**kwargs)
|
||||
context['title'] = "Création de catégorie"
|
||||
return context
|
||||
|
||||
|
||||
class DeleteCategory(generic.DeleteView):
|
||||
|
@ -42,3 +48,9 @@ class EditCategory(generic.UpdateView):
|
|||
model = Category
|
||||
fields = '__all__'
|
||||
template_name = "edit.html"
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(generic.UpdateView, self).get_context_data(**kwargs)
|
||||
context['title'] = "Édition de " + self.object.name
|
||||
return context
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
Django==2.0.1
|
||||
django-bootstrap4==0.0.6
|
||||
Pillow==5.0.0
|
||||
pycrypto==2.6.1
|
||||
pytz==2017.3
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-14 18:04
|
||||
# Generated by Django 2.0.1 on 2018-02-28 12:53
|
||||
|
||||
from django.db import migrations, models
|
||||
import settings.models
|
||||
import settings.aes_field
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -16,17 +16,17 @@ class Migration(migrations.Migration):
|
|||
name='ContentSettings',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('URL du FTP', models.URLField(max_length=255)),
|
||||
('Identifiant sur le FTP', models.CharField(max_length=255)),
|
||||
('Mot de passe', settings.models.AESEncryptedField(max_length=255)),
|
||||
('ftp_url', models.URLField(default='', max_length=255, verbose_name='URL du FTP')),
|
||||
('ftp_id', models.CharField(default='', max_length=255, verbose_name='Identifiant sur le FTP')),
|
||||
('ftp_pass', settings.aes_field.AESEncryptedField(default='', max_length=255, verbose_name='Mot de passe')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SiteSettings',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('allow_upload', models.BooleanField(help_text="Autoriser l'upload de vidéos.")),
|
||||
('site_name', models.CharField(help_text='Nom du site', max_length=255)),
|
||||
('allow_upload', models.BooleanField(default=False, verbose_name="Autoriser l'upload de vidéos.")),
|
||||
('home_message', models.TextField(default='', verbose_name="Message de la page d'accueil")),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-14 18:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('settings', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='sitesettings',
|
||||
name='allow_upload',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='sitesettings',
|
||||
name='site_name',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sitesettings',
|
||||
name="Autoriser l'upload de vidéos.",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sitesettings',
|
||||
name="Message de la page d'accueil",
|
||||
field=models.TextField(default=''),
|
||||
),
|
||||
]
|
|
@ -1,59 +0,0 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-14 18:37
|
||||
|
||||
from django.db import migrations, models
|
||||
import settings.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('settings', '0002_auto_20180114_1832'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='contentsettings',
|
||||
name='Identifiant sur le FTP',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contentsettings',
|
||||
name='Mot de passe',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contentsettings',
|
||||
name='URL du FTP',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='sitesettings',
|
||||
name="Autoriser l'upload de vidéos.",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='sitesettings',
|
||||
name="Message de la page d'accueil",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contentsettings',
|
||||
name='ftp_id',
|
||||
field=models.CharField(default='', max_length=255, verbose_name='Identifiant sur le FTP'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contentsettings',
|
||||
name='ftp_pass',
|
||||
field=settings.models.AESEncryptedField(default='', max_length=255, verbose_name='Mot de passe'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contentsettings',
|
||||
name='ftp_url',
|
||||
field=models.URLField(default='', max_length=255, verbose_name='URL du FTP'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sitesettings',
|
||||
name='allow_upload',
|
||||
field=models.BooleanField(default=False, verbose_name="Autoriser l'upload de vidéos."),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='sitesettings',
|
||||
name='home_message',
|
||||
field=models.TextField(default='', verbose_name="Message de la page d'accueil"),
|
||||
),
|
||||
]
|
|
@ -4,6 +4,7 @@ from .aes_field import AESEncryptedField
|
|||
|
||||
|
||||
class ContentSettings(models.Model):
|
||||
PRETTY_NAME = "Réglages des contenus"
|
||||
ftp_url = models.URLField(
|
||||
max_length=255,
|
||||
verbose_name="URL du FTP",
|
||||
|
@ -22,6 +23,7 @@ class ContentSettings(models.Model):
|
|||
|
||||
|
||||
class SiteSettings(models.Model):
|
||||
PRETTY_NAME = "Réglages du site"
|
||||
allow_upload = models.BooleanField(
|
||||
verbose_name="Autoriser l'upload de vidéos.",
|
||||
default=False,
|
||||
|
|
|
@ -26,3 +26,10 @@ class EditSiteSettingsView(UpdateView):
|
|||
def get_object(self, queryset=None):
|
||||
obj,_ = self.model.objects.get_or_create()
|
||||
return obj
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdateView, self).get_context_data(**kwargs)
|
||||
context['title'] = "Édition des " + self.object.PRETTY_NAME
|
||||
return context
|
||||
|
||||
|
||||
|
|
|
@ -39,9 +39,9 @@ INSTALLED_APPS = [
|
|||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'bootstrap4',
|
||||
'settings',
|
||||
'content',
|
||||
'vote',
|
||||
'users',
|
||||
]
|
||||
|
||||
|
@ -124,3 +124,6 @@ USE_TZ = True
|
|||
# https://docs.djangoproject.com/en/2.0/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'static', 'media')
|
||||
|
|
|
@ -15,6 +15,8 @@ Including another URLconf
|
|||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
|
||||
from . import views
|
||||
|
||||
|
@ -23,6 +25,7 @@ urlpatterns = [
|
|||
path('', views.home, name="home"),
|
||||
path('content/', include('content.urls')),
|
||||
path('settings/', include('settings.urls')),
|
||||
path('vote/', include('vote.urls')),
|
||||
path('users/', include('users.urls')),
|
||||
]
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
<script defer src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
|
||||
<style>
|
||||
{% block style %}{% endblock %}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% include 'nav_bar.html' %}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load bootstrap3 %}
|
||||
{% load bootstrap4 %}
|
||||
{% block content %}
|
||||
{% if title %}
|
||||
<h1>{{title}}</h1>
|
||||
{% endif %}
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% bootstrap_icon "star" %}
|
||||
{% if validate %}
|
||||
{{validate}}
|
||||
{% else %}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
</a></li>
|
||||
{% endfor %}
|
||||
|
||||
<li class="nav-item {% if vote %}active{% endif %}"><a class="nav-link" href="{% url 'vote:home' %}">Vote</a></li>
|
||||
<li class="nav-item {% if settings %}active{% endif %}"><a class="nav-link" href="{% url 'settings:index' %}">Administration</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-31 09:12
|
||||
# Generated by Django 2.0.1 on 2018-02-28 12:53
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
@ -10,8 +10,8 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0009_alter_user_last_name_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('auth', '0009_alter_user_last_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -19,7 +19,6 @@ class Migration(migrations.Migration):
|
|||
name='SchoolProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('is_school', models.BooleanField()),
|
||||
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
|
||||
],
|
||||
),
|
||||
|
@ -27,7 +26,7 @@ class Migration(migrations.Migration):
|
|||
name='UserProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('school', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.SchoolProfile')),
|
||||
('school', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.SchoolProfile')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-31 10:52
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='schoolprofile',
|
||||
name='is_school',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='userprofile',
|
||||
name='school',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.SchoolProfile'),
|
||||
),
|
||||
]
|
|
@ -4,6 +4,7 @@ from .views import (
|
|||
CreateUserProfile,
|
||||
CreateSchool,
|
||||
EditSchool,
|
||||
DeleteSchool,
|
||||
)
|
||||
|
||||
app_name = 'users'
|
||||
|
@ -28,4 +29,9 @@ urlpatterns = [
|
|||
EditSchool.as_view(),
|
||||
name='edit-school'
|
||||
),
|
||||
path(
|
||||
'school/<int:pk>/delete',
|
||||
DeleteSchool.as_view(),
|
||||
name='delete-school'
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from django.contrib.auth.models import User, Group
|
||||
from django.views.generic import CreateView, UpdateView
|
||||
from django.views.generic import CreateView, UpdateView, DeleteView
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
|
@ -79,3 +79,8 @@ class EditSchool(UpdateView):
|
|||
context['title'] = "Édition de l'école"
|
||||
context['validate'] = "Modifier"
|
||||
return context
|
||||
|
||||
|
||||
class DeleteSchool(DeleteView):
|
||||
model = Group
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
from django.contrib import admin
|
||||
from .models import Vote, Poll
|
||||
|
||||
admin.site.register(Vote)
|
||||
admin.site.register(Poll)
|
|
@ -1,5 +0,0 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class VoteConfig(AppConfig):
|
||||
name = 'vote'
|
|
@ -1,39 +0,0 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-24 10:29
|
||||
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0009_alter_user_last_name_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('content', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Poll',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=1024)),
|
||||
('contents', models.ManyToManyField(to='content.Content')),
|
||||
('voters_group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Vote',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('vote', models.IntegerField(validators=[django.core.validators.MaxValueValidator(5), django.core.validators.MinValueValidator(0)])),
|
||||
('content', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='content.Content')),
|
||||
('poll', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='vote.Poll')),
|
||||
('votant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -1,22 +0,0 @@
|
|||
from django.db import models
|
||||
from django.core import validators
|
||||
from content.models import Content
|
||||
from django.contrib.auth.models import Group, User
|
||||
|
||||
|
||||
class Poll(models.Model):
|
||||
voters_group = models.ForeignKey(Group, on_delete=models.CASCADE)
|
||||
contents = models.ManyToManyField(Content)
|
||||
title = models.CharField(max_length=1024)
|
||||
|
||||
|
||||
class Vote(models.Model):
|
||||
votant = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
content = models.ForeignKey(Content, on_delete=models.CASCADE)
|
||||
poll = models.ForeignKey(Poll, on_delete=models.CASCADE)
|
||||
vote = models.IntegerField(
|
||||
validators=[
|
||||
validators.MaxValueValidator(5),
|
||||
validators.MinValueValidator(0)
|
||||
]
|
||||
)
|
|
@ -1,6 +0,0 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h2>Votes disponibles</h2>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,3 +0,0 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -1,7 +0,0 @@
|
|||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = 'vote'
|
||||
urlpatterns = [
|
||||
path('home', views.home, name='home')
|
||||
]
|
|
@ -1,7 +0,0 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
||||
def home(request):
|
||||
return render(request, 'vote/home.html')
|
Loading…
Reference in a new issue