Gestion des contenus.

This commit is contained in:
Klafyvel 2018-02-28 21:25:44 +01:00
parent 0d237b52a8
commit 95895d23f1
18 changed files with 175 additions and 79 deletions

View file

@ -1,4 +1,4 @@
# Generated by Django 2.0.1 on 2018-02-28 12:53 # Generated by Django 2.0.1 on 2018-02-28 18:43
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
@ -9,7 +9,6 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
] ]
operations = [ operations = [
@ -27,9 +26,8 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Nom du contenu')), ('name', models.CharField(max_length=255, verbose_name='Nom du contenu')),
('content_url', models.URLField(null=True, verbose_name='URL du contenu')), ('file', models.FileField(upload_to='', verbose_name='Fichier')),
('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='content.Category', verbose_name='Catégorie')), ('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='content.Category', verbose_name='Catégorie')),
('group_owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
], ],
), ),
] ]

View file

@ -0,0 +1,22 @@
# Generated by Django 2.0.1 on 2018-02-28 18:43
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('content', '0001_initial'),
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='content',
name='school_owner',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.SchoolProfile'),
),
]

View file

@ -3,6 +3,7 @@ from django.urls import reverse
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.conf import settings from django.conf import settings
from users.models import SchoolProfile
class Category(models.Model): class Category(models.Model):
"""Une catégorie de contenu.""" """Une catégorie de contenu."""
@ -31,20 +32,19 @@ class Content(models.Model):
max_length=255, max_length=255,
verbose_name="Nom du contenu" verbose_name="Nom du contenu"
) )
group_owner = models.ForeignKey( school_owner = models.ForeignKey(
Group, SchoolProfile,
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
content_url = models.URLField(
verbose_name='URL du contenu',
null=True,
)
category = models.ForeignKey( category = models.ForeignKey(
Category, Category,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
verbose_name="Catégorie", verbose_name="Catégorie",
null=True null=True
) )
file = models.FileField(
verbose_name="Fichier"
)
def __str__(self): def __str__(self):
return self.name return self.name

View file

@ -0,0 +1,18 @@
<div class=" col-md-4 text-center overflow-hidden">
<div class="card mb-4 box-shadow">
<video controls class="card-img-top">
<source src="{{content.file.url}}" type="video/mp4">
</video>
<div class="card-body">
<h2 class="display-5">{{content.name}}</h2>
<p class="lead">Contenu proposé par {{content.school_owner.group.name}}</p>
<div class="d-flex justify-content-between align-items-center">
<div class="btn-group">
<a class="btn btn-sm btn-outline-primary"i href="{% url "content:content-edit" content.pk %}"><i class="fa fa-edit"></i> Éditer</a>
<a class="btn btn-sm btn-outline-danger" href="{% url "content:content-delete" content.pk %}" ><i class="fa fa-trash"></i> Supprimer</a>
</div>
</div>
</div>
</div>
</div>

View file

@ -27,18 +27,11 @@ $('html, body').animate({scrollTop: $('#category-content').offset().top}, 800);
</div> </div>
</div> </div>
<br /> <br />
<span id="category-content"></span> <div class="row text-center" id="category-content">
{% for content in contents %} {% for content in contents %}
<div class="bg-dark pt-3 px-3 pt-md-5 px-md-5 text-center text-white overflow-hidden"> {% include "content/content.html" %}
<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 %} {% endfor %}
</div>
<br /> <br />
<br /> <br />
</div> </div>

View file

@ -5,6 +5,9 @@ from .views import (
CreateCategory, CreateCategory,
DeleteCategory, DeleteCategory,
EditCategory, EditCategory,
CreateContent,
DeleteContent,
EditContent,
) )
app_name = 'content' app_name = 'content'
@ -28,5 +31,21 @@ urlpatterns = [
'category/edit/<int:pk>', 'category/edit/<int:pk>',
EditCategory.as_view(), EditCategory.as_view(),
name='category-edit', name='category-edit',
) ),
path(
'new',
CreateContent.as_view(),
name='content-new',
),
path(
'<int:pk>/delete',
DeleteContent.as_view(),
name="content-delete",
),
path(
'<int:pk>/edit',
EditContent.as_view(),
name="content-edit",
),
] ]

View file

@ -54,3 +54,53 @@ class EditCategory(generic.UpdateView):
return context return context
class CreateContent(generic.CreateView):
"""Création de contenu."""
model = Content
fields = [
'name',
'category',
'file'
]
template_name = "edit.html"
extra_context = {
'title' : 'Envoi de contenu',
'validate' : 'Envoyer'
}
def get_success_url(self):
return self.object.school_owner.get_absolute_url()
def form_valid(self, form):
form.instance.school_owner = self.request.user.userprofile.school
r = super().form_valid(form)
return r
class DeleteContent(generic.DeleteView):
"""Suppression de contenu"""
model = Content
template_name = "confirm_delete.html"
def get_success_url(self):
return self.object.school_owner.get_absolute_url()
class EditContent(generic.UpdateView):
"""Édition d'un contenu"""
model = Content
template_name = "edit.html"
fields = [
'name',
'category',
'file'
]
template_name = "edit.html"
extra_context = {
'title' : 'Édition de contenu',
'validate' : 'Envoyer'
}
def get_success_url(self):
return self.object.school_owner.get_absolute_url()

View file

@ -1,6 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import SiteSettings, ContentSettings from .models import SiteSettings
admin.site.register(SiteSettings) admin.site.register(SiteSettings)
admin.site.register(ContentSettings)

View file

@ -1,7 +1,6 @@
# Generated by Django 2.0.1 on 2018-02-28 12:53 # Generated by Django 2.0.1 on 2018-02-28 18:43
from django.db import migrations, models from django.db import migrations, models
import settings.aes_field
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -12,15 +11,6 @@ class Migration(migrations.Migration):
] ]
operations = [ operations = [
migrations.CreateModel(
name='ContentSettings',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('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( migrations.CreateModel(
name='SiteSettings', name='SiteSettings',
fields=[ fields=[

View file

@ -3,25 +3,6 @@ from django.db import models
from .aes_field import AESEncryptedField 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",
default="",
)
ftp_id = models.CharField(
max_length=255,
verbose_name="Identifiant sur le FTP",
default=""
)
ftp_pass = AESEncryptedField(
max_length=255,
verbose_name="Mot de passe",
default=""
)
class SiteSettings(models.Model): class SiteSettings(models.Model):
PRETTY_NAME = "Réglages du site" PRETTY_NAME = "Réglages du site"
allow_upload = models.BooleanField( allow_upload = models.BooleanField(

View file

@ -61,7 +61,6 @@
{% endfor %} {% endfor %}
</table> </table>
<h2>Réglages</h2> <h2>Réglages</h2>
<h3>Réglages du site</h3>
<a class="btn btn-primary btn-sm" href="{% url 'settings:site-settings' %}"> <a class="btn btn-primary btn-sm" href="{% url 'settings:site-settings' %}">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
Éditer Éditer
@ -84,22 +83,4 @@
<td>{{site_settings.home_message}}</td> <td>{{site_settings.home_message}}</td>
</tr> </tr>
</table> </table>
<h3>Réglage du contenu</h3>
<a class="btn btn-primary btn-sm" href="">
<i class="fas fa-edit"></i>
Éditer
</a>
<br />
<br />
<table class="table table-striped">
<tr>
<th>URL du FTP</th>
<td>{{content_settings.ftp_url}}</td>
</tr>
<tr>
<th>Identifiant du FTP</th>
<td>{{content_settings.ftp_id}}</td>
</tr>
</table>
{% endblock %} {% endblock %}

View file

@ -2,7 +2,7 @@ from django.views.generic import TemplateView, UpdateView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from content.models import Category from content.models import Category
from users.models import SchoolProfile from users.models import SchoolProfile
from .models import ContentSettings, SiteSettings from .models import SiteSettings
class SettingsView(TemplateView): class SettingsView(TemplateView):
@ -12,7 +12,6 @@ class SettingsView(TemplateView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all() context['categories'] = Category.objects.all()
context['site_settings'], _ = SiteSettings.objects.get_or_create() context['site_settings'], _ = SiteSettings.objects.get_or_create()
context['content_settings'], _ = ContentSettings.objects.get_or_create()
context['schools'] = SchoolProfile.objects.all() context['schools'] = SchoolProfile.objects.all()
context['settings'] = True context['settings'] = True
return context return context

View file

@ -1,5 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<h1>Suppression</h1>
<form action="" method="post">{% csrf_token %} <form action="" method="post">{% csrf_token %}
<p>Êtes-vous certain de vouloir supprimer "{{ object }}"?</p> <p>Êtes-vous certain de vouloir supprimer "{{ object }}"?</p>
<input type="submit" value="Confirmer" /> <input type="submit" value="Confirmer" />

View file

@ -1,4 +1,4 @@
# Generated by Django 2.0.1 on 2018-02-28 12:53 # Generated by Django 2.0.1 on 2018-02-28 18:43
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@ -10,8 +10,8 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('auth', '0009_alter_user_last_name_max_length'), ('auth', '0009_alter_user_last_name_max_length'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ operations = [
@ -19,7 +19,7 @@ class Migration(migrations.Migration):
name='SchoolProfile', name='SchoolProfile',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')), ('group', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='students', to='auth.Group')),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(

View file

@ -1,16 +1,23 @@
from django.db import models from django.db import models
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.urls import reverse
from django.dispatch import receiver from django.dispatch import receiver
class SchoolProfile(models.Model): class SchoolProfile(models.Model):
"""Ajoute un champ pour distinguer les groupes écoles des autres.""" """Ajoute un champ pour distinguer les groupes écoles des autres."""
group = models.OneToOneField(Group, on_delete=models.CASCADE) group = models.OneToOneField(
Group,
on_delete=models.CASCADE,
related_name="students"
)
def __str__(self): def __str__(self):
return self.group.name return self.group.name
def get_absolute_url(self):
return reverse("users:school", kwargs={'pk':self.pk})
class UserProfile(models.Model): class UserProfile(models.Model):
"""Profil d'un utilisateur""" """Profil d'un utilisateur"""

View file

@ -0,0 +1,21 @@
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
<h1>{{object.group.name}}</h1>
<a class="btn btn-primary btn-sm" href="{% url 'users:edit-school' object.pk %}">
<i class="fa fa-edit"></i>
Éditer
</a>
<a class="btn btn-success btn-sm" href="{% url 'content:content-new' %}">
<i class="fa fa-plus"></i>
Ajouter un contenu
</a>
<br />
<br />
<div class="row">
{% for content in contents %}
{% include "content/content.html" %}
{%endfor%}
</div>
{% endblock %}

View file

@ -9,6 +9,7 @@ from .views import (
Logout, Logout,
PasswordChange, PasswordChange,
Profile, Profile,
School,
) )
app_name = 'users' app_name = 'users'
@ -48,6 +49,11 @@ urlpatterns = [
CreateSchool.as_view(), CreateSchool.as_view(),
name='new-school' name='new-school'
), ),
path(
'school/<int:pk>',
School.as_view(),
name='school'
),
path( path(
'school/<int:pk>/edit', 'school/<int:pk>/edit',
EditSchool.as_view(), EditSchool.as_view(),

View file

@ -1,5 +1,5 @@
from django.contrib.auth.models import User, Group from django.contrib.auth.models import User, Group
from django.views.generic import CreateView, UpdateView, DeleteView from django.views.generic import CreateView, UpdateView, DeleteView, DetailView
from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView
from django.contrib.auth.hashers import make_password from django.contrib.auth.hashers import make_password
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
@ -7,6 +7,7 @@ from django.urls import reverse, reverse_lazy
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from .models import UserProfile, SchoolProfile from .models import UserProfile, SchoolProfile
from content.models import Content
class CreateUser(CreateView): class CreateUser(CreateView):
@ -116,6 +117,16 @@ class DeleteSchool(DeleteView):
model = Group model = Group
class School(DetailView):
model = SchoolProfile
template_name = "users/school.html"
def get_context_data(self, **kwargs):
context = super().get_context_data()
context['contents'] = Content.objects.filter(school_owner=self.object)
return context
class Logout(SuccessMessageMixin, LogoutView): class Logout(SuccessMessageMixin, LogoutView):
success_message = "Vous vous êtes bien déconnecté." success_message = "Vous vous êtes bien déconnecté."