From d4052f9c3534df5d446eae540640fff62a98408e Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Fri, 15 Nov 2024 13:46:45 +1300 Subject: [PATCH 01/19] feat: add session recordings --- ietf/meeting/urls.py | 1 + ietf/meeting/views.py | 57 +++++++++++++++ .../meeting/add_session_recordings.html | 70 +++++++++++++++++++ .../meeting/session_details_panel.html | 49 +++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 ietf/templates/meeting/add_session_recordings.html diff --git a/ietf/meeting/urls.py b/ietf/meeting/urls.py index 42a5de623c..a5d6435518 100644 --- a/ietf/meeting/urls.py +++ b/ietf/meeting/urls.py @@ -16,6 +16,7 @@ def get_redirect_url(self, *args, **kwargs): safe_for_all_meeting_types = [ url(r'^session/(?P[-a-z0-9]+)/?$', views.session_details), url(r'^session/(?P\d+)/drafts$', views.add_session_drafts), + url(r'^session/(?P\d+)/recordings$', views.add_session_recordings), url(r'^session/(?P\d+)/attendance$', views.session_attendance), url(r'^session/(?P\d+)/bluesheets$', views.upload_session_bluesheets), url(r'^session/(?P\d+)/minutes$', views.upload_session_minutes), diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index a195e74ce3..3625550d81 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -2568,6 +2568,63 @@ def add_session_drafts(request, session_id, num): 'form': form, }) +class SessionNotesAndRecordingsForm(forms.Form): + title = forms.CharField(max_length=255) + url = forms.URLField(label="Link to recording (YouTube only)") + + def __init__(self, *args, **kwargs): + self.already_linked = kwargs.pop('already_linked') + super(self.__class__, self).__init__(*args, **kwargs) + + def clean(self):title = forms.CharField(max_length=255) + url = forms.URLField(label="Link to recording (YouTube only)") + + def __init__(self, *args, **kwargs): + self.already_linked = kwargs.pop('already_linked') + super(self.__class__, self).__init__(*args, **kwargs) + + def clean(self): + url = self.cleaned_data['url'] + parsed_url = urlparse(url) + if parsed_url.hostname not in ['www.youtube.com', 'youtube.com', 'youtu.be', 'm.youtube.com', 'youtube-nocookie.com', 'www.youtube-nocookie.com']: + raise forms.ValidationError("Must be a YouTube URL") + problems = set(url).intersection(set(self.already_linked)) + if problems: + raise forms.ValidationError("Already linked: %s" % ', '.join([d.name for d in problems])) + return self.cleaned_data + + +def add_session_recordings(request, session_id, num): + # num is redundant, but we're dragging it along an artifact of where we are in the current URL structure + session = get_object_or_404(Session,pk=session_id) + if not session.can_manage_materials(request.user): + raise Http404 + if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"): + raise Http404 + + already_linked = [material.external_url for material in session.materials.filter(type="recording").exclude(states__type="recording", states__slug='deleted').order_by('presentations__order')] + + session_number = None + sessions = get_sessions(session.meeting.number,session.group.acronym) + if len(sessions) > 1: + session_number = 1 + sessions.index(session) + + if request.method == 'POST': + form = SessionNotesAndRecordingsForm(request.POST,already_linked=already_linked) + if form.is_valid(): + title = form.cleaned_data['title'] + url = form.cleaned_data['url'] + create_recording(session, url, title=title, user=request.user.person) + return redirect('ietf.meeting.views.session_details', num=session.meeting.number, acronym=session.group.acronym) + else: + form = SessionNotesAndRecordingsForm(already_linked=already_linked) + + return render(request, "meeting/add_session_recordings.html", + { 'session': session, + 'session_number': session_number, + 'already_linked': session.materials.filter(type="recording").exclude(states__type="recording", states__slug='deleted').order_by('presentations__order'), + 'form': form, + }) def session_attendance(request, session_id, num): """Session attendance view diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html new file mode 100644 index 0000000000..d7d8e933dc --- /dev/null +++ b/ietf/templates/meeting/add_session_recordings.html @@ -0,0 +1,70 @@ +{% extends "base.html" %} +{# Copyright The IETF Trust 2015, All Rights Reserved #} +{% load origin static django_bootstrap5 %} +{% block title %}Add I-Ds to {{ session.meeting }} : {{ session.group.acronym }}{% endblock %} +{% block pagehead %}{{ form.media.css }}{% endblock %} +{% block content %} + {% origin %} +

+ Add Notes and Recordings to {{ session.meeting }} + {% if session_number %}: Session {{ session_number }}{% endif %} +
+ {{ session.group.acronym }} + {% if session.name %}: {{ session.name }}{% endif %} + +

+ {% comment %} TODO: put the session name here or calculate the number at the meeting {% endcomment %} + {% if session.is_material_submission_cutoff %} +
+ The deadline for submission corrections has passed. This may affect published proceedings. +
+ {% endif %} +
+ This form will link additional Notes and Recordings to this session with a revision of "Current at time of presentation". For more fine grained control of versions, or to remove Notes and Recordings from a session, adjust the sessions associated with an Internet-Draft from the Internet-Draft's main page. +
+

Notes and Recordings already linked to this session

+ + + + + + + + {% if already_linked %} + + {% for sp in already_linked %} + + + + + {% endfor %} + + {% else %} + + + + + + {% endif %} +
RevisionDocument
+ {% if sp.rev %} + -{{ sp.rev }} + {% else %} + (current) + {% endif %} + {{ sp.title }}
(none)
+

Add additional Notes and Recordings to this session

+
+ {% csrf_token %} + {% bootstrap_form form %} + + + Back + +
+{% endblock %} +{% block js %}{{ form.media.js }}{% endblock %} \ No newline at end of file diff --git a/ietf/templates/meeting/session_details_panel.html b/ietf/templates/meeting/session_details_panel.html index a0f5884b9d..abe67037d3 100644 --- a/ietf/templates/meeting/session_details_panel.html +++ b/ietf/templates/meeting/session_details_panel.html @@ -367,6 +367,55 @@

Notes and recordings

{% endif %} + + +

Notes and Recordings already linked to this session

+ + + + + + + + {% if already_linked %} + + {% for sp in already_linked %} + + + + + {% endfor %} + + {% else %} + + + + + + {% endif %} +
RevisionDocument
+ {% if sp.rev %} + -{{ sp.rev }} + {% else %} + (current) + {% endif %} + {{ sp.title }}
(none)
+ +

Add additional recordings to this session

+
+ {% csrf_token %} + {% bootstrap_form form %} + + + Back + +
+{% endblock %} +{% block js %}{{ form.media.js }}{% endblock %} {% endif %} {% endwith %}{% endwith %} {% endfor %} From 420e296b29421b696653143d321d49b0a4671547 Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Mon, 18 Nov 2024 10:02:55 +1300 Subject: [PATCH 02/19] feat: add session recordings --- ietf/meeting/views.py | 16 +++--- ietf/settings.py | 3 ++ .../meeting/add_session_recordings.html | 11 ++-- .../meeting/session_details_panel.html | 53 +++---------------- 4 files changed, 21 insertions(+), 62 deletions(-) diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 3625550d81..9ed136159f 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -20,7 +20,7 @@ from functools import partialmethod import jsonschema from pathlib import Path -from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit +from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit, urlparse from tempfile import mkstemp from wsgiref.handlers import format_date_time @@ -103,6 +103,7 @@ from ietf.utils.response import permission_denied from ietf.utils.text import xslugify from ietf.utils.timezone import datetime_today, date_today +from ietf.settings import YOUTUBE_DOMAINS from .forms import (InterimMeetingModelForm, InterimAnnounceForm, InterimSessionModelForm, InterimCancelForm, InterimSessionInlineFormSet, RequestMinutesForm, @@ -2576,22 +2577,19 @@ def __init__(self, *args, **kwargs): self.already_linked = kwargs.pop('already_linked') super(self.__class__, self).__init__(*args, **kwargs) - def clean(self):title = forms.CharField(max_length=255) - url = forms.URLField(label="Link to recording (YouTube only)") - def __init__(self, *args, **kwargs): self.already_linked = kwargs.pop('already_linked') super(self.__class__, self).__init__(*args, **kwargs) - def clean(self): + def clean_url(self): url = self.cleaned_data['url'] parsed_url = urlparse(url) - if parsed_url.hostname not in ['www.youtube.com', 'youtube.com', 'youtu.be', 'm.youtube.com', 'youtube-nocookie.com', 'www.youtube-nocookie.com']: + if parsed_url.hostname not in YOUTUBE_DOMAINS: raise forms.ValidationError("Must be a YouTube URL") problems = set(url).intersection(set(self.already_linked)) if problems: raise forms.ValidationError("Already linked: %s" % ', '.join([d.name for d in problems])) - return self.cleaned_data + return url def add_session_recordings(request, session_id, num): @@ -2617,7 +2615,9 @@ def add_session_recordings(request, session_id, num): create_recording(session, url, title=title, user=request.user.person) return redirect('ietf.meeting.views.session_details', num=session.meeting.number, acronym=session.group.acronym) else: - form = SessionNotesAndRecordingsForm(already_linked=already_linked) + today = datetime.datetime.now() + initial = {'title': f"Video recording for {session.group.acronym} on {today.strftime('%b-%d-%Y at %H:%M:%S')}"} + form = SessionNotesAndRecordingsForm(initial=initial, already_linked=already_linked) return render(request, "meeting/add_session_recordings.html", { 'session': session, diff --git a/ietf/settings.py b/ietf/settings.py index 30f4b367c6..4d6e1b7920 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -1304,3 +1304,6 @@ def skip_unreadable_post(record): CSRF_TRUSTED_ORIGINS += ['http://localhost:8000', 'http://127.0.0.1:8000', 'http://[::1]:8000'] SESSION_COOKIE_SECURE = False SESSION_COOKIE_SAMESITE = 'Lax' + + +YOUTUBE_DOMAINS = ['www.youtube.com', 'youtube.com', 'youtu.be', 'm.youtube.com', 'youtube-nocookie.com', 'www.youtube-nocookie.com'] diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index d7d8e933dc..15f09cca92 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -6,7 +6,7 @@ {% block content %} {% origin %}

- Add Notes and Recordings to {{ session.meeting }} + Add Recordings to {{ session.meeting }} {% if session_number %}: Session {{ session_number }}{% endif %}
{{ session.group.acronym }} @@ -19,10 +19,7 @@

The deadline for submission corrections has passed. This may affect published proceedings. {% endif %} -
- This form will link additional Notes and Recordings to this session with a revision of "Current at time of presentation". For more fine grained control of versions, or to remove Notes and Recordings from a session, adjust the sessions associated with an Internet-Draft from the Internet-Draft's main page. -
-

Notes and Recordings already linked to this session

+

Recordings already linked to this session

@@ -53,13 +50,13 @@

Notes and Recordings already linked to this session

{% endif %}
-

Add additional Notes and Recordings to this session

+

Add additional recordings to this session

{% csrf_token %} {% bootstrap_form form %} diff --git a/ietf/templates/meeting/session_details_panel.html b/ietf/templates/meeting/session_details_panel.html index abe67037d3..9b7a192f05 100644 --- a/ietf/templates/meeting/session_details_panel.html +++ b/ietf/templates/meeting/session_details_panel.html @@ -367,55 +367,14 @@

Notes and recordings

{% endif %} + {% endif %} - -

Notes and Recordings already linked to this session

- - - - - - - - {% if already_linked %} - - {% for sp in already_linked %} - - - - - {% endfor %} - - {% else %} - - - - - - {% endif %} -
RevisionDocument
- {% if sp.rev %} - -{{ sp.rev }} - {% else %} - (current) - {% endif %} - {{ sp.title }}
(none)
- -

Add additional recordings to this session

- - {% csrf_token %} - {% bootstrap_form form %} - -
- Back + {% if can_manage_materials %} + + Link additional recordings to session -
-{% endblock %} -{% block js %}{{ form.media.js }}{% endblock %} {% endif %} + {% endwith %}{% endwith %} {% endfor %} From 061b68a232c847663c51dd2e9216c0d9c95c142b Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Mon, 18 Nov 2024 10:41:27 +1300 Subject: [PATCH 03/19] feat: deleting recordings --- ietf/meeting/utils.py | 6 ++ ietf/meeting/views.py | 26 ++++---- .../meeting/add_session_recordings.html | 65 ++++++++++--------- 3 files changed, 57 insertions(+), 40 deletions(-) diff --git a/ietf/meeting/utils.py b/ietf/meeting/utils.py index b68a311f5d..a63c41d58b 100644 --- a/ietf/meeting/utils.py +++ b/ietf/meeting/utils.py @@ -870,6 +870,12 @@ def create_recording(session, url, title=None, user=None): return doc +def delete_recording(session, pk): + ''' + Delete a session recording + ''' + Document.objects.get(pk=pk, group=session.group).delete() + def get_next_sequence(group, meeting, type): ''' Returns the next sequence number to use for a document of type = type. diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 9ed136159f..95179bd1f4 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -86,7 +86,7 @@ from ietf.meeting.utils import swap_meeting_schedule_timeslot_assignments, bulk_create_timeslots from ietf.meeting.utils import preprocess_meeting_important_dates from ietf.meeting.utils import new_doc_for_session, write_doc_for_session -from ietf.meeting.utils import get_activity_stats, post_process, create_recording +from ietf.meeting.utils import get_activity_stats, post_process, create_recording, delete_recording from ietf.meeting.utils import participants_for_meeting, generate_bluesheet, bluesheet_data, save_bluesheet from ietf.message.utils import infer_message from ietf.name.models import SlideSubmissionStatusName, ProceedingsMaterialTypeName, SessionPurposeName @@ -2608,16 +2608,20 @@ def add_session_recordings(request, session_id, num): session_number = 1 + sessions.index(session) if request.method == 'POST': - form = SessionNotesAndRecordingsForm(request.POST,already_linked=already_linked) - if form.is_valid(): - title = form.cleaned_data['title'] - url = form.cleaned_data['url'] - create_recording(session, url, title=title, user=request.user.person) - return redirect('ietf.meeting.views.session_details', num=session.meeting.number, acronym=session.group.acronym) - else: - today = datetime.datetime.now() - initial = {'title': f"Video recording for {session.group.acronym} on {today.strftime('%b-%d-%Y at %H:%M:%S')}"} - form = SessionNotesAndRecordingsForm(initial=initial, already_linked=already_linked) + delete = request.POST['delete'] + if delete: + delete_recording(pk=delete, session=session) + else: + form = SessionNotesAndRecordingsForm(request.POST,already_linked=already_linked) + if form.is_valid(): + title = form.cleaned_data['title'] + url = form.cleaned_data['url'] + create_recording(session, url, title=title, user=request.user.person) + return redirect('ietf.meeting.views.session_details', num=session.meeting.number, acronym=session.group.acronym) + + today = datetime.datetime.now() + initial = {'title': f"Video recording for {session.group.acronym} on {today.strftime('%b-%d-%Y at %H:%M:%S')}"} + form = SessionNotesAndRecordingsForm(initial=initial, already_linked=already_linked) return render(request, "meeting/add_session_recordings.html", { 'session': session, diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index 15f09cca92..9bfd168cd8 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -19,37 +19,44 @@

The deadline for submission corrections has passed. This may affect published proceedings. {% endif %} -

Recordings already linked to this session

- - - - - - - - {% if already_linked %} - - {% for sp in already_linked %} - - - - - {% endfor %} - - {% else %} - + + {% csrf_token %} +

Recordings already linked to this session

+
RevisionDocument
- {% if sp.rev %} - -{{ sp.rev }} - {% else %} - (current) - {% endif %} - {{ sp.title }}
+ - + + + - - {% endif %} -
(none)RevisionDocumentDelete
+ + {% if already_linked %} + + {% for sp in already_linked %} + + + {% if sp.rev %} + -{{ sp.rev }} + {% else %} + (current) + {% endif %} + + {{ sp.pk }} + + {{ sp.title }} + + + {% endfor %} + + {% else %} + + + (none) + + + {% endif %} + +

Add additional recordings to this session

{% csrf_token %} From 2c15423891e43293ecdc1c8e74238dd9f9cae92a Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Mon, 18 Nov 2024 11:08:26 +1300 Subject: [PATCH 04/19] feat: deleting recordings and initial form values --- ietf/meeting/views.py | 29 ++++++------------- .../meeting/add_session_recordings.html | 2 -- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 95179bd1f4..ed17fd489a 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -2569,26 +2569,15 @@ def add_session_drafts(request, session_id, num): 'form': form, }) -class SessionNotesAndRecordingsForm(forms.Form): +class SessionRecordingsForm(forms.Form): title = forms.CharField(max_length=255) url = forms.URLField(label="Link to recording (YouTube only)") - def __init__(self, *args, **kwargs): - self.already_linked = kwargs.pop('already_linked') - super(self.__class__, self).__init__(*args, **kwargs) - - def __init__(self, *args, **kwargs): - self.already_linked = kwargs.pop('already_linked') - super(self.__class__, self).__init__(*args, **kwargs) - def clean_url(self): url = self.cleaned_data['url'] parsed_url = urlparse(url) if parsed_url.hostname not in YOUTUBE_DOMAINS: raise forms.ValidationError("Must be a YouTube URL") - problems = set(url).intersection(set(self.already_linked)) - if problems: - raise forms.ValidationError("Already linked: %s" % ', '.join([d.name for d in problems])) return url @@ -2600,28 +2589,28 @@ def add_session_recordings(request, session_id, num): if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"): raise Http404 - already_linked = [material.external_url for material in session.materials.filter(type="recording").exclude(states__type="recording", states__slug='deleted').order_by('presentations__order')] - session_number = None sessions = get_sessions(session.meeting.number,session.group.acronym) + today = datetime.datetime.now() + initial = {'title': f"Video recording for {session.group.acronym} on {today.strftime('%b-%d-%Y at %H:%M:%S')}"} + if len(sessions) > 1: session_number = 1 + sessions.index(session) if request.method == 'POST': - delete = request.POST['delete'] + delete = request.POST.get('delete', False) if delete: delete_recording(pk=delete, session=session) + form = SessionRecordingsForm(initial=initial) else: - form = SessionNotesAndRecordingsForm(request.POST,already_linked=already_linked) + form = SessionRecordingsForm(request.POST) if form.is_valid(): title = form.cleaned_data['title'] url = form.cleaned_data['url'] create_recording(session, url, title=title, user=request.user.person) return redirect('ietf.meeting.views.session_details', num=session.meeting.number, acronym=session.group.acronym) - - today = datetime.datetime.now() - initial = {'title': f"Video recording for {session.group.acronym} on {today.strftime('%b-%d-%Y at %H:%M:%S')}"} - form = SessionNotesAndRecordingsForm(initial=initial, already_linked=already_linked) + else: + form = SessionRecordingsForm(initial=initial) return render(request, "meeting/add_session_recordings.html", { 'session': session, diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index 9bfd168cd8..d6cc8f1be7 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -40,8 +40,6 @@

Recordings already linked to this session

{% else %} (current) {% endif %} - - {{ sp.pk }} {{ sp.title }} From f0598e98b2f6d0c3df0644591e4dfe23565d2f9f Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Tue, 19 Nov 2024 09:54:40 +1300 Subject: [PATCH 05/19] feat: use meeting date rather than today for initial title field. Fix delete recording --- ietf/meeting/utils.py | 4 +++- ietf/meeting/views.py | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ietf/meeting/utils.py b/ietf/meeting/utils.py index a63c41d58b..18927d0cf9 100644 --- a/ietf/meeting/utils.py +++ b/ietf/meeting/utils.py @@ -874,7 +874,9 @@ def delete_recording(session, pk): ''' Delete a session recording ''' - Document.objects.get(pk=pk, group=session.group).delete() + document = Document.objects.get(pk=pk, group=session.group).first() + if document: + document.delete() def get_next_sequence(group, meeting, type): ''' diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index ed17fd489a..e0f18c9b15 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -2591,8 +2591,9 @@ def add_session_recordings(request, session_id, num): session_number = None sessions = get_sessions(session.meeting.number,session.group.acronym) - today = datetime.datetime.now() - initial = {'title': f"Video recording for {session.group.acronym} on {today.strftime('%b-%d-%Y at %H:%M:%S')}"} + initial = { + 'title': f"Video recording for {session.group.acronym} on {session.meeting.date.strftime('%b-%d-%Y at %H:%M:%S')}" + } if len(sessions) > 1: session_number = 1 + sessions.index(session) From 3cc968e76231f70f8be9cdbe22a410ff9937e411 Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Tue, 19 Nov 2024 12:56:22 +1300 Subject: [PATCH 06/19] feat: confirm delete recordings modal. fix server utils delete recording --- ietf/meeting/utils.py | 2 +- .../meeting/add_session_recordings.html | 46 ++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/ietf/meeting/utils.py b/ietf/meeting/utils.py index 18927d0cf9..1848c16fef 100644 --- a/ietf/meeting/utils.py +++ b/ietf/meeting/utils.py @@ -874,7 +874,7 @@ def delete_recording(session, pk): ''' Delete a session recording ''' - document = Document.objects.get(pk=pk, group=session.group).first() + document = Document.objects.filter(pk=pk, group=session.group).first() if document: document.delete() diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index d6cc8f1be7..9b753f0bba 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -19,7 +19,7 @@

The deadline for submission corrections has passed. This may affect published proceedings. {% endif %} - + {% csrf_token %}

Recordings already linked to this session

@@ -55,6 +55,14 @@

Recordings already linked to this session

{% endif %}
+ +

Really delete ?

+
+ {% csrf_token %} + + +
+

Add additional recordings to this session

{% csrf_token %} @@ -69,4 +77,38 @@

Add additional recordings to this session

{% endblock %} -{% block js %}{{ form.media.js }}{% endblock %} \ No newline at end of file +{% block js %} + {{ form.media.js }} + + + +{% endblock %} \ No newline at end of file From ce04b71566b7d2bd117f18d4b536125cf1557d23 Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Tue, 19 Nov 2024 13:00:08 +1300 Subject: [PATCH 07/19] fix: removing debug console.log --- ietf/templates/meeting/add_session_recordings.html | 1 - 1 file changed, 1 deletion(-) diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index 9b753f0bba..9040cbdb39 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -90,7 +90,6 @@

Add additional recordings to this session

form.addEventListener('submit', (e) => { e.preventDefault() - console.log() dialog_submit.value = e.submitter.value const recording_link = e.submitter.closest('tr').querySelector('a') dialog_link.setAttribute('href', recording_link.getAttribute('href')) From 606bf6d64302918fae38ca4539ddc7b9163af20e Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Tue, 19 Nov 2024 13:02:14 +1300 Subject: [PATCH 08/19] feat: change button name from 'Ok' to 'Delete' for confirm deletion to be clearer --- ietf/templates/meeting/add_session_recordings.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index 9040cbdb39..a16089895d 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -60,7 +60,7 @@

Recordings already linked to this session

{% csrf_token %} - +

Add additional recordings to this session

From bda706155d1250b0d427c62d8d40a809e65791e8 Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Wed, 20 Nov 2024 07:34:25 +1300 Subject: [PATCH 09/19] feat: UTC time in string and delete modal text --- ietf/meeting/views.py | 4 +++- ietf/templates/meeting/add_session_recordings.html | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index e0f18c9b15..7b6646f4e2 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -2591,8 +2591,10 @@ def add_session_recordings(request, session_id, num): session_number = None sessions = get_sessions(session.meeting.number,session.group.acronym) + official_timeslotassignment = session.official_timeslotassignment() + assertion("official_timeslotassignment is not None") initial = { - 'title': f"Video recording for {session.group.acronym} on {session.meeting.date.strftime('%b-%d-%Y at %H:%M:%S')}" + 'title': f"Video recording for {session.group.acronym} on {official_timeslotassignment.timeslot.utc_start_time().strftime('%b-%d-%Y at %H:%M:%S')}" } if len(sessions) > 1: diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index a16089895d..bb2b8adc67 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -55,12 +55,12 @@

Recordings already linked to this session

{% endif %} - -

Really delete ?

+ +

Really delete the link to ?

{% csrf_token %} - +

Add additional recordings to this session

From 7b7caa27a05934936652c0b87f058173496dd365 Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Wed, 20 Nov 2024 10:12:33 +1300 Subject: [PATCH 10/19] fix: django html validation tests --- .../meeting/add_session_recordings.html | 77 ++++++++++--------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index bb2b8adc67..ccfe74acd5 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -19,47 +19,54 @@

The deadline for submission corrections has passed. This may affect published proceedings. {% endif %} -
- {% csrf_token %} -

Recordings already linked to this session

- - - - - - - - - {% if already_linked %} - - {% for sp in already_linked %} - - - - - - {% endfor %} - - {% else %} - + {% if already_linked %} + + {% csrf_token %} +

Recordings already linked to this session

+
RevisionDocumentDelete
- {% if sp.rev %} - -{{ sp.rev }} - {% else %} - (current) - {% endif %} - {{ sp.title }}
+ - + + + + + + {% for sp in already_linked %} + + + + + + {% endfor %} - {% endif %} +
(none)RevisionDocumentDelete
+ {% if sp.rev %} + -{{ sp.rev }} + {% else %} + (current) + {% endif %} + {{ sp.title }}
+
+ {% else %} + + + + + +
(none)
- - -

Really delete the link to ?

+ {% endif %} + + +

Really delete the link to (default)?

{% csrf_token %} - +
From fd4547ebfb3c5a173407c1babfe2592df45043b7 Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Wed, 20 Nov 2024 11:57:04 +1300 Subject: [PATCH 11/19] fix: django html validation tests --- ietf/templates/meeting/add_session_recordings.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index ccfe74acd5..05fceee95a 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -59,7 +59,7 @@

Recordings already linked to this session

{% endif %} @@ -95,6 +95,8 @@

Add additional recordings to this session

const dialog_submit = document.getElementById('delete_confirm_submit') const dialog_cancel = document.getElementById('delete_confirm_cancel') + dialog.style.maxWidth = '30vw'; + form.addEventListener('submit', (e) => { e.preventDefault() dialog_submit.value = e.submitter.value @@ -114,7 +116,7 @@

Add additional recordings to this session

dialog.close() } }) - }); + }) {% endblock %} \ No newline at end of file From 749730658fb23c8d8897221a7a49b16a5d724442 Mon Sep 17 00:00:00 2001 From: Matthew Holloway Date: Wed, 20 Nov 2024 12:21:33 +1300 Subject: [PATCH 12/19] fix: django html validation tests --- ietf/templates/meeting/add_session_recordings.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index 05fceee95a..2cf5b32e18 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -19,10 +19,10 @@

The deadline for submission corrections has passed. This may affect published proceedings. {% endif %} +

Recordings already linked to this session

{% if already_linked %}
{% csrf_token %} -

Recordings already linked to this session

@@ -50,6 +50,13 @@

Recordings already linked to this session

{% else %}
+ + + + + + + @@ -57,11 +64,6 @@

Recordings already linked to this session

RevisionDocumentDelete
(none)
{% endif %} -

Really delete the link to (default)?

From 01c8e8a48a216eff07fc7236b77a8fa23b8f97ce Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 14 Jan 2025 11:58:50 -0400 Subject: [PATCH 13/19] refactor: Work with SessionPresentations --- ietf/meeting/utils.py | 28 ++-- ietf/meeting/views.py | 26 +++- .../meeting/add_session_recordings.html | 128 +++++++++--------- 3 files changed, 102 insertions(+), 80 deletions(-) diff --git a/ietf/meeting/utils.py b/ietf/meeting/utils.py index 1848c16fef..adc19aa0c1 100644 --- a/ietf/meeting/utils.py +++ b/ietf/meeting/utils.py @@ -23,7 +23,7 @@ from ietf.dbtemplate.models import DBTemplate from ietf.meeting.models import (Session, SchedulingEvent, TimeSlot, Constraint, SchedTimeSessAssignment, SessionPresentation, Attended) -from ietf.doc.models import Document, State, NewRevisionDocEvent +from ietf.doc.models import Document, State, NewRevisionDocEvent, StateDocEvent from ietf.doc.models import DocEvent from ietf.group.models import Group from ietf.group.utils import can_manage_materials @@ -870,13 +870,25 @@ def create_recording(session, url, title=None, user=None): return doc -def delete_recording(session, pk): - ''' - Delete a session recording - ''' - document = Document.objects.filter(pk=pk, group=session.group).first() - if document: - document.delete() +def delete_recording(session_presentation, user=None): + """Delete a session recording""" + document = session_presentation.document + if document.type_id != "recording": + raise ValueError(f"Document {document.pk} is not a recording (type_id={document.type_id})") + recording_state = document.get_state("recording") + deleted_state = State.objects.get(type_id="recording", slug="deleted") + if recording_state.slug is None or recording_state != deleted_state: + # Update the recording state and create a history event + document.set_state(deleted_state) + StateDocEvent.objects.create( + type="changed_state", + by=user or Person.objects.get(name="(System)"), + doc=document, + rev=document.rev, + state_type=deleted_state.type, + state=deleted_state, + ) + session_presentation.delete() def get_next_sequence(group, meeting, type): ''' diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index abaa0356ee..13820991d5 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -2571,7 +2571,7 @@ def add_session_drafts(request, session_id, num): class SessionRecordingsForm(forms.Form): title = forms.CharField(max_length=255) - url = forms.URLField(label="Link to recording (YouTube only)") + url = forms.URLField(label="URL of the recording (YouTube only)") def clean_url(self): url = self.cleaned_data['url'] @@ -2590,20 +2590,32 @@ def add_session_recordings(request, session_id, num): raise Http404 session_number = None - sessions = get_sessions(session.meeting.number,session.group.acronym) official_timeslotassignment = session.official_timeslotassignment() assertion("official_timeslotassignment is not None") initial = { 'title': f"Video recording for {session.group.acronym} on {official_timeslotassignment.timeslot.utc_start_time().strftime('%b-%d-%Y at %H:%M:%S')}" } - + + # find session number if WG has more than one session at the meeting + sessions = get_sessions(session.meeting.number,session.group.acronym) if len(sessions) > 1: session_number = 1 + sessions.index(session) + recordings = session.get_material("recording", only_one=False) # Document queryset + presentations = session.presentations.filter(document__in=recordings) # SessionPresentation queryset + if request.method == 'POST': - delete = request.POST.get('delete', False) - if delete: - delete_recording(pk=delete, session=session) + pk_to_delete = request.POST.get('delete', None) + if pk_to_delete is not None: + session_presentation = get_object_or_404(session.presentations, pk=pk_to_delete) + try: + delete_recording(session_presentation) + except ValueError as err: + log(f"Error deleting recording from session {session.pk}: {err}") + messages.error( + request, + "Unable to delete this recording. Please contact the secretariat for assistance.", + ) form = SessionRecordingsForm(initial=initial) else: form = SessionRecordingsForm(request.POST) @@ -2618,7 +2630,7 @@ def add_session_recordings(request, session_id, num): return render(request, "meeting/add_session_recordings.html", { 'session': session, 'session_number': session_number, - 'already_linked': session.materials.filter(type="recording").exclude(states__type="recording", states__slug='deleted').order_by('presentations__order'), + 'already_linked': presentations, 'form': form, }) diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index 2cf5b32e18..13d2647fc7 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -6,7 +6,7 @@ {% block content %} {% origin %}

- Add Recordings to {{ session.meeting }} + Add Recordings to {{ session.meeting }} {% if session_number %}: Session {{ session_number }}{% endif %}
{{ session.group.acronym }} @@ -20,50 +20,49 @@

{% endif %}

Recordings already linked to this session

- {% if already_linked %} - - {% csrf_token %} - - - - - - - - - - {% for sp in already_linked %} - - - - - - {% endfor %} - -
RevisionDocumentDelete
- {% if sp.rev %} - -{{ sp.rev }} - {% else %} - (current) - {% endif %} - {{ sp.title }}
- - {% else %} +
+ {% csrf_token %} - - - - - + + + + + + + {% if already_linked|length == 0 %} - + - + {% else %} + {% for sp in already_linked %}{% with recording_doc=sp.document %} + + + + + + + {% endwith %}{% endfor %} + + {% endif %}
RevisionDocumentDelete
RevisionTitleURLDelete
(none)(none)
+ {% if sp.rev %} + {{ sp.rev }} + {% else %} + (current) + {% endif %} + {{ recording_doc.title }}{{ recording_doc.external_url }} + +
- {% endif %} +

Really delete the link to (default)?

@@ -72,7 +71,7 @@

Recordings already linked to this session

-

Add additional recordings to this session

+

Add a recording to this session

{% csrf_token %} {% bootstrap_form form %} @@ -88,37 +87,36 @@

Add additional recordings to this session

{% endblock %} {% block js %} {{ form.media.js }} - - + {% endblock %} \ No newline at end of file From 4783c4377ad54a55ec4e247feedf3e1e3644aece Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 14 Jan 2025 12:03:03 -0400 Subject: [PATCH 14/19] fix: better ordering --- ietf/meeting/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index 13820991d5..a9f799c385 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -2601,8 +2601,9 @@ def add_session_recordings(request, session_id, num): if len(sessions) > 1: session_number = 1 + sessions.index(session) - recordings = session.get_material("recording", only_one=False) # Document queryset - presentations = session.presentations.filter(document__in=recordings) # SessionPresentation queryset + presentations = session.presentations.filter( + document__in=session.get_material("recording", only_one=False), + ).order_by("document__title", "document__external_url") if request.method == 'POST': pk_to_delete = request.POST.get('delete', None) From 25a8eeb6b55fe9bfa9139c31930041d8358100d1 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 14 Jan 2025 12:11:28 -0400 Subject: [PATCH 15/19] chore: drop rev, hide table when empty --- .../meeting/add_session_recordings.html | 40 +++++++------------ 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/ietf/templates/meeting/add_session_recordings.html b/ietf/templates/meeting/add_session_recordings.html index 13d2647fc7..c701b8e478 100644 --- a/ietf/templates/meeting/add_session_recordings.html +++ b/ietf/templates/meeting/add_session_recordings.html @@ -19,33 +19,21 @@

The deadline for submission corrections has passed. This may affect published proceedings. {% endif %} -

Recordings already linked to this session

- - {% csrf_token %} - - - - - - - - - - - {% if already_linked|length == 0 %} + {% if already_linked|length > 0 %} +

Recordings already linked to this session

+ + {% csrf_token %} +
RevisionTitleURLDelete
+ - + + + - {% else %} + + {% for sp in already_linked %}{% with recording_doc=sp.document %} - {% endwith %}{% endfor %} - {% endif %} -
(none)TitleURLDelete
- {% if sp.rev %} - {{ sp.rev }} - {% else %} - (current) - {% endif %} - {{ recording_doc.title }} {{ recording_doc.external_url }} @@ -60,9 +48,9 @@

Recordings already linked to this session

- + + + {% endif %}

Really delete the link to (default)?

From 761eb3641280e72fc320fc7866f9fe6cd1b96f16 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 14 Jan 2025 12:48:15 -0400 Subject: [PATCH 16/19] test: test delete_recordings method --- ietf/meeting/tests_views.py | 44 ++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index 0647da52ab..6e9ea8c9a0 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -48,7 +48,7 @@ from ietf.meeting.test_data import make_meeting_test_data, make_interim_meeting, make_interim_test_data from ietf.meeting.utils import condition_slide_order from ietf.meeting.utils import add_event_info_to_session_qs, participants_for_meeting -from ietf.meeting.utils import create_recording, get_next_sequence, bluesheet_data +from ietf.meeting.utils import create_recording, delete_recording, get_next_sequence, bluesheet_data from ietf.meeting.views import session_draft_list, parse_agenda_filter_params, sessions_post_save, agenda_extract_schedule from ietf.meeting.views import get_summary_by_area, get_summary_by_type, get_summary_by_purpose, generate_agenda_data from ietf.name.models import SessionStatusName, ImportantDateName, RoleName, ProceedingsMaterialTypeName @@ -441,6 +441,48 @@ def test_session_recordings_via_factories(self): self.assertIn(new_recording_title, links[0].text_content()) #debug.show("q(f'#notes_and_recordings_{session_pk}')") + def test_delete_recordings(self): + # No user specified, active recording state + sp = SessionPresentationFactory( + document__type_id="recording", + document__external_url="https://example.com/some-recording", + document__states=[("recording", "active")], + ) + doc = sp.document + doc.docevent_set.all().delete() # clear this out + delete_recording(sp) + self.assertFalse(SessionPresentation.objects.filter(pk=sp.pk).exists()) + self.assertEqual(doc.get_state("recording").slug, "deleted", "recording state updated") + self.assertEqual(doc.docevent_set.count(), 1, "one event added") + event = doc.docevent_set.first() + self.assertEqual(event.type, "changed_state", "event is a changed_state event") + self.assertEqual(event.by.name, "(System)", "system user is responsible") + + # Specified user, no recording state + sp = SessionPresentationFactory( + document__type_id="recording", + document__external_url="https://example.com/some-recording", + document__states=[], + ) + doc = sp.document + doc.docevent_set.all().delete() # clear this out + user = PersonFactory() # naming matches the methods - user is a Person, not a User + delete_recording(sp, user=user) + self.assertFalse(SessionPresentation.objects.filter(pk=sp.pk).exists()) + self.assertEqual(doc.get_state("recording").slug, "deleted", "recording state updated") + self.assertEqual(doc.docevent_set.count(), 1, "one event added") + event = doc.docevent_set.first() + self.assertEqual(event.type, "changed_state", "event is a changed_state event") + self.assertEqual(event.by, user, "user is responsible") + + # Document is not a recording + sp = SessionPresentationFactory( + document__type_id="draft", + document__external_url="https://example.com/some-recording", + ) + with self.assertRaises(ValueError): + delete_recording(sp) + def test_agenda_ical_next_meeting_type(self): # start with no upcoming IETF meetings, just an interim MeetingFactory( From 9b293b1032e83540a086f4782e120bd786bdb016 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 14 Jan 2025 12:48:37 -0400 Subject: [PATCH 17/19] fix: debug delete_recordings --- ietf/meeting/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/meeting/utils.py b/ietf/meeting/utils.py index adc19aa0c1..7594ddc693 100644 --- a/ietf/meeting/utils.py +++ b/ietf/meeting/utils.py @@ -877,7 +877,7 @@ def delete_recording(session_presentation, user=None): raise ValueError(f"Document {document.pk} is not a recording (type_id={document.type_id})") recording_state = document.get_state("recording") deleted_state = State.objects.get(type_id="recording", slug="deleted") - if recording_state.slug is None or recording_state != deleted_state: + if recording_state != deleted_state: # Update the recording state and create a history event document.set_state(deleted_state) StateDocEvent.objects.create( From 67536d6426aaea4d8f3ca8b3c750e77900155fa3 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 14 Jan 2025 16:02:14 -0400 Subject: [PATCH 18/19] test: test add_session_recordings view --- ietf/meeting/tests_views.py | 113 ++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/ietf/meeting/tests_views.py b/ietf/meeting/tests_views.py index 6e9ea8c9a0..e81f29ebb4 100644 --- a/ietf/meeting/tests_views.py +++ b/ietf/meeting/tests_views.py @@ -52,6 +52,7 @@ from ietf.meeting.views import session_draft_list, parse_agenda_filter_params, sessions_post_save, agenda_extract_schedule from ietf.meeting.views import get_summary_by_area, get_summary_by_type, get_summary_by_purpose, generate_agenda_data from ietf.name.models import SessionStatusName, ImportantDateName, RoleName, ProceedingsMaterialTypeName +from ietf.settings import YOUTUBE_BASE_URL from ietf.utils.decorators import skip_coverage from ietf.utils.mail import outbox, empty_outbox, get_payload_text from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent @@ -7406,6 +7407,118 @@ def test_request_minutes(self): self.assertEqual(r.status_code,302) self.assertEqual(len(outbox),1) + @override_settings(YOUTUBE_DOMAINS=["youtube.com"]) + def test_add_session_recordings(self): + session = SessionFactory(meeting__type_id="ietf") + url = urlreverse( + "ietf.meeting.views.add_session_recordings", + kwargs={"session_id": session.pk, "num": session.meeting.number}, + ) + # does not fully validate authorization for non-secretariat users :-( + login_testing_unauthorized(self, "secretary", url) + r = self.client.get(url) + pq = PyQuery(r.content) + title_input = pq("input#id_title") + self.assertIsNotNone(title_input) + self.assertEqual( + title_input.attr.value, + "Video recording for {acro} on {timestamp}".format( + acro=session.group.acronym, + timestamp=session.official_timeslotassignment().timeslot.utc_start_time().strftime( + "%b-%d-%Y at %H:%M:%S" + ), + ), + ) + + with patch("ietf.meeting.views.create_recording") as mock_create: + r = self.client.post( + url, + data={ + "title": "This is my video title", + "url": "", + } + ) + self.assertFalse(mock_create.called) + + with patch("ietf.meeting.views.create_recording") as mock_create: + r = self.client.post( + url, + data={ + "title": "This is my video title", + "url": "https://yubtub.com/this-is-not-a-youtube-video", + } + ) + self.assertFalse(mock_create.called) + + with patch("ietf.meeting.views.create_recording") as mock_create: + r = self.client.post( + url, + data={ + "title": "This is my video title", + "url": "https://youtube.com/finally-a-video", + } + ) + self.assertTrue(mock_create.called) + self.assertEqual( + mock_create.call_args, + call( + session, + "https://youtube.com/finally-a-video", + title="This is my video title", + user=Person.objects.get(user__username="secretary"), + ), + ) + + # CAN delete session presentation for this session + sp = SessionPresentationFactory( + session=session, + document__type_id="recording", + document__external_url="https://example.com/some-video", + ) + with patch("ietf.meeting.views.delete_recording") as mock_delete: + r = self.client.post( + url, + data={ + "delete": str(sp.pk), + } + ) + self.assertEqual(r.status_code, 200) + self.assertTrue(mock_delete.called) + self.assertEqual(mock_delete.call_args, call(sp)) + + # ValueError message from delete_recording does not reach the user + sp = SessionPresentationFactory( + session=session, + document__type_id="recording", + document__external_url="https://example.com/some-video", + ) + with patch("ietf.meeting.views.delete_recording", side_effect=ValueError("oh joy!")) as mock_delete: + r = self.client.post( + url, + data={ + "delete": str(sp.pk), + } + ) + self.assertTrue(mock_delete.called) + self.assertNotContains(r, "oh joy!", status_code=200) + + # CANNOT delete session presentation for a different session + sp_for_other_session = SessionPresentationFactory( + document__type_id="recording", + document__external_url="https://example.com/some-other-video", + ) + with patch("ietf.meeting.views.delete_recording") as mock_delete: + r = self.client.post( + url, + data={ + "delete": str(sp_for_other_session.pk), + } + ) + self.assertEqual(r.status_code, 404) + self.assertFalse(mock_delete.called) + + + class HasMeetingsTests(TestCase): settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['AGENDA_PATH'] From d0bc1aee02a51bccf2bc501335759599871d43d9 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 14 Jan 2025 16:02:31 -0400 Subject: [PATCH 19/19] fix: better permissions handling --- ietf/meeting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/meeting/views.py b/ietf/meeting/views.py index a9f799c385..7f3ad25f7b 100644 --- a/ietf/meeting/views.py +++ b/ietf/meeting/views.py @@ -2585,7 +2585,7 @@ def add_session_recordings(request, session_id, num): # num is redundant, but we're dragging it along an artifact of where we are in the current URL structure session = get_object_or_404(Session,pk=session_id) if not session.can_manage_materials(request.user): - raise Http404 + permission_denied(request, "You don't have permission to manage recordings for this session.") if session.is_material_submission_cutoff() and not has_role(request.user, "Secretariat"): raise Http404