Gestion des contenus.
This commit is contained in:
parent
0d237b52a8
commit
95895d23f1
18 changed files with 175 additions and 79 deletions
|
@ -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
|
||||
import django.db.models.deletion
|
||||
|
@ -9,7 +9,6 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0009_alter_user_last_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -27,9 +26,8 @@ class Migration(migrations.Migration):
|
|||
fields=[
|
||||
('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')),
|
||||
('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')),
|
||||
('group_owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
22
content/migrations/0002_content_school_owner.py
Normal file
22
content/migrations/0002_content_school_owner.py
Normal 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'),
|
||||
),
|
||||
]
|
|
@ -3,6 +3,7 @@ from django.urls import reverse
|
|||
from django.contrib.auth.models import Group
|
||||
from django.conf import settings
|
||||
|
||||
from users.models import SchoolProfile
|
||||
|
||||
class Category(models.Model):
|
||||
"""Une catégorie de contenu."""
|
||||
|
@ -31,20 +32,19 @@ class Content(models.Model):
|
|||
max_length=255,
|
||||
verbose_name="Nom du contenu"
|
||||
)
|
||||
group_owner = models.ForeignKey(
|
||||
Group,
|
||||
school_owner = models.ForeignKey(
|
||||
SchoolProfile,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
content_url = models.URLField(
|
||||
verbose_name='URL du contenu',
|
||||
null=True,
|
||||
)
|
||||
category = models.ForeignKey(
|
||||
Category,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name="Catégorie",
|
||||
null=True
|
||||
)
|
||||
file = models.FileField(
|
||||
verbose_name="Fichier"
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
18
content/templates/content/content.html
Normal file
18
content/templates/content/content.html
Normal 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>
|
||||
|
|
@ -27,18 +27,11 @@ $('html, body').animate({scrollTop: $('#category-content').offset().top}, 800);
|
|||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<span id="category-content"></span>
|
||||
<div class="row text-center" id="category-content">
|
||||
{% 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>
|
||||
{% include "content/content.html" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,9 @@ from .views import (
|
|||
CreateCategory,
|
||||
DeleteCategory,
|
||||
EditCategory,
|
||||
CreateContent,
|
||||
DeleteContent,
|
||||
EditContent,
|
||||
)
|
||||
|
||||
app_name = 'content'
|
||||
|
@ -28,5 +31,21 @@ urlpatterns = [
|
|||
'category/edit/<int:pk>',
|
||||
EditCategory.as_view(),
|
||||
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",
|
||||
),
|
||||
|
||||
]
|
||||
|
|
|
@ -54,3 +54,53 @@ class EditCategory(generic.UpdateView):
|
|||
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()
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import SiteSettings, ContentSettings
|
||||
from .models import SiteSettings
|
||||
|
||||
admin.site.register(SiteSettings)
|
||||
admin.site.register(ContentSettings)
|
||||
|
|
|
@ -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
|
||||
import settings.aes_field
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
@ -12,15 +11,6 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
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(
|
||||
name='SiteSettings',
|
||||
fields=[
|
||||
|
|
|
@ -3,25 +3,6 @@ from django.db import models
|
|||
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):
|
||||
PRETTY_NAME = "Réglages du site"
|
||||
allow_upload = models.BooleanField(
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
{% endfor %}
|
||||
</table>
|
||||
<h2>Réglages</h2>
|
||||
<h3>Réglages du site</h3>
|
||||
<a class="btn btn-primary btn-sm" href="{% url 'settings:site-settings' %}">
|
||||
<i class="fas fa-edit"></i>
|
||||
Éditer
|
||||
|
@ -84,22 +83,4 @@
|
|||
<td>{{site_settings.home_message}}</td>
|
||||
</tr>
|
||||
</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 %}
|
||||
|
|
|
@ -2,7 +2,7 @@ from django.views.generic import TemplateView, UpdateView
|
|||
from django.urls import reverse_lazy
|
||||
from content.models import Category
|
||||
from users.models import SchoolProfile
|
||||
from .models import ContentSettings, SiteSettings
|
||||
from .models import SiteSettings
|
||||
|
||||
|
||||
class SettingsView(TemplateView):
|
||||
|
@ -12,7 +12,6 @@ class SettingsView(TemplateView):
|
|||
context = super().get_context_data(**kwargs)
|
||||
context['categories'] = Category.objects.all()
|
||||
context['site_settings'], _ = SiteSettings.objects.get_or_create()
|
||||
context['content_settings'], _ = ContentSettings.objects.get_or_create()
|
||||
context['schools'] = SchoolProfile.objects.all()
|
||||
context['settings'] = True
|
||||
return context
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
<h1>Suppression</h1>
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
<p>Êtes-vous certain de vouloir supprimer "{{ object }}"?</p>
|
||||
<input type="submit" value="Confirmer" />
|
||||
|
|
|
@ -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.db import migrations, models
|
||||
|
@ -10,8 +10,8 @@ class Migration(migrations.Migration):
|
|||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('auth', '0009_alter_user_last_name_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -19,7 +19,7 @@ class Migration(migrations.Migration):
|
|||
name='SchoolProfile',
|
||||
fields=[
|
||||
('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(
|
||||
|
|
|
@ -1,16 +1,23 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.db.models.signals import post_save
|
||||
from django.urls import reverse
|
||||
from django.dispatch import receiver
|
||||
|
||||
|
||||
class SchoolProfile(models.Model):
|
||||
"""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):
|
||||
return self.group.name
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("users:school", kwargs={'pk':self.pk})
|
||||
|
||||
class UserProfile(models.Model):
|
||||
"""Profil d'un utilisateur"""
|
||||
|
|
21
users/templates/users/school.html
Normal file
21
users/templates/users/school.html
Normal 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 %}
|
|
@ -9,6 +9,7 @@ from .views import (
|
|||
Logout,
|
||||
PasswordChange,
|
||||
Profile,
|
||||
School,
|
||||
)
|
||||
|
||||
app_name = 'users'
|
||||
|
@ -48,6 +49,11 @@ urlpatterns = [
|
|||
CreateSchool.as_view(),
|
||||
name='new-school'
|
||||
),
|
||||
path(
|
||||
'school/<int:pk>',
|
||||
School.as_view(),
|
||||
name='school'
|
||||
),
|
||||
path(
|
||||
'school/<int:pk>/edit',
|
||||
EditSchool.as_view(),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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.hashers import make_password
|
||||
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 .models import UserProfile, SchoolProfile
|
||||
from content.models import Content
|
||||
|
||||
|
||||
class CreateUser(CreateView):
|
||||
|
@ -116,6 +117,16 @@ class DeleteSchool(DeleteView):
|
|||
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):
|
||||
success_message = "Vous vous êtes bien déconnecté."
|
||||
|
||||
|
|
Loading…
Reference in a new issue