Skip to content

Conversation

@naincy128
Copy link

@naincy128 naincy128 commented Dec 1, 2025

Description

This update introduces a comprehensive Mute / Unmute feature for discussion forums, enabling learners and staff to manage unwanted interactions more effectively while maintaining a healthy learning environment. The feature supports both personal and course-wide mute scopes, with clear role-based restrictions and overrides.

The implementation ensures muted content is hidden retroactively as well as for future posts, without notifying muted users. Special handling is included to prevent learners from muting staff or themselves, while giving staff full moderation control across the course.

Features

Learner Capabilities

  • Added ability for learners to mute other learners (staff cannot be muted).
  • Added personal mute list view and management.
  • Enabled unmuting of previously muted users.
  • Introduced “Mute and Report” action to mute a user and report their content in a single step.
  • Prevented learners from muting themselves.

Staff Capabilities

  • Includes all learner-level mute features.
  • Added ability to mute users course-wide.
  • Enabled viewing of both personal and course-wide mute lists.
  • Allowed staff to unmute users across all mute scopes.

Key Behaviors

  • Muted users are not notified when they are muted.
  • Staff users cannot be muted by learners.
  • Course-wide mutes override personal mutes.

Linked PRs

@naincy128 naincy128 changed the title feat: implement discussion mute/unmute feature with user and staff-le… feat: implement discussion mute/unmute feature with user and staff-level controls Dec 1, 2025
@naincy128 naincy128 marked this pull request as ready for review January 16, 2026 04:57
Copilot AI review requested due to automatic review settings January 16, 2026 04:57
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a comprehensive mute/unmute feature for discussion forums, enabling users to manage unwanted interactions with role-based controls. The implementation adds personal and course-wide mute capabilities for learners and staff, with retroactive content filtering and proper permission checks.

Changes:

  • Added five new API endpoints for mute/unmute operations with dual implementations
  • Implemented content filtering to hide posts from muted users
  • Added permission checks to prevent learners from muting staff and self-muting
  • Integrated with forum service backend for mute record management

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
views.py Added MuteUserView, UnmuteUserView, MuteAndReportView, MutedUsersListView, and MuteStatusView endpoints
urls.py Registered new mute/unmute endpoints including duplicate forum-prefixed routes
permissions.py Added can_mute_user, can_unmute_user, can_view_muted_users functions and CanMuteUsers permission class
serializers.py Added 9 new serializers for mute request/response handling
forms.py Added include_muted field to ThreadListGetForm and CommentListGetForm
api.py Added get_muted_user_ids and filter_muted_content functions with integration into thread/comment retrieval
forum_integration.py New service layer for forum API integration with ForumMuteService and ForumIntegrationService classes
forum_mute_views.py Duplicate implementation of mute views prefixed with "Forum"
test files Added test coverage for permissions and updated existing tests with mocking

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

content_type=content_type,
content_object_id=thread_id,
user=request.user,
defaults={'flagged_at': datetime.now()}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using datetime.now() instead of timezone.now(). This creates a naive datetime which can cause timezone-related bugs. The code imports timezone from django.utils (line 14) and uses timezone.now() elsewhere (line 2122), but here uses datetime.now(). This inconsistency should be fixed.

Copilot uses AI. Check for mistakes.
content_type=content_type,
content_object_id=comment_id,
user=request.user,
defaults={'flagged_at': datetime.now()}
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using datetime.now() instead of timezone.now(). This creates a naive datetime which can cause timezone-related bugs. Should use timezone.now() for consistency with the rest of the codebase and to ensure timezone-aware datetimes.

Copilot uses AI. Check for mistakes.
Comment on lines +32 to +39
# Import new forum-based mute views
from lms.djangoapps.discussion.rest_api.forum_mute_views import (
ForumMuteUserView,
ForumUnmuteUserView,
ForumMuteAndReportView,
ForumMutedUsersListView,
ForumMuteStatusView,
)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR includes two complete implementations of the mute functionality: one set in views.py (MuteUserView, UnmuteUserView, etc.) and a duplicate set in forum_mute_views.py with 'Forum' prefixes. This creates significant code duplication (~590 lines duplicated). The two implementations are nearly identical with only minor variations. Consider consolidating to a single implementation to reduce maintenance burden and potential for bugs.

Suggested change
# Import new forum-based mute views
from lms.djangoapps.discussion.rest_api.forum_mute_views import (
ForumMuteUserView,
ForumUnmuteUserView,
ForumMuteAndReportView,
ForumMutedUsersListView,
ForumMuteStatusView,
)

Copilot uses AI. Check for mistakes.
username = raw_data.get('username')
is_course_wide = raw_data.get('is_course_wide', False)

log.info(f"[UNMUTE_DEBUG] Received username={username}, is_course_wide={is_course_wide}")
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug logging with '[UNMUTE_DEBUG]' prefix should be removed before merging to production. Debug log statements are appropriate during development but should be cleaned up for production code.

Suggested change
log.info(f"[UNMUTE_DEBUG] Received username={username}, is_course_wide={is_course_wide}")

Copilot uses AI. Check for mistakes.
Comment on lines +246 to +249
log.info(
'[UNMUTE_DEBUG] scope=%s, muted_user_id=%s, course_id=%s',
data.get("scope", "personal"), target_user.id, str(course_key)
)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug logging with '[UNMUTE_DEBUG]' prefix should be removed before merging to production. Debug log statements are appropriate during development but should be cleaned up for production code.

Suggested change
log.info(
'[UNMUTE_DEBUG] scope=%s, muted_user_id=%s, course_id=%s',
data.get("scope", "personal"), target_user.id, str(course_key)
)

Copilot uses AI. Check for mistakes.
if thread_id:
# Report thread using AbuseFlagger
try:
forum_thread = ForumThread.objects.get(pk=thread_id)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable forum_thread is not used.

Suggested change
forum_thread = ForumThread.objects.get(pk=thread_id)
ForumThread.objects.get(pk=thread_id)

Copilot uses AI. Check for mistakes.
if thread:
thread.flagAbuse(request.user, voteable=thread)

report_record = {
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable report_record is not used.

Copilot uses AI. Check for mistakes.
thread = Thread.find(thread_id)
if thread:
thread.flagAbuse(request.user, voteable=thread)
report_record = {
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable report_record is not used.

Copilot uses AI. Check for mistakes.
elif comment_id:
# Report comment using AbuseFlagger
try:
forum_comment = ForumComment.objects.get(pk=comment_id)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable forum_comment is not used.

Suggested change
forum_comment = ForumComment.objects.get(pk=comment_id)
ForumComment.objects.get(pk=comment_id)

Copilot uses AI. Check for mistakes.
if comment:
comment.flagAbuse(request.user, voteable=comment)

report_record = {
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable report_record is not used.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants