From a1c1a206612a39aae861217bae05327bde1819ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Bonavent?= <56730254+LoicBonavent@users.noreply.github.com> Date: Fri, 8 Mar 2024 11:39:04 +0100 Subject: [PATCH 01/17] [DONE] Correctly counts the number of sub-theme videos in a theme (#1068) * Correctly counts the number of sub-theme videos in a theme * Displays different messages according to channel / theme / sub-theme level * Translations added * Add type hint, refactor some source code and manage theme as serializable (in the new function) so that tests pass --- pod/locale/fr/LC_MESSAGES/django.po | 89 +++++++++++++----------- pod/locale/fr/LC_MESSAGES/djangojs.po | 2 +- pod/locale/nl/LC_MESSAGES/django.po | 41 ++++++----- pod/locale/nl/LC_MESSAGES/djangojs.po | 2 +- pod/video/templates/channel/channel.html | 14 ++-- pod/video/views.py | 44 ++++++++++-- 6 files changed, 121 insertions(+), 71 deletions(-) diff --git a/pod/locale/fr/LC_MESSAGES/django.po b/pod/locale/fr/LC_MESSAGES/django.po index a4e9c88e11..43cc5e54d4 100644 --- a/pod/locale/fr/LC_MESSAGES/django.po +++ b/pod/locale/fr/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-01 13:49+0100\n" +"POT-Creation-Date: 2024-03-06 12:13+0100\n" "PO-Revision-Date: \n" "Last-Translator: obado \n" "Language-Team: Pod Team cotech-esup-pod@esup-portail.org\n" @@ -2579,8 +2579,8 @@ msgid "" "a>." msgstr "" "L’accès à l’ajout d’enregistrements externes a été limité. Si vous souhaitez " -"ajouter des enregistrements externes sur la plateforme, veuillez nous contacter." +"ajouter des enregistrements externes sur la plateforme, veuillez nous contacter." #: pod/import_video/templates/import_video/add_or_edit.html msgid "" @@ -2970,8 +2970,8 @@ msgstr "" #: pod/import_video/views.py #, python-format msgid "" -"This video “%(name)s” was uploaded to Pod; its origin is %(type)s: %(url)s

%(desc)s" +"This video “%(name)s” was uploaded to Pod; its origin is %(type)s: %(url)s

%(desc)s" msgstr "" "Cette vidéo « %(name)s » a été téléversée sur Pod ; son origine est " "%(type)s : %(url)s

%(desc)s" @@ -2979,8 +2979,8 @@ msgstr "" #: pod/import_video/views.py #, python-format msgid "" -"This video “%(name)s” was uploaded to Pod; its origin is Youtube: %(url)s" +"This video “%(name)s” was uploaded to Pod; its origin is Youtube: %(url)s" msgstr "" "Cette vidéo « %(name)s » a été téléversée sur Pod ; son origine est " "Youtube : %(url)s" @@ -4822,17 +4822,6 @@ msgstr "Onglet additionnel de chaînes" msgid "Additional channel Tabs" msgstr "Onglets additionnels de chaînes" -#: pod/main/settings.py -msgid "" -"Pod is aimed at users of our institutions, by allowing the publication of " -"videos in the fields of research (promotion of platforms, etc.), training " -"(tutorials, distance training, student reports, etc.), institutional life " -"(video of events), offering several days of content." -msgstr "" -"Pod a pour but de faciliter la mise à disposition de vidéos et de ce fait, " -"d’encourager l’utilisation de celles-ci dans le cadre de l’enseignement et " -"la recherche." - #: pod/main/templates/403.html msgid "Permission denied" msgstr "Accès refusé" @@ -5956,16 +5945,16 @@ msgid "" msgstr "" "\n" "

Bonjour,\n" -"

%(owner)s vous invite à une réunion récurrente " -"%(meeting_title)s.

\n" +"

%(owner)s vous invite à une réunion récurrente " +"%(meeting_title)s.

\n" "

Date de début : %(start_date_time)s

\n" "

Récurrent jusqu’à la date : %(end_date)s

\n" "

La réunion se tiendra tou(te)s les %(frequency)s %(recurrence)s \n" "

Voici le lien pour rejoindre la réunion :\n" " %(join_link)s

\n" -"

Vous avez besoin de ce mot de passe pour entrer : " -"%(password)s

\n" +"

Vous avez besoin de ce mot de passe pour entrer : " +"%(password)s

\n" "

Cordialement

\n" " " @@ -5991,8 +5980,8 @@ msgstr "" "

Date de fin : %(end_date)s

\n" "

Voici le lien pour rejoindre la réunion :\n" " %(join_link)s

\n" -"

Vous avez besoin de ce mot de passe pour entrer : " -"%(password)s

\n" +"

Vous avez besoin de ce mot de passe pour entrer : " +"%(password)s

\n" "

Cordialement

\n" " " @@ -7045,8 +7034,8 @@ msgstr "Prévisualisation d’enregistrement" #: pod/video/templates/videos/video-element.html msgid "" "To view this video please enable JavaScript, and consider upgrading to a web " -"browser that supports HTML5 video" +"browser that supports HTML5 video" msgstr "" "Pour visionner cette vidéo, veuillez activer JavaScript et envisager de " "passer à un navigateur Web qui Bonjour,
un nouvel enregistrement a été ajouté sur la plateforme " "%(title_site)s à partir de l’enregistreur « %(recorder)s ».
Pour " -"l’ajouter, cliquez sur le lien ci-dessous.

%(link_url)s
Si le lien n’est pas actif, il " -"faut le copier-coller dans la barre d’adresse de votre navigateur.

Cordialement.

" +"l’ajouter, cliquez sur le lien ci-dessous.

" +"%(link_url)s
Si le lien n’est pas actif, il faut le copier-coller " +"dans la barre d’adresse de votre navigateur.

Cordialement.

" #: pod/recorder/views.py msgid "New recording added." @@ -7475,8 +7463,8 @@ msgid "" "%(url)s

\n" msgstr "" "vous pouvez changer la date de suppression en éditant votre vidéo :

\n" -"

%(scheme)s:%(url)s

\n" +"

" +"%(scheme)s:%(url)s

\n" "\n" #: pod/video/management/commands/check_obsolete_videos.py @@ -7896,12 +7884,16 @@ msgstr[0] "%(counter)s thème trouvé dans cette chaine" msgstr[1] "%(counter)s thèmes trouvés dans cette chaine" #: pod/video/templates/channel/channel.html -#: pod/video/templates/videos/dashboard.html -#, python-format -msgid "%(counter)s video" -msgid_plural "%(counter)s videos" -msgstr[0] "%(counter)s vidéo" -msgstr[1] "%(counter)s vidéos" +msgid "%(counter)s video for this channel" +msgid_plural "%(counter)s videos for this channel" +msgstr[0] "%(counter)s vidéo trouvée dans cette chaine" +msgstr[1] "%(counter)s vidéos trouvées dans cette chaine" + +#: pod/video/templates/channel/channel.html +msgid "%(counter)s subtheme found for this theme" +msgid_plural "%(counter)s subthemes found for this theme" +msgstr[0] "%(counter)s sous-thème trouvé dans ce thème" +msgstr[1] "%(counter)s sous-thèmes trouvés dans ce thème" # python-format #: pod/video/templates/channel/channel.html @@ -8196,6 +8188,13 @@ msgstr "Nouvelle valeur pour le champ %(field_label)s" msgid "No video found" msgstr "Aucune vidéo trouvée." +#: pod/video/templates/videos/dashboard.html +#, python-format +msgid "%(counter)s video" +msgid_plural "%(counter)s videos" +msgstr[0] "%(counter)s vidéo" +msgstr[1] "%(counter)s vidéos" + #: pod/video/templates/videos/dashboard.html msgid "Clear selection" msgstr "Effacer la sélection" @@ -8320,8 +8319,8 @@ msgid "" "This video is chaptered. Click the chapter button on the video player to view them." msgstr "" -"Cette vidéo est chapitrée. Cliquez sur le bouton de chapitre sur le lecteur vidéo pour les voir." +"Cette vidéo est chapitrée. Cliquez sur le bouton de chapitre sur le lecteur vidéo pour les voir." #: pod/video/templates/videos/video-all-info.html msgid "Other versions" @@ -9278,6 +9277,16 @@ msgstr "Résultats de la recherche" msgid "Esup-Pod xAPI" msgstr "xAPI Esup-Pod" +#~ msgid "" +#~ "Pod is aimed at users of our institutions, by allowing the publication of " +#~ "videos in the fields of research (promotion of platforms, etc.), training " +#~ "(tutorials, distance training, student reports, etc.), institutional life " +#~ "(video of events), offering several days of content." +#~ msgstr "" +#~ "Pod a pour but de faciliter la mise à disposition de vidéos et de ce " +#~ "fait, d’encourager l’utilisation de celles-ci dans le cadre de " +#~ "l’enseignement et la recherche." + #~ msgid "The HTML file for this recording was not found on the server." #~ msgstr "" #~ "Le fichier HTML de cet enregistrement est introuvable sur le serveur." diff --git a/pod/locale/fr/LC_MESSAGES/djangojs.po b/pod/locale/fr/LC_MESSAGES/djangojs.po index 11a9ebb8a1..3eea069894 100644 --- a/pod/locale/fr/LC_MESSAGES/djangojs.po +++ b/pod/locale/fr/LC_MESSAGES/djangojs.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: Esup-Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-01 13:49+0100\n" +"POT-Creation-Date: 2024-03-06 12:13+0100\n" "PO-Revision-Date: \n" "Last-Translator: obado \n" "Language-Team: \n" diff --git a/pod/locale/nl/LC_MESSAGES/django.po b/pod/locale/nl/LC_MESSAGES/django.po index a2d63bde3c..6b75e5fb3c 100644 --- a/pod/locale/nl/LC_MESSAGES/django.po +++ b/pod/locale/nl/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-01 13:49+0100\n" +"POT-Creation-Date: 2024-03-06 12:13+0100\n" "PO-Revision-Date: 2023-06-08 14:37+0200\n" "Last-Translator: obado \n" "Language-Team: \n" @@ -2787,15 +2787,15 @@ msgstr "" #: pod/import_video/views.py #, python-format msgid "" -"This video “%(name)s” was uploaded to Pod; its origin is %(type)s: %(url)s

%(desc)s" +"This video “%(name)s” was uploaded to Pod; its origin is %(type)s: %(url)s

%(desc)s" msgstr "" #: pod/import_video/views.py #, python-format msgid "" -"This video “%(name)s” was uploaded to Pod; its origin is Youtube: %(url)s" +"This video “%(name)s” was uploaded to Pod; its origin is Youtube: %(url)s" msgstr "" #: pod/import_video/views.py @@ -4563,14 +4563,6 @@ msgstr "" msgid "Additional channel Tabs" msgstr "" -#: pod/main/settings.py -msgid "" -"Pod is aimed at users of our institutions, by allowing the publication of " -"videos in the fields of research (promotion of platforms, etc.), training " -"(tutorials, distance training, student reports, etc.), institutional life " -"(video of events), offering several days of content." -msgstr "" - #: pod/main/templates/403.html msgid "Permission denied" msgstr "" @@ -6637,8 +6629,8 @@ msgstr "" #: pod/video/templates/videos/video-element.html msgid "" "To view this video please enable JavaScript, and consider upgrading to a web " -"browser that supports HTML5 video" +"browser that supports HTML5 video" msgstr "" #: pod/recorder/templates/recorder/link_record.html @@ -7394,10 +7386,16 @@ msgstr[0] "" msgstr[1] "" #: pod/video/templates/channel/channel.html -#: pod/video/templates/videos/dashboard.html #, python-format -msgid "%(counter)s video" -msgid_plural "%(counter)s videos" +msgid "%(counter)s video for this channel" +msgid_plural "%(counter)s videos for this channel" +msgstr[0] "" +msgstr[1] "" + +#: pod/video/templates/channel/channel.html +#, python-format +msgid "%(counter)s subtheme found for this theme" +msgid_plural "%(counter)s subthemes found for this theme" msgstr[0] "" msgstr[1] "" @@ -7676,6 +7674,13 @@ msgstr "" msgid "No video found" msgstr "" +#: pod/video/templates/videos/dashboard.html +#, python-format +msgid "%(counter)s video" +msgid_plural "%(counter)s videos" +msgstr[0] "" +msgstr[1] "" + #: pod/video/templates/videos/dashboard.html msgid "Clear selection" msgstr "" diff --git a/pod/locale/nl/LC_MESSAGES/djangojs.po b/pod/locale/nl/LC_MESSAGES/djangojs.po index af89d1a839..f9778905ef 100644 --- a/pod/locale/nl/LC_MESSAGES/djangojs.po +++ b/pod/locale/nl/LC_MESSAGES/djangojs.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: Esup-Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-01 13:49+0100\n" +"POT-Creation-Date: 2024-03-06 12:13+0100\n" "PO-Revision-Date: 2023-02-08 15:22+0100\n" "Last-Translator: obado \n" "Language-Team: \n" diff --git a/pod/video/templates/channel/channel.html b/pod/video/templates/channel/channel.html index bc15471e49..6621ffecbe 100644 --- a/pod/video/templates/channel/channel.html +++ b/pod/video/templates/channel/channel.html @@ -110,11 +110,17 @@

{{title}}

- {% if count_themes > 0 %} - {% blocktrans count counter=count_themes %}{{ counter }} theme found for this channel{% plural %}{{ counter }} themes found for this channel{% endblocktrans %} - - + {% if not parent_title %} + {% if count_themes > 0 %} + {% blocktrans count counter=count_themes %}{{ counter }} theme found for this channel{% plural %}{{ counter }} themes found for this channel{% endblocktrans %} + - + {% endif %} + {% blocktrans count counter=channel.video_count %}{{counter}} video for this channel{% plural %}{{counter}} videos for this channel{% endblocktrans %} + {% else %} + {% if count_themes > 0 %} + {% blocktrans count counter=count_themes %}{{ counter }} subtheme found for this theme{% plural %}{{ counter }} subthemes found for this theme{% endblocktrans %} + {% endif %} {% endif %} - {% blocktrans count counter=channel.video_count %}{{counter}} video{% plural %}{{counter}} videos{% endblocktrans %}

{% if theme_children|length %}
diff --git a/pod/video/views.py b/pod/video/views.py index 0748b1721b..3b404d3499 100644 --- a/pod/video/views.py +++ b/pod/video/views.py @@ -85,8 +85,9 @@ from chunked_upload.models import ChunkedUpload from chunked_upload.views import ChunkedUploadView, ChunkedUploadCompleteView -from django.db import transaction from django.db import IntegrityError +from django.db.models import QuerySet +from django.db import transaction RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY = getattr( settings, "RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY", False @@ -190,6 +191,36 @@ # ############################################################################ +def get_theme_children_as_list(channel: Channel, theme_children: QuerySet) -> list: + """Get theme children as a list, and not a Queryset. + + Args: + channel (Channel): current channel + theme_children (QuerySet): QuerySet of children in the theme + Returns: + list: list of children in the theme, with the right number of videos + """ + # List of children in the theme + children = list() + for child in theme_children: + if child is not None: + # Get a flat list of all theme children. + list_theme = child.get_all_children_flat() + # Videos for each child theme + videos_list = get_available_videos().filter( + channel=channel, + theme__in=list_theme + ) + child.video_count = videos_list.count() + child_serializable = { + "slug": child.slug, + "title": child.title, + "video_count": child.video_count + } + children.append(child_serializable) + return children + + def _regroup_videos_by_theme(request, videos, channel, theme=None): """Regroup videos by theme. @@ -234,12 +265,12 @@ def _regroup_videos_by_theme(request, videos, channel, theme=None): if theme_children is not None: count_themes = theme_children.count() has_more_themes = (offset + limit) < count_themes + # Default value for each child theme theme_children = theme_children.annotate( - video_count=Count("video", filter=Q(video__is_draft=False), distinct=True) + video_count=Value(0) ) - theme_children = theme_children.values("slug", "title", "video_count")[ - offset : limit + offset - ] + # List of children in the theme + children = get_theme_children_as_list(channel, theme_children) next_url, previous_url, theme_pages_info = pagination_data( request.path, offset, limit, count_themes ) @@ -249,7 +280,7 @@ def _regroup_videos_by_theme(request, videos, channel, theme=None): "previous": previous_url, "has_more_themes": has_more_themes, "count_themes": count_themes, - "theme_children": list(theme_children), + "theme_children": children, "pages_info": theme_pages_info, } title = channel.title if theme is None else theme.title @@ -302,7 +333,6 @@ def paginator(videos_list, page): def channel(request, slug_c, slug_t=None): channel = get_object_or_404(Channel, slug=slug_c, site=get_current_site(request)) - videos_list = get_available_videos().filter(channel=channel) channel.video_count = videos_list.count() From 66132f27b75af19ab80e1f088af4235c2508bd21 Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 8 Mar 2024 10:39:40 +0000 Subject: [PATCH 02/17] Fixup. Format code with Black --- pod/video/views.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pod/video/views.py b/pod/video/views.py index 3b404d3499..1bd6450829 100644 --- a/pod/video/views.py +++ b/pod/video/views.py @@ -208,14 +208,13 @@ def get_theme_children_as_list(channel: Channel, theme_children: QuerySet) -> li list_theme = child.get_all_children_flat() # Videos for each child theme videos_list = get_available_videos().filter( - channel=channel, - theme__in=list_theme + channel=channel, theme__in=list_theme ) child.video_count = videos_list.count() child_serializable = { "slug": child.slug, "title": child.title, - "video_count": child.video_count + "video_count": child.video_count, } children.append(child_serializable) return children @@ -266,9 +265,7 @@ def _regroup_videos_by_theme(request, videos, channel, theme=None): count_themes = theme_children.count() has_more_themes = (offset + limit) < count_themes # Default value for each child theme - theme_children = theme_children.annotate( - video_count=Value(0) - ) + theme_children = theme_children.annotate(video_count=Value(0)) # List of children in the theme children = get_theme_children_as_list(channel, theme_children) next_url, previous_url, theme_pages_info = pagination_data( From d1f21b9dbc8689b9109eb2df6a98e998e1cc034c Mon Sep 17 00:00:00 2001 From: MattBild <34771705+mattbild@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:58:52 +0100 Subject: [PATCH 03/17] [DONE] Live event restriction (#1066) * Fixup. Format code with Prettier * fix selector + display access_group field * remove access_group if not restricted --------- Co-authored-by: github-actions --- pod/live/forms.py | 3 +++ pod/live/static/js/broadcaster_from_building.js | 11 +++++++---- pod/live/templates/live/event_edit.html | 17 +++++++++-------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/pod/live/forms.py b/pod/live/forms.py index 81245facf7..b580737a51 100644 --- a/pod/live/forms.py +++ b/pod/live/forms.py @@ -410,6 +410,9 @@ def clean(self): cleaned_data["is_restricted"] = False cleaned_data["restrict_access_to_groups"] = [] + if not cleaned_data.get("is_restricted", True): + cleaned_data["restrict_access_to_groups"] = [] + class Meta(object): model = Event fields = [ diff --git a/pod/live/static/js/broadcaster_from_building.js b/pod/live/static/js/broadcaster_from_building.js index 39faf31537..966c0ccf63 100644 --- a/pod/live/static/js/broadcaster_from_building.js +++ b/pod/live/static/js/broadcaster_from_building.js @@ -3,19 +3,22 @@ document.addEventListener("DOMContentLoaded", function () { let restrictedCheckBox = document.getElementById("event_is_restricted"); let restrictedHelp = document.getElementById("event_is_restrictedHelp"); let restrictedLabel = document.getElementsByClassName( - "field_is_restricted", + "field-is_restricted", )[0]; let change_restriction = (restrict) => { if (restrict === true) { - restrictedCheckBox.checked = true; - restrictedCheckBox.setAttribute("onclick", "return false"); + if (!restrictedCheckBox.checked) { + restrictedCheckBox.click(); + restrictedCheckBox.checked = true; + } + restrictedCheckBox.setAttribute("disabled", "disabled"); restrictedHelp.innerHTML = gettext( "Restricted because the broadcaster is restricted", ); restrictedLabel.style.opacity = "0.5"; } else { - restrictedCheckBox.removeAttribute("onclick"); + restrictedCheckBox.removeAttribute("disabled"); restrictedHelp.innerHTML = gettext( "If this box is checked, the event will only be accessible to authenticated users.", ); diff --git a/pod/live/templates/live/event_edit.html b/pod/live/templates/live/event_edit.html index 9c0a0c69db..c553c1e4f9 100644 --- a/pod/live/templates/live/event_edit.html +++ b/pod/live/templates/live/event_edit.html @@ -160,12 +160,6 @@

{% trans "Event planning" %}

{% endblock more_script %} diff --git a/pod/meeting/templates/meeting/internal_recordings.html b/pod/meeting/templates/meeting/internal_recordings.html index cbcee3be1f..b139c90f55 100644 --- a/pod/meeting/templates/meeting/internal_recordings.html +++ b/pod/meeting/templates/meeting/internal_recordings.html @@ -21,9 +21,11 @@

{% trans "After recording a Big Blue Button meeting, recordings of that meeting will be available at that level after a while." %}
-

- {% trans "When a new recording is available at this level, you will receive an email to inform you." %} -

+ {% if meeting.owner == request.user %} +

+ {% trans "When a new recording is available at this level, you will receive an email to inform you." %} +

+ {% endif %}

diff --git a/pod/meeting/templates/meeting/link_meeting.html b/pod/meeting/templates/meeting/link_meeting.html index 09a1aa08af..9cee56e3e1 100644 --- a/pod/meeting/templates/meeting/link_meeting.html +++ b/pod/meeting/templates/meeting/link_meeting.html @@ -2,9 +2,11 @@ {% spaceless %} {% if meeting.owner == request.user or request.user.is_superuser or request.user in meeting.additional_owners.all or perms.meeting.change_meeting %} - - - + {% if not meeting.is_personal or meeting.owner == request.user %} + + + + {% endif %} - + {% if not meeting.is_personal %} + + + + {% endif %} {% endif %} {%endspaceless%} diff --git a/pod/meeting/templates/meeting/meeting_card.html b/pod/meeting/templates/meeting/meeting_card.html index 4dc5b0774e..9093e84904 100644 --- a/pod/meeting/templates/meeting/meeting_card.html +++ b/pod/meeting/templates/meeting/meeting_card.html @@ -1,18 +1,35 @@ {% load i18n l10n %} {% spaceless %} -
+
-
+
{{meeting.name|capfirst|truncatechars:43}}
- {% if meeting.recurrence %} - {{meeting.next_occurrence_from_today}} {% localize on %}{{ meeting.start_at|time }}{% endlocalize %} + {% if meeting.is_personal %} + {% if meeting.owner != request.user %} + {{ meeting.owner.get_full_name }} + {% endif %} {% else %} - {% localize on %}{{ meeting.start_at }}{% endlocalize %} + {% if meeting.recurrence %} + {{meeting.next_occurrence_from_today}} {% localize on %}{{ meeting.start_at|time }}{% endlocalize %} + {% else %} + {% localize on %}{{ meeting.start_at }}{% endlocalize %} + {% endif %} {% endif %}
+ {% if meeting.is_personal %} + {% if meeting.owner == request.user %} + + + + {% else %} + + + + {% endif%} + {% endif %} {% if meeting.is_restricted %} diff --git a/pod/meeting/tests/test_views.py b/pod/meeting/tests/test_views.py index 8d3ea547cd..d8afbbe88b 100644 --- a/pod/meeting/tests/test_views.py +++ b/pod/meeting/tests/test_views.py @@ -36,6 +36,7 @@ def setUp(self): def test_meeting_TestView_get_request(self): self.client = Client() + # A personal meeting room is created at this moment url = reverse("meeting:my_meetings", kwargs={}) response = self.client.get(url) self.assertEqual(response.status_code, 302) @@ -46,7 +47,9 @@ def test_meeting_TestView_get_request(self): list_id = [meeting.id for meeting in response.context["meetings"]] self.assertEqual( list_id, - list(self.user.owner_meeting.all().values_list("id", flat=True)), + list(self.user.owner_meeting.all().values_list( + "id", flat=True + ).order_by("-is_personal", "-start_at")), ) print(" ---> test_meeting_TestView_get_request of meeting_TestView: OK!") @@ -68,7 +71,9 @@ def test_meeting_TestView_get_request_restrict(self): list_id = [meeting.id for meeting in response.context["meetings"]] self.assertEqual( list_id, - list(self.user.owner_meeting.all().values_list("id", flat=True)), + list(self.user.owner_meeting.all().values_list( + "id", flat=True + ).order_by("-is_personal", "-start_at")), ) print( " ---> test_meeting_TestView_get_request_restrict ", @@ -154,7 +159,8 @@ def test_meeting_add_post_request(self): # check if meeting has been updated m = Meeting.objects.get(name="test1") self.assertEqual(m.attendee_password, "1234") - self.assertEqual(Meeting.objects.all().count(), nb_meeting + 1) + # Also includes personal meeting room + self.assertEqual(Meeting.objects.all().count(), nb_meeting + 2) self.assertEqual(m.start_at, timezone.make_aware(datetime(2022, 8, 26, 21, 0, 0))) print(" ---> test_meeting_add_post_request of MeetingEditTestView: OK!") @@ -272,7 +278,8 @@ def test_meeting_delete_post_request(self): response = self.client.post(url, {"agree": True}, follow=True) self.assertTrue(b"The meeting has been deleted." in response.content) # check if meeting has been deleted - self.assertEqual(Meeting.objects.all().count(), 0) + # Also includes personal meeting room + self.assertEqual(Meeting.objects.all().count(), 1) print(" ---> test_meeting_delete_post_request of MeetingDeleteTestView: OK!") diff --git a/pod/meeting/views.py b/pod/meeting/views.py index 9809653dec..40e9f5045b 100644 --- a/pod/meeting/views.py +++ b/pod/meeting/views.py @@ -8,7 +8,7 @@ import traceback from .forms import MeetingForm, MeetingDeleteForm, MeetingPasswordForm -from .forms import MeetingInviteForm +from .forms import MeetingInviteForm, get_random_string from .models import Meeting, InternalRecording from .utils import get_nth_week_number, send_email_recording_ready from datetime import datetime @@ -104,16 +104,24 @@ def my_meetings(request): site = get_current_site(request) if RESTRICT_EDIT_MEETING_ACCESS_TO_STAFF_ONLY and request.user.is_staff is False: return render(request, "meeting/my_meetings.html", {"access_not_allowed": True}) + # Manage personal meeting room + manage_personal_meeting_room(request) if request.GET and request.GET.get("all") == "true": meetings = request.user.owner_meeting.all().filter( site=site ) | request.user.owners_meetings.all().filter(site=site) meetings = meetings.distinct() + meetings = meetings.order_by("-is_personal", "-start_at") else: # remove past meeting meetings = [ meeting - for meeting in (request.user.owner_meeting.all().filter(site=site)) + for meeting in ( + request.user.owner_meeting.all().filter( + site=site + ) | request.user.owners_meetings.all().filter(site=site) + .order_by("-is_personal", "-start_at") + ) if meeting.is_active ] return render( @@ -128,6 +136,33 @@ def my_meetings(request): ) +def manage_personal_meeting_room(request): + """Create, if necessary, the personal meeting room for this user. + + Args: + request (Request): HTTP request + """ + site = get_current_site(request) + personal_meeting_room = Meeting.objects.filter( + owner=request.user, + site=site, + is_personal=True + ).first() + + if not personal_meeting_room: + # Create 1st time the personal meeting room + Meeting.objects.create( + name=_("Personal meeting room"), + owner=request.user, + site=site, + attendee_password=get_random_string(8), + moderator_password=get_random_string(8), + start_at=datetime.now().replace(minute=0, second=0, microsecond=0), + recurrence=None, + is_personal=True + ) + + @csrf_protect @ensure_csrf_cookie @login_required(redirect_field_name="referrer") @@ -156,6 +191,12 @@ def add_or_edit(request, meeting_id=None): ) raise PermissionDenied + if meeting and meeting.is_personal and request.user != meeting.owner: + display_message_with_icon( + request, messages.ERROR, _("You cannot edit this meeting.") + ) + raise PermissionDenied + if RESTRICT_EDIT_MEETING_ACCESS_TO_STAFF_ONLY and request.user.is_staff is False: return render(request, "meeting/add_or_edit.html", {"access_not_allowed": True}) @@ -241,6 +282,12 @@ def delete(request, meeting_id): ) raise PermissionDenied + if meeting.is_personal: + display_message_with_icon( + request, messages.ERROR, _("You cannot delete a personal meeting room.") + ) + raise PermissionDenied + form = MeetingDeleteForm() if request.method == "POST": @@ -680,6 +727,7 @@ def secure_internal_recordings(request, meeting): and not ( request.user.is_superuser or request.user.has_perm("meeting.view_meeting") ) + and (request.user not in meeting.additional_owners.all()) ): display_message_with_icon( request, messages.ERROR, _("You cannot view the recordings of this meeting.") @@ -789,8 +837,15 @@ def invite(request, meeting_id): Meeting, meeting_id=meeting_id, site=get_current_site(request) ) - if request.user != meeting.owner and not ( - request.user.is_superuser or request.user.has_perm("meeting.delete_meeting") + if ( + request.user != meeting.owner + and ( + not ( + request.user.is_superuser + or request.user.has_perm("meeting.delete_meeting") + ) + ) + and (request.user not in meeting.additional_owners.all()) ): display_message_with_icon( request, messages.ERROR, _("You cannot invite for this meeting.") @@ -886,30 +941,54 @@ def get_html_content(request, meeting): } ) else: - html_content = ( - _( - """ -

Hello,

-

%(owner)s invites you to the meeting %(meeting_title)s.

-

Start date: %(start_date_time)s

-

End date: %(end_date)s

-

here the link to join the meeting: - %(join_link)s

-

You need this password to enter: %(password)s

-

Regards

- """ + if meeting.is_personal: + html_content = ( + _( + """ +

Hello,

+

%(owner)s invites you to the meeting %(meeting_title)s.

+

here the link to join the meeting: + %(join_link)s

+

You need this password to enter: %(password)s

+

Regards

+ """ + ) + % { + "owner": full_name, + "meeting_title": meeting.name, + "start_date_time": meeting_start_datetime, + "end_date": timezone.localtime( + meeting.start_at + meeting.expected_duration + ).strftime("%d/%m/%Y %H:%M"), + "join_link": join_link, + "password": meeting.attendee_password, + } + ) + else: + html_content = ( + _( + """ +

Hello,

+

%(owner)s invites you to the meeting %(meeting_title)s.

+

Start date: %(start_date_time)s

+

End date: %(end_date)s

+

here the link to join the meeting: + %(join_link)s

+

You need this password to enter: %(password)s

+

Regards

+ """ + ) + % { + "owner": full_name, + "meeting_title": meeting.name, + "start_date_time": meeting_start_datetime, + "end_date": timezone.localtime( + meeting.start_at + meeting.expected_duration + ).strftime("%d/%m/%Y %H:%M"), + "join_link": join_link, + "password": meeting.attendee_password, + } ) - % { - "owner": full_name, - "meeting_title": meeting.name, - "start_date_time": meeting_start_datetime, - "end_date": timezone.localtime( - meeting.start_at + meeting.expected_duration - ).strftime("%d/%m/%Y %H:%M"), - "join_link": join_link, - "password": meeting.attendee_password, - } - ) return html_content From 83fc7dc3e62f6151af001d24f2748e07b33795e5 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 14 Mar 2024 10:12:41 +0000 Subject: [PATCH 09/17] Fixup. Format code with Black --- pod/meeting/tests/test_views.py | 16 ++++++++++------ pod/meeting/views.py | 12 +++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/pod/meeting/tests/test_views.py b/pod/meeting/tests/test_views.py index d8afbbe88b..59ba7cc87f 100644 --- a/pod/meeting/tests/test_views.py +++ b/pod/meeting/tests/test_views.py @@ -47,9 +47,11 @@ def test_meeting_TestView_get_request(self): list_id = [meeting.id for meeting in response.context["meetings"]] self.assertEqual( list_id, - list(self.user.owner_meeting.all().values_list( - "id", flat=True - ).order_by("-is_personal", "-start_at")), + list( + self.user.owner_meeting.all() + .values_list("id", flat=True) + .order_by("-is_personal", "-start_at") + ), ) print(" ---> test_meeting_TestView_get_request of meeting_TestView: OK!") @@ -71,9 +73,11 @@ def test_meeting_TestView_get_request_restrict(self): list_id = [meeting.id for meeting in response.context["meetings"]] self.assertEqual( list_id, - list(self.user.owner_meeting.all().values_list( - "id", flat=True - ).order_by("-is_personal", "-start_at")), + list( + self.user.owner_meeting.all() + .values_list("id", flat=True) + .order_by("-is_personal", "-start_at") + ), ) print( " ---> test_meeting_TestView_get_request_restrict ", diff --git a/pod/meeting/views.py b/pod/meeting/views.py index 40e9f5045b..a08f5105f4 100644 --- a/pod/meeting/views.py +++ b/pod/meeting/views.py @@ -117,9 +117,9 @@ def my_meetings(request): meetings = [ meeting for meeting in ( - request.user.owner_meeting.all().filter( - site=site - ) | request.user.owners_meetings.all().filter(site=site) + request.user.owner_meeting.all().filter(site=site) + | request.user.owners_meetings.all() + .filter(site=site) .order_by("-is_personal", "-start_at") ) if meeting.is_active @@ -144,9 +144,7 @@ def manage_personal_meeting_room(request): """ site = get_current_site(request) personal_meeting_room = Meeting.objects.filter( - owner=request.user, - site=site, - is_personal=True + owner=request.user, site=site, is_personal=True ).first() if not personal_meeting_room: @@ -159,7 +157,7 @@ def manage_personal_meeting_room(request): moderator_password=get_random_string(8), start_at=datetime.now().replace(minute=0, second=0, microsecond=0), recurrence=None, - is_personal=True + is_personal=True, ) From f749bd6233cb97071f9fec2985369cadf80e5cd2 Mon Sep 17 00:00:00 2001 From: Pascal Rigaux Date: Thu, 14 Mar 2024 16:58:56 +0100 Subject: [PATCH 10/17] do not fail on HTTP requests with numerous/long cookies (#1076) In case of numerous/long cookies and/or long query string, the HTTP header may exceed default 4k. When it occurs, uwsgi rejects those rejects with error "invalid request block size" and nginx returns HTTP 502. Allowing 8k is a safe value that still allows weird long cookies set on .univ-xxx.fr --- pod_uwsgi.ini | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pod_uwsgi.ini b/pod_uwsgi.ini index 822848cda8..86e6736347 100644 --- a/pod_uwsgi.ini +++ b/pod_uwsgi.ini @@ -20,5 +20,7 @@ socket = /home/pod/django_projects/podv3/podv3.sock chmod-socket = 666 # clear environment on exit vacuum = true - - +# In case of numerous/long cookies and/or long query string, the HTTP header may exceed default 4k. +# When it occurs, uwsgi rejects those rejects with error "invalid request block size" and nginx returns HTTP 502. +# Allowing 8k is a safe value that still allows weird long cookies set on .univ-xxx.fr +buffer-size = 8192 From 05ee0ff9bcb1fc6d7d41d5f7ef3e465cf9db1fca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Mar 2024 08:25:50 +0100 Subject: [PATCH 11/17] [DONE] Bump django from 3.2.24 to 3.2.25 (#1079) Bumps [django](https://github.com/django/django) from 3.2.24 to 3.2.25. - [Commits](https://github.com/django/django/compare/3.2.24...3.2.25) --- updated-dependencies: - dependency-name: django dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: Ptitloup Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8f1f8292c1..fd6445068c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -r requirements-encode.txt -Django==3.2.24 +Django==3.2.25 django-ckeditor==6.3.0 Pillow==10.2.0 django-tagging==0.5.0 From fbc79c5559651109ec1edd59c45ad4107e87fffa Mon Sep 17 00:00:00 2001 From: MattBild <34771705+mattbild@users.noreply.github.com> Date: Tue, 19 Mar 2024 10:50:46 +0100 Subject: [PATCH 12/17] rollback timeout to displya on hold video (#1077) --- pod/live/templates/live/event-script.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pod/live/templates/live/event-script.html b/pod/live/templates/live/event-script.html index 7cc92c1916..cfd9e00a5c 100644 --- a/pod/live/templates/live/event-script.html +++ b/pod/live/templates/live/event-script.html @@ -20,7 +20,6 @@ fluid: !{{ request.GET.is_iframe|yesno:"true,false" }}, playbackRates: [0.5, 1, 1.5, 2], errorDisplay: false, - muted: false, loop: false } @@ -767,10 +766,7 @@ {% endif %} {% if event.is_current %} - // timeout pour éviter que la video d'attente démarre pour moins d'une sec et demie - setTimeout(() => { - showWaitingVideo(true); - }, 1500); + showWaitingVideo(true); {% endif %} reloadIfNeeded(); From b29c3453ed028c4f96a675fd56fe1c71c797e0ea Mon Sep 17 00:00:00 2001 From: MattBild <34771705+mattbild@users.noreply.github.com> Date: Tue, 19 Mar 2024 16:21:22 +0100 Subject: [PATCH 13/17] [DONE] fix live_event save for many_to_many fields (#1080) * fix live_event save for many_to_many fields * use save_m2m() instead --- pod/live/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pod/live/views.py b/pod/live/views.py index b9d9a53b00..683832ad5b 100644 --- a/pod/live/views.py +++ b/pod/live/views.py @@ -588,6 +588,7 @@ def update_event(form): d_fin = timezone.make_aware(d_fin) evt.end_date = d_fin evt.save() + form.save_m2m() return evt From 8064413e5240cd3d08b37d7a2479d3ec4a298874 Mon Sep 17 00:00:00 2001 From: Aymeric Jakobowski <106442526+AymericJak@users.noreply.github.com> Date: Thu, 28 Mar 2024 16:37:34 +0100 Subject: [PATCH 14/17] [DONE] Fix dressing and cutting and add unit tests 3.5.2 (#1078) * Fix watermark in dressings * Fix filewidget error * Fix get_dressing_input * Fix test_dressing_to_json * Add remote encoding test for dressing * change video_test to mp4 format * Modify video type & add credit video * Add remote encoding cut test * Fix flake8 * Readd settings local for docker full test * Add remote test calls * Add str convert * Remove the convertion * Fix flake8 * Add test image & fix debug print * Prevent dressing page access when video is encoding & hide favorite button * remove staff access for restriction but add it for thumbnail * Add USE_DRESSING setting * Fix flake8 * Add USE_CUT setting in context processors file. * Add USE_CUT setting * Update configuration JSON file * Change video password type field to 'password' * Fix #1081 * Fix the time field * remove date today from global variable and replace it in each function * print json dressing encoding to see error * add missing whitespace * Test ls command * Test LS * Test LS * Test LS * Add some debug prints * Add some prints * Add some prints * Test dressing utils * Test dressing utils * Test dressing utils * Test dressing utils * Add debug prints * Add debug prints * Call the setUp function in the remote_encoding_cut [test] * Add condition & prints [test] * Remove some prints & uncomment assertions * Add prints [test] * Remove test_remote_encoding_transcoding [test] * Update tests * Remove unnecessary prints * add decode data for completion base mail * add pydoc and return * Replace style inline by BS class * Remove unnecessary console.log * Correctly indent the template * Put the datetime.date.today() in variable * Reorganize the file * remove console.log * Use today_date * Remove some prints * Rename function * Add test for verify_time * Fix video access tokens. * Update configuration * Update configuration * Remove unnecessary print * Add unit tests for Completion model * Simplify the verify_document method * Add & improve dressing views tests * Add some unit tests for Document model in the Completion App * Fix flake8 * Set the pydoc in english * Add some unit tests for CutVideo model * Add cut tests & fix view * Simplify verify_end_start_time method * Add unit tests for enrichment * Improve test views for playlists * add remote encoding for studio, add task, add rest views and call it from router, split encoding studio * Add USE_DEBUG_TOOLBAR setting * remove unused recording id --------- Co-authored-by: Ptitloup Co-authored-by: SebastienCozeDev --- pod/completion/models.py | 13 +- pod/completion/tests/test_models.py | 57 +++++- pod/cut/context_processors.py | 11 ++ pod/cut/forms.py | 18 +- pod/cut/models.py | 11 +- pod/cut/templates/video_cut.html | 2 +- pod/cut/tests/test_models.py | 47 ++++- pod/cut/tests/test_views.py | 125 ++++++++++--- pod/cut/urls.py | 6 +- pod/cut/views.py | 18 +- pod/dressing/context_processors.py | 12 ++ pod/dressing/models.py | 8 + pod/dressing/templates/video_dressing.html | 6 +- pod/dressing/tests/test_models.py | 8 + pod/dressing/tests/test_views.py | 176 ++++++++++++++++-- pod/dressing/views.py | 5 + pod/enrichment/models.py | 5 +- pod/enrichment/tests/test_models.py | 54 +++++- pod/main/configuration.json | 79 ++++++++ pod/main/rest_router.py | 5 + pod/main/settings.py | 7 + pod/main/static/js/main.js | 11 +- .../video_test_encodage_transcription.mp4 | Bin 0 -> 2091340 bytes pod/main/templates/navbar.html | 6 +- pod/main/test_settings.py | 4 + pod/playlist/tests/test_views.py | 146 +++++++++++++-- pod/podfile/static/podfile/js/filewidget.js | 5 +- pod/recorder/plugins/type_studio.py | 3 +- pod/settings.py | 4 +- pod/urls.py | 44 +++-- pod/video/forms.py | 17 +- pod/video/templates/videos/dashboard.html | 12 +- pod/video/templates/videos/link_video.html | 11 +- .../templates/videos/video_access_tokens.html | 19 +- .../templates/videos/video_row_select.html | 2 +- pod/video/urls.py | 5 - pod/video_encode_transcript/Encoding_video.py | 4 +- pod/video_encode_transcript/encode.py | 91 ++++----- .../encoding_studio.py | 63 ++----- pod/video_encode_transcript/encoding_tasks.py | 36 ++++ pod/video_encode_transcript/rest_views.py | 13 ++ .../tests/test_remote_encode_transcode.py | 162 +++++++++++++++- .../tests/testimage.jpg | Bin 0 -> 16980 bytes 43 files changed, 1084 insertions(+), 247 deletions(-) create mode 100644 pod/cut/context_processors.py create mode 100644 pod/dressing/context_processors.py create mode 100644 pod/main/static/video_test/video_test_encodage_transcription.mp4 create mode 100644 pod/video_encode_transcript/tests/testimage.jpg diff --git a/pod/completion/models.py b/pod/completion/models.py index e1be046df0..089bdb56bd 100644 --- a/pod/completion/models.py +++ b/pod/completion/models.py @@ -137,11 +137,13 @@ def verify_not_same_contributor(self): def __str__(self): return "Video:{0} - Name:{1} - Role:{2}".format(self.video, self.name, self.role) - def get_base_mail(self): + def get_base_mail(self) -> str: + """Encode an email as base64 to prevent robots crawling.""" data = base64.b64encode(self.email_address.encode()) - return data + return data.decode("utf-8") - def get_noscript_mail(self): + def get_noscript_mail(self) -> str: + """Replace @ by __AT__ in email adress to prevent robots crawling.""" return self.email_address.replace("@", "__AT__") @@ -178,10 +180,7 @@ def verify_document(self): msg = list() if not self.document: msg.append(_("Please enter a document.")) - if len(msg) > 0: - return msg - else: - return list() + return msg def verify_not_same_document(self): msg = list() diff --git a/pod/completion/tests/test_models.py b/pod/completion/tests/test_models.py index a4c46b53cf..74e0656da1 100644 --- a/pod/completion/tests/test_models.py +++ b/pod/completion/tests/test_models.py @@ -1,10 +1,13 @@ """Unit tests for completion models.""" +import base64 from django.conf import settings +from django.contrib.sites.models import Site from django.test import TestCase from django.contrib.auth.models import User from django.core.files.uploadedfile import SimpleUploadedFile from django.core.exceptions import ValidationError +from django.utils.translation import ugettext_lazy as _ from pod.video.models import Video from pod.video.models import Type from pod.completion.models import Contributor @@ -30,11 +33,13 @@ class ContributorModelTestCase(TestCase): ] def setUp(self): + self.site = Site.objects.get(id=1) owner = User.objects.create(username="test") - videotype = Type.objects.create(title="others") + video_type = Type.objects.create(title="others") video = Video.objects.create( - title="video", type=videotype, owner=owner, video="test.mp4" + title="video", type=video_type, owner=owner, video="test.mp4", ) + video.sites.add(self.site) Contributor.objects.create( video=video, name="contributor", @@ -98,6 +103,33 @@ def test_delete(self): print(" ---> test_delete: OK! --- ContributorModel") + def test_sites_property(self): + """Test the sites property of the Contributor model.""" + contributor = Contributor.objects.get(id=1) + self.assertEqual(contributor.sites, Video.objects.get(id=1).sites) + print(" ---> test_sites_property: OK! --- ContributorModel") + + def test_str(self): + """Test the sites property of the Contributor model when the video is deleted.""" + contributor = Contributor.objects.get(id=1) + video = Video.objects.get(id=1) + self.assertEqual(str(contributor), f"Video:{video} - Name:{contributor.name} - Role:{contributor.role}") + print(" ---> test_str: OK! --- ContributorModel") + + def test_get_base_mail(self): + """Test the get_base_mail method of the Contributor model.""" + contributor = Contributor.objects.get(id=1) + self.assertEqual( + contributor.get_base_mail(), + base64.b64encode(contributor.email_address.encode()).decode("utf-8") + ) + print(" ---> test_get_base_mail: OK! --- ContributorModel") + + def test_get_noscript_mail(self): + """Test the get_noscript_mail method of the Contributor model.""" + contributor = Contributor.objects.get(id=1) + self.assertEqual(contributor.get_noscript_mail(), "contributor__AT__pod.com") + class DocumentModelTestCase(TestCase): fixtures = [ @@ -155,6 +187,27 @@ def test_delete(self): print(" ---> test_delete: OK! --- DocumentModel") + def test_sites_property(self): + """Test the sites property of the Contributor model.""" + document = Document.objects.get(id=1) + self.assertEqual(document.sites, Video.objects.get(id=1).sites) + print(" ---> test_sites_property: OK! --- DocumentModel") + + def test_str(self): + """Test the __str__ method of the Document model.""" + document = Document.objects.get(id=1) + video = Video.objects.get(id=1) + self.assertEqual(str(document), f"Document: {document.document.name} - Video: {video}") + print(" ---> test_str: OK! --- DocumentModel") + + def test_verify_document(self): + """Test the verify_document method of the Document model.""" + document = Document.objects.get(id=1) + document.document = None + document.save() + self.assertIn(_("Please enter a document."), document.verify_document()) + print(" ---> test_verify_document: OK! --- DocumentModel") + class OverlayModelTestCase(TestCase): fixtures = [ diff --git a/pod/cut/context_processors.py b/pod/cut/context_processors.py new file mode 100644 index 0000000000..a208878e6d --- /dev/null +++ b/pod/cut/context_processors.py @@ -0,0 +1,11 @@ +"""Esup-Pod context processors for cut app.""" +from django.conf import settings as django_settings + +USE_CUT = getattr(django_settings, "USE_CUT", True) + + +def context_settings(request): + """Return all context settings for cut app""" + new_settings = {} + new_settings["USE_CUT"] = USE_CUT + return new_settings diff --git a/pod/cut/forms.py b/pod/cut/forms.py index f778bd0c5c..cd91bb5788 100644 --- a/pod/cut/forms.py +++ b/pod/cut/forms.py @@ -14,6 +14,20 @@ class Meta: model = CutVideo fields = ["video", "start", "end", "duration"] widgets = { - "start": forms.TimeInput(attrs={"type": "time", "step": "1"}), - "end": forms.TimeInput(attrs={"type": "time", "step": "1"}), + "start": forms.TimeInput( + attrs={ + "step": "1", + "required": "required", + "size": "8", + "class": "text-center", + } + ), + "end": forms.TimeInput( + attrs={ + "step": "1", + "required": "required", + "size": "8", + "class": "text-center", + } + ), } diff --git a/pod/cut/models.py b/pod/cut/models.py index 646c22aef3..074dd64c56 100644 --- a/pod/cut/models.py +++ b/pod/cut/models.py @@ -23,16 +23,19 @@ def clean(self): ) def verify_time(self): - """Vérifier si ce sont les mêmes valeurs (pour éviter un réencodage inutile)""" + """Check if they are the same values (to avoid unnecessary re-encoding).""" if CutVideo.objects.filter(video=self.video.id).exists(): previous_cut = CutVideo.objects.get(video=self.video.id) if previous_cut.start == self.start and previous_cut.end == self.end: return False # Convert start and end to seconds - start = time_to_seconds(self.start) - end = time_to_seconds(self.end) - duration = time_to_seconds(self.duration) + try: + start = time_to_seconds(self.start) + end = time_to_seconds(self.end) + duration = time_to_seconds(self.duration) + except ValueError: + return False if start < 0 or start >= end or end is None or start is None or end > duration: return False else: diff --git a/pod/cut/templates/video_cut.html b/pod/cut/templates/video_cut.html index 5b6654a99e..8a1f9246be 100644 --- a/pod/cut/templates/video_cut.html +++ b/pod/cut/templates/video_cut.html @@ -41,7 +41,7 @@
-
+ {% csrf_token %}