First Commit
This commit is contained in:
commit
ce0758fbbb
496 changed files with 52062 additions and 0 deletions
0
forum/__init__.py
Executable file
0
forum/__init__.py
Executable file
33
forum/admin.py
Executable file
33
forum/admin.py
Executable file
|
|
@ -0,0 +1,33 @@
|
|||
from django.contrib import admin
|
||||
from .models import Category, Forum, Topic, Post
|
||||
|
||||
class CategoryAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'description', 'created', 'updated')
|
||||
list_filter = ('created', 'updated')
|
||||
search_fields = ('name', 'description')
|
||||
ordering = ('-created',)
|
||||
fields = ('name', 'description')
|
||||
|
||||
class ForumAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'author', 'category', 'created', 'updated')
|
||||
list_filter = ('category', 'author', 'created', 'updated')
|
||||
search_fields = ('name', 'description')
|
||||
ordering = ('-created',)
|
||||
fields = ('category', 'author', 'name', 'description')
|
||||
|
||||
class TopicAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'author', 'created', 'updated')
|
||||
list_filter = ('forum', 'author', 'created', 'updated', 'state')
|
||||
search_fields = ('title',)
|
||||
ordering = ('-created',)
|
||||
fields = ('forum', 'author', 'title', 'state')
|
||||
|
||||
class PostAdmin(admin.ModelAdmin):
|
||||
list_display = ('type', 'topic', 'author', 'created', 'updated')
|
||||
list_filter = ('topic', 'author', 'created', 'updated', 'type')
|
||||
fields = ('topic', 'author', 'content', 'type')
|
||||
|
||||
admin.site.register(Category, CategoryAdmin)
|
||||
admin.site.register(Forum, ForumAdmin)
|
||||
admin.site.register(Topic, TopicAdmin)
|
||||
admin.site.register(Post, PostAdmin)
|
||||
6
forum/apps.py
Executable file
6
forum/apps.py
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ForumConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "forum"
|
||||
26
forum/forms.py
Executable file
26
forum/forms.py
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
from django import forms
|
||||
|
||||
class CreateTopic(forms.Form):
|
||||
TOPIC_TYPE_CHOICES = (
|
||||
('normal', 'Normal'),
|
||||
('announce', 'Annonce'),
|
||||
)
|
||||
title = forms.CharField(
|
||||
max_length=150,
|
||||
label='',
|
||||
widget=forms.TextInput(attrs={'placeholder': 'Titre du sujet'})
|
||||
)
|
||||
content = forms.CharField(
|
||||
label='',
|
||||
widget=forms.Textarea(attrs={'placeholder': 'Contenu du sujet'})
|
||||
)
|
||||
type = forms.ChoiceField(choices=TOPIC_TYPE_CHOICES, label='Type de sujet')
|
||||
|
||||
class CreatePost(forms.Form):
|
||||
content = forms.CharField(
|
||||
label='',
|
||||
widget=forms.Textarea(attrs={'placeholder': 'Contenu du message'})
|
||||
)
|
||||
|
||||
class EditPost(forms.Form):
|
||||
content = forms.CharField(widget=forms.Textarea, label="Message")
|
||||
47
forum/middleware.py
Executable file
47
forum/middleware.py
Executable file
|
|
@ -0,0 +1,47 @@
|
|||
from django.utils.deprecation import MiddlewareMixin
|
||||
from .models import Forum, Topic, Post
|
||||
from users.models import User
|
||||
from django.db.models import Count
|
||||
|
||||
class ForumStatsMiddleware(MiddlewareMixin):
|
||||
# On récupère les statistiques du forum pour les affiché dans le menu
|
||||
def process_request(self, request):
|
||||
# Nombre total de forums
|
||||
total_forums = Forum.objects.count()
|
||||
|
||||
# Nombre total de topics
|
||||
total_topics = Topic.objects.count()
|
||||
|
||||
# Nombre total de posts
|
||||
total_posts = Post.objects.count()
|
||||
|
||||
# Utilisateur ayant posté le plus de messages
|
||||
user_with_most_posts = User.objects.annotate(num_posts=Count('post')).order_by('-num_posts').first()
|
||||
|
||||
# Nombre de messages de l'utilisateur ayant posté le plus de messages
|
||||
most_posts = user_with_most_posts.num_posts if user_with_most_posts else 0
|
||||
|
||||
# Utilisateur ayant créé le plus de topics
|
||||
user_with_most_topics = None
|
||||
most_topics = 0
|
||||
for user in User.objects.all():
|
||||
if user.topic_set.count() > most_topics:
|
||||
user_with_most_topics = user
|
||||
most_topics = user.topic_set.count()
|
||||
|
||||
# Dernier message posté
|
||||
last_post = Post.objects.latest('created') if total_posts > 0 else None
|
||||
|
||||
# Derneir topic créé
|
||||
last_topic = Topic.objects.latest('created') if total_topics > 0 else None
|
||||
|
||||
# Ajouter les variables à l'objet request
|
||||
request.total_forums = total_forums
|
||||
request.total_topics = total_topics
|
||||
request.total_posts = total_posts
|
||||
request.user_with_most_posts = user_with_most_posts
|
||||
request.most_posts = most_posts
|
||||
request.user_with_most_topics = user_with_most_topics
|
||||
request.most_topics = most_topics
|
||||
request.last_post = last_post
|
||||
request.last_topic = last_topic
|
||||
98
forum/migrations/0001_initial.py
Executable file
98
forum/migrations/0001_initial.py
Executable file
|
|
@ -0,0 +1,98 @@
|
|||
# Generated by Django 4.2.16 on 2024-10-22 11:02
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Category",
|
||||
fields=[
|
||||
("id", models.AutoField(primary_key=True, serialize=False)),
|
||||
("name", models.CharField(max_length=50)),
|
||||
("description", models.CharField(max_length=100)),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("updated", models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Forum",
|
||||
fields=[
|
||||
("id", models.AutoField(primary_key=True, serialize=False)),
|
||||
("name", models.CharField(max_length=50)),
|
||||
("description", models.CharField(max_length=100)),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("updated", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"author",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"category",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="forum.category"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Topic",
|
||||
fields=[
|
||||
("id", models.AutoField(primary_key=True, serialize=False)),
|
||||
("title", models.CharField(max_length=50)),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("updated", models.DateTimeField(auto_now=True)),
|
||||
("state", models.CharField(default="open", max_length=10)),
|
||||
(
|
||||
"author",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"forum",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="forum.forum"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Post",
|
||||
fields=[
|
||||
("id", models.AutoField(primary_key=True, serialize=False)),
|
||||
("content", models.CharField(max_length=100)),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("updated", models.DateTimeField(auto_now=True)),
|
||||
("type", models.CharField(default="post", max_length=10)),
|
||||
("active", models.BooleanField(default=True)),
|
||||
(
|
||||
"author",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="forum_author",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"topic",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="forum.topic"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
51
forum/migrations/0002_alter_category_options_topicread.py
Executable file
51
forum/migrations/0002_alter_category_options_topicread.py
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
# Generated by Django 4.2.16 on 2024-10-22 18:36
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("forum", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="category",
|
||||
options={"verbose_name": "Catégorie", "verbose_name_plural": "Catégories"},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="TopicRead",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("last_read", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"topic",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="forum.topic"
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"unique_together": {("user", "topic")},
|
||||
},
|
||||
),
|
||||
]
|
||||
23
forum/migrations/0003_alter_post_topic.py
Executable file
23
forum/migrations/0003_alter_post_topic.py
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 4.2.16 on 2024-10-22 18:49
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("forum", "0002_alter_category_options_topicread"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="post",
|
||||
name="topic",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="posts",
|
||||
to="forum.topic",
|
||||
),
|
||||
),
|
||||
]
|
||||
18
forum/migrations/0004_alter_post_content.py
Normal file
18
forum/migrations/0004_alter_post_content.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.17 on 2024-12-23 16:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('forum', '0003_alter_post_topic'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='post',
|
||||
name='content',
|
||||
field=models.TextField(),
|
||||
),
|
||||
]
|
||||
0
forum/migrations/__init__.py
Executable file
0
forum/migrations/__init__.py
Executable file
87
forum/models.py
Executable file
87
forum/models.py
Executable file
|
|
@ -0,0 +1,87 @@
|
|||
from django.db import models
|
||||
from users.models import User
|
||||
|
||||
class Category(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
name = models.CharField(max_length=50)
|
||||
description = models.CharField(max_length=100)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Catégorie'
|
||||
verbose_name_plural = 'Catégories'
|
||||
|
||||
def __str__(self):
|
||||
return f'ForumCategory({self.id}, {self.name}, {self.description})'
|
||||
|
||||
class Forum(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
category = models.ForeignKey(Category, on_delete=models.CASCADE)
|
||||
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=50)
|
||||
description = models.CharField(max_length=100)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return f'Forum({self.id}, {self.category}, {self.name}, {self.description})'
|
||||
|
||||
class Topic(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
forum = models.ForeignKey(Forum, on_delete=models.CASCADE)
|
||||
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
title = models.CharField(max_length=50)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
state = models.CharField(max_length=10, default='open')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.id:
|
||||
self.state = 'open'
|
||||
super(Topic, self).save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return f'Topic({self.id}, {self.forum}, {self.title})'
|
||||
|
||||
def is_unread(self, user):
|
||||
if not user.is_authenticated:
|
||||
return False
|
||||
|
||||
last_read = TopicRead.objects.filter(
|
||||
user=user,
|
||||
topic=self
|
||||
).first()
|
||||
|
||||
if not last_read:
|
||||
return True
|
||||
|
||||
last_post = Post.objects.filter(topic=self).order_by('-created').first()
|
||||
if not last_post:
|
||||
return False
|
||||
|
||||
return last_post.created > last_read.last_read
|
||||
|
||||
class Post(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
topic = models.ForeignKey(Topic, on_delete=models.CASCADE, related_name='posts')
|
||||
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='forum_author')
|
||||
content = models.TextField()
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
updated = models.DateTimeField(auto_now=True)
|
||||
type = models.CharField(max_length=10, default='post')
|
||||
active = models.BooleanField(default=True)
|
||||
|
||||
def __str__(self):
|
||||
return f'Post({self.id}, {self.topic}, {self.author}, {self.content})'
|
||||
|
||||
class TopicRead(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
|
||||
last_read = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ['user', 'topic']
|
||||
|
||||
def __str__(self):
|
||||
return f'TopicRead({self.id}, {self.topic}, {self.user})'
|
||||
0
forum/templatetags/__init__.py
Executable file
0
forum/templatetags/__init__.py
Executable file
9
forum/templatetags/_bbcode_tags.py
Normal file
9
forum/templatetags/_bbcode_tags.py
Normal 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)
|
||||
7
forum/templatetags/forum_extras.py
Executable file
7
forum/templatetags/forum_extras.py
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.filter
|
||||
def get_item(dictionary, key):
|
||||
return dictionary.get(key)
|
||||
7
forum/templatetags/paginator_tag.py
Executable file
7
forum/templatetags/paginator_tag.py
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.inclusion_tag('components/paginator.html', takes_context=True)
|
||||
def paginate(context):
|
||||
return context
|
||||
3
forum/tests.py
Executable file
3
forum/tests.py
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
20
forum/urls.py
Executable file
20
forum/urls.py
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
from django.urls import path
|
||||
from django.conf.urls.static import static
|
||||
|
||||
from passion_retro import settings
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.forum_home, name="forum_home"),
|
||||
path("<int:forum_id>/", views.topic_list, name="topic_list"),
|
||||
path("<int:forum_id>/create_topic/", views.create_topic, name="create_topic"),
|
||||
path("<int:forum_id>/<int:topic_id>/", views.topic_detail, name="post_list"),
|
||||
|
||||
path("<int:topic_id>/lock/", views.lock_topic, name="lock_topic"),
|
||||
path("<int:topic_id>/unlock/", views.unlock_topic, name="unlock_topic"),
|
||||
path("<int:forum_id>/<int:topic_id>/deactivate/", views.deactivate_topic, name="deactivate_topic"),
|
||||
path("<int:forum_id>/<int:topic_id>/activate/", views.activate_topic, name="activate_topic"),
|
||||
path("<int:post_id>/deactivate/", views.deactivate_post, name="deactivate_post"),
|
||||
path("<int:post_id>/activate/", views.activate_post, name="activate_post"),
|
||||
path('post/<int:post_id>/edit/', views.edit_post, name='forum_edit_post'),
|
||||
]
|
||||
303
forum/views.py
Executable file
303
forum/views.py
Executable file
|
|
@ -0,0 +1,303 @@
|
|||
from django.core.paginator import Paginator
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseForbidden
|
||||
from django.utils import timezone
|
||||
from .models import Category, Forum, Topic, Post, TopicRead
|
||||
from .forms import CreateTopic, CreatePost, EditPost
|
||||
from django.db.models import Max, F
|
||||
from users.decorators import groups_required
|
||||
from users.models import UserLevel
|
||||
|
||||
def forum_home(request):
|
||||
categories = Category.objects.all()
|
||||
forums = Forum.objects.all()
|
||||
|
||||
last_posts = {}
|
||||
count_topics = {}
|
||||
unread_topics = {}
|
||||
|
||||
for forum in forums:
|
||||
# Dernier post du forum avec une seule requête
|
||||
last_post = Post.objects.select_related(
|
||||
'topic',
|
||||
'author',
|
||||
'topic__forum'
|
||||
).filter(
|
||||
topic__forum=forum
|
||||
).order_by('-created').first()
|
||||
|
||||
last_posts[forum.id] = last_post
|
||||
|
||||
# Nombre de topics (pas de changement)
|
||||
count_topic = Topic.objects.filter(forum=forum).count()
|
||||
count_topics[forum.id] = count_topic
|
||||
|
||||
# Optimisation de la vérification des topics non lus
|
||||
if request.user.is_authenticated:
|
||||
# Récupérer le dernier post de chaque topic du forum en une seule requête
|
||||
latest_posts_per_topic = Post.objects.filter(
|
||||
topic__forum=forum
|
||||
).values('topic_id').annotate(
|
||||
last_post_date=Max('created')
|
||||
)
|
||||
|
||||
# Récupérer les dernières lectures de l'utilisateur pour tous les topics du forum
|
||||
topic_reads = TopicRead.objects.filter(
|
||||
user=request.user,
|
||||
topic__forum=forum
|
||||
).values_list('topic_id', 'last_read')
|
||||
|
||||
# Convertir en dictionnaire pour un accès plus rapide
|
||||
read_dates = dict(topic_reads)
|
||||
|
||||
# Un forum est non lu si au moins un topic a un post plus récent que la dernière lecture
|
||||
is_unread = False
|
||||
for post_info in latest_posts_per_topic:
|
||||
topic_id = post_info['topic_id']
|
||||
last_post_date = post_info['last_post_date']
|
||||
|
||||
# Si le topic n'a jamais été lu
|
||||
if topic_id not in read_dates:
|
||||
is_unread = True
|
||||
break
|
||||
|
||||
# Si le dernier post est plus récent que la dernière lecture
|
||||
if last_post_date > read_dates[topic_id]:
|
||||
is_unread = True
|
||||
break
|
||||
|
||||
unread_topics[forum.id] = is_unread
|
||||
else:
|
||||
unread_topics[forum.id] = False
|
||||
|
||||
print(unread_topics)
|
||||
|
||||
context = {
|
||||
'categories': categories,
|
||||
'forums': forums,
|
||||
'last_posts': last_posts,
|
||||
'count_topics': count_topics,
|
||||
'unread_topics': unread_topics,
|
||||
}
|
||||
|
||||
return render(request, "components/forum_home.html", context)
|
||||
|
||||
def topic_list(request, forum_id):
|
||||
forum = Forum.objects.get(id=forum_id)
|
||||
topics = Topic.objects.filter(forum=forum).order_by('-created')
|
||||
paginator = Paginator(topics, 20)
|
||||
|
||||
page_number = request.GET.get('page')
|
||||
topics = paginator.get_page(page_number)
|
||||
|
||||
count_posts = {}
|
||||
last_posts = {}
|
||||
page_numbers = {}
|
||||
unread_topics = {}
|
||||
|
||||
for topic in topics:
|
||||
count_post = Post.objects.filter(topic=topic).count()
|
||||
count_posts[topic.id] = count_post
|
||||
|
||||
last_post = Post.objects.filter(topic=topic).order_by('-created').first()
|
||||
last_posts[topic.id] = last_post
|
||||
|
||||
if request.user.is_authenticated:
|
||||
# Récupérer le dernier post du topic
|
||||
latest_post = Post.objects.filter(topic=topic).aggregate(last_post_date=Max('created'))['last_post_date']
|
||||
|
||||
# Récupérer la dernière lecture de l'utilisateur pour ce topic
|
||||
topic_read = TopicRead.objects.filter(user=request.user, topic=topic).first()
|
||||
|
||||
# Un topic est non lu si le dernier post est plus récent que la dernière lecture
|
||||
if topic_read:
|
||||
unread_topics[topic.id] = latest_post > topic_read.last_read
|
||||
else:
|
||||
unread_topics[topic.id] = True
|
||||
else:
|
||||
unread_topics[topic.id] = False
|
||||
|
||||
if last_post:
|
||||
# Position du dernier message (nombre de messages jusqu'à ce message)
|
||||
post_position = Post.objects.filter(
|
||||
topic=topic,
|
||||
created__lte=last_post.created
|
||||
).count()
|
||||
|
||||
# Calcul du numéro de page (20 messages par page comme dans topic_detail)
|
||||
page_number = (post_position + 19) // 20 # équivalent à ceil(post_position/20)
|
||||
page_numbers[topic.id] = page_number
|
||||
|
||||
context = {
|
||||
'forum': forum,
|
||||
'topics': topics,
|
||||
'count_posts': count_posts,
|
||||
'last_posts': last_posts,
|
||||
'page_numbers': page_numbers,
|
||||
'unread_topics': unread_topics,
|
||||
'is_paginated': topics.has_other_pages(),
|
||||
'page_obj': topics,
|
||||
'paginator': paginator,
|
||||
}
|
||||
|
||||
return render(request, "forum/topic_list.html", context)
|
||||
|
||||
@login_required
|
||||
def create_topic(request, forum_id):
|
||||
if not request.user.is_active:
|
||||
return HttpResponseForbidden("Vous n'êtes pas autorisé à créer un sujet.")
|
||||
|
||||
if request.method == 'POST':
|
||||
topic_form = request.POST
|
||||
# On créer le topic via le model Topic
|
||||
topic = Topic(
|
||||
forum=Forum.objects.get(id=forum_id),
|
||||
author=request.user,
|
||||
title=topic_form['title'],
|
||||
)
|
||||
|
||||
# Puis le premier post du topic
|
||||
post = Post(
|
||||
topic=topic,
|
||||
author=request.user,
|
||||
content=topic_form['content'],
|
||||
)
|
||||
|
||||
topic.save()
|
||||
post.save()
|
||||
messages.success(request, 'Topic créer avec succès.')
|
||||
UserLevel.objects.update(user=request.user, experience=F('experience') + 15)
|
||||
# on renvoie l'utilisateur sur la page du topic contenu l'id du forum et du topic créer
|
||||
return redirect('post_list', forum_id=forum_id, topic_id=topic.id)
|
||||
|
||||
forum = Forum.objects.get(id=forum_id)
|
||||
topic_form = CreateTopic()
|
||||
context = {
|
||||
'forum': forum,
|
||||
'topic_form': topic_form,
|
||||
}
|
||||
return render(request, "forum/create_topic.html", context)
|
||||
|
||||
def topic_detail(request, topic_id, forum_id):
|
||||
if request.method == 'POST':
|
||||
if not request.user.is_active or not request.user.is_authenticated:
|
||||
return HttpResponseForbidden("Vous n'êtes pas autorisé à poster un message.")
|
||||
|
||||
post_form = CreatePost(request.POST)
|
||||
if post_form.is_valid():
|
||||
post = Post(
|
||||
topic=Topic.objects.get(id=topic_id),
|
||||
author=request.user,
|
||||
content=post_form.cleaned_data['content'],
|
||||
)
|
||||
post.save()
|
||||
UserLevel.objects.update(user=request.user, experience=F('experience') + 10)
|
||||
messages.success(request, 'Message posté avec succès.')
|
||||
|
||||
topic = Topic.objects.get(id=topic_id)
|
||||
posts = Post.objects.filter(topic=topic, active=True)
|
||||
paginator = Paginator(posts, 20)
|
||||
|
||||
page_number = request.GET.get('page')
|
||||
posts = paginator.get_page(page_number)
|
||||
|
||||
# Marquer comme lu si l'utilisateur est connecté
|
||||
if request.user.is_authenticated:
|
||||
TopicRead.objects.update_or_create(
|
||||
user=request.user,
|
||||
topic=topic,
|
||||
defaults={'last_read': timezone.now()}
|
||||
)
|
||||
|
||||
# On compte le nombre de posts de l'auteur
|
||||
count_posts = {}
|
||||
for post in posts:
|
||||
count_post = Post.objects.filter(author=post.author).count()
|
||||
count_posts[post.author.id] = count_post
|
||||
|
||||
context = {
|
||||
'topic': topic,
|
||||
'posts': posts,
|
||||
'count_posts': count_posts,
|
||||
'post_form': CreatePost(),
|
||||
'is_paginated': posts.has_other_pages(),
|
||||
'page_obj': posts,
|
||||
'paginator': paginator,
|
||||
}
|
||||
|
||||
return render(request, "forum/topic_detail.html", context)
|
||||
|
||||
@groups_required('Modérateur', 'Admininistrateur', 'Super Admin')
|
||||
def lock_topic(request, topic_id):
|
||||
topic = Topic.objects.get(id=topic_id)
|
||||
topic.state = 'closed'
|
||||
topic.save()
|
||||
messages.success(request, 'Sujet verrouillé avec succès.')
|
||||
return redirect('post_list', forum_id=topic.forum.id, topic_id=topic_id)
|
||||
|
||||
@groups_required('Modérateur', 'Admininistrateur', 'Super Admin')
|
||||
def unlock_topic(request, topic_id):
|
||||
topic = Topic.objects.get(id=topic_id)
|
||||
topic.state = 'open'
|
||||
topic.save()
|
||||
messages.success(request, 'Sujet déverrouillé avec succès.')
|
||||
return redirect('post_list', forum_id=topic.forum.id, topic_id=topic_id)
|
||||
|
||||
@groups_required('Modérateur', 'Admininistrateur', 'Super Admin')
|
||||
def deactivate_post(request, post_id):
|
||||
post = Post.objects.get(id=post_id)
|
||||
post.active = False
|
||||
post.save()
|
||||
messages.success(request, 'Message désactivé avec succès.')
|
||||
return redirect('post_list', forum_id=post.topic.forum.id, topic_id=post.topic.id)
|
||||
|
||||
@groups_required('Modérateur', 'Admininistrateur', 'Super Admin')
|
||||
def activate_post(request, post_id):
|
||||
post = Post.objects.get(id=post_id)
|
||||
post.active = True
|
||||
post.save()
|
||||
messages.success(request, 'Message activé avec succès.')
|
||||
return redirect('post_list', forum_id=post.topic.forum.id, topic_id=post.topic.id)
|
||||
|
||||
@groups_required('Modérateur', 'Admininistrateur', 'Super Admin')
|
||||
def deactivate_topic(request, topic_id):
|
||||
topic = Topic.objects.get(id=topic_id)
|
||||
topic.active = False
|
||||
topic.save()
|
||||
messages.success(request, 'Sujet désactivé avec succès.')
|
||||
return redirect('topic_list', forum_id=topic.forum.id)
|
||||
|
||||
@groups_required('Modérateur', 'Admininistrateur', 'Super Admin')
|
||||
def activate_topic(request, topic_id):
|
||||
topic = Topic.objects.get(id=topic_id)
|
||||
topic.active = True
|
||||
topic.save()
|
||||
messages.success(request, 'Sujet activé avec succès.')
|
||||
return redirect('topic_list', forum_id=topic.forum.id)
|
||||
|
||||
@login_required
|
||||
def edit_post(request, post_id):
|
||||
post = Post.objects.get(id=post_id)
|
||||
|
||||
# Vérifier si l'utilisateur a le droit d'éditer
|
||||
if not (request.user == post.author or request.user.groups.filter(name__in=['Administrateur', 'Super Admin']).exists()):
|
||||
return HttpResponseForbidden("Vous n'êtes pas autorisé à éditer ce message.")
|
||||
|
||||
if request.method == 'POST':
|
||||
form = EditPost(request.POST)
|
||||
if form.is_valid():
|
||||
post.content = form.cleaned_data['content']
|
||||
post.updated = timezone.now()
|
||||
post.save()
|
||||
messages.success(request, 'Message modifié avec succès.')
|
||||
return redirect('post_list', forum_id=post.topic.forum.id, topic_id=post.topic.id)
|
||||
else:
|
||||
form = EditPost(initial={'content': post.content})
|
||||
|
||||
context = {
|
||||
'form': form,
|
||||
'post': post
|
||||
}
|
||||
return render(request, "forum/edit_post.html", context)
|
||||
Loading…
Add table
Add a link
Reference in a new issue