migration symfo
This commit is contained in:
@ -2,23 +2,18 @@
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Service\EtherpadService;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
class HomeController extends AbstractController
|
||||
{
|
||||
#[Route('/', name: 'app_home')]
|
||||
public function home(Request $request): Response
|
||||
public function home(ProjectRepository $projectRepository): Response
|
||||
{
|
||||
$user = $this->getUser();
|
||||
if (!$user instanceof User) {
|
||||
throw new AccessDeniedException('Vous n\'avez pas accès à cette ressource.');
|
||||
}
|
||||
$projects = $user->getProjects();
|
||||
$projects = $projectRepository->findAll();
|
||||
|
||||
return $this->render('home/home.html.twig', [
|
||||
'usemenu' => true,
|
||||
@ -35,4 +30,19 @@ class HomeController extends AbstractController
|
||||
'usesidebar' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route('/user/etherpad/{id}', name: 'app_ehterpad')]
|
||||
public function etherpad(string $id, EtherpadService $etherpadService): Response
|
||||
{
|
||||
$padAccess = $etherpadService->preparePadAccess($this->getUser(), $id);
|
||||
dump(vars: $padAccess);
|
||||
|
||||
$response = $this->render('etherpad/show.html.twig', [
|
||||
'iframeUrl' => $padAccess['iframeUrl'],
|
||||
]);
|
||||
|
||||
$response->headers->setCookie($padAccess['cookie']);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +121,9 @@ class ProjectController extends AbstractController
|
||||
$attribute = constant(ProjectVoter::class.'::MOVE'.$status);
|
||||
$this->denyAccessUnlessGranted($attribute, $project);
|
||||
|
||||
if (Project::VOTED == $status) {
|
||||
}
|
||||
|
||||
$project->setStatus(constant(Project::class.'::'.$status));
|
||||
$em->flush();
|
||||
|
||||
|
@ -42,6 +42,15 @@ class Project
|
||||
#[ORM\Column(type: 'datetime', nullable: true)]
|
||||
private ?\DateTimeInterface $dueDate = null;
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $nbVoteWhite = 0;
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $nbVoteNull = 0;
|
||||
|
||||
#[ORM\Column(type: 'string', nullable: true)]
|
||||
private ?string $resultVote = null;
|
||||
|
||||
/**
|
||||
* @var Collection<int, User>
|
||||
*/
|
||||
@ -51,14 +60,14 @@ class Project
|
||||
/**
|
||||
* @var Collection<int, ProjectTimeline>
|
||||
*/
|
||||
#[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectTimeline::class, cascade: ['remove'])]
|
||||
#[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectTimeline::class, cascade: ['remove'], orphanRemoval: true)]
|
||||
#[ORM\OrderBy(['createdAt' => 'DESC'])]
|
||||
private Collection $timelines;
|
||||
|
||||
/**
|
||||
* @var Collection<int, ProjectTimeline>
|
||||
* @var Collection<int, ProjectOption>
|
||||
*/
|
||||
#[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectOption::class, cascade: ['remove'])]
|
||||
#[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectOption::class, cascade: ['remove'], orphanRemoval: true)]
|
||||
#[ORM\OrderBy(['title' => 'ASC'])]
|
||||
private Collection $options;
|
||||
|
||||
@ -146,6 +155,42 @@ class Project
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNbVoteWhite(): int
|
||||
{
|
||||
return $this->nbVoteWhite;
|
||||
}
|
||||
|
||||
public function setNbVoteWhite(int $nbVoteWhite): self
|
||||
{
|
||||
$this->nbVoteWhite = $nbVoteWhite;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNbVoteNull(): int
|
||||
{
|
||||
return $this->nbVoteNull;
|
||||
}
|
||||
|
||||
public function setNbVoteNull(int $nbVoteNull): self
|
||||
{
|
||||
$this->nbVoteNull = $nbVoteNull;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getResultVote(): ?string
|
||||
{
|
||||
return $this->resultVote;
|
||||
}
|
||||
|
||||
public function setResultVote(?string $resultVote): self
|
||||
{
|
||||
$this->resultVote = $resultVote;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, User>
|
||||
*/
|
||||
@ -182,4 +227,21 @@ class Project
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
public function addOption(ProjectOption $option): self
|
||||
{
|
||||
if (!$this->options->contains($option)) {
|
||||
$this->options->add($option);
|
||||
$option->setProject($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeOption(ProjectOption $option): self
|
||||
{
|
||||
$this->options->removeElement($option);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,9 @@ class ProjectOption
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $whyNot = null;
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $nbVote = 0;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
@ -78,4 +81,16 @@ class ProjectOption
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNbVote(): int
|
||||
{
|
||||
return $this->nbVote;
|
||||
}
|
||||
|
||||
public function setNbVote(int $nbVote): self
|
||||
{
|
||||
$this->nbVote = $nbVote;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use Bnine\MdEditorBundle\Form\Type\MarkdownType;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
@ -28,6 +29,12 @@ class ProjectType extends AbstractType
|
||||
'label' => 'Titre',
|
||||
])
|
||||
|
||||
->add('dueDate', DateType::class, [
|
||||
'label' => 'A Voter pour le',
|
||||
'required' => false,
|
||||
'html5' => true,
|
||||
])
|
||||
|
||||
->add('summary', TextareaType::class, [
|
||||
'label' => 'Résumé',
|
||||
])
|
||||
|
40
src/Form/ProjectVotedType.php
Normal file
40
src/Form/ProjectVotedType.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\Project;
|
||||
use App\Form\Type\ProjectOptionType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class ProjectVotedType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('submit', SubmitType::class, [
|
||||
'label' => 'Valider',
|
||||
'attr' => ['class' => 'btn btn-success no-print me-1'],
|
||||
])
|
||||
|
||||
->add('options', CollectionType::class, [
|
||||
'entry_type' => ProjectOptionType::class,
|
||||
'entry_options' => ['label' => false],
|
||||
'label' => false,
|
||||
'allow_add' => false,
|
||||
'allow_delete' => false,
|
||||
'by_reference' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Project::class,
|
||||
'mode' => 'submit',
|
||||
]);
|
||||
}
|
||||
}
|
31
src/Form/Type/ProjectOptionType.php
Normal file
31
src/Form/Type/ProjectOptionType.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Entity\ProjectOption;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class ProjectOptionType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||
{
|
||||
$builder
|
||||
->add('title', null, [
|
||||
'disabled' => true,
|
||||
'label' => 'Titre',
|
||||
])
|
||||
->add('nbVote', IntegerType::class, [
|
||||
'label' => 'Nombre de votes',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => ProjectOption::class,
|
||||
]);
|
||||
}
|
||||
}
|
@ -18,10 +18,11 @@ class ProjectVoter extends Voter
|
||||
public const MOVETOVOTE = 'MOVETOVOTE';
|
||||
public const MOVEVOTED = 'MOVEVOTED';
|
||||
public const MOVEARCHIVED = 'MOVEARCHIVED';
|
||||
public const TOME = 'TOME';
|
||||
|
||||
protected function supports(string $attribute, $subject): bool
|
||||
{
|
||||
$attributes = [self::VIEW, self::SUBMIT, self::UPDATE, self::DELETE, self::MOVEDRAFT, self::MOVETOVOTE, self::MOVEVOTED, self::MOVEARCHIVED];
|
||||
$attributes = [self::VIEW, self::SUBMIT, self::UPDATE, self::DELETE, self::MOVEDRAFT, self::MOVETOVOTE, self::MOVEVOTED, self::MOVEARCHIVED, self::TOME];
|
||||
|
||||
return in_array($attribute, $attributes) && $subject instanceof Project;
|
||||
}
|
||||
@ -79,7 +80,6 @@ class ProjectVoter extends Voter
|
||||
{
|
||||
$hasUser = $user->hasRole('ROLE_ADMIN') || $user->hasRole('ROLE_MASTER');
|
||||
$hasStatus = Project::TOVOTE === $project->getStatus() || Project::ARCHIVED === $project->getStatus();
|
||||
dump($hasStatus);
|
||||
|
||||
return $hasUser && $hasStatus;
|
||||
}
|
||||
@ -92,13 +92,17 @@ class ProjectVoter extends Voter
|
||||
return $hasUser && $hasStatus;
|
||||
}
|
||||
|
||||
private function toMe(Project $project, User $user): bool
|
||||
{
|
||||
return $project->getUsers()->contains($user);
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, $project, TokenInterface $token): bool
|
||||
{
|
||||
$user = $token->getUser();
|
||||
if (!$user instanceof User) {
|
||||
return false;
|
||||
}
|
||||
dump($attribute);
|
||||
|
||||
return match ($attribute) {
|
||||
self::VIEW => $this->canView($project, $user),
|
||||
@ -109,6 +113,7 @@ class ProjectVoter extends Voter
|
||||
self::MOVETOVOTE => $this->canMoveToVote($project, $user),
|
||||
self::MOVEVOTED => $this->canMoveVoted($project, $user),
|
||||
self::MOVEARCHIVED => $this->canMoveArchived($project, $user),
|
||||
self::TOME => $this->toMe($project, $user),
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
|
117
src/Service/EtherpadService.php
Normal file
117
src/Service/EtherpadService.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
|
||||
class EtherpadService
|
||||
{
|
||||
private HttpClientInterface $client;
|
||||
private string $etherpadUrl;
|
||||
private string $etherpadInternalUrl;
|
||||
private string $etherpadApiKey;
|
||||
|
||||
public function __construct(HttpClientInterface $client, ParameterBagInterface $parameter)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->etherpadUrl = $parameter->get('etherpadUrl');
|
||||
$this->etherpadInternalUrl = $parameter->get('etherpadInternalUrl');
|
||||
$this->etherpadApiKey = $parameter->get('etherpadApiKey');
|
||||
}
|
||||
|
||||
public function preparePadAccess(User $user, string $padName): array
|
||||
{
|
||||
// 1. Vérifier/créer l'auteur
|
||||
$authorId = $this->createAuthorIfNeeded($user);
|
||||
|
||||
// 2. Vérifier/créer le groupe lié à ce pad
|
||||
$groupId = $this->createGroupIfNeeded($padName);
|
||||
|
||||
// 3. Créer le pad si pas encore créé
|
||||
$this->createGroupPadIfNeeded($groupId, $padName);
|
||||
|
||||
// 4. Créer une session temporaire (ex: 1h)
|
||||
$validUntil = time() + 3600;
|
||||
$sessionId = $this->createSession($groupId, $authorId, $validUntil);
|
||||
|
||||
// 5. Construire le cookie à ajouter à la réponse
|
||||
$cookie = Cookie::create('sessionID', $sessionId)
|
||||
->withDomain(parse_url($this->etherpadInternalUrl, PHP_URL_HOST))
|
||||
->withPath('/')
|
||||
->withSecure(true)
|
||||
->withHttpOnly(false)
|
||||
->withExpires($validUntil);
|
||||
|
||||
// 6. Construire l’URL iframe
|
||||
$iframeUrl = sprintf(
|
||||
'%s/p/%s?showControls=true&showChat=false&userName=%s',
|
||||
rtrim($this->etherpadUrl, '/'),
|
||||
$padName,
|
||||
urlencode($user->getUsername())
|
||||
);
|
||||
|
||||
return [
|
||||
'iframeUrl' => $iframeUrl,
|
||||
'cookie' => $cookie,
|
||||
];
|
||||
}
|
||||
|
||||
private function createAuthorIfNeeded(User $user): string
|
||||
{
|
||||
// Idéalement, stocker $authorId en base pour éviter de le recréer
|
||||
$response = $this->client->request('GET', $this->etherpadInternalUrl.'/api/1.2.15/createAuthor', [
|
||||
'query' => [
|
||||
'apikey' => $this->etherpadApiKey,
|
||||
'name' => $user->getUsername(),
|
||||
],
|
||||
]);
|
||||
|
||||
$data = $response->toArray(false);
|
||||
|
||||
return $data['data']['authorID'] ?? throw new \RuntimeException('Impossible de créer l\'author');
|
||||
}
|
||||
|
||||
private function createGroupIfNeeded(string $padName): string
|
||||
{
|
||||
// Ici tu peux lier un groupID en base à ton padName
|
||||
$response = $this->client->request('GET', $this->etherpadInternalUrl.'/api/1.2.15/createGroup', [
|
||||
'query' => [
|
||||
'apikey' => $this->etherpadApiKey,
|
||||
],
|
||||
]);
|
||||
|
||||
$data = $response->toArray(false);
|
||||
|
||||
return $data['data']['groupID'] ?? throw new \RuntimeException('Impossible de créer le group');
|
||||
}
|
||||
|
||||
private function createGroupPadIfNeeded(string $groupId, string $padName): void
|
||||
{
|
||||
$this->client->request('GET', $this->etherpadInternalUrl.'/api/1.2.15/createGroupPad', [
|
||||
'query' => [
|
||||
'apikey' => $this->etherpadApiKey,
|
||||
'groupID' => $groupId,
|
||||
'padName' => $padName,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
private function createSession(string $groupId, string $authorId, int $validUntil): string
|
||||
{
|
||||
$response = $this->client->request('GET', $this->etherpadInternalUrl.'/api/1.2.15/createSession', [
|
||||
'query' => [
|
||||
'apikey' => $this->etherpadApiKey,
|
||||
'groupID' => $groupId,
|
||||
'authorID' => $authorId,
|
||||
'validUntil' => $validUntil,
|
||||
],
|
||||
]);
|
||||
|
||||
$data = $response->toArray(false);
|
||||
|
||||
return $data['data']['sessionID'] ?? throw new \RuntimeException('Impossible de créer la session');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user