From d286d6291ec0ef44fe2cb1f62d52b7634158c1a9 Mon Sep 17 00:00:00 2001 From: tykayn <15d65f2f-0b14-4f70-bf34-e130180ed62b@users.tedomum.net> Date: Mon, 26 Oct 2020 11:39:04 +0100 Subject: [PATCH] head and foot --- .env | 4 + .env.production | 33 - .gitignore | 7 + assets/app.js | 14 + assets/styles/app.scss | 3 + assets/styles/pages/_global.scss | 3 + assets/styles/pages/_home.scss | 0 assets/styles/pages/libs.scss | 2 + composer.json | 2 + composer.lock | 170 +- config/bundles.php | 1 + config/packages/assets.yaml | 3 + config/packages/prod/webpack_encore.yaml | 4 + config/packages/test/webpack_encore.yaml | 2 + config/packages/twig.yaml | 2 + config/packages/webpack_encore.yaml | 25 + package.json | 40 + src/Controller/DefaultController.php | 16 +- src/Controller/PollController.php | 482 +- src/Controller/api/PollController.php | 412 + src/Form/PollType.php | 39 + symfony.lock | 27 +- templates/base.html.twig | 21 +- templates/pages/home.html.twig | 10 + templates/poll/_delete_form.html.twig | 5 + templates/poll/_form.html.twig | 4 + templates/poll/edit.html.twig | 13 + templates/poll/index.html.twig | 61 + templates/poll/new.html.twig | 11 + templates/poll/show.html.twig | 78 + templates/split/footer.html.twig | 14 + templates/split/header.html.twig | 20 + {public/assets/i18n => translations}/en.json | 0 {public/assets/i18n => translations}/fr.json | 0 webpack.config.js | 74 + yarn.lock | 7343 ++++++++++++++++++ 36 files changed, 8507 insertions(+), 438 deletions(-) delete mode 100755 .env.production create mode 100644 assets/app.js create mode 100644 assets/styles/app.scss create mode 100644 assets/styles/pages/_global.scss create mode 100644 assets/styles/pages/_home.scss create mode 100644 assets/styles/pages/libs.scss create mode 100644 config/packages/assets.yaml create mode 100644 config/packages/prod/webpack_encore.yaml create mode 100644 config/packages/test/webpack_encore.yaml create mode 100644 config/packages/webpack_encore.yaml create mode 100644 package.json create mode 100644 src/Controller/api/PollController.php create mode 100644 src/Form/PollType.php create mode 100644 templates/pages/home.html.twig create mode 100644 templates/poll/_delete_form.html.twig create mode 100644 templates/poll/_form.html.twig create mode 100644 templates/poll/edit.html.twig create mode 100644 templates/poll/index.html.twig create mode 100644 templates/poll/new.html.twig create mode 100644 templates/poll/show.html.twig create mode 100644 templates/split/footer.html.twig create mode 100644 templates/split/header.html.twig rename {public/assets/i18n => translations}/en.json (100%) rename {public/assets/i18n => translations}/fr.json (100%) create mode 100644 webpack.config.js diff --git a/.env b/.env index ba017c1..29eaad1 100755 --- a/.env +++ b/.env @@ -19,6 +19,8 @@ ADMIN_TOKEN=CHANGE_THIS_STRING_HERE APP_SECRET=CHANGE_THIS_STRING_THERE # Base website url, should contain https:// and having no trailing slash. example: BASE_URL=https://framadate.org BASE_URL=https://YOUR_WEBSITE +WEBSITE_NAME=Framadate_2 +WEBSITE_LOGO=logo.svg #TRUSTED_PROXIES=127.0.0.1,127.0.0.2 #TRUSTED_HOSTS='^localhost|example\.com$' ###< symfony/framework-bundle ### @@ -47,3 +49,5 @@ SUPPORT_EMAIL=YOUR_EMAIL ###> symfony/mailer ### MAILER_DSN=smtp://localhost ###< symfony/mailer ### + +DATABASE_URL=mysql://db_user:db_pass@127.0.0.1:5432/db_name diff --git a/.env.production b/.env.production deleted file mode 100755 index ff20db2..0000000 --- a/.env.production +++ /dev/null @@ -1,33 +0,0 @@ -# In all environments, the following files are loaded if they exist, -# the latter taking precedence over the former: -# -# * .env contains default values for the environment variables needed by the app -# * .env.local uncommitted file with local overrides -# * .env.$APP_ENV committed environment-specific defaults -# * .env.$APP_ENV.local uncommitted environment-specific overrides -# -# Real environment variables win over .env files. -# -# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. -# -# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). -# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration - -###> symfony/framework-bundle ### -APP_ENV=prod -APP_SECRET=597b0529ac702d27dcb9089f7e69c362 -# Base website url, should contain https:// and having no trailing slash. example: BASE_URL=https://framadate.org -BASE_URL=https://framadate-api.cipherbliss.com -#TRUSTED_PROXIES=127.0.0.1,127.0.0.2 -#TRUSTED_HOSTS='^localhost|example\.com$' -###< symfony/framework-bundle ### - -###> doctrine/doctrine-bundle ### -# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url -# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db" -# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11" -# IMPORTANT: You MUST also configure your db driver and server_version in config/packages/doctrine.yaml -# CHANGE THIS TO SUIT YOUR PRODUCTION ENV -DATABASE_URL=mysql://root:plopplop01@127.0.0.1:5432/symfony - -###< doctrine/doctrine-bundle ### diff --git a/.gitignore b/.gitignore index 2614f44..5db564d 100755 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,10 @@ public/styles.css ###< symfony/phpunit-bridge ### node_modules + +###> symfony/webpack-encore-bundle ### +/node_modules/ +/public/build/ +npm-debug.log +yarn-error.log +###< symfony/webpack-encore-bundle ### diff --git a/assets/app.js b/assets/app.js new file mode 100644 index 0000000..0481817 --- /dev/null +++ b/assets/app.js @@ -0,0 +1,14 @@ +/* + * Welcome to your app's main JavaScript file! + * + * We recommend including the built version of this JavaScript file + * (and its CSS file) in your base layout (base.html.twig). + */ + +// any CSS you import will output into a single css file (app.css in this case) +import './styles/app.scss'; + +// Need jQuery? Install it with "yarn add jquery", then uncomment to import it. +// import $ from 'jquery'; + +console.log('Hello Webpack Encore! Edit me in assets/app.js'); diff --git a/assets/styles/app.scss b/assets/styles/app.scss new file mode 100644 index 0000000..119312e --- /dev/null +++ b/assets/styles/app.scss @@ -0,0 +1,3 @@ +@import 'pages/libs'; +@import 'pages/global'; +@import 'pages/home'; diff --git a/assets/styles/pages/_global.scss b/assets/styles/pages/_global.scss new file mode 100644 index 0000000..b6f166e --- /dev/null +++ b/assets/styles/pages/_global.scss @@ -0,0 +1,3 @@ +body { + background-color: lightgray; +} diff --git a/assets/styles/pages/_home.scss b/assets/styles/pages/_home.scss new file mode 100644 index 0000000..e69de29 diff --git a/assets/styles/pages/libs.scss b/assets/styles/pages/libs.scss new file mode 100644 index 0000000..787b597 --- /dev/null +++ b/assets/styles/pages/libs.scss @@ -0,0 +1,2 @@ +@import '~font-awesome/css/font-awesome.min.css'; +@import '~tailwindcss/dist/tailwind.min.css'; diff --git a/composer.json b/composer.json index d20b6e4..910e74a 100755 --- a/composer.json +++ b/composer.json @@ -25,9 +25,11 @@ "symfony/mailer": "4.3.*", "symfony/maker-bundle": "^1.14", "symfony/orm-pack": "^1.0", + "symfony/security-csrf": "4.3.*", "symfony/swiftmailer-bundle": "^3.4", "symfony/twig-bundle": "4.3.*", "symfony/validator": "4.3.*", + "symfony/webpack-encore-bundle": "^1.7", "symfony/yaml": "4.3.*" }, "require-dev": { diff --git a/composer.lock b/composer.lock index b2f18a6..6864d8a 100755 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7c5bdef20c7e94f4117ae2b1ef35f486", + "content-hash": "437d493ff28b8ee8bba95a0ca2000d1c", "packages": [ { "name": "doctrine/annotations", @@ -3145,6 +3145,62 @@ ], "time": "2019-11-12T09:31:26+00:00" }, + { + "name": "symfony/asset", + "version": "v4.3.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/asset.git", + "reference": "5bdbd8878b69e3be16d036890ea3081172ea28c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/asset/zipball/5bdbd8878b69e3be16d036890ea3081172ea28c5", + "reference": "5bdbd8878b69e3be16d036890ea3081172ea28c5", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "require-dev": { + "symfony/http-foundation": "~3.4|~4.0", + "symfony/http-kernel": "~3.4|~4.0" + }, + "suggest": { + "symfony/http-foundation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Asset\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Asset Component", + "homepage": "https://symfony.com", + "time": "2020-01-04T12:24:57+00:00" + }, { "name": "symfony/cache", "version": "v4.3.10", @@ -5366,6 +5422,65 @@ "homepage": "https://symfony.com", "time": "2020-01-21T11:08:18+00:00" }, + { + "name": "symfony/security-csrf", + "version": "v4.3.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-csrf.git", + "reference": "9e435026ab45f073880d1fbe0e1b17e7df6bf642" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/9e435026ab45f073880d1fbe0e1b17e7df6bf642", + "reference": "9e435026ab45f073880d1fbe0e1b17e7df6bf642", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/security-core": "~3.4|~4.0" + }, + "conflict": { + "symfony/http-foundation": "<3.4" + }, + "require-dev": { + "symfony/http-foundation": "~3.4|~4.0" + }, + "suggest": { + "symfony/http-foundation": "For using the class SessionTokenStorage." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Csrf\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - CSRF Library", + "homepage": "https://symfony.com", + "time": "2020-01-04T12:24:57+00:00" + }, { "name": "symfony/service-contracts", "version": "v1.1.8", @@ -5927,6 +6042,59 @@ ], "time": "2020-01-01T11:51:43+00:00" }, + { + "name": "symfony/webpack-encore-bundle", + "version": "v1.7.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/webpack-encore-bundle.git", + "reference": "5c0f659eceae87271cce54bbdfb05ed8ec9007bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/webpack-encore-bundle/zipball/5c0f659eceae87271cce54bbdfb05ed8ec9007bd", + "reference": "5c0f659eceae87271cce54bbdfb05ed8ec9007bd", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/asset": "^3.4 || ^4.0 || ^5.0", + "symfony/config": "^3.4 || ^4.0 || ^5.0", + "symfony/dependency-injection": "^3.4 || ^4.0 || ^5.0", + "symfony/http-kernel": "^3.4 || ^4.0 || ^5.0", + "symfony/service-contracts": "^1.0 || ^2.0" + }, + "require-dev": { + "symfony/framework-bundle": "^3.4 || ^4.0 || ^5.0", + "symfony/phpunit-bridge": "^4.3.5 || ^5.0", + "symfony/twig-bundle": "^3.4 || ^4.0 || ^5.0", + "symfony/web-link": "^3.4 || ^4.0 || ^5.0" + }, + "type": "symfony-bundle", + "extra": { + "thanks": { + "name": "symfony/webpack-encore", + "url": "https://github.com/symfony/webpack-encore" + } + }, + "autoload": { + "psr-4": { + "Symfony\\WebpackEncoreBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Integration with your Symfony app & Webpack Encore!", + "time": "2020-01-31T15:31:59+00:00" + }, { "name": "symfony/yaml", "version": "v4.3.10", diff --git a/config/bundles.php b/config/bundles.php index b003748..860f005 100755 --- a/config/bundles.php +++ b/config/bundles.php @@ -15,4 +15,5 @@ return [ Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true], Liip\TestFixturesBundle\LiipTestFixturesBundle::class => ['test' => true], + Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], ]; diff --git a/config/packages/assets.yaml b/config/packages/assets.yaml new file mode 100644 index 0000000..051d36d --- /dev/null +++ b/config/packages/assets.yaml @@ -0,0 +1,3 @@ +framework: + assets: + json_manifest_path: '%kernel.project_dir%/public/build/manifest.json' diff --git a/config/packages/prod/webpack_encore.yaml b/config/packages/prod/webpack_encore.yaml new file mode 100644 index 0000000..d0b3ba8 --- /dev/null +++ b/config/packages/prod/webpack_encore.yaml @@ -0,0 +1,4 @@ +#webpack_encore: + # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes) + # Available in version 1.2 + #cache: true diff --git a/config/packages/test/webpack_encore.yaml b/config/packages/test/webpack_encore.yaml new file mode 100644 index 0000000..02a7651 --- /dev/null +++ b/config/packages/test/webpack_encore.yaml @@ -0,0 +1,2 @@ +#webpack_encore: +# strict_mode: false diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index a707167..535fbec 100755 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -4,3 +4,5 @@ twig: strict_variables: '%kernel.debug%' globals: BASE_URL: '%env(BASE_URL)%' + WEBSITE_NAME: '%env(WEBSITE_NAME)%' + WEBSITE_LOGO: '%env(WEBSITE_LOGO)%' diff --git a/config/packages/webpack_encore.yaml b/config/packages/webpack_encore.yaml new file mode 100644 index 0000000..9191f4f --- /dev/null +++ b/config/packages/webpack_encore.yaml @@ -0,0 +1,25 @@ +webpack_encore: + # The path where Encore is building the assets - i.e. Encore.setOutputPath() + output_path: '%kernel.project_dir%/public/build' + # If multiple builds are defined (as shown below), you can disable the default build: + # output_path: false + + # if using Encore.enableIntegrityHashes() and need the crossorigin attribute (default: false, or use 'anonymous' or 'use-credentials') + # crossorigin: 'anonymous' + + # preload all rendered script and link tags automatically via the http2 Link header + # preload: true + + # Throw an exception if the entrypoints.json file is missing or an entry is missing from the data + # strict_mode: false + + # if you have multiple builds: + # builds: + # pass "frontend" as the 3rg arg to the Twig functions + # {{ encore_entry_script_tags('entry1', null, 'frontend') }} + + # frontend: '%kernel.project_dir%/public/frontend/build' + + # Cache the entrypoints.json (rebuild Symfony's cache when entrypoints.json changes) + # Put in config/packages/prod/webpack_encore.yaml + # cache: true diff --git a/package.json b/package.json new file mode 100644 index 0000000..87e0964 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "date-poll-api", + "version": "1.0.0", + "description": "API date to make surveys, kind of the new Framadate", + "main": "index.js", + "directories": { + "doc": "doc", + "test": "tests" + }, + "dependencies": { + "@symfony/webpack-encore": "^0.31.0", + "font-awesome": "^4.7.0", + "tailwindcss": "^1.9.6" + }, + "devDependencies": { + "node-sass": "^4.14.1", + "sass-loader": "^9.0.0", + "webpack-notifier": "1.6.0" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev-server": "encore dev-server", + "dev": "encore dev", + "watch": "encore dev --watch", + "build": "encore production" + }, + "repository": { + "type": "git", + "url": "https://framagit.org/tykayn/date-poll-api.git" + }, + "keywords": [ + "survey", + "poll", + "sondage", + "api", + "symfony" + ], + "author": "tykayn", + "license": "AGPL-3.0-or-later" +} diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 7c04835..e9d3f7f 100755 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -3,6 +3,8 @@ 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; @@ -13,10 +15,22 @@ use Symfony\Component\Mailer\Exception\TransportExceptionInterface; /** * Class DefaultController * @package App\Controller - * @Route("/api/v1",name="api_") + * @Route("/page",name="api_") */ class DefaultController extends FramadateController { + /** + * @Get(path ="/", + * name = "page_home") + */ + public function indexAction() { + + $polls = $this->getDoctrine()->getRepository( Poll::class )->findAll(); + + return $this->render( 'pages/home.html.twig',[ + 'polls' => $polls, + ]); + } } diff --git a/src/Controller/PollController.php b/src/Controller/PollController.php index 5a3355a..12fb81d 100644 --- a/src/Controller/PollController.php +++ b/src/Controller/PollController.php @@ -2,409 +2,93 @@ namespace App\Controller; -use App\Entity\Choice; -use App\Entity\Owner; use App\Entity\Poll; -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; -use Swift_Mailer; -use Symfony\Component\HttpFoundation\JsonResponse; +use App\Form\PollType; +use App\Repository\PollRepository; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; /** - * Class DefaultController - * @package App\Controller - * @Route("/api/v1/poll",name="api_") + * @Route("/poll") */ -class PollController extends FramadateController { - /** - * @Get( - * path = "/", - * name = "get_all_polls" - * ) - */ - public function getAllPollsAction() { - $repository = $this->getDoctrine()->getRepository( Poll::class ); - $data = $repository->findall(); - - - return $this->json( [ - 'message' => 'here are your polls', - 'poll' => $data, - ], - 200 ); - } - - /** - * @Get( - * path = "/{id}", - * name = "get_poll", - * requirements = {"poll_id"="\d+"} - * ) - * @param SerializerInterface $serializer - * @param Poll $poll - * @param Request $request - * - * @return JsonResponse|Response - */ - public function getPollConfig( - SerializerInterface $serializer, - Poll $poll, - Request $request - ) { - $pass = $poll->getPassword(); - $data = $request->getContent(); - $data = json_decode( $data, true ); - - $comments = $poll->getComments(); - - $returnedPoll = [ - 'message' => 'your poll config', - 'poll' => $poll, - 'stacks_count' => count( $poll->getStacksOfVotes() ), - 'stacks' => $poll->getStacksOfVotes(), - 'choices_count' => $poll->computeAnswers(), - 'choices' => $poll->getChoices(), - 'comments' => $comments, - 'comments_count' => count( $comments ), - ]; - - /** - * password protected content - */ - if ( $pass && $pass !== md5($data[ 'password_input' ])) { - return $this->json( [ - 'message' => 'your password ' . $data[ 'password_input' ] . ' is wrong, and you should feel bad', - 'data' => null, - ], - 403 ); - } else { - $jsonResponse = $serializer->serialize($returnedPoll, 'json'); - - $response = new Response($jsonResponse); - $response->headers->set('Content-Type', 'application/json'); - $response->setStatusCode(200); - - return $response; - } - - } - - /** - * @Put( - * path = "/{id}/{token}", - * name = "update_poll", - * requirements = {"content"="\w+", "poll_id"="\d+"} - * ) - */ - public function updatePollConfig( - Poll $poll, - string $token, - Request $request - ) { - if ( $poll->getAdminKey() !== $token ) { - return $this->json( [ - 'message' => 'you are NOT allowed to update the poll ' . $poll->getTitle(), - ], - 403 ); - } - - // TODO check validity of request - // update only if we have the admin key - $em = $this->getDoctrine()->getManager(); - $em->persist( $poll ); - $em->flush(); - - return $this->json( [ - 'message' => 'you updated the poll ' . $poll->getTitle(), - ], - 200 ); - } - - /** - * @Post( - * path = "/", - * name = "new_poll", - * requirements = {"creator"="\w+"} - * ) - * @param Request $request - * - * @return JsonResponse - */ - public function newPollAction( Request $request ) { - - $data = $request->getContent(); - - $serializer = SerializerBuilder::create()->build(); - try { - $newpoll = $serializer->deserialize( $data, 'App\Entity\Poll', 'json' ); - } catch(RuntimeException $e) { - return $this->json(["message" => "Incorrect JSON in request"], 400); - } - $newpoll - ->setAdminKey( $newpoll->generateAdminKey() ) - ->setCreationDate( new DateTime() ) - ->setModificationPolicy( 'nobody' ); - $timeStamp = time() + ( 3600 * 24 * 90 ); // 90 days by default - $newpoll->setExpiracyDate( ( new DateTime() )->setTimestamp( $timeStamp ), - new DateTimeZone( 'Europe/Paris' ) ); - $data = json_decode( $data, true ); - $em = $this->getDoctrine()->getRepository( Owner::class ); - $foundOwner = $em->findOneBy( [ 'email' => $data[ 'owner' ][ 'email' ] ] ); - - - $userWasFound = false; - if ( ! $foundOwner ) { - //create a new owner - $owner = new Owner(); - - $owner->setPseudo( $data[ 'owner' ][ 'pseudo' ] ); - $owner->setEmail( $data[ 'owner' ][ 'email' ] ); - $foundOwner = $owner; - } else { - $userWasFound = true; - } - // link the owner and the poll - $newpoll->setOwner( $foundOwner ); - $foundOwner->addPoll( $newpoll ); - - - $em = $this->getDoctrine()->getManager(); - $em->persist( $newpoll ); - $em->persist( $foundOwner ); - - // emails - $newpoll->setMailOnComment( true ); - $newpoll->setMailOnVote( true ); - $newpoll->setHideResults( false ); - // possible answers - $newpoll->setAllowedAnswers( [ 'yes' ] ); - if ( $data[ 'voteChoices' ] ) { - switch ( $data[ 'voteChoices' ] ) { - case "only_yes": - default: - - break; - } - } - // setup the password, converting the raw with md5 hash - if ( $data[ 'password' ] ) { - $newpoll->setPassword( $data[ 'password' ] ); - } - // manage choices - // text kind of answers, dates are below - if ( $data[ 'pollType' ] == 'classic' ) { - $choices = $data[ 'dateList' ]; - foreach ( $choices as $c ) { - $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 - } - } elseif ( $data[ 'pollType' ] == 'dates' ) { - if ( $data[ 'allowSeveralHours' ] == true ) { - // different hours spans - $choices = $data[ 'dateList' ]; - } else { -//TODO (Sébastien) I assume this shouldn't be empty ? -// all days have the same hour spans - } - - - } - - $em->persist( $newpoll ); - $em->flush(); - $precision = ''; - if ( $userWasFound ) { - $precision = 'from an existing user : ' . $foundOwner->getEmail(); - } - - $this->sendCreationMailAction( $foundOwner, $newpoll ); - - return $this->json( [ - 'message' => 'you created a poll ' . $precision, - 'poll' => $newpoll, - 'password_protected' => is_string( $newpoll->getPassword() ), - 'admin_key' => $newpoll->getAdminKey(), - 'owner_modifier_token' => $foundOwner->getModifierToken(), - - ], - 201 ); - - } - - /** - * @Get( - * path = "/mail/test-mail-poll/{emailChoice}", - * name = "test-mail-poll", - * ) - * - * send the creation mail to owner - * - * @param Owner $admin_user - * @param Poll $poll - * @param Swift_Mailer $mailer - * - * @return int - * not that the email tktest_commentateur@tktest.com does not really exist - */ -// public function sendCreationMailAction( Owner $admin_user, Poll $poll, \Swift_Mailer $mailer) { - public function testSendCreationMailAction( - $emailChoice = 'tktest_commentateur@tktest.com' - ) { - $em = $this->getDoctrine()->getRepository( Owner::class ); - $foundOwner = $em->findOneByEmail( $emailChoice ); - if ( $foundOwner ) { - $poll = $foundOwner->getPolls()[ 0 ]; - $comment = $foundOwner->getComments()[ 0 ]; - - $sent = $this->sendOwnerPollsAction( $foundOwner, $poll ); - if ( $sent ) { - return $this->json( [ "message" => "test email sent to " . $foundOwner->getEmail() . "!" ], 200 ); - } - } - - return $this->json( [ "message" => "user with this email was not found" ], 400 ); - - } - - - /** - * @Delete( - * path = "/{id}", - * name = "poll_delete", - * requirements = {"accessToken"="\w+", "poll_id"="\d+"} - * ) - * @param Poll $poll - * @param $accessToken - * - * @return JsonResponse - */ - public - function deletePollAction( - Poll $poll, - $accessToken - ) { - - if ( $accessToken == $poll->getAdminKey() ) { - $em = $this->getDoctrine()->getManager(); - $em->remove( $poll ); - $em->flush(); - - return $this->json( [ - 'message' => 'boom! le sondage et ses objets assocités a été supprimé', - ] ); - } else { - return $this->json( [ - 'message' => 'le token d\'autorisation est invalide, vous ne pouvez pas modifier ce sondage', - ] ); - } - - } - - /** - * Check is a slug is already taken by a poll - * @Get( - * path = "/slug/{slug}", - * name = "check_slug_is_unique", - * ) - */ - public function checkSlugIsUniqueAction( string $slug ) { - $emPoll = $this->getDoctrine()->getRepository( Poll::class ); - $found = $emPoll->findOneByCustomUrl( $slug ); - $elaborated_message_version = false; - - if ( $found ) { - if ( ! $elaborated_message_version ) { - return $this->json( null, - 204 ); - } - - // we should use an other slug - return $this->json( [ - 'message' => ' NO, this slug is already taken on this Framadate instance ', - 'data' => [ - 'slug' => $slug, - ], - ], - 204 ); - } - if ( ! $elaborated_message_version ) { - return $this->json( null, - 404 ); - } - - return $this->json( [ - 'message' => ' yes this slug is available on this Framadate instance ', - 'data' => [ - 'slug' => $slug, - ], - ], - 404 ); - - } - - /** - * Get Admin poll config - * @Get( - * path = "/admin/{token}", - * name = "get_admin_config", - * ) - * @param SerializerInterface $serializer - * @param $token - * - * @return JsonResponse|Response - */ - public function getAdministrationConfig(SerializerInterface $serializer, $token ) { - $emPoll = $this->getDoctrine()->getRepository( Poll::class ); - $pollFound = $emPoll->findOneByAdminKey( $token ); - - if ( $pollFound ) { - - $poll = $pollFound; - $comments = $poll->getComments(); - $stacks = $poll->getStacksOfVotes(); - - $returnedPoll = [ - 'message' => 'your poll config', - 'poll' => $poll, - 'stacks_count' => count( $stacks ), - 'stacks' => $stacks, - 'choices_count' => $poll->computeAnswers(), - 'choices' => $poll->getChoices(), - 'comments' => $comments, - 'comments_count' => count( $comments ), - 'token' => $token, - ]; - - $jsonResponse = $serializer->serialize($returnedPoll, 'json'); - - $response = new Response($jsonResponse); - $response->headers->set('Content-Type', 'application/json'); - $response->setStatusCode(200); - - return $response; - } - - return $this->json( [ - 'message' => 'You are not allowed to do anything with this token', - 'data' => [ - 'token' => $token, - ], - ], - 403 ); - - } - +class PollController extends AbstractController +{ + /** + * @Route("/", name="poll_index", methods={"GET"}) + */ + public function index(PollRepository $pollRepository): Response + { + return $this->render('poll/index.html.twig', [ + 'polls' => $pollRepository->findAll(), + ]); + } + + /** + * @Route("/new", name="poll_new", methods={"GET","POST"}) + */ + public function new(Request $request): Response + { + $poll = new Poll(); + $form = $this->createForm(PollType::class, $poll); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $entityManager = $this->getDoctrine()->getManager(); + $entityManager->persist($poll); + $entityManager->flush(); + + return $this->redirectToRoute('poll_index'); + } + + return $this->render('poll/new.html.twig', [ + 'poll' => $poll, + 'form' => $form->createView(), + ]); + } + + /** + * @Route("/{id}", name="poll_show", methods={"GET"}) + */ + public function show(Poll $poll): Response + { + return $this->render('poll/show.html.twig', [ + 'poll' => $poll, + ]); + } + + /** + * @Route("/{id}/edit", name="poll_edit", methods={"GET","POST"}) + */ + public function edit(Request $request, Poll $poll): Response + { + $form = $this->createForm(PollType::class, $poll); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->getDoctrine()->getManager()->flush(); + + return $this->redirectToRoute('poll_index'); + } + + return $this->render('poll/edit.html.twig', [ + 'poll' => $poll, + 'form' => $form->createView(), + ]); + } + + /** + * @Route("/{id}", name="poll_delete", methods={"DELETE"}) + */ + 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); + $entityManager->flush(); + } + + return $this->redirectToRoute('poll_index'); + } } diff --git a/src/Controller/api/PollController.php b/src/Controller/api/PollController.php new file mode 100644 index 0000000..71c4a16 --- /dev/null +++ b/src/Controller/api/PollController.php @@ -0,0 +1,412 @@ +getDoctrine()->getRepository( Poll::class ); + $data = $repository->findall(); + + + return $this->json( [ + 'message' => 'here are your polls', + 'poll' => $data, + ], + 200 ); + } + + /** + * @Get( + * path = "/{id}", + * name = "get_poll", + * requirements = {"poll_id"="\d+"} + * ) + * @param SerializerInterface $serializer + * @param Poll $poll + * @param Request $request + * + * @return JsonResponse|Response + */ + public function getPollConfig( + SerializerInterface $serializer, + Poll $poll, + Request $request + ) { + $pass = $poll->getPassword(); + $data = $request->getContent(); + $data = json_decode( $data, true ); + + $comments = $poll->getComments(); + + $returnedPoll = [ + 'message' => 'your poll config', + 'poll' => $poll, + 'stacks_count' => count( $poll->getStacksOfVotes() ), + 'stacks' => $poll->getStacksOfVotes(), + 'choices_count' => $poll->computeAnswers(), + 'choices' => $poll->getChoices(), + 'comments' => $comments, + 'comments_count' => count( $comments ), + ]; + + /** + * password protected content + */ + if ( $pass && $pass !== md5( $data[ 'password_input' ] ) ) { + return $this->json( [ + 'message' => 'your password ' . $data[ 'password_input' ] . ' is wrong, and you should feel bad', + 'data' => null, + ], + 403 ); + } else { + $jsonResponse = $serializer->serialize( $returnedPoll, 'json' ); + + $response = new Response( $jsonResponse ); + $response->headers->set( 'Content-Type', 'application/json' ); + $response->setStatusCode( 200 ); + + return $response; + } + + } + + /** + * @Put( + * path = "/{id}/{token}", + * name = "update_poll", + * requirements = {"content"="\w+", "poll_id"="\d+"} + * ) + */ + public function updatePollConfig( + Poll $poll, + string $token, + Request $request + ) { + if ( $poll->getAdminKey() !== $token ) { + return $this->json( [ + 'message' => 'you are NOT allowed to update the poll ' . $poll->getTitle(), + ], + 403 ); + } + + // TODO check validity of request + // update only if we have the admin key + $em = $this->getDoctrine()->getManager(); + $em->persist( $poll ); + $em->flush(); + + return $this->json( [ + 'message' => 'you updated the poll ' . $poll->getTitle(), + ], + 200 ); + } + + /** + * @Post( + * path = "/", + * name = "new_poll", + * requirements = {"creator"="\w+"} + * ) + * @param Request $request + * + * @return JsonResponse + */ + public function newPollAction( Request $request ) { + + $data = $request->getContent(); + + $serializer = SerializerBuilder::create()->build(); + try { + $newpoll = $serializer->deserialize( $data, 'App\Entity\Poll', 'json' ); + } catch ( RuntimeException $e ) { + return $this->json( [ "message" => "Incorrect JSON in request" ], 400 ); + } + $newpoll + ->setAdminKey( $newpoll->generateAdminKey() ) + ->setCreationDate( new DateTime() ) + ->setModificationPolicy( 'nobody' ); + $timeStamp = time() + ( 3600 * 24 * 90 ); // 90 days by default + $newpoll->setExpiracyDate( ( new DateTime() )->setTimestamp( $timeStamp ), + new DateTimeZone( 'Europe/Paris' ) ); + $data = json_decode( $data, true ); + $em = $this->getDoctrine()->getRepository( Owner::class ); + $foundOwner = $em->findOneBy( [ 'email' => $data[ 'owner' ][ 'email' ] ] ); + + + $userWasFound = false; + if ( ! $foundOwner ) { + //create a new owner + $owner = new Owner(); + + $owner->setPseudo( $data[ 'owner' ][ 'pseudo' ] ); + $owner->setEmail( $data[ 'owner' ][ 'email' ] ); + $foundOwner = $owner; + } else { + $userWasFound = true; + } + // link the owner and the poll + $newpoll->setOwner( $foundOwner ); + $foundOwner->addPoll( $newpoll ); + + + $em = $this->getDoctrine()->getManager(); + $em->persist( $newpoll ); + $em->persist( $foundOwner ); + + // emails + $newpoll->setMailOnComment( true ); + $newpoll->setMailOnVote( true ); + $newpoll->setHideResults( false ); + // possible answers + $newpoll->setAllowedAnswers( [ 'yes' ] ); + if ( $data[ 'voteChoices' ] ) { + switch ( $data[ 'voteChoices' ] ) { + case "only_yes": + default: + + break; + } + } + // setup the password, converting the raw with md5 hash + if ( $data[ 'password' ] ) { + $newpoll->setPassword( $data[ 'password' ] ); + } + // manage choices + // text kind of answers, dates are below + if ( $data[ 'pollType' ] == 'classic' ) { + $choices = $data[ 'dateList' ]; + foreach ( $choices as $c ) { + $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 + } + } elseif ( $data[ 'pollType' ] == 'dates' ) { + if ( $data[ 'allowSeveralHours' ] == true ) { + // different hours spans + $choices = $data[ 'dateList' ]; + } else { +//TODO (Sébastien) I assume this shouldn't be empty ? +// all days have the same hour spans + } + + + } + + $em->persist( $newpoll ); + $em->flush(); + $precision = ''; + if ( $userWasFound ) { + $precision = 'from an existing user : ' . $foundOwner->getEmail(); + } + + $this->sendCreationMailAction( $foundOwner, $newpoll ); + + return $this->json( [ + 'message' => 'you created a poll ' . $precision, + 'poll' => $newpoll, + 'password_protected' => is_string( $newpoll->getPassword() ), + 'admin_key' => $newpoll->getAdminKey(), + 'owner_modifier_token' => $foundOwner->getModifierToken(), + + ], + 201 ); + + } + + /** + * @Get( + * path = "/mail/test-mail-poll/{emailChoice}", + * name = "test-mail-poll", + * ) + * + * send the creation mail to owner + * + * @param Owner $admin_user + * @param Poll $poll + * @param Swift_Mailer $mailer + * + * @return int + * not that the email tktest_commentateur@tktest.com does not really exist + */ +// public function sendCreationMailAction( Owner $admin_user, Poll $poll, \Swift_Mailer $mailer) { + public function testSendCreationMailAction( + $emailChoice = 'tktest_commentateur@tktest.com' + ) { + $em = $this->getDoctrine()->getRepository( Owner::class ); + $foundOwner = $em->findOneByEmail( $emailChoice ); + if ( $foundOwner ) { + $poll = $foundOwner->getPolls()[ 0 ]; + $comment = $foundOwner->getComments()[ 0 ]; + + $sent = $this->sendOwnerPollsAction( $foundOwner, $poll ); + if ( $sent ) { + return $this->json( [ "message" => "test email sent to " . $foundOwner->getEmail() . "!" ], 200 ); + } + } + + return $this->json( [ "message" => "user with this email was not found" ], 400 ); + + } + + + /** + * @Delete( + * path = "/{id}", + * name = "poll_delete", + * requirements = {"accessToken"="\w+", "poll_id"="\d+"} + * ) + * @param Poll $poll + * @param $accessToken + * + * @return JsonResponse + */ + public + function deletePollAction( + Poll $poll, + $accessToken + ) { + + if ( $accessToken == $poll->getAdminKey() ) { + $em = $this->getDoctrine()->getManager(); + $em->remove( $poll ); + $em->flush(); + + return $this->json( [ + 'message' => 'boom! le sondage et ses objets assocités a été supprimé', + ] ); + } else { + return $this->json( [ + 'message' => 'le token d\'autorisation est invalide, vous ne pouvez pas modifier ce sondage', + ] ); + } + + } + + /** + * Check is a slug is already taken by a poll + * @Get( + * path = "/slug/{slug}", + * name = "check_slug_is_unique", + * ) + */ + public function checkSlugIsUniqueAction( string $slug ) { + $emPoll = $this->getDoctrine()->getRepository( Poll::class ); + $found = $emPoll->findOneByCustomUrl( $slug ); + $elaborated_message_version = false; + + if ( $found ) { + if ( ! $elaborated_message_version ) { + return $this->json( null, + 204 ); + } + + // we should use an other slug + return $this->json( [ + 'message' => ' NO, this slug is already taken on this Framadate instance ', + 'data' => [ + 'slug' => $slug, + ], + ], + 204 ); + } + if ( ! $elaborated_message_version ) { + return $this->json( null, + 404 ); + } + + return $this->json( [ + 'message' => ' yes this slug is available on this Framadate instance ', + 'data' => [ + 'slug' => $slug, + ], + ], + 404 ); + + } + + /** + * Get Admin poll config + * @Get( + * path = "/admin/{token}", + * name = "get_admin_config", + * ) + * + * @param SerializerInterface $serializer + * @param $token + * + * @return JsonResponse|Response + */ + public function getAdministrationConfig( SerializerInterface $serializer, $token ) { + $emPoll = $this->getDoctrine()->getRepository( Poll::class ); + $pollFound = $emPoll->findOneByAdminKey( $token ); + + if ( $pollFound ) { + + $poll = $pollFound; + $comments = $poll->getComments(); + $stacks = $poll->getStacksOfVotes(); + + $returnedPoll = [ + 'message' => 'your poll config', + 'poll' => $poll, + 'stacks_count' => count( $stacks ), + 'stacks' => $stacks, + 'choices_count' => $poll->computeAnswers(), + 'choices' => $poll->getChoices(), + 'comments' => $comments, + 'comments_count' => count( $comments ), + 'token' => $token, + ]; + + $jsonResponse = $serializer->serialize( $returnedPoll, 'json' ); + + $response = new Response( $jsonResponse ); + $response->headers->set( 'Content-Type', 'application/json' ); + $response->setStatusCode( 200 ); + + return $response; + } + + return $this->json( [ + 'message' => 'You are not allowed to do anything with this token', + 'data' => [ + 'token' => $token, + ], + ], + 403 ); + + } + +} diff --git a/src/Form/PollType.php b/src/Form/PollType.php new file mode 100644 index 0000000..56f99a6 --- /dev/null +++ b/src/Form/PollType.php @@ -0,0 +1,39 @@ +add('title') + ->add('customUrl') + ->add('description') + ->add('creationDate') + ->add('expiracyDate') + ->add('kind') + ->add('allowedAnswers') + ->add('modificationPolicy') + ->add('mailOnComment') + ->add('mailOnVote') + ->add('hideResults') + ->add('showResultEvenIfPasswords') + ->add('password') + ->add('adminKey') + ->add('owner') + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => Poll::class, + ]); + } +} diff --git a/symfony.lock b/symfony.lock index 7d09126..283d247 100755 --- a/symfony.lock +++ b/symfony.lock @@ -208,7 +208,7 @@ "version": "2.2.3" }, "php": { - "version": "7.3" + "version": "7.4" }, "phpdocumentor/reflection-common": { "version": "2.0.0" @@ -243,6 +243,9 @@ "swiftmailer/swiftmailer": { "version": "v6.2.3" }, + "symfony/asset": { + "version": "v4.3.11" + }, "symfony/browser-kit": { "version": "v4.3.11" }, @@ -430,6 +433,9 @@ "symfony/security-core": { "version": "v4.3.5" }, + "symfony/security-csrf": { + "version": "v4.3.11" + }, "symfony/service-contracts": { "version": "v1.1.7" }, @@ -496,6 +502,25 @@ "ref": "dae9b39fd6717970be7601101ce5aa960bf53d9a" } }, + "symfony/webpack-encore-bundle": { + "version": "1.6", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "1.6", + "ref": "69e1d805ad95964088bd510c05995e87dc391564" + }, + "files": [ + "assets/app.js", + "assets/styles/app.css", + "config/packages/assets.yaml", + "config/packages/prod/webpack_encore.yaml", + "config/packages/test/webpack_encore.yaml", + "config/packages/webpack_encore.yaml", + "package.json", + "webpack.config.js" + ] + }, "symfony/yaml": { "version": "v4.3.5" }, diff --git a/templates/base.html.twig b/templates/base.html.twig index 34dd070..76db111 100755 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -3,10 +3,25 @@