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

Merge branch 'tuleap-81-allow-a-new-user-to-access-the-capsule-edition-mode' into 'main'

tuleap-81-allow-a-new-user-to-access-the-capsule-edition-mode

See merge request !45
parents 944a6507 f774819b
No related branches found
No related tags found
1 merge request!45tuleap-81-allow-a-new-user-to-access-the-capsule-edition-mode
Pipeline #772 passed
Showing
with 859 additions and 59 deletions
......@@ -53,7 +53,6 @@ button[type=submit]{
border-radius: 3px;
line-height: 1.5;
font-size: 1rem;
margin: 30px 0 0 0;
padding: .5rem 1rem;
text-align: center;
text-transform: uppercase;
......@@ -83,8 +82,6 @@ button[type=submit]{
box-shadow: 0 0 10px rgba(0,0,0,.5);
border-radius: 2px;
margin-bottom: 40px;
align-items: center;
justify-content: center;
}
.list-item {
......@@ -125,7 +122,6 @@ button[type=submit]{
text-decoration: none;
float: left;
color: rgba(255,255,255,.75) !important;
font-size: 24px;
line-height: 60px;
background: -webkit-linear-gradient(top left, #FA772E, #FC4326);
-webkit-background-clip: text;
......
<?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 Version20220125084520 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('CREATE TABLE invitation_editeur_capsule (id INT AUTO_INCREMENT NOT NULL, capsule_id INT NOT NULL, email VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE `utf8_unicode_ci` ENGINE = InnoDB');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP TABLE invitation_editeur_capsule');
}
}
......@@ -33,6 +33,7 @@ class CapsuleBuilder
{
$this->capsule->setCreationAuthor($creation_author);
$this->hasRequiredCreationAuthor = true;
$this->capsule->addEditor($creation_author);
return $this;
}
......
......@@ -22,12 +22,18 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class CapsuleController extends AbstractController
{
private TranslatorInterface $translator;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
/**
* @Route("/my_capsules", name="capsule_list")
* @Route("/", name="home")
*/
public function index(
CapsuleRepository $capsule_repository,
PaginatorInterface $paginator,
Request $request
): Response {
......@@ -37,7 +43,7 @@ class CapsuleController extends AbstractController
return $this->redirectToRoute('app_logout');
}
$all_capsules = $capsule_repository->findBy(['creation_author' => $current_user]);
$all_capsules = $current_user->getCapsules();
$capsules = $paginator->paginate(
$all_capsules,
......@@ -47,6 +53,7 @@ class CapsuleController extends AbstractController
return $this->render('capsule/index.html.twig', [
'capsules' => $capsules,
'current_user' => $current_user,
'legacy_url' => $this->getParameter('app.legacy_external_prefix')
]);
}
......@@ -109,9 +116,15 @@ class CapsuleController extends AbstractController
return $this->redirectToRoute('app_logout');
}
$capsule = $capsuleRepository->findOneBy(['link_path' => $path, 'creation_author' => $current_user]);
$capsule = $capsuleRepository->findOneBy(['link_path' => $path]);
if (null === $capsule) {
$this->addFlash('warning', $translator->trans('capsule.edition_not_allowed'));
$this->addFlash('warning', $translator->trans('capsule.edit.not_found'));
return $this->redirectToRoute('capsule_list');
}
if (! $capsule->getEditors()->contains($current_user)) {
$this->addFlash('warning', $translator->trans('capsule.edit.not_allowed'));
return $this->redirectToRoute('capsule_list');
}
......@@ -133,9 +146,7 @@ class CapsuleController extends AbstractController
*/
public function delete(
int $id,
Request $request,
TranslatorInterface $translator,
CapsuleRepository $capsule_repository
Request $request
): Response {
$form = $this->createForm(DeleteCapsuleFormType::class);
$form->handleRequest($request);
......@@ -155,12 +166,10 @@ class CapsuleController extends AbstractController
}
$capsule_name = $capsule->getName();
$do_capsule_belongs_to_user = $capsule_repository->doCapsuleBelongsToUser($capsule, $current_user);
if (! $do_capsule_belongs_to_user) {
if ($capsule->getCreationAuthor() !== $current_user) {
$this->addFlash(
'warning',
$translator->trans(
$this->translator->trans(
'capsule.delete.error',
[
'capsule_name' => $capsule_name
......@@ -172,12 +181,14 @@ class CapsuleController extends AbstractController
}
if ($form->isSubmitted() && $form->isValid()) {
$current_user->removeCapsule($capsule);
$capsule->removeEditor($current_user);
$entityManager->remove($capsule);
$entityManager->flush();
$this->addFlash(
'success',
$translator->trans(
$this->translator->trans(
'capsule.delete.success',
[
'capsule_name' => $capsule_name
......@@ -200,9 +211,7 @@ class CapsuleController extends AbstractController
public function duplicate(
int $id,
Request $request,
Filesystem $file_system,
TranslatorInterface $translator,
CapsuleRepository $capsule_repository
Filesystem $file_system
): Response {
$form = $this->createForm(DuplicateCapsuleFormType::class);
$form->handleRequest($request);
......@@ -219,11 +228,12 @@ class CapsuleController extends AbstractController
throw new \Exception('The retrieved capsule is not an instance of Capsule.');
}
$capsule_editors = $parent_capsule->getEditors();
if (! $capsule_repository->doCapsuleBelongsToUser($parent_capsule, $current_user)) {
if (! in_array($current_user, $capsule_editors->toArray())) {
$this->addFlash(
'warning',
$translator->trans(
$this->translator->trans(
'capsule.duplicate.error',
[
'capsule_name' => $parent_capsule->getName()
......@@ -239,7 +249,7 @@ class CapsuleController extends AbstractController
if (! $parent_directory_exists) {
$this->addFlash(
'warning',
$translator->trans(
$this->translator->trans(
'project.not_exist',
[
'capsule_name' => $parent_capsule->getName()
......
<?php
namespace App\Controller;
use App\Entity\Capsule;
use App\Entity\CapsuleEditor;
use App\Entity\CapsulePendingEditor;
use App\Entity\User;
use App\Form\CapsuleEditorsFormType;
use App\Repository\CapsulePendingEditorRepository;
use App\Repository\CapsuleRepository;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use http\Env;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class CapsuleEditorController extends AbstractController
{
private TranslatorInterface $translator;
private MailerInterface $mailer;
private CapsulePendingEditorRepository $capsule_pending_editor_repository;
private EntityManagerInterface $entity_manager;
private UrlGeneratorInterface $urlGenerator;
public function __construct(
MailerInterface $mailer,
TranslatorInterface $translator,
CapsulePendingEditorRepository $capsule_pending_editor_repository,
EntityManagerInterface $entity_manager,
UrlGeneratorInterface $urlGenerator
) {
$this->mailer = $mailer;
$this->translator = $translator;
$this->capsule_pending_editor_repository = $capsule_pending_editor_repository;
$this->entity_manager = $entity_manager;
$this->urlGenerator = $urlGenerator;
}
/**
* @Route("/capsule/{capsule_id}/editors", name="edit_capsule_editors")
*/
public function editCapsuleEditors(
Request $request,
int $capsule_id,
CapsuleRepository $capsule_repository,
UserRepository $user_repository
): Response {
$current_user = $this->getUser();
if (! $current_user instanceof User) {
return $this->redirectToRoute('app_logout');
}
$capsule = $capsule_repository->find($capsule_id);
if (! $capsule) {
throw $this->createNotFoundException(
'No capsule found for id ' . $capsule_id
);
}
$current_capsule_editors_users = $capsule->getEditors()->toArray();
if (! $capsule->getEditors()->contains($current_user)) {
// in_array($current_user, $current_capsule_editors_users)) {
$this->addFlash(
'warning',
$this->translator->trans(
'editors.user_not_editor_error',
[
'capsule_name' => $capsule->getName()
]
)
);
return $this->redirectToRoute('capsule_list');
}
$form = $this->createForm(CapsuleEditorsFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$editor_email = $form->get('email')->getData();
$user_associated_with_email_address = $user_repository
->findOneBy(['email' => $editor_email]);
if (! $user_associated_with_email_address instanceof User) {
$this->addPendingEditor($editor_email, $capsule, $current_user);
} else {
$this->addEditor(
$editor_email,
$capsule,
$current_user,
$user_associated_with_email_address,
$current_capsule_editors_users
);
}
return $this->redirectToRoute('edit_capsule_editors', [
'capsule_id' => $capsule_id
]);
}
$pending_editors = $this->capsule_pending_editor_repository->findBy(['capsule_id' => $capsule_id]);
return $this->render('capsule/editors/list_editors.html.twig', [
'userPermissionsCapsuleForm' => $form->createView(),
'capsule_name' => $capsule->getName(),
'editors' => $current_capsule_editors_users,
'pending_editors' => $pending_editors
]);
}
private function addPendingEditor(string $editor_email, Capsule $capsule, User $current_user): void
{
$pending_editors_emails = $this->capsule_pending_editor_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 = new CapsulePendingEditor();
$pending_editor->setCapsuleId($capsule->getId());
$pending_editor->setEmail($editor_email);
$this->entity_manager->persist($pending_editor);
$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
]
)
);
}
}
......@@ -2,8 +2,12 @@
namespace App\Controller;
use App\Entity\Capsule;
use App\Entity\PendingEditorInvitation;
use App\Entity\User;
use App\Form\RegistrationFormType;
use App\Repository\CapsuleRepository;
use App\Repository\PendingEditorInvitationRepository;
use App\Repository\UserRepository;
use App\Security\EmailVerifier;
use Doctrine\ORM\EntityManagerInterface;
......@@ -12,7 +16,6 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mime\Address;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
......@@ -22,13 +25,16 @@ class RegistrationController extends AbstractController
{
private EmailVerifier $emailVerifier;
private RequestStack $requestStack;
private EntityManagerInterface $entity_manager;
public function __construct(
EmailVerifier $emailVerifier,
RequestStack $requestStack
RequestStack $requestStack,
EntityManagerInterface $entity_manager
) {
$this->emailVerifier = $emailVerifier;
$this->requestStack = $requestStack;
$this->entity_manager = $entity_manager;
}
/**
......@@ -36,8 +42,7 @@ class RegistrationController extends AbstractController
*/
public function register(
Request $request,
UserPasswordHasherInterface $userPasswordHasher,
EntityManagerInterface $entityManager
UserPasswordHasherInterface $userPasswordHasher
): Response {
if ($this->getUser()) {
return $this->redirectToRoute('capsule_list');
......@@ -58,21 +63,20 @@ class RegistrationController extends AbstractController
)
);
$entityManager->persist($user);
$entityManager->flush();
$this->entity_manager->persist($user);
$this->entity_manager->flush();
// generate a signed url and email it to the user
$this->emailVerifier->sendEmailConfirmation(
'app_verify_email',
$user,
$user->getId(),
$user->getEmail(),
(new TemplatedEmail())
->to($user->getEmail())
->subject('Please Confirm your Email')
->htmlTemplate('registration/confirmation_email.html.twig')
);
// do anything else you need here, like send an email
// return $this->redirectToRoute('app_login');
$this->requestStack->getSession()->set('userid', $user->getId());
return $this->redirectToRoute('app_register_mail_sent');
}
......@@ -88,6 +92,8 @@ class RegistrationController extends AbstractController
public function verifyUserEmail(
Request $request,
UserRepository $userRepository,
PendingEditorInvitationRepository $pending_editor_invitation_repository,
CapsuleRepository $capsule_repository,
TranslatorInterface $translator
): Response {
$id = $request->get('id');
......@@ -111,6 +117,12 @@ class RegistrationController extends AbstractController
return $this->redirectToRoute('app_register');
}
$this->addCapsuleToConfirmedPreviousPendingEditor(
$user,
$pending_editor_invitation_repository,
$capsule_repository
);
$this->addFlash(
'email_verified_success',
$translator->trans('registration.email_verified_success')
......@@ -132,4 +144,58 @@ class RegistrationController extends AbstractController
['user' => $user]
);
}
private function addCapsuleToConfirmedPreviousPendingEditor(
User $user,
PendingEditorInvitationRepository $pending_editor_invitation_repository,
CapsuleRepository $capsule_repository
): void {
$pending_editor_invitations = $pending_editor_invitation_repository->findBy(['email' => $user->getEmail()]);
if ($pending_editor_invitations == null) {
return;
}
$capsules_ids = $this->removeInvitations($pending_editor_invitations);
$this->addEditorToCapsules($capsules_ids, $user, $capsule_repository);
}
/**
* @param array<PendingEditorInvitation> $pending_editor_invitations
* @return array<int>
*/
private function removeInvitations(array $pending_editor_invitations): array
{
$capsules_ids = [];
foreach ($pending_editor_invitations as $invitation) {
if (! $invitation instanceof PendingEditorInvitation) {
return $capsules_ids;
}
$capsules_ids[] = $invitation->getCapsuleId();
$this->entity_manager->remove($invitation);
}
return $capsules_ids;
}
/**
* @param array<int> $capsules_ids
*/
private function addEditorToCapsules(
array $capsules_ids,
User $user,
CapsuleRepository $capsule_repository
): void {
$capsules = $capsule_repository->findBy(['id' => $capsules_ids]);
foreach ($capsules as $capsule) {
if (! $capsule instanceof Capsule) {
return;
}
$capsule->addEditor($user);
$this->entity_manager->persist($capsule);
}
$this->entity_manager->flush();
}
}
......@@ -32,7 +32,7 @@ class Capsule
/**
*
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="capsulesCreated")
* @ORM\ManyToOne(targetEntity="App\Entity\User")
* @ORM\JoinColumn(name="aut_crea", referencedColumnName="id", nullable=false)
*
*/
......@@ -46,7 +46,7 @@ class Capsule
/**
*
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="capsulesEdited")
* @ORM\ManyToOne(targetEntity="App\Entity\User")
* @ORM\JoinColumn(name="aut_maj", referencedColumnName="id")
*
*/
......@@ -77,6 +77,22 @@ class Capsule
*/
private string $password;
/**
* @var Collection<User>
*
* @ORM\ManyToMany(targetEntity="App\Entity\User", inversedBy="capsules")
* @ORM\JoinTable(name="editeur_capsule",
* joinColumns={@ORM\JoinColumn(name="capsule_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")}
* )
*/
private Collection $editors;
public function __construct()
{
$this->editors = new ArrayCollection();
}
public function getId(): int
{
return $this->id;
......@@ -172,4 +188,25 @@ class Capsule
{
$this->password = $password;
}
public function addEditor(User $editor): void
{
$editor->addCapsule($this);
$this->editors[] = $editor;
}
public function removeEditor(User $editor): Capsule
{
$editor->removeCapsule($this);
$this->editors->removeElement($editor);
return $this;
}
/**
* @return Collection<User> $editors
*/
public function getEditors(): Collection
{
return $this->editors;
}
}
<?php
namespace App\Entity;
use App\Repository\CapsulePendingEditorRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="invitation_editeur_capsule")
* @ORM\Entity(repositoryClass=CapsulePendingEditorRepository::class)
*/
class CapsulePendingEditor
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private int $id;
/**
* @ORM\Column(type="integer")
* @ORM\Column(name="capsule_id", type="integer", nullable=false)
* @ORM\ManyToOne(targetEntity="App\Entity\Capsule", inversedBy="id")
* @ORM\JoinColumn(name="capsule_id", referencedColumnName="id")
*/
private int $capsule_id;
/**
* @ORM\Column(name="email", type="string", length=255, nullable=false)
*/
private string $email;
public function getId(): int
{
return $this->id;
}
public function getCapsuleId(): int
{
return $this->capsule_id;
}
public function setCapsuleId(int $capsule_id): self
{
$this->capsule_id = $capsule_id;
return $this;
}
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
}
<?php
namespace App\Entity;
use App\Repository\PendingEditorInvitationRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Table(name="invitation_editeur_capsule")
* @ORM\Entity(repositoryClass=PendingEditorInvitationRepository::class)
*/
class PendingEditorInvitation
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private int $id;
/**
* @ORM\Column(name="capsule_id", type="integer", nullable=false)
* @ORM\ManyToOne(targetEntity="App\Entity\Capsule", inversedBy="id")
* @ORM\JoinColumn(name="capsule_id", referencedColumnName="id")
*/
private int $capsule_id;
/**
* @ORM\Column(type="string", length=255)
*/
private string $email;
public function getId(): int
{
return $this->id;
}
public function getCapsuleId(): int
{
return $this->capsule_id;
}
public function setCapsuleId(int $capsule_id): self
{
$this->capsule_id = $capsule_id;
return $this;
}
public function getEmail(): string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
}
......@@ -3,6 +3,8 @@
namespace App\Entity;
use App\Repository\UserRepository;
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\Security\Core\User\LegacyPasswordAuthenticatedUserInterface;
......@@ -81,6 +83,17 @@ class User implements UserInterface, LegacyPasswordAuthenticatedUserInterface
*/
private string $salt;
/**
* @var Collection<Capsule>
* @ORM\ManyToMany(targetEntity="App\Entity\Capsule", mappedBy="editors")
*/
private Collection $capsules;
public function __construct()
{
$this->capsules = new ArrayCollection();
}
public function getId(): int
{
return $this->id;
......@@ -211,4 +224,28 @@ class User implements UserInterface, LegacyPasswordAuthenticatedUserInterface
{
$this->salt = $salt;
}
public function getFullName(): string
{
return $this->firstName . " " . $this->lastName;
}
/**
* @return Collection<Capsule>
*/
public function getCapsules(): Collection
{
return $this->capsules;
}
public function addCapsule(Capsule $capsule): void
{
$this->capsules[] = $capsule;
}
public function removeCapsule(Capsule $capsule): User
{
$this->capsules->removeElement($capsule);
return $this;
}
}
<?php
namespace App\Form;
use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
use Symfony\Component\Validator\Constraints\NotBlank;
class CapsuleEditorsFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add(
'email',
EmailType::class,
[
'constraints' => [new NotBlank(['message' => 'email.not_blank'])],
'label' => 'editors.add_email_address',
'empty_data' => ''
]
)
->add(
'validate',
SubmitType::class,
['label' => 'general.validate']
);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([]);
}
}
<?php
namespace App\Repository;
use App\Entity\CapsulePendingEditor;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method CapsulePendingEditor|null find($id, $lockMode = null, $lockVersion = null)
* @method CapsulePendingEditor|null findOneBy(array $criteria, array $orderBy = null)
* @method CapsulePendingEditor[] findAll()
* @method CapsulePendingEditor[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class CapsulePendingEditorRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, CapsulePendingEditor::class);
}
/**
* @return array<string>
*/
public function getPendingEditorsEmails(int $capsule_id): array
{
$editors_emails_result = $this->createQueryBuilder('c')
->select('c.email')
->andWhere('c.capsule_id = :val')
->setParameter('val', $capsule_id)
->getQuery()
->getResult()
;
$editors_emails = [];
foreach ($editors_emails_result as $editor_email_result) {
$editors_emails[] = $editor_email_result['email'];
}
return $editors_emails;
}
}
......@@ -21,11 +21,4 @@ class CapsuleRepository extends ServiceEntityRepository
{
parent::__construct($registry, Capsule::class);
}
public function doCapsuleBelongsToUser(Capsule $capsule, User $user): bool
{
return
$capsule->getCreationAuthor() === $user ||
$capsule->getUpdateAuthor() === $user;
}
}
<?php
namespace App\Repository;
use App\Entity\PendingEditorInvitation;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method PendingEditorInvitation|null find($id, $lockMode = null, $lockVersion = null)
* @method PendingEditorInvitation|null findOneBy(array $criteria, array $orderBy = null)
* @method PendingEditorInvitation[] findAll()
* @method PendingEditorInvitation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PendingEditorInvitationRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, PendingEditorInvitation::class);
}
/**
* @return array<PendingEditorInvitation>
*/
public function getInvitationByCapsule(int $capsule_id): array
{
$query = $this->createQueryBuilder('i')
->where("i.capsuleId like :capId")
->setParameter("capId", $capsule_id)
->getQuery();
return $query->getArrayResult();
}
// /**
// * @return PendingEditorInvitation[] Returns an array of PendingEditorInvitation objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->orderBy('p.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?PendingEditorInvitation
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
......@@ -4,6 +4,8 @@ namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
......@@ -57,4 +59,17 @@ class UserRepository extends ServiceEntityRepository implements PasswordUpgrader
->getOneOrNullResult()
;
}
/**
* @param array<int> $user_ids
* @return array<User>
*/
public function getUsersFromIds(array $user_ids): array
{
return $this->createQueryBuilder('u')
->andWhere('u.id in (:ids)')
->setParameter('ids', $user_ids)
->getQuery()
->getResult();
}
}
......@@ -28,14 +28,15 @@ class EmailVerifier
public function sendEmailConfirmation(
string $verifyEmailRouteName,
User $user,
int $user_id,
string $email_address,
TemplatedEmail $email
): void {
$signatureComponents = $this->verifyEmailHelper->generateSignature(
$verifyEmailRouteName,
(string) $user->getId(),
$user->getEmail(),
['id' => $user->getId()]
(string) $user_id,
$email_address,
['id' => $user_id]
);
$context = $email->getContext();
......
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>
{{ 'editors.add.user.email.title'|trans }}
</title>
</head>
<body>
<div class="container d-flex flex-row justify-content-center">
<div class="col-6">
<p class="alert">
{{ 'editors.add.user.email.text'|trans(
{'%user_name%': user.getFullName(), '%capsule_name%': capsule.getName()}) }}
<a href="{{ capsule_edit_link }}">
{{ 'editors.add.user.email.link'|trans }}
</a>
</p>
<p>{{ 'general.greeting'|trans }}</p>
</div>
</div>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>
{{ 'editors.add.pending_editor.email.title'|trans }}
</title>
</head>
<body>
<div class="container d-flex flex-row justify-content-center">
<div class="col-6">
<p class="alert">
{{ 'editors.add.pending_editor.email.text'|trans({'%user_name%': user.getFullName(), '%capsule_name%': capsule.getName()}) }} :
<a href="{{ 'editors.add.pending_editor.email.link'|trans }}">
{{ 'editors.add.pending_editor.email.link_name'|trans }}
</a>
</p>
<p>{{ 'general.greeting'|trans }}</p>
</div>
</div>
</body>
</html>
\ No newline at end of file
{% extends 'layout.html.twig' %}
{% block title %}
{{ 'editors.title'|trans }}
-
{{ parent() }}
{% endblock %}
{% block body %}
<div>
<div class="row w-100 gx-0">
<div class="row-title-box">
<h3 class="row-title">
{{ 'editors.title_name'|trans({'%capsule_name%': capsule_name}) }}
</h3>
</div>
</div>
{% for flashWarning in app.flashes('warning') %}
<div class="text-center alert alert-warning col-5 mx-auto my-5 mt-2" role="alert">
{{ flashWarning }}
</div>
{% endfor %}
{% for flashSuccess in app.flashes('success') %}
<div class="text-center alert alert-success col-5 mx-auto my-5 mt-2" role="alert">
{{ flashSuccess }}
</div>
{% endfor %}
<div class="d-flex flex-md-row flex-column justify-content-center">
<div class="d-flex flex-column justify-content-center ms-md-5 ms-0 order-md-2 mb-4 col-sm-8 col-md-6 col-lg-5 col-xl-4">
{{ form_start(userPermissionsCapsuleForm, {'attr': {novalidate: 'novalidate'}}) }}
{{ form_row(userPermissionsCapsuleForm.email, {'row_attr': {'class' : 'm-auto mb-4'}}) }}
{{ form_row(userPermissionsCapsuleForm.validate) }}
{{ form_end(userPermissionsCapsuleForm) }}
</div>
<div class="d-flex flex-row pe-md-5 pb-3 fw-normal me-0 me-md-5">
<div class="pe-3 pe-md-4 text-nowrap">
<h5>
{{ 'editors.current_editors_title'|trans }}
</h5>
<ul class="ps-0">
{% for editor in editors %}
<li class="text-capitalize text-secondary list-unstyled p-1">
{{ editor.getFirstName() }} {{ editor.getLastName() }}
</li>
{% endfor %}
</ul>
<h5>
{{ 'editors.pending_editors_title'|trans }}
</h5>
<ul class="ps-1">
{% for pending_editor in pending_editors %}
<li class="text-secondary list-unstyled p-1">
{{ pending_editor.getEmail() }}
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
{% endblock %}
\ No newline at end of file
......@@ -8,20 +8,23 @@
{% block body %}
<div class="row gx-0">
<div class="row-title-box">
<h3 class="row-title">
<div class="row-title-box d-flex justify-content-between align-items-center">
<h2 class="row-title">
{{ 'capsule.title'|trans }}
</h3>
</h2>
<form class="d-none d-md-flex">
<button id="btn-orange" formaction="/create">
+ {{ 'capsule.create_capsule'|trans }}
</button>
</form>
</div>
</div>
<div class="mt-4">
<div class="d-flex justify-content-center align-items-center">
<form>
<form class="d-md-none d-flex justify-content-center align-items-center">
<button id="btn-orange" formaction="/create">
+ {{ 'capsule.create_capsule'|trans }}
</button>
</form>
</div>
{% for flashWarning in app.flashes('warning') %}
<div class="text-center alert alert-warning col-5 mx-auto my-5" role="alert">
......@@ -37,9 +40,9 @@
</div>
<div class="capsules-list d-flex flex-column m-6">
<div class="d-flex flex-column align-items-center mt-4 mb-4">
{% for capsule in capsules %}
<div class="capsule-item pb-4 m-5">
<div class="capsule-item pb-4 col-12 col-lg-10 col-xl-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">
......@@ -64,7 +67,7 @@
<i class="fa-thin fa-gears"></i>
<div class="list-item text-nowrap">
<a href="" class="links text-decoration-none">
<a href="/capsule/{{ capsule.getId() }}/editors" class="links text-decoration-none">
<i class="fas fa-cog m-2"></i>
{{ 'capsule.edit_permissions.link'|trans }}
</a>
......@@ -75,12 +78,16 @@
{{ 'capsule.duplicate.link'|trans }}
</a>
</div>
{% 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">
<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">
<button class="standard-button p-2">
{{ 'capsule.edit.link'|trans }}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment