From a9588949f7e6349f16a27f5e7487c8c8107eda00 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Thu, 6 Jun 2024 11:14:18 +0200 Subject: [PATCH 01/22] =?UTF-8?q?feat(forum=5Fconversation):=20ajoute=20Bl?= =?UTF-8?q?ockedPost=20mod=C3=A8le?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0009_blockedpost.py | 25 +++++++++++++++ .../0010_blockedpost_block_reason.py | 17 ++++++++++ lacommunaute/forum_conversation/models.py | 31 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 lacommunaute/forum_conversation/migrations/0009_blockedpost.py create mode 100644 lacommunaute/forum_conversation/migrations/0010_blockedpost_block_reason.py diff --git a/lacommunaute/forum_conversation/migrations/0009_blockedpost.py b/lacommunaute/forum_conversation/migrations/0009_blockedpost.py new file mode 100644 index 000000000..a12d032f3 --- /dev/null +++ b/lacommunaute/forum_conversation/migrations/0009_blockedpost.py @@ -0,0 +1,25 @@ +# Generated by Django 5.0.6 on 2024-06-05 13:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("forum_conversation", "0008_remove_topic_likers"), + ] + + operations = [ + migrations.CreateModel( + name="BlockedPost", + 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")), + ("username", models.EmailField(blank=True, max_length=254, null=True, verbose_name="Adresse email")), + ("content", models.CharField(verbose_name="Content")), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/lacommunaute/forum_conversation/migrations/0010_blockedpost_block_reason.py b/lacommunaute/forum_conversation/migrations/0010_blockedpost_block_reason.py new file mode 100644 index 000000000..58c375a0d --- /dev/null +++ b/lacommunaute/forum_conversation/migrations/0010_blockedpost_block_reason.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.6 on 2024-06-05 14:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("forum_conversation", "0009_blockedpost"), + ] + + operations = [ + migrations.AddField( + model_name="blockedpost", + name="block_reason", + field=models.CharField(blank=True, null=True, verbose_name="Block Reason"), + ), + ] diff --git a/lacommunaute/forum_conversation/models.py b/lacommunaute/forum_conversation/models.py index 08c4dddc6..8df7d9686 100644 --- a/lacommunaute/forum_conversation/models.py +++ b/lacommunaute/forum_conversation/models.py @@ -199,3 +199,34 @@ 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) + + +class BlockedPost(DatedModel): + """ + When a user submits a Post and it is blocked by our quality control app (forum_moderation), + we save a record of the blocked Post in this table for reference purposes + + It is built of a subset of fields from django-machina's model AbstractPost + """ + + username = models.EmailField(blank=True, null=True, verbose_name=("Adresse email")) + content = models.CharField(verbose_name=_("Content")) + block_reason = models.CharField( + blank=True, + null=True, + verbose_name=_("Block Reason"), + ) + + def __str__(self): + return f"Blocked Message [{ str(self.created) }]" + + @classmethod + def create_from_post(cls, post): + """ + Creates a BlockedPost object from parameterised Post (machina) + """ + return cls.objects.create( + username=getattr(post, "username", ""), + content=post.content, + block_reason=post.update_reason, + ) From 29e60f9e123c76ecd473df0edeeb4db858fbdc80 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Thu, 6 Jun 2024 11:15:52 +0200 Subject: [PATCH 02/22] =?UTF-8?q?feat(forum=5Fconversation):=20sauvegarde?= =?UTF-8?q?=20les=20messages=20bloqu=C3=A9es=20dans=20le=20table=20Blocked?= =?UTF-8?q?Post?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lacommunaute/forum_conversation/forms.py | 8 +++-- .../tests/tests_views_htmx.py | 30 ++++++++++++++++++- lacommunaute/forum_moderation/utils.py | 28 ++++++++++++++--- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/lacommunaute/forum_conversation/forms.py b/lacommunaute/forum_conversation/forms.py index 13c057a7f..407f3da62 100644 --- a/lacommunaute/forum_conversation/forms.py +++ b/lacommunaute/forum_conversation/forms.py @@ -5,8 +5,8 @@ from machina.conf import settings as machina_settings from taggit.models import Tag -from lacommunaute.forum_conversation.models import Post -from lacommunaute.forum_moderation.utils import check_post_approbation +from lacommunaute.forum_conversation.models import BlockedPost, Post +from lacommunaute.forum_moderation.utils import BlockedPostReason, check_post_approbation class CreateUpdatePostMixin: @@ -18,6 +18,10 @@ def clean(self): ) if not post.approved: self.add_error(None, "Votre message ne respecte pas les règles de la communauté.") + + # track the blocked post if it was blocked for a reason we're tracking + if post.update_reason in BlockedPostReason.reasons_tracked_for_stats(): + BlockedPost.create_from_post(post) return cleaned_data def update_post(self, post): diff --git a/lacommunaute/forum_conversation/tests/tests_views_htmx.py b/lacommunaute/forum_conversation/tests/tests_views_htmx.py index bfb9e4b93..dee3f9713 100644 --- a/lacommunaute/forum_conversation/tests/tests_views_htmx.py +++ b/lacommunaute/forum_conversation/tests/tests_views_htmx.py @@ -9,9 +9,10 @@ from lacommunaute.forum_conversation.factories import CertifiedPostFactory, PostFactory, TopicFactory from lacommunaute.forum_conversation.forms import PostForm -from lacommunaute.forum_conversation.models import CertifiedPost, Topic +from lacommunaute.forum_conversation.models import BlockedPost, CertifiedPost, Topic from lacommunaute.forum_conversation.views_htmx import PostListView from lacommunaute.forum_moderation.factories import BlockedDomainNameFactory, BlockedEmailFactory +from lacommunaute.forum_moderation.utils import BlockedPostReason from lacommunaute.forum_upvote.factories import UpVoteFactory from lacommunaute.users.factories import UserFactory @@ -385,6 +386,14 @@ def test_create_post_as_blocked_not_blocked_anonymous(self, *args): self.topic.refresh_from_db() self.assertEqual(self.topic.posts.count(), 2) + # the blocked post should be recorded in the database + blocked_posts = BlockedPost.objects.all() + assert blocked_posts.count() == 1 + blocked_post = blocked_posts.first() + assert blocked_post.content == self.content + assert blocked_post.username == username + assert blocked_post.block_reason == BlockedPostReason.BLOCKED_USER.value + def test_create_post_with_nonfr_content(self): assign_perm("can_reply_to_topics", self.user, self.topic.forum) assign_perm("can_post_without_approval", self.user, self.topic.forum) @@ -407,6 +416,13 @@ def test_create_post_with_nonfr_content(self): self.topic.refresh_from_db() self.assertEqual(self.topic.posts.count(), 1) + # the blocked post should be recorded in the database + blocked_posts = BlockedPost.objects.all() + assert blocked_posts.count() == 1 + blocked_post = blocked_posts.first() + assert blocked_post.content == "популярные лучшие песни слушать онлайн" + assert blocked_post.block_reason == BlockedPostReason.ALTERNATIVE_LANGUAGE.value + def test_create_post_with_html_content(self): assign_perm("can_reply_to_topics", self.user, self.topic.forum) assign_perm("can_post_without_approval", self.user, self.topic.forum) @@ -432,6 +448,10 @@ def test_create_post_with_html_content(self): self.topic.refresh_from_db() self.assertEqual(self.topic.posts.count(), 1) + # we don't create a BlockedPost record for HTML content to avoid storing malicious code + blocked_posts = BlockedPost.objects.all() + assert blocked_posts.count() == 0 + def test_create_post_with_blocked_domain_name(self): BlockedDomainNameFactory(domain="blocked.com") @@ -456,6 +476,14 @@ def test_create_post_with_blocked_domain_name(self): self.topic.refresh_from_db() self.assertEqual(self.topic.posts.count(), 1) + # the blocked post should be recorded in the database + blocked_posts = BlockedPost.objects.all() + assert blocked_posts.count() == 1 + blocked_post = blocked_posts.first() + assert blocked_post.content == "la communauté" + assert blocked_post.username == "spam@blocked.com" + assert blocked_post.block_reason == BlockedPostReason.BLOCKED_DOMAIN.value + class CertifiedPostViewTest(TestCase): @classmethod diff --git a/lacommunaute/forum_moderation/utils.py b/lacommunaute/forum_moderation/utils.py index f409b2e0a..d87b3e794 100644 --- a/lacommunaute/forum_moderation/utils.py +++ b/lacommunaute/forum_moderation/utils.py @@ -1,3 +1,5 @@ +from enum import Enum + from django.conf import settings from langdetect import detect from machina.models.fields import render_func @@ -6,6 +8,21 @@ from lacommunaute.forum_moderation.models import BlockedDomainName, BlockedEmail +class BlockedPostReason(Enum): + HTML_TAGS = "HTML tags detected" + ALTERNATIVE_LANGUAGE = "Alternative Language detected" + BLOCKED_DOMAIN = "Blocked Domain detected" + BLOCKED_USER = "Blocked Email detected" + + @classmethod + def reasons_tracked_for_stats(cls): + """ + We store BlockedPost objects for posts which are of interest for review + The list of "reasons for interest" are returned by this function + """ + return [cls.ALTERNATIVE_LANGUAGE.value, cls.BLOCKED_DOMAIN.value, cls.BLOCKED_USER.value] + + def check_post_approbation(post): """ Check if a post should be approved or not @@ -17,11 +34,14 @@ def check_post_approbation(post): conditions = [ ( post.username and BlockedDomainName.objects.filter(domain=post.username.split("@")[-1]).exists(), - "Blocked Domain detected", + BlockedPostReason.BLOCKED_DOMAIN.value, + ), + (Markdown.html_removed_text_compat in rendered, BlockedPostReason.HTML_TAGS.value), + (detect(post.content.raw) not in settings.LANGUAGE_CODE, BlockedPostReason.ALTERNATIVE_LANGUAGE.value), + ( + post.username and BlockedEmail.objects.filter(email=post.username).exists(), + BlockedPostReason.BLOCKED_USER.value, ), - (Markdown.html_removed_text_compat in rendered, "HTML tags detected"), - (detect(post.content.raw) not in settings.LANGUAGE_CODE, "Alternative Language detected"), - (post.username and BlockedEmail.objects.filter(email=post.username).exists(), "Blocked Email detected"), ] post.approved, post.update_reason = next( ((not condition, reason) for condition, reason in conditions if condition), (post.approved, post.update_reason) From 089036f42d8c49f89d31f4945dd42f5b9a47308c Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Thu, 6 Jun 2024 11:42:37 +0200 Subject: [PATCH 03/22] feat(forum_conversation): BlockedPost admin --- lacommunaute/forum_conversation/admin.py | 8 +++++++- lacommunaute/forum_conversation/models.py | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lacommunaute/forum_conversation/admin.py b/lacommunaute/forum_conversation/admin.py index 8e7aa1a0e..6cffea1a5 100644 --- a/lacommunaute/forum_conversation/admin.py +++ b/lacommunaute/forum_conversation/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from machina.apps.forum_conversation.admin import TopicAdmin as BaseTopicAdmin -from lacommunaute.forum_conversation.models import CertifiedPost, Post, Topic +from lacommunaute.forum_conversation.models import BlockedPost, CertifiedPost, Post, Topic class PostInline(admin.StackedInline): @@ -34,6 +34,12 @@ class CertifiedPostAdmin(admin.ModelAdmin): ) +class BlockedPostAdmin(admin.ModelAdmin): + list_display = ("username", "created", "block_reason") + list_filter = ("block_reason",) + + admin.site.unregister(Topic) admin.site.register(Topic, TopicAdmin) admin.site.register(CertifiedPost, CertifiedPostAdmin) +admin.site.register(BlockedPost, BlockedPostAdmin) diff --git a/lacommunaute/forum_conversation/models.py b/lacommunaute/forum_conversation/models.py index 8df7d9686..b873de50a 100644 --- a/lacommunaute/forum_conversation/models.py +++ b/lacommunaute/forum_conversation/models.py @@ -217,6 +217,10 @@ class BlockedPost(DatedModel): verbose_name=_("Block Reason"), ) + class Meta: + verbose_name = _("Blocked Post") + verbose_name_plural = _("Blocked Posts") + def __str__(self): return f"Blocked Message [{ str(self.created) }]" From efe98b664ce9f9c2dcc64ae6a5ea421588a2c574 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Thu, 6 Jun 2024 11:43:08 +0200 Subject: [PATCH 04/22] =?UTF-8?q?update(forum=5Fconversation):=20traductio?= =?UTF-8?q?ns=20des=20mod=C3=A8les?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locale/fr/LC_MESSAGES/django.mo | Bin 14858 -> 15051 bytes locale/fr/LC_MESSAGES/django.po | 19 ++++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/locale/fr/LC_MESSAGES/django.mo b/locale/fr/LC_MESSAGES/django.mo index e95d06fc7d9ca8b888916750058669f1f4b03d41..93c5c151ca3ecbd27af386b1a3422887516ca66b 100644 GIT binary patch delta 4613 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 ZJ}KGbDa~zi&$o0 Date: Thu, 6 Jun 2024 11:45:07 +0200 Subject: [PATCH 05/22] =?UTF-8?q?fix(search):=20traduction=20fran=C3=A7ais?= =?UTF-8?q?e=20manquante?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locale/fr/LC_MESSAGES/django.mo | Bin 15051 -> 15107 bytes locale/fr/LC_MESSAGES/django.po | 4 ++++ 2 files changed, 4 insertions(+) diff --git a/locale/fr/LC_MESSAGES/django.mo b/locale/fr/LC_MESSAGES/django.mo index 93c5c151ca3ecbd27af386b1a3422887516ca66b..773460deaca74e3686dddfdf9850ea247f9ea0fc 100644 GIT binary patch delta 4422 zcmYk-3s4nR0LJknLMiG6d4m>L69P39P!uX5tb{l|a!Dd@&x?1V3&db|SFKpoOI(|~HI#rhShLx-?Gp22L4h;*M*fJ~;D zi0W_&YGAXF!JB6>iuTPa3fyb9p=O{BHS!-(9XN;ncoDC|%cuv(M!6k|M|C*W)-zG} z<)fBh465NGYbkc7?x3RwS5we~UqJP2E2=}EqAom)8u=+?_RM)y2RcU^6M(^}nTSAj zqz`H!15wvyV=Cs_`cudw%))5qUp-sH35~b`)$`q`j(vt|pbd510aOQ%+4`TT4*JKq z*LkdAs40&@%|N0(KM2)MhBYsS`PbUsZ%>q=Ixx@Ht5G9giE5}G^}r_7b-QeR4{GFJ zqdI;FwU>Ufoc)Y{Ka23ibSRsMqikdeMpIB`%e$by z27X0tzVoOKbhy@Spo=vMH8XLj>xZB=@i2_l`#*+4A5K)F_QDF(6gHqb^f79Lt*8dR zwe_Q@2mWHu|Bl+6mr)(^FwecQCol?n>SHCZ$drbL)3`& zq1N;$s)3WX{tNb{{u^pUJ?O3W&K;PB^HJ~p4%BncB9mh-+w(EKng7Q*F^YM*75Ab> z=HYAPhq0&`>5VxZ*wL6yy}XYxgK;xv;&J3w(>sn0h_|7ZaxLmZvlX?3ZK&_acbJH$ z;v8chp%5Hz%ocnC8?o>8?q)lLya?tJ>Om0+?u-mT%~&?7p*+;yC_=4q2`1wlYa^<` z<2V|-Qt5|<4uvcV)u@rRqNdWHdF_O$s2<;h9?VDjXvU*vYQ8=1v-J(A`+k8 zr!_5gGHP$7qdM;7*%L*m5l+WwTx_jHUAPrD z{-`O=MqNJ^gK#RUfhVyeR-=}%#{T{q>b@p)RMfTs|A=Dkz5-mY3MI&kx?z4V}>c~&1CHxij7FiKX^`L2}8Jmr2sMeluM13!IqMmmU)xpzNf4*AU{a)0}W;hhIb~&isUWD2M z3sF;Ck6QEXsD=-qrus0dV`nfFFQY~ll;(~s62qwXLUk|=SzVKj+DjFv=Q=(L>?^Yw zwYv|aX5=_(_g+9f=xrI$S3dwuE zn?13~FHtHXti0Jl9wm2?`9zEJG5Lb%h$cIIHJ-=`Z&TV%J|ZE6lEg?^n8|^RJ zYWXCNY$LA_y(jI*I}~P+IplRRj(kA01wSD=_PJbnF~{1nNFdLW-6TfuN-YI_UdE92 zV=#pY?G+sIzw*PB_0oMs-XO)KBe{XJkp?Z>nkPqPm(P+V;)&=>q~u2!J%=>skOfh8z^U!`$;*`(Lf&b zoeAzyu!hnuGM?y@IFoE7d&wShH+hQac#cdXZ>hr3N`{ix$YVsuBV<2$v$?Q)Wv7t5 w$+N3U%Zn?#rRCnTDdiI?CO7YoJ{cA|vb3_oJ89}C8~J61`%80kQdHM}0SgMa=l}o! delta 4367 zcmYk-32+W)0LJlGM509UrE&;O#SuB2NgQ#Epw4lXYAck`Dy43rajvs%+ys@@-87D> zBCd+ixQU}Rs%XnNre%zdmQt$p|7729raPYe-raBay?4zRS2WhS@}loj!*Q0pM3(p& z6I#NUfO5KOOut}b>R^AYhYK+Q3sLvq$M)!X(U=fSwGPKJoX^0rI3FwHr`QMgV!kns zsZidS0xq0K>N1O|B?xoSgFEc`5mcXkz(Bl#{`e;b;1jHg{-MUiV-o7TFLuD$7>DQW z@6U>LxESW%*aJ&*!&Fp{Gf@pJMfzq|q8i#}-H+-}5!S*>*b4o^-FiA8lWDr6I-H6c z*n7y}O$Jt=eKUsw&zc<64CJFmej3$*E9i^AVl}*jsyL{E+o3R2hpXB1x~S)xp_ZUE zs^PBIG<<>c5$LGm@f1|?6jaaFp*pk^b>k`2$S)wXW^SN5@EA*>iEwAa57m(n)Ig$8 z_a$NyHn!)(kVj2=1naM!&EtYbyb{&(EvSy|LN$<&y06gsJ*s2Z?C*E1f1sxN5o+Xq z72W59QT2ygV^K@mxFYkfA3AeE4fnM-3`C9i15`sZQ5DWd-Is08*P=$e8P(xEs6A9* zJ&JnnEUKQ%sP=B6>i^5JH~3U?A1sIbGhzJEND@&Ew8Y|$+Vj4sjtoIf^=RY;Ff%a@ zv+eJPP#q{jy?z%k3Lj!BI^n#;$rMImBiw`<$#ql@e?#`0d4TFbK%{#;5>;_LYGe&i zOO=9J^ERjsc0$ca4^(}9P;bo;_qt;;DCov%_J&M*zRbD``Db$ZqmCU$HE;q|(RZj0 zTtPK(%la2;W_(_D?+-z3-b$#MNWkjsKhuqZHp4j76wXFroACx99s%9XMvM zpFzDf7f~I$gH`b{GRx*AT2sBrSO+_!W@-`!I25K+P=yOnJzj=Ya099%Cs89ihgyQG zsD|#@>(5X#Q#Q)I&x6`Ck*L==4(nqJRDENulhM)I%%Y$Q=c5{0ff~_9R8RMz8aQar zk6}FLr%@yN8?|?0=ygL(L%r|IQ1zZfCc|8`*B{{o&TCd>{##SXWxh1BJLrQ?Q8V%! z+n1os*Qqh*U8}RvFcVYo0CJ0Yjyp7PU8;q1L!9CSY&tJXC`R@KwBxHL(d7 zTi`&4f=0F;HI>(}GLjh~?^_!_k&yD<^>;|ur)>ih5ys)6#g+?k3-4I~-02U?=) z?`nS^>|S@wBnql{HfkmoA9!DrMZs!4umjWF?#>wDX8b&k#B|>fa>{l)X3*xDa=B>&)KLE??f%pX;g*3payau z)qy9dJrP{j?LZCGb4jR5RWjCyXobuO|!%xZMh;7JO4;Abp{_mF*Po}n6!W!^m4+}ah@U_aE< z4?~S?JZi*?kQc_}qGs?6YBOFy-aK;+Rlk2C^Pf&3fgRNWbFmBFLp_+1x7!(5vVnvifT9; zHPty7jN7n09zu=mN7Tr!VhG+wb?_OodZrBDW9_A;sCs)N+sLG&Hno#OK~u5`wR;Ot z6&*%(>=LS>r}nyUBllY#fqE_(qp&0T;!q67Q5cAs_Ix?E;e0EqgAZLD0LI}&tc717gEr@ozM9f}Olo2ZYC!L!?w^YKLT;uqR0*nIxO&_{PQbwSIu}$sl{a6*ZG@kzHAx11l#ip!6Qm%I_gV zNOv-gXmJXO_lTgdH)~~Jc>gVwc9R2SF6lr@5gp^n60(fMks0I^(b3q&3`f1qzV>_$ z>WizR26;a#G^lc94yAFrWRC=FY0GbCbqlKGjNoh$nMs=3U$oUak!X@fmJ+=u-eU)a zbTXN&BX5#@L|gD6(NW}5{9^X7Wf4n0A_b(P*6ec%`n}gnS4zqkSavSO7bmPMRc?#?bY>p6kaD`WE06Cqe&OipLj?MqT^L^ zrnuz(sbkCAu_IY+&$F@$J>{c6;cSY%vKCvB-ei=$wi(m19(yXKuAr1pdJ%mR$C3@? wI5|X)kx4|yY%+{&)CtE4l1$c+p+v`Ea+a*m8y1{iI`2Wm&5*pib;1Jv1HN9ZxBvhE diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 32f48cf15..46163e08c 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -42,6 +42,10 @@ msgstr "Contenu" msgid "Block Reason" msgstr "Motif du blocage" +#: lacommunaute/search/forms.py:24 +msgid "Keywords or phrase" +msgstr "Mots clés ou phrase" + msgid "This topic type has been changed successfully." msgstr "Le type du sujet a été modifié avec succès." From 3dc9f2444ce4ccd8d24b5055f14c2a0b9cf0cd6b Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Thu, 6 Jun 2024 12:16:59 +0200 Subject: [PATCH 06/22] fix(build): migrations manquantes --- ...0011_alter_blockedpost_options_and_more.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 lacommunaute/forum_conversation/migrations/0011_alter_blockedpost_options_and_more.py diff --git a/lacommunaute/forum_conversation/migrations/0011_alter_blockedpost_options_and_more.py b/lacommunaute/forum_conversation/migrations/0011_alter_blockedpost_options_and_more.py new file mode 100644 index 000000000..f1f7e1319 --- /dev/null +++ b/lacommunaute/forum_conversation/migrations/0011_alter_blockedpost_options_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.6 on 2024-06-06 10:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("forum_conversation", "0010_blockedpost_block_reason"), + ] + + operations = [ + migrations.AlterModelOptions( + name="blockedpost", + options={"verbose_name": "Blocked Post", "verbose_name_plural": "Blocked Posts"}, + ), + migrations.AlterModelOptions( + name="certifiedpost", + options={ + "ordering": ["-created"], + "verbose_name": "Certified Post", + "verbose_name_plural": "Certified Posts", + }, + ), + ] From 548633b71a2d83473af08841230475a4091cf76a Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Thu, 6 Jun 2024 14:53:41 +0200 Subject: [PATCH 07/22] =?UTF-8?q?feat(forum=5Fmoderation):=20enregistrer?= =?UTF-8?q?=20les=20messages=20supprim=C3=A9s=20lors=20de=20la=20validatio?= =?UTF-8?q?n=20de=20la=20d=C3=A9sapprobation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/test_post_disapprove_view.py | 11 +++++++++++ lacommunaute/forum_moderation/utils.py | 8 +++++++- lacommunaute/forum_moderation/views.py | 11 +++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py index dfb36143d..0efef3207 100644 --- a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py +++ b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py @@ -1,9 +1,11 @@ import pytest # noqa from lacommunaute.forum_conversation.factories import TopicFactory, AnonymousPostFactory +from lacommunaute.forum_conversation.models import Post, BlockedPost from lacommunaute.forum_moderation.models import BlockedEmail from lacommunaute.users.factories import UserFactory from django.urls import reverse from lacommunaute.forum_moderation.factories import BlockedEmailFactory +from lacommunaute.forum_moderation.utils import BlockedPostReason def test_post_disapprove_view(client, db): @@ -15,6 +17,15 @@ def test_post_disapprove_view(client, db): assert response.status_code == 302 assert BlockedEmail.objects.get(email=disapproved_post.username) + # the original Post should be deleted, but a BlockedPost saved + assert Post.objects.count() == 0 + blocked_posts = BlockedPost.objects.all() + assert blocked_posts.count() == 1 + blocked_post = blocked_posts.first() + assert blocked_post.content == str(disapproved_post.content) + assert blocked_post.username == disapproved_post.username + assert blocked_post.block_reason == BlockedPostReason.MODERATOR_DISAPPROVAL.value + def test_post_disapprove_view_with_existing_blocked_email(client, db): disapproved_post = AnonymousPostFactory(topic=TopicFactory(approved=False)) diff --git a/lacommunaute/forum_moderation/utils.py b/lacommunaute/forum_moderation/utils.py index d87b3e794..d0b1335a1 100644 --- a/lacommunaute/forum_moderation/utils.py +++ b/lacommunaute/forum_moderation/utils.py @@ -13,6 +13,7 @@ class BlockedPostReason(Enum): ALTERNATIVE_LANGUAGE = "Alternative Language detected" BLOCKED_DOMAIN = "Blocked Domain detected" BLOCKED_USER = "Blocked Email detected" + MODERATOR_DISAPPROVAL = "Moderator disapproval" @classmethod def reasons_tracked_for_stats(cls): @@ -20,7 +21,12 @@ def reasons_tracked_for_stats(cls): We store BlockedPost objects for posts which are of interest for review The list of "reasons for interest" are returned by this function """ - return [cls.ALTERNATIVE_LANGUAGE.value, cls.BLOCKED_DOMAIN.value, cls.BLOCKED_USER.value] + return [ + cls.ALTERNATIVE_LANGUAGE.value, + cls.BLOCKED_DOMAIN.value, + cls.BLOCKED_USER.value, + cls.MODERATOR_DISAPPROVAL.value, + ] def check_post_approbation(post): diff --git a/lacommunaute/forum_moderation/views.py b/lacommunaute/forum_moderation/views.py index ec8ac84a1..c2092fef2 100644 --- a/lacommunaute/forum_moderation/views.py +++ b/lacommunaute/forum_moderation/views.py @@ -5,7 +5,9 @@ TopicDeleteView as BaseTopicDeleteView, ) +from lacommunaute.forum_conversation.models import BlockedPost from lacommunaute.forum_moderation.models import BlockedEmail +from lacommunaute.forum_moderation.utils import BlockedPostReason class TopicDeleteView(BaseTopicDeleteView): @@ -31,3 +33,12 @@ def post(self, request, *args, **kwargs): "l'adresse email de l'utilisateur est déjà dans la liste des emails bloqués.", ) return self.disapprove(request, *args, **kwargs) + + def disapprove(self, request, *args, **kwargs): + """ + Extends Machina's post disapproval behaviour to save the rejected post before deleting + """ + self.object = self.get_object() + self.object.update_reason = BlockedPostReason.MODERATOR_DISAPPROVAL.value + BlockedPost.create_from_post(self.object) + return super().disapprove(request, *args, **kwargs) From c5953fb00ca750a8043b143bfd03c2159fbb8b06 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Fri, 7 Jun 2024 12:16:11 +0200 Subject: [PATCH 08/22] =?UTF-8?q?refactor(forum):=20migration=20BlockedPos?= =?UTF-8?q?t=20=C3=A0=20forum=5Fmoderation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lacommunaute/forum_conversation/admin.py | 8 +---- lacommunaute/forum_conversation/forms.py | 3 +- .../0010_blockedpost_block_reason.py | 17 --------- ...0011_alter_blockedpost_options_and_more.py | 24 ------------- lacommunaute/forum_conversation/models.py | 35 ------------------ .../tests/tests_views_htmx.py | 3 +- lacommunaute/forum_moderation/admin.py | 8 ++++- .../migrations/0003_blockedpost.py} | 8 +++-- lacommunaute/forum_moderation/models.py | 36 +++++++++++++++++++ .../tests/test_post_disapprove_view.py | 4 +-- lacommunaute/forum_moderation/views.py | 3 +- 11 files changed, 56 insertions(+), 93 deletions(-) delete mode 100644 lacommunaute/forum_conversation/migrations/0010_blockedpost_block_reason.py delete mode 100644 lacommunaute/forum_conversation/migrations/0011_alter_blockedpost_options_and_more.py rename lacommunaute/{forum_conversation/migrations/0009_blockedpost.py => forum_moderation/migrations/0003_blockedpost.py} (69%) diff --git a/lacommunaute/forum_conversation/admin.py b/lacommunaute/forum_conversation/admin.py index 6cffea1a5..8e7aa1a0e 100644 --- a/lacommunaute/forum_conversation/admin.py +++ b/lacommunaute/forum_conversation/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from machina.apps.forum_conversation.admin import TopicAdmin as BaseTopicAdmin -from lacommunaute.forum_conversation.models import BlockedPost, CertifiedPost, Post, Topic +from lacommunaute.forum_conversation.models import CertifiedPost, Post, Topic class PostInline(admin.StackedInline): @@ -34,12 +34,6 @@ class CertifiedPostAdmin(admin.ModelAdmin): ) -class BlockedPostAdmin(admin.ModelAdmin): - list_display = ("username", "created", "block_reason") - list_filter = ("block_reason",) - - admin.site.unregister(Topic) admin.site.register(Topic, TopicAdmin) admin.site.register(CertifiedPost, CertifiedPostAdmin) -admin.site.register(BlockedPost, BlockedPostAdmin) diff --git a/lacommunaute/forum_conversation/forms.py b/lacommunaute/forum_conversation/forms.py index 407f3da62..e4a7bfb08 100644 --- a/lacommunaute/forum_conversation/forms.py +++ b/lacommunaute/forum_conversation/forms.py @@ -5,7 +5,8 @@ from machina.conf import settings as machina_settings from taggit.models import Tag -from lacommunaute.forum_conversation.models import BlockedPost, Post +from lacommunaute.forum_conversation.models import Post +from lacommunaute.forum_moderation.models import BlockedPost from lacommunaute.forum_moderation.utils import BlockedPostReason, check_post_approbation diff --git a/lacommunaute/forum_conversation/migrations/0010_blockedpost_block_reason.py b/lacommunaute/forum_conversation/migrations/0010_blockedpost_block_reason.py deleted file mode 100644 index 58c375a0d..000000000 --- a/lacommunaute/forum_conversation/migrations/0010_blockedpost_block_reason.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.0.6 on 2024-06-05 14:37 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("forum_conversation", "0009_blockedpost"), - ] - - operations = [ - migrations.AddField( - model_name="blockedpost", - name="block_reason", - field=models.CharField(blank=True, null=True, verbose_name="Block Reason"), - ), - ] diff --git a/lacommunaute/forum_conversation/migrations/0011_alter_blockedpost_options_and_more.py b/lacommunaute/forum_conversation/migrations/0011_alter_blockedpost_options_and_more.py deleted file mode 100644 index f1f7e1319..000000000 --- a/lacommunaute/forum_conversation/migrations/0011_alter_blockedpost_options_and_more.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.0.6 on 2024-06-06 10:16 - -from django.db import migrations - - -class Migration(migrations.Migration): - dependencies = [ - ("forum_conversation", "0010_blockedpost_block_reason"), - ] - - operations = [ - migrations.AlterModelOptions( - name="blockedpost", - options={"verbose_name": "Blocked Post", "verbose_name_plural": "Blocked Posts"}, - ), - migrations.AlterModelOptions( - name="certifiedpost", - options={ - "ordering": ["-created"], - "verbose_name": "Certified Post", - "verbose_name_plural": "Certified Posts", - }, - ), - ] diff --git a/lacommunaute/forum_conversation/models.py b/lacommunaute/forum_conversation/models.py index b873de50a..08c4dddc6 100644 --- a/lacommunaute/forum_conversation/models.py +++ b/lacommunaute/forum_conversation/models.py @@ -199,38 +199,3 @@ 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) - - -class BlockedPost(DatedModel): - """ - When a user submits a Post and it is blocked by our quality control app (forum_moderation), - we save a record of the blocked Post in this table for reference purposes - - It is built of a subset of fields from django-machina's model AbstractPost - """ - - username = models.EmailField(blank=True, null=True, verbose_name=("Adresse email")) - content = models.CharField(verbose_name=_("Content")) - block_reason = models.CharField( - blank=True, - null=True, - verbose_name=_("Block Reason"), - ) - - class Meta: - verbose_name = _("Blocked Post") - verbose_name_plural = _("Blocked Posts") - - def __str__(self): - return f"Blocked Message [{ str(self.created) }]" - - @classmethod - def create_from_post(cls, post): - """ - Creates a BlockedPost object from parameterised Post (machina) - """ - return cls.objects.create( - username=getattr(post, "username", ""), - content=post.content, - block_reason=post.update_reason, - ) diff --git a/lacommunaute/forum_conversation/tests/tests_views_htmx.py b/lacommunaute/forum_conversation/tests/tests_views_htmx.py index dee3f9713..c725bf45c 100644 --- a/lacommunaute/forum_conversation/tests/tests_views_htmx.py +++ b/lacommunaute/forum_conversation/tests/tests_views_htmx.py @@ -9,9 +9,10 @@ from lacommunaute.forum_conversation.factories import CertifiedPostFactory, PostFactory, TopicFactory from lacommunaute.forum_conversation.forms import PostForm -from lacommunaute.forum_conversation.models import BlockedPost, CertifiedPost, Topic +from lacommunaute.forum_conversation.models import CertifiedPost, Topic from lacommunaute.forum_conversation.views_htmx import PostListView from lacommunaute.forum_moderation.factories import BlockedDomainNameFactory, BlockedEmailFactory +from lacommunaute.forum_moderation.models import BlockedPost from lacommunaute.forum_moderation.utils import BlockedPostReason from lacommunaute.forum_upvote.factories import UpVoteFactory from lacommunaute.users.factories import UserFactory diff --git a/lacommunaute/forum_moderation/admin.py b/lacommunaute/forum_moderation/admin.py index 92c64f719..b5c24af6e 100644 --- a/lacommunaute/forum_moderation/admin.py +++ b/lacommunaute/forum_moderation/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from lacommunaute.forum_moderation.models import BlockedDomainName, BlockedEmail +from lacommunaute.forum_moderation.models import BlockedDomainName, BlockedEmail, BlockedPost @admin.register(BlockedEmail) @@ -13,3 +13,9 @@ class BlockedEmailAdmin(admin.ModelAdmin): class BlockedDomainNameAdmin(admin.ModelAdmin): list_display = ("domain", "created", "reason") list_filter = ("reason",) + + +@admin.register(BlockedPost) +class BlockedPostAdmin(admin.ModelAdmin): + list_display = ("username", "created", "block_reason") + list_filter = ("block_reason",) diff --git a/lacommunaute/forum_conversation/migrations/0009_blockedpost.py b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py similarity index 69% rename from lacommunaute/forum_conversation/migrations/0009_blockedpost.py rename to lacommunaute/forum_moderation/migrations/0003_blockedpost.py index a12d032f3..ef9ab8144 100644 --- a/lacommunaute/forum_conversation/migrations/0009_blockedpost.py +++ b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py @@ -1,11 +1,11 @@ -# Generated by Django 5.0.6 on 2024-06-05 13:24 +# Generated by Django 5.0.6 on 2024-06-07 10:09 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("forum_conversation", "0008_remove_topic_likers"), + ("forum_moderation", "0002_copy_data_from_BouncedEmail_to_BlockedEmail"), ] operations = [ @@ -17,9 +17,11 @@ class Migration(migrations.Migration): ("updated", models.DateTimeField(auto_now=True, verbose_name="Update date")), ("username", models.EmailField(blank=True, max_length=254, null=True, verbose_name="Adresse email")), ("content", models.CharField(verbose_name="Content")), + ("block_reason", models.CharField(blank=True, null=True, verbose_name="Block Reason")), ], options={ - "abstract": False, + "verbose_name": "Blocked Post", + "verbose_name_plural": "Blocked Posts", }, ), ] diff --git a/lacommunaute/forum_moderation/models.py b/lacommunaute/forum_moderation/models.py index a9d3ced48..1616b985e 100644 --- a/lacommunaute/forum_moderation/models.py +++ b/lacommunaute/forum_moderation/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.utils.translation import gettext_lazy as _ from machina.models.abstract_models import DatedModel @@ -24,3 +25,38 @@ class Meta: def __str__(self): return f"{self.domain} - {self.created}" + + +class BlockedPost(DatedModel): + """ + When a user submits a Post and it is blocked by our quality control app (forum_moderation), + we save a record of the blocked Post in this table for reference purposes + + It is built of a subset of fields from django-machina's model AbstractPost + """ + + username = models.EmailField(blank=True, null=True, verbose_name=("Adresse email")) + content = models.CharField(verbose_name=_("Content")) + block_reason = models.CharField( + blank=True, + null=True, + verbose_name=_("Block Reason"), + ) + + class Meta: + verbose_name = _("Blocked Post") + verbose_name_plural = _("Blocked Posts") + + def __str__(self): + return f"Blocked Message [{ str(self.created) }]" + + @classmethod + def create_from_post(cls, post): + """ + Creates a BlockedPost object from parameterised Post (machina) + """ + return cls.objects.create( + username=getattr(post, "username", ""), + content=post.content, + block_reason=post.update_reason, + ) diff --git a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py index 0efef3207..bda375b27 100644 --- a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py +++ b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py @@ -1,7 +1,7 @@ import pytest # noqa from lacommunaute.forum_conversation.factories import TopicFactory, AnonymousPostFactory -from lacommunaute.forum_conversation.models import Post, BlockedPost -from lacommunaute.forum_moderation.models import BlockedEmail +from lacommunaute.forum_conversation.models import Post +from lacommunaute.forum_moderation.models import BlockedEmail, BlockedPost from lacommunaute.users.factories import UserFactory from django.urls import reverse from lacommunaute.forum_moderation.factories import BlockedEmailFactory diff --git a/lacommunaute/forum_moderation/views.py b/lacommunaute/forum_moderation/views.py index c2092fef2..67a69f781 100644 --- a/lacommunaute/forum_moderation/views.py +++ b/lacommunaute/forum_moderation/views.py @@ -5,8 +5,7 @@ TopicDeleteView as BaseTopicDeleteView, ) -from lacommunaute.forum_conversation.models import BlockedPost -from lacommunaute.forum_moderation.models import BlockedEmail +from lacommunaute.forum_moderation.models import BlockedEmail, BlockedPost from lacommunaute.forum_moderation.utils import BlockedPostReason From 25ea3cf32bc2ad5d55b85383e2902330f3548aec Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Fri, 7 Jun 2024 12:21:12 +0200 Subject: [PATCH 09/22] =?UTF-8?q?refactor(forum=5Fmoderation):=20d=C3=A9pl?= =?UTF-8?q?acement=20d'enum=20dans=20un=20fichier=20enums.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lacommunaute/forum_conversation/forms.py | 3 ++- .../tests/tests_views_htmx.py | 2 +- lacommunaute/forum_moderation/enums.py | 22 +++++++++++++++++ .../tests/test_post_disapprove_view.py | 6 +++-- lacommunaute/forum_moderation/utils.py | 24 +------------------ lacommunaute/forum_moderation/views.py | 2 +- 6 files changed, 31 insertions(+), 28 deletions(-) create mode 100644 lacommunaute/forum_moderation/enums.py diff --git a/lacommunaute/forum_conversation/forms.py b/lacommunaute/forum_conversation/forms.py index e4a7bfb08..91f124931 100644 --- a/lacommunaute/forum_conversation/forms.py +++ b/lacommunaute/forum_conversation/forms.py @@ -6,8 +6,9 @@ from taggit.models import Tag from lacommunaute.forum_conversation.models import Post +from lacommunaute.forum_moderation.enums import BlockedPostReason from lacommunaute.forum_moderation.models import BlockedPost -from lacommunaute.forum_moderation.utils import BlockedPostReason, check_post_approbation +from lacommunaute.forum_moderation.utils import check_post_approbation class CreateUpdatePostMixin: diff --git a/lacommunaute/forum_conversation/tests/tests_views_htmx.py b/lacommunaute/forum_conversation/tests/tests_views_htmx.py index c725bf45c..0b1b96b9f 100644 --- a/lacommunaute/forum_conversation/tests/tests_views_htmx.py +++ b/lacommunaute/forum_conversation/tests/tests_views_htmx.py @@ -11,9 +11,9 @@ from lacommunaute.forum_conversation.forms import PostForm from lacommunaute.forum_conversation.models import CertifiedPost, Topic from lacommunaute.forum_conversation.views_htmx import PostListView +from lacommunaute.forum_moderation.enums import BlockedPostReason from lacommunaute.forum_moderation.factories import BlockedDomainNameFactory, BlockedEmailFactory from lacommunaute.forum_moderation.models import BlockedPost -from lacommunaute.forum_moderation.utils import BlockedPostReason from lacommunaute.forum_upvote.factories import UpVoteFactory from lacommunaute.users.factories import UserFactory diff --git a/lacommunaute/forum_moderation/enums.py b/lacommunaute/forum_moderation/enums.py new file mode 100644 index 000000000..ae7cf08fc --- /dev/null +++ b/lacommunaute/forum_moderation/enums.py @@ -0,0 +1,22 @@ +from enum import Enum + + +class BlockedPostReason(Enum): + HTML_TAGS = "HTML tags detected" + ALTERNATIVE_LANGUAGE = "Alternative Language detected" + BLOCKED_DOMAIN = "Blocked Domain detected" + BLOCKED_USER = "Blocked Email detected" + MODERATOR_DISAPPROVAL = "Moderator disapproval" + + @classmethod + def reasons_tracked_for_stats(cls): + """ + We store BlockedPost objects for posts which are of interest for review + The list of "reasons for interest" are returned by this function + """ + return [ + cls.ALTERNATIVE_LANGUAGE.value, + cls.BLOCKED_DOMAIN.value, + cls.BLOCKED_USER.value, + cls.MODERATOR_DISAPPROVAL.value, + ] diff --git a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py index bda375b27..bcf0daa37 100644 --- a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py +++ b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py @@ -1,11 +1,13 @@ import pytest # noqa + +from django.urls import reverse + from lacommunaute.forum_conversation.factories import TopicFactory, AnonymousPostFactory from lacommunaute.forum_conversation.models import Post from lacommunaute.forum_moderation.models import BlockedEmail, BlockedPost from lacommunaute.users.factories import UserFactory -from django.urls import reverse +from lacommunaute.forum_moderation.enums import BlockedPostReason from lacommunaute.forum_moderation.factories import BlockedEmailFactory -from lacommunaute.forum_moderation.utils import BlockedPostReason def test_post_disapprove_view(client, db): diff --git a/lacommunaute/forum_moderation/utils.py b/lacommunaute/forum_moderation/utils.py index d0b1335a1..7b52c0954 100644 --- a/lacommunaute/forum_moderation/utils.py +++ b/lacommunaute/forum_moderation/utils.py @@ -1,34 +1,12 @@ -from enum import Enum - from django.conf import settings from langdetect import detect from machina.models.fields import render_func from markdown2 import Markdown +from lacommunaute.forum_moderation.enums import BlockedPostReason from lacommunaute.forum_moderation.models import BlockedDomainName, BlockedEmail -class BlockedPostReason(Enum): - HTML_TAGS = "HTML tags detected" - ALTERNATIVE_LANGUAGE = "Alternative Language detected" - BLOCKED_DOMAIN = "Blocked Domain detected" - BLOCKED_USER = "Blocked Email detected" - MODERATOR_DISAPPROVAL = "Moderator disapproval" - - @classmethod - def reasons_tracked_for_stats(cls): - """ - We store BlockedPost objects for posts which are of interest for review - The list of "reasons for interest" are returned by this function - """ - return [ - cls.ALTERNATIVE_LANGUAGE.value, - cls.BLOCKED_DOMAIN.value, - cls.BLOCKED_USER.value, - cls.MODERATOR_DISAPPROVAL.value, - ] - - def check_post_approbation(post): """ Check if a post should be approved or not diff --git a/lacommunaute/forum_moderation/views.py b/lacommunaute/forum_moderation/views.py index 67a69f781..9c51f23de 100644 --- a/lacommunaute/forum_moderation/views.py +++ b/lacommunaute/forum_moderation/views.py @@ -5,8 +5,8 @@ TopicDeleteView as BaseTopicDeleteView, ) +from lacommunaute.forum_moderation.enums import BlockedPostReason from lacommunaute.forum_moderation.models import BlockedEmail, BlockedPost -from lacommunaute.forum_moderation.utils import BlockedPostReason class TopicDeleteView(BaseTopicDeleteView): From d466965a55df548cd31d3bbb98824db1f5dd4cf7 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Fri, 7 Jun 2024 12:25:12 +0200 Subject: [PATCH 10/22] style(forum_conversation): suppression du commentaire --- lacommunaute/forum_conversation/forms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lacommunaute/forum_conversation/forms.py b/lacommunaute/forum_conversation/forms.py index 91f124931..c512c9404 100644 --- a/lacommunaute/forum_conversation/forms.py +++ b/lacommunaute/forum_conversation/forms.py @@ -21,7 +21,6 @@ def clean(self): if not post.approved: self.add_error(None, "Votre message ne respecte pas les règles de la communauté.") - # track the blocked post if it was blocked for a reason we're tracking if post.update_reason in BlockedPostReason.reasons_tracked_for_stats(): BlockedPost.create_from_post(post) return cleaned_data From c9b11e7eaa566484a59351dc0e6c753e09195eb8 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Fri, 7 Jun 2024 12:30:53 +0200 Subject: [PATCH 11/22] refactor(tests du forum): assertions succinctes --- .../forum_conversation/tests/tests_views_htmx.py | 15 ++++----------- .../tests/test_post_disapprove_view.py | 4 +--- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/lacommunaute/forum_conversation/tests/tests_views_htmx.py b/lacommunaute/forum_conversation/tests/tests_views_htmx.py index 0b1b96b9f..2d14c45bd 100644 --- a/lacommunaute/forum_conversation/tests/tests_views_htmx.py +++ b/lacommunaute/forum_conversation/tests/tests_views_htmx.py @@ -388,9 +388,7 @@ def test_create_post_as_blocked_not_blocked_anonymous(self, *args): self.assertEqual(self.topic.posts.count(), 2) # the blocked post should be recorded in the database - blocked_posts = BlockedPost.objects.all() - assert blocked_posts.count() == 1 - blocked_post = blocked_posts.first() + blocked_post = BlockedPost.objects.get() assert blocked_post.content == self.content assert blocked_post.username == username assert blocked_post.block_reason == BlockedPostReason.BLOCKED_USER.value @@ -418,9 +416,7 @@ def test_create_post_with_nonfr_content(self): self.assertEqual(self.topic.posts.count(), 1) # the blocked post should be recorded in the database - blocked_posts = BlockedPost.objects.all() - assert blocked_posts.count() == 1 - blocked_post = blocked_posts.first() + blocked_post = BlockedPost.objects.get() assert blocked_post.content == "популярные лучшие песни слушать онлайн" assert blocked_post.block_reason == BlockedPostReason.ALTERNATIVE_LANGUAGE.value @@ -450,8 +446,7 @@ def test_create_post_with_html_content(self): self.assertEqual(self.topic.posts.count(), 1) # we don't create a BlockedPost record for HTML content to avoid storing malicious code - blocked_posts = BlockedPost.objects.all() - assert blocked_posts.count() == 0 + assert BlockedPost.objects.count() == 0 def test_create_post_with_blocked_domain_name(self): BlockedDomainNameFactory(domain="blocked.com") @@ -478,9 +473,7 @@ def test_create_post_with_blocked_domain_name(self): self.assertEqual(self.topic.posts.count(), 1) # the blocked post should be recorded in the database - blocked_posts = BlockedPost.objects.all() - assert blocked_posts.count() == 1 - blocked_post = blocked_posts.first() + blocked_post = BlockedPost.objects.get() assert blocked_post.content == "la communauté" assert blocked_post.username == "spam@blocked.com" assert blocked_post.block_reason == BlockedPostReason.BLOCKED_DOMAIN.value diff --git a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py index bcf0daa37..3db49d364 100644 --- a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py +++ b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py @@ -21,9 +21,7 @@ def test_post_disapprove_view(client, db): # the original Post should be deleted, but a BlockedPost saved assert Post.objects.count() == 0 - blocked_posts = BlockedPost.objects.all() - assert blocked_posts.count() == 1 - blocked_post = blocked_posts.first() + blocked_post = BlockedPost.objects.get() assert blocked_post.content == str(disapproved_post.content) assert blocked_post.username == disapproved_post.username assert blocked_post.block_reason == BlockedPostReason.MODERATOR_DISAPPROVAL.value From 8c9b100f164dc0956aa9c0b3dd08cb8b9d8e2405 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Fri, 7 Jun 2024 12:35:39 +0200 Subject: [PATCH 12/22] feat(forum_conversation): nom du CertifiedPost traduit --- .../0009_alter_certifiedpost_options.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 lacommunaute/forum_conversation/migrations/0009_alter_certifiedpost_options.py diff --git a/lacommunaute/forum_conversation/migrations/0009_alter_certifiedpost_options.py b/lacommunaute/forum_conversation/migrations/0009_alter_certifiedpost_options.py new file mode 100644 index 000000000..cc1c696b3 --- /dev/null +++ b/lacommunaute/forum_conversation/migrations/0009_alter_certifiedpost_options.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.6 on 2024-06-07 10:34 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("forum_conversation", "0008_remove_topic_likers"), + ] + + operations = [ + migrations.AlterModelOptions( + name="certifiedpost", + options={ + "ordering": ["-created"], + "verbose_name": "Certified Post", + "verbose_name_plural": "Certified Posts", + }, + ), + ] From 63d22e083bb6fc8c1a954830bf70e9bff50a13f1 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Fri, 7 Jun 2024 12:36:48 +0200 Subject: [PATCH 13/22] fix(locale): Postes -> Message nomanculture de l'application --- locale/fr/LC_MESSAGES/django.mo | Bin 15107 -> 15109 bytes locale/fr/LC_MESSAGES/django.po | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locale/fr/LC_MESSAGES/django.mo b/locale/fr/LC_MESSAGES/django.mo index 773460deaca74e3686dddfdf9850ea247f9ea0fc..d5af9fa722c42806a7be8ae296ee93efd64f08ac 100644 GIT binary patch delta 1458 zcmXxkO-R&17{~F+%r#$f)pbowTeMQwmn=+L6h+@(|LL%NW@hK*nP(pU=i0m4hab2?3C5US zzcGo}k3~3&)i{GOn452m3rkS*)?zZ&+kO)+rXR#Kyl5Rj&3lSk{|2M*J+8*-d=oMY zX?P2aNkt#(fB+_89Y*7BjK}?`16u9>m+bgW)W-L)0w3Zs{ANulbONbFH{%Vs0{0Y# zjESSs%|I;nVm97DFOK6no_U=b8s6f@Kcz9J*dD3Q8ypP1^5Wl@dQzrcoF8j(Vb$VrTy% zRE9$}G_+s`Cg4%j0cUVNUO>I!W&3|WYGK&=3@H=y4s~E8>Ct>I#-kteFo4QfGrFQ9j!Upu0IO{qwiT+vC z6ZK&_^=C$Cs0d$L-=YHfgnGj-r~~Iv0YsEK^J7pOdQh3mLCr6>uE#|BwdlcJsC|y3 zGIkncIo}M}KSpo~{l};cCs4&QYmHjtRDTvKwN${l{aI9i#R11E)KhLj-LMgL z!F{MqcA)m{vtA95e=h^q8Bh_8VgY_Yl49ccnh?C%i22xwiu@L8{uuuIQsQ~~xs`k+ z@eXR=j~IbJP=WozdAOP1PJWr;y!e9|zWSD6u<1aHulew?6K&%|#`c5%2g3LLjqbk~ CFQcpg delta 1456 zcmX}sOGuPa7{>9Fc}w#;qbBBL7fl_tG#xKQg;6S9u%Jan8Ig-hG8-KUf^VTAMJ>`r zEsCf{M66IoLMQ^MP0^xSl!RHjGN?9T6t(F8_#IjdzjMxaF7J8I%Ulfgght2XoMdCn zK(R5Yco)ksg4=Np6R^N!|zs4sT)WQ>Y}@`0FRV9 z#w@4N%RnMt!?kz|GjS4E;S_4#FD%Cx(v*SKxE}YQ0`J0H>_Y`Uh`RYhjKN2kgHOEgZ8xN6N&!M;#bNdNkjQNmz(Q7(iw07^Y&# z)iHfEbl?rt6AYqmHio+K3sj0{P^pZia#WPbn2*`01GZu(p2Q}c!M#|x$z9it>GU1c z6ZK;b^=C$Ds0d$K-=PBejC#Ybr~?;J0hmg6emrW!bX4Z@QS+;;TQH4&1A6fwYM(Qx zjGf0s&NpHEVH8)=e~j923RNug*61p?`twk!twTLsBdXe4P&LqtO7Rft%_mR?zd>bs z8Wq?VbUZZv(9q3N0`ASS&`Wwkq8~opoM2h#P{sZ5R Bp=kgB diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 46163e08c..f49aa081e 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -28,11 +28,11 @@ msgstr "Messages certifiés" #: lacommunaute/forum_conversation/models.py msgid "Blocked Post" -msgstr "Poste Bloquée" +msgstr "Message bloqué" #: lacommunaute/forum_conversation/models.py msgid "Blocked Posts" -msgstr "Postes Bloquées" +msgstr "Messages bloqués" #: lacommunaute/forum_conversation/models.py msgid "Content" From 0565328d03a3347795d8ad406449ff49f64e1bb8 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Fri, 7 Jun 2024 12:44:58 +0200 Subject: [PATCH 14/22] =?UTF-8?q?refactor(PostDisapproveView):=20cr=C3=A9a?= =?UTF-8?q?tion=20du=20BlockedPost=20dans=20post()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lacommunaute/forum_moderation/views.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lacommunaute/forum_moderation/views.py b/lacommunaute/forum_moderation/views.py index 9c51f23de..c34d5ef42 100644 --- a/lacommunaute/forum_moderation/views.py +++ b/lacommunaute/forum_moderation/views.py @@ -31,13 +31,8 @@ def post(self, request, *args, **kwargs): self.request, "l'adresse email de l'utilisateur est déjà dans la liste des emails bloqués.", ) - return self.disapprove(request, *args, **kwargs) - def disapprove(self, request, *args, **kwargs): - """ - Extends Machina's post disapproval behaviour to save the rejected post before deleting - """ - self.object = self.get_object() - self.object.update_reason = BlockedPostReason.MODERATOR_DISAPPROVAL.value - BlockedPost.create_from_post(self.object) - return super().disapprove(request, *args, **kwargs) + post.update_reason = BlockedPostReason.MODERATOR_DISAPPROVAL.value + BlockedPost.create_from_post(post) + + return self.disapprove(request, *args, **kwargs) From 826096f2d29399d354bf0f721eb621fb012bf2ef Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Fri, 7 Jun 2024 13:01:40 +0200 Subject: [PATCH 15/22] =?UTF-8?q?feat(BlockedPost):=20utilisateurs=20authe?= =?UTF-8?q?ntifi=C3=A9=20traqu=C3=A9s=20sur=20le=20mod=C3=A8le?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lacommunaute/forum_conversation/forms.py | 3 +++ .../tests/tests_views_htmx.py | 1 + .../migrations/0003_blockedpost.py | 12 ++++++++++++ lacommunaute/forum_moderation/models.py | 10 ++++++++++ locale/fr/LC_MESSAGES/django.mo | Bin 15109 -> 15155 bytes locale/fr/LC_MESSAGES/django.po | 18 +++++++++++------- 6 files changed, 37 insertions(+), 7 deletions(-) diff --git a/lacommunaute/forum_conversation/forms.py b/lacommunaute/forum_conversation/forms.py index c512c9404..dd1b7f733 100644 --- a/lacommunaute/forum_conversation/forms.py +++ b/lacommunaute/forum_conversation/forms.py @@ -21,6 +21,9 @@ def clean(self): if not post.approved: self.add_error(None, "Votre message ne respecte pas les règles de la communauté.") + if self.user.is_authenticated: + post.poster = self.user + if post.update_reason in BlockedPostReason.reasons_tracked_for_stats(): BlockedPost.create_from_post(post) return cleaned_data diff --git a/lacommunaute/forum_conversation/tests/tests_views_htmx.py b/lacommunaute/forum_conversation/tests/tests_views_htmx.py index 2d14c45bd..ed5c30336 100644 --- a/lacommunaute/forum_conversation/tests/tests_views_htmx.py +++ b/lacommunaute/forum_conversation/tests/tests_views_htmx.py @@ -417,6 +417,7 @@ def test_create_post_with_nonfr_content(self): # the blocked post should be recorded in the database blocked_post = BlockedPost.objects.get() + assert blocked_post.poster == self.user assert blocked_post.content == "популярные лучшие песни слушать онлайн" assert blocked_post.block_reason == BlockedPostReason.ALTERNATIVE_LANGUAGE.value diff --git a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py index ef9ab8144..8df1786d7 100644 --- a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py +++ b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py @@ -1,5 +1,6 @@ # Generated by Django 5.0.6 on 2024-06-07 10:09 +from django.conf import settings from django.db import migrations, models @@ -15,6 +16,17 @@ class Migration(migrations.Migration): ("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")), + ( + "poster", + models.ForeignKey( + blank=True, + null=True, + on_delete=models.deletion.CASCADE, + related_name="blocked_posts", + to=settings.AUTH_USER_MODEL, + verbose_name="Poster", + ), + ), ("username", models.EmailField(blank=True, max_length=254, null=True, verbose_name="Adresse email")), ("content", models.CharField(verbose_name="Content")), ("block_reason", models.CharField(blank=True, null=True, verbose_name="Block Reason")), diff --git a/lacommunaute/forum_moderation/models.py b/lacommunaute/forum_moderation/models.py index 1616b985e..9c0ba510b 100644 --- a/lacommunaute/forum_moderation/models.py +++ b/lacommunaute/forum_moderation/models.py @@ -1,3 +1,4 @@ +from django.conf import settings from django.db import models from django.utils.translation import gettext_lazy as _ from machina.models.abstract_models import DatedModel @@ -35,6 +36,14 @@ class BlockedPost(DatedModel): It is built of a subset of fields from django-machina's model AbstractPost """ + poster = models.ForeignKey( + settings.AUTH_USER_MODEL, + related_name="blocked_posts", + blank=True, + null=True, + on_delete=models.CASCADE, + verbose_name=_("Poster"), + ) username = models.EmailField(blank=True, null=True, verbose_name=("Adresse email")) content = models.CharField(verbose_name=_("Content")) block_reason = models.CharField( @@ -56,6 +65,7 @@ def create_from_post(cls, post): Creates a BlockedPost object from parameterised Post (machina) """ return cls.objects.create( + poster=post.poster, username=getattr(post, "username", ""), content=post.content, block_reason=post.update_reason, diff --git a/locale/fr/LC_MESSAGES/django.mo b/locale/fr/LC_MESSAGES/django.mo index d5af9fa722c42806a7be8ae296ee93efd64f08ac..00fb78b10cbaf59c1c6e50de8296566b3ea68779 100644 GIT binary patch delta 4557 zcmYk5mRhJzu}?XXp5wUVD7L-{1H9`F?KS-%k&h*Oq(V#WY`K zl*6P0*%)G$7-W_nsk3I4(PkO=1ZLuH?1hb}>pRDo<>6qA!wP2=hI3qtE%80P4EN)$ z_$%%-^DHmcY!@e*b2n|;9&CjT*cyLw$N!-E6x_xv3S%)8lQ9B&U>as)51i_bmt#K1 zb(o6HAz)NnNwHM|YgvoBB`I*GdQJnBIqiDt~A#iBZpiouwPnu&g> zjtoZ)qyTl@6zqf3-SP8CleRID^;ge!aY7?Li0b)wsE(aNJ>U%Lx<*t7gWCDWai|V< zL0y;O%tB3h4r&JS-T5)7=M*_-wqyRawhP>eN>m3nxZ`Tn$m>uKsz)_=7b@9MI~`FS>E=<<1NyrQMxbsiK>oAw{Lx6tP!Cv)y0Ow7Z$Ncq zD{9JjATNs5V=5kZpNH_SssmA|*Dw*2(7S@lcq&EMAGc#R{*D?+N=LtgJ&^rq*{BYT zcIPLd8lH_B**pyFdeo9sxZ@S5jy{bVU==cO&o)!h=GpE(*n_(8Q~v@x;*L)^Pb2@? zU;I%Aqb~Iy7>jDB9jYVUQ8U}ynTwjCYfv4WfNglb6;sia%*QUc7PTjKqo%R})uCTd zBW*-I;JiBy;bt`$jXIxz+N8;-4)w)k9EPlhO+&T22s1n?&rs1+?L+nW0II=bs2-od zPIwm8k+>xPx(=wNNJTv;2X%iQYG%f|<091FxgGUd&&4cUik=#LpNgzUt<7hs2ERfz zcnUS5zfm0xp*MO!1nM{%dth74z@ex;Gza@(HR}C8foeC7>0t6KIhpy_2SYe8 z{0lX*zI-b+!;z>N$;Z4P-VVHi<29^TUp#`j7?$Esbw1w2@hsF*?nQm!978SPS=9H* zg%r}a}3CGc>r7l8U=S`uao|n24kD^BSEGA;D^C0TFpHU5*!w|eAomULQF$@Qyz9&YZ z9xxF#Q*%%QS&Z5P%aHaxTk9^^;Xe2n)$o_7nfMl2UTb@~?*LRsCZk4PhMI{b7=c@{ zCDtL^#P*?f{jbOiWI;@euIq%o^!{g1(XZMhWVx*z)$=OU7flVS=Z8=uKZ?QlJ?ecv ziCXKRp8m+=QA?G9YIhK7Kx0rHxfykR2}baIdxVOn>?u@_UqxO3`y6${->43SXZj=P zfLfvfs0WNiP4P_B^$W2Tu0oA;6JCOEpl0q(_xXPGbmP}lbWUd%)c7U5i)pBV<)Q{Q3gfU4)xl}VYMWO= zMX$w+sD?j8_M06??eYk|$TcG|s9l_bYA7Amu_34j-R92ELVdSCh`Mh*CSff$#|CVR z$1qCof1^90e|?PLg9KC$Cpc%IMo^9#;RC1Zk5X4u$5N5sq`<%Z??Qg$ z>>WZgb_>y~r=orEKG{V+C4s{8t&-@qQ7I&E25SC8HHACLC^Fui--1k<9dgI}@gwpC z8B3ldDzB4fff}zYYSi6{Hmb@d;%O~b5l#7YGLL*hhLhFgVWR2PTF)hG$Sg9BsOV=~ z`(i6uN$w{_?QXS?TKn~H&M~|!X8qq z14=B>22**4d`>2j^+Y9{tRUKJ@g$KvpywBpF+^n_d5om!fbyJA;D4D_y_!5rHj`^f zZ&E>;ir$cm%LWdXk`d%V_dyxfk;5b~e%?cCM^rZY*zL}4&QQGG)tmT`N*bvlcaV2U zXQFSq0-|!j$F9Lg$z*aHSx1(*=iB2P3?V8BB$!Mg^+YAd$KJp@$%|wLd6krqgG6O2 z8LSFrA?Zr8$ZV2Mc9R_>mH376Z=*JVv?gzp8;HtGADe*fU41lO=jzWpAHt3#&mG@` z`DCO!7B`bH@)`MnloOTLGWpYm$Qwx;qVgW;NfwYu(p2Wu&W`@KWwYXv+Q}W4#Rm^5 ND=jXo{jE=8=>P9iqO<@2 delta 4516 zcmYk-3wTdu0LSsyY>k;2w*M~3jIj+fHf)HQDRVEnNODasVYy%8n2^O#DEDiOGO;0q zaxLas=_!OHJrpTP^62UN+j;-xJbwGU@A;o|-s?FEM^78=T&Uo?z;K)*mC4daj0r74 zxvb6_lU3fB1bh_}aV6Hr)2Qo9RxqXwMqvmJv`)eR>hrJ+F2}0)KK8()_^~mLiLGeN z9!^|Ao@rL|lybNggYlrPpF#EMM-0N-=#M_3#sp#j*2XZ5$5ysJ6f>zW#yGrUe-HBL zI>(q)3c9f$mcqHH9xp;Quomf?$wxJ`&w2vYp>tRVuVFfR!rbR{LnhPoL3MZ_YG7lL z!J8QvPWxsF1@1LlQ8Q448u@pq4%|dv{1dC=ebj@ihPxfAiRy5Yt+znk*B-S5T~Q79 zv<}14)E#v6;2a8i@N87i3Q!$7gu3uNYUEdu*)z9M9Vi`Pj2{N0X2OH&NDb6L5>eNs zV-jZC`b6XrW@-fUubwUAghrf?>iJ$&#}1w>Hys40&` z%|M(zpNwj!iM3rM^RKmi!Jf!Mbs*c;b5J8+jA|$k^}sEt>vr4vLDa}kqB?#KwU;hg zuc7Yy9rc_CsE+tMmD~oxQ5VFZZcIe}nNlP|u!ELp`_yYGlu$ zmaGqI?FXPbI1DwgF{l}xjO-c5VG*h$H&EBzK`nt# zl-p26)cIbFoMszh(KciLhjoPv7qccPwq1DPCi-=2?*X8y->q9gN^fk#jy3*u|!gH=&8 z5{+$2u%j`R`tTaYG{lYA0xu)Cn&=odAht#=<#N=ArU12sMX2w{*BFOaV;o}!Q3$SS z%qEbdZN~NAlAo8*3GB}FJos6q*5Du zI26(-+2kL#^i(2cms4wHMsHO6+ z=YC+qPy?!q>PRx``gW*}^ujROH$y0>$J0=|cmwK&W2g@OfEvLa)Dndzx(y_trZ@w2 z{j*pOhoTyoge7q%Y6<7q-`Ak-D?~?y!xY#j=37*QzRZtq@L*YtNA{&jL47*=U@(rg z=Aar}j9P*`)Ql9OMqGrN;Y+BQEY-l>l)(*{e_lmXi4$s|C62_wn2DFLBgQAWH;%&! z)U#1bG#|AT1*lDU!uk!WBbQN2coX#&+(mW3C)vF|Aes5s14B5WsjG#$p|Ld$%Tv$9 z5bTb6P!?*&CZHNxXU`X)z88B?&pV6i;C1UGe6_Uuqfs;4%%PyQYm3_Ly-|B$I%wSSy~j#L*j4D}7rCetyC=<}?jCV7SE-ET|uN;M-oo+Nq$){-&Ad(@}U(pLIn zxGif>wbJ;t`#X^=@-~@9ni3t_$?ZuD*-G9a(ZqXfqcDn0B5TNVWDn5}KR|RGb18oB zpSEQYOPm?}*-IkHdqiKauEcvZq|n=2!Nw1jU#6^g`UF`=`jL{PE-501Ne8l-Od!>W zj^$(@S>av(3@UBNZ1N28kPpZ>GJhlf@ Date: Fri, 7 Jun 2024 13:11:09 +0200 Subject: [PATCH 16/22] =?UTF-8?q?refactor(BlockedPost):=20conversion=20exp?= =?UTF-8?q?licite=20=C3=A0=20CharField?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lacommunaute/forum_moderation/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lacommunaute/forum_moderation/models.py b/lacommunaute/forum_moderation/models.py index 9c0ba510b..76a3435e8 100644 --- a/lacommunaute/forum_moderation/models.py +++ b/lacommunaute/forum_moderation/models.py @@ -67,6 +67,6 @@ def create_from_post(cls, post): return cls.objects.create( poster=post.poster, username=getattr(post, "username", ""), - content=post.content, + content=str(post.content), block_reason=post.update_reason, ) From 493d00b03c4553d801f9a8701f76b7e099b853ff Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Tue, 11 Jun 2024 09:49:32 +0200 Subject: [PATCH 17/22] =?UTF-8?q?style(BlockedPost):=20enl=C3=A8ve=20param?= =?UTF-8?q?=C3=A8tres=20superflus=20de=20block=5Freason?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../forum_moderation/migrations/0003_blockedpost.py | 2 +- lacommunaute/forum_moderation/models.py | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py index 8df1786d7..757886ff0 100644 --- a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py +++ b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py @@ -29,7 +29,7 @@ class Migration(migrations.Migration): ), ("username", models.EmailField(blank=True, max_length=254, null=True, verbose_name="Adresse email")), ("content", models.CharField(verbose_name="Content")), - ("block_reason", models.CharField(blank=True, null=True, verbose_name="Block Reason")), + ("block_reason", models.CharField(verbose_name="Block Reason")), ], options={ "verbose_name": "Blocked Post", diff --git a/lacommunaute/forum_moderation/models.py b/lacommunaute/forum_moderation/models.py index 76a3435e8..4466b6ad7 100644 --- a/lacommunaute/forum_moderation/models.py +++ b/lacommunaute/forum_moderation/models.py @@ -46,11 +46,7 @@ class BlockedPost(DatedModel): ) username = models.EmailField(blank=True, null=True, verbose_name=("Adresse email")) content = models.CharField(verbose_name=_("Content")) - block_reason = models.CharField( - blank=True, - null=True, - verbose_name=_("Block Reason"), - ) + block_reason = models.CharField(verbose_name=_("Block Reason")) class Meta: verbose_name = _("Blocked Post") From 5d6903b5a6e58ba6237429b4b6c1156e9f3810c6 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Tue, 11 Jun 2024 09:51:25 +0200 Subject: [PATCH 18/22] =?UTF-8?q?style(BlockedPost):=20enl=C3=A8ve=20param?= =?UTF-8?q?=C3=A8tres=20superflus=20d'username?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lacommunaute/forum_moderation/migrations/0003_blockedpost.py | 2 +- lacommunaute/forum_moderation/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py index 757886ff0..805ceb268 100644 --- a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py +++ b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py @@ -27,7 +27,7 @@ class Migration(migrations.Migration): verbose_name="Poster", ), ), - ("username", models.EmailField(blank=True, max_length=254, null=True, verbose_name="Adresse email")), + ("username", models.EmailField(max_length=254, verbose_name="Adresse email")), ("content", models.CharField(verbose_name="Content")), ("block_reason", models.CharField(verbose_name="Block Reason")), ], diff --git a/lacommunaute/forum_moderation/models.py b/lacommunaute/forum_moderation/models.py index 4466b6ad7..33321d8fd 100644 --- a/lacommunaute/forum_moderation/models.py +++ b/lacommunaute/forum_moderation/models.py @@ -44,7 +44,7 @@ class BlockedPost(DatedModel): on_delete=models.CASCADE, verbose_name=_("Poster"), ) - username = models.EmailField(blank=True, null=True, verbose_name=("Adresse email")) + username = models.EmailField(verbose_name=("Adresse email")) content = models.CharField(verbose_name=_("Content")) block_reason = models.CharField(verbose_name=_("Block Reason")) From 06e1fe65b8f821518023e646ae19521a63841eb3 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Tue, 11 Jun 2024 09:56:56 +0200 Subject: [PATCH 19/22] =?UTF-8?q?style:=20enl=C3=A8ve=20changements=20du?= =?UTF-8?q?=20CertifiedPost=20pour=20plus=20de=20clarit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0009_alter_certifiedpost_options.py | 20 ------------------- lacommunaute/forum_conversation/models.py | 3 --- 2 files changed, 23 deletions(-) delete mode 100644 lacommunaute/forum_conversation/migrations/0009_alter_certifiedpost_options.py diff --git a/lacommunaute/forum_conversation/migrations/0009_alter_certifiedpost_options.py b/lacommunaute/forum_conversation/migrations/0009_alter_certifiedpost_options.py deleted file mode 100644 index cc1c696b3..000000000 --- a/lacommunaute/forum_conversation/migrations/0009_alter_certifiedpost_options.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 5.0.6 on 2024-06-07 10:34 - -from django.db import migrations - - -class Migration(migrations.Migration): - dependencies = [ - ("forum_conversation", "0008_remove_topic_likers"), - ] - - operations = [ - migrations.AlterModelOptions( - name="certifiedpost", - options={ - "ordering": ["-created"], - "verbose_name": "Certified Post", - "verbose_name_plural": "Certified Posts", - }, - ), - ] diff --git a/lacommunaute/forum_conversation/models.py b/lacommunaute/forum_conversation/models.py index 08c4dddc6..c777284e3 100644 --- a/lacommunaute/forum_conversation/models.py +++ b/lacommunaute/forum_conversation/models.py @@ -4,7 +4,6 @@ from django.db import models from django.db.models import Count, F, Q from django.urls import reverse -from django.utils.translation import gettext_lazy as _ from machina.apps.forum_conversation.abstract_models import AbstractPost, AbstractTopic from machina.models.abstract_models import DatedModel from taggit.managers import TaggableManager @@ -189,8 +188,6 @@ class Meta: ordering = [ "-created", ] - verbose_name = _("Certified Post") - verbose_name_plural = _("Certified Posts") def __str__(self): return f"{self.post} - {self.topic} - {self.user}" From 58b69d01652b90f48295c2060146ad4fa2b899d7 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Tue, 11 Jun 2024 14:08:30 +0200 Subject: [PATCH 20/22] fix(BlockedPost): remet username nullable --- lacommunaute/forum_moderation/migrations/0003_blockedpost.py | 2 +- lacommunaute/forum_moderation/models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py index 805ceb268..5c55a5382 100644 --- a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py +++ b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py @@ -27,7 +27,7 @@ class Migration(migrations.Migration): verbose_name="Poster", ), ), - ("username", models.EmailField(max_length=254, verbose_name="Adresse email")), + ("username", models.EmailField(max_length=254, null=True, blank=True, verbose_name="Adresse email")), ("content", models.CharField(verbose_name="Content")), ("block_reason", models.CharField(verbose_name="Block Reason")), ], diff --git a/lacommunaute/forum_moderation/models.py b/lacommunaute/forum_moderation/models.py index 33321d8fd..14bf17b21 100644 --- a/lacommunaute/forum_moderation/models.py +++ b/lacommunaute/forum_moderation/models.py @@ -44,7 +44,7 @@ class BlockedPost(DatedModel): on_delete=models.CASCADE, verbose_name=_("Poster"), ) - username = models.EmailField(verbose_name=("Adresse email")) + username = models.EmailField(null=True, blank=True, verbose_name=("Adresse email")) content = models.CharField(verbose_name=_("Content")) block_reason = models.CharField(verbose_name=_("Block Reason")) From 8b5ce017494ddffb8392ec9aedecc7c1bde0f512 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Tue, 11 Jun 2024 14:27:08 +0200 Subject: [PATCH 21/22] fix(CertifiedPost): missing changes from origin --- lacommunaute/forum_conversation/models.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lacommunaute/forum_conversation/models.py b/lacommunaute/forum_conversation/models.py index c777284e3..08c4dddc6 100644 --- a/lacommunaute/forum_conversation/models.py +++ b/lacommunaute/forum_conversation/models.py @@ -4,6 +4,7 @@ from django.db import models from django.db.models import Count, F, Q from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from machina.apps.forum_conversation.abstract_models import AbstractPost, AbstractTopic from machina.models.abstract_models import DatedModel from taggit.managers import TaggableManager @@ -188,6 +189,8 @@ class Meta: ordering = [ "-created", ] + verbose_name = _("Certified Post") + verbose_name_plural = _("Certified Posts") def __str__(self): return f"{self.post} - {self.topic} - {self.user}" From 9b46db4fb710227508db88710c93def3ef874c06 Mon Sep 17 00:00:00 2001 From: Calum Mackervoy Date: Tue, 11 Jun 2024 17:26:48 +0200 Subject: [PATCH 22/22] refactor(BlockedPostReason): remplace Enum avec models.TextChoices --- lacommunaute/forum_conversation/forms.py | 2 +- .../tests/tests_views_htmx.py | 6 ++--- lacommunaute/forum_moderation/enums.py | 22 +++++++++---------- .../migrations/0003_blockedpost.py | 14 +++++++++++- lacommunaute/forum_moderation/models.py | 4 +++- .../tests/test_post_disapprove_view.py | 2 +- lacommunaute/forum_moderation/utils.py | 8 +++---- lacommunaute/forum_moderation/views.py | 2 +- 8 files changed, 37 insertions(+), 23 deletions(-) diff --git a/lacommunaute/forum_conversation/forms.py b/lacommunaute/forum_conversation/forms.py index dd1b7f733..6fa6f66bb 100644 --- a/lacommunaute/forum_conversation/forms.py +++ b/lacommunaute/forum_conversation/forms.py @@ -24,7 +24,7 @@ def clean(self): if self.user.is_authenticated: post.poster = self.user - if post.update_reason in BlockedPostReason.reasons_tracked_for_stats(): + if post.update_reason in [x.label for x in BlockedPostReason.reasons_tracked_for_stats()]: BlockedPost.create_from_post(post) return cleaned_data diff --git a/lacommunaute/forum_conversation/tests/tests_views_htmx.py b/lacommunaute/forum_conversation/tests/tests_views_htmx.py index ed5c30336..64e40a039 100644 --- a/lacommunaute/forum_conversation/tests/tests_views_htmx.py +++ b/lacommunaute/forum_conversation/tests/tests_views_htmx.py @@ -391,7 +391,7 @@ def test_create_post_as_blocked_not_blocked_anonymous(self, *args): blocked_post = BlockedPost.objects.get() assert blocked_post.content == self.content assert blocked_post.username == username - assert blocked_post.block_reason == BlockedPostReason.BLOCKED_USER.value + assert blocked_post.block_reason == BlockedPostReason.BLOCKED_USER.label def test_create_post_with_nonfr_content(self): assign_perm("can_reply_to_topics", self.user, self.topic.forum) @@ -419,7 +419,7 @@ def test_create_post_with_nonfr_content(self): blocked_post = BlockedPost.objects.get() assert blocked_post.poster == self.user assert blocked_post.content == "популярные лучшие песни слушать онлайн" - assert blocked_post.block_reason == BlockedPostReason.ALTERNATIVE_LANGUAGE.value + assert blocked_post.block_reason == BlockedPostReason.ALTERNATIVE_LANGUAGE.label def test_create_post_with_html_content(self): assign_perm("can_reply_to_topics", self.user, self.topic.forum) @@ -477,7 +477,7 @@ def test_create_post_with_blocked_domain_name(self): blocked_post = BlockedPost.objects.get() assert blocked_post.content == "la communauté" assert blocked_post.username == "spam@blocked.com" - assert blocked_post.block_reason == BlockedPostReason.BLOCKED_DOMAIN.value + assert blocked_post.block_reason == BlockedPostReason.BLOCKED_DOMAIN.label class CertifiedPostViewTest(TestCase): diff --git a/lacommunaute/forum_moderation/enums.py b/lacommunaute/forum_moderation/enums.py index ae7cf08fc..72605b213 100644 --- a/lacommunaute/forum_moderation/enums.py +++ b/lacommunaute/forum_moderation/enums.py @@ -1,12 +1,12 @@ -from enum import Enum +from django.db import models -class BlockedPostReason(Enum): - HTML_TAGS = "HTML tags detected" - ALTERNATIVE_LANGUAGE = "Alternative Language detected" - BLOCKED_DOMAIN = "Blocked Domain detected" - BLOCKED_USER = "Blocked Email detected" - MODERATOR_DISAPPROVAL = "Moderator disapproval" +class BlockedPostReason(models.TextChoices): + HTML_TAGS = "HTML_TAGS", "HTML tags detected" + ALTERNATIVE_LANGUAGE = "ALTERNATIVE_LANGUAGE", "Alternative Language detected" + BLOCKED_DOMAIN = "BLOCKED_DOMAIN", "Blocked Domain detected" + BLOCKED_USER = "BLOCKED_USER", "Blocked Email detected" + MODERATOR_DISAPPROVAL = "MODERATOR_DISAPPROVAL", "Moderator disapproval" @classmethod def reasons_tracked_for_stats(cls): @@ -15,8 +15,8 @@ def reasons_tracked_for_stats(cls): The list of "reasons for interest" are returned by this function """ return [ - cls.ALTERNATIVE_LANGUAGE.value, - cls.BLOCKED_DOMAIN.value, - cls.BLOCKED_USER.value, - cls.MODERATOR_DISAPPROVAL.value, + cls.ALTERNATIVE_LANGUAGE, + cls.BLOCKED_DOMAIN, + cls.BLOCKED_USER, + cls.MODERATOR_DISAPPROVAL, ] diff --git a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py index 5c55a5382..f8fc1bda7 100644 --- a/lacommunaute/forum_moderation/migrations/0003_blockedpost.py +++ b/lacommunaute/forum_moderation/migrations/0003_blockedpost.py @@ -29,7 +29,19 @@ class Migration(migrations.Migration): ), ("username", models.EmailField(max_length=254, null=True, blank=True, verbose_name="Adresse email")), ("content", models.CharField(verbose_name="Content")), - ("block_reason", models.CharField(verbose_name="Block Reason")), + ( + "block_reason", + models.CharField( + choices=[ + ("HTML_TAGS", "HTML tags detected"), + ("ALTERNATIVE_LANGUAGE", "Alternative Language detected"), + ("BLOCKED_DOMAIN", "Blocked Domain detected"), + ("BLOCKED_USER", "Blocked Email detected"), + ("MODERATOR_DISAPPROVAL", "Moderator disapproval"), + ], + verbose_name="Block Reason", + ), + ), ], options={ "verbose_name": "Blocked Post", diff --git a/lacommunaute/forum_moderation/models.py b/lacommunaute/forum_moderation/models.py index 14bf17b21..0250daf87 100644 --- a/lacommunaute/forum_moderation/models.py +++ b/lacommunaute/forum_moderation/models.py @@ -3,6 +3,8 @@ from django.utils.translation import gettext_lazy as _ from machina.models.abstract_models import DatedModel +from lacommunaute.forum_moderation.enums import BlockedPostReason + class BlockedEmail(DatedModel): email = models.EmailField(verbose_name="email", null=False, blank=False, unique=True) @@ -46,7 +48,7 @@ class BlockedPost(DatedModel): ) username = models.EmailField(null=True, blank=True, verbose_name=("Adresse email")) content = models.CharField(verbose_name=_("Content")) - block_reason = models.CharField(verbose_name=_("Block Reason")) + block_reason = models.CharField(verbose_name=_("Block Reason"), choices=BlockedPostReason) class Meta: verbose_name = _("Blocked Post") diff --git a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py index 3db49d364..00fb5afe7 100644 --- a/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py +++ b/lacommunaute/forum_moderation/tests/test_post_disapprove_view.py @@ -24,7 +24,7 @@ def test_post_disapprove_view(client, db): blocked_post = BlockedPost.objects.get() assert blocked_post.content == str(disapproved_post.content) assert blocked_post.username == disapproved_post.username - assert blocked_post.block_reason == BlockedPostReason.MODERATOR_DISAPPROVAL.value + assert blocked_post.block_reason == BlockedPostReason.MODERATOR_DISAPPROVAL.label def test_post_disapprove_view_with_existing_blocked_email(client, db): diff --git a/lacommunaute/forum_moderation/utils.py b/lacommunaute/forum_moderation/utils.py index 7b52c0954..aa85b684c 100644 --- a/lacommunaute/forum_moderation/utils.py +++ b/lacommunaute/forum_moderation/utils.py @@ -18,13 +18,13 @@ def check_post_approbation(post): conditions = [ ( post.username and BlockedDomainName.objects.filter(domain=post.username.split("@")[-1]).exists(), - BlockedPostReason.BLOCKED_DOMAIN.value, + BlockedPostReason.BLOCKED_DOMAIN.label, ), - (Markdown.html_removed_text_compat in rendered, BlockedPostReason.HTML_TAGS.value), - (detect(post.content.raw) not in settings.LANGUAGE_CODE, BlockedPostReason.ALTERNATIVE_LANGUAGE.value), + (Markdown.html_removed_text_compat in rendered, BlockedPostReason.HTML_TAGS.label), + (detect(post.content.raw) not in settings.LANGUAGE_CODE, BlockedPostReason.ALTERNATIVE_LANGUAGE.label), ( post.username and BlockedEmail.objects.filter(email=post.username).exists(), - BlockedPostReason.BLOCKED_USER.value, + BlockedPostReason.BLOCKED_USER.label, ), ] post.approved, post.update_reason = next( diff --git a/lacommunaute/forum_moderation/views.py b/lacommunaute/forum_moderation/views.py index c34d5ef42..17d831830 100644 --- a/lacommunaute/forum_moderation/views.py +++ b/lacommunaute/forum_moderation/views.py @@ -32,7 +32,7 @@ def post(self, request, *args, **kwargs): "l'adresse email de l'utilisateur est déjà dans la liste des emails bloqués.", ) - post.update_reason = BlockedPostReason.MODERATOR_DISAPPROVAL.value + post.update_reason = BlockedPostReason.MODERATOR_DISAPPROVAL.label BlockedPost.create_from_post(post) return self.disapprove(request, *args, **kwargs)