Skip to content

Conversation

@muhammadadeeltajamul
Copy link
Contributor

Added an API to allow priviledged users to bulk delete posts of a user
Following roles are allowed to perform this action

  • Discussion Moderator
  • Discussion Admin
  • Course Staff
  • Course Instructor
  • Global Staff

Purpose of this endpoint is to allow instructors and privileged users to delete all spam posts of a single user in one action.

Ticket: INF-2026

"""

authentication_classes = (
JwtAuthentication, BearerAuthenticationAllowInactiveUser, SessionAuthenticationAllowInactiveUser,
Copy link
Contributor

Choose a reason for hiding this comment

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

Access should only be allowed to verified and active users.

execute_task = request.GET.get("execute", "false").lower() == "true"
if (not username) or (not course_id):
raise BadRequest("username and course_id are required.")
course_or_org = request.GET.get("course_or_org", "course")
Copy link
Contributor

Choose a reason for hiding this comment

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

course and org should be 2 different boolean attributes.

try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise BadRequest("Invalid request") # pylint: disable=raise-missing-from
Copy link
Contributor

Choose a reason for hiding this comment

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

404 error is more appropriate here because the error is record does not exist.

Comment on lines 1575 to 1587
query_params = {
"course_id": {"$in": course_ids},
"author_id": str(user.id),
"_type": "Comment"
}
comment_count = Comment()._collection.count_documents(query_params) # pylint: disable=protected-access

query_params["_type"] = "CommentThread"
thread_count = CommentThread()._collection.count_documents(query_params) # pylint: disable=protected-access

query_params["username"] = username
query_params.pop("_type", None)

Copy link
Contributor

Choose a reason for hiding this comment

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

This code should be moved to django_comment_common models or utils.


for course_id in course_ids:
if (
CourseStaffRole(course_id).has_user(request.user) or
Copy link
Contributor

Choose a reason for hiding this comment

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

This check can be optimized by fetching all active user course staff and course instructor roles. fetch only course_ids then comparing them with the enrollment list

course_id = CourseKey.from_string(course_id)
org_id = course_id.org
course_ids = CourseEnrollment.objects.filter(user=user).values_list('course_id', flat=True)
course_ids = [c_id for c_id in course_ids if c_id.org == org_id]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we filter org directly in the CourseEnrollment query if possible. This approach will query on each course

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will not query database for org. course_id is CourseKeyField. It is basically a string field that gets processed after query and contains attributes

Copy link
Contributor

@hassan-raza-1 hassan-raza-1 Jul 18, 2025

Choose a reason for hiding this comment

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

Yes, It may not add query. But filtering may save extra lines of code
CourseEnrollment.objects.filter(user=user, course__org=org_id).values_list('course_id', flat=True)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will not work... In database it is just a string, not a table

Copy link
Contributor

Choose a reason for hiding this comment

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

In my opinion, this should work because CourseEnrollment has a foreign key to CourseOverview, and CourseOverview includes the org field.

@muhammadadeeltajamul muhammadadeeltajamul merged commit 989ecfe into master Jul 21, 2025
49 checks passed
@muhammadadeeltajamul muhammadadeeltajamul deleted the inf-2026 branch July 21, 2025 04:43
@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX staging environment in preparation for a release to production.

@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX production environment.

1 similar comment
@edx-pipeline-bot
Copy link
Contributor

2U Release Notice: This PR has been deployed to the edX production environment.

sumair-arbisoft pushed a commit to sumair-arbisoft/edx-platform that referenced this pull request Aug 5, 2025
openedx#37030)

* feat: added endpoint for priviledged roles to delete threads of a user

* chore: moved forum calls to django comment common app

* fix: fixed nits
@pdpinch
Copy link
Contributor

pdpinch commented Sep 25, 2025

Is there a UI that goes with this API?

@muhammadadeeltajamul
Copy link
Contributor Author

Is there a UI that goes with this API?

Yes. PR that adds UI in discussions

taimoor-ahmed-1 pushed a commit to edly-io/edx-platform that referenced this pull request Oct 6, 2025
taimoor-ahmed-1 pushed a commit to edly-io/edx-platform that referenced this pull request Dec 14, 2025
openedx#37030)

* feat: added endpoint for priviledged roles to delete threads of a user

* chore: moved forum calls to django comment common app

* fix: fixed nits
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.

6 participants