diff --git a/lacommunaute/forum/tests/tests_views.py b/lacommunaute/forum/tests/tests_views.py
index ab16c1d6e..72a411b2d 100644
--- a/lacommunaute/forum/tests/tests_views.py
+++ b/lacommunaute/forum/tests/tests_views.py
@@ -7,10 +7,9 @@
from lacommunaute.forum.factories import CategoryForumFactory, ForumFactory
from lacommunaute.forum.views import ForumView
-from lacommunaute.forum_conversation.factories import PostFactory, TopicFactory
+from lacommunaute.forum_conversation.factories import CertifiedPostFactory, PostFactory, TopicFactory
from lacommunaute.forum_conversation.forms import PostForm
from lacommunaute.forum_conversation.models import Topic
-from lacommunaute.forum_upvote.factories import CertifiedPostFactory
from lacommunaute.users.factories import UserFactory
diff --git a/lacommunaute/forum_conversation/admin.py b/lacommunaute/forum_conversation/admin.py
index 1853a9608..4fe728e49 100644
--- a/lacommunaute/forum_conversation/admin.py
+++ b/lacommunaute/forum_conversation/admin.py
@@ -1,5 +1,8 @@
+from django.contrib import admin
from machina.apps.forum_conversation.admin import TopicAdmin as BaseTopicAdmin
+from lacommunaute.forum_conversation.models import CertifiedPost
+
# TODO vincentporte, need to be fixed, likers field does not appear like a raw id fields
class TopicAdmin(BaseTopicAdmin):
@@ -8,3 +11,15 @@ class TopicAdmin(BaseTopicAdmin):
"subscribers",
"likers",
)
+
+
+class CertifiedPostAdmin(admin.ModelAdmin):
+ list_display = ("topic", "post", "user")
+ raw_id_fields = (
+ "topic",
+ "post",
+ "user",
+ )
+
+
+admin.site.register(CertifiedPost, CertifiedPostAdmin)
diff --git a/lacommunaute/forum_conversation/factories.py b/lacommunaute/forum_conversation/factories.py
index 72f8c1519..8e33a8b47 100644
--- a/lacommunaute/forum_conversation/factories.py
+++ b/lacommunaute/forum_conversation/factories.py
@@ -3,8 +3,7 @@
from lacommunaute.forum.factories import ForumFactory
from lacommunaute.forum_conversation.forum_polls.factories import TopicPollVoteFactory
-from lacommunaute.forum_conversation.models import Topic
-from lacommunaute.forum_upvote.factories import CertifiedPostFactory
+from lacommunaute.forum_conversation.models import CertifiedPost, Topic
from lacommunaute.users.factories import UserFactory
@@ -14,6 +13,11 @@ class PostFactory(BasePostFactory):
poster = factory.SubFactory(UserFactory)
+class CertifiedPostFactory(factory.django.DjangoModelFactory):
+ class Meta:
+ model = CertifiedPost
+
+
class TopicFactory(BaseTopicFactory):
forum = factory.SubFactory(ForumFactory)
poster = factory.SubFactory(UserFactory)
diff --git a/lacommunaute/forum_conversation/migrations/0007_certifiedpost.py b/lacommunaute/forum_conversation/migrations/0007_certifiedpost.py
new file mode 100644
index 000000000..7949640f0
--- /dev/null
+++ b/lacommunaute/forum_conversation/migrations/0007_certifiedpost.py
@@ -0,0 +1,79 @@
+# Generated by Django 4.2.3 on 2023-07-19 08:59
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ("forum_conversation", "0006_topic_tags"),
+ ("forum_upvote", "0002_certifiedpost"),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="CertifiedPost",
+ fields=[
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("created", models.DateTimeField(auto_now_add=True, verbose_name="Creation date")),
+ ("updated", models.DateTimeField(auto_now=True, verbose_name="Update date")),
+ (
+ "post",
+ models.OneToOneField(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="certified_post",
+ to="forum_conversation.post",
+ verbose_name="Post",
+ ),
+ ),
+ (
+ "topic",
+ models.OneToOneField(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="certified_post",
+ to="forum_conversation.topic",
+ verbose_name="Topic",
+ ),
+ ),
+ (
+ "user",
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="certified_posts",
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="User",
+ ),
+ ),
+ ],
+ options={
+ "ordering": ["-created"],
+ },
+ ),
+ migrations.RunSQL(
+ """
+ INSERT INTO forum_conversation_certifiedpost (
+ id, created, updated, post_id, topic_id, user_id
+ )
+ SELECT
+ id, created, updated, post_id, topic_id, user_id
+ FROM
+ forum_upvote_certifiedpost;
+ SELECT
+ setval(
+ pg_get_serial_sequence('"forum_conversation_certifiedpost"','id'),
+ coalesce(max("id"), 1),
+ max("id") IS NOT null
+ )
+ FROM
+ "forum_conversation_certifiedpost";
+ """,
+ reverse_sql="""
+ INSERT INTO forum_upvote_certifiedpost (id, created, updated, post_id, topic_id, user_id)
+ SELECT id, created, updated, post_id, topic_id, user_id FROM forum_conversation_certifiedpost;
+ """,
+ ),
+ ]
diff --git a/lacommunaute/forum_conversation/models.py b/lacommunaute/forum_conversation/models.py
index e8d936403..3c8ffb713 100644
--- a/lacommunaute/forum_conversation/models.py
+++ b/lacommunaute/forum_conversation/models.py
@@ -3,6 +3,7 @@
from django.db.models import Count, Exists, OuterRef
from django.urls import reverse
from machina.apps.forum_conversation.abstract_models import AbstractPost, AbstractTopic
+from machina.models.abstract_models import DatedModel
from taggit.managers import TaggableManager
from lacommunaute.forum_member.shortcuts import get_forum_member_display_name
@@ -90,3 +91,40 @@ def poster_display_name(self):
@property
def is_certified(self):
return hasattr(self, "certified_post")
+
+
+class CertifiedPost(DatedModel):
+ topic = models.OneToOneField(
+ Topic,
+ related_name="certified_post",
+ on_delete=models.CASCADE,
+ verbose_name="Topic",
+ )
+
+ post = models.OneToOneField(
+ Post,
+ related_name="certified_post",
+ on_delete=models.CASCADE,
+ verbose_name="Post",
+ )
+ user = models.ForeignKey(
+ settings.AUTH_USER_MODEL,
+ related_name="certified_posts",
+ blank=True,
+ null=True,
+ on_delete=models.SET_NULL,
+ verbose_name="User",
+ )
+
+ class Meta:
+ ordering = [
+ "-created",
+ ]
+
+ def __str__(self):
+ return f"{self.post} - {self.topic} - {self.user}"
+
+ def save(self, *args, **kwargs):
+ if self.topic != self.post.topic:
+ raise ValueError("The post is not link to the topic")
+ super().save(*args, **kwargs)
diff --git a/lacommunaute/forum_conversation/shortcuts.py b/lacommunaute/forum_conversation/shortcuts.py
index fba68900f..02699f602 100644
--- a/lacommunaute/forum_conversation/shortcuts.py
+++ b/lacommunaute/forum_conversation/shortcuts.py
@@ -1,5 +1,6 @@
from django.db.models import Count, Exists, OuterRef, Prefetch, Q, QuerySet
+from lacommunaute.forum.enums import Kind as Forum_Kind
from lacommunaute.forum_conversation.models import Post, Topic
from lacommunaute.forum_upvote.models import UpVote
from lacommunaute.users.models import User
@@ -28,3 +29,11 @@ def get_posts_of_a_topic_except_first_one(topic: Topic, user: User) -> QuerySet[
upvotes_count=Count("upvotes"),
)
return qs.order_by("created")
+
+
+def can_certify_post(forum, user):
+ return (
+ user.is_authenticated
+ and forum.kind == Forum_Kind.PUBLIC_FORUM
+ and (user.groups.filter(forum=forum).exists() or user.is_staff)
+ )
diff --git a/lacommunaute/forum_conversation/tests/tests_models.py b/lacommunaute/forum_conversation/tests/tests_models.py
index 87459d9a9..a1422c682 100644
--- a/lacommunaute/forum_conversation/tests/tests_models.py
+++ b/lacommunaute/forum_conversation/tests/tests_models.py
@@ -1,9 +1,10 @@
from django.core.exceptions import ValidationError
+from django.db import IntegrityError
from django.test import TestCase
from django.urls import reverse
from lacommunaute.forum.factories import ForumFactory
-from lacommunaute.forum_conversation.factories import PostFactory, TopicFactory
+from lacommunaute.forum_conversation.factories import CertifiedPostFactory, PostFactory, TopicFactory
from lacommunaute.forum_conversation.models import Post, Topic
from lacommunaute.forum_member.shortcuts import get_forum_member_display_name
@@ -124,3 +125,20 @@ def test_topic_types(self):
self.assertEqual(0, Topic.TOPIC_POST)
self.assertEqual(1, Topic.TOPIC_STICKY)
self.assertEqual(2, Topic.TOPIC_ANNOUNCE)
+
+
+class CertifiedPostModelTest(TestCase):
+ def test_topic_is_unique(self):
+ topic = TopicFactory(with_certified_post=True)
+
+ with self.assertRaises(IntegrityError):
+ CertifiedPostFactory(topic=topic, post=topic.first_post, user=topic.poster)
+
+ def test_post_is_link_to_topic(self):
+ topic_to_be_certified = TopicFactory()
+ other_topic = TopicFactory(with_post=True)
+
+ with self.assertRaises(ValueError):
+ CertifiedPostFactory(
+ topic=topic_to_be_certified, post=other_topic.first_post, user=topic_to_be_certified.poster
+ )
diff --git a/lacommunaute/forum_conversation/tests/tests_shortcuts.py b/lacommunaute/forum_conversation/tests/tests_shortcuts.py
index d425ec1ea..d27c6ef1d 100644
--- a/lacommunaute/forum_conversation/tests/tests_shortcuts.py
+++ b/lacommunaute/forum_conversation/tests/tests_shortcuts.py
@@ -1,9 +1,12 @@
from django.contrib.auth.models import AnonymousUser
from django.test import TestCase
+from lacommunaute.forum.enums import Kind as ForumKind
+from lacommunaute.forum.factories import ForumFactory
from lacommunaute.forum_conversation.factories import PostFactory, TopicFactory
-from lacommunaute.forum_conversation.shortcuts import get_posts_of_a_topic_except_first_one
+from lacommunaute.forum_conversation.shortcuts import can_certify_post, get_posts_of_a_topic_except_first_one
from lacommunaute.forum_upvote.factories import UpVoteFactory
+from lacommunaute.users.factories import UserFactory
class GetPostsofaTopicExceptFirstOneTest(TestCase):
@@ -66,3 +69,30 @@ def test_topic_has_been_upvoted_by_the_user(self):
self.assertEqual(len(posts), 1)
self.assertEqual(post.upvotes_count, 1)
self.assertTrue(post.has_upvoted)
+
+
+class CanCertifyPostShortcutTest(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.user = UserFactory.create()
+ cls.forum = ForumFactory.create()
+
+ def test_user_is_not_authenticated(self):
+ self.assertFalse(can_certify_post(self.forum, AnonymousUser()))
+
+ def test_forum_is_private(self):
+ self.assertFalse(can_certify_post(ForumFactory.create(kind=ForumKind.PRIVATE_FORUM), self.user))
+
+ def test_forum_is_newsfeed(self):
+ self.assertFalse(can_certify_post(ForumFactory.create(kind=ForumKind.NEWS), self.user))
+
+ def test_user_is_not_in_the_forum_members_group(self):
+ self.assertFalse(can_certify_post(self.forum, self.user))
+
+ def test_user_is_in_the_forum_members_group(self):
+ self.forum.members_group.user_set.add(self.user)
+ self.assertTrue(can_certify_post(self.forum, self.user))
+
+ def test_user_is_staff(self):
+ self.user.is_staff = True
+ self.assertTrue(can_certify_post(self.forum, self.user))
diff --git a/lacommunaute/forum_conversation/tests/tests_views.py b/lacommunaute/forum_conversation/tests/tests_views.py
index 21f47a454..eb5915c26 100644
--- a/lacommunaute/forum_conversation/tests/tests_views.py
+++ b/lacommunaute/forum_conversation/tests/tests_views.py
@@ -15,11 +15,11 @@
from lacommunaute.forum.enums import Kind as ForumKind
from lacommunaute.forum.factories import ForumFactory
from lacommunaute.forum_conversation.enums import Filters
-from lacommunaute.forum_conversation.factories import PostFactory, TopicFactory
+from lacommunaute.forum_conversation.factories import CertifiedPostFactory, PostFactory, TopicFactory
from lacommunaute.forum_conversation.forms import PostForm
from lacommunaute.forum_conversation.models import Topic
from lacommunaute.forum_conversation.views import PostDeleteView, TopicCreateView
-from lacommunaute.forum_upvote.factories import CertifiedPostFactory, UpVoteFactory
+from lacommunaute.forum_upvote.factories import UpVoteFactory
from lacommunaute.notification.factories import BouncedEmailFactory
from lacommunaute.users.factories import UserFactory
diff --git a/lacommunaute/forum_conversation/tests/tests_views_htmx.py b/lacommunaute/forum_conversation/tests/tests_views_htmx.py
index 12872e870..da5380502 100644
--- a/lacommunaute/forum_conversation/tests/tests_views_htmx.py
+++ b/lacommunaute/forum_conversation/tests/tests_views_htmx.py
@@ -7,11 +7,11 @@
from machina.core.loading import get_class
from taggit.models import Tag
-from lacommunaute.forum_conversation.factories import PostFactory, TopicFactory
+from lacommunaute.forum_conversation.factories import CertifiedPostFactory, PostFactory, TopicFactory
from lacommunaute.forum_conversation.forms import PostForm
-from lacommunaute.forum_conversation.models import Topic
+from lacommunaute.forum_conversation.models import CertifiedPost, Topic
from lacommunaute.forum_conversation.views_htmx import PostListView
-from lacommunaute.forum_upvote.factories import CertifiedPostFactory, UpVoteFactory
+from lacommunaute.forum_upvote.factories import UpVoteFactory
from lacommunaute.notification.factories import BouncedEmailFactory
from lacommunaute.users.factories import UserFactory
@@ -468,3 +468,71 @@ def test_create_post_as_bounced_not_bounced_anonymous(self, *args):
self.topic.posts.values("content", "username", "approved").last(),
{"content": self.content, "username": username, "approved": False},
)
+
+
+class CertifiedPostViewTest(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.topic = TopicFactory(with_post=True)
+ cls.user = cls.topic.poster
+ assign_perm("can_read_forum", cls.user, cls.topic.forum)
+ cls.url = reverse(
+ "forum_conversation_extension:certify",
+ kwargs={
+ "forum_pk": cls.topic.forum.pk,
+ "forum_slug": cls.topic.forum.slug,
+ "pk": cls.topic.pk,
+ "slug": cls.topic.slug,
+ },
+ )
+ cls.form_data = {"post_pk": cls.topic.last_post.pk}
+
+ def test_get(self):
+ self.client.force_login(self.user)
+ response = self.client.get(self.url)
+ self.assertEqual(response.status_code, 405)
+
+ def test_post_instance_doesnt_exist(self):
+ self.client.force_login(self.user)
+ response = self.client.post(self.url, data={"post_pk": 9999})
+ self.assertEqual(response.status_code, 404)
+
+ def test_certify_without_permission(self):
+ self.client.force_login(self.user)
+ response = self.client.post(self.url, data=self.form_data)
+ self.assertEqual(response.status_code, 403)
+
+ def test_certify_with_permission(self):
+ self.topic.forum.members_group.user_set.add(self.user)
+ self.topic.forum.members_group.save()
+ self.client.force_login(self.user)
+ response = self.client.post(self.url, data=self.form_data)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(CertifiedPost.objects.count(), 1)
+ certified_post = CertifiedPost.objects.first()
+ self.assertEqual(certified_post.post, self.topic.last_post)
+ self.assertEqual(certified_post.user, self.user)
+ self.assertEqual(certified_post.topic, self.topic)
+ self.assertEqual(ForumReadTrack.objects.count(), 1)
+
+ def test_uncertify_with_permission(self):
+ self.topic.forum.members_group.user_set.add(self.user)
+ self.topic.forum.members_group.save()
+ CertifiedPost(topic=self.topic, post=self.topic.last_post, user=self.user).save()
+ self.client.force_login(self.user)
+ response = self.client.post(self.url, data=self.form_data)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(CertifiedPost.objects.count(), 0)
+ self.assertEqual(ForumReadTrack.objects.count(), 1)
+
+ def test_rendered_content(self):
+ self.topic.forum.members_group.user_set.add(self.user)
+ self.topic.forum.members_group.save()
+ self.client.force_login(self.user)
+ response = self.client.post(self.url, data=self.form_data)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, f'
')
+ self.assertTemplateUsed(response, "forum_conversation/partials/posts_list.html")
diff --git a/lacommunaute/forum_conversation/urls.py b/lacommunaute/forum_conversation/urls.py
index 2bb0ddf6c..4689b1023 100644
--- a/lacommunaute/forum_conversation/urls.py
+++ b/lacommunaute/forum_conversation/urls.py
@@ -2,6 +2,7 @@
from lacommunaute.forum_conversation.views import NewsFeedTopicListView, TopicListView
from lacommunaute.forum_conversation.views_htmx import (
+ CertifiedPostView,
ForumTopicListView,
PostFeedCreateView,
PostListView,
@@ -19,6 +20,7 @@
path("topic/
-/showmore/posts", PostListView.as_view(), name="showmore_posts"),
path("topic/-/showmore/certified", TopicCertifiedPostView.as_view(), name="showmore_certified"),
path("topic/-/comment", PostFeedCreateView.as_view(), name="post_create"),
+ path("topic/-/certify", CertifiedPostView.as_view(), name="certify"),
path("topic/", ForumTopicListView.as_view(), name="topic_list"),
]
diff --git a/lacommunaute/forum_conversation/views.py b/lacommunaute/forum_conversation/views.py
index 6a23d5a51..8100e2164 100644
--- a/lacommunaute/forum_conversation/views.py
+++ b/lacommunaute/forum_conversation/views.py
@@ -12,8 +12,7 @@
from lacommunaute.forum_conversation.enums import Filters
from lacommunaute.forum_conversation.forms import PostForm, TopicForm
from lacommunaute.forum_conversation.models import Topic
-from lacommunaute.forum_conversation.shortcuts import get_posts_of_a_topic_except_first_one
-from lacommunaute.forum_upvote.shortcuts import can_certify_post
+from lacommunaute.forum_conversation.shortcuts import can_certify_post, get_posts_of_a_topic_except_first_one
logger = logging.getLogger(__name__)
diff --git a/lacommunaute/forum_conversation/views_htmx.py b/lacommunaute/forum_conversation/views_htmx.py
index 3ff200ab5..9393e1cb9 100644
--- a/lacommunaute/forum_conversation/views_htmx.py
+++ b/lacommunaute/forum_conversation/views_htmx.py
@@ -9,9 +9,8 @@
from lacommunaute.forum.models import Forum
from lacommunaute.forum_conversation.forms import PostForm
-from lacommunaute.forum_conversation.models import Topic
-from lacommunaute.forum_conversation.shortcuts import get_posts_of_a_topic_except_first_one
-from lacommunaute.forum_upvote.shortcuts import can_certify_post
+from lacommunaute.forum_conversation.models import CertifiedPost, Post, Topic
+from lacommunaute.forum_conversation.shortcuts import can_certify_post, get_posts_of_a_topic_except_first_one
logger = logging.getLogger(__name__)
@@ -196,3 +195,52 @@ def get_controlled_object(self):
def perform_permissions_check(self, user, obj, perms):
return self.request.forum_permission_handler.can_add_post(obj, user)
+
+
+class CertifiedPostView(PermissionRequiredMixin, View):
+ def _can_certify_post(self, forum, user):
+ if not hasattr(self, "can_certify_post"):
+ self.can_certify_post = can_certify_post(forum, user)
+ return self.can_certify_post
+
+ def dispatch(self, request, *args, **kwargs):
+ if request.method != "POST":
+ return self.http_method_not_allowed(request)
+ return super().dispatch(request, *args, **kwargs)
+
+ def get_object(self):
+ if not hasattr(self, "object"):
+ self.object = get_object_or_404(
+ Post,
+ pk=self.request.POST["post_pk"],
+ )
+ return self.object
+
+ def post(self, request, **kwargs):
+ post = self.get_object()
+ certified_post = CertifiedPost.objects.filter(post=post)
+
+ if certified_post.exists():
+ certified_post.delete()
+ else:
+ CertifiedPost(post=post, topic=post.topic, user=request.user).save()
+
+ track_handler.mark_topic_read(post.topic, request.user)
+
+ return render(
+ request,
+ "forum_conversation/partials/posts_list.html",
+ context={
+ "topic": post.topic,
+ "posts": get_posts_of_a_topic_except_first_one(post.topic, request.user),
+ "form": PostForm(forum=post.topic.forum, user=request.user),
+ "next_url": post.topic.get_absolute_url(),
+ "can_certify_post": self._can_certify_post(post.topic.forum, request.user),
+ },
+ )
+
+ def get_controlled_object(self):
+ return self.get_object().topic.forum
+
+ def perform_permissions_check(self, user, obj, perms):
+ return self.request.forum_permission_handler.can_read_forum(obj, user) and self._can_certify_post(obj, user)
diff --git a/lacommunaute/forum_upvote/admin.py b/lacommunaute/forum_upvote/admin.py
index 3307f0533..cd732201a 100644
--- a/lacommunaute/forum_upvote/admin.py
+++ b/lacommunaute/forum_upvote/admin.py
@@ -1,6 +1,6 @@
from django.contrib import admin
-from lacommunaute.forum_upvote.models import CertifiedPost, UpVote
+from lacommunaute.forum_upvote.models import UpVote
class UpVoteAdmin(admin.ModelAdmin):
@@ -11,14 +11,4 @@ class UpVoteAdmin(admin.ModelAdmin):
)
-class CertifiedPostAdmin(admin.ModelAdmin):
- list_display = ("topic", "post", "user")
- raw_id_fields = (
- "topic",
- "post",
- "user",
- )
-
-
-admin.site.register(CertifiedPost, CertifiedPostAdmin)
admin.site.register(UpVote, UpVoteAdmin)
diff --git a/lacommunaute/forum_upvote/factories.py b/lacommunaute/forum_upvote/factories.py
index a013112fe..38c49326c 100644
--- a/lacommunaute/forum_upvote/factories.py
+++ b/lacommunaute/forum_upvote/factories.py
@@ -1,14 +1,9 @@
import factory
import factory.django
-from lacommunaute.forum_upvote.models import CertifiedPost, UpVote
+from lacommunaute.forum_upvote.models import UpVote
class UpVoteFactory(factory.django.DjangoModelFactory):
class Meta:
model = UpVote
-
-
-class CertifiedPostFactory(factory.django.DjangoModelFactory):
- class Meta:
- model = CertifiedPost
diff --git a/lacommunaute/forum_upvote/migrations/0003_delete_certifiedpost.py b/lacommunaute/forum_upvote/migrations/0003_delete_certifiedpost.py
new file mode 100644
index 000000000..ca40f5bdd
--- /dev/null
+++ b/lacommunaute/forum_upvote/migrations/0003_delete_certifiedpost.py
@@ -0,0 +1,13 @@
+# Generated by Django 4.2.3 on 2023-07-19 09:28
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+ dependencies = [("forum_upvote", "0002_certifiedpost"), ("forum_conversation", "0007_certifiedpost")]
+
+ operations = [
+ migrations.DeleteModel(
+ name="CertifiedPost",
+ ),
+ ]
diff --git a/lacommunaute/forum_upvote/models.py b/lacommunaute/forum_upvote/models.py
index 5823664b9..f13590ca1 100644
--- a/lacommunaute/forum_upvote/models.py
+++ b/lacommunaute/forum_upvote/models.py
@@ -1,8 +1,7 @@
from django.conf import settings
from django.db import models
-from machina.models.abstract_models import DatedModel
-from lacommunaute.forum_conversation.models import Post, Topic
+from lacommunaute.forum_conversation.models import Post
class UpVote(models.Model):
@@ -32,40 +31,3 @@ class Meta:
ordering = [
"-created_at",
]
-
-
-class CertifiedPost(DatedModel):
- topic = models.OneToOneField(
- Topic,
- related_name="certified_post",
- on_delete=models.CASCADE,
- verbose_name="Topic",
- )
-
- post = models.OneToOneField(
- Post,
- related_name="certified_post",
- on_delete=models.CASCADE,
- verbose_name="Post",
- )
- user = models.ForeignKey(
- settings.AUTH_USER_MODEL,
- related_name="certified_posts",
- blank=True,
- null=True,
- on_delete=models.SET_NULL,
- verbose_name="User",
- )
-
- class Meta:
- ordering = [
- "-created",
- ]
-
- def __str__(self):
- return f"{self.post} - {self.topic} - {self.user}"
-
- def save(self, *args, **kwargs):
- if self.topic != self.post.topic:
- raise ValueError("The post is not link to the topic")
- super().save(*args, **kwargs)
diff --git a/lacommunaute/forum_upvote/shortcuts.py b/lacommunaute/forum_upvote/shortcuts.py
deleted file mode 100644
index daefa9935..000000000
--- a/lacommunaute/forum_upvote/shortcuts.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from lacommunaute.forum.enums import Kind as Forum_Kind
-
-
-def can_certify_post(forum, user):
- return (
- user.is_authenticated
- and forum.kind == Forum_Kind.PUBLIC_FORUM
- and (user.groups.filter(forum=forum).exists() or user.is_staff)
- )
diff --git a/lacommunaute/forum_upvote/tests/tests_models.py b/lacommunaute/forum_upvote/tests/tests_models.py
index df314319c..4a9a6885e 100644
--- a/lacommunaute/forum_upvote/tests/tests_models.py
+++ b/lacommunaute/forum_upvote/tests/tests_models.py
@@ -2,7 +2,6 @@
from django.test import TestCase
from lacommunaute.forum_conversation.factories import TopicFactory
-from lacommunaute.forum_upvote.factories import CertifiedPostFactory
from lacommunaute.forum_upvote.models import UpVote
@@ -13,20 +12,3 @@ def test_post_and_voter_are_uniques_together(self):
with self.assertRaises(IntegrityError):
UpVote.objects.create(post=topic.first_post, voter=topic.first_post.poster)
-
-
-class CertifiedPostModelTest(TestCase):
- def test_topic_is_unique(self):
- topic = TopicFactory(with_certified_post=True)
-
- with self.assertRaises(IntegrityError):
- CertifiedPostFactory(topic=topic, post=topic.first_post, user=topic.poster)
-
- def test_post_is_link_to_topic(self):
- topic_to_be_certified = TopicFactory()
- other_topic = TopicFactory(with_post=True)
-
- with self.assertRaises(ValueError):
- CertifiedPostFactory(
- topic=topic_to_be_certified, post=other_topic.first_post, user=topic_to_be_certified.poster
- )
diff --git a/lacommunaute/forum_upvote/tests/tests_shortcuts.py b/lacommunaute/forum_upvote/tests/tests_shortcuts.py
deleted file mode 100644
index 704c196f3..000000000
--- a/lacommunaute/forum_upvote/tests/tests_shortcuts.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from django.contrib.auth.models import AnonymousUser
-from django.test import TestCase
-
-from lacommunaute.forum.enums import Kind as ForumKind
-from lacommunaute.forum.factories import ForumFactory
-from lacommunaute.forum_upvote.shortcuts import can_certify_post
-from lacommunaute.users.factories import UserFactory
-
-
-class CanCertifyPostShortcutTest(TestCase):
- @classmethod
- def setUpTestData(cls):
- cls.user = UserFactory.create()
- cls.forum = ForumFactory.create()
-
- def test_user_is_not_authenticated(self):
- self.assertFalse(can_certify_post(self.forum, AnonymousUser()))
-
- def test_forum_is_private(self):
- self.assertFalse(can_certify_post(ForumFactory.create(kind=ForumKind.PRIVATE_FORUM), self.user))
-
- def test_forum_is_newsfeed(self):
- self.assertFalse(can_certify_post(ForumFactory.create(kind=ForumKind.NEWS), self.user))
-
- def test_user_is_not_in_the_forum_members_group(self):
- self.assertFalse(can_certify_post(self.forum, self.user))
-
- def test_user_is_in_the_forum_members_group(self):
- self.forum.members_group.user_set.add(self.user)
- self.assertTrue(can_certify_post(self.forum, self.user))
-
- def test_user_is_staff(self):
- self.user.is_staff = True
- self.assertTrue(can_certify_post(self.forum, self.user))
diff --git a/lacommunaute/forum_upvote/tests/tests_views.py b/lacommunaute/forum_upvote/tests/tests_views.py
index 97df5b923..270c43721 100644
--- a/lacommunaute/forum_upvote/tests/tests_views.py
+++ b/lacommunaute/forum_upvote/tests/tests_views.py
@@ -5,7 +5,7 @@
from machina.core.loading import get_class
from lacommunaute.forum_conversation.factories import PostFactory, TopicFactory
-from lacommunaute.forum_upvote.models import CertifiedPost, UpVote
+from lacommunaute.forum_upvote.models import UpVote
from lacommunaute.users.factories import UserFactory
@@ -74,63 +74,3 @@ def test_topic_is_marked_as_read_when_upvoting(self):
self.assertEqual(response.status_code, 200)
self.assertEqual(1, ForumReadTrack.objects.count())
-
-
-class CertifiedPostViewTest(TestCase):
- @classmethod
- def setUpTestData(cls):
- cls.topic = TopicFactory(with_post=True)
- cls.user = cls.topic.poster
- assign_perm("can_read_forum", cls.user, cls.topic.forum)
- cls.url = reverse("forum_upvote:certify")
- cls.form_data = {"post_pk": cls.topic.last_post.pk}
-
- def test_get(self):
- self.client.force_login(self.user)
- response = self.client.get(self.url)
- self.assertEqual(response.status_code, 405)
-
- def test_post_instance_doesnt_exist(self):
- self.client.force_login(self.user)
- response = self.client.post(self.url, data={"post_pk": 9999})
- self.assertEqual(response.status_code, 404)
-
- def test_certify_without_permission(self):
- self.client.force_login(self.user)
- response = self.client.post(self.url, data=self.form_data)
- self.assertEqual(response.status_code, 403)
-
- def test_certify_with_permission(self):
- self.topic.forum.members_group.user_set.add(self.user)
- self.topic.forum.members_group.save()
- self.client.force_login(self.user)
- response = self.client.post(self.url, data=self.form_data)
-
- self.assertEqual(response.status_code, 200)
- self.assertEqual(CertifiedPost.objects.count(), 1)
- certified_post = CertifiedPost.objects.first()
- self.assertEqual(certified_post.post, self.topic.last_post)
- self.assertEqual(certified_post.user, self.user)
- self.assertEqual(certified_post.topic, self.topic)
- self.assertEqual(ForumReadTrack.objects.count(), 1)
-
- def test_uncertify_with_permission(self):
- self.topic.forum.members_group.user_set.add(self.user)
- self.topic.forum.members_group.save()
- CertifiedPost(topic=self.topic, post=self.topic.last_post, user=self.user).save()
- self.client.force_login(self.user)
- response = self.client.post(self.url, data=self.form_data)
-
- self.assertEqual(response.status_code, 200)
- self.assertEqual(CertifiedPost.objects.count(), 0)
- self.assertEqual(ForumReadTrack.objects.count(), 1)
-
- def test_rendered_content(self):
- self.topic.forum.members_group.user_set.add(self.user)
- self.topic.forum.members_group.save()
- self.client.force_login(self.user)
- response = self.client.post(self.url, data=self.form_data)
-
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, f'')
- self.assertTemplateUsed(response, "forum_conversation/partials/posts_list.html")
diff --git a/lacommunaute/forum_upvote/urls.py b/lacommunaute/forum_upvote/urls.py
index 8c191a302..212d6c78b 100644
--- a/lacommunaute/forum_upvote/urls.py
+++ b/lacommunaute/forum_upvote/urls.py
@@ -1,6 +1,6 @@
from django.urls import path
-from lacommunaute.forum_upvote.views import CertifiedPostView, PostUpvoteView
+from lacommunaute.forum_upvote.views import PostUpvoteView
app_name = "forum_upvote"
@@ -8,5 +8,4 @@
urlpatterns = [
path("upvote/", PostUpvoteView.as_view(), name="upvote"),
- path("certify/", CertifiedPostView.as_view(), name="certify"),
]
diff --git a/lacommunaute/forum_upvote/views.py b/lacommunaute/forum_upvote/views.py
index 2d7f23150..61fb70c37 100644
--- a/lacommunaute/forum_upvote/views.py
+++ b/lacommunaute/forum_upvote/views.py
@@ -4,11 +4,8 @@
from django.views import View
from machina.core.loading import get_class
-from lacommunaute.forum_conversation.forms import PostForm
from lacommunaute.forum_conversation.models import Post
-from lacommunaute.forum_conversation.shortcuts import get_posts_of_a_topic_except_first_one
-from lacommunaute.forum_upvote.models import CertifiedPost, UpVote
-from lacommunaute.forum_upvote.shortcuts import can_certify_post
+from lacommunaute.forum_upvote.models import UpVote
logger = logging.getLogger(__name__)
@@ -56,52 +53,3 @@ def get_controlled_object(self):
def perform_permissions_check(self, user, obj, perms):
return self.request.forum_permission_handler.can_read_forum(obj, user)
-
-
-class CertifiedPostView(PermissionRequiredMixin, View):
- def _can_certify_post(self, forum, user):
- if not hasattr(self, "can_certify_post"):
- self.can_certify_post = can_certify_post(forum, user)
- return self.can_certify_post
-
- def dispatch(self, request, *args, **kwargs):
- if request.method != "POST":
- return self.http_method_not_allowed(request)
- return super().dispatch(request, *args, **kwargs)
-
- def get_object(self):
- if not hasattr(self, "object"):
- self.object = get_object_or_404(
- Post,
- pk=self.request.POST["post_pk"],
- )
- return self.object
-
- def post(self, request, **kwargs):
- post = self.get_object()
- certified_post = CertifiedPost.objects.filter(post=post)
-
- if certified_post.exists():
- certified_post.delete()
- else:
- CertifiedPost(post=post, topic=post.topic, user=request.user).save()
-
- track_handler.mark_topic_read(post.topic, request.user)
-
- return render(
- request,
- "forum_conversation/partials/posts_list.html",
- context={
- "topic": post.topic,
- "posts": get_posts_of_a_topic_except_first_one(post.topic, request.user),
- "form": PostForm(forum=post.topic.forum, user=request.user),
- "next_url": post.topic.get_absolute_url(),
- "can_certify_post": self._can_certify_post(post.topic.forum, request.user),
- },
- )
-
- def get_controlled_object(self):
- return self.get_object().topic.forum
-
- def perform_permissions_check(self, user, obj, perms):
- return self.request.forum_permission_handler.can_read_forum(obj, user) and self._can_certify_post(obj, user)
diff --git a/lacommunaute/templates/forum_conversation/partials/post_certified_actions.html b/lacommunaute/templates/forum_conversation/partials/post_certified_actions.html
index d03298aaf..eae5989ff 100644
--- a/lacommunaute/templates/forum_conversation/partials/post_certified_actions.html
+++ b/lacommunaute/templates/forum_conversation/partials/post_certified_actions.html
@@ -1,7 +1,7 @@
{%if can_certify_post %}
{% if not topic.is_certified or post.is_certified %}
-
-