Browse Source

lock and unlock chats feature

models updated with lock values , course model has two group lock and professor lock options

serializers updated with new fields

admin panel updated for this new fields

signals configured to automatically lock/unlock the chatrooms related to courses based on the boolean values of course

to chat room page navigation shortcut is updated with unfold action buttons instead of html files
master
Mohsen Taba 3 weeks ago
parent
commit
b179437dfd
  1. 23
      apps/chat/admin.py
  2. 18
      apps/chat/migrations/0002_roommessage_is_locked.py
  3. 6
      apps/chat/models.py
  4. 3
      apps/course/admin/course.py
  5. 23
      apps/course/migrations/0002_course_is_chat_group_lock_course_is_prof_chat_lock.py
  6. 23
      apps/course/migrations/0003_rename_is_chat_group_lock_course_is_group_chat_locked_and_more.py
  7. 9
      apps/course/models/course.py
  8. 5
      apps/course/serializers/course.py
  9. 28
      apps/course/signals.py
  10. 12
      templates/admin/chat/chatmessage/change_list.html

23
apps/chat/admin.py

@ -4,6 +4,9 @@ from django.utils.translation import gettext_lazy as _
from django.db.models import Count
from unfold.admin import ModelAdmin, TabularInline
from unfold.contrib.filters.admin import RangeNumericFilter, RangeDateTimeFilter
from django.shortcuts import redirect
from django.urls import reverse
from unfold.decorators import action
from apps.chat.models import RoomMessage, ChatMessage, MessageReadStatus
from utils.admin import project_admin_site
@ -44,13 +47,13 @@ User = get_user_model()
class RoomMessageAdmin(ModelAdmin):
list_display = (
'name', 'room_type_badge', 'course', 'initiator',
'messages_count', 'view_messages_button'
'messages_count', 'view_messages_button','is_locked'
)
list_filter = (
'room_type',
('created_at', RangeDateTimeFilter),
('updated_at', RangeDateTimeFilter),
'course'
'course','is_locked'
)
search_fields = ('name', 'description', 'course__title', 'initiator__username', 'recipient__username')
ordering = ('-created_at',)
@ -59,7 +62,7 @@ class RoomMessageAdmin(ModelAdmin):
fieldsets = (
(_("Room Information"), {
'fields': ('name', 'description', 'room_type', 'messages_count'),
'fields': ('name', 'description', 'room_type', 'messages_count','is_locked'),
'classes': ('grid-col-2',),
}),
(_("Relations"), {
@ -127,7 +130,7 @@ class MessageReadStatusInline(TabularInline):
class ChatMessageAdmin(ModelAdmin):
change_list_template = 'admin/chat/chatmessage/change_list.html'
# change_list_template = 'admin/chat/chatmessage/change_list.html'
list_display = (
'id', 'room', 'sender', 'content_type_badge', 'content_preview',
'content_size_display', 'has_attachment', 'sent_at', 'is_deleted_status'
@ -167,7 +170,17 @@ class ChatMessageAdmin(ModelAdmin):
'classes': ('grid-col-2',),
}),
)
actions_list = ["back_to_chat_rooms"]
@action(
description=_("Back to Chat Rooms"),
icon="arrow_back", # Unfold natively supports Google Material Icons!
)
def back_to_chat_rooms(self, request):
"""Redirects the admin back to the RoomMessage list"""
url = reverse('admin:chat_roommessage_changelist')
return redirect(url)
def content_preview(self, obj):
if obj.content_type == 'text':
preview = obj.content[:50] + '...' if len(obj.content) > 50 else obj.content

18
apps/chat/migrations/0002_roommessage_is_locked.py

@ -0,0 +1,18 @@
# Generated by Django 5.2.12 on 2026-04-26 14:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chat', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='roommessage',
name='is_locked',
field=models.BooleanField(default=False, help_text='If True, only the professor and admins can send new messages.', verbose_name='Is Locked'),
),
]

6
apps/chat/models.py

@ -50,6 +50,12 @@ class RoomMessage(models.Model):
auto_now=True,
verbose_name="Updated At"
)
is_locked = models.BooleanField(
default=False,
verbose_name="Is Locked",
help_text="If True, only the professor and admins can send new messages."
)
room_type = models.CharField(
max_length=10,
choices=RoomTypeChoices.choices,

3
apps/course/admin/course.py

@ -293,6 +293,9 @@ class CourseAdmin(DirectCourseAdmin):
(_('Status'), {
'fields': ('status', 'is_online', 'online_link'),
}),
(_('Chat Settings'), {
'fields': ('is_group_chat_locked', 'is_professor_chat_locked'),
}),
(_('Course Details'), {
'fields': ('description', 'short_description', 'level', 'duration', 'lessons_count',),
# 'classes': ['tab'],

23
apps/course/migrations/0002_course_is_chat_group_lock_course_is_prof_chat_lock.py

@ -0,0 +1,23 @@
# Generated by Django 5.2.12 on 2026-04-26 15:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('course', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='course',
name='is_chat_group_lock',
field=models.BooleanField(default=False, verbose_name='Lock Group Chat'),
),
migrations.AddField(
model_name='course',
name='is_prof_chat_lock',
field=models.BooleanField(default=False, verbose_name='Lock Private Chats with Professor'),
),
]

23
apps/course/migrations/0003_rename_is_chat_group_lock_course_is_group_chat_locked_and_more.py

@ -0,0 +1,23 @@
# Generated by Django 5.2.12 on 2026-04-26 15:18
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('course', '0002_course_is_chat_group_lock_course_is_prof_chat_lock'),
]
operations = [
migrations.RenameField(
model_name='course',
old_name='is_chat_group_lock',
new_name='is_group_chat_locked',
),
migrations.RenameField(
model_name='course',
old_name='is_prof_chat_lock',
new_name='is_professor_chat_locked',
),
]

9
apps/course/models/course.py

@ -98,6 +98,15 @@ class Course(models.Model):
verbose_name=_('Course Final Price'), decimal_places=2, max_digits=10, default=0.00, blank=True,
help_text=_('This field is automatically calculated based on the discount percentage.')
)
is_group_chat_locked = models.BooleanField(
default=False,
verbose_name=_('Lock Group Chat')
)
is_professor_chat_locked = models.BooleanField(
default=False,
verbose_name=_('Lock Private Chats with Professor')
)
timing = models.JSONField(blank=True, null=True, default=default_timing, verbose_name=_("Timing"))
features = models.JSONField(verbose_name=_('Course features'), default=dict, blank=True, null=True)

5
apps/course/serializers/course.py

@ -1,5 +1,4 @@
from rest_framework import serializers
# from dj_filer.admin import get_thumbs
from utils import get_thumbs
from apps.course.models import Course, CourseCategory, Attachment, Glossary, LessonCompletion, Participant, Lesson, CourseAttachment, CourseGlossary, CourseLesson
@ -134,7 +133,9 @@ class CourseDetailSerializer(serializers.ModelSerializer):
'features',
'last_lesson_id',
'room_id',
'user_transaction_status'
'user_transaction_status',
'is_group_chat_locked',
'is_professor_chat_locked'
]
def get_room_id(self, obj):

28
apps/course/signals.py

@ -1,5 +1,6 @@
from apps.course.models import Course
from apps.chat.models import RoomMessage
from django.db.models import Q
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
@ -53,4 +54,29 @@ def invalidate_professor_course_cache(sender, instance, **kwargs):
def invalidate_professor_profile_cache(sender, instance, **kwargs):
if instance.user_type == UserModel.UserType.PROFESSOR:
cache_key = f"professor_detail_{instance.slug}"
cache.delete(cache_key)
cache.delete(cache_key)
@receiver(post_save, sender=Course)
def sync_course_chat_locks(sender, instance, **kwargs):
"""
Automatically locks/unlocks the related chat rooms when the admin
toggles the chat locks on the Course page.
"""
# 1. Update the Group Chat
RoomMessage.objects.filter(
course=instance,
room_type=RoomMessage.RoomTypeChoices.GROUP
).update(is_locked=instance.is_group_chat_locked)
# 2. Update the Private Chats between the Professor and Students of this course
# Get all student IDs enrolled in this course
student_ids = instance.participants.values_list('student_id', flat=True)
if student_ids:
RoomMessage.objects.filter(
room_type=RoomMessage.RoomTypeChoices.PRIVATE
).filter(
# Find rooms where initiator is prof and recipient is student, OR vice versa
Q(initiator_id=instance.professor_id, recipient_id__in=student_ids) |
Q(initiator_id__in=student_ids, recipient_id=instance.professor_id)
).update(is_locked=instance.is_professor_chat_locked)

12
templates/admin/chat/chatmessage/change_list.html

@ -1,12 +0,0 @@
{% extends "admin/change_list.html" %}
{% load i18n admin_urls %}
{% block object-tools-items %}
{{ block.super }}
<li>
<a href="{% url 'admin:chat_roommessage_changelist' %}" class="unfold-button unfold-button-secondary">
<span class="material-icons-outlined">arrow_back</span>
{% translate "Back to Chat Rooms" %}
</a>
</li>
{% endblock %}
Loading…
Cancel
Save