Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e19479b
feat: AI moderation system (#1)
jcapphelix Nov 5, 2025
1eb7b28
fix: team posts now visible by handling standalone context properly (#5)
naincy128 Nov 18, 2025
8f6699d
feat: soft delete feature (#4)
jcapphelix Jan 6, 2026
41179a1
Revert "feat: soft delete feature (#4)" (#9)
jcapphelix Jan 7, 2026
9d492b4
feat: added soft delete functionality (#10)
Alam-2U Jan 14, 2026
db2c513
fix: remove read state deletion in soft delete of thread (#12)
jcapphelix Jan 22, 2026
20e90bf
feat: auto deletion of spam via AI (#15)
jcapphelix Feb 9, 2026
02de16c
fix: reported tag remained visible after reported response/comment wa…
Alam-2U Feb 16, 2026
266f5c2
feat: bulkdelete ban feature in forum
mraman-2U Feb 10, 2026
67acca9
fix: optimize mongo query for user vote list (#19)
jcapphelix Mar 24, 2026
040add1
feat: implement discussion mute/unmute feature with user and staff-le…
naincy128 Apr 1, 2026
af5f40c
feat: implement bulk delete for MySQL database (#23)
Alam-2U Apr 7, 2026
82e25d5
fix: add missing audit log for muted learner in mysql table (#24)
naincy128 Apr 8, 2026
d073d1e
fix: restrict mute privileges to discussion moderators only (#27)
Alam-2U Apr 10, 2026
7050d61
fix: migrate soft delete fields and support updating existing content…
Alam-2U Apr 10, 2026
4535222
feat: add instrumentation to forum (#29)
naincy128 Apr 13, 2026
f328d11
feat: update init file (#30)
naincy128 Apr 14, 2026
5eb6f5b
Revert "feat: add instrumentation to forum (#29)"
naincy128 Apr 14, 2026
248ab9c
Merge pull request #31 from edx/revert-29-naincy/Cosmo2-853
santhosh-apphelix-2u Apr 15, 2026
9ae39d7
fix: team posts not appearing after enabling MySQL waffle flag (#35)
naincy128 Apr 17, 2026
5fee94e
fix: mysql referential integrity error in update_user during retire_f…
Alam-2U Apr 22, 2026
028ab29
fix: mysql referential integrity error in update_user during retire_f…
Alam-2U Apr 23, 2026
c98ed5f
feat: update init file
naincy128 Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion forum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Openedx forum app.
"""

__version__ = "0.3.8"
__version__ = "0.5.3"
128 changes: 126 additions & 2 deletions forum/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
UserVote,
Subscription,
MongoContent,
ModerationAuditLog,
DiscussionMuteRecord,
)


Expand Down Expand Up @@ -55,11 +57,12 @@ class CommentThreadAdmin(admin.ModelAdmin): # type: ignore
"context",
"closed",
"pinned",
"is_spam",
"created_at",
"updated_at",
)
search_fields = ("title", "body", "author__username", "course_id")
list_filter = ("thread_type", "context", "closed", "pinned")
list_filter = ("thread_type", "context", "closed", "pinned", "is_spam")


@admin.register(Comment)
Expand All @@ -74,9 +77,10 @@ class CommentAdmin(admin.ModelAdmin): # type: ignore
"updated_at",
"endorsed",
"anonymous",
"is_spam",
)
search_fields = ("body", "author__username", "comment_thread__title")
list_filter = ("endorsed", "anonymous")
list_filter = ("endorsed", "anonymous", "is_spam")


@admin.register(EditHistory)
Expand Down Expand Up @@ -146,9 +150,129 @@ class SubscriptionAdmin(admin.ModelAdmin): # type: ignore
list_filter = ("source_content_type",)


@admin.register(DiscussionMuteRecord)
class DiscussionMuteAdmin(admin.ModelAdmin): # type: ignore
"""Admin interface for DiscussionMuteRecord model."""

list_display = (
"muted_user",
"muted_by",
"course_id",
"scope",
"reason",
"is_active",
"created",
"modified",
)
search_fields = (
"muted_user__username",
"muted_by__username",
"reason",
"course_id",
)
list_filter = ("scope", "is_active", "created", "modified")


@admin.register(MongoContent)
class MongoContentAdmin(admin.ModelAdmin): # type: ignore
"""Admin interface for MongoContent model."""

list_display = ("mongo_id", "content_object_id", "content_type")
search_fields = ("mongo_id",)


@admin.register(ModerationAuditLog)
class ModerationAuditLogAdmin(admin.ModelAdmin): # type: ignore
"""Admin interface for ModerationAuditLog model."""

list_display = (
"timestamp",
"classification",
"actions_taken",
"body_preview",
"original_author",
"moderator_override",
"confidence_score",
)
list_filter = (
"classification",
"moderator_override",
"timestamp",
)
search_fields = (
"original_author__username",
"moderator__username",
"reasoning",
"override_reason",
"body",
)
readonly_fields = (
"timestamp",
"body",
"classifier_output",
"reasoning",
"classification",
"actions_taken",
"confidence_score",
"original_author",
)
fieldsets = (
(
"Moderation Decision",
{
"fields": (
"timestamp",
"classification",
"actions_taken",
"confidence_score",
"reasoning",
)
},
),
("Content Information", {"fields": ("body",)}),
("Author Information", {"fields": ("original_author",)}),
(
"Human Override",
{
"fields": (
"moderator_override",
"moderator",
"override_reason",
)
},
),
(
"Technical Details",
{
"fields": ("classifier_output",),
"classes": ("collapse",),
},
),
)

def body_preview(self, obj): # type: ignore
"""Return a truncated preview of the body for list display."""
if obj.body:
return obj.body[:100] + "..." if len(obj.body) > 100 else obj.body
return "-"

body_preview.short_description = "Body Preview" # type: ignore

# pylint: disable=unused-argument
def has_add_permission(self, request): # type: ignore[no-untyped-def]
"""Disable adding audit logs manually."""
return False

# pylint: disable=unused-argument
def has_delete_permission(self, request, obj=None): # type: ignore[no-untyped-def]
"""Disable deleting audit logs to maintain integrity."""
return False

def get_queryset(self, request): # type: ignore
"""Optimize queryset with related objects."""
return (
super()
.get_queryset(request)
.select_related("original_author", "moderator")
.order_by("-timestamp")
)
Loading