diff --git a/src/backend/core/api/__init__.py b/src/backend/core/api/__init__.py index 311edf32..6c60ee29 100644 --- a/src/backend/core/api/__init__.py +++ b/src/backend/core/api/__init__.py @@ -40,6 +40,7 @@ def get_frontend_configuration(request): "recording": { "is_enabled": settings.RECORDING_ENABLE, "available_modes": settings.RECORDING_WORKER_CLASSES.keys(), + "expiration_days": settings.RECORDING_EXPIRATION_DAYS, }, } frontend_configuration.update(settings.FRONTEND_CONFIGURATION) diff --git a/src/backend/core/api/serializers.py b/src/backend/core/api/serializers.py index a32773c9..d07aba90 100644 --- a/src/backend/core/api/serializers.py +++ b/src/backend/core/api/serializers.py @@ -159,7 +159,17 @@ class RecordingSerializer(serializers.ModelSerializer): class Meta: model = models.Recording - fields = ["id", "room", "created_at", "updated_at", "status", "mode", "key"] + fields = [ + "id", + "room", + "created_at", + "updated_at", + "status", + "mode", + "key", + "is_expired", + "expired_at", + ] read_only_fields = fields diff --git a/src/backend/core/models.py b/src/backend/core/models.py index 77ef198f..7db9d83b 100644 --- a/src/backend/core/models.py +++ b/src/backend/core/models.py @@ -3,8 +3,9 @@ Declare and configure the models for the Meet core application """ import uuid +from datetime import datetime, timedelta from logging import getLogger -from typing import List +from typing import List, Optional from django.conf import settings from django.contrib.auth import models as auth_models @@ -12,6 +13,7 @@ from django.contrib.auth.base_user import AbstractBaseUser from django.core import mail, validators from django.core.exceptions import PermissionDenied, ValidationError from django.db import models +from django.utils import timezone from django.utils.functional import lazy from django.utils.text import capfirst, slugify from django.utils.translation import gettext_lazy as _ @@ -601,6 +603,34 @@ class Recording(BaseModel): return f"{settings.RECORDING_OUTPUT_FOLDER}/{self.id}.{self.extension}" + @property + def expired_at(self) -> Optional[datetime]: + """ + Calculate the expiration date based on created_at and RECORDING_EXPIRATION_DAYS. + Returns None if no expiration is configured. + + Note: This is a naive and imperfect implementation since recordings are actually + saved to the bucket after created_at timestamp is set. The actual expiration + will be determined by the bucket lifecycle policy which operates on the object's + timestamp in the storage system, not this value. + """ + + if not settings.RECORDING_EXPIRATION_DAYS: + return None + + return self.created_at + timedelta(days=settings.RECORDING_EXPIRATION_DAYS) + + @property + def is_expired(self) -> bool: + """ + Determine if the recording has expired by comparing expired_at with current UTC time. + Returns False if no expiration is configured or if expiration date is in the future. + """ + if not self.expired_at: + return False + + return self.expired_at < timezone.now() + class RecordingAccess(BaseAccess): """Relation model to give access to a recording for a user or a team with a role.""" diff --git a/src/backend/core/tests/recording/test_api_recordings_list.py b/src/backend/core/tests/recording/test_api_recordings_list.py index 44bac4f9..f09a2594 100644 --- a/src/backend/core/tests/recording/test_api_recordings_list.py +++ b/src/backend/core/tests/recording/test_api_recordings_list.py @@ -47,11 +47,12 @@ def test_api_recordings_list_authenticated_no_recording(): "role", ["administrator", "member", "owner"], ) -def test_api_recordings_list_authenticated_direct(role): +def test_api_recordings_list_authenticated_direct(role, settings): """ Authenticated users listing recordings, should only see the recordings to which they are related. """ + settings.RECORDING_EXPIRATIONS_DAYS = None user = factories.UserFactory() client = APIClient() client.force_login(user) @@ -89,6 +90,8 @@ def test_api_recordings_list_authenticated_direct(role): }, "status": "initiated", "updated_at": recording.updated_at.isoformat().replace("+00:00", "Z"), + "expired_at": None, + "is_expired": False, } diff --git a/src/backend/core/tests/recording/test_api_recordings_retrieve.py b/src/backend/core/tests/recording/test_api_recordings_retrieve.py index 5ad84c62..5196b700 100644 --- a/src/backend/core/tests/recording/test_api_recordings_retrieve.py +++ b/src/backend/core/tests/recording/test_api_recordings_retrieve.py @@ -2,7 +2,10 @@ Test recordings API endpoints in the Meet core app: retrieve. """ +import random + import pytest +from freezegun import freeze_time from rest_framework.test import APIClient from ...factories import RecordingFactory, UserFactory, UserRecordingAccessFactory @@ -61,8 +64,9 @@ def test_api_recording_retrieve_members(): } -def test_api_recording_retrieve_administrators(): +def test_api_recording_retrieve_administrators(settings): """A user who is an administrator of a recording should be able to retrieve it.""" + settings.RECORDING_EXPIRATION_DAYS = None user = UserFactory() recording = RecordingFactory() @@ -91,11 +95,14 @@ def test_api_recording_retrieve_administrators(): "updated_at": recording.updated_at.isoformat().replace("+00:00", "Z"), "status": str(recording.status), "mode": str(recording.mode), + "expired_at": None, + "is_expired": False, } -def test_api_recording_retrieve_owners(): +def test_api_recording_retrieve_owners(settings): """A user who is an owner of a recording should be able to retrieve it.""" + settings.RECORDING_EXPIRATION_DAYS = None user = UserFactory() recording = RecordingFactory() @@ -123,6 +130,87 @@ def test_api_recording_retrieve_owners(): "updated_at": recording.updated_at.isoformat().replace("+00:00", "Z"), "status": str(recording.status), "mode": str(recording.mode), + "expired_at": None, + "is_expired": False, + } + + +@freeze_time("2023-01-15 12:00:00") +def test_api_recording_retrieve_compute_expiration_date_correctly(settings): + """Test that the API returns the correct expiration date for a non-expired recording.""" + settings.RECORDING_EXPIRATION_DAYS = 1 + + user = UserFactory() + recording = RecordingFactory() + + UserRecordingAccessFactory( + recording=recording, user=user, role=random.choice(["administrator", "owner"]) + ) + + client = APIClient() + client.force_login(user) + + response = client.get(f"/api/v1.0/recordings/{recording.id!s}/") + + assert response.status_code == 200 + content = response.json() + room = recording.room + + assert content == { + "id": str(recording.id), + "key": recording.key, + "room": { + "access_level": str(room.access_level), + "id": str(room.id), + "name": room.name, + "slug": room.slug, + }, + "created_at": "2023-01-15T12:00:00Z", + "updated_at": "2023-01-15T12:00:00Z", + "status": str(recording.status), + "mode": str(recording.mode), + "expired_at": "2023-01-16T12:00:00Z", + "is_expired": False, # Ensure the recording is still valid and hasn't expired + } + + +def test_api_recording_retrieve_expired(settings): + """Test that the API returns the correct expiration date and flag for an expired recording.""" + settings.RECORDING_EXPIRATION_DAYS = 2 + + user = UserFactory() + + with freeze_time("2023-01-15 12:00:00"): + recording = RecordingFactory() + + UserRecordingAccessFactory( + recording=recording, user=user, role=random.choice(["administrator", "owner"]) + ) + + client = APIClient() + client.force_login(user) + + response = client.get(f"/api/v1.0/recordings/{recording.id!s}/") + + assert response.status_code == 200 + content = response.json() + room = recording.room + + assert content == { + "id": str(recording.id), + "key": recording.key, + "room": { + "access_level": str(room.access_level), + "id": str(room.id), + "name": room.name, + "slug": room.slug, + }, + "created_at": "2023-01-15T12:00:00Z", + "updated_at": "2023-01-15T12:00:00Z", + "status": str(recording.status), + "mode": str(recording.mode), + "expired_at": "2023-01-17T12:00:00Z", + "is_expired": True, # Ensure the recording has expired } diff --git a/src/backend/locale/en_US/LC_MESSAGES/django.po b/src/backend/locale/en_US/LC_MESSAGES/django.po index 0b093068..60018dd5 100644 --- a/src/backend/locale/en_US/LC_MESSAGES/django.po +++ b/src/backend/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-22 15:15+0000\n" +"POT-Creation-Date: 2025-04-23 13:47+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -29,7 +29,7 @@ msgstr "Permissions" msgid "Important dates" msgstr "Important dates" -#: core/api/serializers.py:60 +#: core/api/serializers.py:63 msgid "You must be administrator or owner of a room to add accesses to it." msgstr "You must be administrator or owner of a room to add accesses to it." @@ -45,106 +45,107 @@ msgstr "User account is disabled" msgid "Multiple user accounts share a common email." msgstr "Multiple user accounts share a common email." -#: core/models.py:29 +#: core/models.py:30 msgid "Member" msgstr "Member" -#: core/models.py:30 +#: core/models.py:31 msgid "Administrator" msgstr "Administrator" -#: core/models.py:31 +#: core/models.py:32 msgid "Owner" msgstr "Owner" -#: core/models.py:47 +#: core/models.py:48 msgid "Initiated" msgstr "Initiated" -#: core/models.py:48 +#: core/models.py:49 msgid "Active" msgstr "Active" -#: core/models.py:49 +#: core/models.py:50 msgid "Stopped" msgstr "Stopped" -#: core/models.py:50 +#: core/models.py:51 msgid "Saved" msgstr "Saved" -#: core/models.py:51 +#: core/models.py:52 msgid "Aborted" msgstr "Aborted" -#: core/models.py:52 +#: core/models.py:53 msgid "Failed to Start" msgstr "Failed to Start" -#: core/models.py:53 +#: core/models.py:54 msgid "Failed to Stop" msgstr "Failed to Stop" -#: core/models.py:54 +#: core/models.py:55 msgid "Notification succeeded" msgstr "Notification succeeded" -#: core/models.py:81 +#: core/models.py:82 msgid "SCREEN_RECORDING" msgstr "SCREEN_RECORDING" -#: core/models.py:82 +#: core/models.py:83 msgid "TRANSCRIPT" msgstr "TRANSCRIPT" -#: core/models.py:88 +#: core/models.py:89 msgid "Public Access" msgstr "Public Access" -#: core/models.py:89 +#: core/models.py:90 msgid "Trusted Access" msgstr "Trusted Access" -#: core/models.py:90 +#: core/models.py:91 msgid "Restricted Access" msgstr "Restricted Access" -#: core/models.py:102 +#: core/models.py:103 msgid "id" msgstr "id" -#: core/models.py:103 +#: core/models.py:104 msgid "primary key for the record as UUID" msgstr "primary key for the record as UUID" -#: core/models.py:109 +#: core/models.py:110 msgid "created on" msgstr "created on" -#: core/models.py:110 +#: core/models.py:111 msgid "date and time at which a record was created" msgstr "date and time at which a record was created" -#: core/models.py:115 +#: core/models.py:116 msgid "updated on" msgstr "updated on" -#: core/models.py:116 +#: core/models.py:117 msgid "date and time at which a record was last updated" msgstr "date and time at which a record was last updated" -#: core/models.py:136 +#: core/models.py:137 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." -msgstr "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" +msgstr "" +"Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." -#: core/models.py:142 +#: core/models.py:143 msgid "sub" msgstr "sub" -#: core/models.py:144 +#: core/models.py:145 msgid "" "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ " "characters only." @@ -152,55 +153,55 @@ msgstr "" "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ " "characters only." -#: core/models.py:152 +#: core/models.py:153 msgid "identity email address" msgstr "identity email address" -#: core/models.py:157 +#: core/models.py:158 msgid "admin email address" msgstr "admin email address" -#: core/models.py:159 +#: core/models.py:160 msgid "full name" msgstr "full name" -#: core/models.py:161 +#: core/models.py:162 msgid "short name" msgstr "short name" -#: core/models.py:167 +#: core/models.py:168 msgid "language" msgstr "language" -#: core/models.py:168 +#: core/models.py:169 msgid "The language in which the user wants to see the interface." msgstr "The language in which the user wants to see the interface." -#: core/models.py:174 +#: core/models.py:175 msgid "The timezone in which the user wants to see times." msgstr "The timezone in which the user wants to see times." -#: core/models.py:177 +#: core/models.py:178 msgid "device" msgstr "device" -#: core/models.py:179 +#: core/models.py:180 msgid "Whether the user is a device or a real user." msgstr "Whether the user is a device or a real user." -#: core/models.py:182 +#: core/models.py:183 msgid "staff status" msgstr "staff status" -#: core/models.py:184 +#: core/models.py:185 msgid "Whether the user can log into this admin site." msgstr "Whether the user can log into this admin site." -#: core/models.py:187 +#: core/models.py:188 msgid "active" msgstr "active" -#: core/models.py:190 +#: core/models.py:191 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." @@ -208,55 +209,55 @@ msgstr "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." -#: core/models.py:203 +#: core/models.py:204 msgid "user" msgstr "user" -#: core/models.py:204 +#: core/models.py:205 msgid "users" msgstr "users" -#: core/models.py:263 +#: core/models.py:264 msgid "Resource" msgstr "Resource" -#: core/models.py:264 +#: core/models.py:265 msgid "Resources" msgstr "Resources" -#: core/models.py:318 +#: core/models.py:319 msgid "Resource access" msgstr "Resource access" -#: core/models.py:319 +#: core/models.py:320 msgid "Resource accesses" msgstr "Resource accesses" -#: core/models.py:325 +#: core/models.py:326 msgid "Resource access with this User and Resource already exists." msgstr "Resource access with this User and Resource already exists." -#: core/models.py:381 +#: core/models.py:382 msgid "Visio room configuration" msgstr "Visio room configuration" -#: core/models.py:382 +#: core/models.py:383 msgid "Values for Visio parameters to configure the room." msgstr "Values for Visio parameters to configure the room." -#: core/models.py:388 core/models.py:508 +#: core/models.py:389 core/models.py:509 msgid "Room" msgstr "Room" -#: core/models.py:389 +#: core/models.py:390 msgid "Rooms" msgstr "Rooms" -#: core/models.py:519 +#: core/models.py:520 msgid "Worker ID" msgstr "Worker ID" -#: core/models.py:521 +#: core/models.py:522 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." @@ -264,39 +265,39 @@ msgstr "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." -#: core/models.py:529 +#: core/models.py:530 msgid "Recording mode" msgstr "Recording mode" -#: core/models.py:530 +#: core/models.py:531 msgid "Defines the mode of recording being called." msgstr "Defines the mode of recording being called." -#: core/models.py:536 +#: core/models.py:537 msgid "Recording" msgstr "Recording" -#: core/models.py:537 +#: core/models.py:538 msgid "Recordings" msgstr "Recordings" -#: core/models.py:617 +#: core/models.py:646 msgid "Recording/user relation" msgstr "Recording/user relation" -#: core/models.py:618 +#: core/models.py:647 msgid "Recording/user relations" msgstr "Recording/user relations" -#: core/models.py:624 +#: core/models.py:653 msgid "This user is already in this recording." msgstr "This user is already in this recording." -#: core/models.py:630 +#: core/models.py:659 msgid "This team is already in this recording." msgstr "This team is already in this recording." -#: core/models.py:636 +#: core/models.py:665 msgid "Either user or team must be set, not both." msgstr "Either user or team must be set, not both." @@ -347,7 +348,8 @@ msgstr "JOIN THE CALL" msgid "" "If you can't click the button, copy and paste the URL into your browser to " "join the call." -msgstr "If you can't click the button, copy and paste the URL into your browser to " +msgstr "" +"If you can't click the button, copy and paste the URL into your browser to " "join the call." #: core/templates/mail/html/invitation.html:235 @@ -423,7 +425,8 @@ msgstr "Open" msgid "" " If you have any questions or need assistance, please contact our support " "team at %(support_email)s. " -msgstr " If you have any questions or need assistance, please contact our support " +msgstr "" +" If you have any questions or need assistance, please contact our support " "team at %(support_email)s. " #: meet/settings.py:162 diff --git a/src/backend/locale/fr_FR/LC_MESSAGES/django.po b/src/backend/locale/fr_FR/LC_MESSAGES/django.po index 1aefe9fc..0aa9da17 100644 --- a/src/backend/locale/fr_FR/LC_MESSAGES/django.po +++ b/src/backend/locale/fr_FR/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-22 15:15+0000\n" +"POT-Creation-Date: 2025-04-23 13:47+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: antoine.lebaud@mail.numerique.gouv.fr\n" "Language-Team: LANGUAGE \n" @@ -29,13 +29,17 @@ msgstr "Permissions" msgid "Important dates" msgstr "Dates importantes" -#: core/api/serializers.py:60 +#: core/api/serializers.py:63 msgid "You must be administrator or owner of a room to add accesses to it." -msgstr "Vous devez être administrateur ou propriétaire d'une salle pour y ajouter des accès." +msgstr "" +"Vous devez être administrateur ou propriétaire d'une salle pour y ajouter " +"des accès." #: core/authentication/backends.py:77 msgid "User info contained no recognizable user identification" -msgstr "Les informations utilisateur ne contiennent aucune identification utilisateur reconnaissable" +msgstr "" +"Les informations utilisateur ne contiennent aucune identification " +"utilisateur reconnaissable" #: core/authentication/backends.py:102 msgid "User account is disabled" @@ -45,251 +49,263 @@ msgstr "Le compte utilisateur est désactivé" msgid "Multiple user accounts share a common email." msgstr "Plusieurs comptes utilisateur partagent une adresse e-mail commune." -#: core/models.py:29 +#: core/models.py:30 msgid "Member" msgstr "Membre" -#: core/models.py:30 +#: core/models.py:31 msgid "Administrator" msgstr "Administrateur" -#: core/models.py:31 +#: core/models.py:32 msgid "Owner" msgstr "Propriétaire" -#: core/models.py:47 +#: core/models.py:48 msgid "Initiated" msgstr "Initié" -#: core/models.py:48 +#: core/models.py:49 msgid "Active" msgstr "Actif" -#: core/models.py:49 +#: core/models.py:50 msgid "Stopped" msgstr "Arrêté" -#: core/models.py:50 +#: core/models.py:51 msgid "Saved" msgstr "Enregistré" -#: core/models.py:51 +#: core/models.py:52 msgid "Aborted" msgstr "Abandonné" -#: core/models.py:52 +#: core/models.py:53 msgid "Failed to Start" msgstr "Échec au démarrage" -#: core/models.py:53 +#: core/models.py:54 msgid "Failed to Stop" msgstr "Échec à l'arrêt" -#: core/models.py:54 +#: core/models.py:55 msgid "Notification succeeded" msgstr "Notification réussie" -#: core/models.py:81 +#: core/models.py:82 msgid "SCREEN_RECORDING" msgstr "ENREGISTREMENT_ÉCRAN" -#: core/models.py:82 +#: core/models.py:83 msgid "TRANSCRIPT" msgstr "TRANSCRIPTION" -#: core/models.py:88 +#: core/models.py:89 msgid "Public Access" msgstr "Accès public" -#: core/models.py:89 +#: core/models.py:90 msgid "Trusted Access" msgstr "Accès de confiance" -#: core/models.py:90 +#: core/models.py:91 msgid "Restricted Access" msgstr "Accès restreint" -#: core/models.py:102 +#: core/models.py:103 msgid "id" msgstr "id" -#: core/models.py:103 +#: core/models.py:104 msgid "primary key for the record as UUID" msgstr "clé primaire pour l'enregistrement sous forme d'UUID" -#: core/models.py:109 +#: core/models.py:110 msgid "created on" msgstr "créé le" -#: core/models.py:110 +#: core/models.py:111 msgid "date and time at which a record was created" msgstr "date et heure auxquelles un enregistrement a été créé" -#: core/models.py:115 +#: core/models.py:116 msgid "updated on" msgstr "mis à jour le" -#: core/models.py:116 +#: core/models.py:117 msgid "date and time at which a record was last updated" -msgstr "date et heure auxquelles un enregistrement a été mis à jour pour la dernière fois" +msgstr "" +"date et heure auxquelles un enregistrement a été mis à jour pour la dernière " +"fois" -#: core/models.py:136 +#: core/models.py:137 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." -msgstr "Entrez un identifiant valide. Cette valeur ne peut contenir que des lettres, des chiffres et les caractères @/./+/-/_." +msgstr "" +"Entrez un identifiant valide. Cette valeur ne peut contenir que des lettres, " +"des chiffres et les caractères @/./+/-/_." -#: core/models.py:142 +#: core/models.py:143 msgid "sub" msgstr "identifiant" -#: core/models.py:144 +#: core/models.py:145 msgid "" "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ " "characters only." -msgstr "Obligatoire. 255 caractères ou moins. Lettres, chiffres et caractères @/./+/-/_ uniquement." +msgstr "" +"Obligatoire. 255 caractères ou moins. Lettres, chiffres et caractères @/./" +"+/-/_ uniquement." -#: core/models.py:152 +#: core/models.py:153 msgid "identity email address" msgstr "adresse e-mail d'identité" -#: core/models.py:157 +#: core/models.py:158 msgid "admin email address" msgstr "adresse e-mail d'administrateur" -#: core/models.py:159 +#: core/models.py:160 msgid "full name" msgstr "nom complet" -#: core/models.py:161 +#: core/models.py:162 msgid "short name" msgstr "nom court" -#: core/models.py:167 +#: core/models.py:168 msgid "language" msgstr "langue" -#: core/models.py:168 +#: core/models.py:169 msgid "The language in which the user wants to see the interface." msgstr "La langue dans laquelle l'utilisateur souhaite voir l'interface." -#: core/models.py:174 +#: core/models.py:175 msgid "The timezone in which the user wants to see times." msgstr "Le fuseau horaire dans lequel l'utilisateur souhaite voir les heures." -#: core/models.py:177 +#: core/models.py:178 msgid "device" msgstr "appareil" -#: core/models.py:179 +#: core/models.py:180 msgid "Whether the user is a device or a real user." msgstr "Si l'utilisateur est un appareil ou un utilisateur réel." -#: core/models.py:182 +#: core/models.py:183 msgid "staff status" msgstr "statut du personnel" -#: core/models.py:184 +#: core/models.py:185 msgid "Whether the user can log into this admin site." msgstr "Si l'utilisateur peut se connecter à ce site d'administration." -#: core/models.py:187 +#: core/models.py:188 msgid "active" msgstr "actif" -#: core/models.py:190 +#: core/models.py:191 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." -msgstr "Si cet utilisateur doit être traité comme actif. Désélectionnez cette option au lieu de supprimer des comptes." +msgstr "" +"Si cet utilisateur doit être traité comme actif. Désélectionnez cette option " +"au lieu de supprimer des comptes." -#: core/models.py:203 +#: core/models.py:204 msgid "user" msgstr "utilisateur" -#: core/models.py:204 +#: core/models.py:205 msgid "users" msgstr "utilisateurs" -#: core/models.py:263 +#: core/models.py:264 msgid "Resource" msgstr "Ressource" -#: core/models.py:264 +#: core/models.py:265 msgid "Resources" msgstr "Ressources" -#: core/models.py:318 +#: core/models.py:319 msgid "Resource access" msgstr "Accès aux ressources" -#: core/models.py:319 +#: core/models.py:320 msgid "Resource accesses" msgstr "Accès aux ressources" -#: core/models.py:325 +#: core/models.py:326 msgid "Resource access with this User and Resource already exists." -msgstr "L'accès à la ressource avec cet utilisateur et cette ressource existe déjà." +msgstr "" +"L'accès à la ressource avec cet utilisateur et cette ressource existe déjà." -#: core/models.py:381 +#: core/models.py:382 msgid "Visio room configuration" msgstr "Configuration de la salle de visioconférence" -#: core/models.py:382 +#: core/models.py:383 msgid "Values for Visio parameters to configure the room." msgstr "Valeurs des paramètres de visioconférence pour configurer la salle." -#: core/models.py:388 core/models.py:508 +#: core/models.py:389 core/models.py:509 msgid "Room" msgstr "Salle" -#: core/models.py:389 +#: core/models.py:390 msgid "Rooms" msgstr "Salles" -#: core/models.py:519 +#: core/models.py:520 msgid "Worker ID" msgstr "ID du Worker" -#: core/models.py:521 +#: core/models.py:522 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." -msgstr "Entrez un identifiant pour l'enregistrement du travailleur. Cet identifiant est conservé même lorsque le travailleur s'arrête, permettant un suivi facile." +msgstr "" +"Entrez un identifiant pour l'enregistrement du travailleur. Cet identifiant " +"est conservé même lorsque le travailleur s'arrête, permettant un suivi " +"facile." -#: core/models.py:529 +#: core/models.py:530 msgid "Recording mode" msgstr "Mode d'enregistrement" -#: core/models.py:530 +#: core/models.py:531 msgid "Defines the mode of recording being called." msgstr "Définit le mode d'enregistrement appelé." -#: core/models.py:536 +#: core/models.py:537 msgid "Recording" msgstr "Enregistrement" -#: core/models.py:537 +#: core/models.py:538 msgid "Recordings" msgstr "Enregistrements" -#: core/models.py:617 +#: core/models.py:646 msgid "Recording/user relation" msgstr "Relation enregistrement/utilisateur" -#: core/models.py:618 +#: core/models.py:647 msgid "Recording/user relations" msgstr "Relations enregistrement/utilisateur" -#: core/models.py:624 +#: core/models.py:653 msgid "This user is already in this recording." msgstr "Cet utilisateur est déjà dans cet enregistrement." -#: core/models.py:630 +#: core/models.py:659 msgid "This team is already in this recording." msgstr "Cette équipe est déjà dans cet enregistrement." -#: core/models.py:636 +#: core/models.py:665 msgid "Either user or team must be set, not both." msgstr "Soit l'utilisateur, soit l'équipe doit être défini, pas les deux." @@ -340,7 +356,9 @@ msgstr "REJOINDRE L'APPEL" msgid "" "If you can't click the button, copy and paste the URL into your browser to " "join the call." -msgstr "Si vous ne pouvez pas cliquer sur le bouton, copiez et collez l'URL dans votre navigateur pour rejoindre l'appel." +msgstr "" +"Si vous ne pouvez pas cliquer sur le bouton, copiez et collez l'URL dans " +"votre navigateur pour rejoindre l'appel." #: core/templates/mail/html/invitation.html:235 #: core/templates/mail/text/invitation.txt:15 @@ -416,7 +434,9 @@ msgstr "Ouvrir" msgid "" " If you have any questions or need assistance, please contact our support " "team at %(support_email)s. " -msgstr " Si vous avez des questions ou besoin d'assistance, veuillez contacter notre équipe d'assistance à %(support_email)s. " +msgstr "" +" Si vous avez des questions ou besoin d'assistance, veuillez contacter notre " +"équipe d'assistance à %(support_email)s. " #: meet/settings.py:162 msgid "English" diff --git a/src/backend/locale/nl_NL/LC_MESSAGES/django.po b/src/backend/locale/nl_NL/LC_MESSAGES/django.po index 5fa18202..1b3954b5 100644 --- a/src/backend/locale/nl_NL/LC_MESSAGES/django.po +++ b/src/backend/locale/nl_NL/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-04-22 15:15+0000\n" +"POT-Creation-Date: 2025-04-23 13:47+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -29,9 +29,10 @@ msgstr "Rechten" msgid "Important dates" msgstr "Belangrijke datums" -#: core/api/serializers.py:60 +#: core/api/serializers.py:63 msgid "You must be administrator or owner of a room to add accesses to it." -msgstr "Je moet beheerder of eigenaar van een ruimte zijn om toegang toe te voegen." +msgstr "" +"Je moet beheerder of eigenaar van een ruimte zijn om toegang toe te voegen." #: core/authentication/backends.py:77 msgid "User info contained no recognizable user identification" @@ -45,95 +46,95 @@ msgstr "Gebruikersaccount is uitgeschakeld" msgid "Multiple user accounts share a common email." msgstr "Meerdere gebruikersaccounts delen een gemeenschappelijk e-mailadres." -#: core/models.py:29 +#: core/models.py:30 msgid "Member" msgstr "Lid" -#: core/models.py:30 +#: core/models.py:31 msgid "Administrator" msgstr "Beheerder" -#: core/models.py:31 +#: core/models.py:32 msgid "Owner" msgstr "Eigenaar" -#: core/models.py:47 +#: core/models.py:48 msgid "Initiated" msgstr "Gestart" -#: core/models.py:48 +#: core/models.py:49 msgid "Active" msgstr "Actief" -#: core/models.py:49 +#: core/models.py:50 msgid "Stopped" msgstr "Gestopt" -#: core/models.py:50 +#: core/models.py:51 msgid "Saved" msgstr "Opgeslagen" -#: core/models.py:51 +#: core/models.py:52 msgid "Aborted" msgstr "Afgebroken" -#: core/models.py:52 +#: core/models.py:53 msgid "Failed to Start" msgstr "Starten mislukt" -#: core/models.py:53 +#: core/models.py:54 msgid "Failed to Stop" msgstr "Stoppen mislukt" -#: core/models.py:54 +#: core/models.py:55 msgid "Notification succeeded" msgstr "Notificatie geslaagd" -#: core/models.py:81 +#: core/models.py:82 msgid "SCREEN_RECORDING" msgstr "SCHERM_OPNAME" -#: core/models.py:82 +#: core/models.py:83 msgid "TRANSCRIPT" msgstr "TRANSCRIPT" -#: core/models.py:88 +#: core/models.py:89 msgid "Public Access" msgstr "Openbare toegang" -#: core/models.py:89 +#: core/models.py:90 msgid "Trusted Access" msgstr "Vertrouwde toegang" -#: core/models.py:90 +#: core/models.py:91 msgid "Restricted Access" msgstr "Beperkte toegang" -#: core/models.py:102 +#: core/models.py:103 msgid "id" msgstr "id" -#: core/models.py:103 +#: core/models.py:104 msgid "primary key for the record as UUID" msgstr "primaire sleutel voor het record als UUID" -#: core/models.py:109 +#: core/models.py:110 msgid "created on" msgstr "aangemaakt op" -#: core/models.py:110 +#: core/models.py:111 msgid "date and time at which a record was created" msgstr "datum en tijd waarop een record werd aangemaakt" -#: core/models.py:115 +#: core/models.py:116 msgid "updated on" msgstr "bijgewerkt op" -#: core/models.py:116 +#: core/models.py:117 msgid "date and time at which a record was last updated" msgstr "datum en tijd waarop een record voor het laatst werd bijgewerkt" -#: core/models.py:136 +#: core/models.py:137 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." @@ -141,163 +142,162 @@ msgstr "" "Voer een geldige sub in. Deze waarde mag alleen letters, cijfers en @/./+/-/" "_ tekens bevatten." -#: core/models.py:142 +#: core/models.py:143 msgid "sub" msgstr "sub" -#: core/models.py:144 +#: core/models.py:145 msgid "" "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ " "characters only." msgstr "" -"Vereist. 255 tekens of minder. Alleen letters, cijfers en @/./+/-/_ " -"tekens." +"Vereist. 255 tekens of minder. Alleen letters, cijfers en @/./+/-/_ tekens." -#: core/models.py:152 +#: core/models.py:153 msgid "identity email address" msgstr "identiteit e-mailadres" -#: core/models.py:157 +#: core/models.py:158 msgid "admin email address" msgstr "beheerder e-mailadres" -#: core/models.py:159 +#: core/models.py:160 msgid "full name" msgstr "volledige naam" -#: core/models.py:161 +#: core/models.py:162 msgid "short name" msgstr "korte naam" -#: core/models.py:167 +#: core/models.py:168 msgid "language" msgstr "taal" -#: core/models.py:168 +#: core/models.py:169 msgid "The language in which the user wants to see the interface." msgstr "De taal waarin de gebruiker de interface wil zien." -#: core/models.py:174 +#: core/models.py:175 msgid "The timezone in which the user wants to see times." msgstr "De tijdzone waarin de gebruiker tijden wil zien." -#: core/models.py:177 +#: core/models.py:178 msgid "device" msgstr "apparaat" -#: core/models.py:179 +#: core/models.py:180 msgid "Whether the user is a device or a real user." msgstr "Of de gebruiker een apparaat is of een echte gebruiker." -#: core/models.py:182 +#: core/models.py:183 msgid "staff status" msgstr "personeelsstatus" -#: core/models.py:184 +#: core/models.py:185 msgid "Whether the user can log into this admin site." msgstr "Of de gebruiker kan inloggen op deze beheersite." -#: core/models.py:187 +#: core/models.py:188 msgid "active" msgstr "actief" -#: core/models.py:190 +#: core/models.py:191 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." msgstr "" -"Of deze gebruiker als actief moet worden behandeld. Deselecteer dit in plaats van " -"accounts te verwijderen." +"Of deze gebruiker als actief moet worden behandeld. Deselecteer dit in " +"plaats van accounts te verwijderen." -#: core/models.py:203 +#: core/models.py:204 msgid "user" msgstr "gebruiker" -#: core/models.py:204 +#: core/models.py:205 msgid "users" msgstr "gebruikers" -#: core/models.py:263 +#: core/models.py:264 msgid "Resource" msgstr "Bron" -#: core/models.py:264 +#: core/models.py:265 msgid "Resources" msgstr "Bronnen" -#: core/models.py:318 +#: core/models.py:319 msgid "Resource access" msgstr "Brontoegang" -#: core/models.py:319 +#: core/models.py:320 msgid "Resource accesses" msgstr "Brontoegangsrechten" -#: core/models.py:325 +#: core/models.py:326 msgid "Resource access with this User and Resource already exists." msgstr "Brontoegang met deze gebruiker en bron bestaat al." -#: core/models.py:381 +#: core/models.py:382 msgid "Visio room configuration" msgstr "Visio-ruimteconfiguratie" -#: core/models.py:382 +#: core/models.py:383 msgid "Values for Visio parameters to configure the room." msgstr "Waarden voor Visio-parameters om de ruimte te configureren." -#: core/models.py:388 core/models.py:508 +#: core/models.py:389 core/models.py:509 msgid "Room" msgstr "Ruimte" -#: core/models.py:389 +#: core/models.py:390 msgid "Rooms" msgstr "Ruimtes" -#: core/models.py:519 +#: core/models.py:520 msgid "Worker ID" msgstr "Worker ID" -#: core/models.py:521 +#: core/models.py:522 msgid "" "Enter an identifier for the worker recording.This ID is retained even when " "the worker stops, allowing for easy tracking." msgstr "" -"Voer een identificatie in voor de worker-opname. Deze ID blijft behouden, zelfs wanneer " -"de worker stopt, waardoor eenvoudige tracking mogelijk is." +"Voer een identificatie in voor de worker-opname. Deze ID blijft behouden, " +"zelfs wanneer de worker stopt, waardoor eenvoudige tracking mogelijk is." -#: core/models.py:529 +#: core/models.py:530 msgid "Recording mode" msgstr "Opnamemodus" -#: core/models.py:530 +#: core/models.py:531 msgid "Defines the mode of recording being called." msgstr "Definieert de modus van opname die wordt aangeroepen." -#: core/models.py:536 +#: core/models.py:537 msgid "Recording" msgstr "Opname" -#: core/models.py:537 +#: core/models.py:538 msgid "Recordings" msgstr "Opnames" -#: core/models.py:617 +#: core/models.py:646 msgid "Recording/user relation" msgstr "Opname/gebruiker-relatie" -#: core/models.py:618 +#: core/models.py:647 msgid "Recording/user relations" msgstr "Opname/gebruiker-relaties" -#: core/models.py:624 +#: core/models.py:653 msgid "This user is already in this recording." msgstr "Deze gebruiker is al in deze opname." -#: core/models.py:630 +#: core/models.py:659 msgid "This team is already in this recording." msgstr "Dit team is al in deze opname." -#: core/models.py:636 +#: core/models.py:665 msgid "Either user or team must be set, not both." msgstr "Ofwel gebruiker of team moet worden ingesteld, niet beide." @@ -349,8 +349,8 @@ msgid "" "If you can't click the button, copy and paste the URL into your browser to " "join the call." msgstr "" -"Als je niet op de knop kunt klikken, kopieer en plak dan de URL in je browser om " -"deel te nemen aan de oproep." +"Als je niet op de knop kunt klikken, kopieer en plak dan de URL in je " +"browser om deel te nemen aan de oproep." #: core/templates/mail/html/invitation.html:235 #: core/templates/mail/text/invitation.txt:15 diff --git a/src/backend/meet/settings.py b/src/backend/meet/settings.py index 9155040d..23f0e7e7 100755 --- a/src/backend/meet/settings.py +++ b/src/backend/meet/settings.py @@ -485,6 +485,11 @@ class Base(Configuration): RECORDING_STORAGE_EVENT_TOKEN = values.Value( None, environ_name="RECORDING_STORAGE_EVENT_TOKEN", environ_prefix=None ) + # Number of days before recordings expire - must be synced with bucket lifecycle policy + # Set to None for no expiration + RECORDING_EXPIRATION_DAYS = values.IntegerValue( + None, environ_name="RECORDING_EXPIRATION_DAYS", environ_prefix=None + ) SUMMARY_SERVICE_ENDPOINT = values.Value( None, environ_name="SUMMARY_SERVICE_ENDPOINT", environ_prefix=None )