diff --git a/.coveragerc b/.coveragerc index 60de8f7942..ce808f4bfb 100755 --- a/.coveragerc +++ b/.coveragerc @@ -14,6 +14,5 @@ omit = pod/*settings*.py */migrations/* pod/recorder/plugins/type_*.py pod/*/forms.py - pod/video/bbb.py scripts/bbb-pod-live/*.* pod/live/pilotingInterface.py diff --git a/.env.dev-exemple b/.env.dev-exemple index 1d9d568b66..e1f76665cd 100644 --- a/.env.dev-exemple +++ b/.env.dev-exemple @@ -2,11 +2,11 @@ DJANGO_SUPERUSER_USERNAME= DJANGO_SUPERUSER_PASSWORD= DJANGO_SUPERUSER_EMAIL= ### You can use internal registry -ELASTICSEARCH_TAG=elasticsearch:8.8.1 +ELASTICSEARCH_TAG=elasticsearch:8.13.0 NODE_TAG=node:19 -PYTHON_TAG=python:3.9-buster +PYTHON_TAG=python:3.9-bullseye REDIS_TAG=redis:alpine3.16 -### DOCKER_ENV : You can specify light or full. +### DOCKER_ENV: You can specify light or full. ### In case of value changing, you have to rebuild and restart your container. ### All yours datas will be kept. -DOCKER_ENV=light \ No newline at end of file +DOCKER_ENV=light diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5687ad4a46..1b86a134d3 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,4 +1,4 @@ -Before sending your pull request, make sure the following are done : +Before sending your pull request, make sure the following are done: * [ ] You have read our [contribution guidelines](https://github.com/EsupPortail/Esup-Pod/blob/master/CONTRIBUTING.md). * [ ] Your PR targets the `develop` branch. diff --git a/.github/workflows/pod_dev.yml b/.github/workflows/pod_dev.yml index c40cb71049..e15429666a 100644 --- a/.github/workflows/pod_dev.yml +++ b/.github/workflows/pod_dev.yml @@ -20,7 +20,7 @@ env: ELASTICSEARCH_TAG: "elasticsearch:7.17.18" NODE_VERSION: "19" NODE_TAG: "node:19" - PYTHON_TAG: "python:3.9-buster" + PYTHON_TAG: "python:3.9-bullseye" REDIS_TAG: "redis:alpine3.16" DOCKER_ENV: "full-test" @@ -67,7 +67,7 @@ jobs: - name: Flake8 compliance run: | flake8 --max-complexity=7 --ignore=E501,W503,E203 --exclude .git,pod/*/migrations/*.py,*_settings.py - + ## Start remote encoding and transcoding test ## - name: Create settings local file run: | @@ -93,8 +93,8 @@ jobs: sudo rm -rf ./pod/log sudo rm -rf ./pod/static sudo rm -rf ./pod/node_modules - docker-compose -f ./docker-compose-full-dev-with-volumes-test.yml -p esup-pod build --build-arg ELASTICSEARCH_VERSION=$ELASTICSEARCH_TAG --build-arg NODE_VERSION=$NODE_TAG --build-arg PYTHON_VERSION=$PYTHON_TAG --no-cache - docker-compose -f ./docker-compose-full-dev-with-volumes-test.yml up --detach --force-recreate --always-recreate-deps + docker compose -f ./docker-compose-full-dev-with-volumes-test.yml -p esup-pod build --build-arg ELASTICSEARCH_VERSION=$ELASTICSEARCH_TAG --build-arg NODE_VERSION=$NODE_TAG --build-arg PYTHON_VERSION=$PYTHON_TAG --no-cache + docker compose -f ./docker-compose-full-dev-with-volumes-test.yml up --detach --force-recreate --always-recreate-deps - name: Sleep for 60 seconds to wait run server on pod back uses: jakejarvis/wait-action@master with: @@ -107,19 +107,19 @@ jobs: docker exec pod-back-with-volumes python manage.py loaddata pod/video/fixtures/sample_videos.json - name: run test in docker run: docker exec pod-back-with-volumes coverage run --append --source='.' manage.py test -v 3 --keepdb pod.video_encode_transcript.tests.test_remote_encode_transcode - + - name: Run pa11y-ci full run: docker exec pa11y-ci pa11y-ci -c /usr/src/app/dockerfile-dev-with-volumes/pa11y-ci/config.json - name: Run pa11y-ci mobile run: docker exec pa11y-ci pa11y-ci -c /usr/src/app/dockerfile-dev-with-volumes/pa11y-ci/config_mobile.json - + - name: show pa11y results run: cat pa11y-results.json - name: Stop containers if: always() - run: docker-compose -f ./docker-compose-full-dev-with-volumes-test.yml down + run: docker compose -f ./docker-compose-full-dev-with-volumes-test.yml down - name: delete unused file and change owner run: | sudo rm -f pod/custom/settings_local.py diff --git a/AUTHORS.md b/AUTHORS.md index 6c10eacecb..085248eab4 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -10,14 +10,14 @@ Contributors for the V3 ---------------------------- A list of much-appreciated contributors who have submitted patches and reported bugs for the V3: * Olivier Bado-Faustin, University Cote d'Azur (design and template) -* Nicolas Lahoche, University of Lille (design and template) with all the PRI Team +* Nicolas Lahoche, University of Lille (design and template) with all the PRI Team * Nathaniel Burlot, University of Lille (member of PRI team for Logo and color of V3) * Céline Didier and Matthieu Bildstein, University of Lorraine (Live's Event App) * Farid Ait Karra, University of Lille (Docker part) * Maxime Taisne and Laurine Sajdak, University of Lille (Documentation and User part) * French Ministry of Education (who funded the development of some features) -Partnership +Partnership ---------------------------- * Elygames * OrionStudio diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index ebe8985996..1a6c4d0de6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -5,11 +5,11 @@ En tant que membres, contributeur·trice·s et dirigeant·e·s, nous nous engageons à faire de la participation à notre communauté -une expérience sans harcèlement, quel que soit l'âge, +une expérience sans harcèlement, quel que soit l'âge, la taille corporelle, le handicap visible ou invisible, l'appartenance ethnique, -les caractéristiques sexuelles, l'identité et l'expression de genre, -le niveau d'expérience, l'éducation, le statut socio-économique, -la nationalité, l'apparence personnelle, la race, la religion, +les caractéristiques sexuelles, l'identité et l'expression de genre, +le niveau d'expérience, l'éducation, le statut socio-économique, +la nationalité, l'apparence personnelle, la race, la religion, ou l'identité et l'orientation sexuelle. Nous nous engageons à agir et interagir de manière à contribuer à une communauté ouverte, accueillante, diversifiée, inclusive et saine. @@ -48,7 +48,7 @@ le cas échéant. ## Portée d'application -Ce code de conduite s'applique à la fois au sein des espaces du projet ainsi que dans les espaces publics lorsqu'un individu représente officiellement le projet ou sa communauté. +Ce code de conduite s'applique à la fois au sein des espaces du projet ainsi que dans les espaces publics lorsqu'un individu représente officiellement le projet ou sa communauté. Font parties des exemples de représentation d'un projet ou d'une communauté l'utilisation d'une adresse électronique officielle, la publication sur les réseaux sociaux à l'aide d'un compte officiel ou le fait d'agir en tant que représentant·e désigné·e lors d'un événement en ligne ou hors-ligne. @@ -56,7 +56,7 @@ communauté l'utilisation d'une adresse électronique officielle, la publication Les cas de comportements abusifs, harcelants ou tout autre comportement inacceptables peuvent être signalés aux dirigeant·e·s de la communauté responsables de l'application du code de conduite à -[Esup-Pod](https://github.com/EsupPortail/Esup-Pod). +[Esup-Pod](https://github.com/EsupPortail/Esup-Pod). Toutes les plaintes seront examinées et feront l'objet d'une enquête rapide et équitable. Tou·te·s les dirigeant·e·s de la communauté sont tenu·e·s de respecter la vie privée et la sécurité des personnes ayant signalé un incident. @@ -77,15 +77,15 @@ le comportement était inapproprié. Des excuses publiques peuvent être demand **Impact communautaire** : un non-respect par un seul incident ou une série d'actions. **Conséquence** : un avertissement avec des conséquences dû à la poursuite du comportement. -Aucune interaction avec les personnes concernées, y compris l'interaction non sollicitée avec celles et ceux qui sont chargé·e·s de l'application de ce code de conduite, pendant une période déterminée. -Cela comprend le fait d'éviter les interactions dans les espaces communautaires ainsi que sur les canaux externes comme les médias sociaux. +Aucune interaction avec les personnes concernées, y compris l'interaction non sollicitée avec celles et ceux qui sont chargé·e·s de l'application de ce code de conduite, pendant une période déterminée. +Cela comprend le fait d'éviter les interactions dans les espaces communautaires ainsi que sur les canaux externes comme les médias sociaux. Le non-respect de ces conditions peut entraîner un bannissement temporaire ou permanent. ### 3. Bannissement temporaire **Impact communautaire** : un non-respect grave des normes communautaires, notamment un comportement inapproprié soutenu. -**Conséquence** : un bannissement temporaire de toutes formes d'interactions ou de communications avec la communauté pendant une période déterminée. +**Conséquence** : un bannissement temporaire de toutes formes d'interactions ou de communications avec la communauté pendant une période déterminée. Aucune interaction publique ou privée avec les personnes concernées, y compris les interactions non sollicitées avec celles et ceux qui appliquent ce code de conduite, n'est autorisée pendant cette période. Le non-respect de ces conditions peut entraîner un bannissement permanent. @@ -105,7 +105,7 @@ disponible à Les Directives d'application ont été inspirées par le [Code of conduct enforcement ladder][Mozilla CoC] de Mozilla. -Pour obtenir des réponses aux questions courantes sur ce code de conduite, consultez la FAQ à [https://www.contributor-covenant.org/faq][FAQ]. +Pour obtenir des réponses aux questions courantes sur ce code de conduite, consultez la FAQ à [https://www.contributor-covenant.org/faq][FAQ]. Les traductions sont disponibles sur [https://www.contributor-covenant.org/translations][translations]. [homepage]: https://www.contributor-covenant.org @@ -113,4 +113,3 @@ Les traductions sont disponibles sur [https://www.contributor-covenant.org/trans [Mozilla CoC]: https://github.com/mozilla/diversity [FAQ]: https://www.contributor-covenant.org/faq [translations]: https://www.contributor-covenant.org/translations - diff --git a/CONFIGURATION_FR.md b/CONFIGURATION_FR.md index 7f2d4debe3..6ec7d00e07 100644 --- a/CONFIGURATION_FR.md +++ b/CONFIGURATION_FR.md @@ -76,7 +76,7 @@ Voici les configurations des applications tierces utilisées par Esup-Pod.
> valeur par défaut : `1.1.0` - >> Mise en place du mode PWA grâce à l'application Django-pwa
+ >> Mise en place du mode PWA grâce à l’application Django-pwa
>> Voici la configuration par défaut pour Pod, vous pouvez surcharger chaque variable dans votre fichier de configuration.
>> PWA_APP_NAME = "Pod"
>> PWA_APP_DESCRIPTION = (
@@ -94,13 +94,13 @@ Voici les configurations des applications tierces utilisées par Esup-Pod.
>> PWA_APP_STATUS_BAR_COLOR = "default"
>> PWA_APP_DIR = "ltr"
>> PWA_APP_LANG = "fr-FR"
- >> Pour en savoir plus : [https://github.com/silviolleite/django-pwa]()
+ >> Pour en savoir plus : [https://github.com/silviolleite/django-pwa]()
- `rest_framework` > valeur par défaut : `3.14.0` - >> version 3.14.0: mise en place de l’API rest pour l’application
+ >> version 3.14.0 : mise en place de l’API rest pour l’application
>> [https://www.django-rest-framework.org/]()
- `shibboleth` @@ -556,7 +556,7 @@ Vous pouvez tout à fait rajouter des langues comme vous le souhaitez. Il faudra > valeur par défaut : `True` - >> Une valeur booléenne qui active ou désactive l'outil de débogage.

+ >> Une valeur booléenne qui active ou désactive l’outil de débogage.

>> Ne déployez jamais de site en production avec le réglage USE_DEBUG_TOOLBAR activé.

>> __ref: [https://django-debug-toolbar.readthedocs.io/en/latest/]()__
@@ -599,9 +599,11 @@ Vous pouvez tout à fait rajouter des langues comme vous le souhaitez. Il faudra - `SECURE_SSL_REDIRECT` - > valeur par défaut : `not DEBUG` - + > valeur par défaut : `False` + >> À moins que votre site ne doive être disponible sur des connexions SSL et non SSL,
+ >> vous souhaiterez probablement définir ce paramètre sur True ou configurer un
+ >> load balancer ou reverse-proxy pour rediriger toutes les connexions vers HTTPS.
- `SESSION_COOKIE_AGE` @@ -905,8 +907,8 @@ Vous pouvez tout à fait rajouter des langues comme vous le souhaitez. Il faudra >> 'FAVICON': 'img/pod_favicon.svg', >> >> # Si souhaitée, à créer et sauvegarder - >> # dans le répertoire static de l'application custom et - >> # préciser le chemin d'accès. Par exemple : "custom/etab.css" + >> # dans le répertoire static de l’application custom et + >> # préciser le chemin d’accès. Par exemple : "custom/etab.css" >> 'CSS_OVERRIDE': '', >> >> # Vous pouvez créer un template dans votre application custom et @@ -1176,7 +1178,7 @@ Vous pouvez tout à fait rajouter des langues comme vous le souhaitez. Il faudra > valeur par défaut : `preferred_username` - >> Noms des Claim permettant de récupérer l'attribut login mais dépendant de l'attribut du client dans l'IDP
+ >> Noms des Claim permettant de récupérer l’attribut login mais dépendant de l’attribut du client dans l’IDP
- `OIDC_CLAIM_GIVEN_NAME` @@ -1433,7 +1435,7 @@ Vous pouvez tout à fait rajouter des langues comme vous le souhaitez. Il faudra >> voir `ACTIVE_MODEL_ENRICH`
-### Configuration de l'application Cut +### Configuration de l’application Cut Application Cut permettant de découper des vidéos.
Mettre `USE_CUT` à True pour activer cette application.
@@ -1441,9 +1443,9 @@ Mettre `USE_CUT` à True pour activer cette application.
> valeur par défaut : `True` - >> Activation de l'application Cut
+ >> Activation de l’application Cut
-### Configuration de l'application dressing +### Configuration de l’application dressing Application Dressing pour customiser une vidéo avec un filigrane et des crédits.
Mettre `USE_DRESSING` à True pour activer cette application.
@@ -1455,8 +1457,8 @@ Mettre `USE_DRESSING` à True pour activer cette application.
### Configuration application enrichment -### Configuration application d'import vidéo -Application Import_video permettant d'importer des vidéos externes dans Pod.
+### Configuration application d’import vidéo +Application Import_video permettant d’importer des vidéos externes dans Pod.
Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
- `MAX_UPLOAD_SIZE_ON_IMPORT` @@ -1464,7 +1466,7 @@ Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
> valeur par défaut : `4` >> Taille maximum en Go des fichiers vidéos qui peuvent être importés sur la plateforme
- >> via l'application import_video (0 = pas de taille maximum).
+ >> via l’application import_video (0 = pas de taille maximum).
- `RESTRICT_EDIT_IMPORT_VIDEO_ACCESS_TO_STAFF_ONLY` @@ -1476,7 +1478,7 @@ Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
> valeur par défaut : `True` - >> Activation de l’application d'import des vidéos
+ >> Activation de l’application d’import des vidéos
- `USE_IMPORT_VIDEO_BBB_RECORDER` @@ -1490,8 +1492,8 @@ Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
> valeur par défaut : `/home/pod/bbb-recorder/` >> Répertoire du plugin bbb-recorder (voir la documentation https://github.com/jibon57/bbb-recorder).
- >> bbb-recorder doit être installé dans ce répertoire, sur tous les serveurs d'encodage.
- >> bbb-recorder crée un répertoire Downloads, au même niveau, qui nécessite de l'espace disque.
+ >> bbb-recorder doit être installé dans ce répertoire, sur tous les serveurs d’encodage.
+ >> bbb-recorder crée un répertoire Downloads, au même niveau, qui nécessite de l’espace disque.
- `IMPORT_VIDEO_BBB_RECORDER_PATH` @@ -1558,7 +1560,7 @@ Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
> valeur par défaut : `10` - >> Nombre de tentatives maximum pour vérifier la présence / taille d'un fichier sur le filesystem
+ >> Nombre de tentatives maximum pour vérifier la présence / taille d’un fichier sur le filesystem
- `EVENT_GROUP_ADMIN` @@ -1622,7 +1624,7 @@ Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
> valeur par défaut : `False` - >> Activer l'auto-transcription pour les directs
+ >> Activer l’auto-transcription pour les directs
- `VIEW_EXPIRATION_DELAY` @@ -1661,7 +1663,7 @@ Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
> valeur par défaut : `False` - >> Affiche les vidéos de chaines non visibles sur la page d'accueil
+ >> Affiche les vidéos de chaines non visibles sur la page d’accueil
- `USE_BBB` @@ -1679,7 +1681,7 @@ Mettre `USE_IMPORT_VIDEO` à True pour activer cette application.
> valeur par défaut : `True` - >> Activation de l’application d'import des vidéos
+ >> Activation de l’application d’import des vidéos
- `USE_MEETING` @@ -1813,7 +1815,7 @@ Mettre `USE_MEETING` à True pour activer cette application.
>> Diaporama préchargé pour les réunions virtuelles.
- >> Un utilisateur peut remplacer cette valeur en choisissant un diaporama lors de la création d'une réunion virtuelle.
+ >> Un utilisateur peut remplacer cette valeur en choisissant un diaporama lors de la création d’une réunion virtuelle.
>> Doit se trouver dans le répertoire statique.
- `MEETING_RECORD_FIELDS` @@ -1853,6 +1855,52 @@ Mettre `USE_MEETING` à True pour activer cette application.
>> Seuls les utilisateurs "staff" pourront éditer les réunions
+ - `USE_MEETING_WEBINAR` + + > valeur par défaut : `False` + + >> Activation du mode Webinaire pour le module des réunions
+ + - `MEETING_WEBINAR_SIPMEDIAGW_URL` + + > valeur par défaut : `` + + >> URL du serveur SIPMediaGW qui gère les webinaires (Ex: https://sipmediagw.univ.fr)
+ + - `MEETING_WEBINAR_SIPMEDIAGW_TOKEN` + + > valeur par défaut : `` + + >> Jeton bearer du serveur SIPMediaGW qui gère les webinaires
+ + - `MEETING_WEBINAR_FIELDS` + + > valeur par défaut : `("is_webinar", "enable_chat")` + + >> Permet de définir les champs complémentaires du formulaire de création d’un webinaire
+ >> ces champs complémentaires sont affichés directement dans la page de formulaire d’un webinaire
+ >> + >> ``` + >> MEETING_WEBINAR_FIELDS: + >> ( + >> "is_webinar", + >> "enable_chat", + >> ) + >> + >> ``` + + - `MEETING_WEBINAR_AFFILIATION` + + > valeur par défaut : `['faculty', 'employee', 'staff']` + + >> Groupes d’accès ou affiliations des personnes autorisées à créer un webinaire
+ + - `MEETING_WEBINAR_GROUP_ADMIN` + + > valeur par défaut : `webinar admin` + + >> Groupe des personnes autorisées à créer un webinaire
+ - `USE_MEETING` > valeur par défaut : `False` @@ -1867,8 +1915,8 @@ Mettre `USE_PLAYLIST` à True pour activer cette application.
> valeur par défaut : `0` - >> Compte à rebours utilisé entre chaque vidéo lors de la lecture d'une playlist en lecture automatique.
- >> Le compte à rebours n'est pas présent s'il est à 0.
+ >> Compte à rebours utilisé entre chaque vidéo lors de la lecture d’une playlist en lecture automatique.
+ >> Le compte à rebours n’est pas présent s’il est à 0.
- `DEFAULT_PLAYLIST_THUMBNAIL` @@ -1877,17 +1925,29 @@ Mettre `USE_PLAYLIST` à True pour activer cette application.
>> Image par défaut affichée comme poster ou vignette, utilisée pour présenter la playlist.
>> Cette image doit se situer dans le répertoire `static`.
+ - `RESTRICT_PROMOTED_PLAYLIST_ACCESS_TO_STAFF_ONLY` + + > valeur par défaut : `True` + + >> Restreindre l’accès à la création de listes de lecture promues au staff uniquement.
+ - `USE_FAVORITES` > valeur par défaut : `True` - >> Activation des vidéos favorites. Permet aux utilisateurs d'ajouter des vidéos dans leurs favoris.
+ >> Activation des vidéos favorites. Permet aux utilisateurs d’ajouter des vidéos dans leurs favoris.
- `USE_PLAYLIST` > valeur par défaut : `True` - >> Activation des playlist. Permet aux utilisateurs d'ajouter des vidéos dans une playlist.
+ >> Activation des playlist. Permet aux utilisateurs d’ajouter des vidéos dans une playlist.
+ + - `USE_PROMOTED_PLAYLIST` + + > valeur par défaut : `True` + + >> Activation des playlist promues. Permet aux utilisateurs d'utiliser les listes de lecture promues.
### Configuration application podfile @@ -2088,7 +2148,7 @@ Mettre `USE_PLAYLIST` à True pour activer cette application.
> valeur par défaut : `600` - >> Temps en seconde de conservation des données de l'application video
+ >> Temps en seconde de conservation des données de l’application video
- `CHANNEL_FORM_FIELDS_HELP_TEXT` @@ -2353,7 +2413,7 @@ Mettre `USE_PLAYLIST` à True pour activer cette application.
> valeur par défaut : `100` - >> nombre d'item renvoyé par le flux rss
+ >> nombre d’item renvoyé par le flux rss
- `VIDEO_FORM_FIELDS` @@ -2628,8 +2688,8 @@ Mettre `USE_PLAYLIST` à True pour activer cette application.
### Configuration application video encodage et transcription Application pour l’encodage et la transcription de vidéo.
-Il est possible d'encoder en local ou en distant.
-Attention, il faut configurer Celery pour l’envoi des instructions pour l'encodage distant.
+Il est possible d’encoder en local ou en distant.
+Attention, il faut configurer Celery pour l’envoi des instructions pour l’encodage distant.
- `CELERY_BROKER_URL` @@ -2696,8 +2756,8 @@ Attention, il faut configurer Celery pour l’envoi des instructions pour l'enco > valeur par défaut : `False` - >> Il faut renseigner l'url du redis sur lequel Celery va chercher les ordres d'encodage et de transcription
- >> par exemple : "redis://redis:6379/7"
+ >> Il faut renseigner l’url du redis sur lequel Celery va chercher les ordres d’encodage et de transcription
+ >> par exemple : "redis://redis:6379/7"
- `FORMAT_CHOICES` @@ -2722,21 +2782,21 @@ Attention, il faut configurer Celery pour l’envoi des instructions pour l'enco > valeur par défaut : `False` - >> Si True, active l'encodage et la transcription sur un environnement distant via redis+celery
+ >> Si True, active l’encodage et la transcription sur un environnement distant via redis+celery
- `POD_API_URL` > valeur par défaut : `` - >> Adresse de l'API rest a appeler en fin d'encodage distant ou de transcription à distance.
- >> Exemple : https://pod.univ.fr/rest/
+ >> Adresse de l’API rest à appeler en fin d’encodage distant ou de transcription à distance.
+ >> Exemple : https://pod.univ.fr/rest/
- `POD_API_TOKEN` > valeur par défaut : `` - >> Token d'authentification utilisé pour l'appel en fin d'encodage distant ou de transcription à distance.
- >> Pour le créer, il faut aller dans la partie Admin > Jeton d'authentification > token.
+ >> Token d’authentification utilisé pour l’appel en fin d’encodage distant ou de transcription à distance.
+ >> Pour le créer, il faut aller dans la partie Admin > Jeton d’authentification > token.
- `VIDEO_RENDITIONS` @@ -2814,14 +2874,14 @@ Attention, il faut configurer Celery pour l’envoi des instructions pour l'enco >> Pour utiliser la version 7 ou 8, faire une mise à jour du paquet elasticsearch-py
>> Pour la 7, `pip3 install elasticsearch==7.17.7`,
>> et pour la 8, `pip3 install elasticsearch==8.8.1`.
- >> Voir [https://elasticsearch-py.readthedocs.io/]() pour plus d'information.
+ >> Voir [https://elasticsearch-py.readthedocs.io/]() pour plus d’information.
- `ES_OPTIONS` > valeur par défaut : `{}` >> Options d’ElasticSearch, notamment utilisées pour ES8 en SSL et avec un user en paramètre
- >> Voir [https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/config.html]() pour plus d'informations.
+ >> Voir [https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/config.html]() pour plus d’informations.
### Configuration application xapi @@ -2834,32 +2894,32 @@ Attention, il faut configurer Celery pour l’envoi des instructions.
> valeur par défaut : `False` - >> Activation de l'application xAPI
+ >> Activation de l’application xAPI
- `XAPI_ANONYMIZE_ACTOR` > valeur par défaut : `True` - >> Si False, le nom de l'utilisateur sera stocké en clair dans les statements xAPI, si True, son nom d'utilisateur sera anonymisé
+ >> Si False, le nom de l’utilisateur sera stocké en clair dans les statements xAPI, si True, son nom d’utilisateur sera anonymisé
- `XAPI_LRS_LOGIN` > valeur par défaut : `` - >> identifiant de connexion du LRS pour l'envoi des statements
+ >> identifiant de connexion du LRS pour l’envoi des statements
- `XAPI_LRS_PWD` > valeur par défaut : `` - >> mot de passe de connexion du LRS pour l'envoi des statements
+ >> mot de passe de connexion du LRS pour l’envoi des statements
- `XAPI_LRS_URL` > valeur par défaut : `` - >> URL de destination pour l'envoi des statements. I.E. : https://ralph.univ.fr/xAPI/statements
\ No newline at end of file + >> URL de destination pour l’envoi des statements. I.E. : https://ralph.univ.fr/xAPI/statements
\ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 438452f2b1..27a821ec73 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -108,7 +108,7 @@ While the prerequisites above must be satisfied prior to having your pull reques ### Git config -Warning about the configuration of line ending : https://docs.github.com/fr/get-started/getting-started-with-git/configuring-git-to-handle-line-endings +Warning about the configuration of line ending: https://docs.github.com/fr/get-started/getting-started-with-git/configuring-git-to-handle-line-endings We add a .gitattributes file at the root of repository ### Git Commit Messages diff --git a/Makefile b/Makefile index af8e910193..c542406ed0 100755 --- a/Makefile +++ b/Makefile @@ -89,9 +89,9 @@ createconfigs: # Use for docker run and docker exec commands -include .env.dev export -COMPOSE = docker-compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod -COMPOSE_FULL = docker-compose -f ./docker-compose-full-dev-with-volumes.yml -p esup-pod -COMPOSE_FULL_TEST = docker-compose -f ./docker-compose-full-dev-with-volumes-test.yml -p esup-pod +COMPOSE = docker compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod +COMPOSE_FULL = docker compose -f ./docker-compose-full-dev-with-volumes.yml -p esup-pod +COMPOSE_FULL_TEST = docker compose -f ./docker-compose-full-dev-with-volumes-test.yml -p esup-pod DOCKER_LOGS = docker logs -f #docker-start-build: @@ -156,6 +156,7 @@ else ifeq ($(DOCKER_ENV), full-test) else @$(COMPOSE) down -v endif + # Arrête le serveur de test et supprime les fichiers générés docker-reset: ifeq ($(DOCKER_ENV), full) @@ -165,16 +166,11 @@ else ifeq ($(DOCKER_ENV), full-test) else @$(COMPOSE) down -v endif - # sudo rm -rf ./pod/log + # Supprime les fichiers générés. sudo rm -rf ./pod/log - # sudo rm -rf ./pod/static sudo rm -rf ./pod/static - # sudo rm -rf ./pod/node_modules sudo rm -rf ./pod/node_modules - # sudo rm -rf ./pod/db_migrations sudo rm -rf ./pod/db_migrations - # sudo rm -rf ./pod/db.sqlite3 sudo rm -rf ./pod/db.sqlite3 sudo rm -rf ./pod/db_remote.sqlite3 - # sudo rm -rf ./pod/media sudo rm -rf ./pod/media diff --git a/README.md b/README.md index 06cee884d6..1a3eb9ef91 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Esup-Pod -[![Licence LGPL 3.0](https://img.shields.io/github/license/EsupPortail/Esup-Pod)](https://github.com/EsupPortail/Esup-Pod/blob/master/LICENSE) [![Testing Status](https://github.com/EsupPortail/Esup-Pod/actions/workflows/pod_main.yml/badge.svg)](https://github.com/EsupPortail/Esup-Pod/actions) [![Coverage Status](https://coveralls.io/repos/github/EsupPortail/Esup-Pod/badge.svg?branch=master)](https://coveralls.io/github/EsupPortail/Esup-Pod?branch=master) +[![Licence LGPL 3.0](https://img.shields.io/github/license/EsupPortail/Esup-Pod)](https://github.com/EsupPortail/Esup-Pod/blob/master/LICENSE) [![Testing Status](https://github.com/EsupPortail/Esup-Pod/actions/workflows/pod_main.yml/badge.svg)](https://github.com/EsupPortail/Esup-Pod/actions) [![Coverage Status](https://coveralls.io/repos/github/EsupPortail/Esup-Pod/badge.svg?branch=master)](https://coveralls.io/github/EsupPortail/Esup-Pod?branch=master) [![Awesome CodeGouvFr score](https://img.shields.io/badge/awesome-codegouvfr_8/10-blue)](https://github.com/codegouvfr/awesome-codegouvfr#awesome-codegouvfr-score) [FR] ## Plateforme de gestion de fichier vidéo @@ -29,4 +29,3 @@ The project and the platform of the same name are aimed at users of our institut | | Ministère de lʼEnseignement supérieur et de la Recherche :-----:|:-----:|:----: - diff --git a/docker-compose-dev-with-volumes.yml b/docker-compose-dev-with-volumes.yml index 9eb189ed34..1b58c29537 100755 --- a/docker-compose-dev-with-volumes.yml +++ b/docker-compose-dev-with-volumes.yml @@ -41,7 +41,7 @@ services: - ./.env.dev ports: - 6379:6379 - + # redis-commander: # container_name: redis-commander # hostname: redis-commander diff --git a/docker-compose-full-dev-with-volumes-test.yml b/docker-compose-full-dev-with-volumes-test.yml index 8913c02ebf..607ef863ea 100755 --- a/docker-compose-full-dev-with-volumes-test.yml +++ b/docker-compose-full-dev-with-volumes-test.yml @@ -31,7 +31,7 @@ services: env_file: - ./.env.dev volumes: *pod-volumes - + pod-transcript: container_name: pod-transcript-with-volumes build: @@ -83,7 +83,7 @@ services: depends_on: - pod-back volumes: *pod-volumes - + # redis-commander: # container_name: redis-commander # hostname: redis-commander diff --git a/docker-compose-full-dev-with-volumes.yml b/docker-compose-full-dev-with-volumes.yml index 52977c66a5..76c28915c8 100755 --- a/docker-compose-full-dev-with-volumes.yml +++ b/docker-compose-full-dev-with-volumes.yml @@ -31,7 +31,7 @@ services: env_file: - ./.env.dev volumes: *pod-volumes - + pod-transcript: container_name: pod-transcript-with-volumes build: @@ -74,7 +74,7 @@ services: - ./.env.dev ports: - 6379:6379 - + # redis-commander: # container_name: redis-commander # hostname: redis-commander diff --git a/dockerfile-dev-with-volumes/README.adoc b/dockerfile-dev-with-volumes/README.adoc index 8097bcc2ae..1bb70e6356 100755 --- a/dockerfile-dev-with-volumes/README.adoc +++ b/dockerfile-dev-with-volumes/README.adoc @@ -5,7 +5,7 @@ v1.2, 2023-08-30 :toc-title: Liste des rubriques :imagesdir: ./images -== Docker / docker-compose avec volumes sur la machine hôte +== Docker / docker compose avec volumes sur la machine hôte === Conteneur ElasticSearch http://localhost:9200 diff --git a/dockerfile-dev-with-volumes/pod-back/Dockerfile b/dockerfile-dev-with-volumes/pod-back/Dockerfile index e4733869fd..99aed8134c 100755 --- a/dockerfile-dev-with-volumes/pod-back/Dockerfile +++ b/dockerfile-dev-with-volumes/pod-back/Dockerfile @@ -34,7 +34,7 @@ COPY --from=source-build-js /tmp/pod/node_modules/ /tmp/node_modules/ RUN pip3 install --no-cache-dir -r requirements-conteneur.txt \ && pip3 install elasticsearch==7.17.7 -# ENTRYPOINT : +# ENTRYPOINT: COPY ./dockerfile-dev-with-volumes/pod-back/my-entrypoint-back.sh /tmp/my-entrypoint-back.sh RUN chmod 755 /tmp/my-entrypoint-back.sh diff --git a/dockerfile-dev-with-volumes/pod-encode/Dockerfile b/dockerfile-dev-with-volumes/pod-encode/Dockerfile index c29e44ad54..46b420eeb9 100755 --- a/dockerfile-dev-with-volumes/pod-encode/Dockerfile +++ b/dockerfile-dev-with-volumes/pod-encode/Dockerfile @@ -28,7 +28,7 @@ COPY ./requirements-encode.txt . RUN pip3 install --no-cache-dir -r requirements-encode.txt -# ENTRYPOINT : +# ENTRYPOINT: COPY ./dockerfile-dev-with-volumes/pod-encode/my-entrypoint-encode.sh /tmp/my-entrypoint-encode.sh RUN chmod 755 /tmp/my-entrypoint-encode.sh diff --git a/dockerfile-dev-with-volumes/pod-transcript/Dockerfile b/dockerfile-dev-with-volumes/pod-transcript/Dockerfile index 71b1ca3f52..008f823cb0 100755 --- a/dockerfile-dev-with-volumes/pod-transcript/Dockerfile +++ b/dockerfile-dev-with-volumes/pod-transcript/Dockerfile @@ -27,9 +27,9 @@ COPY ./requirements-encode.txt . COPY ./requirements-transcripts.txt . RUN pip3 install --no-cache-dir -r requirements-transcripts.txt \ - && pip3 install --no-cache-dir -r requirements-encode.txt + && pip3 install --no-cache-dir -r requirements-encode.txt -# ENTRYPOINT : +# ENTRYPOINT: COPY ./dockerfile-dev-with-volumes/pod-transcript/my-entrypoint-transcript.sh /tmp/my-entrypoint-transcript.sh RUN chmod 755 /tmp/my-entrypoint-transcript.sh diff --git a/dockerfile-dev-with-volumes/pod-xapi/Dockerfile b/dockerfile-dev-with-volumes/pod-xapi/Dockerfile index a8e22be23c..ef1da5e0d0 100755 --- a/dockerfile-dev-with-volumes/pod-xapi/Dockerfile +++ b/dockerfile-dev-with-volumes/pod-xapi/Dockerfile @@ -24,7 +24,7 @@ COPY ./requirements-encode.txt . RUN pip3 install --no-cache-dir -r requirements-encode.txt -# ENTRYPOINT : +# ENTRYPOINT: COPY ./dockerfile-dev-with-volumes/pod-xapi/my-entrypoint-xapi.sh /tmp/my-entrypoint-xapi.sh RUN chmod 755 /tmp/my-entrypoint-xapi.sh diff --git a/dockerfile-dev-with-volumes/pod/Dockerfile b/dockerfile-dev-with-volumes/pod/Dockerfile index a716a6ff92..f459305a76 100755 --- a/dockerfile-dev-with-volumes/pod/Dockerfile +++ b/dockerfile-dev-with-volumes/pod/Dockerfile @@ -39,7 +39,7 @@ COPY --from=source-build-js /tmp/pod/node_modules/ /tmp/node_modules/ RUN pip3 install --no-cache-dir -r requirements-conteneur.txt \ && pip3 install elasticsearch==8.9.0 -# ENTRYPOINT : +# ENTRYPOINT: COPY ./dockerfile-dev-with-volumes/pod/my-entrypoint.sh /tmp/my-entrypoint.sh RUN chmod 755 /tmp/my-entrypoint.sh diff --git a/make.bat b/make.bat index 06872ef1e4..8db22f01bd 100644 --- a/make.bat +++ b/make.bat @@ -3,39 +3,39 @@ for /f "delims=" %%a in (.env.dev) do call set %%a if /i "%1"=="docker-build" ( echo "Suppression du repertoire node_modules" rmdir /s /q .\pod\node_modules - echo "Suppression du repertoire node_modules" + echo "Suppression du repertoire static" rmdir /s /q .\pod\static echo "Suppression du repertoire log" rmdir /s /q .\pod\log echo "Chargement des variables d'environnement depuis le fichier .env.dev" for /f "delims=" %%a in (.env.dev) do call set %%a echo "Debut du build" - docker-compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod build --build-arg ELASTICSEARCH_VERSION=%ELASTICSEARCH_TAG% --build-arg NODE_VERSION=%NODE_TAG% --build-arg PYTHON_VERSION=%PYTHON_TAG% --no-cache + docker compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod build --build-arg ELASTICSEARCH_VERSION=%ELASTICSEARCH_TAG% --build-arg NODE_VERSION=%NODE_TAG% --build-arg PYTHON_VERSION=%PYTHON_TAG% --no-cache echo "Debut du start" - docker-compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod up + docker compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod up echo "Vous devriez obtenir ce message une fois esup-pod lance" echo "pod-dev-with-volumes | Superuser created successfully." echo "Fin du build" ) else if /i "%1"=="docker-start" ( echo "Debut du start" - docker-compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod up + docker compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod up echo "Fin du start" ) else if /i "%1"=="docker-stop" ( echo "Debut du stop" - docker-compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod down -v + docker compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod down -v echo "Fin du stop" ) else if /i "%1"=="docker-reset" ( echo "Debut du reset" - docker-compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod down -v + docker compose -f ./docker-compose-dev-with-volumes.yml -p esup-pod down -v echo "Suppression du repertoire node_modules" rmdir /s /q .\pod\node_modules - echo "Suppression du repertoire node_modules" + echo "Suppression du repertoire static" rmdir /s /q .\pod\static echo "Suppression du repertoire log" rmdir /s /q .\pod\log echo "Suppression du repertoire db_migrations" rmdir /s /q .\pod\db_migrations - echo "Suppression du repertoire db.sqlite3" + echo "Suppression du fichier db.sqlite3" del /s /q .\pod\db.sqlite3 echo "Suppression du repertoire media" rmdir /s /q .\pod\media diff --git a/pod/authentication/models.py b/pod/authentication/models.py index 1cd8c126fd..8db9f8be44 100644 --- a/pod/authentication/models.py +++ b/pod/authentication/models.py @@ -1,3 +1,5 @@ +"""Esup-Pod authentication models.""" + from django.db import models from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User, Permission, Group @@ -66,7 +68,7 @@ def get_name(self) -> str: """ - Returns the user's full name, including the username if not hidden. + Return the user's full name, including the username if not hidden. Returns: str: The user's full name and username if not hidden. diff --git a/pod/authentication/shibmiddleware.py b/pod/authentication/shibmiddleware.py index 31c22e3dbf..ab7285ffea 100644 --- a/pod/authentication/shibmiddleware.py +++ b/pod/authentication/shibmiddleware.py @@ -1,3 +1,5 @@ +"""Esup-Pod Shibboleth middleware authentication.""" + from shibboleth.middleware import ShibbolethRemoteUserMiddleware from django.conf import settings from pod.authentication.models import AFFILIATION_STAFF @@ -46,7 +48,7 @@ class ShibbMiddleware(ShibbolethRemoteUserMiddleware): header = REMOTE_USER_HEADER def check_user_meta(self, user, shib_meta): - """Check shibboleth access rights with user's meta + """Check Shibboleth access rights with user's meta. Args: user: User, @@ -62,7 +64,7 @@ def check_user_meta(self, user, shib_meta): ) def is_staffable(self, user): - """Check that given user, his domain is in authorized domains of shibboleth staff + """Check that given user, his domain is in authorized domains of shibboleth staff. Args: user: User diff --git a/pod/authentication/tests/test_models.py b/pod/authentication/tests/test_models.py index e7e433a4ad..66c09e61bb 100644 --- a/pod/authentication/tests/test_models.py +++ b/pod/authentication/tests/test_models.py @@ -1,3 +1,5 @@ +"""Esup-Pod authentication models test cases.""" + from django.test import TestCase from pod.authentication.models import Owner, AccessGroup from django.contrib.auth.models import User @@ -9,15 +11,15 @@ class OwnerTestCase(TestCase): - """OwnerTestCase""" + """Owner Test Case.""" def setUp(self): - """setUp OwnerTestCase create user pod""" + """Set up OwnerTestCase create user Pod.""" User.objects.create(username="pod", password="pod1234pod") # Owner.objects.create(user=user) def test_creation_owner(self): - """check if owner exist with username pod and check hashkey""" + """Check if owner exist with username pod and check hashkey.""" owner = Owner.objects.get(user__username="pod") user = User.objects.get(username="pod") self.assertEqual(user.owner, owner) @@ -33,16 +35,16 @@ def test_suppression_owner(self): class AccessGroupTestCase(TestCase): - """AcessGroupTestCase""" + """Acess Group Test Case.""" def setUp(self): - """setUp AcessGroupTestCase create user pod""" + """Set up AcessGroupTestCase create user Pod.""" User.objects.create(username="pod", password="pod1234pod") AccessGroup.objects.create(code_name="group1", display_name="Group 1") def test_creation_accessgroup(self): - """check if owner exist with username pod and check hashkey""" + """Check if owner exist with username pod and check hashkey.""" accessgroup = AccessGroup.objects.get(code_name="group1") self.assertEqual(accessgroup.code_name, "group1") self.assertEqual(accessgroup.display_name, "Group 1") diff --git a/pod/authentication/tests/test_populated.py b/pod/authentication/tests/test_populated.py index 92a0c881c6..8939026a60 100644 --- a/pod/authentication/tests/test_populated.py +++ b/pod/authentication/tests/test_populated.py @@ -1,4 +1,4 @@ -"""test populated authentication.""" +"""Esup-Pod test populated authentication.""" import random from django.conf import settings @@ -108,7 +108,7 @@ class PopulatedCASTestCase(TestCase): """ def setUp(self): - """setUp PopulatedCASTestCase create user pod""" + """Set up PopulatedCASTestCase create user Pod.""" User.objects.create(username="pod", password="pod1234pod") AccessGroup.objects.create(code_name="groupTest", display_name="Group de test") AccessGroup.objects.create( @@ -295,7 +295,7 @@ class PopulatedLDAPTestCase(TestCase): entry = "" def setUp(self): - """setUp PopulatedLDAPTestCase create user pod""" + """Set up PopulatedLDAPTestCase create user Pod.""" User.objects.create(username="pod", password="pod1234pod") AccessGroup.objects.create(code_name="groupTest", display_name="Group de test") AccessGroup.objects.create( @@ -433,7 +433,7 @@ def test_populate_user_from_entry_unpopulate_group(self): class PopulatedShibTestCase(TestCase): def setUp(self): - """setUp PopulatedShibTestCase create user pod""" + """Set up PopulatedShibTestCase create user Pod.""" self.hmap = {} for a in SHIBBOLETH_ATTRIBUTE_MAP: self.hmap[SHIBBOLETH_ATTRIBUTE_MAP[a][1]] = a diff --git a/pod/bbb/management/commands/bbb.py b/pod/bbb/management/commands/bbb.py index 7412b42194..5ecc0ec2ac 100644 --- a/pod/bbb/management/commands/bbb.py +++ b/pod/bbb/management/commands/bbb.py @@ -1,4 +1,6 @@ """ +Esup-Pod BBB command. + This command is used to manage the recordings made by BigBlueButton. To achieve this, this command performs the following tasks: @@ -43,7 +45,8 @@ VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3.6; cd /data/www/%userpod%/django_projects/podv3; source /usr/bin/virtualenvwrapper.sh; workon django_pod; python manage.py bbb -main' """ +main' +""" import os import traceback diff --git a/pod/chapter/templates/video_chapter.html b/pod/chapter/templates/video_chapter.html index ed4640c4d7..d37b76ac4c 100644 --- a/pod/chapter/templates/video_chapter.html +++ b/pod/chapter/templates/video_chapter.html @@ -48,6 +48,7 @@ {% endblock page_content %} + {% block page_aside %} {% if video.owner == request.user or request.user.is_superuser or perms.chapter.add_chapter or request.user in video.additional_owners.all %}
diff --git a/pod/chapter/views.py b/pod/chapter/views.py index a67010ab32..9e062764f3 100644 --- a/pod/chapter/views.py +++ b/pod/chapter/views.py @@ -81,6 +81,37 @@ def video_chapter_new(request, video): ) +def toggle_valid_form__video_chapter(request, video): + """Toggle the form validity.""" + list_chapter = video.chapter_set.all() + if is_ajax(request): + csrf_token_value = get_token(request) + some_data_to_dump = { + "list_chapter": render_to_string( + "chapter/list_chapter.html", + { + "list_chapter": list_chapter, + "video": video, + "csrf_token_value": csrf_token_value, + }, + request=request, + ), + "video-elem": render_to_string( + "videos/video-element.html", + {"video": video}, + request=request, + ), + } + data = json.dumps(some_data_to_dump) + return HttpResponse(data, content_type="application/json") + else: + return render( + request, + "video_chapter.html", + {"video": video, "list_chapter": list_chapter}, + ) + + def video_chapter_save(request, video): """Save a video chapter form request.""" list_chapter = video.chapter_set.all() @@ -94,33 +125,7 @@ def video_chapter_save(request, video): form_chapter = ChapterForm(request.POST) if form_chapter.is_valid(): form_chapter.save() - list_chapter = video.chapter_set.all() - if is_ajax(request): - csrf_token_value = get_token(request) - some_data_to_dump = { - "list_chapter": render_to_string( - "chapter/list_chapter.html", - { - "list_chapter": list_chapter, - "video": video, - "csrf_token_value": csrf_token_value, - }, - request=request, - ), - "video-elem": render_to_string( - "videos/video-element.html", - {"video": video}, - request=request, - ), - } - data = json.dumps(some_data_to_dump) - return HttpResponse(data, content_type="application/json") - else: - return render( - request, - "video_chapter.html", - {"video": video, "list_chapter": list_chapter}, - ) + return toggle_valid_form__video_chapter(request, video) else: if is_ajax(request): csrf_token_value = get_token(request) @@ -175,7 +180,6 @@ def video_chapter_modify(request, video): def video_chapter_delete(request, video): - list_chapter = video.chapter_set.all() chapter = get_object_or_404(Chapter, id=request.POST.get("id")) chapter.delete() list_chapter = video.chapter_set.all() @@ -220,33 +224,7 @@ def video_chapter_import(request, video): form_chapter = ChapterForm(initial={"video": video}) form_import = ChapterImportForm(request.POST, user=request.user, video=video) if form_import.is_valid(): - list_chapter = video.chapter_set.all() - if is_ajax(request): - csrf_token_value = get_token(request) - some_data_to_dump = { - "list_chapter": render_to_string( - "chapter/list_chapter.html", - { - "list_chapter": list_chapter, - "video": video, - "csrf_token_value": csrf_token_value, - }, - request=request, - ), - "video-elem": render_to_string( - "videos/video-element.html", - {"video": video}, - request=request, - ), - } - data = json.dumps(some_data_to_dump) - return HttpResponse(data, content_type="application/json") - else: - return render( - request, - "video_chapter.html", - {"video": video, "list_chapter": list_chapter}, - ) + return toggle_valid_form__video_chapter(request, video) else: if is_ajax(request): some_data_to_dump = { diff --git a/pod/completion/admin.py b/pod/completion/admin.py index 830b993f92..6c12cc425f 100644 --- a/pod/completion/admin.py +++ b/pod/completion/admin.py @@ -161,11 +161,11 @@ def debug(text): def check_if_treatment_in_progress() -> bool: return EnrichModelQueue.objects.filter(in_treatment=True).exists() - def write_into_kaldi_file(enrichModelQueue: EnrichModelQueue): + def write_into_kaldi_file(enrich_model_queue: EnrichModelQueue): with open( - MODEL_COMPILE_DIR + "/" + enrichModelQueue.lang + "/db/extra.txt", "w" + MODEL_COMPILE_DIR + "/" + enrich_model_queue.lang + "/db/extra.txt", "w" ) as f: - f.write(enrichModelQueue.text) + f.write(enrich_model_queue.text) subprocess.call( [ "docker", @@ -174,18 +174,18 @@ def write_into_kaldi_file(enrichModelQueue: EnrichModelQueue): MODEL_COMPILE_DIR + ":/kaldi/compile-model", "-it", "kaldi", - enrichModelQueue.lang, + enrich_model_queue.lang, ] ) - def copy_result_into_current_model(enrichModelQueue: EnrichModelQueue): + def copy_result_into_current_model(enrich_model_queue: EnrichModelQueue): from_path: str = ( - MODEL_COMPILE_DIR + "/" + enrichModelQueue.lang + "/exp/chain/tdnn/graph" + MODEL_COMPILE_DIR + "/" + enrich_model_queue.lang + "/exp/chain/tdnn/graph" ) to_path: str = ( - TRANSCRIPTION_MODEL_PARAM[enrichModelQueue.model_type][enrichModelQueue.lang][ - "model" - ] + TRANSCRIPTION_MODEL_PARAM[enrich_model_queue.model_type][ + enrich_model_queue.lang + ]["model"] + "/graph" ) if os.path.exists(to_path): @@ -193,12 +193,12 @@ def copy_result_into_current_model(enrichModelQueue: EnrichModelQueue): shutil.copytree(from_path, to_path) from_path: str = ( - MODEL_COMPILE_DIR + "/" + enrichModelQueue.lang + "/data/lang_test_rescore" + MODEL_COMPILE_DIR + "/" + enrich_model_queue.lang + "/data/lang_test_rescore" ) to_path: str = ( - TRANSCRIPTION_MODEL_PARAM[enrichModelQueue.model_type][enrichModelQueue.lang][ - "model" - ] + TRANSCRIPTION_MODEL_PARAM[enrich_model_queue.model_type][ + enrich_model_queue.lang + ]["model"] + "/rescore/" ) if os.path.isfile(from_path + "/G.fst") and os.path.isfile( @@ -208,12 +208,12 @@ def copy_result_into_current_model(enrichModelQueue: EnrichModelQueue): shutil.copy(from_path + "/G.carpa", to_path) from_path: str = ( - MODEL_COMPILE_DIR + "/" + enrichModelQueue.lang + "/exp/rnnlm_out" + MODEL_COMPILE_DIR + "/" + enrich_model_queue.lang + "/exp/rnnlm_out" ) to_path: str = ( - TRANSCRIPTION_MODEL_PARAM[enrichModelQueue.model_type][enrichModelQueue.lang][ - "model" - ] + TRANSCRIPTION_MODEL_PARAM[enrich_model_queue.model_type][ + enrich_model_queue.lang + ]["model"] + "/rnnlm/" ) if os.path.exists(from_path): @@ -221,17 +221,17 @@ def copy_result_into_current_model(enrichModelQueue: EnrichModelQueue): def enrich_kaldi_model_launch(): TrackAdmin.debug("enrich_kaldi_model") - enrichModelQueue = EnrichModelQueue.objects.filter(model_type="VOSK").first() - if enrichModelQueue is not None: - enrichModelQueue.in_treatment = True - enrichModelQueue.save() + enrich_model_queue = EnrichModelQueue.objects.filter(model_type="VOSK").first() + if enrich_model_queue is not None: + enrich_model_queue.in_treatment = True + enrich_model_queue.save() TrackAdmin.debug("start subprocess") - TrackAdmin.write_into_kaldi_file(enrichModelQueue) + TrackAdmin.write_into_kaldi_file(enrich_model_queue) TrackAdmin.debug("finish subprocess") TrackAdmin.debug("start copy result") - TrackAdmin.copy_result_into_current_model(enrichModelQueue) + TrackAdmin.copy_result_into_current_model(enrich_model_queue) TrackAdmin.debug("finish copy result") - enrichModelQueue.delete() + enrich_model_queue.delete() TrackAdmin.enrich_kaldi_model_launch() return else: diff --git a/pod/completion/templates/contributor/list_contributor.html b/pod/completion/templates/contributor/list_contributor.html index 922907664b..7164ca69db 100644 --- a/pod/completion/templates/contributor/list_contributor.html +++ b/pod/completion/templates/contributor/list_contributor.html @@ -26,13 +26,13 @@

{% trans 'List of contributors' %} ( {% csrf_token %} - +
{% csrf_token %} - +
diff --git a/pod/completion/templates/video_completion.html b/pod/completion/templates/video_completion.html index bbeaefbf95..125781dd7c 100644 --- a/pod/completion/templates/video_completion.html +++ b/pod/completion/templates/video_completion.html @@ -2,7 +2,6 @@ {% extends 'base.html' %} {% load i18n %} {% load static %} -{% block page_title %}{% trans 'Video additions' %} "{{video.title}}"{% endblock page_title %} {% block page_extra_head %} @@ -36,7 +35,7 @@   {% if form_contributor %} - {% include 'contributor/form_contributor.html' with form_contributor=form_contributor %} + {% include 'contributor/form_contributor.html' with form_contributor=form_contributor %} {% endif %}
diff --git a/pod/completion/tests/test_views.py b/pod/completion/tests/test_views.py index 36f1da2e17..bf7e47f672 100644 --- a/pod/completion/tests/test_views.py +++ b/pod/completion/tests/test_views.py @@ -1,6 +1,4 @@ -""" -Unit tests for completion views -""" +"""Unit tests for completion views.""" from django.test import TestCase from django.conf import settings diff --git a/pod/completion/utils.py b/pod/completion/utils.py index a368cde207..512bf7ef2f 100644 --- a/pod/completion/utils.py +++ b/pod/completion/utils.py @@ -1,3 +1,6 @@ +"""Esup-Pod completion app utilities.""" + + def get_video_completion_context( video, list_contributor=None, @@ -10,7 +13,8 @@ def get_video_completion_context( form_overlay=None, ): """ - Returns a dictionary containing information extracted from the video + Return a dictionary containing information extracted from the video. + and its associated objects (video, list of contributors, list of documents, track and overlay, as well as forms for creating or updating these objects) diff --git a/pod/completion/views.py b/pod/completion/views.py index c77aa8969d..248a9d4fd9 100644 --- a/pod/completion/views.py +++ b/pod/completion/views.py @@ -11,6 +11,7 @@ from django.contrib.auth.decorators import login_required from django.contrib.admin.views.decorators import staff_member_required from django.core.exceptions import PermissionDenied +from django.core.handlers.wsgi import WSGIRequest from pod.video.models import Video from .models import Contributor from .forms import ContributorForm @@ -47,6 +48,11 @@ } +def get_completion_home_page_title(video: Video): + """Get page title.""" + return _("Additions for the video “%s”") % video.title + + @csrf_protect @staff_member_required(redirect_field_name="referrer") def video_caption_maker(request, slug): @@ -75,11 +81,11 @@ def video_caption_maker(request, slug): else: track_language = LANGUAGE_CODE track_kind = "captions" - captionFileId = request.GET.get("src") - if captionFileId: - captionFile = CustomFileModel.objects.filter(id=captionFileId).first() - if captionFile: - track = Track.objects.filter(video=video, src=captionFile).first() + caption_file_id = request.GET.get("src") + if caption_file_id: + caption_file = CustomFileModel.objects.filter(id=caption_file_id).first() + if caption_file: + track = Track.objects.filter(video=video, src=caption_file).first() if track: track_language = track.lang track_kind = track.kind @@ -119,12 +125,12 @@ def video_caption_maker_save(request, video): response = file_edit_save(request, cur_folder) response_data = json.loads(response.content) if ("list_element" in response_data) and (lang in __LANG_CHOICES_DICT__): - captFile = get_object_or_404(CustomFileModel, id=response_data["file_id"]) + capt_file = get_object_or_404(CustomFileModel, id=response_data["file_id"]) # immediately assign the newly created captions file to the video - desired = Track.objects.filter(video=video, src=captFile) + desired = Track.objects.filter(video=video, src=capt_file) if desired.exists(): desired.update( - lang=lang, kind=kind, src=captFile, enrich_ready=enrich_ready + lang=lang, kind=kind, src=capt_file, enrich_ready=enrich_ready ) else: # check if the same combination of lang and kind exists @@ -133,7 +139,7 @@ def video_caption_maker_save(request, video): video=video, kind=kind, lang=lang, - src=captFile, + src=capt_file, enrich_ready=enrich_ready, ) track.save() @@ -169,9 +175,10 @@ def video_caption_maker_save(request, video): @csrf_protect @login_required(redirect_field_name="referrer") -def video_completion(request, slug): +def video_completion(request: WSGIRequest, slug: str): """Video Completion view.""" video = get_object_or_404(Video, slug=slug, sites=get_current_site(request)) + page_title = get_completion_home_page_title(video) if ( request.user != video.owner and not ( @@ -202,12 +209,12 @@ def video_completion(request, slug): request, "video_completion.html", { + "page_title": page_title, "video": video, "list_contributor": list_contributor, "list_track": list_track, "list_document": list_document, "list_overlay": list_overlay, - "page_title": _("Video additions"), }, ) else: @@ -215,18 +222,19 @@ def video_completion(request, slug): request, "video_completion.html", { + "page_title": page_title, "video": video, "list_contributor": list_contributor, - "page_title": _("Video additions"), }, ) @csrf_protect @login_required(redirect_field_name="referrer") -def video_completion_contributor(request, slug): +def video_completion_contributor(request: WSGIRequest, slug: str): """View to manage contributors of a video.""" video = get_object_or_404(Video, slug=slug, sites=get_current_site(request)) + page_title = get_completion_home_page_title(video) if request.user != video.owner and not ( request.user.is_superuser or request.user.has_perm("completion.add_contributor") @@ -256,12 +264,12 @@ def video_completion_contributor(request, slug): request, "video_completion.html", { + "page_title": page_title, "video": video, "list_contributor": list_contributor, "list_track": list_track, "list_document": list_document, "list_overlay": list_overlay, - "page_title": _("Video additions"), }, ) else: @@ -269,22 +277,27 @@ def video_completion_contributor(request, slug): request, "video_completion.html", { + "page_title": page_title, "video": video, "list_contributor": list_contributor, - "page_title": _("Video additions"), }, ) -def video_completion_contributor_new(request, video): +def video_completion_contributor_new(request: WSGIRequest, video: Video): """View to add new contributor to a video.""" form_contributor = ContributorForm(initial={"video": video}) context = get_video_completion_context(video, form_contributor=form_contributor) + context["page_title"] = _("Add a new contributor to the video “%s”") % video.title if request.is_ajax(): return render( request, "contributor/form_contributor.html", - {"form_contributor": form_contributor, "video": video}, + { + "page_title": context["page_title"], + "form_contributor": form_contributor, + "video": video, + }, ) else: return render( @@ -294,7 +307,7 @@ def video_completion_contributor_new(request, video): ) -def video_completion_contributor_save(request, video): +def video_completion_contributor_save(request: WSGIRequest, video: Video): """View to save contributors of a video.""" form_contributor = None if request.POST.get("contributor_id") and request.POST["contributor_id"] != "None": @@ -309,7 +322,12 @@ def video_completion_contributor_save(request, video): some_data_to_dump = { "list_data": render_to_string( "contributor/list_contributor.html", - {"list_contributor": list_contributor, "video": video}, + { + "page_title": _("Add a new contributor to the video “%s”") + % video.title, + "list_contributor": list_contributor, + "video": video, + }, request=request, ), } @@ -319,6 +337,10 @@ def video_completion_contributor_save(request, video): context = get_video_completion_context( video, list_contributor=list_contributor ) + context["page_title"] = get_completion_home_page_title(video) + messages.add_message( + request, messages.SUCCESS, _("The contributor has been saved.") + ) return render( request, "video_completion.html", @@ -330,13 +352,19 @@ def video_completion_contributor_save(request, video): "errors": "{0}".format(_("Please correct errors")), "form": render_to_string( "contributor/form_contributor.html", - {"video": video, "form_contributor": form_contributor}, + { + "page_title": _("Add a new contributor to the video “%s”") + % video.title, + "video": video, + "form_contributor": form_contributor, + }, request=request, ), } data = json.dumps(some_data_to_dump) return HttpResponse(data, content_type="application/json") context = get_video_completion_context(video, form_contributor=form_contributor) + context["page_title"] = get_completion_home_page_title(video) return render( request, "video_completion.html", @@ -344,17 +372,23 @@ def video_completion_contributor_save(request, video): ) -def video_completion_contributor_modify(request, video): +def video_completion_contributor_modify(request: WSGIRequest, video: Video): """View to modify a video contributor.""" contributor = get_object_or_404(Contributor, id=request.POST["id"]) form_contributor = ContributorForm(instance=contributor) + page_title = _("Edit the contributor “%s”") % contributor.name if request.is_ajax(): return render( request, "contributor/form_contributor.html", - {"form_contributor": form_contributor, "video": video}, + { + "page_title": page_title, + "form_contributor": form_contributor, + "video": video, + }, ) context = get_video_completion_context(video, form_contributor=form_contributor) + context["page_title"] = page_title return render( request, "video_completion.html", @@ -362,23 +396,28 @@ def video_completion_contributor_modify(request, video): ) -def video_completion_contributor_delete(request, video): +def video_completion_contributor_delete(request: WSGIRequest, video: Video): """View to delete a video contributor.""" - contributor = get_object_or_404(Contributor, id=request.POST["id"]) contributor.delete() + page_title = get_completion_home_page_title(video) list_contributor = video.contributor_set.all() if request.is_ajax(): some_data_to_dump = { "list_data": render_to_string( "contributor/list_contributor.html", - {"list_contributor": list_contributor, "video": video}, + { + "page_title": page_title, + "list_contributor": list_contributor, + "video": video, + }, request=request, ) } data = json.dumps(some_data_to_dump) return HttpResponse(data, content_type="application/json") context = get_video_completion_context(video) + context["page_title"] = page_title return render( request, "video_completion.html", @@ -504,7 +543,6 @@ def video_completion_document_modify(request, video): def video_completion_document_delete(request, video): """View to delete a document associated to a video.""" - document = get_object_or_404(Document, id=request.POST["id"]) document.delete() list_document = video.document_set.all() @@ -587,6 +625,27 @@ def video_completion_get_form_track(request): return form_track +def toggle_form_track_is_valid__video_completion_track(request, video, list_track): + """Toggle form_track is valid.""" + if request.is_ajax(): + some_data_to_dump = { + "list_data": render_to_string( + "track/list_track.html", + {"list_track": list_track, "video": video}, + request=request, + ) + } + data = json.dumps(some_data_to_dump) + return HttpResponse(data, content_type="application/json") + else: + context = get_video_completion_context(video, list_track=list_track) + return render( + request, + "video_completion.html", + context, + ) + + def video_completion_track_save(request, video): """View to save a track associated to a video.""" form_track = video_completion_get_form_track(request) @@ -594,24 +653,9 @@ def video_completion_track_save(request, video): if form_track.is_valid(): form_track.save() - - if request.is_ajax(): - some_data_to_dump = { - "list_data": render_to_string( - "track/list_track.html", - {"list_track": list_track, "video": video}, - request=request, - ) - } - data = json.dumps(some_data_to_dump) - return HttpResponse(data, content_type="application/json") - else: - context = get_video_completion_context(video, list_track=list_track) - return render( - request, - "video_completion.html", - context, - ) + return toggle_form_track_is_valid__video_completion_track( + request, video, list_track + ) else: if request.is_ajax(): some_data_to_dump = { @@ -659,24 +703,7 @@ def video_completion_track_delete(request, video): track = get_object_or_404(Track, id=request.POST["id"]) track.delete() list_track = video.track_set.all() - - if request.is_ajax(): - some_data_to_dump = { - "list_data": render_to_string( - "track/list_track.html", - {"list_track": list_track, "video": video}, - request=request, - ) - } - data = json.dumps(some_data_to_dump) - return HttpResponse(data, content_type="application/json") - else: - context = get_video_completion_context(video, list_track=list_track) - return render( - request, - "video_completion.html", - context, - ) + return toggle_form_track_is_valid__video_completion_track(request, video, list_track) def is_already_link(url, text): diff --git a/pod/cut/context_processors.py b/pod/cut/context_processors.py index f85873609e..2fe8f4bde6 100644 --- a/pod/cut/context_processors.py +++ b/pod/cut/context_processors.py @@ -6,7 +6,7 @@ def context_settings(request): - """Return all context settings for cut app""" + """Return all context settings for cut app.""" new_settings = {} new_settings["USE_CUT"] = USE_CUT return new_settings diff --git a/pod/cut/templates/video_cut.html b/pod/cut/templates/video_cut.html index 8a1f9246be..71af5234ba 100644 --- a/pod/cut/templates/video_cut.html +++ b/pod/cut/templates/video_cut.html @@ -35,6 +35,12 @@

{% endif %} +{% if access_not_allowed == True %} +

+  {% trans "Access to cutting video has been restricted. If you want to cut videos on the platform, please" %} {% trans 'contact us' %} +

+{% else %} + {% include 'videos/video-element.html' %} @@ -107,6 +113,8 @@

{% trans 'Are you sure +{% endif %} + {% endblock page_content %} {% block page_aside %} diff --git a/pod/cut/tests/test_models.py b/pod/cut/tests/test_models.py index a3a83ae2d8..6866a8a867 100644 --- a/pod/cut/tests/test_models.py +++ b/pod/cut/tests/test_models.py @@ -40,7 +40,7 @@ def test_bad_time(self): cut.start = "00:00:12" cut.end = "00:00:08" self.assertRaises(ValidationError, cut.clean) - print(" ---> test_bad_time: OK ! --- CutVideoModel") + print(" ---> test_bad_time: OK! --- CutVideoModel") def test_verify_time__value_error(self): """Test verify_time method with bad values.""" @@ -51,7 +51,7 @@ def test_verify_time__value_error(self): cut.start = "bad_value" cut.end = "bad_value" self.assertFalse(cut.verify_time()) - print(" ---> test_verify_time_value_error: OK ! --- CutVideoModel") + print(" ---> test_verify_time_value_error: OK! --- CutVideoModel") def test_verify_time__good_values(self): """Test verify_time method with good values.""" @@ -62,16 +62,16 @@ def test_verify_time__good_values(self): cut.start = "00:00:00" cut.end = "00:00:15" self.assertTrue(cut.verify_time()) - print(" ---> test_verify_time_good_values: OK ! --- CutVideoModel") + print(" ---> test_verify_time_good_values: OK! --- CutVideoModel") def test_start_in_int(self): """Test the start_in_int property.""" cut = CutVideo.objects.get(id=1) self.assertEqual(cut.start_in_int, 0) - print(" ---> test_start_in_int: OK ! --- CutVideoModel") + print(" ---> test_start_in_int: OK! --- CutVideoModel") def test_end_in_int(self): """Test the end_in_int property.""" cut = CutVideo.objects.get(id=1) self.assertEqual(cut.end_in_int, 20) - print(" ---> test_end_in_int: OK ! --- CutVideoModel") + print(" ---> test_end_in_int: OK! --- CutVideoModel") diff --git a/pod/cut/tests/test_views.py b/pod/cut/tests/test_views.py index 544c88ae1a..cec982dc02 100644 --- a/pod/cut/tests/test_views.py +++ b/pod/cut/tests/test_views.py @@ -9,6 +9,8 @@ from datetime import time from django.contrib.messages import get_messages from django.utils.translation import ugettext_lazy as _ +from .. import views +from importlib import reload class CutVideoViewsTestCase(TestCase): @@ -58,13 +60,13 @@ def test_get_full_duration(self): self.assertEqual(duration, 10) print(" ---> test_get_full_duration ok") - @override_settings(RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY=True) + @override_settings(RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY=True, USE_CUT=True) def test_restrict_edit_video_access_staff_only(self): """Test test_restrict_edit_video_access_staff_only.""" + reload(views) self.client.force_login(self.user2) url = reverse("cut:video_cut", kwargs={"slug": self.video.slug}) response = self.client.get(url) - self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "video_cut.html") self.assertTrue(response.context["access_not_allowed"]) diff --git a/pod/cut/views.py b/pod/cut/views.py index aaedf1f0d4..6bff49b9ac 100644 --- a/pod/cut/views.py +++ b/pod/cut/views.py @@ -1,3 +1,5 @@ +"""Esup-Pod video cutting app views.""" + from django.shortcuts import render from pod.main.views import in_maintenance from django.shortcuts import redirect @@ -20,11 +22,15 @@ from django.conf import settings +RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY = getattr( + settings, "RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY", False +) + @csrf_protect @login_required(redirect_field_name="referrer") def cut_video(request, slug): # noqa: C901 - """View for video cutting""" + """View for video cutting.""" if in_maintenance(): return redirect(reverse("maintenance")) video = get_object_or_404(Video, slug=slug, sites=get_current_site(request)) @@ -36,10 +42,7 @@ def cut_video(request, slug): # noqa: C901 cutting = CutVideo.objects.get(video=video.id) duration = cutting.duration - if ( - settings.RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY - and request.user.is_staff is False - ): + if RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY and request.user.is_staff is False: return render( request, "video_cut.html", {"access_not_allowed": True, "video": video} ) @@ -76,7 +79,7 @@ def cut_video(request, slug): # noqa: C901 start_encode(video.id) - messages.add_message(request, messages.INFO, _("The cut was made.")) + messages.add_message(request, messages.SUCCESS, _("The cut was made.")) return redirect(reverse("video:dashboard")) else: diff --git a/pod/dressing/templates/dressing_delete.html b/pod/dressing/templates/dressing_delete.html index e36e0ccd4d..e6366402ce 100644 --- a/pod/dressing/templates/dressing_delete.html +++ b/pod/dressing/templates/dressing_delete.html @@ -4,8 +4,12 @@ {% block breadcrumbs %} {{ block.super }} - - + + {% endblock %} {% block page_content %} diff --git a/pod/dressing/templates/dressing_edit.html b/pod/dressing/templates/dressing_edit.html index f07ed87eba..0c38c1cccd 100644 --- a/pod/dressing/templates/dressing_edit.html +++ b/pod/dressing/templates/dressing_edit.html @@ -7,23 +7,21 @@ {% endblock page_extra_head %} -{% block breadcrumbs %}{{ block.super }} - +{% block breadcrumbs %} + {{ block.super }} + - + {% endblock %} {% block page_content %} - + {% csrf_token %}

@@ -33,30 +31,30 @@

{% trans "One or more errors have been found in the form." %}

{% endif %} {% for field_hidden in form.hidden_fields %} - {{field_hidden}} + {{ field_hidden }} {% endfor %} {% for field in form.visible_fields %} - {% spaceless %} -
-
-
- {{ field.errors }} - {% if "form-check-input" in field.field.widget.attrs.class %} -
- {{ field }} -
- {% else %} - - {{ field }} - {% endif %} - {% if field.help_text %} - {{ field.help_text|safe }} - {% endif %} - {% if field.field.required %}
{% trans "Please provide a valid value for this field." %}
{% endif %} + {% spaceless %} +
+
+
+ {{ field.errors }} + {% if "form-check-input" in field.field.widget.attrs.class %} +
+ {{ field }} +
+ {% else %} + + {{ field }} + {% endif %} + {% if field.help_text %} + {{ field.help_text|safe }} + {% endif %} + {% if field.field.required %}
{% trans "Please provide a valid value for this field." %}
{% endif %} +
-
- {% endspaceless %} + {% endspaceless %} {% endfor %}
@@ -74,5 +72,5 @@

- {{form.media}} + {{ form.media }} {% endblock more_script %} diff --git a/pod/dressing/templates/my_dressings.html b/pod/dressing/templates/my_dressings.html index 8cfcb966de..14711c212f 100644 --- a/pod/dressing/templates/my_dressings.html +++ b/pod/dressing/templates/my_dressings.html @@ -7,108 +7,108 @@ {% endblock page_extra_head %} {% block breadcrumbs %} - {{block.super}} + {{ block.super }} {% endblock %} {% block page_content %} -
- - - - - - - - - - - - - - - - - - {% for dressing in dressings %} +
+
{% trans 'Available video dressings' %}
{% trans 'Title' %}{% trans 'Owners' %}{% trans 'Users' %}{% trans 'Allow to Groups' %}{% trans 'Watermark' %}{% trans 'Position' %}{% trans 'Opacity' %}{% trans 'Opening credits' %}{% trans 'Ending credits' %}{% trans 'Actions' %}
+ + - - + + + + + + + + + + - {% empty %} - - - - {% endfor %} - -
{% trans 'Available video dressings' %}
{{ dressing.title }} - {% for owner in dressing.owners.all %} - {{ owner }} - {% if not forloop.last %}, {% endif %} - {% empty %} - {% trans 'No owners' %} - {% endfor %} - - {% for user in dressing.users.all %} - {{ user }} - {% if not forloop.last %}, {% endif %} - {% empty %} - {% trans 'No users' %} - {% endfor %} - - {% for group in dressing.allow_to_groups.all %} - {{ group }}{% if not forloop.last %}, {% endif %} - {% empty %} - {% trans 'No groups' %} - {% endfor %} - - {% if dressing.watermark %} - {{ dressing.watermark.name }} - {% else %} - {% trans 'None' %} - {% endif %} - - {% if dressing.watermark %} - {{ dressing.get_position_display }} - {% else %} -
- -
- {% endif %} -
- {% if dressing.watermark %} - {{ dressing.opacity }} - {% else %} -
- -
- {% endif %} -
- {% if dressing.opening_credits %} - {{ dressing.opening_credits }} - {% else %} - {% trans 'None' %} - {% endif %} - - {% if dressing.ending_credits %} - {{ dressing.ending_credits }} - {% else %} - {% trans 'None' %} - {% endif %} - - - {% trans 'Title' %}{% trans 'Owners' %}{% trans 'Users' %}{% trans 'Allow to Groups' %}{% trans 'Watermark' %}{% trans 'Position' %}{% trans 'Opacity' %}{% trans 'Opening credits' %}{% trans 'Ending credits' %}{% trans 'Actions' %}
{% trans 'No dressings found.' %}
-
-{% trans 'Add new dressing' %} + + + {% for dressing in dressings %} + + {{ dressing.title }} + + {% for owner in dressing.owners.all %} + {{ owner }} + {% if not forloop.last %}, {% endif %} + {% empty %} + {% trans 'No owners' %} + {% endfor %} + + {% for user in dressing.users.all %} + {{ user }} + {% if not forloop.last %}, {% endif %} + {% empty %} + {% trans 'No users' %} + {% endfor %} + + {% for group in dressing.allow_to_groups.all %} + {{ group }}{% if not forloop.last %}, {% endif %} + {% empty %} + {% trans 'No groups' %} + {% endfor %} + + {% if dressing.watermark %} + {{ dressing.watermark.name }} + {% else %} + {% trans 'None' %} + {% endif %} + + {% if dressing.watermark %} + {{ dressing.get_position_display }} + {% else %} +
+ +
+ {% endif %} + + {% if dressing.watermark %} + {{ dressing.opacity }} + {% else %} +
+ +
+ {% endif %} + + {% if dressing.opening_credits %} + {{ dressing.opening_credits }} + {% else %} + {% trans 'None' %} + {% endif %} + + {% if dressing.ending_credits %} + {{ dressing.ending_credits }} + {% else %} + {% trans 'None' %} + {% endif %} + + + + + {% empty %} + + {% trans 'No dressings found.' %} + + {% endfor %} + + +

+ {% trans 'Add new dressing' %} {% endblock page_content %} {% block collapse_page_aside %}{% endblock collapse_page_aside %} diff --git a/pod/dressing/templates/video_dressing.html b/pod/dressing/templates/video_dressing.html index 3271cd16cc..5cf342b5bf 100644 --- a/pod/dressing/templates/video_dressing.html +++ b/pod/dressing/templates/video_dressing.html @@ -24,116 +24,143 @@ {% endblock %} {% block page_content %} + {% if video.encoding_in_progress %} + + {% endif %} -{% if video.encoding_in_progress %} - -{% endif %} - - - {% include 'videos/video-element.html' %} - + + {% include 'videos/video-element.html' %} + - - {% csrf_token %} -
- {% trans 'Pick a dressing below' %} - - - - - - - - - - - - - - - {% for dressing in dressings %} + + {% csrf_token %} +
+ {% trans 'Pick a dressing below' %} +
{% trans 'Available video dressings' %}
{% trans 'Select' %}{% trans 'Title' %}{% trans 'Watermark' %}{% trans 'Position' %}{% trans 'Opacity' %}{% trans 'Opening credits' %}{% trans 'Ending credits' %}
+ + - - - - - + + + + + + + - {% empty %} - - - - {% endfor %} - -
{% trans 'Available video dressings' %}
- - - {% if dressing.watermark %} - {{ dressing.watermark.name }} - {% else %} - {% trans 'None' %} - {% endif %} - - {% if dressing.watermark %} - {{ dressing.get_position_display }} - {% else %} -
- -
- {% endif %} -
- {% if dressing.watermark %} - {{ dressing.opacity }} - {% else %} -
- -
- {% endif %} -
- {% if dressing.opening_credits %} - {{ dressing.opening_credits }} - {% else %} - {% trans 'None' %} - {% endif %} - - {% if dressing.ending_credits %} - {{ dressing.ending_credits }} - {% else %} - {% trans 'None' %} - {% endif %} - {% trans 'Select' %}{% trans 'Title' %}{% trans 'Watermark' %}{% trans 'Position' %}{% trans 'Opacity' %}{% trans 'Opening credits' %}{% trans 'Ending credits' %}
{% trans 'No dressings found.' %}
-
- - - - - -  {% trans 'My dressings' %} - -  {% trans 'Back to the video' %} - - + + + {% for dressing in dressings %} + + + + + + + {% if dressing.watermark %} + {{ dressing.watermark.name }} + {% else %} + {% trans 'None' %} + {% endif %} + + + {% if dressing.watermark %} + {{ dressing.get_position_display }} + {% else %} +
+   +
+ {% endif %} + + + {% if dressing.watermark %} + {{ dressing.opacity }} + {% else %} +
+   +
+ {% endif %} + + + {% if dressing.opening_credits %} + {{ dressing.opening_credits }} + {% else %} + {% trans 'None' %} + {% endif %} + + + {% if dressing.ending_credits %} + {{ dressing.ending_credits }} + {% else %} + {% trans 'None' %} + {% endif %} + + + {% empty %} + + {% trans 'No dressings found.' %} + + {% endfor %} + + + + + + + +  {% trans 'My dressings' %} + +  {% trans 'Back to the video' %} + + {% endblock page_content %} {% block page_aside %} - {% if access_not_allowed == True %} - {% else %} -
-

 {% trans "Manage video" %}

-
- {% include "videos/link_video.html" with hide_favorite_link=True %} -
+
+

 {% trans "Manage video" %}

+
+ {% include "videos/link_video.html" with hide_favorite_link=True %}
- {% endif %} +
+ +
+

{% trans "Help"%}

+ +
+

+ {% trans "Dressings are a way to customize your video. You can add a watermark, opening and ending credits" %} +

+
+ + +
+

+ {% trans "A watermark is an image that will be displayed on the video. You can choose the position and the opacity of the watermark." %} +

+
+ + +
+

+ {% trans "Opening and ending credits are videos that will be displayed at the beginning or at the end of the video." %} +

+
+
{% endblock page_aside %} {% block more_script %} diff --git a/pod/dressing/tests/test_views.py b/pod/dressing/tests/test_views.py index 8fdd78855a..18f7cf65e1 100644 --- a/pod/dressing/tests/test_views.py +++ b/pod/dressing/tests/test_views.py @@ -61,6 +61,39 @@ def test_video_dressing_page(self): self.assertTemplateUsed(response, "video_dressing.html") print(" ---> test_video_dressing_page ok") + def test_video_encoding_in_progress(self): + """Test video encoding in progress in VideoDressingViewTest.""" + self.first_video.encoding_in_progress = True + self.first_video.save() + self.client.force_login(self.user) + response = self.client.get( + reverse("dressing:video_dressing", args=[self.first_video.slug]) + ) + messages = [m for m in get_messages(response.wsgi_request)] + + self.assertEqual(response.status_code, 403) + self.assertEqual(len(messages), 1) + self.assertEqual(messages[0].tags, "error") + self.assertEqual(messages[0].message, _("The video is currently being encoded.")) + print(" ---> test_video_encoding_in_progress ok") + + def test_video_dressing_permission_denied(self): + """Test test_video_dressing_permission_denied in VideoDressingViewTest.""" + user_without_permission = User.objects.create_user( + username="useless_user", password="testpass" + ) + self.client.force_login(user_without_permission) + response = self.client.get( + reverse("dressing:video_dressing", args=[self.first_video.slug]) + ) + messages = [m for m in get_messages(response.wsgi_request)] + + self.assertEqual(response.status_code, 403) + self.assertEqual(len(messages), 1) + self.assertEqual(messages[0].tags, "error") + self.assertEqual(messages[0].message, _("You cannot dress this video.")) + print(" ---> test_video_dressing_permission_denied ok") + class MyDressingViewTest(TestCase): """My dressing page tests case.""" @@ -109,6 +142,21 @@ def test_my_dressing_page(self): self.assertTemplateUsed(response, "my_dressings.html") print(" ---> test_my_dressing_page ok") + def test_my_dressing_permission_denied(self): + """Test test_my_dressing_permission_denied in MyDressingViewTest.""" + user_without_permission = User.objects.create_user( + username="useless_user", password="testpass" + ) + self.client.force_login(user_without_permission) + response = self.client.get(reverse("dressing:my_dressings")) + messages = [m for m in get_messages(response.wsgi_request)] + + self.assertEqual(response.status_code, 403) + self.assertEqual(len(messages), 1) + self.assertEqual(messages[0].tags, "error") + self.assertEqual(messages[0].message, _("You cannot access this page.")) + print(" ---> test_my_dressing_permission_denied ok") + class DressingEditViewTest(TestCase): """Dressing edit page tests case.""" @@ -180,6 +228,22 @@ def setUp(self) -> None: self.dressing.users.set([self.user]) self.dressing.videos.set([self.first_video]) + def test_maintenance(self): + """Test Pod maintenance mode in VideoDressingViewTest.""" + self.client.force_login(self.user) + url = reverse("dressing:dressing_delete", args=[self.dressing.id]) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + conf = Configuration.objects.get(key="maintenance_mode") + conf.value = "1" + conf.save() + + response = self.client.get(url) + self.assertEqual(response.status_code, 302) + self.assertRedirects(response, "/maintenance/") + print(" ---> test_maintenance ok") + def test_dressing_delete_view_get(self): """Test test_dressing_delete_view_get.""" self.client.force_login(self.user) diff --git a/pod/dressing/utils.py b/pod/dressing/utils.py index bcd6728dda..8b179eed64 100644 --- a/pod/dressing/utils.py +++ b/pod/dressing/utils.py @@ -6,28 +6,28 @@ from django.db.models import Q -def get_dressing_input(dressing: Dressing, FFMPEG_DRESSING_INPUT: str) -> str: +def get_dressing_input(dressing: Dressing, ffmpeg_dressing_input: str) -> str: """ Obtain the files necessary for encoding a dressed video. Args: dressing (:class:`pod.dressing.models.Dressing`): The dressing object. - FFMPEG_DRESSING_INPUT (str): Source file for encoding. + ffmpeg_dressing_input (str): Source file for encoding. Returns: command (str): params for the ffmpeg command. """ command = "" if dressing.watermark: - command += FFMPEG_DRESSING_INPUT % {"input": dressing.watermark.file.path} + command += ffmpeg_dressing_input % {"input": dressing.watermark.file.path} if dressing.opening_credits: - command += FFMPEG_DRESSING_INPUT % { + command += ffmpeg_dressing_input % { "input": os.path.join( settings.MEDIA_ROOT, str(dressing.opening_credits.video) ) } if dressing.ending_credits: - command += FFMPEG_DRESSING_INPUT % { + command += ffmpeg_dressing_input % { "input": os.path.join(settings.MEDIA_ROOT, str(dressing.ending_credits.video)) } return command diff --git a/pod/dressing/views.py b/pod/dressing/views.py index c7e65a274b..32fddb152a 100644 --- a/pod/dressing/views.py +++ b/pod/dressing/views.py @@ -25,6 +25,7 @@ def video_dressing(request, slug): return redirect(reverse("maintenance")) video = get_object_or_404(Video, slug=slug, sites=get_current_site(request)) + if not video.encoded and video.encoding_in_progress is True: messages.add_message( request, messages.ERROR, _("The video is currently being encoded.") @@ -33,11 +34,7 @@ def video_dressing(request, slug): dressings = get_dressings(request.user, request.user.owner.accessgroup_set.all()) - if not ( - request.user.is_superuser - or request.user.is_staff - or request.user.has_perm("dressing.video_dressing") - ): + if not (request.user.is_superuser or request.user.is_staff): messages.add_message(request, messages.ERROR, _("You cannot dress this video.")) raise PermissionDenied @@ -67,10 +64,10 @@ def video_dressing(request, slug): request, "video_dressing.html", { + "page_title": _("Dress the video “%s”") % video.title, "video": video, "dressings": dressings, "current": current, - "page_title": _("Dress the video “%s”") % video.title, }, ) @@ -81,13 +78,7 @@ def dressing_edit(request, dressing_id): """Edit a dressing object.""" dressing_edit = get_object_or_404(Dressing, id=dressing_id) - if dressing_edit and ( - not ( - request.user.is_superuser - or request.user.is_staff - or request.user.has_perm("dressing.dressing_edit") - ) - ): + if dressing_edit and (not (request.user.is_superuser or request.user.is_staff)): messages.add_message(request, messages.ERROR, _("You cannot edit this dressing.")) raise PermissionDenied @@ -110,14 +101,14 @@ def dressing_edit(request, dressing_id): ) form_dressing.save() return redirect(reverse("dressing:my_dressings")) - page_title = f'{_("Editing the dressing")} "{dressing_edit.title}"' + page_title = _("Edit the dressing “%s”") % dressing_edit.title return render( request, "dressing_edit.html", { + "page_title": page_title, "dressing_edit": dressing_edit, "form": form_dressing, - "page_title": page_title, }, ) @@ -126,11 +117,7 @@ def dressing_edit(request, dressing_id): @login_required(redirect_field_name="referrer") def dressing_create(request): """Create a dressing object.""" - if not ( - request.user.is_superuser - or request.user.is_staff - or request.user.has_perm("dressing.my_dressings") - ): + if not (request.user.is_superuser or request.user.is_staff): messages.add_message( request, messages.ERROR, _("You cannot create a video dressing.") ) @@ -148,8 +135,8 @@ def dressing_create(request): request, "dressing_edit.html", { - "dressing_create": dressing_create, "page_title": _("Create a new dressing"), + "dressing_create": dressing_create, "form": form_dressing, }, ) @@ -160,14 +147,10 @@ def dressing_create(request): def dressing_delete(request, dressing_id): """Delete the dressing identified by 'id'.""" dressing = get_object_or_404(Dressing, id=dressing_id) + if in_maintenance(): + return redirect(reverse("maintenance")) - if dressing and ( - not ( - request.user.is_superuser - or request.user.is_staff - or request.user.has_perm("dressing.dressing_delete") - ) - ): + if dressing and (not (request.user.is_superuser or request.user.is_staff)): messages.add_message( request, messages.ERROR, _("You cannot delete this dressing.") ) @@ -194,9 +177,9 @@ def dressing_delete(request, dressing_id): request, "dressing_delete.html", { + "page_title": _("Deleting the dressing “%s”") % dressing.title, "dressing": dressing, "form": form, - "page_title": _("Deleting the dressing “%s”") % dressing.title, }, ) @@ -205,19 +188,19 @@ def dressing_delete(request, dressing_id): @login_required(redirect_field_name="referrer") def my_dressings(request): """Render the logged user's dressings.""" - if not ( - request.user.is_superuser - or request.user.is_staff - or request.user.has_perm("dressing.my_dressings") - ): - messages.add_message(request, messages.ERROR, _("You cannot access this page.")) - raise PermissionDenied if in_maintenance(): return redirect(reverse("maintenance")) - dressings = get_dressings(request.user, request.user.owner.accessgroup_set.all()) + if not (request.user.is_superuser or request.user.is_staff): + messages.add_message(request, messages.ERROR, _("You cannot access this page.")) + raise PermissionDenied + + dressings = get_dressings(request.user, request.user.owner.accessgroup_set.all()) return render( request, "my_dressings.html", - {"dressings": dressings, "page_title": _("My dressings")}, + { + "page_title": _("My dressings"), + "dressings": dressings, + }, ) diff --git a/pod/enrichment/models.py b/pod/enrichment/models.py index 6f28383cdb..1051746710 100755 --- a/pod/enrichment/models.py +++ b/pod/enrichment/models.py @@ -62,26 +62,26 @@ def enrichment_to_vtt(list_enrichment, video): videodir, created = UserFolder.objects.get_or_create( name="%s" % video.slug, owner=video.owner ) - previousEnrichmentFile = CustomFileModel.objects.filter( + previous_enrichment_file = CustomFileModel.objects.filter( name__startswith="enrichment", folder=videodir, created_by=video.owner, ) - for enr in previousEnrichmentFile: + for enr in previous_enrichment_file: enr.delete() # do it like this to delete file - enrichmentFile, created = CustomFileModel.objects.get_or_create( + enrichment_file, created = CustomFileModel.objects.get_or_create( name="enrichment", folder=videodir, created_by=video.owner ) - if enrichmentFile.file and os.path.isfile(enrichmentFile.file.path): - os.remove(enrichmentFile.file.path) + if enrichment_file.file and os.path.isfile(enrichment_file.file.path): + os.remove(enrichment_file.file.path) else: - enrichmentFile, created = CustomFileModel.objects.get_or_create() - enrichmentFile.file.save("enrichment.vtt", File(temp_vtt_file)) - enrichmentVtt, created = EnrichmentVtt.objects.get_or_create(video=video) - enrichmentVtt.src = enrichmentFile - enrichmentVtt.save() - return enrichmentFile.file.path + enrichment_file, created = CustomFileModel.objects.get_or_create() + enrichment_file.file.save("enrichment.vtt", File(temp_vtt_file)) + enrichment_vtt, created = EnrichmentVtt.objects.get_or_create(video=video) + enrichment_vtt.src = enrichment_file + enrichment_vtt.save() + return enrichment_file.file.path def enrichment_to_vtt_type(enrich): diff --git a/pod/enrichment/tests/test_views.py b/pod/enrichment/tests/test_views.py index d322c47c64..f2761bb927 100644 --- a/pod/enrichment/tests/test_views.py +++ b/pod/enrichment/tests/test_views.py @@ -1,8 +1,6 @@ # gitguardian:ignore -""" -Unit tests for enrichment views. -""" +"""Esup-Pod unit tests for enrichment views.""" from django.test import TestCase from django.contrib.auth import authenticate from django.contrib.auth.models import User diff --git a/pod/enrichment/views.py b/pod/enrichment/views.py index a09f81572d..0c91e45a96 100644 --- a/pod/enrichment/views.py +++ b/pod/enrichment/views.py @@ -256,6 +256,7 @@ def video_enrichment( slug (`str`): The video slug. slug_c (`str`): The channel slug. slug_t (`str`): The theme slug. + slug_private (`str`): The private slug. Returns: :class:`django.http.HttpResponse`: The HTTP response. diff --git a/pod/import_video/templates/import_video/list.html b/pod/import_video/templates/import_video/list.html index 14d5434ed1..5fd5c533f3 100644 --- a/pod/import_video/templates/import_video/list.html +++ b/pod/import_video/templates/import_video/list.html @@ -149,7 +149,7 @@ }); /** - * Manage the upload To Pod : confirm and loader + * Manage the upload To Pod: confirm and loader */ function confirmUploadToPod(message) { const answer=confirm(message); diff --git a/pod/import_video/tests/test_models.py b/pod/import_video/tests/test_models.py index 668b9bd3f1..1e2424f597 100644 --- a/pod/import_video/tests/test_models.py +++ b/pod/import_video/tests/test_models.py @@ -55,10 +55,10 @@ def test_change_attributs(self): """Change attributs values in a recording and save it.""" recording1 = ExternalRecording.objects.get(id=1) self.assertEqual(recording1.name, "test recording1") - recording1.name = "New test recording1 !" + recording1.name = "New test recording1!" recording1.save() newrecording1 = ExternalRecording.objects.get(id=1) - self.assertEqual(newrecording1.name, "New test recording1 !") + self.assertEqual(newrecording1.name, "New test recording1!") def test_delete_object(self): """Delete a recording.""" diff --git a/pod/import_video/utils.py b/pod/import_video/utils.py index 2dc4e46b1f..efd895a7c2 100644 --- a/pod/import_video/utils.py +++ b/pod/import_video/utils.py @@ -79,7 +79,7 @@ def parse_remote_file(session: Session, source_html_url: str): """Parse the remote HTML file on the BBB server. Args: - session (Session) : session useful to achieve requests (and keep cookies between) + session (Session): session useful to achieve requests (and keep cookies between) source_html_url (String): URL to parse Raises: @@ -189,7 +189,7 @@ def manage_download( ) -> str: """Manage the download of a BBB video file. - 2 possibilities : + 2 possibilities: - Download BBB video file directly. - Download BBB video file where source URL is protected by a single-use token. In such a case, 2 requests are made, using the same session. @@ -200,13 +200,13 @@ def manage_download( and download_video_file. Args: - session (Session) : Session useful to achieve requests (and keep cookies between) + session (Session): Session useful to achieve requests (and keep cookies between) source_url (String): Source file URL video_file_add (String): Name of the video file to add to the URL dest_file (String): Destination file of the Pod video Returns: - source_video_url (String) : video source file URL + source_video_url (String): video source file URL Raises: ValueError: if impossible download @@ -223,7 +223,7 @@ def download_video_file(session: Session, source_video_url: str, dest_file: str) """Download video file. Args: - session (Session) : Session useful to achieve requests (and keep cookies between) + session (Session): Session useful to achieve requests (and keep cookies between) source_video_url (String): Video file URL dest_file (String): Destination file of the Pod video @@ -244,12 +244,12 @@ def download_video_file(session: Session, source_video_url: str, dest_file: str) # Total size, in bytes, from response header # total_size = int(response.headers.get('content-length', 0)) # Other possible methods - # Method 1 : iterate over every chunk and calculate % of total + # Method 1: iterate over every chunk and calculate % of total # for chunk in response.iter_content(chunk_size=1024*1024): # file.write(chunk) - # Method 2 : Binary download + # Method 2: Binary download # file.write(response.content) - # Method 3 : The fastest + # Method 3: The fastest shutil.copyfileobj(response.raw, file) except Exception as exc: raise ValueError(mark_safe(str(exc))) @@ -359,11 +359,11 @@ def check_video_size(video_size: int): def check_source_url(source_url: str): # noqa: C901 """Check the source URL to identify the used platform. - Platforms managed : - - Mediacad platform (Médiathèque académique) : rewrite source URL if required + Platforms managed: + - Mediacad platform (Médiathèque académique): rewrite source URL if required and manage JSON API. - - BBB ESR infrastructure : rewrite source URL if required - - Old BigBlueButton presentation : source URL for old BBB presentation playback + - BBB ESR infrastructure: rewrite source URL if required + - Old BigBlueButton presentation: source URL for old BBB presentation playback """ base_url = "" media_id = "" @@ -437,7 +437,7 @@ def check_source_url(source_url: str): # noqa: C901 format = "m4v" platform = "BBB_ESR" elif source_url.find("bbb.numerique-esr.fr/playback/presentation/2.3/") != -1: - # BBB ESR presentation link : rewrite for video source URL + # BBB ESR presentation link: rewrite for video source URL # https://univ.scalelite.bbb.numerique-esr.fr/playback/presentation/2.3/#id# source_video_url = ( source_url.replace("/playback/presentation/2.3/", "/video/") + "/" @@ -445,7 +445,7 @@ def check_source_url(source_url: str): # noqa: C901 format = "m4v" platform = "BBB_ESR" elif source_url.find("bbb.numerique-esr.fr/recording/") != -1: - # BBB ESR video or presentation link : rewrite for video source URL if pres + # BBB ESR video or presentation link: rewrite for video source URL if pres # https://univ.scalelite.bbb.numerique-esr.fr/recording/#id#/presentation? # https://univ.scalelite.bbb.numerique-esr.fr/recording/#id#/video? source_video_url = source_url.replace("/presentation", "/video") @@ -463,14 +463,14 @@ def check_source_url(source_url: str): # noqa: C901 format = "webm" platform = "BBB_Presentation" elif source_url.find("/playback/presentation/2.3/") != -1: - # Old BBB 2.3 presentation link : no conversion needed + # Old BBB 2.3 presentation link: no conversion needed source_video_url = source_url format = "webm" platform = "BBB_Presentation" # Platform's URL identified if platform == "Mediacad": - # Mediacad API (JSON format) is available at : + # Mediacad API (JSON format) is available at: # ##mediacadBaseUrl##/default/media/display/m/##mediaId##/d/j url_api_video = "%s/default/media/display/m/%s/d/j" % (base_url, media_id) # Platform type @@ -546,7 +546,7 @@ def check_url_need_token(source_url: str) -> str: """Check if the URL is used by an infrastructure that need a token. Useful for generating the single-use token required to access video file. - 2 conditions : + 2 conditions: - URL was created on the same BBB infrastructure as the meeting module, - At present, only ESR's BBB infrastructure uses single-use tokens. If both conditions are met, then returns the recording_id. @@ -651,9 +651,9 @@ def handle_starttag(self, tag, attrs): attrs = dict(attrs) # Search for source tag if tag == "source": - # Found the line. Managed format : + # Found the line. Managed format: # attrs = {'src': 'video-0.m4v', 'type': 'video/mp4'} - # print("video line : %s" % attrs) + # print("video line: %s" % attrs) self.video_check = True self.video_file = attrs.get("src", "") self.video_type = attrs.get("type", "") @@ -673,7 +673,7 @@ def handle_data(self, data): class StatelessRecording: """Recording model, not saved in database. - Useful to manage : + Useful to manage: - internal (meeting module) recordings - external (import_video module) recordings for the views. diff --git a/pod/import_video/views.py b/pod/import_video/views.py index 55fdde4a0a..1e045a27a0 100644 --- a/pod/import_video/views.py +++ b/pod/import_video/views.py @@ -1,4 +1,5 @@ """Esup-Pod import_video views. + More information on this module at: https://www.esup-portail.org/wiki/x/BQCnSw """ @@ -286,7 +287,7 @@ def add_or_edit_external_recording(request, id=None): if form.is_valid(): recording = save_recording_form(request, form) display_message_with_icon( - request, messages.INFO, _("The changes have been saved.") + request, messages.SUCCESS, _("The changes have been saved.") ) return redirect(reverse("import_video:external_recordings")) else: @@ -347,7 +348,7 @@ def delete_external_recording(request, id: int): msg += args["message"] if delete and msg == "": msg += _("The external recording has been deleted.") - display_message_with_icon(request, messages.INFO, msg) + display_message_with_icon(request, messages.SUCCESS, msg) else: display_message_with_icon(request, messages.ERROR, msg) return redirect(reverse("import_video:external_recordings", args=())) @@ -457,7 +458,7 @@ def upload_video_recording_to_pod(request, record_id: int): # noqa: C901 # Mediacad platform return upload_mediacad_recording_to_pod(request, record_id, type_source_url) elif type_source_url is not None and type_source_url.type == "BBB_Presentation": - # Old BigBlueBlutton playback (presentation format) : + # Old BigBlueBlutton playback (presentation format): # convert this presentation in video and upload automatically to Pod # via an asynchronous task if USE_IMPORT_VIDEO_BBB_RECORDER: @@ -493,6 +494,7 @@ def upload_video_recording_to_pod(request, record_id: int): # noqa: C901 def upload_standard_video_recording_to_pod(record_id: int) -> bool: """Upload a standard video file (or BBB video file) recording to Pod. + Used with an URL. Args: @@ -607,6 +609,7 @@ def upload_bbb_esr_video_recording_to_pod(record_id: int, source_url: str) -> bo def upload_local_video_recording_to_pod(record_id: id, dest_file: str, dest_path: str): """Upload a local (typically in Pod filesystem) video file recording to Pod. + Useful for video files that have been encoded following the recording of a BBB presentation. @@ -695,7 +698,7 @@ def upload_mediacad_recording_to_pod( def get_mediacad_api_description(type_source_url: TypeSourceURL) -> str: - """Returns description of a Mediacad video, after a call to Mediacad JSON API. + """Return description of a Mediacad video, after a call to Mediacad JSON API. Args: type_source_url (TypeSourceURL): informations about source URL @@ -936,7 +939,8 @@ def upload_peertube_recording_to_pod(request, record_id: int) -> bool: # noqa: def start_bbb_encode_presentation_and_upload_to_pod( record_id: int, url: str, extension: str ): - """Send an asynchronous task or a thread to encode a BBB presentation + """Send an asynchronous task or a thread to encode a BBB presentation. + into a video file and upload it to Pod. With Celery, logs can be found in encoding servers, worker.log. @@ -962,7 +966,8 @@ def start_bbb_encode_presentation_and_upload_to_pod( def start_bbb_encode_presentation_and_move_to_destination( filename: str, url: str, dest_file: str ): - """Send an asynchronous task to encode or encode direclty a BBB presentation + """Send an asynchronous task to encode or encode direclty a BBB presentation. + into a video file and move it to a specific directory. With Celery, logs can be found in encoding servers, worker.log. @@ -1087,7 +1092,7 @@ def bbb_encode_presentation_and_upload_to_pod(record_id: int, url: str, extensio recording.save() else: - # Video file not generated : inform the user via the recording state + # Video file not generated: inform the user via the recording state recording.state = _( "Impossible to upload to Pod the video, " "the link provided does not seem valid." @@ -1185,7 +1190,9 @@ def get_status_recording(data: ExternalRecording) -> str: @ensure_csrf_cookie @login_required(redirect_field_name="referrer") def recording_with_token(request, id): - """Get for specific recording (recording created on BBB infrastructure that need a + """Get for specific recording. + + (recording created on BBB infrastructure that need a token and used by meeting module), the presentation and video source URL, in JSON. Args: diff --git a/pod/live/management/commands/checkLiveStartStop.py b/pod/live/management/commands/checkLiveStartStop.py index 3cf941a93c..42ecf42b4d 100644 --- a/pod/live/management/commands/checkLiveStartStop.py +++ b/pod/live/management/commands/checkLiveStartStop.py @@ -1,4 +1,4 @@ -"""start or stop broadcaster recording based on live events.""" +"""Esup-Pod start or stop broadcaster recording based on live events.""" import json from datetime import datetime @@ -55,6 +55,7 @@ def handle(self, *args, **options): def stop_finished(self): """ Stop all the recording of today's already finished events but yet not stopped. + Including the non auto-started events (to be sure they are not forgotten). """ self.stdout.write("- Stopping finished events (if started with Pod) -") @@ -70,7 +71,7 @@ def stop_finished(self): for event in events: self.stdout.write( - f"Event : '{event.slug}', " f"on Broadcaster '{event.broadcaster_id}' ", + f"Event: '{event.slug}', " f"on Broadcaster '{event.broadcaster_id}' ", ending="", ) @@ -96,7 +97,8 @@ def stop_finished(self): def start_new(self): """ - Starts all recording of the current events + Start all recording of the current events. + that are auto-started configured and not stopped by manager. """ self.stdout.write("- Starting new events -") @@ -110,7 +112,7 @@ def start_new(self): for event in events: self.stdout.write( - f"Event : '{event.slug}', " f"on Broadcaster '{event.broadcaster_id}'", + f"Event: '{event.slug}', " f"on Broadcaster '{event.broadcaster_id}'", ending="", ) diff --git a/pod/live/pilotingInterface.py b/pod/live/pilotingInterface.py index 0a26135e2a..feb1919223 100644 --- a/pod/live/pilotingInterface.py +++ b/pod/live/pilotingInterface.py @@ -184,7 +184,7 @@ def validate_json_implementation(broadcaster: Broadcaster) -> bool: logger.error( "'piloting_conf' format value for '" + broadcaster.name - + "' broadcaster must be like : " + + "' broadcaster must be like: " + "{" + mandatory[:-1] + "}" @@ -233,9 +233,9 @@ def get_piloting_implementation(broadcaster) -> Optional[PilotingInterface]: if piloting_impl.lower() == "smp": logger.debug( - "piloting_implementation found : '" + "piloting_implementation found: '" + piloting_impl.lower() - + "' for broadcaster : '" + + "' for broadcaster: '" + broadcaster.name + "'" ) @@ -666,10 +666,7 @@ def copy_file_to_pod_dir(self, filename): pod_file_name = file_head_tail[1] pod_file_path = os.path.join(DEFAULT_EVENT_PATH, pod_file_name) logger.debug( - "-- try to copy from SMP : " - + smp_file_path - + " to Pod : " - + pod_file_path + "-- try to copy from SMP: " + smp_file_path + " to Pod: " + pod_file_path ) # copy from remote to local @@ -681,7 +678,7 @@ def copy_file_to_pod_dir(self, filename): sftp.close() return True except OSError as e: - logger.error("Failed to copy file over SFTP : " + str(e)) + logger.error("Failed to copy file over SFTP: " + str(e)) return False def can_manage_stream(self) -> bool: diff --git a/pod/live/templates/bbb/bbb_form.html b/pod/live/templates/bbb/bbb_form.html deleted file mode 100644 index c85e1f3c05..0000000000 --- a/pod/live/templates/bbb/bbb_form.html +++ /dev/null @@ -1,23 +0,0 @@ -{% load i18n %} - -
-
- {% csrf_token %} -
-

{% trans 'Send message' %}

- {% if user.is_authenticated %} - {% trans 'You can send a message (100 characters maximum) to the BigBlueButton session. It will be displayed within 15 to 30 seconds on the live video.' %} - - - {% else %} - {% trans 'You must be authenticated to send a message.' %} - {% endif %} -
-
-
- {% if user.is_authenticated %} - - {% endif %} -
-
\ No newline at end of file diff --git a/pod/live/templates/live/direct.html b/pod/live/templates/live/direct.html index 1aaaa50958..108fd5401f 100644 --- a/pod/live/templates/live/direct.html +++ b/pod/live/templates/live/direct.html @@ -259,46 +259,5 @@

if ($("#divvideoplayer")) { $("#divvideoplayer").css("display", "block"); } }) -{% if display_chat %} - -{% endif %} {% endblock more_script %} diff --git a/pod/live/templates/live/event-info.html b/pod/live/templates/live/event-info.html index 8bf28ec9b1..9d4f44fdbb 100644 --- a/pod/live/templates/live/event-info.html +++ b/pod/live/templates/live/event-info.html @@ -93,19 +93,16 @@

    diff --git a/pod/live/templates/meeting/meeting_live_form.html b/pod/live/templates/meeting/meeting_live_form.html new file mode 100644 index 0000000000..0356478cb1 --- /dev/null +++ b/pod/live/templates/meeting/meeting_live_form.html @@ -0,0 +1,27 @@ +{% load i18n %} + +
    +
    +

    {% trans 'Send message' %}

    + {% if user.is_authenticated %} +
    + {% csrf_token %} +
    +

    + {% trans 'You can send a message to the webinar presenters (100 characters maximum).' %} + {% trans 'It will be displayed after 10 to 30 seconds on the live stream.' %} +

    + + +
    + +
    + +
    +
    + {% else %} +
    {% trans 'You must be authenticated to send a message.' %}
    + {% endif %} +
    +
    +
    diff --git a/pod/live/tests/test_views.py b/pod/live/tests/test_views.py index 27fb990e46..c96be30b9c 100644 --- a/pod/live/tests/test_views.py +++ b/pod/live/tests/test_views.py @@ -248,7 +248,7 @@ def test_heartbeat(self): ) self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), data_no_viewers) - print(" ---> test_heartbeat broadcaster with current event (no viewer): OK !") + print(" ---> test_heartbeat broadcaster with current event (no viewer): OK!") # with event (anonymous) url_with_event = "%s?key=anonymous_key&eventid=1" % heartbeat_url @@ -532,7 +532,7 @@ def test_crossing_events(self): ) self.assertFalse(form.is_valid()) self.assertIn("An event cannot be planned in the past", form.errors["__all__"]) - print(" ---> test_crossing_events in the past: NOK !") + print(" ---> test_crossing_events in the past: NOK!") # event 1 hour before the previous and half an hour after starting # crossing the start @@ -555,7 +555,7 @@ def test_crossing_events(self): "An event is already planned at these dates", form.errors["start_date"] ) self.assertIn("Planification error.", form.errors["__all__"]) - print(" ---> test_crossing_events in the futur and crossing the start: NOK !") + print(" ---> test_crossing_events in the futur and crossing the start: NOK!") # test changing broadcaster data["broadcaster"] = 2 form = EventForm( @@ -588,7 +588,7 @@ def test_crossing_events(self): "An event is already planned at these dates", form.errors["start_date"] ) self.assertIn("Planification error.", form.errors["__all__"]) - print(" ---> test_crossing_events in the futur and crossing the start: NOK !") + print(" ---> test_crossing_events in the futur and crossing the start: NOK!") # event start during the previous and finish before # crossing inside @@ -611,7 +611,7 @@ def test_crossing_events(self): "An event is already planned at these dates", form.errors["start_date"] ) self.assertIn("Planification error.", form.errors["__all__"]) - print(" ---> test_crossing_events crossing inside: NOK !") + print(" ---> test_crossing_events crossing inside: NOK!") # event start before the previous and finish after # crossing outside @@ -634,7 +634,7 @@ def test_crossing_events(self): "An event is already planned at these dates", form.errors["start_date"] ) self.assertIn("Planification error.", form.errors["__all__"]) - print(" ---> test_crossing_events crossing outside: NOK !") + print(" ---> test_crossing_events crossing outside: NOK!") # -------------------------------------------------------------------------- print(20 * "/") @@ -683,7 +683,7 @@ def test_crossing_events(self): # for err in form.errors: # print("- ", err, form.errors[err]) self.assertIn("An event cannot be planned in the past", form.errors["__all__"]) - print(" ---> test_crossing_events in the past: NOK !") + print(" ---> test_crossing_events in the past: NOK!") # event 1 hour before the previous and half an hour after starting # crossing the start @@ -707,7 +707,7 @@ def test_crossing_events(self): "An event is already planned at these dates", form.errors["start_date"] ) self.assertIn("Planification error.", form.errors["__all__"]) - print(" ---> test_crossing_events in the futur and crossing the start: NOK !") + print(" ---> test_crossing_events in the futur and crossing the start: NOK!") # event start during the previous and finish after # crossing the end @@ -731,7 +731,7 @@ def test_crossing_events(self): "An event is already planned at these dates", form.errors["start_date"] ) self.assertIn("Planification error.", form.errors["__all__"]) - print(" ---> test_crossing_events in the futur and crossing the start: NOK !") + print(" ---> test_crossing_events in the futur and crossing the start: NOK!") # event start during the previous and finish before # crossing inside @@ -755,7 +755,7 @@ def test_crossing_events(self): "An event is already planned at these dates", form.errors["start_date"] ) self.assertIn("Planification error.", form.errors["__all__"]) - print(" ---> test_crossing_events crossing inside: NOK !") + print(" ---> test_crossing_events crossing inside: NOK!") # event start before the previous and finish after # crossing outside @@ -779,7 +779,7 @@ def test_crossing_events(self): "An event is already planned at these dates", form.errors["start_date"] ) self.assertIn("Planification error.", form.errors["__all__"]) - print(" ---> test_crossing_events crossing outside: NOK !") + print(" ---> test_crossing_events crossing outside: NOK!") print(" ---> test_crossing_events of liveViewsTestCase: OK!") @@ -789,8 +789,8 @@ def test_events(self): access_group = AccessGroup.objects.get(code_name="group1") # User not logged in response = self.client.get("/") - self.assertTemplateUsed(response, "live/events_next.html") - print(" ---> test_events of `/`: OK!") + # self.assertTemplateUsed(response, "live/events_next.html") + # print(" ---> test_events of `/`: OK!") response = self.client.get("/live/events/") self.assertTemplateUsed(response, "live/events.html") @@ -798,11 +798,11 @@ def test_events(self): response = self.client.get("/live/events/?page=100") self.assertTemplateUsed(response, "live/events.html") - print(" ---> test_events of live/events paginator empty: OK !") + print(" ---> test_events of live/events paginator empty: OK!") response = self.client.get("/live/events/?page=notint") self.assertTemplateUsed(response, "live/events.html") - print(" ---> test_events of live/events paginator not integer: OK !") + print(" ---> test_events of live/events paginator not integer: OK!") response = self.client.get("/live/my_events/") self.assertRedirects( @@ -922,11 +922,11 @@ def test_events(self): response = self.client.get("/live/my_events/?ppage=100") self.assertTemplateUsed(response, "live/my_events.html") - print(" ---> test_events of live/my_events paginator empty: OK !") + print(" ---> test_events of live/my_events paginator empty: OK!") response = self.client.get("/live/my_events/?ppage=notint") self.assertTemplateUsed(response, "live/my_events.html") - print(" ---> test_events of live/my_events paginator not integer: OK !") + print(" ---> test_events of live/my_events paginator not integer: OK!") # event draft (permission denied) self.event = Event.objects.get(title="event1") @@ -956,13 +956,13 @@ def test_events(self): self.user.owner.accessgroup_set.add(access_group, through_defaults={"site": 1}) response = self.client.get("/live/event/%s/" % self.event.slug) self.assertEqual(200, response.status_code) - print(" ---> test_events restricted access group match: OK !") + print(" ---> test_events restricted access group match: OK!") # recording buttons (only for owner) response = self.client.get("/live/event/%s/" % self.event.slug) self.assertTemplateUsed(response, "live/event.html") self.assertFalse(response.context["can_record"]) - print(" ---> test_events can_record event for not owner: OK !") + print(" ---> test_events can_record event for not owner: OK!") # wrong event id response = self.client.get("/live/event/what-ever/") @@ -999,7 +999,7 @@ def test_events(self): response = self.client.get("/live/my_events/") self.assertTemplateUsed(response, "live/my_events.html") self.assertTemplateUsed(response, "live/events_list.html") - print(" ---> test_events owner sees his event's list: OK !") + print(" ---> test_events owner sees his event's list: OK!") # user's event draft self.event.is_draft = True @@ -1007,20 +1007,20 @@ def test_events(self): self.event.password = None response = self.client.get("/live/event/%s/" % self.event.slug) self.assertTemplateUsed(response, "live/event.html") - print(" ---> test_events access of restricted event for owner: OK !") + print(" ---> test_events access of restricted event for owner: OK!") # user's event restricted self.event.is_draft = False self.event.is_restricted = True response = self.client.get("/live/event/%s/" % self.event.slug) self.assertTemplateUsed(response, "live/event.html") - print(" ---> test_events access of restricted event for owner: OK !") + print(" ---> test_events access of restricted event for owner: OK!") # user's event password self.event.password = event_pswd response = self.client.get("/live/event/%s/" % self.event.slug) self.assertTemplateUsed(response, "live/event.html") - print(" ---> test_events access of restricted event for owner: OK !") + print(" ---> test_events access of restricted event for owner: OK!") # user's event edition response = self.client.get("/live/event_edit/%s/" % self.event.slug) @@ -1032,7 +1032,7 @@ def test_events(self): response = self.client.get("/live/event/%s/" % self.event.slug) self.assertTemplateUsed(response, "live/event.html") self.assertFalse(response.context["can_record"]) - print(" ---> test_events can_record event for owner no impl broadcaster: OK !") + print(" ---> test_events can_record event for owner no impl broadcaster: OK!") br2 = Broadcaster.objects.get(id=2) self.event.broadcaster = br2 @@ -1041,7 +1041,7 @@ def test_events(self): self.assertTemplateUsed(response, "live/event.html") self.assertTrue(response.context["can_record"]) print( - " ---> test_events can_record event for owner with impl broadcaster: OK !" + " ---> test_events can_record event for owner with impl broadcaster: OK!" ) # Superuser logged in @@ -1377,7 +1377,7 @@ def response_is_recording_ok(url, request): response = is_available_to_record(broadcaster) self.assertFalse(response) - print(" ---> test misc_broadcaster is_available_to_record no impl: OK !") + print(" ---> test misc_broadcaster is_available_to_record no impl: OK!") with HTTMock(response_is_recording_ok): response = is_available_to_record(broad_with_impl) @@ -1507,7 +1507,7 @@ def response_info_current_record_ok(url, request): } response = get_info_current_record(broadcaster) self.assertEqual(response, expected_on_error) - print(" ---> test misc_broadcaster get_info_current_record no impl: OK !") + print(" ---> test misc_broadcaster get_info_current_record no impl: OK!") with HTTMock(response_info_current_record_ko): response = get_info_current_record(broad_with_impl) @@ -1579,7 +1579,7 @@ def test_immediate_event(self): status_code=302, target_status_code=302, ) - print(" ---> test test_immediate_event not logged OK !") + print(" ---> test test_immediate_event not logged OK!") # Superuser logged in self.client.force_login(self.superuser) @@ -1587,7 +1587,7 @@ def test_immediate_event(self): "/live/event_immediate_edit/%s/" % notExistingBroadcasterId ) self.assertEqual(response.status_code, 404) - print(" ---> test test_immediate_event logged event non existant OK !") + print(" ---> test test_immediate_event logged event non existant OK!") # Récup d'un broadcaster self.broadcaster = Broadcaster.objects.get(id=1) @@ -1597,13 +1597,13 @@ def test_immediate_event(self): response = self.client.get("/live/event_immediate_edit/%s/" % self.broadcaster.id) self.assertTemplateUsed(response, "live/event_edit.html") self.assertEqual(response.context["access_not_allowed"], True) - print(" ---> test test_immediate_event logged sans droit event existant OK !") + print(" ---> test test_immediate_event logged sans droit event existant OK!") # Superuser avec broadcaster existant self.client.force_login(self.superuser) response = self.client.get("/live/event_immediate_edit/%s/" % self.broadcaster.id) self.assertTemplateUsed(response, "live/event_immediate_edit.html") - print(" ---> test test_immediate_event logged event existant OK !") + print(" ---> test test_immediate_event logged event existant OK!") def test_immediate_event_form_post(self): """Test immediate event form.""" @@ -1633,7 +1633,7 @@ def test_immediate_event_form_post(self): "/live/event_immediate_edit/%s/" % self.broadcaster.id, data, format="json" ) self.assertEqual(response.status_code, HTTPStatus.FOUND) - print(" ---> test test_immediate_event_form_post valid OK !") + print(" ---> test test_immediate_event_form_post valid OK!") # same form submitted again - fail because start and end dates are the same response = self.client.post( @@ -1641,7 +1641,7 @@ def test_immediate_event_form_post(self): ) messages = list(response.context["messages"]) self.assertGreaterEqual(len(messages), 1) - print(" ---> test test_immediate_event_form_post not valid OK !") + print(" ---> test test_immediate_event_form_post not valid OK!") def test_immediate_event_maintenance(self): """Test immediate event maintenance.""" @@ -1662,7 +1662,7 @@ def test_immediate_event_maintenance(self): status_code=302, target_status_code=200, ) - print(" ---> test test_immediate_event in maintenance OK !") + print(" ---> test test_immediate_event in maintenance OK!") def test_transform_to_video(self): """Test transform event to video.""" @@ -1679,7 +1679,7 @@ def test_transform_to_video(self): self.assertEqual(response.status_code, 200) self.assertTrue("error" in json_response) self.assertEqual(json_response["error"], "implementation error") - print(" ---> test test_transform_to_video no impl OK !") + print(" ---> test test_transform_to_video no impl OK!") # FIXME: this leads to the following error "database table is locked: video_video" # # Create a temporary file for testing @@ -1690,7 +1690,7 @@ def test_transform_to_video(self): # # This should create the video # response = transform_to_video(broad_wowza, 1, infos) # self.assertEqual(response.status_code, 200) - # print(" ---> test test_transform_to_video wowza OK !") + # print(" ---> test test_transform_to_video wowza OK!") # # remove the temporary file # os.unlink(video_file_path) @@ -1700,7 +1700,7 @@ def test_transform_to_video(self): json_response = json.loads(response.content) self.assertEqual(response.status_code, 200) self.assertTrue(json_response["success"]) - print(" ---> test test_transform_to_video smp OK !") + print(" ---> test test_transform_to_video smp OK!") def test_create_video(self): """Test create_video.""" @@ -1719,7 +1719,7 @@ def test_create_video(self): # # self.assertEqual(Video.objects.count(), nbr_videos + 1) # self.assertEqual(event.videos.count(), nbr_event_videos + 1) - # print(" ---> test test_create_video smp OK !") + # print(" ---> test test_create_video smp OK!") def test_ajax_event_get_rtmp_config(self): """Test ajax_event_get_rtmp_config.""" @@ -1830,7 +1830,7 @@ def rtmp_response_data(url, request): "success": True, } self.assertEqual(response.json(), expected) - print(" ---> test ajax_event_get_rtmp_config rtmp_response_data : OK!") + print(" ---> test ajax_event_get_rtmp_config rtmp_response_data: OK!") def test_ajax_event_start_streaming(self): """Test ajax_event_start_streaming.""" diff --git a/pod/live/views.py b/pod/live/views.py index 683832ad5b..b1818de3c0 100644 --- a/pod/live/views.py +++ b/pod/live/views.py @@ -30,9 +30,8 @@ from django.utils import timezone from django.utils.translation import ugettext_lazy as _ from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect +from pod.meeting.models import Livestream from rest_framework import status - -from pod.bbb.models import Livestream from .forms import EventPasswordForm, EventForm, EventDeleteForm, EventImmediateForm from .models import ( Building, @@ -55,8 +54,8 @@ HEARTBEAT_DELAY = getattr(settings, "HEARTBEAT_DELAY", 45) -USE_BBB = getattr(settings, "USE_BBB", False) -USE_BBB_LIVE = getattr(settings, "USE_BBB_LIVE", False) +USE_MEETING = getattr(settings, "USE_MEETING", False) +USE_MEETING_WEBINAR = getattr(settings, "USE_MEETING_WEBINAR", False) DEFAULT_EVENT_PATH = getattr(settings, "DEFAULT_EVENT_PATH", "") DEFAULT_EVENT_THUMBNAIL = getattr( @@ -124,18 +123,11 @@ def direct(request, slug): "%s?%sreferrer=%s" % (settings.LOGIN_URL, iframe_param, request.get_full_path()) ) - # Search if broadcaster is used to display a BBB streaming live - # for which students can send message from this live page - display_chat = False - if USE_BBB and USE_BBB_LIVE: - livestreams_list = Livestream.objects.filter(broadcaster_id=broadcaster.id) - for livestream in livestreams_list: - display_chat = livestream.enable_chat + return render( request, "live/direct.html", { - "display_chat": display_chat, "display_event_btn": can_manage_event(request.user), "broadcaster": broadcaster, "heartbeat_delay": HEARTBEAT_DELAY, @@ -340,20 +332,20 @@ def render_event_template(request, evemnt, user_owns_event): }, ) - # Search if broadcaster is used to display a BBB streaming live + # Search if livestream is used to display a webinar streaming live # for which students can send message from this live page - display_chat = False - if USE_BBB and USE_BBB_LIVE: - livestreams_list = Livestream.objects.filter(broadcaster_id=evemnt.broadcaster_id) - for livestream in livestreams_list: - display_chat = livestream.enable_chat + enable_chat = False + if USE_MEETING and USE_MEETING_WEBINAR: + livestream = Livestream.objects.filter(event=evemnt).first() + if livestream: + enable_chat = livestream.meeting.enable_chat return render( request, template_event, { "event": evemnt, - "display_chat": display_chat, + "enable_chat": enable_chat, "can_record": ( user_owns_event and evemnt.broadcaster.piloting_implementation @@ -799,7 +791,8 @@ def ajax_event_splitrecord(request): def event_splitrecord(event_id, broadcaster_id): - """Call the split method of the broadcaster's implementation + """Call the split method of the broadcaster's implementation. + and converts the file to a Pod video (linked to the event). Returns: a JsonResponse with success state and the error (in case of failure). @@ -835,7 +828,8 @@ def ajax_event_stoprecord(request): def event_stoprecord(event_id, broadcaster_id): - """Call the stop method of the broadcaster's implementation + """Call the stop method of the broadcaster's implementation. + and converts the file to a Pod video (linked to the event). Returns: a JsonResponse with success state and the error (in case of failure). @@ -873,8 +867,9 @@ def ajax_event_info_record(request): def event_info_record(event_id, broadcaster_id): - """Return a JsonResponse with success state and : + """Return a JsonResponse with state and duration. + JsonResponse contains success state and: * the duration of the recording in seconds * or the error (in case of failure). """ @@ -1146,6 +1141,8 @@ def is_recording(broadcaster: Broadcaster, with_file_check=False) -> bool: def transform_to_video(broadcaster, event_id, current_record_info): """ + Transform current_record_info to video. + Args: broadcaster (Broadcaster): the broadcaster event_id (int): event's id @@ -1178,7 +1175,8 @@ def transform_to_video(broadcaster, event_id, current_record_info): def copy_and_transform(impl_class, event_id, current_record_info): """ - Copy the file from remote to Pod and creates the video + Copy the file from remote to Pod and creates the video. + Args: impl_class (PilotingInterface): the piloting interface of the broadcaster event_id (int): event's id diff --git a/pod/locale/fr/LC_MESSAGES/django.mo b/pod/locale/fr/LC_MESSAGES/django.mo index 877d2b8601..8e64235eba 100644 Binary files a/pod/locale/fr/LC_MESSAGES/django.mo and b/pod/locale/fr/LC_MESSAGES/django.mo differ diff --git a/pod/locale/fr/LC_MESSAGES/django.po b/pod/locale/fr/LC_MESSAGES/django.po index a4e0033ed4..85b06ae4bf 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-12 12:01+0100\n" +"POT-Creation-Date: 2024-04-29 10:28+0000\n" "PO-Revision-Date: \n" "Last-Translator: obado \n" "Language-Team: Pod Team cotech-esup-pod@esup-portail.org\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 3.4\n" +"X-Generator: Poedit 3.4.2\n" #: pod/authentication/admin.py pod/main/forms.py msgid "Email" @@ -228,7 +228,8 @@ msgstr "Fermer" #: pod/playlist/templates/playlist/add_or_edit.html #: pod/playlist/templates/playlist/delete.html #: pod/playlist/templates/playlist/protected-playlist-form.html -#: pod/playlist/views.py pod/recorder/templates/recorder/add_recording.html +#: pod/playlist/tests/test_views.py pod/playlist/views.py +#: pod/recorder/templates/recorder/add_recording.html #: pod/recorder/templates/recorder/record_delete.html pod/recorder/views.py #: pod/video/templates/channel/channel_edit.html #: pod/video/templates/videos/dashboard.html @@ -460,19 +461,19 @@ msgstr "Date de fin" msgid "End date of the live." msgstr "Date de fin du direct." -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Live not started" msgstr "Le direct n’a pas encore démarré" -#: pod/bbb/models.py pod/bbb/templates/bbb/live_card.html +#: pod/bbb/models.py pod/bbb/templates/bbb/live_card.html pod/meeting/models.py msgid "Live in progress" msgstr "Le direct est en cours" -#: pod/bbb/models.py pod/bbb/templates/bbb/live_card.html +#: pod/bbb/models.py pod/bbb/templates/bbb/live_card.html pod/meeting/models.py msgid "Live stopped" msgstr "Le direct a été arrêté" -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Live status" msgstr "Statut du direct" @@ -499,7 +500,7 @@ msgstr "Accès restreint" msgid "Is live only accessible to authenticated users?" msgstr "Le direct est-il uniquement accessible aux utilisateurs authentifiés ?" -#: pod/bbb/models.py pod/live/admin.py pod/live/models.py +#: pod/bbb/models.py pod/live/admin.py pod/live/models.py pod/meeting/models.py msgid "Broadcaster" msgstr "Diffuseur" @@ -509,11 +510,11 @@ msgstr "Diffuseur en charge de réaliser le direct." #: pod/bbb/models.py msgid "Show public chat" -msgstr "Affichage du tchat public" +msgstr "Affichage du chat public" #: pod/bbb/models.py msgid "Do you want to show the public chat in the live?" -msgstr "Souhaitez-vous montrer le tchat public en direct ?" +msgstr "Souhaitez-vous montrer le chat public en direct ?" #: pod/bbb/models.py msgid "Save meeting in dashboard" @@ -527,17 +528,17 @@ msgstr "" "Souhaitez-vous enregistrer la vidéo de cette session, à la fin du direct, " "directement dans le « Tableau de bord » ?" -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Enable chat" -msgstr "Activer le tchat" +msgstr "Activer le chat" #: pod/bbb/models.py msgid "" "Do you want a chat on the live page for students? Messages sent in this live " "page’s chat will end up in BigBlueButton’s public chat." msgstr "" -"Voulez-vous un tchat sur la page de direct pour les étudiants ? Les messages " -"envoyés dans le tchat de cette page de direct se retrouveront dans le tchat " +"Voulez-vous un chat sur la page de direct pour les étudiants ? Les messages " +"envoyés dans le chat de cette page de direct se retrouveront dans le chat " "public de BigBlueButton." #: pod/bbb/models.py @@ -546,7 +547,7 @@ msgstr "Nom d’hôte REDIS" #: pod/bbb/models.py msgid "Redis hostname, useful for chat" -msgstr "Nom d’hôte REDIS, utile pour le tchat" +msgstr "Nom d’hôte REDIS, utile pour le chat" #: pod/bbb/models.py msgid "Redis port" @@ -554,7 +555,7 @@ msgstr "Port REDIS" #: pod/bbb/models.py msgid "Redis port, useful for chat" -msgstr "Port REDIS, utile pour le tchat" +msgstr "Port REDIS, utile pour le chat" #: pod/bbb/models.py msgid "Redis channel" @@ -562,13 +563,13 @@ msgstr "Channel REDIS" #: pod/bbb/models.py msgid "Redis channel, useful for chat" -msgstr "Channel REDIS, utile pour le tchat" +msgstr "Channel REDIS, utile pour le chat" -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Livestream" msgstr "Direct" -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Livestreams" msgstr "Directs" @@ -1061,7 +1062,7 @@ msgstr "Gérer la vidéo" #: pod/chapter/templates/video_chapter.html #: pod/completion/templates/video_completion.html -#: pod/cut/templates/video_cut.html +#: pod/cut/templates/video_cut.html pod/dressing/templates/video_dressing.html msgid "Help" msgstr "Aide" @@ -1103,8 +1104,7 @@ msgstr "Veuillez corriger les erreurs." msgid "Enrich with selected subtitles" msgstr "Enrichir avec les sous-titres sélectionnés" -#: pod/completion/apps.py pod/completion/templates/video_completion.html -#: pod/completion/views.py +#: pod/completion/apps.py msgid "Video additions" msgstr "Compléments de la vidéo" @@ -1254,7 +1254,7 @@ msgstr "Vidéo" msgid "Document" msgstr "Document" -#: pod/completion/models.py +#: pod/completion/models.py pod/video/templates/videos/video-info.html msgid "Private document" msgstr "Document privé" @@ -1266,7 +1266,7 @@ msgstr "Le document est privé." msgid "Documents" msgstr "Documents" -#: pod/completion/models.py +#: pod/completion/models.py pod/completion/tests/test_models.py msgid "Please enter a document." msgstr "Veuillez joindre un document." @@ -1850,6 +1850,11 @@ msgstr "" msgid "editor" msgstr "éditeur" +#: pod/completion/views.py +#, python-format +msgid "Additions for the video “%s”" +msgstr "Compléments pour la vidéo « %s »" + #: pod/completion/views.py msgid "You cannot complement this video." msgstr "Vous ne pouvez pas compléter cette vidéo." @@ -1866,10 +1871,24 @@ msgstr "Le fichier a été sauvegardé." msgid "The file has not been saved." msgstr "Le fichier n’a pas été enregistré." +#: pod/completion/views.py +#, python-format +msgid "Add a new contributor to the video “%s”" +msgstr "Ajouter un nouveau contributeur à la vidéo « %s »" + +#: pod/completion/views.py +msgid "The contributor has been saved." +msgstr "Le contributeur a été sauvegardé." + #: pod/completion/views.py pod/podfile/views.py pod/video/views.py msgid "Please correct errors" msgstr "Veuillez corriger les erreurs" +#: pod/completion/views.py +#, python-format +msgid "Edit the contributor “%s”" +msgstr "Modifier le contributeur « %s »" + #: pod/cut/apps.py pod/cut/models.py msgid "Video cuts" msgstr "Découpes de vidéo" @@ -1888,12 +1907,28 @@ msgid "Cut the video" msgstr "Découper la vidéo" #: pod/cut/templates/video_cut.html pod/dressing/templates/video_dressing.html +#: pod/dressing/views.py #: pod/enrichment/templates/enrichment/group_enrichment.html #: pod/video/templates/videos/video_edit.html #: pod/video/templates/videos/video_page_content.html msgid "The video is currently being encoded." msgstr "La vidéo est en cours d’encodage." +#: pod/cut/templates/video_cut.html +msgid "" +"Access to cutting video has been restricted. If you want to cut videos on " +"the platform, please" +msgstr "" +"L’accès au découpage de vidéo est restreint, si vous voulez couper des " +"vidéos, veuillez" + +#: pod/cut/templates/video_cut.html pod/live/templates/live/event_edit.html +#: pod/live/templates/live/event_immediate_edit.html +#: pod/video/templates/videos/add_video.html +#: pod/video/templates/videos/video_edit.html +msgid "contact us" +msgstr "nous contacter" + #: pod/cut/templates/video_cut.html msgid "Get the start time from the video player" msgstr "Définir le temps de début à partir du lecteur vidéo" @@ -1912,6 +1947,7 @@ msgstr "Réinitialiser les temps de début et de fin à leurs valeurs précéden #: pod/cut/templates/video_cut.html pod/dressing/templates/video_dressing.html #: pod/playlist/templates/playlist/filter_aside.html +#: pod/video/templates/videos/dashboard.html #: pod/video/templates/videos/filter_aside.html msgid "Reset" msgstr "Réinitialiser" @@ -1960,14 +1996,18 @@ msgstr "" "Lors de la sauvegarde de votre découpe, un encodage est relancé pour " "remplacer l’ancien." +#: pod/cut/tests/test_views.py pod/cut/views.py +msgid "The cut was made." +msgstr "Découpe effectuée." + +#: pod/cut/tests/test_views.py +msgid "Please select values between 00:00:00 and 00:00:20." +msgstr "Veuillez renseigner des valeurs comprises entre 00:00:00 et 00:00:20." + #: pod/cut/views.py msgid "You cannot cut this video." msgstr "Vous ne pouvez pas découper cette vidéo." -#: pod/cut/views.py -msgid "The cut was made." -msgstr "Découpe effectuée." - #: pod/dressing/apps.py pod/dressing/models.py msgid "Video dressings" msgstr "Habillages de vidéos" @@ -2069,14 +2109,6 @@ msgstr "" msgid "Agreement required" msgstr "Accord requis" -#: pod/dressing/templates/dressing_edit.html -msgid "Editing the dressing" -msgstr "Éditer l’habillage" - -#: pod/dressing/templates/dressing_edit.html pod/dressing/views.py -msgid "Create a new dressing" -msgstr "Créer un nouvel habillage" - #: pod/dressing/templates/dressing_edit.html pod/import_video/forms.py #: pod/live/forms.py pod/video/forms.py msgid "General settings" @@ -2142,11 +2174,6 @@ msgstr "Aucun habillage trouvé." msgid "Add new dressing" msgstr "Ajouter un nouvel habillage" -#: pod/dressing/templates/video_dressing.html -#: pod/video/templates/videos/link_video.html -msgid "Dress the video" -msgstr "Habiller la vidéo" - #: pod/dressing/templates/video_dressing.html msgid "Pick a dressing below" msgstr "Choisissez un habillage ci-dessous" @@ -2165,6 +2192,50 @@ msgstr "Sélectionner" msgid "Apply" msgstr "Appliquer" +#: pod/dressing/templates/video_dressing.html +msgid "Dressings" +msgstr "Habillages" + +#: pod/dressing/templates/video_dressing.html +msgid "" +"Dressings are a way to customize your video. You can add a watermark, " +"opening and ending credits" +msgstr "" +"Les habillages sont un moyen de personnaliser votre vidéo. Vous pouvez " +"ajouter un filigrane, un générique de début et de fin" + +#: pod/dressing/templates/video_dressing.html +msgid "" +"A watermark is an image that will be displayed on the video. You can choose " +"the position and the opacity of the watermark." +msgstr "" +"Un filigrane est une image qui sera affichée sur la vidéo. Vous pouvez " +"choisir la position et l'opacité du filigrane." + +#: pod/dressing/templates/video_dressing.html +msgid "Credits" +msgstr "Générique" + +#: pod/dressing/templates/video_dressing.html +msgid "" +"Opening and ending credits are videos that will be displayed at the " +"beginning or at the end of the video." +msgstr "" +"Les génériques de début et de fin sont des vidéos qui s'affichent au début " +"ou à la fin de la vidéo." + +#: pod/dressing/tests/test_views.py pod/dressing/views.py +msgid "You cannot edit this dressing." +msgstr "Vous ne pouvez pas éditer cet habillage." + +#: pod/dressing/tests/test_views.py pod/dressing/views.py +msgid "The dressing has been deleted." +msgstr "L’habillage a été supprimé." + +#: pod/dressing/tests/test_views.py pod/dressing/views.py +msgid "You cannot create a video dressing." +msgstr "Vous ne pouvez pas créer d’habillage de vidéo." + #: pod/dressing/views.py msgid "You cannot dress this video." msgstr "Vous ne pouvez pas habiller cette vidéo." @@ -2174,26 +2245,23 @@ msgstr "Vous ne pouvez pas habiller cette vidéo." msgid "Dress the video “%s”" msgstr "Habiller la vidéo « %s »" -#: pod/dressing/views.py -msgid "You cannot edit this dressing." -msgstr "Vous ne pouvez pas éditer cet habillage." - #: pod/dressing/views.py pod/import_video/views.py pod/live/views.py #: pod/meeting/views.py pod/video/views.py msgid "The changes have been saved." msgstr "Les modifications ont été sauvegardées." #: pod/dressing/views.py -msgid "You cannot create a video dressing." -msgstr "Vous ne pouvez pas créer d’habillage de vidéo." +#, python-format +msgid "Edit the dressing “%s”" +msgstr "Modifier l’habillage « %s »" #: pod/dressing/views.py -msgid "You cannot delete this dressing." -msgstr "Vous ne pouvez pas supprimer cet habillage." +msgid "Create a new dressing" +msgstr "Créer un nouvel habillage" #: pod/dressing/views.py -msgid "The dressing has been deleted." -msgstr "L’habillage a été supprimé." +msgid "You cannot delete this dressing." +msgstr "Vous ne pouvez pas supprimer cet habillage." #: pod/dressing/views.py #, python-format @@ -2263,7 +2331,7 @@ msgstr "Temps de fin de l’affichage de l’enrichissement en secondes." #: pod/enrichment/models.py #: pod/enrichment/templates/enrichment/list_enrichment.html #: pod/import_video/templates/import_video/list.html pod/live/models.py -#: pod/video/forms.py pod/video/models.py +#: pod/main/models.py pod/video/forms.py pod/video/models.py #: pod/video/templates/videos/dashboard.html pod/video/views.py #: pod/video_search/templates/search/search.html msgid "Type" @@ -2651,6 +2719,7 @@ msgstr "" "complétées." #: pod/import_video/templates/import_video/add_or_edit.html +#: pod/meeting/templates/meeting/filter_aside_meeting.html msgid "Useful tips" msgstr "Conseils pratiques" @@ -2964,7 +3033,7 @@ msgid "" "solution of the French Ministry of Higher Education and Research." msgstr "" "Cette vidéo a été téléversée sur Pod et sa source est la solution de classe " -"virtuelle du Ministère Français de l'Enseignement Supérieur et de la " +"virtuelle du Ministère Français de l’Enseignement Supérieur et de la " "Recherche." #: pod/import_video/views.py @@ -3295,7 +3364,6 @@ msgstr "" "barre d’outils." #: pod/live/models.py pod/meeting/models.py pod/playlist/models.py -#: pod/playlist/templates/playlist/playlist-informations-card.html #: pod/video/forms.py pod/video/models.py #: pod/video/templates/videos/dashboard.html msgid "Additional owners" @@ -3448,31 +3516,6 @@ msgstr "Signal" msgid "Heartbeats" msgstr "Signaux" -#: pod/live/templates/bbb/bbb_form.html -msgid "Send message" -msgstr "Envoyer un message" - -#: pod/live/templates/bbb/bbb_form.html -msgid "" -"You can send a message (100 characters maximum) to the BigBlueButton " -"session. It will be displayed within 15 to 30 seconds on the live video." -msgstr "" -"Vous pouvez envoyer un message (100 caractères maximum) à la session " -"BigBlueButton. Il sera affiché dans les 15 à 30 secondes sur la vidéo en " -"direct." - -#: pod/live/templates/bbb/bbb_form.html -msgid "Message" -msgstr "Message" - -#: pod/live/templates/bbb/bbb_form.html -msgid "You must be authenticated to send a message." -msgstr "Vous devez être authentifié pour envoyer un message." - -#: pod/live/templates/bbb/bbb_form.html pod/main/templates/aside.html -msgid "Submit" -msgstr "Envoyer" - #: pod/live/templates/live/direct.html #: pod/live/templates/live/event-script.html msgid "Recording in progress" @@ -3488,6 +3531,7 @@ msgid "Plan an event" msgstr "Programmer un évènement" #: pod/live/templates/live/direct.html pod/live/templates/live/events_next.html +#: pod/main/models.py pod/main/templatetags/flat_page_edito_filter.py msgid "Next events" msgstr "Prochains évènements" @@ -3552,21 +3596,6 @@ msgstr "" "Le direct n’a pas encore commencé, nouvelle tentative de lecture dans 10 " "secondes" -#: pod/live/templates/live/direct.html -#: pod/live/templates/live/event-script.html -msgid "Message sent" -msgstr "Message envoyé" - -#: pod/live/templates/live/direct.html -#: pod/live/templates/live/event-script.html -msgid "Message not sent: no broadcaster found" -msgstr "Message non envoyé: aucun diffuseur trouvé" - -#: pod/live/templates/live/direct.html -#: pod/live/templates/live/event-script.html -msgid "Message not sent: connection problem (REDIS)" -msgstr "Message non envoyé: problème de connexion (REDIS)" - #: pod/live/templates/live/directs_all.html msgid "Display all broadcasters of this building" msgstr "Afficher tous les diffuseurs de ce bâtiment" @@ -3688,21 +3717,6 @@ msgstr "Réseaux sociaux" msgid "Share on" msgstr "Partager sur" -#: pod/live/templates/live/event-info.html pod/main/templates/aside.html -#: pod/video/templates/videos/video-info.html -msgid "Share on Facebook" -msgstr "Partager sur Facebook" - -#: pod/live/templates/live/event-info.html pod/main/templates/aside.html -#: pod/video/templates/videos/video-info.html -msgid "Share on X (Twitter)" -msgstr "Partager sur X (Twitter)" - -#: pod/live/templates/live/event-info.html pod/main/templates/aside.html -#: pod/video/templates/videos/video-info.html -msgid "Share on LinkedIn" -msgstr "Partager sur LinkedIn" - #: pod/live/templates/live/event-info.html #: pod/video/templates/videos/video-info.html msgid "Share the link" @@ -3772,6 +3786,14 @@ msgstr "Impossible de fermer le flux vidéo" msgid "Recording duration" msgstr "Temps d’enregistrement" +#: pod/live/templates/live/event-script.html +msgid "Message sent" +msgstr "Message envoyé" + +#: pod/live/templates/live/event-script.html +msgid "Message not sent" +msgstr "Le message n’a pas été envoyé" + #: pod/live/templates/live/event.html pod/live/templates/live/event_delete.html #: pod/live/templates/live/event_edit.html #: pod/live/templates/live/event_immediate_edit.html pod/live/views.py @@ -3878,13 +3900,6 @@ msgstr "" "L’accès à l’ajout d’évènement est restreint. Si vous voulez ajouter des " "évènements, veuillez" -#: pod/live/templates/live/event_edit.html -#: pod/live/templates/live/event_immediate_edit.html -#: pod/video/templates/videos/add_video.html -#: pod/video/templates/videos/video_edit.html -msgid "contact us" -msgstr "nous contacter" - #: pod/live/templates/live/event_edit.html msgid "The event is currently in progress. Editing options are limited." msgstr "" @@ -4008,6 +4023,34 @@ msgstr "Évènements à venir" msgid "Past events" msgstr "Évènements passés" +#: pod/live/templates/meeting/meeting_live_form.html +msgid "Send message" +msgstr "Envoyer un message" + +#: pod/live/templates/meeting/meeting_live_form.html +msgid "" +"You can send a message to the webinar presenters (100 characters maximum)." +msgstr "" +"Vous pouvez envoyer un message aux présentateurs du webinaire (100 " +"caractères maximum)." + +#: pod/live/templates/meeting/meeting_live_form.html +msgid "It will be displayed after 10 to 30 seconds on the live stream." +msgstr "Il sera affiché après 10 à 30 secondes lors de la diffusion en direct." + +#: pod/live/templates/meeting/meeting_live_form.html +msgid "Message" +msgstr "Message" + +#: pod/live/templates/meeting/meeting_live_form.html +#: pod/main/templates/aside.html +msgid "Submit" +msgstr "Envoyer" + +#: pod/live/templates/meeting/meeting_live_form.html +msgid "You must be authenticated to send a message." +msgstr "Vous devez être authentifié pour envoyer un message." + #: pod/live/templatetags/event_tags.py msgid "QR code event’s link" msgstr "QR code pour le lien de l’évènement" @@ -4822,6 +4865,157 @@ msgstr "Onglet additionnel de chaînes" msgid "Additional channel Tabs" msgstr "Onglets additionnels de chaînes" +#: pod/main/models.py +msgid "Carousel" +msgstr "Carrousel" + +#: pod/main/models.py +msgid "Multiple carousel" +msgstr "Carrousel multiple" + +#: pod/main/models.py +msgid "Card list" +msgstr "Liste de cartes" + +#: pod/main/models.py +msgid "HTML" +msgstr "HTML" + +#: pod/main/models.py pod/video/models.py +#: pod/video_search/templates/search/search.html +msgid "Channel" +msgstr "Chaîne" + +#: pod/main/models.py pod/video/models.py +msgid "Theme" +msgstr "Thème" + +#: pod/main/models.py pod/playlist/models.py +#: pod/playlist/templates/playlist/playlist_breadcrumbs.html +#: pod/playlist/tests/test_models.py +#: pod/video/templates/videos/video_breadcrumbs.html +msgid "Playlist" +msgstr "Liste de lecture" + +#: pod/main/models.py pod/main/templatetags/flat_page_edito_filter.py +msgid "Last videos" +msgstr "Dernières vidéos" + +#: pod/main/models.py pod/main/templatetags/flat_page_edito_filter.py +msgid "Most views" +msgstr "Les plus vues" + +#: pod/main/models.py +msgid "Order" +msgstr "Ordre" + +#: pod/main/models.py pod/video/models.py +msgid "Visible" +msgstr "Visible" + +#: pod/main/models.py +msgid "Check this box if block is visible in page." +msgstr "Cocher cette case si vous voulez voir le bloc sur la page." + +#: pod/main/models.py +msgid "Data type" +msgstr "Type de données" + +#: pod/main/models.py +msgid "Select the channel you want to link with." +msgstr "Sélectionner la chaîne que vous voulez joindre." + +#: pod/main/models.py +msgid "Select the theme you want to link with." +msgstr "Sélectionner le thème que vous voulez joindre." + +#: pod/main/models.py +msgid "Select the playlist you want to link with." +msgstr "Sélectionner la liste de lecture que vous voulez joindre." + +#: pod/main/models.py +msgid "Write in html inside this field." +msgstr "Écrire en html à l’intérieur de ce champ." + +#: pod/main/models.py +msgid "Display title" +msgstr "Afficher le titre" + +#: pod/main/models.py +msgid "No cache" +msgstr "Pas de cache" + +#: pod/main/models.py +msgid "Check this box if you don’t want to keep the cache." +msgstr "Cocher cette case si vous ne voulez pas garder le cache." + +#: pod/main/models.py +msgid "Debug" +msgstr "Debug" + +#: pod/main/models.py +msgid "Check this box if you want to activate debug mode." +msgstr "Cocher cette case si vous voulez activer le mode débogage." + +#: pod/main/models.py +msgid "Show restricted content" +msgstr "Montrer le contenu restreint" + +#: pod/main/models.py +msgid "Check this box if you want to show restricted content." +msgstr "Cocher cette case si vous voulez montrer le contenu restreint." + +#: pod/main/models.py +msgid "Must be authenticated" +msgstr "Doit être authentifié" + +#: pod/main/models.py +msgid "Check this box if users must be authenticated to view content." +msgstr "" +"Cochez cette case si les utilisateurs doivent être authentifiés pour voir le " +"contenu." + +#: pod/main/models.py +msgid "Auto slide" +msgstr "Défilement automatique" + +#: pod/main/models.py +msgid "Check this box if you want auto slide." +msgstr "Cocher cette case si vous voulez activer le défilement automatique." + +#: pod/main/models.py +msgid "Maximum number of element" +msgstr "Nombre d’éléments maximum" + +#: pod/main/models.py +msgid "Number of element per page (multi carousel)" +msgstr "Nombre d’éléments par page (carrousel multiple)" + +#: pod/main/models.py +msgid "View videos from non visible channel" +msgstr "Voir les vidéos d’une chaine non visible" + +#: pod/main/models.py +msgid "Check this box if you want view videos from non visible channel." +msgstr "" +"Cocher cette case si vous voulez voir les vidéos d’une chaine non visible." + +#: pod/main/models.py +msgid "View videos with password" +msgstr "Voir les vidéos avec un mot de passe" + +#: pod/main/models.py +msgid "Check this box if you want view videos with password." +msgstr "Cocher cette case si vous voulez voir les vidéos avec un mot de passe." + +#: pod/main/models.py +msgid "Block" +msgstr "Bloc" + +#: pod/main/models.py +msgid "Blocks" +msgstr "Blocs" + #: pod/main/templates/403.html msgid "Permission denied" msgstr "Accès refusé" @@ -4905,6 +5099,27 @@ msgstr "En savoir plus" msgid "I understand" msgstr "J’ai compris" +#: pod/main/templates/block/card_list.html +#: pod/main/templates/block/carousel.html +#: pod/main/templates/block/multi_carousel.html +msgid "Show all videos" +msgstr "Afficher toutes les vidéos" + +#: pod/main/templates/block/carousel.html +#, python-format +msgid "Video %(video_number)s" +msgstr "Vidéo %(video_number)s" + +#: pod/main/templates/block/carousel.html +#: pod/main/templates/block/multi_carousel.html +msgid "Previous thumbnail" +msgstr "Vignette précédente" + +#: pod/main/templates/block/carousel.html +#: pod/main/templates/block/multi_carousel.html +msgid "Next thumbnail" +msgstr "Vignette suivante" + #: pod/main/templates/contact_us.html msgid "Your message" msgstr "Votre message" @@ -5216,6 +5431,22 @@ msgstr "rejoindre" msgid "Recurring" msgstr "Récurrence" +#: pod/meeting/admin.py pod/meeting/forms.py +msgid "Webinar options" +msgstr "Options du webinaire" + +#: pod/meeting/admin.py +msgid "Mode insert, nothing to display" +msgstr "Mode insertion, rien à afficher" + +#: pod/meeting/admin.py +msgid "Moderators" +msgstr "Modérateurs" + +#: pod/meeting/admin.py +msgid "Viewers" +msgstr "Spectateurs" + #: pod/meeting/forms.py pod/video/feeds.py pod/video/models.py #: pod/video/templates/videos/video_row_select.html #: pod/video/templates/videos/video_sort_select.html @@ -5520,6 +5751,26 @@ msgstr "" "Autoriser l’utilisateur à arrêter/démarrer l’enregistrement. (vrai par " "défaut)" +#: pod/meeting/models.py +msgid "Always accept" +msgstr "Toujours accepter" + +#: pod/meeting/models.py +msgid "Always deny" +msgstr "Toujours refuser" + +#: pod/meeting/models.py +msgid "Ask moderator" +msgstr "Demander au modérateur" + +#: pod/meeting/models.py +msgid "Guest policy" +msgstr "Politique à l’égard des invités" + +#: pod/meeting/models.py +msgid "Will set the guest policy for the meeting." +msgstr "Fixera la politique des invités pour la réunion." + #: pod/meeting/models.py msgid "Disable Camera" msgstr "Désactiver les caméras" @@ -5590,12 +5841,38 @@ msgstr "Salle de réunion personnelle" #: pod/meeting/models.py msgid "" -"If this box is checked, this meeting corresponds to the user's personal " +"If this box is checked, this meeting corresponds to the user’s personal " "meeting room." msgstr "" "Si cette case est cochée, cette réunion correspond à la salle de réunion " "personnelle de l’utilisateur." +#: pod/meeting/models.py +msgid "Webinar mode" +msgstr "Mode webinaire" + +#: pod/meeting/models.py +msgid "" +"Do you want to start this meeting as a webinar? In such a case, you can " +"invite presenters to join you in BigBlueButton, and listeners will have " +"direct access to a livestream in the livestreams page." +msgstr "" +"Souhaitez-vous commencer cette réunion sous la forme d’un webinaire ? Dans " +"ce cas, vous pouvez inviter les présentateurs à se joindre à vous dans " +"BigBlueButton, et les auditeurs auront un accès via un direct dans la page " +"des directs." + +#: pod/meeting/models.py +msgid "" +"Do you want a chat on the live page for listeners? Messages sent in this " +"live page's chat will end up in BigBlueButton's public chat. This public " +"chat will be also displayed in the live." +msgstr "" +"Voulez-vous un chat sur la page en direct pour les auditeurs ? Les messages " +"envoyés dans le chat de cette page en direct se retrouveront dans le chat " +"public de BigBlueButton. Cette discussion publique sera également affichée " +"en direct." + #: pod/meeting/models.py msgid "" "The day of the start date of the meeting must be included in the recurrence " @@ -5616,6 +5893,27 @@ msgstr "Impossible d’obtenir les enregistrements de la réunion !" msgid "Unable to delete recording!" msgstr "Impossible de supprimer l’enregistrement !" +#: pod/meeting/models.py +msgid "meeting" +msgstr "reunion" + +#: pod/meeting/models.py +msgid "creator" +msgstr "createur" + +#: pod/meeting/models.py +#, python-format +msgid "Session of the %(meeting_name)s meeting on %(creation_date)s" +msgstr "Session de la reunion %(meeting_name)s le %(creation_date)s" + +#: pod/meeting/models.py +msgid "Meeting session log" +msgstr "Journal des sessions des réunions" + +#: pod/meeting/models.py +msgid "Meeting session logs" +msgstr "Journaux des sessions des réunions" + #: pod/meeting/models.py msgid "Recording ID" msgstr "Identifiant de l’enregistrement" @@ -5624,6 +5922,42 @@ msgstr "Identifiant de l’enregistrement" msgid "Recordings" msgstr "Enregistrements" +#: pod/meeting/models.py +msgid "URL of the RTMP stream" +msgstr "Adresse URL du flux RTMP" + +#: pod/meeting/models.py +msgid "Example format: rtmp://live.univ.fr/live/name" +msgstr "Exemple de format : rtmp://live.univ.fr/live/name" + +#: pod/meeting/models.py +msgid "Broadcaster in charge to perform lives." +msgstr "Diffuseur chargé de réaliser des directs." + +#: pod/meeting/models.py +msgid "Live gateway" +msgstr "Passerelle de live" + +#: pod/meeting/models.py +msgid "Live gateways" +msgstr "Passerelles de live" + +#: pod/meeting/models.py +msgid "Event managed for this live" +msgstr "Gestion de l’événement pour ce direct" + +#: pod/meeting/models.py +msgid "Live event for this livestream" +msgstr "Événement en direct pour cette diffusion" + +#: pod/meeting/models.py +msgid "Live gateway used for this live" +msgstr "Passerelle de live utilisé pour ce direct" + +#: pod/meeting/models.py +msgid "Live gateway (encoder and broadcaster) that perform the livestream" +msgstr "Passerelle de live (encodeur et diffuseur) qui réalise la diffusion" + #: pod/meeting/templates/meeting/add_or_edit.html #: pod/meeting/templates/meeting/link_meeting.html pod/meeting/views.py msgid "Edit the meeting" @@ -5693,6 +6027,140 @@ msgstr "Voir mes réunions actives" msgid "See all my meetings" msgstr "Voir toutes mes réunions" +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "Informations about meetings" +msgstr "Informations sur les réunions" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"This meeting module is based on the OpenSource BigBlueButton solution." +msgstr "" +"Ce module de réunion est basé sur la solution OpenSource " +"BigBlueButton." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"This solution enables voice and video image sharing, presentations with or " +"without a whiteboard, public and private chat tools, screen sharing, voice " +"over IP, online polling and the use of office documents." +msgstr "" +"Cette solution permet le partage d’images vocales et vidéo, des " +"présentations avec ou sans tableau blanc, des outils de chat public et " +"privé, le partage d’écran, la voix sur IP, les sondages en ligne et " +"l’utilisation de documents bureautiques." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "Informations about webinars" +msgstr "Informations sur les webinaires" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"If you want to hold an online conference for a large audience (over 200 " +"users), you can use the Webinar mode, accessible from this meetings module." +msgstr "" +"Si vous souhaitez organiser une conférence en ligne pour un large public " +"(plus de 200 utilisateurs), vous pouvez utiliser le mode Webinaire, " +"accessible à partir de ce module de réunions." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"This Webinar mode enables you to transmit information to a large audience " +"via a live broadcast (accessible from the platform’s live page) and " +"interaction - if you wish - via an integrated chat." +msgstr "" +"Ce mode Webinaire vous permet de transmettre des informations à un large " +"public via une diffusion en direct (accessible depuis la page des directs de " +"la plateforme) et une interaction - si vous le souhaitez - via un chat " +"intégré." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Once you’ve saved the form, you can start the webinar by clicking on the " +"“Start the webinar” button." +msgstr "" +"Une fois le formulaire enregistré, vous pouvez démarrer le webinaire en " +"cliquant sur le bouton « Démarrer le webinaire »." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Shortly after clicking the “Start the webinar” button, the live stream will " +"be available to users on the Lives page." +msgstr "" +"Peu de temps après avoir cliqué sur le bouton « Démarrer le webinaire », le " +"direct sera disponible pour les utilisateurs sur la page Directs." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"You can invite other speakers/trainers to join you in BigBlueButton. Live " +"should only be used by listeners." +msgstr "" +"Vous pouvez inviter d’autres orateurs/formateurs à vous rejoindre dans " +"BigBlueButton. Le direct ne doit être utilisé que par les auditeurs." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Once the webinar has been created, you can modify the date and time " +"information as you wish, and the live webinar will be updated accordingly." +msgstr "" +"Une fois le webinaire créé, vous pouvez modifier la date et l’heure à votre " +"guise, et le webinaire en direct sera mis à jour en conséquence." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "This can be very useful for pre-event testing." +msgstr "" +"Cela peut s’avérer très utile pour les tests préalables aux événements." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Once started, you can access the webinar information and additional actions " +"via " +"Show webinar informations in the meetings list." +msgstr "" +"Une fois démarré, vous pouvez accéder aux informations sur le webinaire et à " +"des actions supplémentaires via Afficher les informations sur le webinaire dans la liste des réunions." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"As you have the appropriate rights, once the webinar has been created, you " +"can access additional settings for the created event via My Events in the " +"main menu." +msgstr "" +"Comme vous disposez des droits appropriés, une fois le webinaire créé, vous " +"pouvez accéder à des paramètres supplémentaires pour l’événement créé via Mes événements dans le menu principal." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "Recommendations" +msgstr "Recommandations" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "There are just a few recommendations to follow: " +msgstr "Il y a juste quelques recommandations à suivre : " + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Please do not end the meeting before it is actually over." +msgstr "" +"Veuillez ne pas terminer la réunion avant qu’elle ne soit " +"réellement terminée." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "No recurrence available for webinars." +msgstr "Aucune récurrence disponible pour les webinaires." + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "Remember to not use breakout rooms in this case." +msgstr "" +"Rappelez-vous de ne pas utiliser les salles privées dans ce " +"cas." + #: pod/meeting/templates/meeting/internal_recordings.html msgid "" "After recording a Big Blue Button meeting, recordings of that meeting will " @@ -5792,6 +6260,10 @@ msgstr "S’authentifier" msgid "Copy the direct join link" msgstr "Copiez le lien d’accès direct" +#: pod/meeting/templates/meeting/link_meeting.html +msgid "Copy the join link" +msgstr "Copiez le lien d’accès avec restriction d'accès" + #: pod/meeting/templates/meeting/link_meeting.html msgid "Invite to the meeting" msgstr "Inviter à la réunion" @@ -5812,6 +6284,10 @@ msgstr "" "Il s’agit de votre salle de réunion personnelle, une salle spécifique à " "votre profil, qui est toujours disponible." +#: pod/meeting/templates/meeting/meeting_card.html +msgid "This meeting is a webinar" +msgstr "Cette réunion est un webinaire" + #: pod/meeting/templates/meeting/meeting_card.html msgid "Access to this meeting is restricted" msgstr "L’accès à cette réunion est restreint" @@ -5820,10 +6296,22 @@ msgstr "L’accès à cette réunion est restreint" msgid "This meeting is inactive" msgstr "Cette réunion est inactive" +#: pod/meeting/templates/meeting/meeting_card.html +msgid "Show webinar informations" +msgstr "Voir les informations sur le webinaire" + +#: pod/meeting/templates/meeting/meeting_card.html +msgid "Join the webinar" +msgstr "Rejoindre le webinaire" + #: pod/meeting/templates/meeting/meeting_card.html msgid "Show meeting informations" msgstr "Voir les informations sur la réunion" +#: pod/meeting/templates/meeting/meeting_card.html +msgid "Start the webinar" +msgstr "Démarrer le webinaire" + #: pod/meeting/templates/meeting/meeting_card.html msgid "Start the meeting" msgstr "Démarrer la réunion" @@ -5930,6 +6418,16 @@ msgstr "Nombre de modérateurs" msgid "You cannot edit this meeting." msgstr "Vous ne pouvez pas éditer cette réunion." +#: pod/meeting/views.py +msgid "" +"It is not possible to hold a webinar during this period. Webinar mode has " +"been disabled for this meeting. Please try to change the period or contact " +"the administrator." +msgstr "" +"Il n’est pas possible d’organiser un webinaire pendant cette période. Le " +"mode webinaire a été désactivé pour cette réunion. Veuillez essayer de " +"modifier la période ou contacter l’administrateur." + #: pod/meeting/views.py msgid "You cannot delete this meeting." msgstr "Vous ne pouvez pas supprimer cette réunion." @@ -5950,6 +6448,14 @@ msgstr "Vous ne pouvez pas accéder à cette réunion." msgid "Password given is not correct." msgstr "Le mot de passe est incorrect." +#: pod/meeting/views.py +msgid "You cannot end this meeting." +msgstr "Vous ne pouvez pas arrêter cette réunion." + +#: pod/meeting/views.py +msgid "The meeting was successfully stopped." +msgstr "La réunion a été arrêtée avec succès." + #: pod/meeting/views.py msgid "Meeting recordings" msgstr "Enregistrements de réunion" @@ -6077,6 +6583,82 @@ msgstr "" msgid "Impossible to create the internal recording" msgstr "Impossible de créer l’enregistrement" +#: pod/meeting/views.py +msgid "You can’t end this webinar live." +msgstr "Vous ne pouvez pas terminer ce webinaire en direct." + +#: pod/meeting/views.py +msgid "You can’t restart this webinar live." +msgstr "Vous ne pouvez pas redémarrer ce webinaire en direct." + +#: pod/meeting/webinar.py +#, python-format +msgid "Webinar mode has been successfully started for “%s” meeting." +msgstr "Le mode webinaire a bien été démarré pour la réunion “%s”." + +#: pod/meeting/webinar.py +#, python-format +msgid "Error to start webinar mode for “%s” meeting: %s" +msgstr "Erreur de démarrage du mode webinaire pour la réunion “%s” : %s" + +#: pod/meeting/webinar.py +#, python-format +msgid "Webinar mode has been successfully stopped for “%s” meeting." +msgstr "Le mode webinaire a bien été arrêté pour la réunion “%s”." + +#: pod/meeting/webinar.py +#, python-format +msgid "Error to stop webinar mode for “%s” meeting: %s" +msgstr "Erreur dans l’arrêt du mode webinaire pour la réunion “%s” : %s" + +#: pod/meeting/webinar.py +msgid "" +"it is not possible to use a development server (localhost) for this " +"functionality." +msgstr "" +"il n’est pas possible d’utiliser un serveur de développement (localhost) " +"pour cette fonctionnalité." + +#: pod/meeting/webinar_utils.py +msgid "Too many webinars" +msgstr "Trop de webinaires" + +#: pod/meeting/webinar_utils.py +#, python-format +msgid "" +"There are too many webinars (%s) for the number of live gateways allocated " +"(%s). The next meeting has been created but not like a webinar:%s %s [%s-" +"%s].\n" +"Please fix the problem either by increasing the number of live gateways or " +"by modifying/deleting one of the affected webinars (with the users' " +"agreement).\n" +"Other webinars: %s" +msgstr "" +"Il y a trop de webinaires (%s) pour le nombre de passerelles de live " +"allouées (%s). La prochaine réunion a été créée mais pas comme un webinaire :" +"%s %s [%s-%s].\n" +"Veuillez résoudre le problème en augmentant le nombre de passerelles de live " +"ou en modifiant/supprimant l’un des webinaires concernés (avec l’accord des " +"utilisateurs).\n" +"Autres webinaires : %s" + +#: pod/meeting/webinar_utils.py +#, python-format +msgid "" +"

    There are too many webinars (%s) for the number of live gateways " +"allocated (%s). The next webinar has been created but not like a " +"webinar:

    • %s %s [%s-%s].

    Please fix the problem " +"either by increasing the number of live gateways or by modifying/deleting " +"one of the affected webinars (with the users' agreement).
    Other webinars: " +"%s" +msgstr "" +"

    Il y a trop de webinaires (%s) pour le nombre de passerelles live " +"allouées (%s). La prochaine réunion a été créée mais pas comme un " +"webinaire :

    • %s %s [%s-%s].

    Veuillez résoudre " +"le problème en augmentant le nombre de passerelles live ou en modifiant/" +"supprimant l’un des webinaires concernés (avec l’accord des utilisateurs)." +"
    Autres webinaires : %s" + #: pod/playlist/apps.py pod/playlist/models.py #: pod/playlist/templates/playlist/add_or_edit.html #: pod/playlist/templates/playlist/delete.html @@ -6099,7 +6681,7 @@ msgstr "Informations générales" msgid "Security informations" msgstr "Informations de sécurité" -#: pod/playlist/forms.py pod/playlist/models.py +#: pod/playlist/forms.py msgid "Please choose a title between 1 and 250 characters." msgstr "Veuillez entrer un titre contenant entre 1 et 250 caractères." @@ -6189,11 +6771,13 @@ msgid "Private" msgstr "Privé" #: pod/playlist/models.py -#: pod/playlist/templates/playlist/playlist_breadcrumbs.html -#: pod/playlist/tests/test_models.py pod/playlist/views.py -#: pod/video/templates/videos/video_breadcrumbs.html -msgid "Playlist" -msgstr "Liste de lecture" +#, python-brace-format +msgid "" +"Please choose a title between 1 and {__MAX_LENGTH_FOR_PLAYLIST_NAME__} " +"characters." +msgstr "" +"Veuillez entrer un titre contenant entre 1 et " +"{__MAX_LENGTH_FOR_PLAYLIST_NAME__} caractères." #: pod/playlist/models.py msgid "" @@ -6371,6 +6955,10 @@ msgstr "Propriétaire de la liste de lecture :" msgid "Access to playlist owner profile" msgstr "Accéder au profil du propriétaire de la liste de lecture" +#: pod/playlist/templates/playlist/playlist-informations-card.html +msgid "Additional owners:" +msgstr "Propriétaires additionnels :" + #: pod/playlist/templates/playlist/playlist-informations-card.html msgid "Access to playlist additional owner profile" msgstr "Accéder au profil du propriétaire additionnel" @@ -6569,7 +7157,12 @@ msgstr "Ce champ est requis." msgid "You cannot create a playlist named \"Favorites\"" msgstr "Vous ne pouvez créer une liste de lecture nommé \"Favoris\"" -#: pod/playlist/views.py +#: pod/playlist/tests/test_views.py pod/playlist/views.py +#, python-format +msgid "Playlist: %(name)s" +msgstr "Liste de lecture : %(name)s" + +#: pod/playlist/tests/test_views.py pod/playlist/views.py msgid "The playlist has been deleted." msgstr "La liste de lecture a été supprimée." @@ -7580,8 +8173,8 @@ msgstr "Bonjour au(x) gestionnaire(s) de %(site_title)s," #: pod/video/management/commands/check_obsolete_videos.py msgid "" -"For information, you will find below the list of video will soon arrive at " -"the deletion deadline." +"For your information, below is the list of videos that will soon reach the " +"deletion deadline." msgstr "" "Pour information, vous trouverez ci-dessous la liste des vidéos qui " "arriveront bientôt à la date limite de suppression." @@ -7693,10 +8286,6 @@ msgstr "" msgid "The style will be added to your channel to show it" msgstr "Le style sera ajouté à votre chaîne" -#: pod/video/models.py -msgid "Visible" -msgstr "Visible" - #: pod/video/models.py msgid "" "If checked, the channel appear in a list of available channels on the " @@ -7714,10 +8303,6 @@ msgstr "" msgid "Additionals channels tab" msgstr "Onglet additionnel de chaînes" -#: pod/video/models.py pod/video_search/templates/search/search.html -msgid "Channel" -msgstr "Chaîne" - #: pod/video/models.py msgid "Theme parent" msgstr "Thème parent" @@ -7735,10 +8320,6 @@ msgstr "" msgid "A theme must be in the same channel as its parent." msgstr "Un thème doit se trouver dans la même chaîne que son parent." -#: pod/video/models.py -msgid "Theme" -msgstr "Thème" - #: pod/video/models.py msgid "Icon" msgstr "Icone" @@ -7914,11 +8495,11 @@ msgstr "Catégories" #: pod/video/models.py msgid "Video access token" -msgstr "Jeton d'accès à la vidéo" +msgstr "Jeton d’accès à la vidéo" #: pod/video/models.py msgid "Video access tokens" -msgstr "Jetons d'accès à la vidéo" +msgstr "Jetons d’accès à la vidéo" #: pod/video/templates/channel/channel.html msgid "Show view statistics for all videos in this theme" @@ -8079,13 +8660,27 @@ msgstr "Mention légale" #: pod/video/templates/videos/add_video.html msgid "" -"Please note: make sure that you have the necessary authorizations signed by " -"the speakers and that you respect the Intellectual Property Code before " -"publishing a video." +"Please note: make sure that you respect the Intellectual Property Code " +"before publishing a video." +msgstr "" +"Attention : assurez-vous de respecter le code de la propriété intellectuelle " +"avant de publier une vidéo." + +#: pod/video/templates/videos/add_video.html +msgid "" +"I confirm that I have the necessary authorizations signed by the parties " +"involved in the uploaded content." msgstr "" -"Attention : assurez-vous d’être en possession des autorisations de diffusion " -"signées par les intervenants et de respecter le Code de la Propriété " -"Intellectuelle avant de publier une vidéo." +"Je confirme que je dispose des autorisations nécessaires signées par les " +"parties concernées par ce média." + +#: pod/video/templates/videos/add_video.html +msgid "Selected file:" +msgstr "Fichier sélectionné :" + +#: pod/video/templates/videos/add_video.html +msgid "Undo" +msgstr "Annuler" #: pod/video/templates/videos/add_video.html msgid "Upload progress:" @@ -8105,6 +8700,10 @@ msgstr "" "la mise en ligne, elle reprendra automatiquement quand votre connexion sera " "de nouveau disponible." +#: pod/video/templates/videos/add_video.html +msgid "Upload" +msgstr "Téléverser" + #: pod/video/templates/videos/add_video.html #, python-format msgid "The file size must be lower than %(video_max_upload_size)s Go." @@ -8134,6 +8733,20 @@ msgstr "" msgid "Help for form fields" msgstr "Aide pour les champs de formulaire" +#: pod/video/templates/videos/add_video.html +msgid "You must accept the necessary authorizations to continue." +msgstr "Vous devez accepter les autorisations nécessaires pour continuer." + +#: pod/video/templates/videos/add_video.html +msgid "Upload a media file first." +msgstr "Téléversez d’abord un média." + +#: pod/video/templates/videos/add_video.html +msgid "The file extension is not in the allowed extension:" +msgstr "" +"Cette extension de fichier n’est pas présente dans les extensions " +"autorisées :" + #: pod/video/templates/videos/card.html #: pod/video/templates/videos/card_select.html #: pod/video/templatetags/video_tags.py @@ -8272,10 +8885,6 @@ 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" - #: pod/video/templates/videos/dashboard.html msgid "" "You have not uploaded any videos yet, please use the ”Add a new video” " @@ -8286,11 +8895,11 @@ msgstr "" #: pod/video/templates/videos/dashboard.html msgid "Grid display mode" -msgstr "Mode d'affichage par grille" +msgstr "Mode d’affichage par grille" #: pod/video/templates/videos/dashboard.html msgid "List display mode" -msgstr "Mode d'affichage par liste" +msgstr "Mode d’affichage par liste" #: pod/video/templates/videos/dashboard_modal.html msgid "Loading..." @@ -8332,14 +8941,6 @@ msgstr "Éditer la catégorie" msgid "Links" msgstr "Liens" -#: pod/video/templates/videos/last_videos.html -msgid "Last videos" -msgstr "Dernières vidéos" - -#: pod/video/templates/videos/last_videos.html -msgid "Show all videos" -msgstr "Afficher toutes les vidéos" - #: pod/video/templates/videos/link_video.html #: pod/video/templates/videos/video_row_select.html msgid "Remove from playlist" @@ -8372,6 +8973,10 @@ msgstr "Compléter la vidéo" msgid "Chapter the video" msgstr "Chapitrer la vidéo" +#: pod/video/templates/videos/link_video.html +msgid "Dress the video" +msgstr "Habiller la vidéo" + #: pod/video/templates/videos/link_video.html #: pod/video/templates/videos/video_edit.html #: pod/video/templates/videos/video_row_select.html @@ -8483,6 +9088,11 @@ msgstr "" msgid "Tags:" msgstr "Mots clés :" +#: pod/video/templates/videos/video-info.html +#, python-format +msgid "Show video list having tag “%(video_tag)s”" +msgstr "Afficher la liste des vidéos ayant le tag « %(video_tag)s »" + #: pod/video/templates/videos/video-info.html msgid "No information available" msgstr "Aucune information disponible" @@ -8491,6 +9101,11 @@ msgstr "Aucune information disponible" msgid "Infos" msgstr "Informations" +#: pod/video/templates/videos/video-info.html +#, python-format +msgid "Show video list of user “%(video_user_name)s”" +msgstr "Afficher la liste des vidéos de l'utilisateur « %(video_user_name)s »" + #: pod/video/templates/videos/video-info.html msgid "Additional owner(s):" msgstr "Propriétaire(s) additionnel(s) :" @@ -8500,12 +9115,13 @@ msgid "Contributor(s):" msgstr "Contributeur(s) :" #: pod/video/templates/videos/video-info.html -msgid "send an email" -msgstr "envoyer un courriel" +#, python-format +msgid "Send an email to “%(contributor_name)s”" +msgstr "Envoyer un courriel à « %(contributor_name)s »" #: pod/video/templates/videos/video-info.html -msgid "contributor web link" -msgstr "lien du contributeur" +msgid "Contributor web link" +msgstr "Lien du contributeur" #: pod/video/templates/videos/video-info.html msgid "Updated on:" @@ -8517,6 +9133,11 @@ msgid_plural " Channels:" msgstr[0] "Chaîne :" msgstr[1] " Chaînes :" +#: pod/video/templates/videos/video-info.html +#, python-format +msgid "Show video list of type “%(type_title)s”" +msgstr "Afficher la liste des vidéos de type « %(type_title)s »" + #: pod/video/templates/videos/video-info.html msgid "Main language:" msgstr "Langue principale :" @@ -8529,6 +9150,11 @@ msgstr "Public :" msgid "Discipline(s):" msgstr "Discipline(s) :" +#: pod/video/templates/videos/video-info.html +#, python-format +msgid "Show video list of discipline “%(discipline_title)s”" +msgstr "Afficher la liste des vidéos de la discipline « %(discipline_title)s »" + #: pod/video/templates/videos/video-info.html msgid "Licence:" msgstr "Licence :" @@ -8545,15 +9171,21 @@ msgstr "Fichier audio :" msgid "Document:" msgstr "Document :" +#: pod/video/templates/videos/video-info.html +msgid "Public document" +msgstr "Document public" + +#: pod/video/templates/videos/video-info.html +msgid "Please note that your video is in draft mode." +msgstr "Veuillez noter que votre vidéo est en mode brouillon." + #: pod/video/templates/videos/video-info.html msgid "" -"Please note that your video is in draft mode.
    \n" -" The following links contain a key allowing access.\n" -" Anyone with this links can access it." +"The following links contain a key allowing access. Anyone with this links " +"can access it." msgstr "" -"Veuillez noter que votre vidéo est en mode brouillon.
    \n" -"Les liens suivants contiennent une clé permettant l’accès.\n" -"Toute personne disposant de ces liens peut y accéder." +"Les liens suivants contiennent une clé permettant l’accès. Toute personne " +"disposant de ces liens peut y accéder." #: pod/video/templates/videos/video-info.html msgid "Options" @@ -8584,9 +9216,13 @@ msgid "Check the box to indicate the beginning of playing desired." msgstr "Cocher la case pour indiquer le début de lecture souhaité." #: pod/video/templates/videos/video-info.html -#: pod/video/templates/videos/video_edit.html -msgid "Manage access tokens" -msgstr "Gestion des jetons d'accès" +msgid "Use an embed code to display the video in a web page." +msgstr "" +"Utilisez un code d'intégration pour afficher la vidéo dans une page web." + +#: pod/video/templates/videos/video-info.html +msgid "Manage integration code in access tokens page" +msgstr "Gérer le code d'intégration dans la page des jetons d'accès" #: pod/video/templates/videos/video-info.html msgid "BigBlueButton/H5P Integration" @@ -8600,6 +9236,16 @@ msgstr "" "Utilisez ces liens dans une conférence BigBlueButton ou une activité vidéo " "interactive H5P :" +#: pod/video/templates/videos/video-info.html +msgid "By sharing a link, you allow others to view the video." +msgstr "" +"En partageant un lien, vous permettez à d'autres personnes de visionner la " +"vidéo." + +#: pod/video/templates/videos/video-info.html +msgid "Manage sharing link in access tokens page" +msgstr "Gérer le lien de partage dans la page des jetons d'accès" + #: pod/video/templates/videos/video-info.html #: pod/video/templates/videos/video_access_tokens.html msgid "Use this link to share the video:" @@ -8646,8 +9292,8 @@ msgid "Video Collaborate" msgstr "Collaborer pour la vidéo" #: pod/video/templates/videos/video_collaborate.html -msgid "Tricks" -msgstr "Astuces" +msgid "Tricks:" +msgstr "Astuces :" #: pod/video/templates/videos/video_collaborate.html msgid "" @@ -8732,6 +9378,10 @@ msgstr "" msgid "Edit the %(app)s" msgstr "Éditer le %(app)s" +#: pod/video/templates/videos/video_edit.html +msgid "Manage access tokens" +msgstr "Gestion des jetons d’accès" + #: pod/video/templates/videos/video_edit.html #, python-format msgid "The file size must be lower than %(video_max_upload_size)s GB." @@ -8843,7 +9493,7 @@ msgstr "QR code pour le lien de la vidéo" #: pod/video/templatetags/video_tags.py msgid "This content is not password protected." -msgstr "Ce contenu n'est pas protégé par un mot de passe." +msgstr "Ce contenu n’est pas protégé par un mot de passe." #: pod/video/templatetags/video_tags.py msgid "This content is public." @@ -9281,8 +9931,8 @@ msgid "Encoding" msgstr "Encodage" #: pod/video_encode_transcript/utils.py -msgid "The transcripting" -msgstr "La transcription" +msgid "The transcripting of content" +msgstr "La transcription du contenu" #: pod/video_encode_transcript/utils.py #, python-format @@ -9353,45 +10003,3 @@ msgstr "Résultats de la recherche" #: pod/xapi/apps.py 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." - -#, python-format -#~ msgid "Error number: %s" -#~ msgstr "Numéro d’erreur : %s" - -#~ msgid "Playlist image" -#~ msgstr "Image de la playlist" - -#~ msgid "Live" -#~ msgstr "Directs" - -#~ msgid "Deleting the event \"%(vtitle)s\"" -#~ msgstr "Supprimer l’évènement « %(vtitle)s »" - -#~ msgid "To delete the event, please checked in and click send." -#~ msgstr "Pour supprimer l’évènement, veuillez cocher et cliquer sur envoyer." - -#~ msgid "To delete the record, please checked in and click send." -#~ msgstr "Pour supprimer la vidéo, veuillez cocher et cliquer sur envoyer." - -#~ msgid "To delete the video, please checked in and click send." -#~ msgstr "Pour supprimer la vidéo, veuillez cocher et cliquer sur envoyer." - -#~ msgid "Unable to find information about the meeting" -#~ msgstr "Impossible de trouver des informations sur la réunion" - -#~ msgid "End the meeting" -#~ msgstr "Terminer la réunion" diff --git a/pod/locale/fr/LC_MESSAGES/djangojs.mo b/pod/locale/fr/LC_MESSAGES/djangojs.mo index fecebaff64..57bf990c1e 100644 Binary files a/pod/locale/fr/LC_MESSAGES/djangojs.mo and b/pod/locale/fr/LC_MESSAGES/djangojs.mo differ diff --git a/pod/locale/fr/LC_MESSAGES/djangojs.po b/pod/locale/fr/LC_MESSAGES/djangojs.po index 3c170ededc..e28819ae98 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-12 12:01+0100\n" +"POT-Creation-Date: 2024-04-29 07:03+0000\n" "PO-Revision-Date: \n" "Last-Translator: obado \n" "Language-Team: \n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 3.4\n" +"X-Generator: Poedit 3.4.2\n" #: pod/chapter/static/js/chapters.js pod/enrichment/static/js/enrichment.js msgid "Get time from the player" @@ -501,6 +501,18 @@ msgstr "Information" msgid "Unable to find information about the meeting" msgstr "Impossible de trouver des informations sur la réunion" +#: pod/meeting/static/js/my_meetings.js +msgid "Restart only the live" +msgstr "Redémarrer seulement le direct" + +#: pod/meeting/static/js/my_meetings.js +msgid "End only the live" +msgstr "Arrêter seulement le direct" + +#: pod/meeting/static/js/my_meetings.js +msgid "End the webinar (meeting and live)" +msgstr "Terminer le webinaire (réunion et direct)" + #: pod/meeting/static/js/my_meetings.js msgid "End the meeting" msgstr "Terminer la réunion" @@ -566,6 +578,10 @@ msgstr "Changer" msgid "Enter new name of folder" msgstr "Indiquer un nouveau nom au dossier" +#: pod/podfile/static/podfile/js/filewidget.js +msgid "No user found" +msgstr "Aucun utilisateur trouvé" + #: pod/podfile/static/podfile/js/filewidget.js msgid "Remove" msgstr "Retirer" @@ -744,6 +760,12 @@ msgid_plural "Please confirm the editing of the following videos:" msgstr[0] "Veuillez confirmer l'édition de la vidéo suivante :" msgstr[1] "Veuillez confirmer l'édition des vidéos suivantes :" +#: pod/video/static/js/dashboard.js +msgid "Please confirm the deletion of the following video:" +msgid_plural "Please confirm the deletion of the following videos:" +msgstr[0] "Veuillez confirmer la suppression de la vidéo suivante :" +msgstr[1] "Veuillez confirmer la suppression des vidéos suivantes :" + #: pod/video/static/js/filter_aside_video_list_refresh.js msgid "%(count)s video found" msgid_plural "%(count)s videos found" diff --git a/pod/locale/nl/LC_MESSAGES/django.mo b/pod/locale/nl/LC_MESSAGES/django.mo index 64a2e7c9d4..a48f9d1105 100644 Binary files a/pod/locale/nl/LC_MESSAGES/django.mo and b/pod/locale/nl/LC_MESSAGES/django.mo differ diff --git a/pod/locale/nl/LC_MESSAGES/django.po b/pod/locale/nl/LC_MESSAGES/django.po index 1e3a7aecd8..07a9b75d46 100644 --- a/pod/locale/nl/LC_MESSAGES/django.po +++ b/pod/locale/nl/LC_MESSAGES/django.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-12 12:01+0100\n" -"PO-Revision-Date: 2023-06-08 14:37+0200\n" +"POT-Creation-Date: 2024-04-29 10:28+0000\n" +"PO-Revision-Date: 2024-04-15 14:27+0200\n" "Last-Translator: obado \n" "Language-Team: \n" "Language: nl_NL\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.3\n" +"X-Generator: Poedit 3.4.2\n" #: pod/authentication/admin.py pod/main/forms.py msgid "Email" @@ -224,7 +224,8 @@ msgstr "" #: pod/playlist/templates/playlist/add_or_edit.html #: pod/playlist/templates/playlist/delete.html #: pod/playlist/templates/playlist/protected-playlist-form.html -#: pod/playlist/views.py pod/recorder/templates/recorder/add_recording.html +#: pod/playlist/tests/test_views.py pod/playlist/views.py +#: pod/recorder/templates/recorder/add_recording.html #: pod/recorder/templates/recorder/record_delete.html pod/recorder/views.py #: pod/video/templates/channel/channel_edit.html #: pod/video/templates/videos/dashboard.html @@ -449,19 +450,19 @@ msgstr "" msgid "End date of the live." msgstr "" -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Live not started" msgstr "" -#: pod/bbb/models.py pod/bbb/templates/bbb/live_card.html +#: pod/bbb/models.py pod/bbb/templates/bbb/live_card.html pod/meeting/models.py msgid "Live in progress" msgstr "" -#: pod/bbb/models.py pod/bbb/templates/bbb/live_card.html +#: pod/bbb/models.py pod/bbb/templates/bbb/live_card.html pod/meeting/models.py msgid "Live stopped" msgstr "" -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Live status" msgstr "" @@ -487,7 +488,7 @@ msgstr "" msgid "Is live only accessible to authenticated users?" msgstr "" -#: pod/bbb/models.py pod/live/admin.py pod/live/models.py +#: pod/bbb/models.py pod/live/admin.py pod/live/models.py pod/meeting/models.py msgid "Broadcaster" msgstr "" @@ -513,7 +514,7 @@ msgid "" "directly in “Dashboard”?" msgstr "" -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Enable chat" msgstr "" @@ -547,11 +548,11 @@ msgstr "" msgid "Redis channel, useful for chat" msgstr "" -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Livestream" msgstr "" -#: pod/bbb/models.py +#: pod/bbb/models.py pod/meeting/models.py msgid "Livestreams" msgstr "" @@ -1004,7 +1005,7 @@ msgstr "" #: pod/chapter/templates/video_chapter.html #: pod/completion/templates/video_completion.html -#: pod/cut/templates/video_cut.html +#: pod/cut/templates/video_cut.html pod/dressing/templates/video_dressing.html msgid "Help" msgstr "" @@ -1040,8 +1041,7 @@ msgstr "" msgid "Enrich with selected subtitles" msgstr "" -#: pod/completion/apps.py pod/completion/templates/video_completion.html -#: pod/completion/views.py +#: pod/completion/apps.py msgid "Video additions" msgstr "" @@ -1189,7 +1189,7 @@ msgstr "" msgid "Document" msgstr "" -#: pod/completion/models.py +#: pod/completion/models.py pod/video/templates/videos/video-info.html msgid "Private document" msgstr "" @@ -1201,7 +1201,7 @@ msgstr "" msgid "Documents" msgstr "" -#: pod/completion/models.py +#: pod/completion/models.py pod/completion/tests/test_models.py msgid "Please enter a document." msgstr "" @@ -1760,6 +1760,11 @@ msgstr "" msgid "editor" msgstr "" +#: pod/completion/views.py +#, python-format +msgid "Additions for the video “%s”" +msgstr "" + #: pod/completion/views.py msgid "You cannot complement this video." msgstr "" @@ -1776,10 +1781,24 @@ msgstr "" msgid "The file has not been saved." msgstr "" +#: pod/completion/views.py +#, python-format +msgid "Add a new contributor to the video “%s”" +msgstr "" + +#: pod/completion/views.py +msgid "The contributor has been saved." +msgstr "" + #: pod/completion/views.py pod/podfile/views.py pod/video/views.py msgid "Please correct errors" msgstr "" +#: pod/completion/views.py +#, python-format +msgid "Edit the contributor “%s”" +msgstr "" + #: pod/cut/apps.py pod/cut/models.py msgid "Video cuts" msgstr "" @@ -1798,12 +1817,26 @@ msgid "Cut the video" msgstr "" #: pod/cut/templates/video_cut.html pod/dressing/templates/video_dressing.html +#: pod/dressing/views.py #: pod/enrichment/templates/enrichment/group_enrichment.html #: pod/video/templates/videos/video_edit.html #: pod/video/templates/videos/video_page_content.html msgid "The video is currently being encoded." msgstr "" +#: pod/cut/templates/video_cut.html +msgid "" +"Access to cutting video has been restricted. If you want to cut videos on " +"the platform, please" +msgstr "" + +#: pod/cut/templates/video_cut.html pod/live/templates/live/event_edit.html +#: pod/live/templates/live/event_immediate_edit.html +#: pod/video/templates/videos/add_video.html +#: pod/video/templates/videos/video_edit.html +msgid "contact us" +msgstr "" + #: pod/cut/templates/video_cut.html msgid "Get the start time from the video player" msgstr "" @@ -1822,6 +1855,7 @@ msgstr "" #: pod/cut/templates/video_cut.html pod/dressing/templates/video_dressing.html #: pod/playlist/templates/playlist/filter_aside.html +#: pod/video/templates/videos/dashboard.html #: pod/video/templates/videos/filter_aside.html msgid "Reset" msgstr "" @@ -1862,12 +1896,16 @@ msgstr "" msgid "When saving your cut, an encoding is restarted to replace the old one." msgstr "" -#: pod/cut/views.py -msgid "You cannot cut this video." +#: pod/cut/tests/test_views.py pod/cut/views.py +msgid "The cut was made." +msgstr "" + +#: pod/cut/tests/test_views.py +msgid "Please select values between 00:00:00 and 00:00:20." msgstr "" #: pod/cut/views.py -msgid "The cut was made." +msgid "You cannot cut this video." msgstr "" #: pod/dressing/apps.py pod/dressing/models.py @@ -1964,14 +2002,6 @@ msgstr "" msgid "Agreement required" msgstr "" -#: pod/dressing/templates/dressing_edit.html -msgid "Editing the dressing" -msgstr "" - -#: pod/dressing/templates/dressing_edit.html pod/dressing/views.py -msgid "Create a new dressing" -msgstr "" - #: pod/dressing/templates/dressing_edit.html pod/import_video/forms.py #: pod/live/forms.py pod/video/forms.py msgid "General settings" @@ -2037,11 +2067,6 @@ msgstr "" msgid "Add new dressing" msgstr "" -#: pod/dressing/templates/video_dressing.html -#: pod/video/templates/videos/link_video.html -msgid "Dress the video" -msgstr "" - #: pod/dressing/templates/video_dressing.html msgid "Pick a dressing below" msgstr "" @@ -2060,6 +2085,44 @@ msgstr "" msgid "Apply" msgstr "" +#: pod/dressing/templates/video_dressing.html +msgid "Dressings" +msgstr "" + +#: pod/dressing/templates/video_dressing.html +msgid "" +"Dressings are a way to customize your video. You can add a watermark, " +"opening and ending credits" +msgstr "" + +#: pod/dressing/templates/video_dressing.html +msgid "" +"A watermark is an image that will be displayed on the video. You can choose " +"the position and the opacity of the watermark." +msgstr "" + +#: pod/dressing/templates/video_dressing.html +msgid "Credits" +msgstr "" + +#: pod/dressing/templates/video_dressing.html +msgid "" +"Opening and ending credits are videos that will be displayed at the " +"beginning or at the end of the video." +msgstr "" + +#: pod/dressing/tests/test_views.py pod/dressing/views.py +msgid "You cannot edit this dressing." +msgstr "" + +#: pod/dressing/tests/test_views.py pod/dressing/views.py +msgid "The dressing has been deleted." +msgstr "" + +#: pod/dressing/tests/test_views.py pod/dressing/views.py +msgid "You cannot create a video dressing." +msgstr "" + #: pod/dressing/views.py msgid "You cannot dress this video." msgstr "" @@ -2069,25 +2132,22 @@ msgstr "" msgid "Dress the video “%s”" msgstr "" -#: pod/dressing/views.py -msgid "You cannot edit this dressing." -msgstr "" - #: pod/dressing/views.py pod/import_video/views.py pod/live/views.py #: pod/meeting/views.py pod/video/views.py msgid "The changes have been saved." msgstr "" #: pod/dressing/views.py -msgid "You cannot create a video dressing." +#, python-format +msgid "Edit the dressing “%s”" msgstr "" #: pod/dressing/views.py -msgid "You cannot delete this dressing." +msgid "Create a new dressing" msgstr "" #: pod/dressing/views.py -msgid "The dressing has been deleted." +msgid "You cannot delete this dressing." msgstr "" #: pod/dressing/views.py @@ -2158,7 +2218,7 @@ msgstr "" #: pod/enrichment/models.py #: pod/enrichment/templates/enrichment/list_enrichment.html #: pod/import_video/templates/import_video/list.html pod/live/models.py -#: pod/video/forms.py pod/video/models.py +#: pod/main/models.py pod/video/forms.py pod/video/models.py #: pod/video/templates/videos/dashboard.html pod/video/views.py #: pod/video_search/templates/search/search.html msgid "Type" @@ -2507,6 +2567,7 @@ msgid "An email will be sent to you when all encoding tasks are completed." msgstr "" #: pod/import_video/templates/import_video/add_or_edit.html +#: pod/meeting/templates/meeting/filter_aside_meeting.html msgid "Useful tips" msgstr "" @@ -3081,7 +3142,6 @@ msgid "" msgstr "" #: pod/live/models.py pod/meeting/models.py pod/playlist/models.py -#: pod/playlist/templates/playlist/playlist-informations-card.html #: pod/video/forms.py pod/video/models.py #: pod/video/templates/videos/dashboard.html msgid "Additional owners" @@ -3222,28 +3282,6 @@ msgstr "" msgid "Heartbeats" msgstr "" -#: pod/live/templates/bbb/bbb_form.html -msgid "Send message" -msgstr "" - -#: pod/live/templates/bbb/bbb_form.html -msgid "" -"You can send a message (100 characters maximum) to the BigBlueButton " -"session. It will be displayed within 15 to 30 seconds on the live video." -msgstr "" - -#: pod/live/templates/bbb/bbb_form.html -msgid "Message" -msgstr "" - -#: pod/live/templates/bbb/bbb_form.html -msgid "You must be authenticated to send a message." -msgstr "" - -#: pod/live/templates/bbb/bbb_form.html pod/main/templates/aside.html -msgid "Submit" -msgstr "" - #: pod/live/templates/live/direct.html #: pod/live/templates/live/event-script.html msgid "Recording in progress" @@ -3259,6 +3297,7 @@ msgid "Plan an event" msgstr "" #: pod/live/templates/live/direct.html pod/live/templates/live/events_next.html +#: pod/main/models.py pod/main/templatetags/flat_page_edito_filter.py msgid "Next events" msgstr "" @@ -3318,21 +3357,6 @@ msgstr "" msgid "Live not found, retry in 10 seconds" msgstr "" -#: pod/live/templates/live/direct.html -#: pod/live/templates/live/event-script.html -msgid "Message sent" -msgstr "" - -#: pod/live/templates/live/direct.html -#: pod/live/templates/live/event-script.html -msgid "Message not sent: no broadcaster found" -msgstr "" - -#: pod/live/templates/live/direct.html -#: pod/live/templates/live/event-script.html -msgid "Message not sent: connection problem (REDIS)" -msgstr "" - #: pod/live/templates/live/directs_all.html msgid "Display all broadcasters of this building" msgstr "" @@ -3448,21 +3472,6 @@ msgstr "" msgid "Share on" msgstr "" -#: pod/live/templates/live/event-info.html pod/main/templates/aside.html -#: pod/video/templates/videos/video-info.html -msgid "Share on Facebook" -msgstr "" - -#: pod/live/templates/live/event-info.html pod/main/templates/aside.html -#: pod/video/templates/videos/video-info.html -msgid "Share on X (Twitter)" -msgstr "" - -#: pod/live/templates/live/event-info.html pod/main/templates/aside.html -#: pod/video/templates/videos/video-info.html -msgid "Share on LinkedIn" -msgstr "" - #: pod/live/templates/live/event-info.html #: pod/video/templates/videos/video-info.html msgid "Share the link" @@ -3532,6 +3541,14 @@ msgstr "" msgid "Recording duration" msgstr "" +#: pod/live/templates/live/event-script.html +msgid "Message sent" +msgstr "" + +#: pod/live/templates/live/event-script.html +msgid "Message not sent" +msgstr "" + #: pod/live/templates/live/event.html pod/live/templates/live/event_delete.html #: pod/live/templates/live/event_edit.html #: pod/live/templates/live/event_immediate_edit.html pod/live/views.py @@ -3634,13 +3651,6 @@ msgid "" "platform, please" msgstr "" -#: pod/live/templates/live/event_edit.html -#: pod/live/templates/live/event_immediate_edit.html -#: pod/video/templates/videos/add_video.html -#: pod/video/templates/videos/video_edit.html -msgid "contact us" -msgstr "" - #: pod/live/templates/live/event_edit.html msgid "The event is currently in progress. Editing options are limited." msgstr "" @@ -3753,6 +3763,32 @@ msgstr "" msgid "Past events" msgstr "" +#: pod/live/templates/meeting/meeting_live_form.html +msgid "Send message" +msgstr "" + +#: pod/live/templates/meeting/meeting_live_form.html +msgid "" +"You can send a message to the webinar presenters (100 characters maximum)." +msgstr "" + +#: pod/live/templates/meeting/meeting_live_form.html +msgid "It will be displayed after 10 to 30 seconds on the live stream." +msgstr "" + +#: pod/live/templates/meeting/meeting_live_form.html +msgid "Message" +msgstr "" + +#: pod/live/templates/meeting/meeting_live_form.html +#: pod/main/templates/aside.html +msgid "Submit" +msgstr "" + +#: pod/live/templates/meeting/meeting_live_form.html +msgid "You must be authenticated to send a message." +msgstr "" + #: pod/live/templatetags/event_tags.py msgid "QR code event’s link" msgstr "" @@ -4563,108 +4599,277 @@ msgstr "" msgid "Additional channel Tabs" msgstr "" -#: pod/main/templates/403.html -msgid "Permission denied" +#: pod/main/models.py +msgid "Carousel" msgstr "" -#: pod/main/templates/403.html -msgid "Permission Denied" +#: pod/main/models.py +msgid "Multiple carousel" msgstr "" -#: pod/main/templates/403.html -msgid "You are not allowed to access this web page." +#: pod/main/models.py +msgid "Card list" msgstr "" -#: pod/main/templates/404.html -msgid "Page not found" +#: pod/main/models.py +msgid "HTML" msgstr "" -#: pod/main/templates/404.html -msgid "We’re sorry, but the requested page could not be found." +#: pod/main/models.py pod/video/models.py +#: pod/video_search/templates/search/search.html +msgid "Channel" msgstr "" -#: pod/main/templates/500.html -msgid "Server error (500)" +#: pod/main/models.py pod/video/models.py +msgid "Theme" msgstr "" -#: pod/main/templates/500.html -msgid "Server Error (500)" +#: pod/main/models.py pod/playlist/models.py +#: pod/playlist/templates/playlist/playlist_breadcrumbs.html +#: pod/playlist/tests/test_models.py +#: pod/video/templates/videos/video_breadcrumbs.html +msgid "Playlist" msgstr "" -#: pod/main/templates/500.html -msgid "" -"There’s been an error. It’s been reported to the site administrators via " -"email and should be fixed shortly. Thanks for your patience." +#: pod/main/models.py pod/main/templatetags/flat_page_edito_filter.py +msgid "Last videos" msgstr "" -#: pod/main/templates/admin/base_site.html -msgid "Django site admin" +#: pod/main/models.py pod/main/templatetags/flat_page_edito_filter.py +msgid "Most views" msgstr "" -#: pod/main/templates/admin/base_site.html -msgid "Django administration" +#: pod/main/models.py +msgid "Order" msgstr "" -#: pod/main/templates/aside.html -msgid "Share" +#: pod/main/models.py pod/video/models.py +msgid "Visible" msgstr "" -#: pod/main/templates/aside.html pod/recorder/models.py pod/video/forms.py -#: pod/video/models.py pod/video/templates/videos/dashboard.html -#: pod/video/templates/videos/filter_aside.html -msgid "Disciplines" +#: pod/main/models.py +msgid "Check this box if block is visible in page." msgstr "" -#: pod/main/templates/base.html -msgid "Side menu" +#: pod/main/models.py +msgid "Data type" msgstr "" -#: pod/main/templates/base.html -msgid "Toggle side menu" +#: pod/main/models.py +msgid "Select the channel you want to link with." msgstr "" -#: pod/main/templates/base.html -msgid "Breadcrumb" +#: pod/main/models.py +msgid "Select the theme you want to link with." msgstr "" -#: pod/main/templates/base.html -msgid "" -"We use third party cookies to personalize content, manage session and " -"analyze site traffic." +#: pod/main/models.py +msgid "Select the playlist you want to link with." msgstr "" -#: pod/main/templates/base.html -msgid "Learn more" +#: pod/main/models.py +msgid "Write in html inside this field." msgstr "" -#: pod/main/templates/base.html -msgid "I understand" +#: pod/main/models.py +msgid "Display title" msgstr "" -#: pod/main/templates/contact_us.html -msgid "Your message" +#: pod/main/models.py +msgid "No cache" msgstr "" -#: pod/main/templates/contact_us.html -msgid "Back to the previous page" +#: pod/main/models.py +msgid "Check this box if you don’t want to keep the cache." msgstr "" -#: pod/main/templates/footer.html -msgid "Return to top of page" +#: pod/main/models.py +msgid "Debug" msgstr "" -#: pod/main/templates/footer.html -msgid "" -"Esup Portal: Community of French higher education establishments for digital " -"innovation" +#: pod/main/models.py +msgid "Check this box if you want to activate debug mode." msgstr "" -#: pod/main/templates/footer.html -msgid "Esup Portal" +#: pod/main/models.py +msgid "Show restricted content" msgstr "" -#: pod/main/templates/footer.html -msgid "Show Esup-Pod project" +#: pod/main/models.py +msgid "Check this box if you want to show restricted content." +msgstr "" + +#: pod/main/models.py +msgid "Must be authenticated" +msgstr "" + +#: pod/main/models.py +msgid "Check this box if users must be authenticated to view content." +msgstr "" + +#: pod/main/models.py +msgid "Auto slide" +msgstr "" + +#: pod/main/models.py +msgid "Check this box if you want auto slide." +msgstr "" + +#: pod/main/models.py +msgid "Maximum number of element" +msgstr "" + +#: pod/main/models.py +msgid "Number of element per page (multi carousel)" +msgstr "" + +#: pod/main/models.py +msgid "View videos from non visible channel" +msgstr "" + +#: pod/main/models.py +msgid "Check this box if you want view videos from non visible channel." +msgstr "" + +#: pod/main/models.py +msgid "View videos with password" +msgstr "" + +#: pod/main/models.py +msgid "Check this box if you want view videos with password." +msgstr "" + +#: pod/main/models.py +msgid "Block" +msgstr "" + +#: pod/main/models.py +msgid "Blocks" +msgstr "" + +#: pod/main/templates/403.html +msgid "Permission denied" +msgstr "" + +#: pod/main/templates/403.html +msgid "Permission Denied" +msgstr "" + +#: pod/main/templates/403.html +msgid "You are not allowed to access this web page." +msgstr "" + +#: pod/main/templates/404.html +msgid "Page not found" +msgstr "" + +#: pod/main/templates/404.html +msgid "We’re sorry, but the requested page could not be found." +msgstr "" + +#: pod/main/templates/500.html +msgid "Server error (500)" +msgstr "" + +#: pod/main/templates/500.html +msgid "Server Error (500)" +msgstr "" + +#: pod/main/templates/500.html +msgid "" +"There’s been an error. It’s been reported to the site administrators via " +"email and should be fixed shortly. Thanks for your patience." +msgstr "" + +#: pod/main/templates/admin/base_site.html +msgid "Django site admin" +msgstr "" + +#: pod/main/templates/admin/base_site.html +msgid "Django administration" +msgstr "" + +#: pod/main/templates/aside.html +msgid "Share" +msgstr "" + +#: pod/main/templates/aside.html pod/recorder/models.py pod/video/forms.py +#: pod/video/models.py pod/video/templates/videos/dashboard.html +#: pod/video/templates/videos/filter_aside.html +msgid "Disciplines" +msgstr "" + +#: pod/main/templates/base.html +msgid "Side menu" +msgstr "" + +#: pod/main/templates/base.html +msgid "Toggle side menu" +msgstr "" + +#: pod/main/templates/base.html +msgid "Breadcrumb" +msgstr "" + +#: pod/main/templates/base.html +msgid "" +"We use third party cookies to personalize content, manage session and " +"analyze site traffic." +msgstr "" + +#: pod/main/templates/base.html +msgid "Learn more" +msgstr "" + +#: pod/main/templates/base.html +msgid "I understand" +msgstr "" + +#: pod/main/templates/block/card_list.html +#: pod/main/templates/block/carousel.html +#: pod/main/templates/block/multi_carousel.html +msgid "Show all videos" +msgstr "" + +#: pod/main/templates/block/carousel.html +#, python-format +msgid "Video %(video_number)s" +msgstr "" + +#: pod/main/templates/block/carousel.html +#: pod/main/templates/block/multi_carousel.html +msgid "Previous thumbnail" +msgstr "" + +#: pod/main/templates/block/carousel.html +#: pod/main/templates/block/multi_carousel.html +msgid "Next thumbnail" +msgstr "" + +#: pod/main/templates/contact_us.html +msgid "Your message" +msgstr "" + +#: pod/main/templates/contact_us.html +msgid "Back to the previous page" +msgstr "" + +#: pod/main/templates/footer.html +msgid "Return to top of page" +msgstr "" + +#: pod/main/templates/footer.html +msgid "" +"Esup Portal: Community of French higher education establishments for digital " +"innovation" +msgstr "" + +#: pod/main/templates/footer.html +msgid "Esup Portal" +msgstr "" + +#: pod/main/templates/footer.html +msgid "Show Esup-Pod project" msgstr "" #: pod/main/templates/footer.html @@ -4948,6 +5153,22 @@ msgstr "" msgid "Recurring" msgstr "" +#: pod/meeting/admin.py pod/meeting/forms.py +msgid "Webinar options" +msgstr "" + +#: pod/meeting/admin.py +msgid "Mode insert, nothing to display" +msgstr "" + +#: pod/meeting/admin.py +msgid "Moderators" +msgstr "" + +#: pod/meeting/admin.py +msgid "Viewers" +msgstr "" + #: pod/meeting/forms.py pod/video/feeds.py pod/video/models.py #: pod/video/templates/videos/video_row_select.html #: pod/video/templates/videos/video_sort_select.html @@ -5227,6 +5448,26 @@ msgstr "" msgid "Allow the user to start/stop recording. (default true)" msgstr "" +#: pod/meeting/models.py +msgid "Always accept" +msgstr "" + +#: pod/meeting/models.py +msgid "Always deny" +msgstr "" + +#: pod/meeting/models.py +msgid "Ask moderator" +msgstr "" + +#: pod/meeting/models.py +msgid "Guest policy" +msgstr "" + +#: pod/meeting/models.py +msgid "Will set the guest policy for the meeting." +msgstr "" + #: pod/meeting/models.py msgid "Disable Camera" msgstr "" @@ -5297,10 +5538,28 @@ msgstr "" #: pod/meeting/models.py msgid "" -"If this box is checked, this meeting corresponds to the user's personal " +"If this box is checked, this meeting corresponds to the user’s personal " "meeting room." msgstr "" +#: pod/meeting/models.py +msgid "Webinar mode" +msgstr "" + +#: pod/meeting/models.py +msgid "" +"Do you want to start this meeting as a webinar? In such a case, you can " +"invite presenters to join you in BigBlueButton, and listeners will have " +"direct access to a livestream in the livestreams page." +msgstr "" + +#: pod/meeting/models.py +msgid "" +"Do you want a chat on the live page for listeners? Messages sent in this " +"live page's chat will end up in BigBlueButton's public chat. This public " +"chat will be also displayed in the live." +msgstr "" + #: pod/meeting/models.py msgid "" "The day of the start date of the meeting must be included in the recurrence " @@ -5319,6 +5578,27 @@ msgstr "" msgid "Unable to delete recording!" msgstr "" +#: pod/meeting/models.py +msgid "meeting" +msgstr "" + +#: pod/meeting/models.py +msgid "creator" +msgstr "" + +#: pod/meeting/models.py +#, python-format +msgid "Session of the %(meeting_name)s meeting on %(creation_date)s" +msgstr "" + +#: pod/meeting/models.py +msgid "Meeting session log" +msgstr "" + +#: pod/meeting/models.py +msgid "Meeting session logs" +msgstr "" + #: pod/meeting/models.py msgid "Recording ID" msgstr "" @@ -5327,6 +5607,42 @@ msgstr "" msgid "Recordings" msgstr "" +#: pod/meeting/models.py +msgid "URL of the RTMP stream" +msgstr "" + +#: pod/meeting/models.py +msgid "Example format: rtmp://live.univ.fr/live/name" +msgstr "" + +#: pod/meeting/models.py +msgid "Broadcaster in charge to perform lives." +msgstr "" + +#: pod/meeting/models.py +msgid "Live gateway" +msgstr "" + +#: pod/meeting/models.py +msgid "Live gateways" +msgstr "" + +#: pod/meeting/models.py +msgid "Event managed for this live" +msgstr "" + +#: pod/meeting/models.py +msgid "Live event for this livestream" +msgstr "" + +#: pod/meeting/models.py +msgid "Live gateway used for this live" +msgstr "" + +#: pod/meeting/models.py +msgid "Live gateway (encoder and broadcaster) that perform the livestream" +msgstr "" + #: pod/meeting/templates/meeting/add_or_edit.html #: pod/meeting/templates/meeting/link_meeting.html pod/meeting/views.py msgid "Edit the meeting" @@ -5387,6 +5703,105 @@ msgstr "" msgid "See all my meetings" msgstr "" +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "Informations about meetings" +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"This meeting module is based on the OpenSource BigBlueButton solution." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"This solution enables voice and video image sharing, presentations with or " +"without a whiteboard, public and private chat tools, screen sharing, voice " +"over IP, online polling and the use of office documents." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "Informations about webinars" +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"If you want to hold an online conference for a large audience (over 200 " +"users), you can use the Webinar mode, accessible from this meetings module." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"This Webinar mode enables you to transmit information to a large audience " +"via a live broadcast (accessible from the platform’s live page) and " +"interaction - if you wish - via an integrated chat." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Once you’ve saved the form, you can start the webinar by clicking on the " +"“Start the webinar” button." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Shortly after clicking the “Start the webinar” button, the live stream will " +"be available to users on the Lives page." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"You can invite other speakers/trainers to join you in BigBlueButton. Live " +"should only be used by listeners." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Once the webinar has been created, you can modify the date and time " +"information as you wish, and the live webinar will be updated accordingly." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "This can be very useful for pre-event testing." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Once started, you can access the webinar information and additional actions " +"via " +"Show webinar informations in the meetings list." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"As you have the appropriate rights, once the webinar has been created, you " +"can access additional settings for the created event via My Events in the " +"main menu." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "Recommendations" +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "There are just a few recommendations to follow: " +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "" +"Please do not end the meeting before it is actually over." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "No recurrence available for webinars." +msgstr "" + +#: pod/meeting/templates/meeting/filter_aside_meeting.html +msgid "Remember to not use breakout rooms in this case." +msgstr "" + #: pod/meeting/templates/meeting/internal_recordings.html msgid "" "After recording a Big Blue Button meeting, recordings of that meeting will " @@ -5477,6 +5892,10 @@ msgstr "" msgid "Copy the direct join link" msgstr "" +#: pod/meeting/templates/meeting/link_meeting.html +msgid "Copy the join link" +msgstr "" + #: pod/meeting/templates/meeting/link_meeting.html msgid "Invite to the meeting" msgstr "" @@ -5495,6 +5914,10 @@ msgid "" "is always available." msgstr "" +#: pod/meeting/templates/meeting/meeting_card.html +msgid "This meeting is a webinar" +msgstr "" + #: pod/meeting/templates/meeting/meeting_card.html msgid "Access to this meeting is restricted" msgstr "" @@ -5503,10 +5926,22 @@ msgstr "" msgid "This meeting is inactive" msgstr "" +#: pod/meeting/templates/meeting/meeting_card.html +msgid "Show webinar informations" +msgstr "" + +#: pod/meeting/templates/meeting/meeting_card.html +msgid "Join the webinar" +msgstr "" + #: pod/meeting/templates/meeting/meeting_card.html msgid "Show meeting informations" msgstr "" +#: pod/meeting/templates/meeting/meeting_card.html +msgid "Start the webinar" +msgstr "" + #: pod/meeting/templates/meeting/meeting_card.html msgid "Start the meeting" msgstr "" @@ -5607,6 +6042,13 @@ msgstr "" msgid "You cannot edit this meeting." msgstr "" +#: pod/meeting/views.py +msgid "" +"It is not possible to hold a webinar during this period. Webinar mode has " +"been disabled for this meeting. Please try to change the period or contact " +"the administrator." +msgstr "" + #: pod/meeting/views.py msgid "You cannot delete this meeting." msgstr "" @@ -5627,6 +6069,14 @@ msgstr "" msgid "Password given is not correct." msgstr "" +#: pod/meeting/views.py +msgid "You cannot end this meeting." +msgstr "" + +#: pod/meeting/views.py +msgid "The meeting was successfully stopped." +msgstr "" + #: pod/meeting/views.py msgid "Meeting recordings" msgstr "" @@ -5714,6 +6164,67 @@ msgstr "" msgid "Impossible to create the internal recording" msgstr "" +#: pod/meeting/views.py +msgid "You can’t end this webinar live." +msgstr "" + +#: pod/meeting/views.py +msgid "You can’t restart this webinar live." +msgstr "" + +#: pod/meeting/webinar.py +#, python-format +msgid "Webinar mode has been successfully started for “%s” meeting." +msgstr "" + +#: pod/meeting/webinar.py +#, python-format +msgid "Error to start webinar mode for “%s” meeting: %s" +msgstr "" + +#: pod/meeting/webinar.py +#, python-format +msgid "Webinar mode has been successfully stopped for “%s” meeting." +msgstr "" + +#: pod/meeting/webinar.py +#, python-format +msgid "Error to stop webinar mode for “%s” meeting: %s" +msgstr "" + +#: pod/meeting/webinar.py +msgid "" +"it is not possible to use a development server (localhost) for this " +"functionality." +msgstr "" + +#: pod/meeting/webinar_utils.py +msgid "Too many webinars" +msgstr "" + +#: pod/meeting/webinar_utils.py +#, python-format +msgid "" +"There are too many webinars (%s) for the number of live gateways allocated " +"(%s). The next meeting has been created but not like a webinar:%s %s [%s-" +"%s].\n" +"Please fix the problem either by increasing the number of live gateways or " +"by modifying/deleting one of the affected webinars (with the users' " +"agreement).\n" +"Other webinars: %s" +msgstr "" + +#: pod/meeting/webinar_utils.py +#, python-format +msgid "" +"

    There are too many webinars (%s) for the number of live gateways " +"allocated (%s). The next webinar has been created but not like a " +"webinar:

    • %s %s [%s-%s].

    Please fix the problem " +"either by increasing the number of live gateways or by modifying/deleting " +"one of the affected webinars (with the users' agreement).
    Other webinars: " +"%s" +msgstr "" + #: pod/playlist/apps.py pod/playlist/models.py #: pod/playlist/templates/playlist/add_or_edit.html #: pod/playlist/templates/playlist/delete.html @@ -5736,7 +6247,7 @@ msgstr "" msgid "Security informations" msgstr "" -#: pod/playlist/forms.py pod/playlist/models.py +#: pod/playlist/forms.py msgid "Please choose a title between 1 and 250 characters." msgstr "" @@ -5813,10 +6324,10 @@ msgid "Private" msgstr "" #: pod/playlist/models.py -#: pod/playlist/templates/playlist/playlist_breadcrumbs.html -#: pod/playlist/tests/test_models.py pod/playlist/views.py -#: pod/video/templates/videos/video_breadcrumbs.html -msgid "Playlist" +#, python-brace-format +msgid "" +"Please choose a title between 1 and {__MAX_LENGTH_FOR_PLAYLIST_NAME__} " +"characters." msgstr "" #: pod/playlist/models.py @@ -5981,6 +6492,10 @@ msgstr "" msgid "Access to playlist owner profile" msgstr "" +#: pod/playlist/templates/playlist/playlist-informations-card.html +msgid "Additional owners:" +msgstr "" + #: pod/playlist/templates/playlist/playlist-informations-card.html msgid "Access to playlist additional owner profile" msgstr "" @@ -6173,7 +6688,12 @@ msgstr "" msgid "You cannot create a playlist named \"Favorites\"" msgstr "" -#: pod/playlist/views.py +#: pod/playlist/tests/test_views.py pod/playlist/views.py +#, python-format +msgid "Playlist: %(name)s" +msgstr "" + +#: pod/playlist/tests/test_views.py pod/playlist/views.py msgid "The playlist has been deleted." msgstr "" @@ -7082,8 +7602,8 @@ msgstr "" #: pod/video/management/commands/check_obsolete_videos.py msgid "" -"For information, you will find below the list of video will soon arrive at " -"the deletion deadline." +"For your information, below is the list of videos that will soon reach the " +"deletion deadline." msgstr "" #: pod/video/management/commands/check_obsolete_videos.py @@ -7180,10 +7700,6 @@ msgstr "" msgid "The style will be added to your channel to show it" msgstr "" -#: pod/video/models.py -msgid "Visible" -msgstr "" - #: pod/video/models.py msgid "" "If checked, the channel appear in a list of available channels on the " @@ -7198,10 +7714,6 @@ msgstr "" msgid "Additionals channels tab" msgstr "" -#: pod/video/models.py pod/video_search/templates/search/search.html -msgid "Channel" -msgstr "" - #: pod/video/models.py msgid "Theme parent" msgstr "" @@ -7218,10 +7730,6 @@ msgstr "" msgid "A theme must be in the same channel as its parent." msgstr "" -#: pod/video/models.py -msgid "Theme" -msgstr "" - #: pod/video/models.py msgid "Icon" msgstr "" @@ -7558,9 +8066,22 @@ msgstr "" #: pod/video/templates/videos/add_video.html msgid "" -"Please note: make sure that you have the necessary authorizations signed by " -"the speakers and that you respect the Intellectual Property Code before " -"publishing a video." +"Please note: make sure that you respect the Intellectual Property Code " +"before publishing a video." +msgstr "" + +#: pod/video/templates/videos/add_video.html +msgid "" +"I confirm that I have the necessary authorizations signed by the parties " +"involved in the uploaded content." +msgstr "" + +#: pod/video/templates/videos/add_video.html +msgid "Selected file:" +msgstr "" + +#: pod/video/templates/videos/add_video.html +msgid "Undo" msgstr "" #: pod/video/templates/videos/add_video.html @@ -7577,6 +8098,10 @@ msgid "" "upload, it will resume automatically when your connection is available again." msgstr "" +#: pod/video/templates/videos/add_video.html +msgid "Upload" +msgstr "" + #: pod/video/templates/videos/add_video.html #, python-format msgid "The file size must be lower than %(video_max_upload_size)s Go." @@ -7601,6 +8126,18 @@ msgstr "" msgid "Help for form fields" msgstr "" +#: pod/video/templates/videos/add_video.html +msgid "You must accept the necessary authorizations to continue." +msgstr "" + +#: pod/video/templates/videos/add_video.html +msgid "Upload a media file first." +msgstr "" + +#: pod/video/templates/videos/add_video.html +msgid "The file extension is not in the allowed extension:" +msgstr "" + #: pod/video/templates/videos/card.html #: pod/video/templates/videos/card_select.html #: pod/video/templatetags/video_tags.py @@ -7735,10 +8272,6 @@ msgid_plural "%(counter)s videos" msgstr[0] "" msgstr[1] "" -#: pod/video/templates/videos/dashboard.html -msgid "Clear selection" -msgstr "" - #: pod/video/templates/videos/dashboard.html msgid "" "You have not uploaded any videos yet, please use the ”Add a new video” " @@ -7791,14 +8324,6 @@ msgstr "" msgid "Links" msgstr "" -#: pod/video/templates/videos/last_videos.html -msgid "Last videos" -msgstr "" - -#: pod/video/templates/videos/last_videos.html -msgid "Show all videos" -msgstr "" - #: pod/video/templates/videos/link_video.html #: pod/video/templates/videos/video_row_select.html msgid "Remove from playlist" @@ -7831,6 +8356,10 @@ msgstr "" msgid "Chapter the video" msgstr "" +#: pod/video/templates/videos/link_video.html +msgid "Dress the video" +msgstr "" + #: pod/video/templates/videos/link_video.html #: pod/video/templates/videos/video_edit.html #: pod/video/templates/videos/video_row_select.html @@ -7934,6 +8463,11 @@ msgstr "" msgid "Tags:" msgstr "" +#: pod/video/templates/videos/video-info.html +#, python-format +msgid "Show video list having tag “%(video_tag)s”" +msgstr "" + #: pod/video/templates/videos/video-info.html msgid "No information available" msgstr "" @@ -7942,6 +8476,11 @@ msgstr "" msgid "Infos" msgstr "" +#: pod/video/templates/videos/video-info.html +#, python-format +msgid "Show video list of user “%(video_user_name)s”" +msgstr "" + #: pod/video/templates/videos/video-info.html msgid "Additional owner(s):" msgstr "" @@ -7951,11 +8490,12 @@ msgid "Contributor(s):" msgstr "" #: pod/video/templates/videos/video-info.html -msgid "send an email" -msgstr "stuur een email" +#, python-format +msgid "Send an email to “%(contributor_name)s”" +msgstr "" #: pod/video/templates/videos/video-info.html -msgid "contributor web link" +msgid "Contributor web link" msgstr "" #: pod/video/templates/videos/video-info.html @@ -7968,6 +8508,11 @@ msgid_plural " Channels:" msgstr[0] "" msgstr[1] "" +#: pod/video/templates/videos/video-info.html +#, python-format +msgid "Show video list of type “%(type_title)s”" +msgstr "" + #: pod/video/templates/videos/video-info.html msgid "Main language:" msgstr "" @@ -7980,6 +8525,11 @@ msgstr "" msgid "Discipline(s):" msgstr "" +#: pod/video/templates/videos/video-info.html +#, python-format +msgid "Show video list of discipline “%(discipline_title)s”" +msgstr "" + #: pod/video/templates/videos/video-info.html msgid "Licence:" msgstr "" @@ -7996,11 +8546,18 @@ msgstr "" msgid "Document:" msgstr "" +#: pod/video/templates/videos/video-info.html +msgid "Public document" +msgstr "" + +#: pod/video/templates/videos/video-info.html +msgid "Please note that your video is in draft mode." +msgstr "" + #: pod/video/templates/videos/video-info.html msgid "" -"Please note that your video is in draft mode.
    \n" -" The following links contain a key allowing access.\n" -" Anyone with this links can access it." +"The following links contain a key allowing access. Anyone with this links " +"can access it." msgstr "" #: pod/video/templates/videos/video-info.html @@ -8032,8 +8589,11 @@ msgid "Check the box to indicate the beginning of playing desired." msgstr "" #: pod/video/templates/videos/video-info.html -#: pod/video/templates/videos/video_edit.html -msgid "Manage access tokens" +msgid "Use an embed code to display the video in a web page." +msgstr "" + +#: pod/video/templates/videos/video-info.html +msgid "Manage integration code in access tokens page" msgstr "" #: pod/video/templates/videos/video-info.html @@ -8046,6 +8606,14 @@ msgid "" "activity:" msgstr "" +#: pod/video/templates/videos/video-info.html +msgid "By sharing a link, you allow others to view the video." +msgstr "" + +#: pod/video/templates/videos/video-info.html +msgid "Manage sharing link in access tokens page" +msgstr "" + #: pod/video/templates/videos/video-info.html #: pod/video/templates/videos/video_access_tokens.html msgid "Use this link to share the video:" @@ -8092,7 +8660,7 @@ msgid "Video Collaborate" msgstr "" #: pod/video/templates/videos/video_collaborate.html -msgid "Tricks" +msgid "Tricks:" msgstr "" #: pod/video/templates/videos/video_collaborate.html @@ -8170,6 +8738,10 @@ msgstr "" msgid "Edit the %(app)s" msgstr "" +#: pod/video/templates/videos/video_edit.html +msgid "Manage access tokens" +msgstr "" + #: pod/video/templates/videos/video_edit.html #, python-format msgid "The file size must be lower than %(video_max_upload_size)s GB." @@ -8704,7 +9276,7 @@ msgid "Encoding" msgstr "Videocodering" #: pod/video_encode_transcript/utils.py -msgid "The transcripting" +msgid "The transcripting of content" msgstr "" #: pod/video_encode_transcript/utils.py diff --git a/pod/locale/nl/LC_MESSAGES/djangojs.mo b/pod/locale/nl/LC_MESSAGES/djangojs.mo index 6d9a0715fc..85f9b29283 100644 Binary files a/pod/locale/nl/LC_MESSAGES/djangojs.mo and b/pod/locale/nl/LC_MESSAGES/djangojs.mo differ diff --git a/pod/locale/nl/LC_MESSAGES/djangojs.po b/pod/locale/nl/LC_MESSAGES/djangojs.po index 781dc2b728..077ed19cad 100644 --- a/pod/locale/nl/LC_MESSAGES/djangojs.po +++ b/pod/locale/nl/LC_MESSAGES/djangojs.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: Esup-Pod\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-12 12:01+0100\n" -"PO-Revision-Date: 2023-02-08 15:22+0100\n" +"POT-Creation-Date: 2024-04-29 10:28+0000\n" +"PO-Revision-Date: 2024-04-15 14:27+0200\n" "Last-Translator: obado \n" "Language-Team: \n" "Language: nl\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 3.2.2\n" +"X-Generator: Poedit 3.4.2\n" #: pod/chapter/static/js/chapters.js pod/enrichment/static/js/enrichment.js msgid "Get time from the player" @@ -163,19 +163,11 @@ msgstr "" msgid "A caption cannot contain more than 80 characters." msgstr "" -#: pod/completion/static/js/caption_maker.js -msgid "Add a caption/subtitle after this one" -msgstr "" - #: pod/completion/static/js/caption_maker.js #: pod/podfile/static/podfile/js/filewidget.js msgid "Add" msgstr "" -#: pod/completion/static/js/caption_maker.js -msgid "Delete this caption/subtitle" -msgstr "" - #: pod/completion/static/js/caption_maker.js #: pod/video/static/js/comment-script.js msgid "Delete" @@ -477,6 +469,18 @@ msgstr "" msgid "Unable to find information about the meeting" msgstr "" +#: pod/meeting/static/js/my_meetings.js +msgid "Restart only the live" +msgstr "" + +#: pod/meeting/static/js/my_meetings.js +msgid "End only the live" +msgstr "" + +#: pod/meeting/static/js/my_meetings.js +msgid "End the webinar (meeting and live)" +msgstr "" + #: pod/meeting/static/js/my_meetings.js msgid "End the meeting" msgstr "" @@ -520,14 +524,6 @@ msgstr "" msgid "Loading…" msgstr "" -#: pod/podfile/static/podfile/js/filewidget.js -msgid "Change image" -msgstr "" - -#: pod/podfile/static/podfile/js/filewidget.js -msgid "Change file" -msgstr "" - #: pod/podfile/static/podfile/js/filewidget.js msgid "Open file in a new tab" msgstr "" @@ -540,6 +536,10 @@ msgstr "" msgid "Enter new name of folder" msgstr "" +#: pod/podfile/static/podfile/js/filewidget.js +msgid "No user found" +msgstr "" + #: pod/podfile/static/podfile/js/filewidget.js msgid "Remove" msgstr "" @@ -645,37 +645,14 @@ msgstr "" msgid "Cancel" msgstr "" -#: pod/video/static/js/comment-script.js -#, javascript-format -msgid "%s vote" -msgid_plural "%s votes" -msgstr[0] "" -msgstr[1] "" - #: pod/video/static/js/comment-script.js msgid "Agree with the comment" msgstr "" -#: pod/video/static/js/comment-script.js -msgid "Reply to comment" -msgstr "" - -#: pod/video/static/js/comment-script.js -msgid "Reply" -msgstr "" - #: pod/video/static/js/comment-script.js msgid "Remove this comment" msgstr "" -#: pod/video/static/js/comment-script.js -msgid "Add a public comment" -msgstr "" - -#: pod/video/static/js/comment-script.js -msgid "Send" -msgstr "" - #: pod/video/static/js/comment-script.js msgid "Show answers" msgstr "" @@ -688,6 +665,13 @@ msgstr "" msgid "Sorry, you’re not allowed to vote by now." msgstr "" +#: pod/video/static/js/comment-script.js +#, javascript-format +msgid "%s vote" +msgid_plural "%s votes" +msgstr[0] "" +msgstr[1] "" + #: pod/video/static/js/comment-script.js msgid "Sorry, you can’t comment this video by now." msgstr "" @@ -713,6 +697,12 @@ msgid_plural "Please confirm the editing of the following videos:" msgstr[0] "" msgstr[1] "" +#: pod/video/static/js/dashboard.js +msgid "Please confirm the deletion of the following video:" +msgid_plural "Please confirm the deletion of the following videos:" +msgstr[0] "" +msgstr[1] "" + #: pod/video/static/js/filter_aside_video_list_refresh.js msgid "%(count)s video found" msgid_plural "%(count)s videos found" @@ -720,47 +710,38 @@ msgstr[0] "" msgstr[1] "" #: pod/video/static/js/regroup_videos_by_theme.js -#: pod/video/static/js/video_category.js msgid "This content is password protected." msgstr "" #: pod/video/static/js/regroup_videos_by_theme.js -#: pod/video/static/js/video_category.js msgid "This content is chaptered." msgstr "" #: pod/video/static/js/regroup_videos_by_theme.js -#: pod/video/static/js/video_category.js msgid "This content is in draft." msgstr "" #: pod/video/static/js/regroup_videos_by_theme.js -#: pod/video/static/js/video_category.js msgid "Video content." msgstr "" #: pod/video/static/js/regroup_videos_by_theme.js -#: pod/video/static/js/video_category.js msgid "Audio content." msgstr "" #: pod/video/static/js/regroup_videos_by_theme.js -#: pod/video/static/js/video_category.js msgid "Edit the video" msgstr "" #: pod/video/static/js/regroup_videos_by_theme.js -#: pod/video/static/js/video_category.js msgid "Complete the video" msgstr "" #: pod/video/static/js/regroup_videos_by_theme.js -#: pod/video/static/js/video_category.js msgid "Chapter the video" msgstr "" #: pod/video/static/js/regroup_videos_by_theme.js -#: pod/video/static/js/video_category.js msgid "Delete the video" msgstr "" @@ -832,18 +813,6 @@ msgstr "" msgid "Edit the category" msgstr "" -#: pod/video/static/js/video_category.js -msgid "Delete the category" -msgstr "" - -#: pod/video/static/js/video_category.js -msgid "Success!" -msgstr "" - -#: pod/video/static/js/video_category.js -msgid "Error…" -msgstr "" - #: pod/video/static/js/video_category.js msgid "Category created successfully" msgstr "" diff --git a/pod/main/admin.py b/pod/main/admin.py index 326afcacad..c23483d38f 100644 --- a/pod/main/admin.py +++ b/pod/main/admin.py @@ -1,5 +1,8 @@ +"""Esup-Pod main admin page.""" + from ckeditor.widgets import CKEditorWidget from django.contrib import admin +from django import forms from django.contrib.flatpages.admin import FlatpageForm from django.contrib.flatpages.models import FlatPage from django.contrib.sites.models import Site @@ -9,6 +12,7 @@ from modeltranslation.admin import TranslationAdmin from pod.main.models import LinkFooter, Configuration from pod.main.models import AdditionalChannelTab +from pod.main.models import Block SITE_ID = getattr(settings, "SITE_ID", 1) @@ -26,10 +30,9 @@ class Meta: widgets = content_widget -# CustomFlatPage admin panel - +class AdditionalChannelTabAdmin(TranslationAdmin): + """Create translation for additional Channel Tab Field.""" -class AdditionalChannelTabAdmin(admin.ModelAdmin): list_display = ("name",) @@ -101,14 +104,53 @@ def get_queryset(self, request): return qs def formfield_for_foreignkey(self, db_field, request, **kwargs): + """Exclude sites fields in admin for non-superuser.""" if (db_field.name) == "page": kwargs["queryset"] = FlatPage.objects.filter(sites=Site.objects.get_current()) return super().formfield_for_foreignkey(db_field, request, **kwargs) +class BlockAdminForm(forms.ModelForm): + """The form for Block administration in the Django admin panel.""" + + class Meta: + """Metadata class defining the associated model and fields.""" + + model = Block + fields = "__all__" + + +class BlockAdmin(TranslationAdmin): + """The admin configuration for the Block model in the Django admin panel.""" + + list_display = ( + "title", + "page", + "type", + "data_type", + ) + + def get_form(self, request, obj=None, **kwargs): + """ + Get the form to be used in the Django admin. + + Args: + request: The Django request object. + obj: The Block object being edited, or None if creating a new one. + **kwargs: Additional keyword arguments. + + Returns: + Type[forms.ModelForm]: The form class to be used in the admin. + """ + form = super().get_form(request, obj, **kwargs) + + return form + + # Unregister the default FlatPage admin and register CustomFlatPageAdmin. admin.site.unregister(FlatPage) admin.site.register(FlatPage, CustomFlatPageAdmin) admin.site.register(LinkFooter, LinkFooterAdmin) admin.site.register(Configuration, ConfigurationAdmin) admin.site.register(AdditionalChannelTab, AdditionalChannelTabAdmin) +admin.site.register(Block, BlockAdmin) diff --git a/pod/main/apps.py b/pod/main/apps.py index a2ba6a5629..88af325554 100644 --- a/pod/main/apps.py +++ b/pod/main/apps.py @@ -108,6 +108,43 @@ def create_missing_conf(sender, **kwargs): print("--> No missing configurations found, all is up to date!") +def create_first_block(sender, **kwargs): + """Create first block from first_block.json.""" + from pod.main.models import Block + from django.contrib.flatpages.models import FlatPage + + print("---> Creating block if not exist...") + json_data = [] + with open("./pod/main/fixtures/first_block.json", encoding="utf-8") as data_file: + json_data = json.loads(data_file.read()) + + count = 0 + for fixture in json_data: + if fixture["model"] == "main.block": + title = fixture["fields"]["title"] + type = fixture["fields"]["type"] + data_type = fixture["fields"]["data_type"] + display_title_en = fixture["fields"]["display_title_en"] + display_title_fr = fixture["fields"]["display_title_fr"] + block_count = Block.objects.all().count() + if block_count > 0: + print("-> block exist...") + else: + print("-> Creating block...") + count = count + 1 + url_homepage = "/" + Block.objects.create( + title=title, + type=type, + data_type=data_type, + page=FlatPage.objects.get(url=url_homepage), + display_title_en=display_title_en, + display_title_fr=display_title_fr, + ) + if count == 0: + print("--> No block add, all is up to date!") + + class MainConfig(AppConfig): """Esup-pod Main configurations class.""" @@ -119,3 +156,4 @@ def ready(self): """Run code when Django starts.""" post_migrate.connect(create_missing_conf, sender=self) post_migrate.connect(create_missing_pages, sender=self) + post_migrate.connect(create_first_block, sender=self) diff --git a/pod/main/configuration.json b/pod/main/configuration.json index cab82eb8c2..514f6ed9d3 100644 --- a/pod/main/configuration.json +++ b/pod/main/configuration.json @@ -330,7 +330,7 @@ "" ], "fr": [ - "Noms des Claim permettant de récupérer l'attribut login mais dépendant de l'attribut du client dans l'IDP" + "Noms des Claim permettant de récupérer l’attribut login mais dépendant de l’attribut du client dans l’IDP" ] }, "pod_version_end": "", @@ -871,7 +871,7 @@ "Activation of the Cut application" ], "fr": [ - "Activation de l'application Cut" + "Activation de l’application Cut" ] }, "pod_version_end": "", @@ -880,7 +880,7 @@ }, "title": { "en": "Cut application configuration", - "fr": "Configuration de l'application Cut" + "fr": "Configuration de l’application Cut" } }, "dressing": { @@ -911,7 +911,7 @@ }, "title": { "en": "Dressing application configuration", - "fr": "Configuration de l'application dressing" + "fr": "Configuration de l’application dressing" } }, "enrichment": { @@ -929,7 +929,7 @@ "Set `USE_IMPORT_VIDEO` to True to activate this application." ], "fr": [ - "Application Import_video permettant d'importer des vidéos externes dans Pod.", + "Application Import_video permettant d’importer des vidéos externes dans Pod.", "Mettre `USE_IMPORT_VIDEO` à True pour activer cette application." ] }, @@ -943,7 +943,7 @@ ], "fr": [ "Taille maximum en Go des fichiers vidéos qui peuvent être importés sur la plateforme ", - "via l'application import_video (0 = pas de taille maximum)." + "via l’application import_video (0 = pas de taille maximum)." ] }, "pod_version_end": "", @@ -969,7 +969,7 @@ "Activation of the video import application" ], "fr": [ - "Activation de l’application d'import des vidéos" + "Activation de l’application d’import des vidéos" ] }, "pod_version_end": "", @@ -1000,8 +1000,8 @@ ], "fr": [ "Répertoire du plugin bbb-recorder (voir la documentation https://github.com/jibon57/bbb-recorder).", - "bbb-recorder doit être installé dans ce répertoire, sur tous les serveurs d'encodage.", - "bbb-recorder crée un répertoire Downloads, au même niveau, qui nécessite de l'espace disque." + "bbb-recorder doit être installé dans ce répertoire, sur tous les serveurs d’encodage.", + "bbb-recorder crée un répertoire Downloads, au même niveau, qui nécessite de l’espace disque." ] }, "pod_version_end": "", @@ -1023,7 +1023,7 @@ }, "title": { "en": "Video import application configuration", - "fr": "Configuration application d'import vidéo" + "fr": "Configuration application d’import vidéo" } }, "live": { @@ -1145,7 +1145,7 @@ "" ], "fr": [ - "Nombre de tentatives maximum pour vérifier la présence / taille d'un fichier sur le filesystem" + "Nombre de tentatives maximum pour vérifier la présence / taille d’un fichier sur le filesystem" ] }, "pod_version_end": "", @@ -1264,7 +1264,7 @@ ], "fr": [ "", - "Activer l'auto-transcription pour les directs" + "Activer l’auto-transcription pour les directs" ] }, "pod_version_end": "", @@ -1352,10 +1352,10 @@ "Display videos from non visible channels on the homepage" ], "fr": [ - "Affiche les vidéos de chaines non visibles sur la page d'accueil" + "Affiche les vidéos de chaines non visibles sur la page d’accueil" ] }, - "pod_version_end": "", + "pod_version_end": "3.6.0", "pod_version_init": "3.2.0" }, "USE_BBB": { @@ -1391,7 +1391,7 @@ "Activation of the video import application" ], "fr": [ - "Activation de l’application d'import des vidéos" + "Activation de l’application d’import des vidéos" ] }, "pod_version_end": "", @@ -1629,7 +1629,7 @@ "fr": [ "", "Diaporama préchargé pour les réunions virtuelles.", - "Un utilisateur peut remplacer cette valeur en choisissant un diaporama lors de la création d'une réunion virtuelle.", + "Un utilisateur peut remplacer cette valeur en choisissant un diaporama lors de la création d’une réunion virtuelle.", "Doit se trouver dans le répertoire statique." ] }, @@ -1691,6 +1691,93 @@ "pod_version_end": "", "pod_version_init": "3.1" }, + "USE_MEETING_WEBINAR": { + "default_value": false, + "description": { + "en": [ + "Activate Webinar mode for the meetings module" + ], + "fr": [ + "Activation du mode Webinaire pour le module des réunions" + ] + }, + "pod_version_end": "", + "pod_version_init": "3.6.0" + }, + "MEETING_WEBINAR_SIPMEDIAGW_URL": { + "default_value": "", + "description": { + "en": [ + "URL of the SIPMediaGW server that manages webinars (e.g. https://sipmediagw.univ.fr)" + ], + "fr": [ + "URL du serveur SIPMediaGW qui gère les webinaires (Ex: https://sipmediagw.univ.fr)" + ] + }, + "pod_version_end": "", + "pod_version_init": "3.6.0" + }, + "MEETING_WEBINAR_SIPMEDIAGW_TOKEN": { + "default_value": "", + "description": { + "en": [ + "Bearer token for the SIPMediaGW server that manages webinars" + ], + "fr": [ + "Jeton bearer du serveur SIPMediaGW qui gère les webinaires" + ] + }, + "pod_version_end": "", + "pod_version_init": "3.6.0" + }, + "MEETING_WEBINAR_FIELDS": { + "default_value": "(\"is_webinar\", \"enable_chat\")", + "description": { + "en": [ + "List the additional fields for the webinar session form", + "the additional fields are displayed directly in the form page of a webinar" + ], + "fr": [ + "Permet de définir les champs complémentaires du formulaire de création d’un webinaire", + "ces champs complémentaires sont affichés directement dans la page de formulaire d’un webinaire", + "```", + "MEETING_WEBINAR_FIELDS:", + "(", + " \"is_webinar\",", + " \"enable_chat\",", + ")", + "```" + ] + }, + "pod_version_end": "", + "pod_version_init": "3.6.0" + }, + "MEETING_WEBINAR_AFFILIATION": { + "default_value": "['faculty', 'employee', 'staff']", + "description": { + "en": [ + "Access groups or affiliations of people authorized to create a webinar" + ], + "fr": [ + "Groupes d’accès ou affiliations des personnes autorisées à créer un webinaire" + ] + }, + "pod_version_end": "", + "pod_version_init": "3.6.0" + }, + "MEETING_WEBINAR_GROUP_ADMIN": { + "default_value": "webinar admin", + "description": { + "en": [ + "Group of people authorized to create a webinar" + ], + "fr": [ + "Groupe des personnes autorisées à créer un webinaire" + ] + }, + "pod_version_end": "", + "pod_version_init": "3.6.0" + }, "USE_MEETING": { "default_value": false, "description": { @@ -1730,8 +1817,8 @@ "The coutdown is not present if it at 0." ], "fr": [ - "Compte à rebours utilisé entre chaque vidéo lors de la lecture d'une playlist en lecture automatique.", - "Le compte à rebours n'est pas présent s'il est à 0." + "Compte à rebours utilisé entre chaque vidéo lors de la lecture d’une playlist en lecture automatique.", + "Le compte à rebours n’est pas présent s’il est à 0." ] }, "pod_version_end": "", @@ -1752,6 +1839,19 @@ "pod_version_end": "", "pod_version_init": "3.4" }, + "RESTRICT_PROMOTED_PLAYLIST_ACCESS_TO_STAFF_ONLY": { + "default_value": true, + "description": { + "en": [ + "Restrict access to promoted playlists creation to staff only." + ], + "fr": [ + "Restreindre l’accès à la création de listes de lecture promues au staff uniquement." + ] + }, + "pod_version_end": "", + "pod_version_init": "3.6" + }, "USE_FAVORITES": { "default_value": true, "description": { @@ -1759,7 +1859,7 @@ "Activation of favorite videos. Allows users to add videos to their favorites." ], "fr": [ - "Activation des vidéos favorites. Permet aux utilisateurs d'ajouter des vidéos dans leurs favoris." + "Activation des vidéos favorites. Permet aux utilisateurs d’ajouter des vidéos dans leurs favoris." ] }, "pod_version_end": "", @@ -1772,11 +1872,24 @@ "Activation of playlist. Allows users to add videos in a playlist." ], "fr": [ - "Activation des playlist. Permet aux utilisateurs d'ajouter des vidéos dans une playlist." + "Activation des playlist. Permet aux utilisateurs d’ajouter des vidéos dans une playlist." ] }, "pod_version_end": "", "pod_version_init": "3.4" + }, + "USE_PROMOTED_PLAYLIST": { + "default_value": true, + "description": { + "en": [ + "Activation of promoted playlists. Allows users to use the promoted playlists." + ], + "fr": [ + "Activation des playlist promues. Permet aux utilisateurs d'utiliser les listes de lecture promues." + ] + }, + "pod_version_end": "", + "pod_version_init": "3.6" } }, "title": { @@ -2179,7 +2292,7 @@ ], "fr": [ "", - "Temps en seconde de conservation des données de l'application video" + "Temps en seconde de conservation des données de l’application video" ] }, "pod_version_end": "", @@ -2611,7 +2724,7 @@ ], "fr": [ "", - "nombre d'item renvoyé par le flux rss" + "nombre d’item renvoyé par le flux rss" ] }, "pod_version_end": "", @@ -2947,8 +3060,8 @@ "fr": [ "", "Application pour l’encodage et la transcription de vidéo.", - "Il est possible d'encoder en local ou en distant.", - "Attention, il faut configurer Celery pour l’envoi des instructions pour l'encodage distant." + "Il est possible d’encoder en local ou en distant.", + "Attention, il faut configurer Celery pour l’envoi des instructions pour l’encodage distant." ] }, "settings": { @@ -3074,8 +3187,8 @@ ], "fr": [ "", - "Il faut renseigner l'url du redis sur lequel Celery va chercher les ordres d'encodage et de transcription", - "par exemple : \"redis://redis:6379/7\"" + "Il faut renseigner l’url du redis sur lequel Celery va chercher les ordres d’encodage et de transcription", + "par exemple : \"redis://redis:6379/7\"" ] }, "pod_version_end": "", @@ -3112,7 +3225,7 @@ ], "fr": [ "", - "Si True, active l'encodage et la transcription sur un environnement distant via redis+celery" + "Si True, active l’encodage et la transcription sur un environnement distant via redis+celery" ] }, "pod_version_end": "", @@ -3123,11 +3236,11 @@ "description": { "en": [ "Address of API rest to be called at the end of remote encoding or remote transcription.", - "Example : https://pod.univ.fr/rest/" + "Example: https://pod.univ.fr/rest/" ], "fr": [ - "Adresse de l'API rest a appeler en fin d'encodage distant ou de transcription à distance.", - "Exemple : https://pod.univ.fr/rest/" + "Adresse de l’API rest à appeler en fin d’encodage distant ou de transcription à distance.", + "Exemple : https://pod.univ.fr/rest/" ] }, "pod_version_end": "", @@ -3141,8 +3254,8 @@ "To create it, go to Admin > Authentication token > token." ], "fr": [ - "Token d'authentification utilisé pour l'appel en fin d'encodage distant ou de transcription à distance.", - "Pour le créer, il faut aller dans la partie Admin > Jeton d'authentification > token." + "Token d’authentification utilisé pour l’appel en fin d’encodage distant ou de transcription à distance.", + "Pour le créer, il faut aller dans la partie Admin > Jeton d’authentification > token." ] }, "pod_version_end": "", @@ -3267,7 +3380,7 @@ "Pour utiliser la version 7 ou 8, faire une mise à jour du paquet elasticsearch-py ", "Pour la 7, `pip3 install elasticsearch==7.17.7`,", "et pour la 8, `pip3 install elasticsearch==8.8.1`.", - "Voir [https://elasticsearch-py.readthedocs.io/]() pour plus d'information." + "Voir [https://elasticsearch-py.readthedocs.io/]() pour plus d’information." ] }, "pod_version_end": "", @@ -3281,7 +3394,7 @@ ], "fr": [ "Options d’ElasticSearch, notamment utilisées pour ES8 en SSL et avec un user en paramètre", - "Voir [https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/config.html]() pour plus d'informations." + "Voir [https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/config.html]() pour plus d’informations." ] }, "pod_version_end": "", @@ -3317,7 +3430,7 @@ ], "fr": [ "", - "Activation de l'application xAPI" + "Activation de l’application xAPI" ] }, "pod_version_end": "", @@ -3331,7 +3444,7 @@ ], "fr": [ "", - "Si False, le nom de l'utilisateur sera stocké en clair dans les statements xAPI, si True, son nom d'utilisateur sera anonymisé" + "Si False, le nom de l’utilisateur sera stocké en clair dans les statements xAPI, si True, son nom d’utilisateur sera anonymisé" ] }, "pod_version_end": "", @@ -3345,7 +3458,7 @@ ], "fr": [ "", - "identifiant de connexion du LRS pour l'envoi des statements" + "identifiant de connexion du LRS pour l’envoi des statements" ] }, "pod_version_end": "", @@ -3359,7 +3472,7 @@ ], "fr": [ "", - "mot de passe de connexion du LRS pour l'envoi des statements" + "mot de passe de connexion du LRS pour l’envoi des statements" ] }, "pod_version_end": "", @@ -3373,7 +3486,7 @@ ], "fr": [ "", - "URL de destination pour l'envoi des statements. I.E. : https://ralph.univ.fr/xAPI/statements" + "URL de destination pour l’envoi des statements. I.E. : https://ralph.univ.fr/xAPI/statements" ] }, "pod_version_end": "", @@ -4218,7 +4331,7 @@ "__ref: [https://django-debug-toolbar.readthedocs.io/en/latest/]()__" ], "fr": [ - "Une valeur booléenne qui active ou désactive l'outil de débogage.
    ", + "Une valeur booléenne qui active ou désactive l’outil de débogage.
    ", "Ne déployez jamais de site en production avec le réglage USE_DEBUG_TOOLBAR activé.
    ", "__ref: [https://django-debug-toolbar.readthedocs.io/en/latest/]()__" ] @@ -4299,13 +4412,18 @@ "pod_version_init": "3.1" }, "SECURE_SSL_REDIRECT": { - "default_value": "not DEBUG", + "default_value": "False", "description": { "en": [ - "" + "Unless your site should be available over both SSL and non-SSL ", + "connections, you may want to either set this setting True ", + "or configure a load balancer or reverse-proxy server ", + "to redirect all connections to HTTPS." ], "fr": [ - "" + "À moins que votre site ne doive être disponible sur des connexions SSL et non SSL,", + " vous souhaiterez probablement définir ce paramètre sur True ou configurer un", + " load balancer ou reverse-proxy pour rediriger toutes les connexions vers HTTPS." ] }, "pod_version_end": "", @@ -4718,7 +4836,7 @@ "Nombre de vidéos à afficher sur la page d’accueil." ] }, - "pod_version_end": "", + "pod_version_end": "3.6.0", "pod_version_init": "3.1.0" }, "HOMEPAGE_SHOWS_PASSWORDED": { @@ -4731,7 +4849,7 @@ "Afficher les vidéos dont l’accès est protégé par mot de passe sur la page d’accueil." ] }, - "pod_version_end": "", + "pod_version_end": "3.6.0", "pod_version_init": "3.1.0" }, "HOMEPAGE_SHOWS_RESTRICTED": { @@ -4744,7 +4862,7 @@ "Afficher les vidéos dont l’accès est protégé par authentification sur la page d’accueil." ] }, - "pod_version_end": "", + "pod_version_end": "3.6.0", "pod_version_init": "3.1.0" }, "MENUBAR_HIDE_INACTIVE_OWNERS": { @@ -4797,7 +4915,7 @@ "Si True, affiche les prochains évènements sur la page d’accueil." ] }, - "pod_version_end": "", + "pod_version_end": "3.6.0", "pod_version_init": "3.1.0" }, "SHOW_ONLY_PARENT_THEMES": { @@ -4861,8 +4979,8 @@ "'FAVICON': 'img/pod_favicon.svg',", " ", "# Si souhaitée, à créer et sauvegarder", - "# dans le répertoire static de l'application custom et", - "# préciser le chemin d'accès. Par exemple : \"custom/etab.css\"", + "# dans le répertoire static de l’application custom et", + "# préciser le chemin d’accès. Par exemple : \"custom/etab.css\"", "'CSS_OVERRIDE': '',", " ", "# Vous pouvez créer un template dans votre application custom et", @@ -5171,7 +5289,7 @@ "description": { "en": "", "fr": [ - "Mise en place du mode PWA grâce à l'application Django-pwa", + "Mise en place du mode PWA grâce à l’application Django-pwa", "Voici la configuration par défaut pour Pod, vous pouvez surcharger chaque variable dans votre fichier de configuration.", "PWA_APP_NAME = \"Pod\"", "PWA_APP_DESCRIPTION = (", @@ -5189,7 +5307,7 @@ "PWA_APP_STATUS_BAR_COLOR = \"default\"", "PWA_APP_DIR = \"ltr\"", "PWA_APP_LANG = \"fr-FR\"", - "Pour en savoir plus : [https://github.com/silviolleite/django-pwa]()" + "Pour en savoir plus : [https://github.com/silviolleite/django-pwa]()" ] }, "pod_version_end": "", @@ -5200,7 +5318,7 @@ "description": { "en": "", "fr": [ - "version 3.14.0: mise en place de l’API rest pour l’application", + "version 3.14.0 : mise en place de l’API rest pour l’application", "[https://www.django-rest-framework.org/]()" ] }, diff --git a/pod/main/context_processors.py b/pod/main/context_processors.py index ba0616da58..b4aa95530d 100644 --- a/pod/main/context_processors.py +++ b/pod/main/context_processors.py @@ -1,7 +1,7 @@ from django.conf import settings as django_settings from django.core.exceptions import ImproperlyConfigured -from pod.main.models import LinkFooter +from pod.main.models import LinkFooter, Block from django.core.exceptions import ObjectDoesNotExist from pod.main.models import Configuration @@ -70,12 +70,12 @@ COOKIE_LEARN_MORE = getattr(django_settings, "COOKIE_LEARN_MORE", "") -SHOW_EVENTS_ON_HOMEPAGE = getattr(django_settings, "SHOW_EVENTS_ON_HOMEPAGE", False) - USE_OPENCAST_STUDIO = getattr(django_settings, "USE_OPENCAST_STUDIO", False) USE_MEETING = getattr(django_settings, "USE_MEETING", False) +USE_MEETING_WEBINAR = getattr(django_settings, "USE_MEETING_WEBINAR", False) + RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY = getattr( django_settings, "RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY", False ) @@ -137,8 +137,8 @@ def context_settings(request): new_settings["DYSLEXIAMODE_ENABLED"] = DYSLEXIAMODE_ENABLED new_settings["USE_OPENCAST_STUDIO"] = USE_OPENCAST_STUDIO new_settings["COOKIE_LEARN_MORE"] = COOKIE_LEARN_MORE - new_settings["SHOW_EVENTS_ON_HOMEPAGE"] = SHOW_EVENTS_ON_HOMEPAGE new_settings["USE_MEETING"] = USE_MEETING + new_settings["USE_MEETING_WEBINAR"] = USE_MEETING_WEBINAR new_settings["RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY"] = ( RESTRICT_EDIT_VIDEO_ACCESS_TO_STAFF_ONLY ) @@ -150,7 +150,26 @@ def context_settings(request): def context_footer(request): - linkFooter = LinkFooter.objects.all().filter(sites=get_current_site(request)) + link_footer = LinkFooter.objects.all().filter(sites=get_current_site(request)) + return { + "LINK_FOOTER": link_footer, + } + + +def context_block(request): + """ + Return the context for blocks to be displayed in templates. + + Args: + request (HttpRequest): The Django request object. + + Returns: + dict[str, Any]: A dictionary containing the context with the key "BLOCK" + associated with the sorted list of blocks. + """ + block = Block.objects.filter(sites=get_current_site(request), visible=True).order_by( + "order" + ) return { - "LINK_FOOTER": linkFooter, + "BLOCK": block, } diff --git a/pod/main/fixtures/first_block.json b/pod/main/fixtures/first_block.json new file mode 100644 index 0000000000..d87526bb76 --- /dev/null +++ b/pod/main/fixtures/first_block.json @@ -0,0 +1,18 @@ +[ + { + "pk": 1, + "model": "main.block", + "fields": { + "title": "Dernières vidéos", + "display_title_en": "Last videos", + "display_title_fr": "Dernières vidéos", + "type": "card_list", + "data_type": "last_videos", + "page": 1, + "sites": [ + 1 + ] + } + } + ] + \ No newline at end of file diff --git a/pod/main/fixtures/initial_data.json b/pod/main/fixtures/initial_data.json index 1e6ef923e3..788135ac49 100644 --- a/pod/main/fixtures/initial_data.json +++ b/pod/main/fixtures/initial_data.json @@ -31,7 +31,7 @@ "pk": 3, "model": "main.configuration", "fields": { - "key" : "maintenance_text_welcome", + "key": "maintenance_text_welcome", "value": "La plateforme Pod est actuellement en maintenance", "description_fr": "Texte qui sera affiché sur la page d’accueil pendant une maintenance", "description_en": "Text that will be displayed on the home page during maintenance" @@ -41,7 +41,7 @@ "pk": 4, "model": "main.configuration", "fields": { - "key" : "maintenance_text_disabled", + "key": "maintenance_text_disabled", "value": "Pod est en maintenance, cette fonctionnalité est donc désactivée pour le moment", "description_fr": "Texte qui sera affiché sur les pages qui sont désactivées pendant la maintenance", "description_en": "Text that will be displayed on pages that are disabled during maintenance" diff --git a/pod/main/forms_utils.py b/pod/main/forms_utils.py index 5bc269d65e..b6e5474c5e 100644 --- a/pod/main/forms_utils.py +++ b/pod/main/forms_utils.py @@ -56,37 +56,37 @@ def decompress(self, value): def add_describedby_attr(fields): """Add aria-describedby attribute to specified fields.""" for fieldName in fields: - myField = fields[fieldName] - if myField.widget.__class__.__name__ != "HiddenInput" and myField.help_text: - myField.widget.attrs["aria-describedby"] = "id_%sHelp" % fieldName + my_field = fields[fieldName] + if my_field.widget.__class__.__name__ != "HiddenInput" and my_field.help_text: + my_field.widget.attrs["aria-describedby"] = "id_%sHelp" % fieldName return fields def add_placeholder_and_asterisk(fields): """Add placeholder and asterisk to specified fields.""" for fieldName in fields: - myField = fields[fieldName] - classname = myField.widget.__class__.__name__ - if classname == "PasswordInput" or classname == "TextInput": - myField.widget.attrs["placeholder"] = myField.label + my_field = fields[fieldName] + classname = my_field.widget.__class__.__name__ + if classname in {"PasswordInput", "TextInput", "EmailInput", "CaptchaTextInput"}: + my_field.widget.attrs["placeholder"] = my_field.label - if classname == "CheckboxInput" or classname == "CheckboxSelectMultiple": - bsClass = "form-check-input" + if classname in {"CheckboxInput", "CheckboxSelectMultiple"}: + bs_class = "form-check-input" elif classname == "Select": - bsClass = "form-select" + bs_class = "form-select" else: - bsClass = "form-control" + bs_class = "form-control" - init_class = myField.widget.attrs.get("class", "") - bsClass = bsClass + " " + init_class - if myField.required: - myField.label = mark_safe( - '%s *' % myField.label + init_class = my_field.widget.attrs.get("class", "") + bs_class = bs_class + " " + init_class + if my_field.required: + my_field.label = mark_safe( + '%s *' % my_field.label ) - myField.widget.attrs["required"] = "" - myField.widget.attrs["class"] = "required " + bsClass + my_field.widget.attrs["required"] = "" + my_field.widget.attrs["class"] = "required " + bs_class else: - myField.widget.attrs["class"] = bsClass + my_field.widget.attrs["class"] = bs_class return fields diff --git a/pod/main/management/commands/addsetting.py b/pod/main/management/commands/addsetting.py index 88329cb7d7..b102e7efa2 100644 --- a/pod/main/management/commands/addsetting.py +++ b/pod/main/management/commands/addsetting.py @@ -41,7 +41,7 @@ def get_setting(self, options, config_part): if settings.get(options["setting_name"]): self.stdout.write(self.style.WARNING(20 * "*")) self.stdout.write( - self.style.WARNING("Setting found in json file, you will modify it !") + self.style.WARNING("Setting found in json file, you will modify it!") ) setting_json = json.dumps( settings[options["setting_name"]], @@ -79,9 +79,9 @@ def save_setting(self, options, config_part, setting): json.dump(data, f, sort_keys=True, indent=4, ensure_ascii=False) def fix_default_value(self, default_value): - msg = "Default value (leave blank to keep previous value : %s) : " % default_value + msg = "Default value (leave blank to keep previous value: %s): " % default_value if default_value == "": - msg = "Default value : " + msg = "Default value: " input_value = input(msg) if input_value != "": default_value = input_value @@ -95,7 +95,7 @@ def fix_default_value(self, default_value): def get_description(self, previous_description): if previous_description != [""]: - print("(--> Type enter directly to keep previous value !)") + print("(--> Type enter directly to keep previous value!)") description = [""] while True: user_input = input() @@ -117,7 +117,7 @@ def handle(self, *args, **options): config_part = "main" if options["app_name"] == "pod": list_conf = self.get_configuration_pod() - print("Here is available configuration : %s" % ", ".join(list_conf)) + print("Here is available configuration: %s" % ", ".join(list_conf)) config_part = input( "Give configuration part in available configuration, " + "leave blank to use main: " @@ -125,19 +125,19 @@ def handle(self, *args, **options): if config_part == "": config_part = "main" if config_part not in list_conf: - self.stdout.write(self.style.ERROR("Configuration not available !")) + self.stdout.write(self.style.ERROR("Configuration not available!")) return setting = self.get_setting(options, config_part) pod_version_init = input( - "Pod initial version (leave blank to put current version : %s): " % VERSION + "Pod initial version (leave blank to put current version: %s): " % VERSION ) if pod_version_init == "": pod_version_init = VERSION pod_version_end = input( - "Pod last version (i.e : 2.9.0, deprecated or not use anymore): " + "Pod last version (i.e: 2.9.0, deprecated or not use anymore): " ) default_value = self.fix_default_value(setting.get("default_value", "")) @@ -167,11 +167,11 @@ def handle(self, *args, **options): ensure_ascii=False, ) self.stdout.write(self.style.SUCCESS(setting_json)) - confirm = input("Save it to config file ? y/n : ") + confirm = input("Save it to config file? y/n: ") if confirm != "y": - self.stdout.write(self.style.ERROR("Not saving, End !")) + self.stdout.write(self.style.ERROR("Not saving, End!")) return self.save_setting(options, config_part, setting) - self.stdout.write(self.style.SUCCESS("End !")) + self.stdout.write(self.style.SUCCESS("End!")) diff --git a/pod/main/management/commands/compareconfiguration.py b/pod/main/management/commands/compareconfiguration.py index df9a1754fb..ebe73d40d3 100644 --- a/pod/main/management/commands/compareconfiguration.py +++ b/pod/main/management/commands/compareconfiguration.py @@ -51,7 +51,7 @@ def handle(self, *args, **options): def print_log(self, title: str, data: List[str]) -> None: """Pretty print of array with title.""" print(20 * "-") - print(f"{title} :") + print(f"{title}:") print("\n - " + "\n - ".join(data)) def get_local_missing(self, distant_configuration, local_configuration): diff --git a/pod/main/management/commands/comparesettings.py b/pod/main/management/commands/comparesettings.py index 1020fc55e4..a1fe3be3ac 100644 --- a/pod/main/management/commands/comparesettings.py +++ b/pod/main/management/commands/comparesettings.py @@ -42,7 +42,7 @@ def handle(self, *args, **options): def print_log(self, title: str, data: List[str]) -> None: print(20 * "-") - print(f"{title} :") + print(f"{title}:") print("\n - " + "\n - ".join(data)) # print(20 * "-") diff --git a/pod/main/management/commands/getsettings.py b/pod/main/management/commands/getsettings.py index 5b1973975a..35bc052d8f 100644 --- a/pod/main/management/commands/getsettings.py +++ b/pod/main/management/commands/getsettings.py @@ -55,6 +55,6 @@ def handle(self, *args, **options): def print_log(self, title: str, data: List[str]) -> None: print(20 * "-") - print(f"{title} :") + print(f"{title}:") print("\n - " + "\n - ".join(data)) # print(20 * "-") diff --git a/pod/main/models.py b/pod/main/models.py index f1501772c6..88fc65acfa 100644 --- a/pod/main/models.py +++ b/pod/main/models.py @@ -1,3 +1,5 @@ +"""Esup-Pod Main models.""" + from django.db import models from django.conf import settings from django.utils.translation import ugettext_lazy as _ @@ -7,24 +9,21 @@ from django.template.defaultfilters import slugify from django.dispatch import receiver from django.db.models.signals import post_save -from django.db import connection +from django.db.models import Max import os import mimetypes +from ckeditor.fields import RichTextField + FILES_DIR = getattr(settings, "FILES_DIR", "files") def get_nextautoincrement(model): - cursor = connection.cursor() - cursor.execute( - "SELECT Auto_increment FROM information_schema.tables " - + 'WHERE table_name="{0}" AND table_schema=DATABASE();'.format( - model._meta.db_table - ) - ) - row = cursor.fetchone() - cursor.close() - return row[0] + """Get potential next id for the current model when an object will be created""" + objects = model.objects.all() + if objects: + return objects.aggregate(Max("id"))["id__max"] + 1 + return 1 def get_upload_path_files(instance, filename): @@ -193,3 +192,178 @@ def __str__(self): class Meta: verbose_name = _("Additional channels Tab") verbose_name_plural = _("Additional channel Tabs") + + +class Block(models.Model): + """Class describing Block objects.""" + + CAROUSEL = "carousel" + MULTI_CAROUSEL = "multi_carousel" + CARD_LIST = "card_list" + HTML = "html" + TYPE = ( + (CAROUSEL, _("Carousel")), + (MULTI_CAROUSEL, _("Multiple carousel")), + (CARD_LIST, _("Card list")), + (HTML, _("HTML")), + ) + + CHANNEL = "channel" + THEME = "theme" + PLAYLIST = "playlist" + LAST_VIDEOS = "last_videos" + MOST_VIEWS = "most_views" + EVENT_NEXT = "event_next" + DATA_TYPE = ( + (CHANNEL, _("Channel")), + (THEME, _("Theme")), + (PLAYLIST, _("Playlist")), + (LAST_VIDEOS, _("Last videos")), + (MOST_VIEWS, _("Most views")), + (EVENT_NEXT, _("Next events")), + ) + + title = models.CharField( + verbose_name=_("Title"), max_length=250, blank=True, null=True + ) + + order = models.PositiveSmallIntegerField( + verbose_name=_("Order"), default=1, blank=True, null=True + ) + + visible = models.BooleanField( + verbose_name=_("Visible"), + default=True, + help_text=_("Check this box if block is visible in page."), + ) + + page = models.ForeignKey( + FlatPage, + blank=True, + null=True, + on_delete=models.CASCADE, + help_text=_("Select the page of Pod you want to link with."), + ) + + sites = models.ManyToManyField(Site) + + type = models.CharField( + verbose_name=_("Type"), + max_length=200, + choices=TYPE, + default=CAROUSEL, + blank=True, + null=True, + ) + + data_type = models.CharField( + verbose_name=_("Data type"), + max_length=200, + choices=DATA_TYPE, + default=None, + blank=True, + null=True, + ) + + Channel = models.ForeignKey( + "video.Channel", + blank=True, + null=True, + on_delete=models.CASCADE, + help_text=_("Select the channel you want to link with."), + ) + + Theme = models.ForeignKey( + "video.Theme", + blank=True, + null=True, + on_delete=models.CASCADE, + help_text=_("Select the theme you want to link with."), + ) + + Playlist = models.ForeignKey( + "playlist.Playlist", + blank=True, + null=True, + on_delete=models.CASCADE, + help_text=_("Select the playlist you want to link with."), + ) + + html = RichTextField( + config_name="complete", + verbose_name=_("HTML"), + null=True, + blank=True, + help_text=_("Write in html inside this field."), + ) + + display_title = models.CharField( + verbose_name=_("Display title"), max_length=250, blank=True, null=True + ) + + no_cache = models.BooleanField( + default=True, + verbose_name=_("No cache"), + help_text=_("Check this box if you don’t want to keep the cache."), + ) + + debug = models.BooleanField( + verbose_name=_("Debug"), + default=False, + help_text=_("Check this box if you want to activate debug mode."), + ) + + show_restricted = models.BooleanField( + verbose_name=_("Show restricted content"), + default=False, + help_text=_("Check this box if you want to show restricted content."), + ) + + must_be_auth = models.BooleanField( + verbose_name=_("Must be authenticated"), + default=False, + help_text=_("Check this box if users must be authenticated to view content."), + ) + + auto_slide = models.BooleanField( + verbose_name=_("Auto slide"), + default=False, + help_text=_("Check this box if you want auto slide."), + ) + + nb_element = models.PositiveIntegerField( + verbose_name=_("Maximum number of element"), default=5, blank=True, null=True + ) + + multi_carousel_nb = models.PositiveIntegerField( + verbose_name=_("Number of element per page (multi carousel)"), + default=5, + blank=True, + null=True, + ) + + view_videos_from_non_visible_channels = models.BooleanField( + verbose_name=_("View videos from non visible channel"), + default=False, + help_text=_("Check this box if you want view videos from non visible channel."), + ) + + shows_passworded = models.BooleanField( + verbose_name=_("View videos with password"), + default=False, + help_text=_("Check this box if you want view videos with password."), + ) + + def __str__(self): + return "%s" % (self.title) + + class Meta: + verbose_name = _("Block") + verbose_name_plural = _("Blocks") + + +@receiver(post_save, sender=Block) +def default_site_block(sender, instance, created, **kwargs): + """Set a default site for the instance if it has no associated sites upon creation.""" + if len(instance.sites.all()) == 0: + instance.sites.add(Site.objects.get_current()) diff --git a/pod/main/rest_router.py b/pod/main/rest_router.py index 68b9323df6..1fed02eb1f 100644 --- a/pod/main/rest_router.py +++ b/pod/main/rest_router.py @@ -24,6 +24,9 @@ if getattr(settings, "USE_BBB", True): from pod.bbb import rest_views as bbb_views +if getattr(settings, "USE_MEETING", True): + from pod.meeting import rest_views as meeting_views + router = routers.DefaultRouter() router.register(r"mainfiles", main_views.CustomFileModelViewSet) @@ -72,6 +75,14 @@ router.register(r"bbb_attendee", bbb_views.AttendeeModelViewSet) router.register(r"bbb_livestream", bbb_views.LivestreamModelViewSet) +if getattr(settings, "USE_MEETING", True): + router.register(r"meeting_session", meeting_views.MeetingModelViewSet) + router.register( + r"meeting_internal_recording", meeting_views.InternalRecordingModelViewSet + ) + router.register(r"meeting_livestream", meeting_views.LivestreamModelViewSet) + router.register(r"meeting_live_gateway", meeting_views.LiveGatewayModelViewSet) + urlpatterns = [ url(r"dublincore/$", video_views.DublinCoreView.as_view(), name="dublincore"), url( diff --git a/pod/main/settings.py b/pod/main/settings.py index 34878bd744..e85699ec49 100644 --- a/pod/main/settings.py +++ b/pod/main/settings.py @@ -281,7 +281,6 @@ # https://sorl-thumbnail.readthedocs.io/en/latest/reference/settings.html THUMBNAIL_PRESERVE_FORMAT = True -SHOW_EVENTS_ON_HOMEPAGE = False DEFAULT_EVENT_PATH = "" DEFAULT_EVENT_THUMBNAIL = "/img/default-event.svg" DEFAULT_EVENT_TYPE_ID = 1 diff --git a/pod/main/static/css/block.css b/pod/main/static/css/block.css new file mode 100644 index 0000000000..dc8af4dbbf --- /dev/null +++ b/pod/main/static/css/block.css @@ -0,0 +1,155 @@ +/***************************** + Editorial +******************************/ + +.video_time, +.time { + display: flex !important; + align-items: center; +} +.video_time:before, +.time:before { + content: "\f293"; + font-family: bootstrap-icons; + margin-right: 10px; +} + +/* Carousel*/ + +.edito-carousel .carousel-caption.d-md-block { + text-align: left; + display: flex !important; + flex-direction: column; + justify-content: center; + height: inherit; + width: 100%; + bottom: 0; + left: 0; + right: 0; + background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.7) 40%); + padding: 5em 3em 3em 3em; +} + +.edito-carousel .video_title { + font-size: 36px; + font-family: var(--bs-body-font-family); +} +.edito-carousel .video_title:after { + content: ""; + display: block; + width: 90px; + height: 3px; + background-color: var(--pod-primary); + margin-top: 15px; +} +.edito-carousel .video_time { + font-size: 18px; + font-weight: 700; + color: #fff !important; +} +.edito-carousel .video_time:before { + color: var(--pod-primary); +} + +.edito-carousel .carousel-indicators [data-bs-target] { + border: 3px solid #fff; + height: 15px; + width: 15px; + background: none; + border-radius: 100%; +} + +.edito-carousel .carousel-indicators button:hover, +.edito-carousel .carousel-indicators button:focus { + border-color: var(--pod-primary); + opacity: 1; +} + +/* Multiple carousel */ + +.edito-multi-carousel .carousel-control-next, +.edito-multi-carousel .carousel-control-prev { + width: 2%; + height: 185px; +} +.edito-multi-carousel .carousel-control-prev { + background-image: linear-gradient( + to left, + rgba(0, 0, 0, 0), + rgba(0, 0, 0, 0.3) 40% + ); +} +.edito-multi-carousel .carousel-control-next { + background-image: linear-gradient( + to right, + rgba(0, 0, 0, 0), + rgba(0, 0, 0, 0.3) 40% + ); +} + +.edito-multi-carousel .carousel-inner .carousel-item.active, +.edito-multi-carousel .carousel-inner .carousel-item-next, +.edito-multi-carousel .carousel-inner .carousel-item-prev { + display: flex; +} + +.edito-multi-carousel .carousel-item .card { + margin-right: 20px; +} + +.edito-multi-carousel .carousel-item .col-md-4 { + background-color: #fafafa; +} + +.edito-multi-carousel .carousel-item .col-md-3:last-child .card, +.edito-multi-carousel .carousel-item .col-md-4:last-child .card { + margin: 0; +} + +.edito-multi-carousel img { + height: 185px; + object-fit: cover; + width: 100%; +} +.edito-multi-carousel .carousel-caption { + position: initial; + color: #000; + padding: 1rem 0; + background-color: #fafafa; +} +.edito-multi-carousel a { + text-decoration: none; +} +.edito-multi-carousel .video_desc { + display: none; +} +.edito-multi-carousel .video_time { + justify-content: center; +} + +/* Multiple carousel */ +@media (min-width: 768px) { + .edito-multi-carousel .carousel-inner .carousel-item-end.active, + .edito-multi-carousel .carousel-inner .carousel-item-next { + transform: translateX(25%); + } + + .edito-multi-carousel .carousel-inner .carousel-item-start.active, + .edito-multi-carousel .carousel-inner .carousel-item-prev { + transform: translateX(-25%); + } + + .edito-multi-carousel .carousel-inner .carousel-item-end, + .edito-multi-carousel .carousel-inner .carousel-item-start { + transform: translateX(0); + } +} + +@media (max-width: 767px) { + .edito-multi-carousel .carousel-inner .carousel-item > div { + display: none; + } + .edito-multi-carousel .carousel-inner .carousel-item > div:first-child { + display: block; + } +} diff --git a/pod/main/static/css/pod.css b/pod/main/static/css/pod.css index df0e184e2d..8d6f11c58d 100755 --- a/pod/main/static/css/pod.css +++ b/pod/main/static/css/pod.css @@ -211,6 +211,10 @@ a:not(.btn, .dropdown-item):focus { margin-bottom: 1.5rem; } +.pod-share-draft-icon { + font-size: 1.5rem; +} + :not(footer) > .pod-btn-social { font-size: 2rem; } @@ -339,7 +343,6 @@ a:not(.btn, .dropdown-item):focus { /** modal **/ .modal-title svg { - /* color:var(--pod-primary); */ height: 40px; width: 40px; } @@ -726,10 +729,15 @@ div.card a img { transition: all 0.3s; } +.dashboard-container .infinite-item:not(.selected), +.dashboard-container .infinite-item .checked_overlay .card_selected { + transition: transform 0.2s ease-in-out; +} + .dashboard-container .infinite-item:not(.selected):hover, .dashboard-container .infinite-item .checked_overlay:hover .card_selected { transform: scale(1.03); - transition: transform 0.25s ease; + transition: transform 0.2s ease-in-out; z-index: 6; } @@ -909,10 +917,6 @@ span[data-bs-placement] { } } -/* -@media screen and (min-width:768px) { } -*/ - @media screen and (min-width: 992px) { .card-img-top { max-height: 195.36px; @@ -1079,23 +1083,22 @@ body { .pod-aside-collapse { position: absolute; - right: 0.5rem; + right: 0.75rem; z-index: 6; + margin: 0.25rem; } .pod-aside-collapse__button { - border-radius: 50%; + border-radius: var(--bs-btn-border-radius); width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; font-size: 1.4rem; - transition: all 0.5s ease-in; background: none; border: 1px solid silver; color: var(--pod-font-color); - text-shadow: 1px 1px 0 var(--pod-background); } .pod-aside-collapse__button:hover { @@ -1103,9 +1106,12 @@ body { color: var(--pod-btn-text); } +/* Commented in 3.6.0 +[Todo] remove in 3.7.0 .pod-aside-collapse[aria-expanded="true"] { transform: rotate(-90deg); } +*/ /*** footer **/ .pod-footer { @@ -1129,10 +1135,6 @@ body { .pod-footer__credits { font-size: 85%; - - /* - opacity: .8; - */ } .pod-footer__links { @@ -1332,16 +1334,16 @@ body { /* small screens */ .pod-grid-content { - display: grid; - grid-template-columns: minmax(0%, 100%) repeat(auto-fit, 100%); position: relative; } /* medium screens */ @media (width >= 992px) { .pod-grid-content { + display: grid; gap: 2rem; grid-template-columns: minmax(0%, 100%) repeat(auto-fit, 33%); + grid-template-areas: "main sidebar"; } } @@ -1367,6 +1369,7 @@ body { } .pod-aside { + grid-area: sidebar; display: grid; gap: 1rem; grid-template-columns: 1fr; @@ -1375,34 +1378,42 @@ body { grid-auto-rows: min-content; background: var(--pod-background-neutre2-bloc); padding: 1rem; + border-radius: var(--bs-border-radius-sm); + /* + Commented in 3.6.0 + [Todo] remove in 3.7.0 width: 0; overflow: hidden; margin-left: 100%; transition: all 0.15s ease; + */ z-index: 5; padding-top: 5rem; box-shadow: 0.05rem 0.05rem 0.2rem 0.05rem rgb(0 0 0 / 20%); } +/* +Commented in 3.6.0 +[Todo] remove in 3.7.0 .pod-aside.show { margin-left: 0; width: auto; } - -@media (width >= 992px) { - .pod-show-lg { - margin-left: 0; - width: auto; - } -} +*/ @media (max-width: 992px) { + /* .pod-aside { /* THIS IS NORMAL, it's to fit with pod-aside-collapse */ + + /* + Commented in 3.6.0 + [Todo] remove in 3.7.0 width: 101% !important; min-height: 100%; position: absolute; } + */ .pod-aside-collapse { position: fixed; @@ -1420,9 +1431,13 @@ body { } } +/* +Commented in 3.6.0 +[Todo] remove in 3.7.0 .pod-aside.collapsing { height: auto; } +*/ .pod-aside .card { background: none; @@ -1529,7 +1544,7 @@ table .alert.alert-danger.btn.pod-btn-social { font-size: 11px; } -/* Upload page */ +/** Upload page **/ .form-group.hide-on-processing { border: dotted var(--pod-link-color); @@ -1538,6 +1553,11 @@ table .alert.alert-danger.btn.pod-btn-social { width: fit-content; } +/* Drag over styles */ +.dragover .form-group.hide-on-processing { + border: solid var(--bs-form-valid-border-color); +} + /** Esup-Pod callout messages **/ .pod-callout { padding: 0.7rem 1rem; @@ -1625,7 +1645,6 @@ a.player-element.disabled { } .podfile-icon-selector { - /* display: inline-block; */ width: 1.6em; padding: 1.7em 1em 0; margin-top: 0.1em; diff --git a/pod/main/static/js/admin.js b/pod/main/static/js/admin.js new file mode 100644 index 0000000000..7464bdd86b --- /dev/null +++ b/pod/main/static/js/admin.js @@ -0,0 +1,127 @@ +/** + * @file Esup-Pod script for admin panel. + * @since 3.6.0 + */ +const selectedType = document.getElementById("id_type"); +const selectedDataType = document.getElementById("id_data_type"); +fieldDataType = document.getElementsByClassName("field-data_type")[0]; +fieldChannel = document.getElementsByClassName("field-Channel")[0]; +fieldTheme = document.getElementsByClassName("field-Theme")[0]; +fieldPlaylist = document.getElementsByClassName("field-Playlist")[0]; +fieldHtml = document.getElementsByClassName("field-html")[0]; + +/** + * Function for show field. + * + * @param {HTMLElement} field The field to show. + */ +function showField(field) { + field.classList.remove("d-none"); + field.classList.add("d-block"); +} + +/** + * Function for hide field. + * + * @param {HTMLElement} field The field to hide. + */ +function hideField(field) { + field.classList.remove("d-block"); + field.classList.add("d-none"); +} + +/** + * Function init. + */ +function initializeFieldDisplay() { + if (selectedType && selectedDataType) { + if (selectedType.value === "html") { + showField(fieldHtml); + hideField(fieldDataType); + } else { + hideField(fieldHtml); + showField(fieldDataType); + } + + switch (selectedDataType.value) { + case "channel": + showField(fieldChannel); + break; + case "theme": + showField(fieldTheme); + break; + case "playlist": + showField(fieldPlaylist); + break; + default: + hideField(fieldChannel); + hideField(fieldTheme); + hideField(fieldPlaylist); + break; + } + } +} + +/** + * Listen selectedType and selectedDataType if change. + */ +if (selectedType) { + selectedType.addEventListener("change", function () { + handleTypeChange(); + handleDataTypeChange(); + }); +} + +if (selectedDataType) { + selectedDataType.addEventListener("change", handleDataTypeChange); +} + +/** + * Function change Type. + */ +function handleTypeChange() { + if (selectedType && selectedDataType) { + if (selectedType.value === "html") { + showField(fieldHtml); + hideField(fieldDataType); + } else { + hideField(fieldHtml); + showField(fieldDataType); + } + } +} + +/** + * Function change Data Type. + */ +function handleDataTypeChange() { + if (selectedType && selectedDataType) { + switch (selectedDataType.value) { + case "channel": + showField(fieldChannel); + hideField(fieldTheme); + hideField(fieldPlaylist); + break; + case "theme": + showField(fieldTheme); + hideField(fieldChannel); + hideField(fieldPlaylist); + break; + case "playlist": + showField(fieldPlaylist); + hideField(fieldChannel); + hideField(fieldTheme); + break; + default: + hideField(fieldChannel); + hideField(fieldTheme); + hideField(fieldPlaylist); + break; + } + } +} + +/** + * Call init function. + */ +initializeFieldDisplay(); diff --git a/pod/main/templates/admin/base_site.html b/pod/main/templates/admin/base_site.html index be21e108bd..7284ea14da 100644 --- a/pod/main/templates/admin/base_site.html +++ b/pod/main/templates/admin/base_site.html @@ -41,3 +41,8 @@

    {{ site_header|default:_('D {% endblock %} {% block nav-global %}{% endblock %} + +{% block footer %} +{{ block.super }} + +{% endblock %} diff --git a/pod/main/templates/aside.html b/pod/main/templates/aside.html index ece1749753..59b8ed96f1 100644 --- a/pod/main/templates/aside.html +++ b/pod/main/templates/aside.html @@ -10,15 +10,12 @@

    diff --git a/pod/main/templates/base.html b/pod/main/templates/base.html index 56f4912265..6403535632 100644 --- a/pod/main/templates/base.html +++ b/pod/main/templates/base.html @@ -27,6 +27,8 @@ + + {% if DARKMODE_ENABLED == True %} {% endif %} @@ -36,7 +38,7 @@ {% if CSS_OVERRIDE %} {% endif %} - {% if SHOW_EVENTS_ON_HOMEPAGE and "live" in THIRD_PARTY_APPS %} + {% if "live" in THIRD_PARTY_APPS %} {% endif %} @@ -74,21 +76,30 @@ {% endif %}
    + {% if not request.GET.is_iframe %} +
    + {% block collapse_page_aside %} + + {% endblock collapse_page_aside %} +
    + {% endif %}
    + {% if not request.GET.is_iframe %} + + {% endif %}
    {% if not request.GET.is_iframe %} -
    - {% block collapse_page_aside %} - - {% endblock collapse_page_aside %} -
    {% if MAINTENANCE_SHEDULED %} {% endif %} @@ -119,21 +130,8 @@

    {{page_title|capfirst}}

    {% endblock main_page_title %} {% block page_content %}{% endblock page_content %}
    - {% if request.path == "/" %} - {% if SHOW_EVENTS_ON_HOMEPAGE and "live" in THIRD_PARTY_APPS %} - {% include "live/events_next.html" %} - {% endif %} - {% include "videos/last_videos.html" %} - {% endif %}
    - {% if not request.GET.is_iframe %} - - {% endif %}

    {% if USE_NOTIFICATIONS %} diff --git a/pod/main/templates/block/card_list.html b/pod/main/templates/block/card_list.html new file mode 100644 index 0000000000..1ddfa82932 --- /dev/null +++ b/pod/main/templates/block/card_list.html @@ -0,0 +1,24 @@ +{% load i18n %} {% load video_filters static %} {% if elements.count >= 1 %} + +
    +

    {{ title |capfirst|truncatechars:43 }}

    + + {% if type == "video" %} + {% include "videos/video_list.html" with videos=elements %} + {% elif type == "event" %} + {% include "live/events_list.html" with events=elements hide_counter=True %} + {% elif type == "last" %} + {% include "videos/video_list.html" with videos=elements %} + + {% endif %} +
    + {% endif %} diff --git a/pod/main/templates/block/carousel.html b/pod/main/templates/block/carousel.html new file mode 100644 index 0000000000..d7a5c80e55 --- /dev/null +++ b/pod/main/templates/block/carousel.html @@ -0,0 +1,99 @@ +{% load i18n %} {% load video_filters static %} {% if elements.count >= 1 %} + diff --git a/pod/main/templates/block/html.html b/pod/main/templates/block/html.html new file mode 100644 index 0000000000..726ba75837 --- /dev/null +++ b/pod/main/templates/block/html.html @@ -0,0 +1,3 @@ +{% load i18n %} {% load video_filters static %} + +
    {{ body|safe }}
    diff --git a/pod/main/templates/block/multi_carousel.html b/pod/main/templates/block/multi_carousel.html new file mode 100644 index 0000000000..9541bfe5c0 --- /dev/null +++ b/pod/main/templates/block/multi_carousel.html @@ -0,0 +1,109 @@ +{% load i18n %} {% load video_filters static %} {% if elements.count >= 1 %} + diff --git a/pod/main/templates/flatpages/default.html b/pod/main/templates/flatpages/default.html index 85834e5065..29c14c0484 100644 --- a/pod/main/templates/flatpages/default.html +++ b/pod/main/templates/flatpages/default.html @@ -1,4 +1,5 @@ {% extends 'base.html' %} +{% load flat_page_edito_filter %} {% block opengraph %}{% load video_filters static %} @@ -24,4 +25,13 @@ {% block page_content %} {{ flatpage.content }} + + {% if BLOCK %} + {% for link in BLOCK %} + {% if flatpage.url == link.page.url %} + {{link|edito:request|safe}} + {% endif %} + {% endfor %} + {% endif %} + {% endblock page_content %} diff --git a/pod/main/templates/navbar.html b/pod/main/templates/navbar.html index e35cfc35be..f3e0526a7f 100644 --- a/pod/main/templates/navbar.html +++ b/pod/main/templates/navbar.html @@ -54,7 +54,7 @@
    {% trans "Main menu" %} {% endif %} - {% if USE_PLAYLIST %} + {% if USE_PLAYLIST and USE_PROMOTED_PLAYLIST %} - {% endif %} + +
      + {% for vid in video.get_video_mp4 %} + {% if vid.source_file|file_exists %} +
    • +
      + {% csrf_token %} + + +
      +
    • + {% endif %} {% endfor %}
    - - + {% if video.get_video_mp3 %} {% trans 'Audio file:' %}
      @@ -275,173 +278,180 @@

    {% endif %} - - {% endif %} - - {% if video.document_set.all %} - {% trans 'Document:' %} -
      - {% for doc in video.document_set.all %} - {% if request.user.is_superuser or not doc.private %} -
    • -
      - {% csrf_token %} - - - {% if request.user.is_superuser %} - {% if doc.private %} - - {% else %} - - {% endif %} + {% if video.document_set.all %} + +  {% trans 'Document:' %} + +
        + {% for doc in video.document_set.all %} + {% if request.user.is_superuser or not doc.private %} +
      • + + {% csrf_token %} + + + {% if request.user.is_superuser %} + {% if doc.private %} + + {% else %} + {% endif %} -
      • - - {% endif %} - {% endfor%} -
      - {% endif %} - + {% endif %} + +
    • + {% endif %} + {% endfor%} +
    + {% endif %} -{% if video.is_draft == False or video.owner == request.user or request.user in video.additional_owners.all%} -