diff --git a/assets/styles/app.scss b/assets/styles/app.scss index 261d49f0073f098d5d9f9870ad51fe4d6b091630..bd5c6847ec762c895b35d46ff0331cf8e436379a 100644 --- a/assets/styles/app.scss +++ b/assets/styles/app.scss @@ -20,7 +20,6 @@ form input { background: rgba(0,0,0,.25); color: #fff; height: auto; - padding: 15px 10px; } form input[type=checkbox]{ diff --git a/config/packages/security.yaml b/config/packages/security.yaml index c1eba95ef3655200af0847acae0dfeec83ff14b2..908d1b02cc0da1aadc8f456f79eaf8daa0923b50 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -34,4 +34,4 @@ security: # switch_user: true access_control: - - { path: ^/(?!register|login|generate-captcha|verify/email|reset-password|capsule/preview|legacy), roles: ROLE_USER } \ No newline at end of file + - { path: ^/(?!register|login|generate-captcha|verify/email|reset-password|en/capsule/preview|legacy), roles: ROLE_USER } \ No newline at end of file diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml index abb76aae82db19944114e4c29c75ac913b18c897..1de35e06aaf527100a2751eb53462d7e39680df4 100644 --- a/config/packages/translation.yaml +++ b/config/packages/translation.yaml @@ -1,13 +1,6 @@ -framework: - default_locale: en - translator: - default_path: '%kernel.project_dir%/translations' - fallbacks: - - en -# providers: -# crowdin: -# dsn: '%env(CROWDIN_DSN)%' -# loco: -# dsn: '%env(LOCO_DSN)%' -# lokalise: -# dsn: '%env(LOKALISE_DSN)%' +#framework: +# default_locale: en +# translator: +# default_path: '%kernel.project_dir%/translations' +# fallbacks: +# - en \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 1cf8a0e394925c95f57d0608cf1c1647dd1bea6f..68a3cb7ba80082ca880a2d81e5ad589dc84cce25 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -25,3 +25,4 @@ parameters: app.legacy_url_external: '%env(LEGACY_URL_EXTERNAL)%' app.legacy_url: '%env(LEGACY_URL)%' app.legacy_external_prefix: '%env(LEGACY_EXTERNAL_PREFIX)%' + app.supported_locales: 'en|fr' diff --git a/migrations/Version20220311092854.php b/migrations/Version20220311092854.php new file mode 100644 index 0000000000000000000000000000000000000000..f1b10183f7ab52be407d3a35c32af460569c1d7a --- /dev/null +++ b/migrations/Version20220311092854.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace DoctrineMigrations; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\Migrations\AbstractMigration; + +/** + * Auto-generated Migration: Please modify to your needs! + */ +final class Version20220311092854 extends AbstractMigration +{ + public function getDescription(): string + { + return ''; + } + + public function up(Schema $schema): void + { + // this up() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE user ADD locale VARCHAR(255) NOT NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE capsule CHANGE nom nom VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE link link VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE edition_link edition_link VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE password password VARCHAR(50) NOT NULL COLLATE `utf8_unicode_ci`'); + $this->addSql('ALTER TABLE `group` CHANGE name name VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`'); + $this->addSql('ALTER TABLE invitation_editeur_capsule CHANGE email email VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`'); + $this->addSql('ALTER TABLE reset_password_request CHANGE selector selector VARCHAR(20) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE hashed_token hashed_token VARCHAR(100) NOT NULL COLLATE `utf8_unicode_ci`'); + $this->addSql('ALTER TABLE `user` DROP locale, CHANGE email email VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE email_canonical email_canonical VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE name name VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE firstname firstname VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE username username VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE username_canonical username_canonical VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE roles roles LONGTEXT NOT NULL COLLATE `utf8_unicode_ci` COMMENT \'(DC2Type:json)\', CHANGE password password VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`, CHANGE salt salt VARCHAR(255) NOT NULL COLLATE `utf8_unicode_ci`'); + } +} diff --git a/src/Builder/UserBuilder.php b/src/Builder/UserBuilder.php index 1b3cea4c6b3f8a2f513112f06eb762acf4f0e750..89292d434bd45f2796f934b92de05ebb16c2d330 100755 --- a/src/Builder/UserBuilder.php +++ b/src/Builder/UserBuilder.php @@ -96,6 +96,7 @@ class UserBuilder $this->user->setSalt(random_bytes(100)); $this->user->setPassword($this->password_hasher->hashPassword($this->user, $this->user->plainPassword)); $this->user->eraseCredentials(); + $this->user->setLocale('en'); return $this->user; } diff --git a/src/Command/UpdateUserLocale.php b/src/Command/UpdateUserLocale.php new file mode 100644 index 0000000000000000000000000000000000000000..3288d52059f53c8d028b59e986cbddbe28bd9736 --- /dev/null +++ b/src/Command/UpdateUserLocale.php @@ -0,0 +1,46 @@ +<?php + +namespace App\Command; + +use App\Repository\UserRepository; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +#[AsCommand( + name: 'rekall:set-default-user-locales', + description: 'Set default user locales for user that did not set it', + aliases: ['r:set-default-user-locale'], + hidden: false +)] +class UpdateUserLocale extends Command +{ + protected static $defaultName = 'rekall:set-default-user-locales'; + + public function __construct( + private UserRepository $user_repository, + private EntityManagerInterface $entity_manager + ) { + parent::__construct(); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('Setting locales for users that did not set it yet'); + + $all_users = $this->user_repository->findAll(); + foreach ($all_users as $user) { + if ($user->getLocale() === "") { + $user->setLocale('en'); + $this->entity_manager->persist($user); + } + } + $this->entity_manager->flush(); + + $output->writeln('User locales successfully set to english'); + + return Command::SUCCESS; + } +} diff --git a/src/Controller/CapsuleController.php b/src/Controller/CapsuleController.php index f230ff1bced86ca0c2f43ddc02cc3c500ed9c46a..b29733250dcc8901215d1eee9a7f5341295d69d1 100755 --- a/src/Controller/CapsuleController.php +++ b/src/Controller/CapsuleController.php @@ -25,6 +25,8 @@ use Symfony\Contracts\Translation\TranslatorInterface; class CapsuleController extends AbstractController { + private const DEFAULT_LOCALE = 'en'; + public function __construct( private CapsuleRepository $capsule_repository, private TranslatorInterface $translator, @@ -32,8 +34,30 @@ class CapsuleController extends AbstractController ) { } - #[Route('/my_capsules', name:'capsule_list')] - #[Route('/', name:'home')] + + #[Route('/')] + public function indexNoLocale(Request $request): Response + { + $locale = self::DEFAULT_LOCALE; + $current_user = $this->getUser(); + + if (! $current_user instanceof User) { + return $this->redirectToRoute('app_logout'); + } + + if ($request->getLocale()) { + $locale = $request->getLocale(); + } + + if ($current_user->getLocale()) { + $locale = $current_user->getLocale(); + } + + return $this->redirectToRoute('home', ['_locale' => $locale]); + } + + #[Route('/{_locale<%app.supported_locales%>}/my_capsules', name:'capsule_list')] + #[Route('/{_locale<%app.supported_locales%>}', name:'home')] public function index( PaginatorInterface $paginator, Request $request @@ -77,7 +101,7 @@ class CapsuleController extends AbstractController ]); } - #[Route('/create', name:'create_capsule')] + #[Route('/{_locale<%app.supported_locales%>}/create', name:'create_capsule')] public function create(Request $request): Response { $form = $this->createForm(CreateCapsuleFormType::class); @@ -104,7 +128,7 @@ class CapsuleController extends AbstractController ]); } - #[Route('capsule/preview/{path}', name:'preview_capsule')] + #[Route('/{_locale<%app.supported_locales%>}/capsule/preview/{path}', name:'preview_capsule')] public function preview(string $path): Response { $file_path = '../legacy/' . $path; @@ -120,7 +144,7 @@ class CapsuleController extends AbstractController ); } - #[Route('/capsule/edit/{path}', name:'edit_capsule')] + #[Route('/{_locale<%app.supported_locales%>}/capsule/edit/{path}', name:'edit_capsule')] public function edit(string $path): Response { $current_user = $this->getUser(); @@ -153,9 +177,11 @@ class CapsuleController extends AbstractController ); } - #[Route('/capsule/delete/{id}', name:'delete_capsule')] - public function delete(int $id, Request $request): Response - { + #[Route('/{_locale<%app.supported_locales%>}/capsule/delete/{capsule_id}', name:'delete_capsule')] + public function delete( + int $capsule_id, + Request $request + ): Response { $form = $this->createForm(DeleteCapsuleFormType::class); $form->handleRequest($request); $current_user = $this->getUser(); @@ -164,11 +190,11 @@ class CapsuleController extends AbstractController return $this->redirectToRoute('app_logout'); } - $capsule = $this->capsule_repository->findOneBy(['id' => $id]); + $capsule = $this->capsule_repository->findOneBy(['id' => $capsule_id]); if (! $capsule) { throw $this->createNotFoundException( - 'No capsule found for id ' . $id + 'No capsule found for id ' . $capsule_id ); } $capsule_name = $capsule->getName(); @@ -212,9 +238,9 @@ class CapsuleController extends AbstractController ]); } - #[Route('/capsule/duplicate/{id}', name:'duplicate_capsule')] + #[Route('/{_locale<%app.supported_locales%>}/capsule/duplicate/{capsule_id}', name:'duplicate_capsule')] public function duplicate( - int $id, + int $capsule_id, Request $request, Filesystem $file_system ): Response { @@ -226,7 +252,7 @@ class CapsuleController extends AbstractController return $this->redirectToRoute('app_logout'); } - $parent_capsule = $this->capsule_repository->findOneBy(['id' => $id]); + $parent_capsule = $this->capsule_repository->findOneBy(['id' => $capsule_id]); if (! $parent_capsule instanceof Capsule) { throw new \Exception('The retrieved capsule is not an instance of Capsule.'); diff --git a/src/Controller/CapsuleEditorController.php b/src/Controller/CapsuleEditorController.php index 0cc7c3de8b75de491138d3b38e1fcf4252f17827..f2685fd1c8ca65201b82238c000bc60275eaa83a 100755 --- a/src/Controller/CapsuleEditorController.php +++ b/src/Controller/CapsuleEditorController.php @@ -34,7 +34,7 @@ class CapsuleEditorController extends AbstractController ) { } - #[Route('/capsule/{capsule_id}/editors', name:'edit_capsule_editors')] + #[Route('/{_locale<%app.supported_locales%>}/capsule/{capsule_id}/editors', name:'edit_capsule_editors')] public function editCapsuleEditors( Request $request, int $capsule_id @@ -102,109 +102,10 @@ class CapsuleEditorController extends AbstractController ]); } - private function addPendingEditor(string $editor_email, Capsule $capsule, User $current_user): void - { - $pending_editors_emails = $this->capsule_pending_editor_invitation_repository - ->getPendingEditorsEmails($capsule->getId()); - - if (in_array($editor_email, $pending_editors_emails)) { - $this->addFlash( - 'warning', - $this->translator->trans( - 'editors.add.pending_editor.already_added', - [ - 'user_email' => $editor_email - ] - ) - ); - return; - } - - $pending_editor_invitation = new PendingEditorInvitation(); - $pending_editor_invitation->setCapsuleId($capsule->getId()); - $pending_editor_invitation->setEmail($editor_email); - $this->entity_manager->persist($pending_editor_invitation); - $this->entity_manager->flush(); - - $email = (new TemplatedEmail()) - ->to($editor_email) - ->subject($this->translator->trans('editors.add.pending_editor.email.title')) - ->htmlTemplate('capsule/editors/email_pending_editor.html.twig') - ->context([ - 'user' => $current_user, - 'capsule' => $capsule - ]); - - $this->mailer->send($email); - - $this->addFlash( - 'success', - $this->translator->trans( - 'editors.add.pending_editor.success', - [ - 'user_email' => $editor_email - ] - ) - ); - } - - /** - * @param array<User> $current_capsule_editors_users - * @throws TransportExceptionInterface - */ - private function addEditor( - string $editor_email, - Capsule $capsule, - User $current_user, - User $user_associated_with_email_address, - array $current_capsule_editors_users - ): void { - if (in_array($user_associated_with_email_address, $current_capsule_editors_users)) { - $this->addFlash( - 'warning', - $this->translator->trans( - 'editors.add.user.already_added', - [ - 'user_email' => $editor_email - ] - ) - ); - return; - } - - $capsule->addEditor($user_associated_with_email_address); - $this->entity_manager->persist($capsule); - $this->entity_manager->flush(); - - $email = (new TemplatedEmail()) - ->to($editor_email) - ->subject($this->translator->trans('editors.add.user.email.title')) - ->htmlTemplate('capsule/editors/email_editor.html.twig') - ->context([ - 'user' => $current_user, - 'capsule' => $capsule, - 'capsule_edit_link' => $this->urlGenerator->generate( - 'edit_capsule', - [ 'path' => $capsule->getLinkPath() ], - UrlGeneratorInterface::ABSOLUTE_URL - ) - ]); - - $this->mailer->send($email); - - $this->addFlash( - 'success', - $this->translator->trans( - 'editors.add.user.success', - [ - 'capsule_name' => $capsule->getName(), - 'user_email' => $editor_email - ] - ) - ); - } - - #[Route('/capsule/{capsule_id}/editors/{editor_id}/remove', name:'remove_editor')] + #[Route( + '/{_locale<%app.supported_locales%>}/capsule/{capsule_id}/editors/{editor_id}/remove', + name:'remove_editor' + )] public function removeEditor( int $capsule_id, int $editor_id, @@ -272,7 +173,10 @@ class CapsuleEditorController extends AbstractController ]); } - #[Route('/capsule/{capsule_id}/pending_editor/{pending_editor_invitation_id}/remove', name:'remove_pending_editor')] + #[Route( + '/{_locale<%app.supported_locales%>}/capsule/{capsule_id}/pending_editor/{pending_editor_invitation_id}/remove', + name:'remove_pending_editor' + )] public function removePendingEditor( int $pending_editor_invitation_id, int $capsule_id, @@ -331,4 +235,106 @@ class CapsuleEditorController extends AbstractController 'capsule_id' => $capsule_id ]); } + + private function addPendingEditor(string $editor_email, Capsule $capsule, User $current_user): void + { + $pending_editors_emails = $this->capsule_pending_editor_invitation_repository + ->getPendingEditorsEmails($capsule->getId()); + + if (in_array($editor_email, $pending_editors_emails)) { + $this->addFlash( + 'warning', + $this->translator->trans( + 'editors.add.pending_editor.already_added', + [ + 'user_email' => $editor_email + ] + ) + ); + return; + } + + $pending_editor_invitation = new PendingEditorInvitation(); + $pending_editor_invitation->setCapsuleId($capsule->getId()); + $pending_editor_invitation->setEmail($editor_email); + $this->entity_manager->persist($pending_editor_invitation); + $this->entity_manager->flush(); + + $email = (new TemplatedEmail()) + ->to($editor_email) + ->subject($this->translator->trans('editors.add.pending_editor.email.title')) + ->htmlTemplate('capsule/editors/email_pending_editor.html.twig') + ->context([ + 'user' => $current_user, + 'capsule' => $capsule + ]); + + $this->mailer->send($email); + + $this->addFlash( + 'success', + $this->translator->trans( + 'editors.add.pending_editor.success', + [ + 'user_email' => $editor_email + ] + ) + ); + } + + /** + * @param array<User> $current_capsule_editors_users + * @throws TransportExceptionInterface + */ + private function addEditor( + string $editor_email, + Capsule $capsule, + User $current_user, + User $user_associated_with_email_address, + array $current_capsule_editors_users + ): void { + if (in_array($user_associated_with_email_address, $current_capsule_editors_users)) { + $this->addFlash( + 'warning', + $this->translator->trans( + 'editors.add.user.already_added', + [ + 'user_email' => $editor_email + ] + ) + ); + return; + } + + $capsule->addEditor($user_associated_with_email_address); + $this->entity_manager->persist($capsule); + $this->entity_manager->flush(); + + $email = (new TemplatedEmail()) + ->to($editor_email) + ->subject($this->translator->trans('editors.add.user.email.title')) + ->htmlTemplate('capsule/editors/email_editor.html.twig') + ->context([ + 'user' => $current_user, + 'capsule' => $capsule, + 'capsule_edit_link' => $this->urlGenerator->generate( + 'edit_capsule', + [ 'path' => $capsule->getLinkPath() ], + UrlGeneratorInterface::ABSOLUTE_URL + ) + ]); + + $this->mailer->send($email); + + $this->addFlash( + 'success', + $this->translator->trans( + 'editors.add.user.success', + [ + 'capsule_name' => $capsule->getName(), + 'user_email' => $editor_email + ] + ) + ); + } } diff --git a/src/Controller/CapsuleGroupController.php b/src/Controller/CapsuleGroupController.php index 0c9f1b286013468d66742a2710c5b173bcff88d5..fb61dd0269b16d823db01ed85b162c451efc9647 100755 --- a/src/Controller/CapsuleGroupController.php +++ b/src/Controller/CapsuleGroupController.php @@ -29,7 +29,7 @@ class CapsuleGroupController extends AbstractController ) { } - #[Route('/my_groups', name:'edit_user_groups')] + #[Route('/{_locale<%app.supported_locales%>}/my_groups', name:'edit_user_groups')] public function editUserGroups(): Response { $current_user = $this->getUser(); @@ -45,7 +45,7 @@ class CapsuleGroupController extends AbstractController ]); } - #[Route('my_groups/create', name:'create_group')] + #[Route('/{_locale<%app.supported_locales%>}/my_groups/create', name:'create_group')] public function create(Request $request): Response { $current_user = $this->getUser(); @@ -81,7 +81,7 @@ class CapsuleGroupController extends AbstractController ]); } - #[Route('/my_groups/{group_id}/delete', name:'delete_group')] + #[Route('/{_locale<%app.supported_locales%>}/my_groups/{group_id}/delete', name:'delete_group')] public function deleteGroup(int $group_id, Request $request): Response { $current_user = $this->getUser(); @@ -129,7 +129,7 @@ class CapsuleGroupController extends AbstractController ]); } - #[Route('/capsule/{capsule_id}/groups/edit', name:'edit_capsule_groups')] + #[Route('/{_locale<%app.supported_locales%>}/capsule/{capsule_id}/groups/edit', name:'edit_capsule_groups')] public function edit(int $capsule_id, Request $request): Response { $current_user = $this->getUser(); @@ -186,7 +186,7 @@ class CapsuleGroupController extends AbstractController ]); } - #[Route('/capsule/{capsule_id}/groups/{group_id}/remove', name:'remove_group')] + #[Route('/{_locale<%app.supported_locales%>}/capsule/{capsule_id}/groups/{group_id}/remove', name:'remove_group')] public function remove( int $capsule_id, int $group_id, diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index f156772b5f67eba187ee4fb66e834aa12e3e922d..42053f80c0a810c8d37d8922a17566b7f61e66c4 100755 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -23,7 +23,7 @@ class ProjectController extends AbstractController { } - #[Route('/project/create', name:'create_project', methods:['POST'])] + #[Route('/{_locale<%app.supported_locales%>}/project/create', name:'create_project', methods:['POST'])] /** * @throws ZipArchiveNotOpeningException */ @@ -69,7 +69,7 @@ class ProjectController extends AbstractController return $this->redirectToRoute('capsule_list'); } - #[Route('/project/duplicate', name:'duplicate_project', methods:['POST'])] + #[Route('/{_locale<%app.supported_locales%>}/project/duplicate', name:'duplicate_project', methods:['POST'])] /** * @throws ZipArchiveNotOpeningException */ @@ -114,61 +114,7 @@ class ProjectController extends AbstractController return $this->redirectToRoute('capsule_list'); } - /** - * @throws ZipArchiveNotOpeningException The archive file doesn't exist. - */ - private function extractZipArchiveInNewCapsuleDirectory(string $capsule_directory): void - { - $zip = new ZipArchive(); - $zip_file_archive_is_open = $zip->open("create.zip"); - - if (! $zip_file_archive_is_open) { - throw new ZipArchiveNotOpeningException("Create Zip Archive could not be open"); - } - - $zip->extractTo($capsule_directory); - $zip->close(); - } - - private function setVideoUrlNodeAttributeInXMLProjectFile(string $project_directory, string $video_url): void - { - $project_xml_file = $project_directory . "/file/project.xml"; - $xml_file_content = simplexml_load_file($project_xml_file); - - if (false === $xml_file_content) { - throw new FileNotFoundException("The XML project file could not be found"); - } - - /** @phpstan-ignore-next-line */ - $xml_file_content->video['url'] = $video_url; - $video_url_XML_tag_is_filled = $xml_file_content->saveXML($project_xml_file); - - if (false === $video_url_XML_tag_is_filled) { - throw new XmlParsingException('Video URL could not be written in XML project file'); - } - } - - private function createOrUpdatePasswordFile(string $capsule_directory, string $password): void - { - $project_password_file = $capsule_directory . "/file/projectPassword.txt"; - file_put_contents($project_password_file, $password); - } - - private function replaceTheWholeFileDirectoryWithParentOne( - FileSystem $file_system, - string $parent_directory, - string $child_directory - ): void { - $file_directory_path = '/file/'; - $file_system->mirror( - $parent_directory . $file_directory_path, - $child_directory . $file_directory_path, - null, - ['override' => true] - ); - } - - #[Route('/capsule/{capsule_id}/edit_video_url', name:'edit_video_url')] + #[Route('/{_locale<%app.supported_locales%>}/capsule/{capsule_id}/edit_video_url', name:'edit_video_url')] public function editVideoUrl( int $capsule_id, Request $request, @@ -226,4 +172,58 @@ class ProjectController extends AbstractController 'editVideoUrlForm' => $form ]); } + + /** + * @throws ZipArchiveNotOpeningException The archive file doesn't exist. + */ + private function extractZipArchiveInNewCapsuleDirectory(string $capsule_directory): void + { + $zip = new ZipArchive(); + $zip_file_archive_is_open = $zip->open("create.zip"); + + if (! $zip_file_archive_is_open) { + throw new ZipArchiveNotOpeningException("Create Zip Archive could not be open"); + } + + $zip->extractTo($capsule_directory); + $zip->close(); + } + + private function setVideoUrlNodeAttributeInXMLProjectFile(string $project_directory, string $video_url): void + { + $project_xml_file = $project_directory . "/file/project.xml"; + $xml_file_content = simplexml_load_file($project_xml_file); + + if (false === $xml_file_content) { + throw new FileNotFoundException("The XML project file could not be found"); + } + + /** @phpstan-ignore-next-line */ + $xml_file_content->video['url'] = $video_url; + $video_url_XML_tag_is_filled = $xml_file_content->saveXML($project_xml_file); + + if (false === $video_url_XML_tag_is_filled) { + throw new XmlParsingException('Video URL could not be written in XML project file'); + } + } + + private function createOrUpdatePasswordFile(string $capsule_directory, string $password): void + { + $project_password_file = $capsule_directory . "/file/projectPassword.txt"; + file_put_contents($project_password_file, $password); + } + + private function replaceTheWholeFileDirectoryWithParentOne( + FileSystem $file_system, + string $parent_directory, + string $child_directory + ): void { + $file_directory_path = '/file/'; + $file_system->mirror( + $parent_directory . $file_directory_path, + $child_directory . $file_directory_path, + null, + ['override' => true] + ); + } } diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php index 20ccf5fdeb29a42f81a01710259da18d0e0c4b3e..ab1a4ff460569781ed77e7987468708ebf5d7918 100755 --- a/src/Controller/SecurityController.php +++ b/src/Controller/SecurityController.php @@ -13,7 +13,7 @@ class SecurityController extends AbstractController public function login(AuthenticationUtils $authentication_utils): Response { if ($this->getUser()) { - return $this->redirectToRoute('capsule_list'); + return $this->redirectToRoute('home'); } // get the login error if there is one @@ -30,7 +30,7 @@ class SecurityController extends AbstractController ); } - #[Route('/logout', name:'app_logout')] + #[Route('/{_locale<%app.supported_locales%>}/logout', name:'app_logout')] public function logout(): void { throw new \LogicException( diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 89f7ecae410d7d489d8749a1800b67f471fa688b..a796d50b384df2d2a106140785788bf11e291840 100755 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -4,6 +4,7 @@ namespace App\Controller; use App\Entity\PendingEmailAddress; use App\Entity\User; +use App\Form\ChooseLanguageFormType; use App\Form\EditPasswordFormType; use App\Form\EditUserProfileFormType; use App\Repository\PendingEmailAddressRepository; @@ -27,7 +28,7 @@ class UserController extends AbstractController ) { } - #[Route('/profile', name:'show_profile')] + #[Route('/{_locale<%app.supported_locales%>}/profile', name:'show_profile')] public function showProfile(): Response { $current_user = $this->getUser(); @@ -41,11 +42,9 @@ class UserController extends AbstractController ]); } - #[Route('/edit_profile', name:'edit_profile')] - public function editProfile( - Request $request, - MailerInterface $mailer - ): Response { + #[Route('/{_locale<%app.supported_locales%>}/edit_profile', name:'edit_profile')] + public function editProfile(Request $request, MailerInterface $mailer): Response + { $current_user = $this->getUser(); if (! $current_user instanceof User) { @@ -111,7 +110,7 @@ class UserController extends AbstractController ]); } - #[Route('/edit_password', name:'edit_password')] + #[Route('/{_locale<%app.supported_locales%>}/edit_password', name:'edit_password')] public function editPassword( Request $request, UserPasswordHasherInterface $user_password_hasher @@ -144,6 +143,42 @@ class UserController extends AbstractController ]); } + #[Route('/{_locale<%app.supported_locales%>}/edit_language', name:'choose_language')] + public function editLanguage(Request $request): Response + { + $current_user = $this->getUser(); + + if (! $current_user instanceof User) { + return $this->redirectToRoute('app_logout'); + } + + $form = $this->createForm(ChooseLanguageFormType::class, null, ['locale' => $current_user->getLocale()]); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $locale = $form->get('language')->getData(); + $request->setLocale($locale); + $current_user->setLocale($locale); + $this->entity_manager->persist($current_user); + $this->entity_manager->flush(); + + $this->addFlash( + 'success', + $this->translator->trans( + 'user.profile.language.updated_success', + [], + null, + $locale + ) + ); + + return $this->redirectToRoute('show_profile', ['_locale' => $locale]); + } + return $this->renderForm('user/edit_language.html.twig', [ + 'chooseLanguageForm' => $form + ]); + } + #[Route('/edit/email', name:'verify_new_email_address')] public function verifyNewEmailAddress(): Response { diff --git a/src/Entity/User.php b/src/Entity/User.php index 90f214c9ac4040c68d596f6119e59b1682254412..23ea899ead4397867d52759c868368e3f76c3ad6 100755 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -82,6 +82,9 @@ class User implements UserInterface, LegacyPasswordAuthenticatedUserInterface */ public string $plainPassword; + #[ORM\Column(type:'string')] + public string $locale; + public function __construct() { $this->capsules = new ArrayCollection(); @@ -267,6 +270,16 @@ class User implements UserInterface, LegacyPasswordAuthenticatedUserInterface $this->has_subscribed_newsletter = $is_subscribed_news_letter; } + public function getLocale(): string + { + return $this->locale; + } + + public function setLocale(string $locale): void + { + $this->locale = $locale; + } + /** * @return Collection<Group> */ diff --git a/src/EventSubscriber/LocaleSubscriber.php b/src/EventSubscriber/LocaleSubscriber.php new file mode 100644 index 0000000000000000000000000000000000000000..0ce4464ce4351844932c854c4c4b905707b04877 --- /dev/null +++ b/src/EventSubscriber/LocaleSubscriber.php @@ -0,0 +1,41 @@ +<?php + +namespace App\EventSubscriber; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\RequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +class LocaleSubscriber implements EventSubscriberInterface +{ + private string $defaultLocale; + + public function __construct(string $defaultLocale = 'en') + { + $this->defaultLocale = $defaultLocale; + } + + public function onKernelRequest(RequestEvent $event): void + { + $request = $event->getRequest(); + if (!$request->hasPreviousSession()) { + return; + } + + // try to see if the locale has been set as a _locale routing parameter + if ($locale = $request->attributes->get('_locale')) { + $request->getSession()->set('_locale', $locale); + } else { + // if no explicit locale has been set on this request, use one from the session + $request->setLocale($request->getSession()->get('_locale', $this->defaultLocale)); + } + } + + public static function getSubscribedEvents(): array + { + return [ + // must be registered before (i.e. with a higher priority than) the default Locale listener + KernelEvents::REQUEST => [['onKernelRequest', 20]], + ]; + } +} diff --git a/src/Form/ChooseLanguageFormType.php b/src/Form/ChooseLanguageFormType.php new file mode 100644 index 0000000000000000000000000000000000000000..bf82f88da7706892e32f1dab259cb84835712517 --- /dev/null +++ b/src/Form/ChooseLanguageFormType.php @@ -0,0 +1,35 @@ +<?php + +namespace App\Form; + +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class ChooseLanguageFormType extends AbstractType +{ + public function buildForm(FormBuilderInterface $builder, array $options): void + { + $builder + ->add('language', ChoiceType::class, [ + 'choices' => [ + 'English' => 'en', + 'Français' => 'fr', + ], + 'expanded' => true, + 'multiple' => false, + 'label' => 'user.profile.language.name', + 'data' => $options['locale'] + ]) + ->add('update', SubmitType::class, [ + 'label' => 'general.update', + ]); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults(['locale' => []]); + } +} diff --git a/templates/capsule/editors/list_editors.html.twig b/templates/capsule/editors/list_editors.html.twig index 0b71139fb3a355c7088c6e6ea959357c2dc45914..0009b1941c321a71ddab743743c4a740e04d2580 100644 --- a/templates/capsule/editors/list_editors.html.twig +++ b/templates/capsule/editors/list_editors.html.twig @@ -48,7 +48,8 @@ {{ editor.getFirstName() }} {{ editor.getLastName() }} {% if editor is not same as capsule.getCreationAuthor() %} - - <a href="/capsule/{{ capsule.getId() }}/editors/{{ editor.getId() }}/remove" class="remove-link"> + <a href="{{ path('remove_editor', {'_locale': current_user.getLocale(), 'capsule_id' : capsule.getId(), 'editor_id': editor.getId()}) }}" + class="remove-link"> {{ 'editors.remove.editor.link'|trans }} </a> {% endif %} diff --git a/templates/capsule/index.html.twig b/templates/capsule/index.html.twig index ef87b7308c9f86cb08a26333186fa53472f48726..e0f2a387d7df79af8e8e83d9965ecb37452a9de2 100644 --- a/templates/capsule/index.html.twig +++ b/templates/capsule/index.html.twig @@ -26,7 +26,7 @@ {% endif %} <form class="d-flex mb-4 mb-lg-0"> - <button class="btn btn-orange text-uppercase" formaction="/create"> + <button class="btn btn-orange text-uppercase" formaction="{{ path('create_capsule') }}"> + {{ 'capsule.create_capsule'|trans }} </button> </form> @@ -50,7 +50,7 @@ <div class="capsule-item pb-4 col-12 col-sm-11 col-md-10 col-xxl-8"> <div class="d-flex flex-column flex-md-row justify-content-center align-items-center mt-sm-4"> <div class="list-item"> - <a href="/capsule/preview/{{ capsule.getLinkPath() }}" class="capsule-title"> + <a href="{{ path('preview_capsule', {'_locale': current_user.getLocale(), 'path': capsule.getLinkPath()}) }}" class="capsule-title"> {{ capsule.getName() }} </a> </div> @@ -73,14 +73,16 @@ <i class="fa-thin fa-gears"></i> <div class="list-item text-nowrap"> - <a href="/capsule/{{ capsule.getId() }}/editors" class="links text-decoration-none"> + <a href="{{ path('edit_capsule_editors', {'_locale': current_user.getLocale(), 'capsule_id': capsule.getId()}) }}" + class="links text-decoration-none"> <i class="fas fa-cog m-2"></i> {{ 'capsule.edit_permissions.link'|trans }} </a> </div> <div class="list-item text-nowrap"> - <a href="/capsule/{{ capsule.getId() }}/edit_video_url" class="links text-decoration-none"> + <a href="{{ path('edit_video_url', {'_locale': current_user.getLocale(), 'capsule_id': capsule.getId()}) }}" + class="links text-decoration-none"> <i class="fas fa-link m-2"></i> {{ 'capsule.edit.video_url.link'|trans }} </a> @@ -88,14 +90,16 @@ <div class="list-item text-nowrap"> - <a href="{{ path('edit_capsule_groups', { 'capsule_id' : capsule.getId()}) }}" class="links text-decoration-none"> + <a href="{{ path('edit_capsule_groups', {'_locale': current_user.getLocale(), 'capsule_id' : capsule.getId()}) }}" + class="links text-decoration-none"> <i class="fas fa-layer-group m-2"></i> {{ 'groups.edit.title'|trans({'%capsule_name%': capsule.getName()}) }} </a> </div> <div class="list-item text-nowrap"> - <a href="/capsule/duplicate/{{ capsule.getId() }}" class="links text-decoration-none"> + <a href="{{ path('duplicate_capsule', {'_locale': current_user.getLocale(), 'capsule_id' : capsule.getId()}) }}" + class="links text-decoration-none"> <i class="far fa-clone m-2"></i> {{ 'capsule.duplicate.link'|trans }} </a> @@ -103,14 +107,16 @@ {% if capsule.getCreationauthor() is same as current_user %} <div class="list-item text-nowrap"> - <a href="/capsule/delete/{{ capsule.getId() }}" class="links text-decoration-none"> + <a href="{{ path('delete_capsule', {'_locale': current_user.getLocale(), 'capsule_id' : capsule.getId()}) }}" + class="links text-decoration-none"> <i class="fas fa-trash m-2"></i> {{ 'capsule.delete.link'|trans }} </a> </div> {% endif %} - <a href="capsule/edit/{{ capsule.getLinkPath() }}" class="list-item text-nowrap lh-md"> + <a href="{{ path('edit_capsule', {'_locale': current_user.getLocale(), 'path' : capsule.getLinkPath()}) }}" + class="list-item text-nowrap lh-md"> <button class="btn btn-primary btn-lg rounded-3 col-12"> {{ 'capsule.edit.link'|trans }} </button> diff --git a/templates/connexion.html.twig b/templates/connexion.html.twig index caa9374a3dd7cba009af48f6d2ca6232b9e1c0af..7b69d217aecfcbacea452250e4efc571739bc50a 100644 --- a/templates/connexion.html.twig +++ b/templates/connexion.html.twig @@ -2,7 +2,7 @@ {% block connexion %} <div class="d-flex flex-row justify-content-center mb-10"> - <a href="/login" class="btn btn-primary m-2 px-4 py-3 text-uppercase" > + <a href="{{ path('app_login') }}" class="btn btn-primary m-2 px-4 py-3 text-uppercase" > {{ 'login.log_in'|trans }} </a> <a href="/register" class="btn btn-primary m-2 px-4 py-3 text-uppercase"> diff --git a/templates/layout.html.twig b/templates/layout.html.twig index 18ce4ed8ff3ab5172b530aaae97d03e475a9de1b..a2ce787abb4e1eb546afc3698fd252ebbcbbcb19 100644 --- a/templates/layout.html.twig +++ b/templates/layout.html.twig @@ -20,22 +20,12 @@ <body class="container col-10 col-md-8 m-auto"> <div class="position-relative d-flex flex-row align-items-center justify-content-center mb-5"> - <a href="/my_capsules" class="align-self-center"> + <a href="{{ path('home') }}" class="align-self-center"> <img id="header-memorekall-logo" class="memorekall-logo" src="{{ asset('build/images/MemoRekall.png') }}"> </a> {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %} <div class="d-flex flex-column position-absolute end-0 align-content-end text-end fw-light gray-600"> - <div class="nav-item dropdown"> - <a class="nav-link p-0 dropdown-toggle text-capitalize" href="#" id="dropdown-language" role="button" - data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> - {{ app.request.locale|locale_name(app.request.locale) }} - </a> - <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdown-language"> - <li><a class="dropdown-item" href="{{ path('home', {_locale: 'en'}) }}">English</a></li> - <li><a class="dropdown-item" href="{{ path('home', {_locale: 'fr'}) }}">Français</a></li> - </ul> - </div> <div class="fw-bold text-capitalize">{{ app.user.firstName }} {{ app.user.lastName }}</div> <a href="{{ path('show_profile') }}" class="text-decoration-none fs-5"> {{ 'user.profile.title'|trans }} @@ -44,7 +34,7 @@ {{ 'groups.general.title'|trans }} </a> <form> - <button class="btn btn-primary" formaction="/logout"> + <button class="btn btn-primary" formaction="{{ path('app_logout') }}"> {{ 'general.log_out'|trans }} </button> </form> diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig index aff2910422d3ff81631e8d00161287fd617f286e..a245f4921edd4fa25832f6d685395e940fa0799f 100644 --- a/templates/security/login.html.twig +++ b/templates/security/login.html.twig @@ -41,7 +41,8 @@ </div> #} - <a href="/reset-password" class="m-auto"> + <a href="{{ path('app_forgot_password_request') }}" + class="m-auto"> {{ 'login.forgot_password_link'|trans }} </a> diff --git a/templates/user/edit_language.html.twig b/templates/user/edit_language.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..515e82032091d5e407d3ea28c8e1fde298411c78 --- /dev/null +++ b/templates/user/edit_language.html.twig @@ -0,0 +1,21 @@ +{% extends 'layout.html.twig' %} + +{% block title %} + {{ 'user.profile.language.edit.title'|trans }} +{% endblock %} + +{% block body %} + <div class="row gx-0"> + <div class="row-title-box"> + <h3 class="row-title"> + {{ 'user.profile.language.edit.title'|trans }} + </h3> + + </div> + </div> + + <div> + {{ form_start(chooseLanguageForm, {'attr': {novalidate: 'novalidate', 'class': 'd-flex flex-column justify-content-center align-items-center col-6 m-auto'}}) }} + {{ form_end(chooseLanguageForm) }} + </div> +{% endblock %} \ No newline at end of file diff --git a/templates/user/profile.html.twig b/templates/user/profile.html.twig index d6e80801ccecf76763cd89894490417248212004..c9fac548ae7d91b92ca2b9d8973689aae233f630 100644 --- a/templates/user/profile.html.twig +++ b/templates/user/profile.html.twig @@ -28,31 +28,43 @@ <div class="d-flex flex-column flex-md-row justify-content-center align-items-center"> <div class="profile-block d-flex flex-row ps-3 ps-md-5 pe-3 pe-md-5 pt-4 pb-3 fw-normal me-0 me-md-5"> <div class="pe-3 pe-md-4 text-nowrap"> - <p class=""> + <p> {{ 'user.firstname'|trans }} </p> - <p class=""> + <p> {{ 'user.lastname'|trans }} </p> - <p class=""> + <p> {{ 'user.email'|trans }} </p> + <p> + {{ 'user.locale'|trans }} + </p> </div> <div class="text-white"> <p class="text-capitalize">{{ app.user.firstName }}</p> <p class="text-capitalize">{{ app.user.lastName }}</p> <p>{{ app.user.email }}</p> + {% if app.user.locale is same as 'en' %} + <p>English</p> + {% elseif app.user.locale is same as 'fr' %} + <p>Français</p> + {% endif %} </div> </div> <div class="d-flex flex-column justify-content-center mt-2 mt-md-0"> - <a href="/edit_profile" class="m-2 m-md-3 btn btn-primary text-decoration-none text-center"> + <a href="{{ path('edit_profile') }}" class="m-2 m-md-3 btn btn-primary text-decoration-none text-center"> {{ 'user.profile.edit'|trans }} </a> - <a href="/edit_password" class="m-2 m-md-3 btn btn-primary text-decoration-none text-center"> + <a href="{{ path('edit_password') }}" class="m-2 m-md-3 btn btn-primary text-decoration-none text-center"> {{ 'user.password.edit'|trans }} </a> + + <a href="{{ path('choose_language') }}" class="m-2 m-md-3 btn btn-primary text-decoration-none text-center" type="button"> + {{ 'user.profile.language.edit.title'|trans }} + </a> </div> </div> {% endblock %} \ No newline at end of file diff --git a/tests/functional/CapsuleControllerTest.php b/tests/functional/CapsuleControllerTest.php index 76de4550dc6a869cf40d0e2cf7d2bf692a1df831..dddc2a2d04548747e2830b58dad71229a061369a 100644 --- a/tests/functional/CapsuleControllerTest.php +++ b/tests/functional/CapsuleControllerTest.php @@ -36,7 +36,7 @@ class CapsuleControllerTest extends WebTestCase $this->verified_user = $verified_user; $client->loginUser($this->verified_user); - $crawler = $client->request('GET', '/create'); + $crawler = $client->request('GET', '/en/create'); $this->assertResponseIsSuccessful(); $client->enableProfiler(); @@ -51,13 +51,13 @@ class CapsuleControllerTest extends WebTestCase $client->submit($form); $this->assertResponseRedirects( - '/my_capsules', + '/en/my_capsules', 302, 'Once the capsule is created, the user should be redirected to its capsules lists' ); $client->followRedirect(); - $this->assertResponseIsSuccessful('/my_capsules'); + $this->assertResponseIsSuccessful('/en/my_capsules'); $capsule_repository = $object_manager->getRepository(Capsule::class); $capsule_in_db = $capsule_repository->findOneBy(['name' => self::CAPSULE_NAME]); @@ -88,7 +88,7 @@ class CapsuleControllerTest extends WebTestCase public function testANonAuthenticatedUserShouldNotBeAbleToAccessTheCapsuleListPage(): void { $client = static::createClient(); - $client->request('GET', '/my_capsules'); + $client->request('GET', '/en/my_capsules'); $this->assertResponseRedirects("/login", 302); } @@ -97,7 +97,7 @@ class CapsuleControllerTest extends WebTestCase { $client = static::createClient(); - $client->request('GET', '/capsule/preview/' . $this->created_capsule->getLinkPath()); + $client->request('GET', '/en/capsule/preview/' . $this->created_capsule->getLinkPath()); $this->assertResponseIsSuccessful('The preview should be allowed for none authenticated user'); } @@ -106,7 +106,7 @@ class CapsuleControllerTest extends WebTestCase $client = static::createClient(); $client->loginUser($this->verified_user); - $client->request('GET', '/capsule/preview/' . $this->created_capsule->getLinkPath()); + $client->request('GET', '/en/capsule/preview/' . $this->created_capsule->getLinkPath()); $this->assertResponseIsSuccessful('The preview should be allowed for none authenticated user'); } @@ -115,16 +115,15 @@ class CapsuleControllerTest extends WebTestCase $client = static::createClient(); $client->loginUser($this->verified_user); - $client->request('GET', '/capsule/preview/' . substr($this->created_capsule->getLinkPath(), -6)); - $this->assertResponseIsSuccessful('The preview should be allowed for none authenticated user'); - $crawler = $client->getCrawler(); + $client->request('GET', '/en/capsule/preview/' . substr($this->created_capsule->getLinkPath(), -6)); + $this->assertResponseIsSuccessful('The preview should be allowed for non authenticated user'); } public function testNonLoggedUserShouldNotAccessToCapsuleEdition(): void { $client = static::createClient(); - $client->request('GET', '/capsule/edit/' . $this->created_capsule->getLinkPath()); + $client->request('GET', '/en/capsule/edit/' . $this->created_capsule->getLinkPath()); $this->assertResponseRedirects( '/login', 302, @@ -137,7 +136,7 @@ class CapsuleControllerTest extends WebTestCase $client = static::createClient(); $client->loginUser($this->verified_user); - $client->request('GET', '/capsule/edit/' . $this->created_capsule->getLinkPath()); + $client->request('GET', '/en/capsule/edit/' . $this->created_capsule->getLinkPath()); $this->assertResponseIsSuccessful( 'An authenticated user should be able to access to the edition of its capsules' ); diff --git a/tests/functional/CapsuleEditorControllerTest.php b/tests/functional/CapsuleEditorControllerTest.php index b843c959b01bad2f5dfb185151ae14d236f3eab4..d2382029f53a22a11a05d448af0dbff1811dc8eb 100644 --- a/tests/functional/CapsuleEditorControllerTest.php +++ b/tests/functional/CapsuleEditorControllerTest.php @@ -46,7 +46,7 @@ class CapsuleEditorControllerTest extends WebTestCase public function testAuthorShouldBeAbleToDeleteACapsule(): void { $this->client->loginUser($this->user_author); - $this->client->request('GET', '/capsule/delete/' . $this->capsule->getId()); + $this->client->request('GET', '/en/capsule/delete/' . $this->capsule->getId()); $this->assertResponseIsSuccessful(); } @@ -54,14 +54,14 @@ class CapsuleEditorControllerTest extends WebTestCase public function testEditorNonAuthorShouldNotBeAbleToDeleteACapsule(): void { $this->client->loginUser($this->editor_non_author); - $this->client->request('GET', '/capsule/delete/' . $this->capsule->getId()); + $this->client->request('GET', '/en/capsule/delete/' . $this->capsule->getId()); - $this->assertResponseRedirects('/my_capsules', 302); + $this->assertResponseRedirects('/en/my_capsules', 302); } public function testAuthorShouldBeAbleToAddANewEditorForACapsule(): void { - $uri = '/capsule/' . $this->capsule->getId() . '/editors'; + $uri = '/en/capsule/' . $this->capsule->getId() . '/editors'; $this->client->loginUser($this->user_author); $crawler = $this->client->request('GET', $uri); @@ -89,7 +89,7 @@ class CapsuleEditorControllerTest extends WebTestCase public function testEditorShouldBeAbleToAccessTheCapsuleEditorsPage(): void { $this->capsule->addEditor($this->editor_non_author); - $uri = '/capsule/' . $this->capsule->getId() . '/editors'; + $uri = '/en/capsule/' . $this->capsule->getId() . '/editors'; $this->client->loginUser($this->editor_non_author); $this->client->request('GET', $uri); @@ -99,7 +99,7 @@ class CapsuleEditorControllerTest extends WebTestCase public function testNonRegisteredUserAddedAsEditorShouldReceiveAnEmail(): void { - $uri = '/capsule/' . $this->capsule->getId() . '/editors'; + $uri = '/en/capsule/' . $this->capsule->getId() . '/editors'; $this->client->loginUser($this->user_author); $crawler = $this->client->request('GET', $uri); @@ -133,7 +133,7 @@ class CapsuleEditorControllerTest extends WebTestCase public function testRegisteredUserShouldReceiveAnEmailWithCapsuleEditionLink(): void { - $uri = '/capsule/' . $this->capsule->getId() . '/editors'; + $uri = '/en/capsule/' . $this->capsule->getId() . '/editors'; $this->client->loginUser($this->user_author); $crawler = $this->client->request('GET', $uri); @@ -171,7 +171,7 @@ class CapsuleEditorControllerTest extends WebTestCase $this->object_manager->flush(); $this->assertContains($this->editor_non_author, $this->capsule->getEditors()->toArray()); - $uri = '/capsule/' . $this->capsule->getId() . '/editors'; + $uri = '/en/capsule/' . $this->capsule->getId() . '/editors'; $this->client->loginUser($this->user_author); $crawler = $this->client->request('GET', $uri); diff --git a/tests/functional/CapsuleGroupControllerTest.php b/tests/functional/CapsuleGroupControllerTest.php index ac32fb8a6671c163a987464f1106e30afa93507d..9ebea5b10547933bad422fac6aac4b5686b9933f 100644 --- a/tests/functional/CapsuleGroupControllerTest.php +++ b/tests/functional/CapsuleGroupControllerTest.php @@ -52,7 +52,7 @@ class CapsuleGroupControllerTest extends WebTestCase public function testUserShouldNotBeAbleToCreateACapsuleGroupWithExistingGroupName(): void { $this->client->loginUser($this->user_1); - $uri = '/my_groups/create'; + $uri = '/en/my_groups/create'; $crawler = $this->client->request('GET', $uri); $this->assertResponseIsSuccessful(); @@ -70,7 +70,7 @@ class CapsuleGroupControllerTest extends WebTestCase public function testUserShouldBeAbleToCreateACapsuleGroupWithTheSameGroupNameUsedByAnotherUser(): void { $this->client->loginUser($this->user_2); - $uri = '/my_groups/create'; + $uri = '/en/my_groups/create'; $crawler = $this->client->request('GET', $uri); $this->assertResponseIsSuccessful(); diff --git a/tests/functional/LoginTest.php b/tests/functional/LoginTest.php index a9b529ae182a91c414478a240149a9f19a34fbd0..4645779f6adeb5c23ee613dc14c2716498cfc393 100644 --- a/tests/functional/LoginTest.php +++ b/tests/functional/LoginTest.php @@ -31,14 +31,14 @@ class LoginTest extends WebTestCase $this->client->submit($this->form); $this->assertResponseRedirects( - '/my_capsules', + '/en/my_capsules', 302, 'Once the user is logged in, he should be redirected to its capsules lists' ); $this->client->followRedirect(); - $this->assertResponseIsSuccessful('/my_capsules'); + $this->assertResponseIsSuccessful('/en/my_capsules'); } public function testRegisteredUserWithDisabledAccountIsRedirectedToLoginPageWhenSubmittingLoginForm(): void diff --git a/tests/functional/ProjectControllerTest.php b/tests/functional/ProjectControllerTest.php index 7db3e685f9b301063081f5f4ad7000b24f91192f..f2646635702f7f255d066004595430d046ec51c7 100644 --- a/tests/functional/ProjectControllerTest.php +++ b/tests/functional/ProjectControllerTest.php @@ -78,7 +78,7 @@ class ProjectControllerTest extends WebTestCase public function testProjectDirectoryWithCorrespondingXMLFileIsCreatedWhenCapsuleCreationIsSuccessful(): void { - $crawler = $this->client->request('GET', '/create'); + $crawler = $this->client->request('GET', '/en/create'); $this->assertResponseIsSuccessful(); $this->client->enableProfiler(); @@ -93,13 +93,13 @@ class ProjectControllerTest extends WebTestCase $this->client->submit($this->form); $this->assertResponseRedirects( - '/my_capsules', + '/en/my_capsules', 302, 'Once the capsule is created, the user should be redirected to its capsules lists' ); $this->client->followRedirect(); - $this->assertResponseIsSuccessful('/my_capsules'); + $this->assertResponseIsSuccessful('/en/my_capsules'); $capsule_repository = $this->object_manager->getRepository(Capsule::class); $capsule_in_db = $capsule_repository->findOneBy(['name' => self::CAPSULE_NAME]); @@ -132,7 +132,7 @@ class ProjectControllerTest extends WebTestCase $this->createCapsule(); $this->assertResponseRedirects( - '/my_capsules', + '/en/my_capsules', 302, 'Once the capsule is created, the user should be redirected to its capsules lists' ); @@ -177,7 +177,7 @@ class ProjectControllerTest extends WebTestCase $parent_capsule_directory = $parent_capsule->getLinkPath(); - $crawler = $this->client->request('GET', '/capsule/duplicate/' . $parent_capsule->getId()); + $crawler = $this->client->request('GET', '/en/capsule/duplicate/' . $parent_capsule->getId()); $this->assertResponseIsSuccessful(); $this->client->enableProfiler(); @@ -188,13 +188,13 @@ class ProjectControllerTest extends WebTestCase $this->client->submit($this->form); $this->assertResponseRedirects( - '/my_capsules', + '/en/my_capsules', 302, 'Once the capsule is duplicated, the user should be redirected to its capsules lists' ); $this->client->followRedirect(); - $this->assertResponseIsSuccessful('/my_capsules'); + $this->assertResponseIsSuccessful('/en/my_capsules'); $capsule_repository = $this->object_manager->getRepository(Capsule::class); $duplicated_capsule_in_db = $capsule_repository->findOneBy(['name' => $duplicated_capsule_name]); @@ -232,7 +232,7 @@ class ProjectControllerTest extends WebTestCase public function testVideoUrlNodeAttributeInXMLFileShouldBeUpdatedWhenUserEditVideoUrl(): void { $capsule = $this->createCapsule(); - $crawler = $this->client->request('GET', '/capsule/' . $capsule->getId() . '/edit_video_url'); + $crawler = $this->client->request('GET', '/en/capsule/' . $capsule->getId() . '/edit_video_url'); $this->assertResponseIsSuccessful(); $this->client->enableProfiler(); @@ -244,7 +244,7 @@ class ProjectControllerTest extends WebTestCase $this->client->submit($this->form); $this->client->followRedirect(); - $this->assertResponseIsSuccessful('/my_capsules'); + $this->assertResponseIsSuccessful('/en/my_capsules'); $capsule_directory = $capsule->getLinkPath(); @@ -285,7 +285,7 @@ class ProjectControllerTest extends WebTestCase private function createCapsule(): Capsule { - $crawler = $this->client->request('GET', '/create'); + $crawler = $this->client->request('GET', '/en/create'); $this->assertResponseIsSuccessful(); $this->client->enableProfiler(); diff --git a/tests/functional/RegistrationControllerTest.php b/tests/functional/RegistrationControllerTest.php index 222376a0cdb8fd461957a6f04bdc1322361b80f9..c651c2a2a2187c690b770ffee0f1888c3abae44c 100644 --- a/tests/functional/RegistrationControllerTest.php +++ b/tests/functional/RegistrationControllerTest.php @@ -9,7 +9,6 @@ use Exception; use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\DomCrawler\Crawler; -use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\RawMessage; class RegistrationControllerTest extends WebTestCase @@ -105,7 +104,7 @@ class RegistrationControllerTest extends WebTestCase $this->client->loginUser($testUser); $this->client->request('GET', '/register'); - $this->assertResponseRedirects('/my_capsules', 302); + $this->assertResponseRedirects('/en/my_capsules', 302); } public function testAnAuthenticatedUserShouldNotBeAbleToAccessTheLoginPage(): void @@ -115,7 +114,7 @@ class RegistrationControllerTest extends WebTestCase $this->client->loginUser($testUser); $this->client->request('GET', '/login'); - $this->assertResponseRedirects('/my_capsules', 302); + $this->assertResponseRedirects('/en', 302); } public function testSubmittingTheRegisterFormWithAnInvalidEmailAddressShouldDisplayAFeedbackError(): void diff --git a/tests/functional/ResetPasswordControllerTest.php b/tests/functional/ResetPasswordControllerTest.php index 607a1a5429be2f74730a1e0f0af974d62cd2b7aa..89e246723a8aafdbac56f9f389cebfd490b33acb 100644 --- a/tests/functional/ResetPasswordControllerTest.php +++ b/tests/functional/ResetPasswordControllerTest.php @@ -105,12 +105,12 @@ class ResetPasswordControllerTest extends WebTestCase $this->client->submit($login_form); $this->assertResponseRedirects( - '/my_capsules', + '/en/my_capsules', 302, 'Once the user is logged in, he should be redirected to its capsules lists' ); $this->client->followRedirect(); - $this->assertResponseIsSuccessful('/my_capsules'); + $this->assertResponseIsSuccessful('/en/my_capsules'); } private function userShouldBeRedirectedToTheResetPasswordPageWhenClickingOnEmailLink(): void diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index a7d66eec504c3dd0e14dffa74291c957e0263ff6..00da3552d4eb33df92a6bbc29723f8866a04a2f8 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -10,6 +10,7 @@ general: validate: Validate save: Save delete: Delete + update: Update login: account_disabled_feedback: Your user account is disabled. Please click on the link your receive by email to validate your registration. @@ -103,6 +104,7 @@ user: firstname: First name lastname: Last name email: Email + locale: Language profile: title: My profile edit: Edit profile @@ -111,6 +113,11 @@ user: success: The profile has been updated warning: In order to update your current email address related to your Memorekall account with the new email address new_email_address, please confirm this change following the link your received by email. + language: + name: Language + edit: + title: Edit language + updated_success: The language has been successfully updated password: edit: Edit password current: Current password @@ -118,13 +125,6 @@ user: updated_success: The password has been updated edit_profile: Edit my profile edit_password: Edit my password - edit_email_address: Edit my email address - edit: - email: - title: Hi! Please confirm your email - text: Please click the following link to update your user account and for now on use this email address - confirm_email: Confirm my email - success: Your MemoRekall account is now related to your new email address new_email_address editors: title: Editors @@ -200,8 +200,4 @@ groups: success: Group group_name removed successfully filter: label: Filter by group - no_filter: Show all - -pending_email_address: - email: - unique: \ No newline at end of file + no_filter: Show all \ No newline at end of file diff --git a/translations/messages.fr.yaml b/translations/messages.fr.yaml index 8d67f27f70b9df254e2cfea059a05c6ad44c90da..43df65420617ecd7c1839d4f99f7a6c25743ea73 100644 --- a/translations/messages.fr.yaml +++ b/translations/messages.fr.yaml @@ -10,6 +10,7 @@ general: validate: Valider save: Enregistrer delete: Supprimer + update: Mettre à jour login: account_disabled_feedback: Le compte utilisateur a été désactivé. Veuillez cliquer sur le lien pour recevoir un courriel de validation @@ -102,6 +103,7 @@ user: firstname: Prénom lastname: Nom email: E-mail + locale: Langue profile: title: Mon profil edit: Modifier mon profil @@ -109,6 +111,11 @@ user: updated: success: Votre profil a bien été mis à jour warning: Veuillez confirmer le changement d'adresse e-mail en activant le lien que vous avez reçu par mail à l'adresse new_email_address. L'adresse e-amil reliée à votre compte Memorekall sera alors mise à jour avec la nouvelle new_email_address. + language: + name: Langue + edit: + title: Modifier la langue + updated_success: La langue a bien été mise à jour password: edit: Modifier mon mot de passe current: Mot de passe actuel