1
0
mirror of https://framagit.org/tykayn/date-poll-api synced 2023-08-25 08:23:11 +02:00

Compare commits

...

12 Commits

Author SHA1 Message Date
root
747ebb9d2f update script pulling on master 2021-04-27 13:11:39 +02:00
f4ce7056cd nullable ip 2021-04-27 12:52:17 +02:00
4bf2625908 cleanup src 2021-04-27 12:51:21 +02:00
94c02be927 use trait timestamp and replace 2021-04-27 12:51:10 +02:00
ecdbf3fb33 format date string for choices in poll of date kind 2021-04-27 11:26:54 +02:00
8c2ad610a5 compute answers in choices 2021-04-27 11:17:41 +02:00
665cf6d173 enrich stats for choices 2021-04-27 11:11:34 +02:00
6831d8d1ae add ip on stack of vote 2021-04-27 10:41:21 +02:00
eaee01838d uniform creation key created_at and format C 2021-04-27 10:30:45 +02:00
7f7265c851 reformat src folder 2021-04-27 10:22:16 +02:00
56c8b0a5c4 add timestamp trait 2021-04-27 10:21:21 +02:00
30c99568dd remove duplicate data 2021-04-26 15:43:33 +02:00
30 changed files with 1474 additions and 1387 deletions

View File

@ -2,14 +2,9 @@
namespace App\Controller;
use App\Entity\Owner;
use App\Entity\Poll;
use App\Repository\PollRepository;
use App\Service\MailService;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\Annotations\Route;
use JMS\Serializer\Type\Exception\Exception;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
/**

View File

@ -5,8 +5,10 @@ namespace App\Controller;
use App\Entity\Owner;
use App\Entity\Poll;
use JMS\Serializer\Type\Exception\Exception;
use Swift_Mailer;
use Swift_Message;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
/**
* sending emails controller
@ -18,16 +20,37 @@ class EmailsController extends AbstractController {
private $mail_service;
public function __construct( \Swift_Mailer $mailer ) {
public function __construct( Swift_Mailer $mailer ) {
$this->mail_service = $mailer;
}
/**
* send created polls to an owner
*
* @param Owner $owner
*
* @return int|void
* @throws Exception
* @throws TransportExceptionInterface
*/
public function sendOwnerPollsAction( Owner $owner ) {
$config = [
'owner' => $owner,
'title' => $this->getParameter( 'WEBSITE_NAME' ) . ' | Mes sondages',
'email_template' => 'emails/owner-list.html.twig',
];
$this->sendMailWithVars( $config );
return 1;
}
/**
* generic way to send email with html template
*
* @param $config
*
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
* @throws TransportExceptionInterface
*/
public function sendMailWithVars( $config ) {
@ -72,33 +95,11 @@ class EmailsController extends AbstractController {
}
/**
* send created polls to an owner
*
* @param Owner $owner
*
* @return int|void
* @throws Exception
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
*/
public function sendOwnerPollsAction( Owner $owner ) {
$config = [
'owner' => $owner,
'title' => $this->getParameter( 'WEBSITE_NAME' ) . ' | Mes sondages',
'email_template' => 'emails/owner-list.html.twig',
];
$this->sendMailWithVars( $config );
return 1;
}
/**
* @param Owner $foundOwner
* @param Poll|null $poll
*
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
* @throws TransportExceptionInterface
*/
public function sendCreationMailAction( Owner $foundOwner, Poll $poll = null ) {
@ -120,7 +121,7 @@ class EmailsController extends AbstractController {
* @param $comment
*
* @return int
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
* @throws TransportExceptionInterface
*/
public function sendCommentNotificationAction( Owner $owner, $comment ) {
@ -141,7 +142,7 @@ class EmailsController extends AbstractController {
* @param $stackOfVotes
*
* @return int
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
* @throws TransportExceptionInterface
*/
public function sendVoteNotificationAction( Owner $owner, $stackOfVotes ) {

View File

@ -1,20 +1,19 @@
<?php
namespace App\Controller;
//use FOS\RestBundle\Controller\Annotations\Get;
//use FOS\RestBundle\Controller\Annotations\Route;
use App\Entity\Choice;
use App\Entity\Comment;
use App\Entity\StackOfVotes;
use App\Entity\Vote;
use App\Repository\PollRepository;
use App\Service\MailService;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\Annotations\Route;
use JMS\Serializer\Type\Exception\Exception;
use Symfony\Component\HttpFoundation\JsonResponse;
use App\Entity\Owner;
use App\Entity\Poll;
use App\Entity\StackOfVotes;
use App\Entity\Vote;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\Annotations\Route;
use PDO;
use Symfony\Component\HttpFoundation\JsonResponse;
/**
* Class DefaultController
@ -34,7 +33,7 @@ class MigrationController extends EmailsController {
return new JsonResponse( [
'error' => 'NOPE! veuillez vérifier votre fichier .env',
] );
};
}
// fetch old Database
@ -45,13 +44,13 @@ class MigrationController extends EmailsController {
$pollsBySlug = [];
$pdo_options[ \PDO::ATTR_ERRMODE ] = \PDO::ERRMODE_EXCEPTION;
$bdd = new \PDO( 'mysql:host=localhost;dbname=' . $this->getParameter( 'OLD_DATABASE_NAME' ),
$pdo_options[ PDO::ATTR_ERRMODE ] = PDO::ERRMODE_EXCEPTION;
$bdd = new PDO( 'mysql:host=localhost;dbname=' . $this->getParameter( 'OLD_DATABASE_NAME' ),
$this->getParameter( 'OLD_DATABASE_USER' ),
$this->getParameter( 'OLD_DATABASE_PASS' ),
$pdo_options );
$res_polls = $bdd->query( 'SELECT * FROM fd_poll' );
while ( $d = $res_polls->fetch( \PDO::FETCH_OBJ ) ) {
while ( $d = $res_polls->fetch( PDO::FETCH_OBJ ) ) {
$debug .= " <br> ajout de sondage : " . $d->title . ' - ' . $d->id;
@ -75,7 +74,7 @@ class MigrationController extends EmailsController {
->setChoicesMax( $d->ValueMax )
->setPassword( $d->password_hash )
->setDescription( $d->description )
->setCreationDate( date_create( $d->creation_date ) );
->setCreatedAt( date_create( $d->creation_date ) );
$pollsBySlug[ $d->id ] = $newPoll;
@ -88,7 +87,7 @@ class MigrationController extends EmailsController {
$pollChoicesOrderedBySlug = [];
$choicesCreated = [];
while ( $d = $res_slots->fetch( \PDO::FETCH_OBJ ) ) {
while ( $d = $res_slots->fetch( PDO::FETCH_OBJ ) ) {
$debug .= " <br> ajout de slot, converti en choix de réponse : " . $d->poll_id . ' : ' . $d->moments;
@ -115,7 +114,7 @@ class MigrationController extends EmailsController {
// get votes
$stacksOfVote = [];
$res_votes = $bdd->query( 'SELECT * FROM fd_vote' );
while ( $d = $res_votes->fetch( \PDO::FETCH_OBJ ) ) {
while ( $d = $res_votes->fetch( PDO::FETCH_OBJ ) ) {
$debug .= " <br> ajout de stack de vote : " . $d->name;
$pollSlug = $d->poll_id;
@ -126,13 +125,11 @@ class MigrationController extends EmailsController {
$newOwner
->setPseudo( $d->name )
->setEmail( 'the_anonymous_email_from_@_migration_offramadate.org' )
->setModifierToken($d->uniqId)
;
->setModifierToken( $d->uniqId );
$newStack->setPoll( $poll )
->setOwner( $newOwner )
->setPseudo($d->name)
;
->setPseudo( $d->name );
// each choice answer is encoded in a value :
@ -150,8 +147,7 @@ class MigrationController extends EmailsController {
->setChoice( $choice )
->setStacksOfVotes( $newStack )
->setPoll( $poll )
->setValue( $this->mapAnswerNumberToWord($vote_code))
;
->setValue( $this->mapAnswerNumberToWord( $vote_code ) );
$newStack->addVote( $newVote );
$em->persist( $newVote );
@ -169,7 +165,7 @@ class MigrationController extends EmailsController {
$comments = [];
$res_comments = $bdd->query( 'SELECT * FROM fd_comment' );
while ( $d = $res_comments->fetch( \PDO::FETCH_OBJ ) ) {
while ( $d = $res_comments->fetch( PDO::FETCH_OBJ ) ) {
$debug .= " <br> ajout de commentaire : " . $d->name . ' ' . $d->comment;
@ -194,7 +190,8 @@ class MigrationController extends EmailsController {
// failure notice
$debug .= " <br> <br> ça c'est fait. ";
return $this->render('pages/migration.html.twig' , [
return $this->render( 'pages/migration.html.twig',
[
"message" => "welcome to the framadate migration endpoint, it has yet to be done",
"debug" => $debug,
"OLD_DATABASE_NAME" => $this->getParameter( 'OLD_DATABASE_NAME' ),
@ -205,13 +202,15 @@ class MigrationController extends EmailsController {
'choices' => count( $choicesCreated ),
'stacks_of_votes' => count( $stacksOfVote ),
'votes' => count( $votes ),
]
],
] );
}
/**
* @param $numberToConvert
* conversion of answer:
* space character : no answer, 0 : no , 1 : maybe , 2 : yes
*
* @return string
*/
public function mapAnswerNumberToWord( $numberToConvert ) {
@ -229,6 +228,7 @@ class MigrationController extends EmailsController {
default:
$word = 'no';
}
return $word;
}

View File

@ -9,25 +9,24 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
/**
* @Route("/poll")
*/
class PollController extends AbstractController
{
class PollController extends AbstractController {
/**
* @Route("/", name="poll_index", methods={"GET"})
*/
public function index(PollRepository $pollRepository): Response
{
public function index( PollRepository $pollRepository ): Response {
$polls = $pollRepository->findAll();
$titles = [];
foreach ( $polls as $poll ) {
$titles[] = $poll->getTitle();
}
return $this->render('poll/index.html.twig', [
return $this->render( 'poll/index.html.twig',
[
'count' => count( $polls ),
'titles' => $titles,
'polls' => $polls,
@ -37,8 +36,7 @@ class PollController extends AbstractController
/**
* @Route("/new", name="poll_new", methods={"GET","POST"})
*/
public function new(Request $request): Response
{
public function new( Request $request ): Response {
$poll = new Poll();
$form = $this->createForm( PollType::class, $poll );
$form->handleRequest( $request );
@ -51,7 +49,8 @@ class PollController extends AbstractController
return $this->redirectToRoute( 'poll_index' );
}
return $this->render('poll/new.html.twig', [
return $this->render( 'poll/new.html.twig',
[
'poll' => $poll,
'form' => $form->createView(),
] );
@ -61,17 +60,18 @@ class PollController extends AbstractController
* on cherche un sondage par son url personnalisée
* @Route("/{id}", name="poll_show", methods={"GET"})
*/
public function show($id): Response
{
public function show( $id ): Response {
$repository = $this->getDoctrine()->getRepository( Poll::class );
$foundPoll = $repository->findOneByCustomUrl( $id );
if ( ! $foundPoll ) {
return $this->json( [
'message' => $id.' : not found'
], 404);
'message' => $id . ' : not found',
],
404 );
}
return $this->render('poll/show.html.twig', [
return $this->render( 'poll/show.html.twig',
[
'poll' => $foundPoll,
] );
}
@ -79,8 +79,7 @@ class PollController extends AbstractController
/**
* @Route("/{id}/edit", name="poll_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Poll $poll): Response
{
public function edit( Request $request, Poll $poll ): Response {
$form = $this->createForm( PollType::class, $poll );
$form->handleRequest( $request );
@ -90,7 +89,8 @@ class PollController extends AbstractController
return $this->redirectToRoute( 'poll_index' );
}
return $this->render('poll/edit.html.twig', [
return $this->render( 'poll/edit.html.twig',
[
'poll' => $poll,
'form' => $form->createView(),
] );
@ -99,8 +99,7 @@ class PollController extends AbstractController
/**
* @Route("/{id}", name="poll_delete", methods={"DELETE"})
*/
public function delete(Request $request, Poll $poll): Response
{
public function delete( Request $request, Poll $poll ): Response {
if ( $this->isCsrfTokenValid( 'delete' . $poll->getId(), $request->request->get( '_token' ) ) ) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove( $poll );

View File

@ -38,7 +38,9 @@ class CommentController extends EmailsController {
) {
$jsonResponse = $serializer->serialize( [
'message' => 'here are your comments of the poll',
'data' => $poll->getComments()], 'json');
'data' => $poll->getComments(),
],
'json' );
$response = new Response( $jsonResponse );
$response->headers->set( 'Content-Type', 'application/json' );

View File

@ -7,6 +7,11 @@ use App\Entity\Choice;
use App\Entity\Owner;
use App\Entity\Poll;
use App\Repository\PollRepository;
use FOS\RestBundle\Controller\Annotations\Delete;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\Annotations\Post;
use FOS\RestBundle\Controller\Annotations\Put;
use FOS\RestBundle\Controller\Annotations\Route;
use JMS\Serializer\Exception\RuntimeException;
use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\SerializerInterface;
@ -14,13 +19,6 @@ use Swift_Mailer;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\Annotations\Put;
use FOS\RestBundle\Controller\Annotations\Delete;
use FOS\RestBundle\Controller\Annotations\Post;
use FOS\RestBundle\Controller\Annotations\Route;
/**
* Class DefaultController
@ -63,19 +61,6 @@ class PollController extends EmailsController {
}
/**
* @param $id
* message when the poll is not found
*
* @return JsonResponse
*/
public function notFoundPoll( $id ): Response {
return $this->json( [
'message' => $id . ' : poll not found',
],
404 );
}
/**
* get a poll config by its custom URL, we do not want polls to be reachable by their numeric id
* @Get(
@ -103,30 +88,9 @@ class PollController extends EmailsController {
$comments = $poll->getComments();
$stacks = $poll->getStacksOfVotes();
$displayedComments = [];
foreach ( $comments as $comment ) {
$displayedComments[] = $comment->display();
}
$displayedStackOfVotes = [];
foreach ( $stacks as $stack ) {
$displayedStackOfVotes[] = $stack->display();
}
$displayedChoices = [];
foreach ( $poll->getChoices() as $choice
) {
$displayedChoices[] = $choice->display();
}
$pass = $poll->getPassword();
$returnedPoll = [
'message' => 'your poll config for ' . $poll->getTitle(),
'poll' => $poll->display(),
// TODO do not render sub objects of owner, it returns too many thing
'stacks' => $displayedStackOfVotes,
'choices' => $displayedChoices,
'comments' => $displayedComments,
];
/**
* password protected content
@ -140,13 +104,24 @@ class PollController extends EmailsController {
} else {
// free access to poll
// return $this->returnPollData( $poll, $serializer );
return $this->json( $returnedPoll );
// return $this->json($poll);
return $this->json( $poll->display() );
}
}
/**
* @param $id
* message when the poll is not found
*
* @return JsonResponse
*/
public function notFoundPoll( $id ): Response {
return $this->json( [
'message' => $id . ' : poll not found',
],
404 );
}
/**
* get a poll config by its custom URL, we do not want polls to be reachable by their numeric id
* @Get(
@ -167,9 +142,9 @@ class PollController extends EmailsController {
return $this->notFoundPoll( $customUrl );
}
if ( $poll->getPassword() === $md5 ) {
if ( md5( $poll->getPassword() ) === $md5 ) {
// good matching pass
return $this->returnPollData( $poll, $serializer );
return $this->json( $poll->display() );
} else {
// wrong pass
return $this->json( [
@ -218,10 +193,8 @@ class PollController extends EmailsController {
$em->persist( $poll );
$em->flush();
return $this->json( [
'message' => 'you updated the poll ' . $poll->getTitle(),
"poll" => $poll,
],
return $this->json( $poll->displayForAdmin()
,
200 );
}
@ -303,7 +276,6 @@ class PollController extends EmailsController {
$newChoice = new Choice();
$newChoice
->setPoll( $newpoll )
// ->setUrl( $c[ 'url' ] )
->setName( $c[ 'literal' ] );
$em->persist( $newChoice );
// TODO add also choices for each time range in a day
@ -331,10 +303,8 @@ class PollController extends EmailsController {
return $this->json( [
'message' => 'you created a poll ' . $precision,
'poll' => $newpoll,
'poll' => $newpoll->displayForAdmin,
'password_protected' => is_string( $newpoll->getPassword() ),
'admin_key' => $newpoll->getAdminKey(),
'owner_modifier_token' => $foundOwner->getModifierToken(),
],
201 );

View File

@ -31,6 +31,7 @@ class VoteController extends EmailsController {
* name = "new_vote_stack",
* requirements = {"content"="\w+", "poll_id"="\d+"}
* )
*
* @param SerializerInterface $serializer
* @param Poll $poll
* @param Request $request
@ -66,10 +67,11 @@ class VoteController extends EmailsController {
}
// TODO anti flood
$foundOwner
->setModifierToken( $poll->generateAdminKey() );
->setModifierToken( $poll->generateRandomKey() );
$stack = new StackOfVotes();
$stack
->setOwner( $foundOwner )
->setIp( $_SERVER[ 'REMOTE_ADDR' ] )
->setPseudo( $data[ 'pseudo' ] )
->setPoll( $poll );
foreach ( $data[ 'votes' ] as $voteInfo ) {
@ -139,6 +141,7 @@ class VoteController extends EmailsController {
* name = "update_vote_stack",
* requirements = { "id"="\d+"}
* )
*
* @param SerializerInterface $serializer
* @param StackOfVotes $id
* @param $modifierToken
@ -175,7 +178,8 @@ class VoteController extends EmailsController {
'message' => 'ok',
'modifier_token' => $voteStack->getOwner()->getModifierToken(),
'vote_stack' => $voteStack,
], 'json');
],
'json' );
$response = new Response( $jsonResponse );
$response->headers->set( 'Content-Type', 'application/json' );
@ -207,11 +211,13 @@ class VoteController extends EmailsController {
return $this->json( [
'message' => 'boom! les ' . $length . ' votes du sondage ont été supprimés',
],200 );
],
200 );
} else {
return $this->json( [
'message' => 'le token d\'autorisation est invalide, vous ne pouvez pas modifier ce sondage'
],403 );
'message' => 'le token d\'autorisation est invalide, vous ne pouvez pas modifier ce sondage',
],
403 );
}
}
}

View File

@ -6,10 +6,8 @@ use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
class AppFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
class AppFixtures extends Fixture {
public function load( ObjectManager $manager ) {
// $product = new Product();
// $manager->persist($product);

View File

@ -3,7 +3,6 @@
namespace App\DataFixtures;
use App\Entity\Owner;
use App\Entity\Poll;
use App\Entity\StackOfVotes;
use App\Entity\Vote;
use Doctrine\Bundle\FixturesBundle\Fixture;
@ -11,8 +10,7 @@ use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager;
class VotesStacksFixtures extends Fixture implements DependentFixtureInterface {
public function getDependencies()
{
public function getDependencies() {
return [
AppPollFixtures::class,
];

View File

@ -2,6 +2,7 @@
namespace App\Entity;
use App\Traits\TimeStampableTrait;
use DateTime;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
@ -15,6 +16,9 @@
* @Serializer\ExclusionPolicy("all")
*/
class Choice {
use TimeStampableTrait;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
@ -36,11 +40,7 @@
*/
public $url;
/**
* @ORM\Column(type="datetime", nullable=true)
* @Serializer\Type("datetime")
*/
public $dateTime;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Vote", mappedBy="choice", cascade={"persist"})
* @Serializer\Type("App\Entity\Vote")
@ -53,6 +53,7 @@
private $poll;
public function __construct( $optionalName = null ) {
$this->setCreatedAt( new DateTime() );
$this->poll = new ArrayCollection();
$this->votes = new ArrayCollection();
$this->setDateTime( new DateTime() );
@ -61,30 +62,31 @@
}
}
public function display() {
return [
'id' => $this->getId(),
'date' => $this->getDateTime(),
'name' => $this->getName(),
'url' => $this->getUrl(),
];
}
public function getId(): ?int {
return $this->id;
}
public function getDateTime(): ?DateTimeInterface {
return $this->dateTime;
}
public function setDateTime( ?DateTimeInterface $dateTime ): self {
$this->dateTime = $dateTime;
return $this;
}
public function display( $kind = 'text' ) {
$fields = [
'id' => $this->getId(),
'created_at' => $this->getCreatedAtAsString(),
'name' => $this->getName(),
'url' => $this->getUrl(),
];
if ( $kind === 'date' ) {
$date = new DateTime( $this->getName() );
$fields[ 'name' ] = $date->format( 'c' );
}
return $fields;
}
public function getId(): ?int {
return $this->id;
}
public function getName(): ?string {
return $this->name;
}
@ -95,6 +97,20 @@
return $this;
}
public function getUrl(): ?string {
return $this->url;
}
public function setUrl( ?string $url ): self {
$this->url = $url;
return $this;
}
public function getDateTime(): ?DateTimeInterface {
return $this->dateTime;
}
public function getPoll(): ?Poll {
return $this->poll;
}
@ -132,14 +148,4 @@
return $this;
}
public function getUrl(): ?string {
return $this->url;
}
public function setUrl( ?string $url ): self {
$this->url = $url;
return $this;
}
}

View File

@ -2,6 +2,8 @@
namespace App\Entity;
use App\Traits\TimeStampableTrait;
use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
@ -11,6 +13,9 @@ use JMS\Serializer\Annotation as Serializer;
* @Serializer\ExclusionPolicy("all")
*/
class Comment {
use TimeStampableTrait;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
@ -39,45 +44,38 @@ class Comment {
*/
private $text;
/**
* @ORM\Column(type="datetime")
* @Serializer\Type("datetime")
* @Serializer\Expose()
*/
private $createdAt;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Poll", inversedBy="comments")
*/
private $poll;
function __construct() {
$this->setCreatedAt( new DateTime() );
}
public function setCreatedAt( DateTimeInterface $createdAt ): self {
$this->createdAt = $createdAt;
return $this;
}
public function getCreatedAt(): ?DateTimeInterface {
return $this->createdAt;
}
function display() {
return [
'id' => $this->getId(),
'text' => $this->getText(),
'pseudo' => $this->getOwner()->getPseudo(),
'date' => $this->getCreatedAt(),
'created_at' => $this->getCreatedAtAsString(),
];
}
function __construct() {
$this->setCreatedAt( new \DateTime() );
}
public function getId(): ?int {
return $this->id;
}
public function getOwner(): ?Owner {
return $this->owner;
}
public function setOwner( ?Owner $owner ): self {
$this->owner = $owner;
return $this;
}
public function getText(): ?string {
return $this->text;
}
@ -88,12 +86,12 @@ class Comment {
return $this;
}
public function getCreatedAt(): ?DateTimeInterface {
return $this->createdAt;
public function getOwner(): ?Owner {
return $this->owner;
}
public function setCreatedAt( DateTimeInterface $createdAt ): self {
$this->createdAt = $createdAt;
public function setOwner( ?Owner $owner ): self {
$this->owner = $owner;
return $this;
}
@ -108,13 +106,11 @@ class Comment {
return $this;
}
public function getPseudo(): ?string
{
public function getPseudo(): ?string {
return $this->pseudo;
}
public function setPseudo(string $pseudo): self
{
public function setPseudo( string $pseudo ): self {
$this->pseudo = $pseudo;
return $this;

View File

@ -2,6 +2,9 @@
namespace App\Entity;
use App\Traits\TimeStampableTrait;
use DateTime;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
@ -11,24 +14,26 @@ use JMS\Serializer\Annotation as Serializer;
* @ORM\Entity(repositoryClass="App\Repository\OwnerRepository")
*/
class Owner {
use TimeStampableTrait;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Type("string")
* @Serializer\Expose()
*/
public $pseudo;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Type("string")
* @Serializer\Expose()
*/
public $email;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Poll", mappedBy="owner",cascade={"persist","remove"},orphanRemoval=true)
* @Serializer\Type("App\Entity\Poll")
@ -49,10 +54,7 @@ class Owner {
* @ORM\Column(type="string", length=255)
*/
private $modifierToken;
/**
* @ORM\Column(type="datetime" , options={"default"="CURRENT_TIMESTAMP"})
*/
private $createdAt;
/**
* @ORM\Column(type="datetime" , options={"default"="CURRENT_TIMESTAMP"},nullable=true)
*/
@ -62,10 +64,54 @@ class Owner {
$this->polls = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->stackOfVotes = new ArrayCollection();
$this->setCreatedAt( new \DateTime() );
$this->setCreatedAt( new DateTime() );
$this->setModifierToken( uniqid() );
$this->setCreatedAt( new DateTime() );
}
public function setCreatedAt( DateTimeInterface $createdAt ): self {
$this->createdAt = $createdAt;
return $this;
}
public function getCreatedAt(): ?DateTimeInterface {
return $this->createdAt;
}
public function display() {
return [
'pseudo' => $this->getPseudo(),
];
}
public function getPseudo(): ?string {
return $this->pseudo;
}
public function setPseudo( string $pseudo ): self {
$this->pseudo = $pseudo;
return $this;
}
public function displayForAdmin() {
return [
'pseudo' => $this->getPseudo(),
'modifier_token' => $this->getModifierToken(),
'created_at' => $this->getCreatedAtAsString(),
];
}
public function getModifierToken(): ?string {
return $this->modifierToken;
}
public function setModifierToken( string $modifierToken ): self {
$this->modifierToken = $modifierToken;
return $this;
}
public function getId(): ?int {
return $this->id;
@ -81,16 +127,6 @@ class Owner {
return $this;
}
public function getPseudo(): ?string {
return $this->pseudo;
}
public function setPseudo( string $pseudo ): self {
$this->pseudo = $pseudo;
return $this;
}
/**
* @return Collection|Poll[]
*/
@ -175,16 +211,6 @@ class Owner {
return $this;
}
public function getModifierToken(): ?string {
return $this->modifierToken;
}
public function setModifierToken( string $modifierToken ): self {
$this->modifierToken = $modifierToken;
return $this;
}
public function addComment( Comment $comment ): self {
if ( ! $this->comments->contains( $comment ) ) {
$this->comments[] = $comment;
@ -206,16 +232,6 @@ class Owner {
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface {
return $this->createdAt;
}
public function setCreatedAt( \DateTimeInterface $createdAt ): self {
$this->createdAt = $createdAt;
return $this;
}
public function getRequestedPollsDate() {
return $this->requestedPollsDate;
}

View File

@ -2,10 +2,14 @@
namespace App\Entity;
use App\Traits\RandomTrait;
use App\Traits\TimeStampableTrait;
use DateTime;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use ErrorException;
use JMS\Serializer\Annotation as Serializer;
/**
@ -13,6 +17,11 @@ namespace App\Entity;
* @Serializer\ExclusionPolicy("all")
*/
class Poll {
use RandomTrait;
use TimeStampableTrait;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
@ -40,11 +49,7 @@ class Poll {
* @Serializer\Type("string")
*/
public $description;
/**
* @ORM\Column(type="datetime" , options={"default"="CURRENT_TIMESTAMP"})
* @Serializer\Expose()
*/
public $creationDate;
/**
* @ORM\Column(type="datetime")
* @Serializer\Expose()
@ -168,6 +173,12 @@ class Poll {
* @Serializer\Type("ArrayCollection<App\Entity\Comment>")
*/
public $comments;
/**
* number of days from now for default expiracy date
* @var int
* @Serializer\Expose()
*/
public $defaultExpiracyDaysFromNow = 60;
/**
* vote restricted by a password in md5 format
* @ORM\Column(type="string", length=255, nullable=true)
@ -179,30 +190,97 @@ class Poll {
* @Serializer\Type("string")
*/
private $adminKey;
/**
* number of days from now for default expiracy date
* @var int
* @Serializer\Expose()
*/
public $defaultExpiracyDaysFromNow = 60;
private $maxChoicesLimit = 25;
public function computeAnswers() {
public function __construct() {
$this->initiate();
$this->setCreatedAt( new DateTime() );
$this->votes = new ArrayCollection();
$this->stacksOfVotes = new ArrayCollection();
$this->choices = new ArrayCollection();
$this->comments = new ArrayCollection();
}
private function initiate() {
$this->votes = new ArrayCollection();
$this->stacksOfVotes = new ArrayCollection();
$this->choices = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->setAdminKey( $this->generateRandomKey() );
$this->setCreatedAt( new DateTime() );
$this->setExpiracyDate( $this->addDaysToDate(
new DateTime(),
$this->defaultExpiracyDaysFromNow
) );
$this->setAllowedAnswers( [ 'yes', 'maybe', 'no' ] );
}
public function setCreatedAt( DateTimeInterface $createdAt ): self {
$this->createdAt = $createdAt;
return $this;
}
public function displayForAdmin() {
$content = $this->display();
$content[ 'owner' ] = $this->getOwner()->displayForAdmin();
$content[ 'admin_key' ] = $this->getAdminKey();
$content[ 'password_hash' ] = $this->getPassword();
$content[ 'id' ] = $this->getId();
return $content;
}
// counts each number of answer for this choice
public function display() {
$computedAnswers = $this->computeAnswers();
$displayedStackOfVotes = [];
foreach ( $this->getStacksOfVotes() as $stack ) {
$displayedStackOfVotes[] = $stack->display();
}
$displayedChoices = [];
foreach ( $this->getChoices() as $choice ) {
$displayedChoices[] = $choice->display();
}
$displayedComments = [];
foreach ( $this->getComments() as $comment ) {
$displayedComments[] = $comment->display();
}
return [
'title' => $this->getTitle(),
'description' => $this->getDescription(),
'created_at' => $this->getCreatedAt()->format( 'c' ),
'expiracy_date' => $this->getExpiracyDate()->format( 'c' ),
'votes_max' => $this->getVotesMax(),
'choices_max' => $this->getChoicesMax(),
'kind' => $this->getKind(),
'allowed_answers' => $this->getAllowedAnswers(),
'votes_allowed' => $this->getVotesAllowed(),
'modification_policy' => $this->getModificationPolicy(),
'hide_results' => $this->getHideResults(),
'show_results_even_if_password' => $this->getShowResultEvenIfPasswords(),
'owner' => [
'pseudo' => $this->getOwner()->getPseudo(),
]
,
'password_protected' => $this->getPassword() ? 'yes' : 'no',
'max_score' => $computedAnswers[ 'max_score' ],
'choices' => $computedAnswers[ 'answers' ],
'stacks' => $displayedStackOfVotes,
'comments' => $displayedComments,
];
}
public function computeAnswers() {
$computedArray = [];
$maxScore = 0;
foreach ( $this->getStacksOfVotes() as $stack_of_vote ) {
foreach ( $stack_of_vote->getVotes() as $vote ) {
$answer = $vote->getValue();
$choice_id = $vote->getChoice()->getId();
if ( ! isset( $computedArray[ $choice_id ] ) ) {
$computedArray[ $choice_id ] = [
'choice_id' => $choice_id,
'choice_text' => $vote->getChoice()->getName(),
'id' => $vote->getId(),
$scoreInfos = [
'score' => 0,
'yes' => [
'count' => 0,
@ -217,6 +295,25 @@ class Poll {
'people' => [],
],
];
// first, prefill all choices
foreach ( $this->getChoices() as $choice ) {
$computedArray[ $choice->getId() ] = array_merge( $scoreInfos, $choice->display( $this->getKind() ) );
}
// then, compute stack of votes scores on each choice
foreach ( $this->getStacksOfVotes() as $stack_of_vote ) {
foreach ( $stack_of_vote->getVotes() as $vote ) {
$answer = $vote->getValue();
$choice_id = $vote->getChoice()->getId();
$choice_url = $vote->getChoice()->getUrl();
if ( ! isset( $computedArray[ $choice_id ] ) ) {
$computedArray[ $choice_id ] = [
'id' => $choice_id,
'url' => $choice_url,
'name' => $vote->getChoice()->getName(),
];
}
$computedArray[ $choice_id ][ $answer ][ 'count' ] ++;
$computedArray[ $choice_id ][ $answer ][ 'people' ][] = $stack_of_vote->getOwner()->getPseudo();
@ -232,106 +329,49 @@ class Poll {
}
}
}
$answersWithStats = [];
foreach ( $computedArray as $choice_stat ) {
$answersWithStats[] = $choice_stat;
}
return [
'counts' => $computedArray,
'maxScore' => $maxScore,
'answers' => $answersWithStats,
'max_score' => $maxScore,
];
}
public function display() {
return [
'config' => $this,
'password_protected' => $this->getPassword() ? 'yes' : 'no',
'answers' => $this->computeAnswers(),
];
}
public function __construct() {
$this->initiate();
}
private function initiate() {
$this->votes = new ArrayCollection();
$this->stacksOfVotes = new ArrayCollection();
$this->choices = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->setAdminKey( $this->generateAdminKey() );
$this->setCreationDate( new \DateTime() );
$this->setExpiracyDate( $this->addDaysToDate(
new \DateTime(),
$this->defaultExpiracyDaysFromNow
) );
$this->setAllowedAnswers( [ 'yes', 'maybe', 'no' ] );
}
public function generateAdminKey() {
$rand = random_int( PHP_INT_MIN, PHP_INT_MAX );
return str_shuffle( md5( $rand ) . $rand . $this->random_str() );
}
/**
* Generate a random string, using a cryptographically secure
* pseudorandom number generator (random_int)
*
* This function uses type hints now (PHP 7+ only), but it was originally
* written for PHP 5 as well.
*
* For PHP 7, random_int is a PHP core function
* For PHP 5.x, depends on https://github.com/paragonie/random_compat
*
* @param int $length How many characters do we want?
* @param string $keyspace A string of all possible characters
* to select from
*
* @return string
* @return Collection|Choice[]
*/
public function random_str(
int $length = 64,
string $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
): string {
if ( $length < 1 ) {
throw new \RangeException( "Length must be a positive integer" );
}
$pieces = [];
$max = mb_strlen( $keyspace, '8bit' ) - 1;
for ( $i = 0 ; $i < $length ; ++ $i ) {
$pieces [] = $keyspace[ random_int( 0, $max ) ];
public function getChoices(): Collection {
return $this->choices;
}
return implode( '', $pieces );
public function getKind(): ?string {
return $this->kind;
}
public function findChoiceById( int $id ) {
public function setKind( string $kind ): self {
$this->kind = $kind;
$choices = $this->getChoices();
$counter = 0;
// there must be something cleaner than this in Doctrine ArrayCollection
foreach ( $choices as $choice ) {
$counter ++;
if ( $counter > $this->maxChoicesLimit ) {
throw new \ErrorException( "max number of choices reached for this poll" );
}
if ( $choice && $choice->getId() == $id ) {
return $choice;
return $this;
}
public function getStacksOfVotes() {
return $this->stacksOfVotes;
}
return null;
public function setStacksOfVotes( ?StackOfVotes $stacksOfVotes ): self {
$this->stacksOfVotes = $stacksOfVotes;
return $this;
}
public function addDaysToDate( \DateTime $date, int $days ) {
$st = strtotime( $date->getTimestamp() );
return new \DateTime( $st );
}
public function getId(): ?int {
return $this->id;
/**
* @return Collection|Comment[]
*/
public function getComments(): Collection {
return $this->comments;
}
public function getTitle(): ?string {
@ -344,50 +384,6 @@ class Poll {
return $this;
}
public function getCreationDate(): ?DateTimeInterface {
return $this->creationDate;
}
public function setCreationDate( DateTimeInterface $creationDate ): self {
$this->creationDate = $creationDate;
return $this;
}
public function setExpiracyDate( DateTimeInterface $expiracyDate ): self {
$this->expiracyDate = $expiracyDate;
return $this;
}
public function getOwner(): ?Owner {
return $this->owner;
}
public function setOwner( ?Owner $owner ): self {
$this->owner = $owner;
return $this;
}
/**
* @return Collection|Vote[]
*/
public function getVotes(): Collection {
return $this->votes;
}
public function getAdminKey(): ?string {
return $this->adminKey;
}
public function setAdminKey( string $adminKey ): self {
$this->adminKey = $adminKey;
return $this;
}
public function getDescription(): ?string {
return $this->description;
}
@ -398,32 +394,56 @@ class Poll {
return $this;
}
public function getKind(): ?string {
return $this->kind;
public function getCreatedAt(): ?DateTimeInterface {
return $this->createdAt;
}
public function setKind( string $kind ): self {
$this->kind = $kind;
public function getExpiracyDate(): ?DateTimeInterface {
return $this->expiracyDate;
}
public function setExpiracyDate( DateTimeInterface $expiracyDate ): self {
$this->expiracyDate = $expiracyDate;
return $this;
}
public function getCustomUrl(): ?string {
return $this->customUrl;
public function getVotesMax() {
return $this->votesMax;
}
public function setCustomUrl( string $customUrl ): self {
$this->customUrl = $customUrl;
public function setVotesMax( $votesMax ): self {
$this->votesMax = $votesMax;
return $this;
}
public function getPassword(): ?string {
return $this->password;
public function getChoicesMax() {
return $this->choicesMax;
}
public function setPassword( string $password ): self {
$this->password = md5( $password );
public function setChoicesMax( $choicesMax ): self {
$this->choicesMax = $choicesMax;
return $this;
}
public function getAllowedAnswers(): ?array {
return $this->allowedAnswers;
}
public function setAllowedAnswers( array $allowedAnswers ): self {
$this->allowedAnswers = $allowedAnswers;
return $this;
}
public function getVotesAllowed(): ?bool {
return $this->votesAllowed;
}
public function setVotesAllowed( ?bool $votesAllowed ): self {
$this->votesAllowed = $votesAllowed;
return $this;
}
@ -438,26 +458,6 @@ class Poll {
return $this;
}
public function getMailOnComment(): ?bool {
return $this->mailOnComment;
}
public function setMailOnComment( bool $mailOnComment ): self {
$this->mailOnComment = $mailOnComment;
return $this;
}
public function getMailOnVote(): ?bool {
return $this->mailOnVote;
}
public function setMailOnVote( bool $mailOnVote ): self {
$this->mailOnVote = $mailOnVote;
return $this;
}
public function getHideResults(): ?bool {
return $this->hideResults;
}
@ -478,12 +478,94 @@ class Poll {
return $this;
}
public function getOwner(): ?Owner {
return $this->owner;
}
public function setOwner( ?Owner $owner ): self {
$this->owner = $owner;
return $this;
}
public function getPassword(): ?string {
return $this->password;
}
public function setPassword( string $password ): self {
$this->password = md5( $password );
return $this;
}
public function getAdminKey(): ?string {
return $this->adminKey;
}
public function setAdminKey( string $adminKey ): self {
$this->adminKey = $adminKey;
return $this;
}
public function getId(): ?int {
return $this->id;
}
public function findChoiceById( int $id ) {
$choices = $this->getChoices();
$counter = 0;
// there must be something cleaner than this in Doctrine ArrayCollection
foreach ( $choices as $choice ) {
$counter ++;
if ( $counter > $this->maxChoicesLimit ) {
throw new ErrorException( "max number of choices reached for this poll" );
}
if ( $choice && $choice->getId() == $id ) {
return $choice;
}
}
return null;
}
/**
* @return Collection|Comment[]
* @return Collection|Vote[]
*/
public function getComments(): Collection {
return $this->comments;
public function getVotes(): Collection {
return $this->votes;
}
public function getCustomUrl(): ?string {
return $this->customUrl;
}
public function setCustomUrl( string $customUrl ): self {
$this->customUrl = $customUrl;
return $this;
}
public function getMailOnComment(): ?bool {
return $this->mailOnComment;
}
public function setMailOnComment( bool $mailOnComment ): self {
$this->mailOnComment = $mailOnComment;
return $this;
}
public function getMailOnVote(): ?bool {
return $this->mailOnVote;
}
public function setMailOnVote( bool $mailOnVote ): self {
$this->mailOnVote = $mailOnVote;
return $this;
}
public function addComment( Comment $comment ): self {
@ -507,17 +589,6 @@ class Poll {
return $this;
}
public function getStacksOfVotes() {
return $this->stacksOfVotes;
}
public function setStacksOfVotes( ?StackOfVotes $stacksOfVotes ): self {
$this->stacksOfVotes = $stacksOfVotes;
return $this;
}
public function addStackOfVote( StackOfVotes $stackOfVote ): self {
if ( ! $this->stacksOfVotes->contains( $stackOfVote ) ) {
$this->stacksOfVotes[] = $stackOfVote;
@ -539,10 +610,6 @@ class Poll {
return $this;
}
public function getExpiracyDate(): ?\DateTimeInterface {
return $this->expiracyDate;
}
public function addVote( Vote $vote ): self {
if ( ! $this->votes->contains( $vote ) ) {
$this->votes[] = $vote;
@ -564,13 +631,6 @@ class Poll {
return $this;
}
/**
* @return Collection|Choice[]
*/
public function getChoices(): Collection {
return $this->choices;
}
public function addTextChoiceArray( array $choiceTextArray ): self {
foreach ( $choiceTextArray as $text ) {
$newChoice = new Choice();
@ -581,13 +641,17 @@ class Poll {
return $this;
}
public function getAllowedAnswers(): ?array {
return $this->allowedAnswers;
public function addChoice( Choice $choice ): self {
if ( ! is_null( $this->choices ) ) {
if ( ! $this->choices->contains( $choice ) ) {
$this->choices[] = $choice;
$choice->setPoll( $this );
}
} else {
$this->choices[] = $choice;
$choice->setPoll( $this );
}
public function setAllowedAnswers( array $allowedAnswers ): self {
$this->allowedAnswers = $allowedAnswers;
return $this;
}
@ -613,21 +677,6 @@ class Poll {
return $this;
}
public function addChoice( Choice $choice ): self {
if ( ! is_null( $this->choices ) ) {
if ( ! $this->choices->contains( $choice ) ) {
$this->choices[] = $choice;
$choice->setPoll( $this );
}
} else {
$this->choices[] = $choice;
$choice->setPoll( $this );
}
return $this;
}
public function removeChoice( Choice $choice ): self {
if ( $this->choices->contains( $choice ) ) {
$this->choices->removeElement( $choice );
@ -640,36 +689,6 @@ class Poll {
return $this;
}
public function getVotesAllowed(): ?bool {
return $this->votesAllowed;
}
public function setVotesAllowed( ?bool $votesAllowed ): self {
$this->votesAllowed = $votesAllowed;
return $this;
}
public function getVotesMax() {
return $this->votesMax;
}
public function setVotesMax( $votesMax ): self {
$this->votesMax = $votesMax;
return $this;
}
public function getChoicesMax() {
return $this->choicesMax;
}
public function setChoicesMax( $choicesMax ): self {
$this->choicesMax = $choicesMax;
return $this;
}
public function getCommentsAllowed(): ?bool {
return $this->commentsAllowed;
}

View File

@ -2,6 +2,8 @@
namespace App\Entity;
use App\Traits\TimeStampableTrait;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
@ -14,12 +16,9 @@ use JMS\Serializer\Annotation as Serializer;
* @Serializer\ExclusionPolicy("all")
*/
class StackOfVotes {
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
use TimeStampableTrait;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Type("string")
@ -31,6 +30,12 @@ class StackOfVotes {
* @Serializer\Expose()
*/
public $votes;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Poll", inversedBy="stacksOfVotes", cascade={"persist"})
*/
@ -42,46 +47,39 @@ class StackOfVotes {
*/
private $owner;
public function display() {
$tab = [
'id' => $this->getId(),
// 'modifier_token' => $this->getOwner()->getModifierToken(),
'pseudo' => '',
'creation_date' => '',
'votes' => [],
];
// prefill votes with all choices ids
foreach ( $this->getPoll()->getChoices() as $choice ) {
$tab[ 'votes' ][ $choice->getId() ] = [
'choice_id' => $choice->getId(),
'name' => $choice->getName(),
];
}
foreach ( $this->getVotes() as $vote ) {
// $tab[ 'votes' ][ $vote->getChoice()->getId() ] = $vote->display();
$tab[ 'votes' ][ $vote->getChoice()->getId() ][ 'stack_id' ] = $this->getId();
$tab[ 'pseudo' ] = $this->getPseudo();
$tab[ 'creation_date' ] = $vote->getCreationDate();
}
return $tab;
}
/**
* @ORM\PrePersist
* @ORM\Column(type="string", length=255, nullable=true)
*/
public function prePersist() {
$this->setPseudo( $this->getOwner()->getPseudo() );
}
private $ip;
public function __construct() {
$this->setCreatedAt( new DateTime() );
$this->votes = new ArrayCollection();
}
public function getId(): ?int {
return $this->id;
public function display() {
$votes = $this->getVotes();
$tab = [
// 'id' => $this->getId(),
// 'modifier_token' => $this->getOwner()->getModifierToken(),
'pseudo' => $this->getPseudo(),
'created_at' => $this->getCreatedAtAsString(),
'votes' => [],
];
// prefill votes with all choices ids
// foreach ( $this->getPoll()->getChoices() as $choice ) {
// $tab[ 'votes' ][ $choice->getId() ] = [
// 'choice_id' => $choice->getId(),
// 'value' => null,
// ];
// }
foreach ( $votes as $vote ) {
$tab[ 'votes' ][ $vote->getChoice()->getId() ] = $vote->display();
}
return $tab;
}
/**
@ -91,6 +89,37 @@ class StackOfVotes {
return $this->votes;
}
public function getPseudo(): ?string {
return $this->pseudo;
}
public function setPseudo( ?string $pseudo ): self {
$this->pseudo = $pseudo;
return $this;
}
/**
* @ORM\PrePersist
*/
public function prePersist() {
$this->setPseudo( $this->getOwner()->getPseudo() );
}
public function getOwner(): ?Owner {
return $this->owner;
}
public function setOwner( ?Owner $owner ): self {
$this->owner = $owner;
return $this;
}
public function getId(): ?int {
return $this->id;
}
public function addVote( Vote $vote ): self {
if ( ! $this->votes->contains( $vote ) ) {
$vote->setPoll( $this->getPoll() );
@ -102,6 +131,16 @@ class StackOfVotes {
return $this;
}
public function getPoll(): ?Poll {
return $this->poll;
}
public function setPoll( ?Poll $poll ): self {
$this->poll = $poll;
return $this;
}
public function removeVote( Vote $vote ): self {
if ( $this->votes->contains( $vote ) ) {
$this->votes->removeElement( $vote );
@ -114,32 +153,12 @@ class StackOfVotes {
return $this;
}
public function getPseudo(): ?string {
return $this->pseudo;
public function getIp(): ?string {
return $this->ip;
}
public function setPseudo( ?string $pseudo ): self {
$this->pseudo = $pseudo;
return $this;
}
public function getOwner(): ?Owner {
return $this->owner;
}
public function setOwner( ?Owner $owner ): self {
$this->owner = $owner;
return $this;
}
public function getPoll(): ?Poll {
return $this->poll;
}
public function setPoll( ?Poll $poll ): self {
$this->poll = $poll;
public function setIp( string $ip ): self {
$this->ip = $ip;
return $this;
}

View File

@ -2,6 +2,8 @@
namespace App\Entity;
use App\Traits\TimeStampableTrait;
use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
@ -11,6 +13,9 @@
* @Serializer\ExclusionPolicy("all")
*/
class Vote {
use TimeStampableTrait;
/**
* for a text kind of choice: could be "yes" "no" "maybe" and empty.
* for a date kind, the choice linked is equivalent to the value selected
@ -19,12 +24,7 @@
* @Serializer\Expose()
*/
public $value;
/**
* @ORM\Column(type="datetime" , options={"default"="CURRENT_TIMESTAMP"})
* @Serializer\Type("datetime")
* @Serializer\Expose()
*/
public $creationDate;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Choice", inversedBy="votes", cascade={"persist"})
* @ORM\JoinColumn(nullable=false)
@ -52,7 +52,16 @@
*/
private $stacksOfVotes;
public function __construct() {
$this->setCreatedAt( new DateTime() );
}
public function display() {
$value = $this->getValue();
if ( ! $value ) {
return null;
} else {
return [
'id' => $this->getId(),
'value' => $this->getValue(),
@ -60,28 +69,22 @@
'text' => $this->getChoice()->getName(),
];
}
}
public function __construct() {
$this->setCreationDate( new \DateTime() );
public function getValue(): ?string {
return $this->value;
}
public function setValue( ?string $value ): self {
$this->value = $value;
return $this;
}
public function getId(): ?int {
return $this->id;
}
public function getPoll(): ?Poll {
return $this->poll;
}
public function setPoll( ?Poll $poll ): self {
$this->poll = $poll;
if ( $poll ) {
$poll->addVote( $this );
}
return $this;
}
public function getChoice(): ?Choice {
return $this->choice;
}
@ -92,12 +95,15 @@
return $this;
}
public function getValue(): ?string {
return $this->value;
public function getPoll(): ?Poll {
return $this->poll;
}
public function setValue( ?string $value ): self {
$this->value = $value;
public function setPoll( ?Poll $poll ): self {
$this->poll = $poll;
if ( $poll ) {
$poll->addVote( $this );
}
return $this;
}

View File

@ -1,12 +0,0 @@
<?php
namespace App\Traits;
use Doctrine\ORM\Mapping as ORM;
trait Timed {
/**
* @ORM\Column(type="datetime" , options={"default"="CURRENT_TIMESTAMP"})
*/
private $createdAt;
}

View File

@ -7,15 +7,13 @@ use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PollType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
class PollType extends AbstractType {
public function buildForm( FormBuilderInterface $builder, array $options ) {
$builder
->add( 'title' )
->add( 'customUrl' )
->add( 'description' )
->add('creationDate')
->add( 'createdAt' )
->add( 'expiracyDate' )
->add( 'kind' )
->add( 'allowedAnswers' )
@ -26,12 +24,10 @@ class PollType extends AbstractType
->add( 'showResultEvenIfPasswords' )
->add( 'password' )
->add( 'adminKey' )
->add('owner')
;
->add( 'owner' );
}
public function configureOptions(OptionsResolver $resolver)
{
public function configureOptions( OptionsResolver $resolver ) {
$resolver->setDefaults( [
'data_class' => Poll::class,
] );

View File

@ -6,32 +6,30 @@ use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
use function dirname;
class Kernel extends BaseKernel
{
class Kernel extends BaseKernel {
use MicroKernelTrait;
protected function configureContainer(ContainerConfigurator $container): void
{
protected function configureContainer( ContainerConfigurator $container ): void {
$container->import( '../config/{packages}/*.yaml' );
$container->import( '../config/{packages}/' . $this->environment . '/*.yaml' );
if (is_file(\dirname(__DIR__).'/config/services.yaml')) {
if ( is_file( dirname( __DIR__ ) . '/config/services.yaml' ) ) {
$container->import( '../config/services.yaml' );
$container->import( '../config/{services}_' . $this->environment . '.yaml' );
} elseif (is_file($path = \dirname(__DIR__).'/config/services.php')) {
} elseif ( is_file( $path = dirname( __DIR__ ) . '/config/services.php' ) ) {
( require $path )( $container->withPath( $path ), $this );
}
}
protected function configureRoutes(RoutingConfigurator $routes): void
{
protected function configureRoutes( RoutingConfigurator $routes ): void {
$routes->import( '../config/{routes}/' . $this->environment . '/*.yaml' );
$routes->import( '../config/{routes}/*.yaml' );
if (is_file(\dirname(__DIR__).'/config/routes.yaml')) {
if ( is_file( dirname( __DIR__ ) . '/config/routes.yaml' ) ) {
$routes->import( '../config/routes.yaml' );
} elseif (is_file($path = \dirname(__DIR__).'/config/routes.php')) {
} elseif ( is_file( $path = dirname( __DIR__ ) . '/config/routes.php' ) ) {
( require $path )( $routes->withPath( $path ), $this );
}
}

View File

@ -12,10 +12,8 @@ use Doctrine\Common\Persistence\ManagerRegistry;
* @method Choice[] findAll()
* @method Choice[] findBy( array $criteria, array $orderBy = null, $limit = null, $offset = null )
*/
class ChoiceRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
class ChoiceRepository extends ServiceEntityRepository {
public function __construct( ManagerRegistry $registry ) {
parent::__construct( $registry, Choice::class );
}

View File

@ -12,10 +12,8 @@ use Doctrine\Common\Persistence\ManagerRegistry;
* @method Comment[] findAll()
* @method Comment[] findBy( array $criteria, array $orderBy = null, $limit = null, $offset = null )
*/
class CommentRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
class CommentRepository extends ServiceEntityRepository {
public function __construct( ManagerRegistry $registry ) {
parent::__construct( $registry, Comment::class );
}

View File

@ -12,10 +12,8 @@ use Doctrine\Common\Persistence\ManagerRegistry;
* @method Owner[] findAll()
* @method Owner[] findBy( array $criteria, array $orderBy = null, $limit = null, $offset = null )
*/
class OwnerRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
class OwnerRepository extends ServiceEntityRepository {
public function __construct( ManagerRegistry $registry ) {
parent::__construct( $registry, Owner::class );
}

View File

@ -5,7 +5,6 @@ namespace App\Repository;
use App\Entity\Poll;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use MongoDB\Driver\Manager;
/**
* @method Poll|null find( $id, $lockMode = null, $lockVersion = null )
@ -13,11 +12,10 @@ use MongoDB\Driver\Manager;
* @method Poll[] findAll()
* @method Poll[] findBy( array $criteria, array $orderBy = null, $limit = null, $offset = null )
*/
class PollRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry
$registry)
{
class PollRepository extends ServiceEntityRepository {
public function __construct(
ManagerRegistry $registry
) {
parent::__construct( $registry, Poll::class );
}

View File

@ -12,10 +12,8 @@ use Doctrine\Common\Persistence\ManagerRegistry;
* @method StackOfVotes[] findAll()
* @method StackOfVotes[] findBy( array $criteria, array $orderBy = null, $limit = null, $offset = null )
*/
class StackOfVotesRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
class StackOfVotesRepository extends ServiceEntityRepository {
public function __construct( ManagerRegistry $registry ) {
parent::__construct( $registry, StackOfVotes::class );
}

View File

@ -12,10 +12,8 @@ use Doctrine\Common\Persistence\ManagerRegistry;
* @method Vote[] findAll()
* @method Vote[] findBy( array $criteria, array $orderBy = null, $limit = null, $offset = null )
*/
class VoteRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
class VoteRepository extends ServiceEntityRepository {
public function __construct( ManagerRegistry $registry ) {
parent::__construct( $registry, Vote::class );
}

View File

@ -12,6 +12,7 @@ use Exception;
use Swift_Mailer;
use Swift_Message;
use Swift_SmtpTransport;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
class MailService {
@ -49,7 +50,7 @@ class MailService {
* @param Owner $foundOwner
* @param Poll|null $poll
*
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
* @throws TransportExceptionInterface
*/
public function sendCreationMailAction( Owner $foundOwner, Poll $poll = null ) {
@ -66,74 +67,12 @@ class MailService {
return $this->sendMailWithVars( $config );
}
/**
* anti spam , limit to every minute TODO
*
* @param Owner $owner
*
* @return bool
*/
public function antispamCheck( Owner $owner ) {
// $lastSend = $admin_user->getRequestedPollsDate();
// $now = new \DateTime();
// if ( date_diff( $lastSend, $now ) < 60 ) {
// // too soon!
// die( 'too soon!' );
// }
// $admin_user->setRequestedPollsDate( $now );
// $em->persist( $admin_user );
// $em->flush();
return true;
}
/**
* send created polls to an owner
*
* @param Owner $owner
*
* @return int|void
* @throws Exception
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
*/
public function sendOwnerPollsAction( Owner $owner ) {
$config = [
'owner' => $owner,
'title' => 'Framadate | Mes sondages',
'email_template' => 'emails/owner-list.html.twig',
];
$this->sendMailWithVars( $config );
return 1;
}
/**
* @param Comment $comment
*
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
*/
public function sendCommentNotification( Comment $comment ) {
$config = [
'comment' => $comment,
'owner' => $comment->getOwner(),
'title' => 'Framadate | commentaire de ' . $comment->getOwner()->getPseudo() . ' _ sondage ' . $comment->getPoll()->getTitle(),
'email_template' => 'emails/comment-notification.html.twig',
];
$this->sendMailWithVars( $config );
}
/**
* generic way to send email with html template
*
* @param $config
*
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
* @throws TransportExceptionInterface
*/
public function sendMailWithVars( $config ) {
@ -209,4 +148,64 @@ class MailService {
}
/**
* anti spam , limit to every minute TODO
*
* @param Owner $owner
*
* @return bool
*/
public function antispamCheck( Owner $owner ) {
// $lastSend = $admin_user->getRequestedPollsDate();
// $now = new \DateTime();
// if ( date_diff( $lastSend, $now ) < 60 ) {
// // too soon!
// die( 'too soon!' );
// }
// $admin_user->setRequestedPollsDate( $now );
// $em->persist( $admin_user );
// $em->flush();
return true;
}
/**
* send created polls to an owner
*
* @param Owner $owner
*
* @return int|void
* @throws Exception
* @throws TransportExceptionInterface
*/
public function sendOwnerPollsAction( Owner $owner ) {
$config = [
'owner' => $owner,
'title' => 'Framadate | Mes sondages',
'email_template' => 'emails/owner-list.html.twig',
];
$this->sendMailWithVars( $config );
return 1;
}
/**
* @param Comment $comment
*
* @throws TransportExceptionInterface
*/
public function sendCommentNotification( Comment $comment ) {
$config = [
'comment' => $comment,
'owner' => $comment->getOwner(),
'title' => 'Framadate | commentaire de ' . $comment->getOwner()->getPseudo() . ' _ sondage ' . $comment->getPoll()->getTitle(),
'email_template' => 'emails/comment-notification.html.twig',
];
$this->sendMailWithVars( $config );
}
}

45
src/Traits/RandomTrait.php Executable file
View File

@ -0,0 +1,45 @@
<?php
namespace App\Traits;
trait RandomTrait {
public function generateRandomKey() {
$rand = random_int( PHP_INT_MIN, PHP_INT_MAX );
return str_shuffle( md5( $rand ) . $rand . $this->random_str() );
}
/**
* Generate a random string, using a cryptographically secure
* pseudorandom number generator (random_int)
*
* This function uses type hints now (PHP 7+ only), but it was originally
* written for PHP 5 as well.
*
* For PHP 7, random_int is a PHP core function
* For PHP 5.x, depends on https://github.com/paragonie/random_compat
*
* @param int $length How many characters do we want?
* @param string $keyspace A string of all possible characters
* to select from
*
* @return string
*/
public function random_str(
int $length = 64,
string $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
): string {
if ( $length < 1 ) {
throw new RangeException( "Length must be a positive integer" );
}
$pieces = [];
$max = mb_strlen( $keyspace, '8bit' ) - 1;
for ( $i = 0 ; $i < $length ; ++ $i ) {
$pieces [] = $keyspace[ random_int( 0, $max ) ];
}
return implode( '', $pieces );
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace App\Traits;
use DateInterval;
use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
trait TimeStampableTrait {
/**
* @ORM\Column(type="datetime" , options={"default"="CURRENT_TIMESTAMP"})
*/
private $createdAt;
public function getCreatedAt(): ?DateTimeInterface {
return $this->createdAt;
}
public function setCreatedAt( DateTimeInterface $createdAt ): self {
$this->createdAt = $createdAt;
return $this;
}
public function getCreatedAtAsString(): string {
return $this->createdAt->format( 'c' );
}
public function addDaysToDate( DateTime $date, int $days ) {
return $date->add( new DateInterval( 'P' . $days . 'D' ) );
}
}

View File

@ -85,6 +85,8 @@ fi
git config --global diff.submodule log
git submodule update
git submodule foreach git reset --hard && git checkout master && git pull
cecho g "######################"
cecho g " check dependencies of the frontend with yarn "
cecho g "######################"
@ -137,6 +139,9 @@ mv polyfills-es5* es5-polyfills.js
mv polyfills* other-polyfills.js
mv scripts* scripts.js
mv styles* styles.css
chown www-data:www-data . -R
cecho b " finished at ------- $(date) ------- "
cecho g "##################################################################"
cecho g " "