First Commit

This commit is contained in:
mrtoine 2025-09-12 11:11:44 +02:00
commit ce0758fbbb
496 changed files with 52062 additions and 0 deletions

0
posts/__init__.py Executable file
View file

25
posts/admin.py Executable file
View file

@ -0,0 +1,25 @@
from django.contrib import admin
from django.db.models import QuerySet
from .models import Post, Category
@admin.action(description="Activer les posts séléctionnés")
def activate_posts(modelAdmin, request, querySet: QuerySet):
updated = querySet.update(active=True)
modelAdmin.message_user(request, f"{updated} post(s) ont été activé(s).")
@admin.action(description="Désactiver les posts séléctionnés")
def deactivate_posts(modelAdmin, request, querySet: QuerySet):
updated = querySet.update(active=False)
modelAdmin.message_user(request, f"{updated} post(s) ont été désactivé(s).")
class PostAdmin(admin.ModelAdmin):
list_display = ('title', 'category', 'author', 'type', 'active', 'created', 'updated')
list_filter = ('category', 'type', 'active', 'created', 'updated')
search_fields = ('title', 'content', 'author__username')
ordering = ('-created',)
fields = ('parent', 'post_parent', 'title', 'slug', 'category', 'content', 'type', 'image', 'author', 'active')
prepopulated_fields = {'slug': ('title',)}
actions = [activate_posts, deactivate_posts]
admin.site.register(Post, PostAdmin)
admin.site.register(Category)

6
posts/apps.py Executable file
View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class PostsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "posts"

View file

@ -0,0 +1,7 @@
from .models import Post
from users.decorators import groups_required
groups_required('Administrateur', 'SuperAdmin')
def pending_posts_count(request):
count = Post.objects.filter(active=False).count()
return {'pending_posts_count': count}

31
posts/forms.py Executable file
View file

@ -0,0 +1,31 @@
from django import forms
from .models import Post
class CreatePost(forms.Form):
title = forms.CharField(
max_length=150,
label='',
required=True,
widget=forms.TextInput(attrs={'placeholder': 'Titre du post'})
)
content = forms.CharField(
label='',
required=True,
widget=forms.Textarea(attrs={'placeholder': 'Contenu du post'})
)
active = forms.BooleanField(
required=True,
label='Actif',
initial=True
)
class EditPost(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content']
widgets = {
'title': forms.TextInput(attrs={'placeholder': 'Titre du post'}),
'content': forms.Textarea(attrs={'placeholder': 'Contenu du post'}),
}

23
posts/middleware.py Executable file
View file

@ -0,0 +1,23 @@
from django.utils.deprecation import MiddlewareMixin
from .models import Post
class PostsMiddleware(MiddlewareMixin):
def process_request(self, request):
# On récupère tous les posts de type 'games'
posts_games = Post.objects.filter(type='games', active=True, parent=True)
# On récupère tous les posts de type 'movies'
posts_movies = Post.objects.filter(type='movies', active=True, parent=True)
# On récupère tous les posts de type 'music'
posts_music = Post.objects.filter(type='music', active=True, parent=True)
# On récupère tous les posts de type 'tech'
posts_tech = Post.objects.filter(type='tech', active=True, parent=True)
# on met tout ça dans l'objet request
request.posts_games = posts_games
request.posts_movies = posts_movies
request.posts_music = posts_music
request.posts_tech = posts_tech

View file

@ -0,0 +1,38 @@
# Generated by Django 4.2.16 on 2024-10-21 18:39
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Category",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
("name", models.CharField(max_length=200)),
("created", models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
name="Post",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
("title", models.CharField(max_length=200)),
("content", models.TextField()),
("date", models.DateTimeField(auto_now_add=True)),
("type", models.CharField(default="news", max_length=200)),
(
"image",
models.ImageField(blank=True, null=True, upload_to="images/"),
),
("active", models.BooleanField(default=False)),
("created", models.DateTimeField(auto_now_add=True)),
("updated", models.DateTimeField(auto_now=True)),
],
),
]

View file

@ -0,0 +1,33 @@
# Generated by Django 4.2.16 on 2024-10-21 18:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
("users", "0001_initial"),
("posts", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="post",
name="author",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="users.user"
),
),
migrations.AddField(
model_name="post",
name="category",
field=models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to="posts.category",
),
),
]

View file

@ -0,0 +1,26 @@
# Generated by Django 4.2.16 on 2024-10-21 21:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("posts", "0002_initial"),
]
operations = [
migrations.AlterModelOptions(
name="category",
options={"verbose_name": "Categorie", "verbose_name_plural": "Categories"},
),
migrations.AlterModelOptions(
name="post",
options={"verbose_name": "Post", "verbose_name_plural": "Posts"},
),
migrations.AddField(
model_name="post",
name="slug",
field=models.SlugField(default="default-slug", max_length=200, unique=True),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 4.2.16 on 2024-10-23 11:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("posts", "0003_alter_category_options_alter_post_options_post_slug"),
]
operations = [
migrations.AddField(
model_name="post",
name="forum_link",
field=models.CharField(blank=True, max_length=200, null=True),
),
]

View file

@ -0,0 +1,17 @@
# Generated by Django 4.2.16 on 2024-10-24 21:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("posts", "0004_post_forum_link"),
]
operations = [
migrations.RemoveField(
model_name="post",
name="date",
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2024-12-16 11:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('posts', '0005_remove_post_date'),
]
operations = [
migrations.AddField(
model_name='post',
name='contribution',
field=models.BooleanField(default=False),
),
]

View file

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2024-12-23 16:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('posts', '0006_post_contribution'),
]
operations = [
migrations.AddField(
model_name='post',
name='post_parent',
field=models.IntegerField(default=0),
preserve_default=False,
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2024-12-23 16:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('posts', '0007_post_post_parent'),
]
operations = [
migrations.AlterField(
model_name='post',
name='post_parent',
field=models.IntegerField(default=0),
),
]

View file

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2024-12-23 16:54
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('posts', '0008_alter_post_post_parent'),
]
operations = [
migrations.AlterField(
model_name='post',
name='post_parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='posts.post'),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2024-12-23 17:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('posts', '0009_alter_post_post_parent'),
]
operations = [
migrations.AddField(
model_name='post',
name='parent',
field=models.BooleanField(default=True),
),
]

View file

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2024-12-26 19:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('posts', '0010_post_parent'),
]
operations = [
migrations.AlterField(
model_name='post',
name='post_parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='child_posts', to='posts.post'),
),
]

0
posts/migrations/__init__.py Executable file
View file

59
posts/models.py Executable file
View file

@ -0,0 +1,59 @@
from django.db import models
from users.models import User
from commons.bbcode_parser import BBCodeParser
from django.db.models.signals import pre_delete
from django.dispatch import receiver
class Category(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Meta:
verbose_name = 'Categorie'
verbose_name_plural = 'Categories'
class Post(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200, unique=True, default='default-slug')
category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1)
title = models.CharField(max_length=200)
content = models.TextField()
type = models.CharField(max_length=200, default='news')
image = models.ImageField(upload_to='images/', null=True, blank=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
active = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
contribution = models.BooleanField(default=False)
forum_link = models.CharField(max_length=200, null=True, blank=True)
post_parent = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='child_posts')
parent = models.BooleanField(default=True)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
# Si le post a un parent, il ne doit pas être considéré comme parent
if self.post_parent:
self.parent = False
else:
self.parent = True
super().save(*args, **kwargs)
class Meta:
verbose_name = 'Post'
verbose_name_plural = 'Posts'
@receiver(pre_delete, sender=Post)
def handle_parent_deletion(sender, instance, **kwargs):
# Si le post supprimé a des enfants
for child in instance.child_posts.all():
# Rendre l'enfant un parent
child.post_parent = None
child.parent = True
child.save()

View file

View file

@ -0,0 +1,9 @@
from django import template
from commons.bbcode_parser import BBCodeParser
register = template.Library()
@register.filter(name='bbcode')
def bbcode(value):
parser = BBCodeParser()
return parser.parse(value)

3
posts/tests.py Executable file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
posts/urls.py Executable file
View file

@ -0,0 +1,12 @@
from django.urls import path
from django.conf.urls.static import static
from passion_retro import settings
from . import views
urlpatterns = [
path("<slug:slug>/", views.view_post, name="view_post"),
path("create/<str:type>/", views.create_post, name="create_news"),
path("<int:post_id>/edit", views.edit_post, name="edit_post"),
]

108
posts/views.py Executable file
View file

@ -0,0 +1,108 @@
from django.shortcuts import render, get_object_or_404, redirect
from posts.models import Post
from forum.models import Topic, Forum, Post as ForumPost
from users.models import UserLevel
from django.contrib import messages
from users.decorators import groups_required
from posts.forms import CreatePost, EditPost
from django.utils.text import slugify
from django.contrib.auth.decorators import login_required
from django.db.models import F
def view_post(request, slug):
post = Post.objects.filter(slug=slug, active=True).first()
subposts = None
if post.parent:
subposts = Post.objects.filter(post_parent=post.id)
context = {
'post': post,
'subposts': subposts
}
return render(request, "posts/post.html", context)
@groups_required('Rédacteur', 'Admininistrateur', 'Super Admin')
def create_post(request, type):
# Create a new post
if request.method == 'POST':
title = request.POST.get('title')
content = request.POST.get('content')
active = request.POST.get('active') == 'on'
image=request.FILES.get('image') if type == 'news' else None
# Si c'est une news, on créer aussi un topic sur le forum news
if type == 'news':
topic = Topic.objects.create(
title=title,
forum=Forum.objects.get(name='Actus du site'),
author=request.user
)
content_forum = f'<p><img src="/media/images/{image}"></p><p>{content}</p>'
forum_post = ForumPost.objects.create(
topic=topic,
content=content_forum,
author=request.user
)
UserLevel.objects.update(user=request.user, experience=F('experience') + 20)
topic.save()
forum_post.save()
forum_link = f'forum/{topic.forum.id}/{topic.id}/'
post = Post.objects.create(
slug=slugify(title),
title=title,
content=content,
type=type,
image=image,
author=request.user,
active=active,
forum_link=forum_link
)
post.save()
messages.success(request, 'Post créer avec succès !')
return redirect('home')
context = {
'type': type,
'post_form': CreatePost(),
}
return render(request, "posts/create_post.html", context)
@login_required()
def edit_post(request, post_id):
post = get_object_or_404(Post, id=post_id, author=request.user)
if request.method == 'POST':
form = EditPost(request.POST, instance=post)
if form.is_valid():
post.title = form.cleaned_data['title']
post.content = form.cleaned_data['content']
post.active = False
post.save()
return redirect('contributions')
else:
form = CreatePost(initial={
'title': post.title,
'content': post.content,
})
return render(request, 'posts/edit_post.html', {'form': form, 'post': post})
@groups_required('Administrateur', 'Super Admin')
def prending_posts(request):
posts = Post.objects.filter(active=False)
return render(request, 'posts/pending_posts.html')