diff --git a/assets/images/project_not_found.jpg b/assets/images/project_not_found.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65bfcbf0ba70ae5d9fa17f1a4820d582845ce217 Binary files /dev/null and b/assets/images/project_not_found.jpg differ diff --git a/src/Controller/CapsuleController.php b/src/Controller/CapsuleController.php index 039087a3d5f81f33da31b630c650cd968b005a61..59797db643231b7efc74416817fa59cb315e3e26 100644 --- a/src/Controller/CapsuleController.php +++ b/src/Controller/CapsuleController.php @@ -10,6 +10,7 @@ use App\Helper\StringHelper; use App\Repository\CapsuleRepository; use App\Builder\CapsuleBuilder; use App\Form\CreateCapsuleFormType; +use App\Retriever\ProjectRetriever; use Knp\Component\Pager\PaginatorInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Filesystem\Filesystem; @@ -22,10 +23,14 @@ use Symfony\Contracts\Translation\TranslatorInterface; class CapsuleController extends AbstractController { + private CapsuleRepository $capsule_repository; private TranslatorInterface $translator; - public function __construct(TranslatorInterface $translator) - { + public function __construct( + CapsuleRepository $capsule_repository, + TranslatorInterface $translator + ) { + $this->capsule_repository = $capsule_repository; $this->translator = $translator; } @@ -94,7 +99,7 @@ class CapsuleController extends AbstractController { $file_path = '../legacy/' . $path; if (!file_exists($file_path)) { - return $this->render('capsule/capsule_not_found.html.twig'); + return $this->render('project/project_not_found.html.twig'); } $url = $this->getParameter('app.legacy_external_prefix') . '/' . $path . "/?w=1"; @@ -105,10 +110,35 @@ class CapsuleController extends AbstractController ); } + /** + * @Route("capsule/{capsule_id}/video_preview", name="video_preview") + */ + public function videoPreview(int $capsule_id): Response + { + chdir('../legacy/'); + $file_system = new Filesystem(); + $capsule = $this->capsule_repository->findOneBy(['id' => $capsule_id]); + + if (! $capsule instanceof Capsule) { + throw new \Exception('The retrieved capsule is not an instance of Capsule.'); + } + + if (! $file_system->exists($capsule->getLinkPath())) { + return $this->render('project/project_not_found.html.twig'); + } + + $video_preview_image_link = $this->getVideoPreviewImageLink($capsule); + + return $this->render( + 'project/project_preview.html.twig', + ['video_preview_url' => $video_preview_image_link] + ); + } + /** * @Route("capsule/edit/{path}", name="edit_capsule") */ - public function edit(string $path, CapsuleRepository $capsuleRepository, TranslatorInterface $translator): Response + public function edit(string $path): Response { $current_user = $this->getUser(); @@ -116,8 +146,7 @@ class CapsuleController extends AbstractController return $this->redirectToRoute('app_logout'); } - $capsule = $capsuleRepository->findOneBy(['link_path' => $path]); - + $capsule = $this->capsule_repository->findOneBy(['link_path' => $path]); if (null === $capsule) { $this->addFlash('warning', $translator->trans('capsule.edit.not_found')); return $this->redirectToRoute('capsule_list'); @@ -130,7 +159,7 @@ class CapsuleController extends AbstractController $file_path = '../legacy/' . $path; if (!file_exists($file_path)) { - return $this->render('capsule/capsule_not_found.html.twig'); + return $this->render('project/project_not_found.html.twig'); } $url = $this->getParameter('app.legacy_external_prefix') . '/' . $capsule->getEditionLink(); @@ -298,4 +327,17 @@ class CapsuleController extends AbstractController return $capsule; } + + private function getVideoPreviewImageLink(Capsule $capsule): string + { + $project_retriever = new ProjectRetriever($capsule); + $video_url = $project_retriever->getVideoUrl(); + $video_id = $project_retriever->getVideoId($video_url); + + if (strpos($video_url, 'yout') !== false) { + return 'https://img.youtube.com/vi/' . $video_id . '/maxresdefault.jpg'; + } + + return 'https://vumbnail.com/' . $video_id . '.jpg'; + } } diff --git a/src/Entity/Capsule.php b/src/Entity/Capsule.php index 4e5249f4215ec05acdd33bcf933604fc6bf5bdaa..28ecd46c74942f9c9dd35e5c1cb3fd405f59a64b 100644 --- a/src/Entity/Capsule.php +++ b/src/Entity/Capsule.php @@ -3,10 +3,12 @@ namespace App\Entity; use App\Entity\User; +use App\Retriever\ProjectRetriever; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Security\Core\User\UserInterface; /** diff --git a/src/Retriever/ProjectRetriever.php b/src/Retriever/ProjectRetriever.php new file mode 100644 index 0000000000000000000000000000000000000000..28e70537f7783401cd249e1b3e1a79b7c522b1e5 --- /dev/null +++ b/src/Retriever/ProjectRetriever.php @@ -0,0 +1,71 @@ +<?php + +namespace App\Retriever; + +use App\Entity\Capsule; + +class ProjectRetriever +{ + private Capsule $capsule; + + public function __construct(Capsule $capsule) + { + $this->capsule = $capsule; + } + + private static function getDomDocument(): \DOMDocument + { + chdir('../legacy/'); + + $dom_xml = new \DOMDocument(); + $dom_xml->preserveWhiteSpace = false; + return $dom_xml; + } + + public function getVideoUrl(): string + { + $dom_xml = self::getDomDocument(); + + $dom_xml->load($this->capsule->getLinkPath() . '/file/project.xml'); + $video_node = $dom_xml->getElementsByTagName('video')->item(0); + + if ($video_node === null) { + throw new \Exception("Video node could not be found in XML project file"); + } + + return $video_node->getAttribute('url'); + } + + public function getVideoId(string $video_url): string + { + if (strpos($video_url, "vimeo")) { + return $this->getVimeoVideoIdFromUrl($video_url); + } + return $this->getYoutubeVideoIdFromUrl($video_url); + } + + private function getYoutubeVideoIdFromUrl(string $video_url): string + { + $pattern = "#(?<=v=|v\/|vi=|vi\/|youtu.be\/)[a-zA-Z0-9_-]{11}#"; + $result = preg_match($pattern, $video_url, $matches); + + $video_id = ""; + + if (false !== $result) { + $video_id = $matches[0]; + } + + return $video_id; + } + + private function getVimeoVideoIdFromUrl(string $video_url): string + { + $video_id = ""; + $pattern = "/https?:\/\/(?:www\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|album\/(\d+)\/video\/|)(\d+)(?:$|\/|\?)/"; //phpcs:ignore + + if (preg_match($pattern, $video_url, $id)) { + $video_id = $id[3]; + } + return $video_id; + } +} diff --git a/templates/capsule/capsule_not_found.html.twig b/templates/capsule/capsule_not_found.html.twig deleted file mode 100644 index 8a1484e9239e7201d119404934760ae0005bae8a..0000000000000000000000000000000000000000 --- a/templates/capsule/capsule_not_found.html.twig +++ /dev/null @@ -1,28 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <meta charset="UTF-8"> - <title>{% block title %}MemoRekall{% endblock %}</title> - - {% block stylesheets %} - {{ encore_entry_link_tags('app') }} - {% endblock %} - - {% block javascripts %} - {{ encore_entry_script_tags('app') }} - {% endblock %} - - <link rel="icon" type="image/x-icon" href="{{ asset('build/images/favicon.ico') }}" /> - -</head> - -<body class="container col-10 col-md-8 col-lg-6 m-auto"> - <div class="position-relative d-flex flex-row align-items-center justify-content-center mb-5"> - <img id="header-memorekall-logo" class="memorekall-logo" src="{{ asset('build/images/MemoRekall.png') }}"> - </div> - <div class="m-auto d-flex flex-row align-items-center justify-content-center mb-5 alert alert-warning" role="status"> - <span class="p-5">{{ 'capsule.not_found' | trans }}</span> - </div> - -</body> -</html> diff --git a/templates/capsule/index.html.twig b/templates/capsule/index.html.twig index f12717ee5238959b9032b5fd571041eb9a115c1d..23bb03df93fff5bc68fc3633e493489ce73b7b51 100644 --- a/templates/capsule/index.html.twig +++ b/templates/capsule/index.html.twig @@ -54,17 +54,26 @@ </div> </div> - <div class="d-flex flex-column flex-md-row justify-content-center align-items-center"> - <div class="m-4 ratio ratio-16x9"> - <iframe - src="/capsule/preview/{{ capsule.getLinkPath() }}" - allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" - allowfullscreen> - </iframe> - </div> + <div class="d-flex flex-column flex-md-row justify-content-center align-items-center"> + <div class="m-4 ratio ratio-16x9"> +{# <img src="{{ capsule.getVideoPreviewImageLink() }}" alt="video preview">#} + {# <img src="/capsule/{{ capsule.getId() }}/video_preview" alt="video preview">#} + </div> - <div class="d-flex flex-column justify-content-center m-2 pe-2"> - <i class="fa-thin fa-gears"></i> +{# {% if 'yout' in video_url %}#} +{# <img src="https://img.youtube.com/vi/{{ capsule.getVideoId() }}maxresdefault.jpg">#} +{# {% else %}#} +{# <img src="https://vumbnail.com/{{ capsule.getVideoId() }}.jpg">#} + {# <div class="m-4 ratio ratio-16x9">#} +{# <iframe#} +{# src="/capsule/preview/{{ capsule.getLinkPath() }}"#} +{# allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"#} +{# allowfullscreen>#} +{# </iframe>#} +{# </div>#} + + <div class="d-flex flex-column justify-content-center m-2 pe-2"> + <i class="fa-thin fa-gears"></i> <div class="list-item text-nowrap"> <a href="/capsule/{{ capsule.getId() }}/editors" class="links text-decoration-none"> diff --git a/templates/project/project_not_found.html.twig b/templates/project/project_not_found.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..0112bbe8103816565d42a611c18f10a6537a7ac0 --- /dev/null +++ b/templates/project/project_not_found.html.twig @@ -0,0 +1,13 @@ +{% extends 'layout.html.twig' %} + +{% block title %} + {{ 'project.not_found'|trans }} + - + {{ parent() }} +{% endblock %} + +{% block body %} + <div class="col-10 col-md-8 col-lg-6 col-xl-5 col-xxl-6 m-auto d-flex flex-row align-items-center justify-content-center mb-5 alert alert-warning" role="status"> + <span class="p-5">{{ 'project.not_found' | trans }}</span> + </div> +{% endblock %} \ No newline at end of file diff --git a/tests/functional/CapsuleControllerTest.php b/tests/functional/CapsuleControllerTest.php index 96c813689e90cac2d62731df13d7defe7f4e2ce2..2282b8c4d4b3835cdc135f744c64f8ca4cf71a11 100644 --- a/tests/functional/CapsuleControllerTest.php +++ b/tests/functional/CapsuleControllerTest.php @@ -45,7 +45,7 @@ class CapsuleControllerTest extends WebTestCase $submit_button = $crawler->selectButton('Create a capsule'); $form = $submit_button->form(); - $video_url = "https://TestUrl"; + $video_url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstley"; $form['create_capsule_form[name]'] = self::CAPSULE_NAME; $form['create_capsule_form[video_url]'] = $video_url; diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index dca9f64e3b542dc44664919219c6ea725cb0c09b..ad0f150076247179414da7228ca4caec93ae8a1b 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -113,47 +113,4 @@ user: change_password: Change password updated_success: The password has been updated edit_profile: Edit my profile - edit_password: Edit my password - -editors: - title: Editors - title_name: Editors of capsule %capsule_name% - add_email_address: Add new editor with email address - current_editors_title: Current editors - pending_editors_title: Pending editors - user_not_editor_error: You are not editor of the capsule - add: - pending_editor: - success: The user user_email has been added to pending editor list. - He will receive an email to invite him register on MemoRekall and to inform him he has been added as an editor of this capsule. - already_added: The user user_email has already been added to pending editor list - email: - title: Invitation to edit a MemoRekall capsule - text: You have been added by %user_name% as editor of the capsule "%capsule_name%". - In order to access and edit it, you first need to register on MemoRekall. Please follow this link to - link: https://project.memorekall.com/register/ - link_name: register - user: - success: The user user_email is now an editor of the capsule capsule_name. - He will receive an email to inform him he has been added as an editor of this capsule. - already_added: The user user_email is already an editor of this capsule - email: - title: New capsule on your list - text: You have been add by %user_name% as editor of the capsule "%capsule_name%". - You can now access and edit it. You will find the capsule in your capsule list. - link: Go to capsule edition page - remove: - button: Remove - pending_editor: - title: Remove pending editor - text: Do you really want to delete pending editor %editor_email%? - link: Remove pending editor - error: The email address has already been removed from pending editors of the capsule capsule_name - success: The email address pending_editor_email has been successfully removed from pending editors of the capsule capsule_name - editor: - title: Remove editor - text: Do you really want to delete editor %editor_email%? - link: Remove editor - success: User editor_email is no longer and editor of the capsule capsule_name - error: You can't remove yourself as editor of your own capsule. - If you want to remove the capsule from your list, go to the capsule list page and click on "delete capsule" \ No newline at end of file + edit_password: Edit my password \ No newline at end of file diff --git a/translations/messages.fr.yaml b/translations/messages.fr.yaml index a6502ad168de0a1998946982b5345b0e7fe95e2b..f9bee5fd65ef2b32435672c1bf5662a50ea9087a 100644 --- a/translations/messages.fr.yaml +++ b/translations/messages.fr.yaml @@ -62,6 +62,7 @@ capsule: created_success: La capsule capsule_name a été créée no_edition_access: Pas d'accès au mode édition contact_capsule_author_for_access: Veuillez contacter l'auteur de la capsule pour lui demander son accès en mode édition + not_found: Le projet n'existe pas edit_permissions: link: Modifier les permissions edit: