from django.contrib import messages from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator from django.db.models import Count from django.http import HttpResponseForbidden from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from common.markdown import render_markdown from .forms import AnnouncementForm from .models import Announcement, AnnouncementComment, AnnouncementRead, Notification def _staff_only(user): return user.is_authenticated and user.is_staff def announcement_list(request): """Every published global announcement.""" announcements = ( Announcement.objects.filter(published=True) .annotate(comments_count=Count('comments')) .order_by('-created_at', '-pk') ) paginator = Paginator(announcements, 10) page = paginator.get_page(request.GET.get('page')) read_ids = set() if request.user.is_authenticated: read_ids = set( AnnouncementRead.objects.filter(user=request.user) .values_list('announcement_id', flat=True) ) return render(request, 'announcements/announcement_list.html', { 'page': page, 'read_ids': read_ids, }) def announcement_detail(request, pk): """One announcement + its comment section. Marks it read on view.""" announcement = get_object_or_404( Announcement.objects.annotate(comments_count=Count('comments')), pk=pk, published=True, ) if request.user.is_authenticated: AnnouncementRead.objects.get_or_create( announcement=announcement, user=request.user, ) comments = announcement.comments.select_related('user').all() return render(request, 'announcements/announcement_detail.html', { 'announcement': announcement, 'comments': comments, 'can_comment': request.user.is_authenticated, }) @login_required def announcement_create(request): if not _staff_only(request.user): return HttpResponseForbidden('Only staff can manage announcements.') if request.method == 'POST': form = AnnouncementForm(request.POST) if form.is_valid(): form.instance.created_by = request.user form.save() messages.success(request, 'Announcement published.') return redirect('announcements:detail', pk=form.instance.pk) else: form = AnnouncementForm() return render(request, 'announcements/announcement_form.html', { 'form': form, 'editing': False, }) @login_required def announcement_edit(request, pk): if not _staff_only(request.user): return HttpResponseForbidden('Only staff can manage announcements.') announcement = get_object_or_404(Announcement, pk=pk) if request.method == 'POST': form = AnnouncementForm(request.POST, instance=announcement) if form.is_valid(): form.save() messages.success(request, 'Announcement updated.') return redirect('announcements:detail', pk=announcement.pk) else: form = AnnouncementForm(instance=announcement) return render(request, 'announcements/announcement_form.html', { 'form': form, 'announcement': announcement, 'editing': True, }) @login_required def announcement_delete(request, pk): if not _staff_only(request.user): return HttpResponseForbidden('Only staff can manage announcements.') announcement = get_object_or_404(Announcement, pk=pk) if request.method == 'POST': announcement.delete() messages.success(request, 'Announcement deleted.') return redirect('announcements:list') @login_required def announcement_add_comment(request, pk): announcement = get_object_or_404(Announcement, pk=pk) if request.method == 'POST': body = request.POST.get('body', '').strip() if body: AnnouncementComment.objects.create( announcement=announcement, user=request.user, body=body, ) messages.success(request, 'Comment added.') else: messages.error(request, 'Comment cannot be empty.') return redirect('announcements:detail', pk=announcement.pk) @login_required def announcement_edit_comment(request, pk, comment_id): announcement = get_object_or_404(Announcement, pk=pk) comment = get_object_or_404( AnnouncementComment, pk=comment_id, announcement=announcement, ) if comment.user_id != request.user.pk and not request.user.is_staff: return HttpResponseForbidden('You can only edit your own comments.') if request.method == 'POST': body = request.POST.get('body', '').strip() if body: comment.body = body comment.save(update_fields=['body', 'updated_at']) messages.success(request, 'Comment updated.') else: messages.error(request, 'Comment cannot be empty.') return redirect('announcements:detail', pk=announcement.pk) @login_required def announcement_delete_comment(request, pk, comment_id): announcement = get_object_or_404(Announcement, pk=pk) comment = get_object_or_404( AnnouncementComment, pk=comment_id, announcement=announcement, ) if comment.user_id != request.user.pk and not request.user.is_staff: return HttpResponseForbidden('You can only delete your own comments.') if request.method == 'POST': comment.delete() messages.success(request, 'Comment deleted.') return redirect('announcements:detail', pk=announcement.pk) @login_required def personal_notifications(request): """The user's own personal announcement page.""" notifications = Notification.objects.filter(user=request.user) paginator = Paginator(notifications, 20) page = paginator.get_page(request.GET.get('page')) return render(request, 'announcements/notifications.html', { 'page': page, }) @login_required def mark_all_notifications_read(request): if request.method == 'POST': Notification.objects.filter(user=request.user, read=False).update(read=True) messages.success(request, 'All notifications marked as read.') return redirect(request.POST.get('next') or reverse('announcements:personal'))