Skip to content
Snippets Groups Projects
Commit c429b876 authored by Sebastien's avatar Sebastien
Browse files

Merge branch 'tuleap-133-change-the-video-url-of-a-capsule' into 'main'

tuleap-133-change-the-video-url-of-a-capsule

See merge request !48
parents c0fa2081 d9cd0174
Branches
Tags
1 merge request!48tuleap-133-change-the-video-url-of-a-capsule
Pipeline #781 passed
...@@ -3,12 +3,18 @@ ...@@ -3,12 +3,18 @@
namespace App\Controller; namespace App\Controller;
use App\Entity\Capsule; use App\Entity\Capsule;
use App\Entity\PendingEditorInvitation;
use App\Exception\ZipArchiveNotOpeningException; use App\Exception\ZipArchiveNotOpeningException;
use App\Form\EditVideoUrlFormType;
use App\Form\RemoveEditorFormType;
use App\Repository\CapsuleRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\FrameworkBundle\Translation\Translator;
use Symfony\Component\Config\Util\Exception\XmlParsingException; use Symfony\Component\Config\Util\Exception\XmlParsingException;
use Symfony\Component\Filesystem\Exception\FileNotFoundException; use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\Exception\FileException; use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
...@@ -16,12 +22,18 @@ use ZipArchive; ...@@ -16,12 +22,18 @@ use ZipArchive;
class ProjectController extends AbstractController class ProjectController extends AbstractController
{ {
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
/** /**
* @Route("/project/create", name="create_project", methods={"POST"}) * @Route("/project/create", name="create_project", methods={"POST"})
* @throws ZipArchiveNotOpeningException * @throws ZipArchiveNotOpeningException
*/ */
public function create( public function create(
TranslatorInterface $translator,
Capsule $capsule, Capsule $capsule,
string $video_url string $video_url
): Response { ): Response {
...@@ -33,7 +45,7 @@ class ProjectController extends AbstractController ...@@ -33,7 +45,7 @@ class ProjectController extends AbstractController
if (file_exists($capsule_directory)) { if (file_exists($capsule_directory)) {
$this->addFlash( $this->addFlash(
'warning', 'warning',
$translator->trans( $this->translator->trans(
'project.already_exists', 'project.already_exists',
[ [
'capsule_name' => $capsule_name 'capsule_name' => $capsule_name
...@@ -46,13 +58,13 @@ class ProjectController extends AbstractController ...@@ -46,13 +58,13 @@ class ProjectController extends AbstractController
$this->extractZipArchiveInNewCapsuleDirectory($capsule_directory); $this->extractZipArchiveInNewCapsuleDirectory($capsule_directory);
$this->addProjectVideoUrlInXMLProjectFile($capsule_directory, $video_url); $this->setVideoUrlNodeAttributeInXMLProjectFile($capsule_directory, $video_url);
$this->createOrUpdatePasswordFile($capsule_directory, $capsule->getPassword()); $this->createOrUpdatePasswordFile($capsule_directory, $capsule->getPassword());
$this->addFlash( $this->addFlash(
'success', 'success',
$translator->trans( $this->translator->trans(
'capsule.created_success', 'capsule.created_success',
[ [
'capsule_name' => $capsule_name 'capsule_name' => $capsule_name
...@@ -68,7 +80,6 @@ class ProjectController extends AbstractController ...@@ -68,7 +80,6 @@ class ProjectController extends AbstractController
* @throws ZipArchiveNotOpeningException * @throws ZipArchiveNotOpeningException
*/ */
public function duplicate( public function duplicate(
TranslatorInterface $translator,
Capsule $capsule, Capsule $capsule,
string $parent_directory, string $parent_directory,
Filesystem $file_system Filesystem $file_system
...@@ -81,7 +92,7 @@ class ProjectController extends AbstractController ...@@ -81,7 +92,7 @@ class ProjectController extends AbstractController
if (file_exists($capsule_directory)) { if (file_exists($capsule_directory)) {
$this->addFlash( $this->addFlash(
'warning', 'warning',
$translator->trans( $this->translator->trans(
'project.already_exists', 'project.already_exists',
[ [
'capsule_name' => $capsule_name 'capsule_name' => $capsule_name
...@@ -98,7 +109,7 @@ class ProjectController extends AbstractController ...@@ -98,7 +109,7 @@ class ProjectController extends AbstractController
$this->addFlash( $this->addFlash(
'success', 'success',
$translator->trans( $this->translator->trans(
'capsule.duplicate.success', 'capsule.duplicate.success',
[ [
'capsule_name' => $capsule_name 'capsule_name' => $capsule_name
...@@ -125,9 +136,9 @@ class ProjectController extends AbstractController ...@@ -125,9 +136,9 @@ class ProjectController extends AbstractController
$zip->close(); $zip->close();
} }
private function addProjectVideoUrlInXMLProjectFile(string $capsule_directory, string $video_url): void private function setVideoUrlNodeAttributeInXMLProjectFile(string $project_directory, string $video_url): void
{ {
$project_xml_file = $capsule_directory . "/file/project.xml"; $project_xml_file = $project_directory . "/file/project.xml";
$xml_file_content = simplexml_load_file($project_xml_file); $xml_file_content = simplexml_load_file($project_xml_file);
if (false === $xml_file_content) { if (false === $xml_file_content) {
...@@ -162,4 +173,62 @@ class ProjectController extends AbstractController ...@@ -162,4 +173,62 @@ class ProjectController extends AbstractController
['override' => true] ['override' => true]
); );
} }
/**
* @Route("/capsule/{capsule_id}/edit_video_url", name="edit_video_url")
*/
public function editVideoUrl(
int $capsule_id,
Request $request,
CapsuleRepository $capsule_repository
): Response {
$capsule = $capsule_repository->findOneBy(['id' => $capsule_id]);
if (! $capsule instanceof Capsule) {
throw new \Exception('The retrieved capsule is not an instance of Capsule.');
}
$file_system = new Filesystem();
$parent_directory_name = $capsule->getLinkPath();
$parent_directory_exists = $file_system->exists('../legacy/' . $parent_directory_name);
if (! $parent_directory_exists) {
$this->addFlash(
'warning',
$this->translator->trans(
'project.not_exist',
[
'capsule_name' => $capsule->getName()
]
)
);
return $this->redirectToRoute('capsule_list');
}
$form = $this->createForm(EditVideoUrlFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$new_video_url = $form->get('new_video_url')->getData();
$project_directory = $capsule->getLinkPath();
chdir('../legacy/');
$this->setVideoUrlNodeAttributeInXMLProjectFile($project_directory, $new_video_url);
$this->addFlash(
'success',
$this->translator->trans(
'capsule.edit.video_url.success'
)
);
return $this->redirectToRoute('capsule_list', [
'capsule_id' => $capsule_id
]);
}
return $this->render('project/edit_video_url.html.twig', [
'editVideoUrlForm' => $form->createView()
]);
}
} }
<?php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\UrlType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
class EditVideoUrlFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add(
'new_video_url',
UrlType::class,
[
'constraints' => [new NotBlank(['message' => 'capsule.video_url.not_blank'])],
'label' => 'capsule.edit.video_url.text',
'empty_data' => ''
]
)
->add(
'save',
SubmitType::class,
['label' => 'general.save']
);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([]);
}
}
...@@ -9,7 +9,9 @@ ...@@ -9,7 +9,9 @@
<div> <div>
<div class="row w-100 gx-0"> <div class="row w-100 gx-0">
<div class="row-title-box"> <div class="row-title-box">
<h3 class="row-title">Create a capsule</h3> <h3 class="row-title">
{{ 'capsule.create_capsule'|trans }}
</h3>
</div> </div>
</div> </div>
......
...@@ -72,6 +72,14 @@ ...@@ -72,6 +72,14 @@
{{ 'capsule.edit_permissions.link'|trans }} {{ 'capsule.edit_permissions.link'|trans }}
</a> </a>
</div> </div>
<div class="list-item text-nowrap">
<a href="/capsule/{{ capsule.getId() }}/edit_video_url" class="links text-decoration-none">
<i class="fas fa-link m-2"></i>
{{ 'capsule.edit.video_url.link'|trans }}
</a>
</div>
<div class="list-item text-nowrap"> <div class="list-item text-nowrap">
<a href="/capsule/duplicate/{{ capsule.getId() }}" class="links text-decoration-none"> <a href="/capsule/duplicate/{{ capsule.getId() }}" class="links text-decoration-none">
<i class="far fa-clone m-2"></i> <i class="far fa-clone m-2"></i>
......
{% extends 'layout.html.twig' %}
{% block title %}
{{ 'capsule.edit.video_url.title'|trans }}
{% endblock %}
{% block body %}
<div>
<div class="row w-100 gx-0">
<div class="row-title-box">
<h3 class="row-title">
{{ 'capsule.edit.video_url.title'|trans }}
</h3>
</div>
</div>
{{ form_start(editVideoUrlForm, {'attr': {novalidate: 'novalidate', 'class': 'd-flex flex-column justify-content-center'}}) }}
{{ form_row(editVideoUrlForm.new_video_url, {'row_attr': {'class' : 'm-auto mb-4 col-6'}}) }}
{{ form_row(editVideoUrlForm.save, {'row_attr': {'class' : 'm-auto mb-5 col-2'}}) }}
{{ form_end(editVideoUrlForm) }}
</div>
{% endblock %}
\ No newline at end of file
...@@ -46,12 +46,7 @@ class ProjectControllerTest extends WebTestCase ...@@ -46,12 +46,7 @@ class ProjectControllerTest extends WebTestCase
{ {
parent::tearDown(); parent::tearDown();
$capsule_repository = $this->object_manager->getRepository(Capsule::class); $capsule = $this->getLastCapsuleInDb();
$capsule = $capsule_repository->findOneBy(['name' => self::CAPSULE_NAME]);
if (! $capsule instanceof Capsule) {
throw new \Exception("Capsule does not exist.");
}
$this->deleteCapsuleDirectoryAndInDatabase($capsule); $this->deleteCapsuleDirectoryAndInDatabase($capsule);
} }
...@@ -234,6 +229,40 @@ class ProjectControllerTest extends WebTestCase ...@@ -234,6 +229,40 @@ class ProjectControllerTest extends WebTestCase
$this->deleteCapsuleDirectoryAndInDatabase($duplicated_capsule_in_db); $this->deleteCapsuleDirectoryAndInDatabase($duplicated_capsule_in_db);
} }
public function testVideoUrlNodeAttributeInXMLFileShouldBeUpdatedWhenUserEditVideoUrl(): void
{
$capsule = $this->createCapsule();
$crawler = $this->client->request('GET', '/capsule/' . $capsule->getId() . '/edit_video_url');
$this->assertResponseIsSuccessful();
$this->client->enableProfiler();
$submit_button = $crawler->selectButton('Save');
$this->form = $submit_button->form();
$new_video_url = "https://youtu.be/tQwVKr8rCYw";
$this->form['edit_video_url_form[new_video_url]'] = $new_video_url;
$this->client->submit($this->form);
$this->client->followRedirect();
$this->assertResponseIsSuccessful('/my_capsules');
$capsule_directory = $capsule->getLinkPath();
$this->assertDirectoryExists($capsule_directory);
$this->assertDirectoryIsReadable($capsule_directory);
$dom_xml = self::getDomDocument($capsule_directory);
$video_node = $dom_xml->getElementsByTagName('video')->item(0);
if (null === $video_node) {
throw new \Exception("Video node could not be found in XML project file");
}
$video_url_in_xml_file = $video_node->getAttribute('url');
$this->assertEquals($new_video_url, $video_url_in_xml_file);
}
private function assertAllFilesOfFileDirectoryAreSameExceptPasswordOne( private function assertAllFilesOfFileDirectoryAreSameExceptPasswordOne(
string $parent_capsule_directory, string $parent_capsule_directory,
string $duplicated_capsule_directory string $duplicated_capsule_directory
...@@ -254,7 +283,7 @@ class ProjectControllerTest extends WebTestCase ...@@ -254,7 +283,7 @@ class ProjectControllerTest extends WebTestCase
} }
} }
private function createCapsule(): void private function createCapsule(): Capsule
{ {
$crawler = $this->client->request('GET', '/create'); $crawler = $this->client->request('GET', '/create');
$this->assertResponseIsSuccessful(); $this->assertResponseIsSuccessful();
...@@ -285,6 +314,8 @@ class ProjectControllerTest extends WebTestCase ...@@ -285,6 +314,8 @@ class ProjectControllerTest extends WebTestCase
null, null,
['override' => true] ['override' => true]
); );
return $capsule;
} }
private function deleteCapsuleDirectoryAndInDatabase(Capsule $capsule): void private function deleteCapsuleDirectoryAndInDatabase(Capsule $capsule): void
...@@ -298,4 +329,16 @@ class ProjectControllerTest extends WebTestCase ...@@ -298,4 +329,16 @@ class ProjectControllerTest extends WebTestCase
$this->object_manager->remove($capsule_to_delete); $this->object_manager->remove($capsule_to_delete);
$this->object_manager->flush(); $this->object_manager->flush();
} }
private function getLastCapsuleInDb(): Capsule
{
$capsule_repository = $this->object_manager->getRepository(Capsule::class);
$capsule = $capsule_repository->findOneBy(['name' => self::CAPSULE_NAME]);
if (! $capsule instanceof Capsule) {
throw new \Exception("Capsule does not exist.");
}
return $capsule;
}
} }
...@@ -8,6 +8,7 @@ general: ...@@ -8,6 +8,7 @@ general:
link_expire: This link will expire in %expirationDuration% link_expire: This link will expire in %expirationDuration%
greeting: Cheers! greeting: Cheers!
validate: Validate validate: Validate
save: Save
login: login:
account_disabled_feedback: Your user account is disabled. Please click on the link your receive by email to validate your registration. account_disabled_feedback: Your user account is disabled. Please click on the link your receive by email to validate your registration.
...@@ -70,6 +71,11 @@ capsule: ...@@ -70,6 +71,11 @@ capsule:
link: Edit capsule link: Edit capsule
not_allowed: You are not allowed to edit this capsule not_allowed: You are not allowed to edit this capsule
not_found: The capsule was not found not_found: The capsule was not found
video_url:
link: Edit video URL
text: Enter a new video URL from Youtube or Vimeo
title: Edit video URL
success: The video URL of the project has been updated successfully
duplicate: duplicate:
link: Duplicate capsule link: Duplicate capsule
title: Duplicate capsule title: Duplicate capsule
...@@ -87,7 +93,7 @@ capsule: ...@@ -87,7 +93,7 @@ capsule:
project: project:
already_exists: Project capsule_name already exists so the capsule could not be created already_exists: Project capsule_name already exists so the capsule could not be created
not_exist: The project to duplicate does not exist not_exist: The project does not exist
preview: preview:
loading: Project loading loading: Project loading
......
...@@ -8,6 +8,7 @@ general: ...@@ -8,6 +8,7 @@ general:
go_back_to_home_page: Page d'accueil go_back_to_home_page: Page d'accueil
cancel_button: Annuler cancel_button: Annuler
validate: Valider validate: Valider
save: Enregistrer
login: login:
account_disabled_feedback: Le compte utilisateur a été désactivé. Veuillez cliquer sur le lien pour recevoir un courriel de validation account_disabled_feedback: Le compte utilisateur a été désactivé. Veuillez cliquer sur le lien pour recevoir un courriel de validation
...@@ -67,6 +68,11 @@ capsule: ...@@ -67,6 +68,11 @@ capsule:
link: Modifier la capsule link: Modifier la capsule
not_allowed: Vous n'êtes pas autorisé a modifier cette capsule not_allowed: Vous n'êtes pas autorisé a modifier cette capsule
not_found: La capsule n'existe pas not_found: La capsule n'existe pas
video_url:
link: Modifier l'URL de la vidéo
text: Saisir l'URL de la nouvelle vidéo Youtube ou Vimeo
title: Modifier l'URL de la vidéo
success: L'URL de la vidéo du projet a bien été mise à jour
duplicate: duplicate:
link: Dupliquer la capsule link: Dupliquer la capsule
title: Dupliquer la capsule title: Dupliquer la capsule
...@@ -85,7 +91,7 @@ capsule: ...@@ -85,7 +91,7 @@ capsule:
project: project:
already_exists: Le projet capsule_name existe déjà. La capsule n'a pas pu être créée already_exists: Le projet capsule_name existe déjà. La capsule n'a pas pu être créée
not_exist: Le projet a dupliquer n'existe pas not_exist: Le projet n'existe pas
preview: preview:
loading: Projet en cours de chargement loading: Projet en cours de chargement
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment