Browse Source

add flex reciepes in base folder

master
Tykayn 1 year ago committed by tykayn
parent
commit
0a15f364a2
  1. 42
      .env
  2. 16
      composer.json
  3. 5991
      composer.lock
  4. 8
      config/bundles.php
  5. 7
      config/packages/dev/jms_serializer.yaml
  6. 4
      config/packages/dev/swiftmailer.yaml
  7. 19
      config/packages/doctrine.yaml
  8. 6
      config/packages/doctrine_migrations.yaml
  9. 15
      config/packages/fos_rest.yaml
  10. 13
      config/packages/jms_serializer.yaml
  11. 20
      config/packages/prod/doctrine.yaml
  12. 6
      config/packages/prod/jms_serializer.yaml
  13. 3
      config/packages/sensio_framework_extra.yaml
  14. 3
      config/packages/swiftmailer.yaml
  15. 7
      config/packages/test/doctrine.yaml
  16. 2
      config/packages/test/swiftmailer.yaml
  17. 2
      config/packages/test/twig.yaml
  18. 2
      config/packages/twig.yaml
  19. 7
      config/routes/annotations.yaml
  20. 13
      doc/database_creation.md
  21. 53
      doc/env-example
  22. 65
      doc/examples.md
  23. 16
      doc/migration.md
  24. 10
      doc/nginx.md
  25. 63
      doc/nginx/base-framadate-api.conf
  26. 50
      doc/nginx/base-symfony.conf
  27. 95
      doc/nginx/framadate-api.conf
  28. 28
      doc/nginx/setup.sh
  29. 0
      migrations/.gitignore
  30. 18
      src/Controller/AdminController.php
  31. 18
      src/Controller/DefaultController.php
  32. 156
      src/Controller/FramadateController.php
  33. 26
      src/Controller/MigrationController.php
  34. 18
      src/Controller/OwnerController.php
  35. 18
      src/Controller/VoteController.php
  36. 0
      src/Entity/.gitignore
  37. 145
      src/Entity/Choice.php
  38. 103
      src/Entity/Comment.php
  39. 228
      src/Entity/Owner.php
  40. 606
      src/Entity/Poll.php
  41. 145
      src/Entity/StackOfVotes.php
  42. 124
      src/Entity/Vote.php
  43. 12
      src/Entity/timedTrait.php
  44. 0
      src/Repository/.gitignore
  45. 50
      src/Repository/ChoiceRepository.php
  46. 50
      src/Repository/CommentRepository.php
  47. 50
      src/Repository/OwnerRepository.php
  48. 52
      src/Repository/PollRepository.php
  49. 50
      src/Repository/StackOfVotesRepository.php
  50. 50
      src/Repository/VoteRepository.php
  51. 239
      symfony.lock
  52. 20
      templates/admin/index.html.twig
  53. 35
      templates/base.html.twig
  54. 20
      templates/default/index.html.twig
  55. 15
      templates/email-base-plaintext.html.twig
  56. 64
      templates/email-base.html.twig
  57. 25
      templates/emails/author-mail.html.twig
  58. 22
      templates/emails/comment-notification.html.twig
  59. 30
      templates/emails/creation-mail.html.twig
  60. 15
      templates/emails/expiration-mail.html.twig
  61. 24
      templates/emails/footer.html.twig
  62. 14
      templates/emails/modification-notification-mail.html.twig
  63. 26
      templates/emails/owner-list.html.twig
  64. 2
      templates/emails/partial/admin_link.html.twig
  65. 39
      templates/emails/partial/poll.html.twig
  66. 11
      templates/emails/partial/public_link.html.twig
  67. 13
      templates/emails/vote-notification.html.twig
  68. 67
      templates/index.html.twig
  69. 20
      templates/migration/index.html.twig
  70. 20
      templates/owner/index.html.twig
  71. 14
      templates/pages/funky.html.twig
  72. 5
      templates/pages/home.html.twig
  73. 5
      templates/poll/_delete_form.html.twig
  74. 4
      templates/poll/_form.html.twig
  75. 13
      templates/poll/edit.html.twig
  76. 65
      templates/poll/index.html.twig
  77. 11
      templates/poll/new.html.twig
  78. 78
      templates/poll/show.html.twig
  79. 14
      templates/split/footer.html.twig
  80. 20
      templates/split/header.html.twig
  81. 20
      templates/vote/index.html.twig
  82. 131
      translations/en.json
  83. 131
      translations/fr.json

42
.env

@ -11,9 +11,47 @@
# 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.html#use-environment-variables-for-infrastructure-configuration
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=07ce2d71aface91c392a17c7081f683c
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 ###
###> 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
DATABASE_URL=mysql://framadate-admin:framadate-admin-password@127.0.0.1:3306/framadate_api
###< doctrine/doctrine-bundle ###
###> nelmio/cors-bundle ###
CORS_ALLOW_ORIGIN=^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$
###< nelmio/cors-bundle ###
###> symfony/swiftmailer-bundle ###
# For Gmail as a transport, use: "gmail://username:password@localhost"
# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
# Delivery is disabled by default via "null://localhost"
MAILER_URL=sendmail://YOUR_WEBSITE
# set the support email who will answer users in case of emergency
SUPPORT_EMAIL=YOUR_EMAIL
###< symfony/swiftmailer-bundle ###
###> symfony/mailer ###
MAILER_DSN=smtp://localhost
###< symfony/mailer ###
## Migration from v1
OLD_DATABASE_NAME=framadate
OLD_DATABASE_USER=framadate
OLD_DATABASE_PASS=framadate
### UNIQ_INSTALL_KEY must be really unique
UNIQ_INSTALL_KEY=superCaliFragilistiExpialidousiousse

16
composer.json

@ -7,14 +7,23 @@
"php": ">=7.2.5",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "1.11.99.1",
"doctrine/doctrine-bundle": "^2.3",
"doctrine/doctrine-migrations-bundle": "^3.1",
"doctrine/orm": "^2.8",
"friendsofsymfony/rest-bundle": "^3.0",
"jms/serializer-bundle": "^3.9",
"sensio/framework-extra-bundle": "^6.1",
"symfony/console": "5.2.*",
"symfony/dotenv": "5.2.*",
"symfony/flex": "^1.3.1",
"symfony/form": "5.2.*",
"symfony/framework-bundle": "5.2.*",
"symfony/proxy-manager-bridge": "5.2.*",
"symfony/swiftmailer-bundle": "^3.5",
"symfony/twig-bundle": "5.2.*",
"symfony/yaml": "5.2.*"
},
"require-dev": {
},
"config": {
"optimize-autoloader": true,
"preferred-install": {
@ -57,5 +66,8 @@
"allow-contrib": false,
"require": "5.2.*"
}
},
"require-dev": {
"symfony/maker-bundle": "^1.30"
}
}

5991
composer.lock generated

File diff suppressed because it is too large Load Diff

8
config/bundles.php

@ -2,4 +2,12 @@
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
FOS\RestBundle\FOSRestBundle::class => ['all' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
];

7
config/packages/dev/jms_serializer.yaml

@ -0,0 +1,7 @@
jms_serializer:
visitors:
json_serialization:
options:
- JSON_PRETTY_PRINT
- JSON_UNESCAPED_SLASHES
- JSON_PRESERVE_ZERO_FRACTION

4
config/packages/dev/swiftmailer.yaml

@ -0,0 +1,4 @@
# See https://symfony.com/doc/current/email/dev_environment.html
swiftmailer:
# send all emails to a specific address
#delivery_addresses: ['me@example.com']

19
config/packages/doctrine.yaml

@ -0,0 +1,19 @@
doctrine:
dbal:
override_url: true
url: '%env(resolve:DATABASE_URL)%'
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '13'
orm:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
mappings:
App:
is_bundle: false
type: annotation
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App

6
config/packages/doctrine_migrations.yaml

@ -0,0 +1,6 @@
doctrine_migrations:
migrations_paths:
# namespace is arbitrary but should be different from App\Migrations
# as migrations classes should NOT be autoloaded
'DoctrineMigrations': '%kernel.project_dir%/migrations'
enable_profiler: '%kernel.debug%'

15
config/packages/fos_rest.yaml

@ -0,0 +1,15 @@
# Read the documentation: https://symfony.com/doc/master/bundles/FOSRestBundle/index.html
fos_rest: null
# param_fetcher_listener: true
# allowed_methods_listener: true
# routing_loader: true
# view:
# view_response_listener: true
# exception:
# codes:
# App\Exception\MyException: 403
# messages:
# App\Exception\MyException: Forbidden area.
# format_listener:
# rules:
# - { path: ^/api, prefer_extension: true, fallback_format: json, priorities: [ json, html ] }

13
config/packages/jms_serializer.yaml

@ -0,0 +1,13 @@
jms_serializer:
visitors:
xml_serialization:
format_output: '%kernel.debug%'
# metadata:
# auto_detection: false
# directories:
# any-name:
# namespace_prefix: "My\\FooBundle"
# path: "@MyFooBundle/Resources/config/serializer"
# another-name:
# namespace_prefix: "My\\BarBundle"
# path: "@MyBarBundle/Resources/config/serializer"

20
config/packages/prod/doctrine.yaml

@ -0,0 +1,20 @@
doctrine:
orm:
auto_generate_proxy_classes: false
metadata_cache_driver:
type: pool
pool: doctrine.system_cache_pool
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system

6
config/packages/prod/jms_serializer.yaml

@ -0,0 +1,6 @@
jms_serializer:
visitors:
json_serialization:
options:
- JSON_UNESCAPED_SLASHES
- JSON_PRESERVE_ZERO_FRACTION

3
config/packages/sensio_framework_extra.yaml

@ -0,0 +1,3 @@
sensio_framework_extra:
router:
annotations: false

3
config/packages/swiftmailer.yaml

@ -0,0 +1,3 @@
swiftmailer:
url: '%env(MAILER_URL)%'
spool: { type: 'memory' }

7
config/packages/test/doctrine.yaml

@ -0,0 +1,7 @@
#doctrine:
# dbal:
# # Overrides the database name in the test environment only
# # "host", "port", "username", & "password" can also be set to override their respective url parts
# #
# # If you're using ParaTest, "TEST_TOKEN" is set by ParaTest otherwise nothing is appended to the database name.
# dbname: main_test%env(default::TEST_TOKEN)%

2
config/packages/test/swiftmailer.yaml

@ -0,0 +1,2 @@
swiftmailer:
disable_delivery: true

2
config/packages/test/twig.yaml

@ -0,0 +1,2 @@
twig:
strict_variables: true

2
config/packages/twig.yaml

@ -0,0 +1,2 @@
twig:
default_path: '%kernel.project_dir%/templates'

7
config/routes/annotations.yaml

@ -0,0 +1,7 @@
controllers:
resource: ../../src/Controller/
type: annotation
kernel:
resource: ../../src/Kernel.php
type: annotation

13
doc/database_creation.md

@ -0,0 +1,13 @@
#Database setup
In the mysql prompt, to create a new user and a dedicated database, run this:
```
CREATE DATABASE IF NOT EXISTS framadate_api;
CREATE USER 'framadate-admin'@'localhost' IDENTIFIED BY 'framadate-admin-password';
GRANT ALL PRIVILEGES ON framadate_api.* TO 'framadate-admin'@'localhost';
FLUSH PRIVILEGES;
SHOW GRANTS FOR 'framadate-admin'@'localhost';
SHOW DATABASES;
```
now you have your config ready to host Framadate API.

53
doc/env-example

@ -0,0 +1,53 @@
# 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=dev
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://localhost
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 ###
###> 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
DATABASE_URL=mysql://framadate-admin:framadate-admin-password@127.0.0.1:5432/framadate-api
###< doctrine/doctrine-bundle ###
###> nelmio/cors-bundle ###
CORS_ALLOW_ORIGIN=^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$
###< nelmio/cors-bundle ###
###> symfony/swiftmailer-bundle ###
# For Gmail as a transport, use: "gmail://username:password@localhost"
# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
# Delivery is disabled by default via "null://localhost"
MAILER_URL=sendmail://YOUR_WEBSITE
# set the support email who will answer users in case of emergency
SUPPORT_EMAIL=YOUR_EMAIL
###< symfony/swiftmailer-bundle ###
###> symfony/mailer ###
MAILER_DSN=smtp://localhost
###< symfony/mailer ###
DATABASE_URL=mysql://framadate-admin:framadate-admin-password@127.0.0.1:5432/framadate-api

65
doc/examples.md

@ -0,0 +1,65 @@
# API example
after having setup your project and database, and having a local server running you can rest these commands
## get Swagger configuration
```
GET
http://127.0.0.1:8000/api/doc.json
```
## create a poll
```http request
POST http://127.0.0.1:8000/api/v1/poll/new
Content-Type:"application/json"
{
"title": "un coup à boire",
"description": "et on boit quoi?",
"kind": "text",
"custom_url": "this-is-sparta",
"password":"hah",
"owner": {
"email": "contact@cipherbliss.com",
"pseudo": "cipherbliss crée un sondage par postman"
},
"choices_to_create": [ "cappou" ,"thé", "café", "vodka" ]
}
```
## get configuration of existing poll, protected with a password
```http request
GET http://127.0.0.1:8000/api/v1/poll/1
Content-Type:"application/json"
{
"password_input": "there_is_my_pass"
}
```
## add a vote to an existing poll
```http request
POST http://127.0.0.1:8000/api/v1/poll/1/vote
Content-Type:"application/json"
{
"pseudo": "tktest",
"email": "testing_vote_people@tktest.com",
"votes": [
{
"choice_id": 5,
"value": "no"
},
{
"choice_id": 6,
"value": "maybe"
},
{
"choice_id": 7,
"value": "yes"
}
]
}
```

16
doc/migration.md

@ -0,0 +1,16 @@
# Migration depuis la version 1 de Framadate
Cela va importer vos anciens sondages dans les tables de la nouvelle version.
* Remplissez les accès en base de données concernant la version 1 de Framadate dans votre fichier [.env](../.env) à la racine de ce dossier.
* Choisissez une chaine unique dans votre fichier .env à la place de superCaliFragilistiExpialidousiousse
* Une fois votre application accessible via navigateur, rendez vous sur votre point d'accès web à l'addresse /migration-from-v1/superCaliFragilistiExpialidousiousse en changeant __superCaliFragilistiExpialidousiousse__ par votre variable unique.
* tadam, vous aurez un message indiquant si vous avez réussi la migration.
## Comparaison de schémas
pour information, voici les schémas des bases de données des deux versions:
### Ancien
![old](../public/assets/img/framadate_v1_schema.svg)
### Nouveau
![old](../public/assets/img/framadate_funky_schema.svg)

10
doc/nginx.md

@ -0,0 +1,10 @@
# CORS config for nginx
To be able to work between some domain and an other you have to setup the cross origin ressource config of your web server.
Use the nginx example config, don't forget to replace the example.com.
The following script copies the example confing and asks you for a replacement of the domains names.
```bash
bash doc/nginx/setup.sh
```

63
doc/nginx/base-framadate-api.conf

@ -0,0 +1,63 @@
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name framadate-api.cipherbliss.com;
root /home/www/tykayn/cipherbliss/framadate/public/;
# Use Mozilla's guidelines for SSL/TLS settings
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
# NOTE: some settings below might be redundant
ssl_certificate /etc/letsencrypt/live/framadate-api.cipherbliss.com/fullchain.pem; # managed by Cert$
ssl_certificate_key /etc/letsencrypt/live/framadate-api.cipherbliss.com/privkey.pem; # managed by Ce$
client_max_body_size 32m;
location / {
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
# optionally disable falling back to PHP script for the asset directories;
# nginx will return a 404 error when files are not found instead of passing the
# request to Symfony (improves performance but Symfony's 404 page is not displayed)
# location /bundles {
# try_files $uri =404;
# }
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
# or your custom php-handler
# fastcgi_pass php-handler
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
#include fastcgi.conf;
# optionally set the value of the environment variables used in the application
# fastcgi_param APP_ENV prod;
# fastcgi_param APP_SECRET <app-secret-id>;
# fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://framadate-api.cipherbliss.com/index.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
# return 404 for all other php files not matching the front controller
# this prevents access to other php files you don't want to be accessible.
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/framadate-api_error.log;
access_log /var/log/nginx/framadate-api_access.log;
}

50
doc/nginx/base-symfony.conf

@ -0,0 +1,50 @@
server {
server_name domain.tld www.domain.tld;
root /var/www/project/public;
location / {
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
# optionally disable falling back to PHP script for the asset directories;
# nginx will return a 404 error when files are not found instead of passing the
# request to Symfony (improves performance but Symfony's 404 page is not displayed)
# location /bundles {
# try_files $uri =404;
# }
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# optionally set the value of the environment variables used in the application
# fastcgi_param APP_ENV prod;
# fastcgi_param APP_SECRET <app-secret-id>;
# fastcgi_param DATABASE_URL "mysql://db_user:db_pass@host:3306/db_name";
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/index.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
# return 404 for all other php files not matching the front controller
# this prevents access to other php files you don't want to be accessible.
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/project_error.log;
access_log /var/log/nginx/project_access.log;
}

95
doc/nginx/framadate-api.conf

@ -0,0 +1,95 @@
server {
listen 80;
listen [::]:80;
server_name framadate-api.cipherbliss.com;
# enforce https
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name framadate-api.cipherbliss.com;
# Use Mozilla's guidelines for SSL/TLS settings
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
# Path to the root of your installation
root /home/www/tykayn/cipherbliss/framadate/;
location / {
# try to serve file directly, fallback to index.html to see the frontend of funky framadate
try_files $uri /index.html$is_args$args;
# handle OPTIONS requests
# @note: don't try to DRY out this "if" block, or you're gonna have a bad time.
# @see: http://wiki.nginx.org/IfIsEvil
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since';
add_header 'Access-Control-Allow-Methods' 'GET, DELETE, OPTIONS, POST, PUT';
add_header 'Access-Control-Allow-Origin' 'https://framadate-api.cipherbliss.com';
add_header 'Access-Control-Max-Age' 2592000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
# send the CORS headers
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Origin' 'https://framadate-api.cipherbliss.com';
# set additional security headers
add_header 'Cache-Control' 'no-cache, no-store, must-revalidate';
add_header 'Content-Security-Policy' 'connect-src framadate-api.cipherbliss.com';
add_header 'Expires' '0';
add_header 'Pragma' 'no-cache';
add_header 'Strict-Transport-Security' 'max-age=31536000; includeSubDomains';
add_header 'X-Content-Type-Options' 'nosniff';
add_header 'X-Frame-Options' 'DENY';
add_header 'X-XSS-Protection' '1; mode=block';
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ~* \.(png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
# PROD
location ~ ^/app\.php(/|$) {
include fastcgi.conf;
fastcgi_intercept_errors on;
fastcgi_pass php-handler;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/app.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
# return 404 for all other php files not matching the front controller
# this prevents access to other php files you don't want to be accessible.
location ~ \.php$ {
return 404;
}
}

28
doc/nginx/setup.sh

@ -0,0 +1,28 @@
#!/bib/bash
echo "copy framadate api nginx config"
sudo cp ./framadate-api.conf /etc/nginx/sites-available/
echo "replace api.example.com with your website api domain"
APISUBDOMAIN='other-api-domain.example.com'
read -p 'sub api domain [$APISUBDOMAIN]: ' APISUBDOMAIN
APIDOMAIN='other-api-domain.example.com'
read -p 'sub api domain [$APIDOMAIN]: ' APIDOMAIN
sudo sed -i 's/api.example.com/$APISUBDOMAIN/g' /etc/nginx/sites-available/framadate-api.conf
echo "replace example.com with your website api domain"
sudo sed -i 's/example.com/$APIDOMAIN/g' /etc/nginx/sites-available/framadate-api.conf
echo "enable nginx config"
sudo ln -s /etc/nginx/sites-available/framadate-api.conf /etc/nginx/sites-enabled/framadate-api.conf
echo "testing nginx config"
EXPECTED_NGINX_TEST="nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful"
CHECK_NGINX=$(sudo nginx -t)
# shellcheck disable=SC1073
if [ "$CHECK_NGINX" = "$EXPECTED_NGINX_TEST"]; then
echo "config is OK"
exit 0
else
echo "something is wrong in your nginx config, check the file /etc/nginx/sites-available/framadate-api.conf"
exit 1
fi

0
migrations/.gitignore vendored

18
src/Controller/AdminController.php

@ -0,0 +1,18 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class AdminController extends AbstractController
{
#[Route('/admin', name: 'admin')]
public function index(): Response
{
return $this->render('admin/index.html.twig', [
'controller_name' => 'AdminController',
]);
}
}

18
src/Controller/DefaultController.php

@ -0,0 +1,18 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DefaultController extends AbstractController
{
#[Route('/', name: 'default')]
public function index(): Response
{
return $this->render('pages/home.html.twig', [
'controller_name' => 'DefaultController',
]);
}
}

156
src/Controller/FramadateController.php

@ -0,0 +1,156 @@
<?php
namespace App\Controller;
use App\Entity\Owner;
use App\Entity\Poll;
use JMS\Serializer\Type\Exception\Exception;
use Swift_Message;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* sending emails controller
*
* Class FramadateController
* @package App\Controller
*/
class FramadateController extends AbstractController {
private $mail_service;
// public function __construct( \Swift_Mailer $mailer ) {
// $this->mail_service = $mailer;
// }
/**
* generic way to send email with html template
*
* @param $config
*/
public function sendMailWithVars( $config ) {
if ( ! isset( $config[ 'poll' ] ) ) {
$config[ 'poll' ] = new Poll();
}
$emailChoicesTemplates = [
'creation_poll' => 'creation-mail.html.twig',
'edit_poll' => 'modification-notification-mail.html.twig',
'creation_poll_admin' => 'author-mail.html.twig',
'owner_list' => 'owner-list.html.twig',
'expiration' => 'expiration-mail.html.twig',
'creation_comment' => 'comment-notification.html.twig',
'creation_vote' => 'vote-notification.html.twig',
];
$emailChoicesTitles = [
'creation_poll' => 'Framadate | Création de sondage - lien public - ' . $config[ 'poll' ]->getTitle(),
'edit_poll' => 'Framadate | Modification de sondage - ' . $config[ 'poll' ]->getTitle(),
'creation_poll_admin' => 'Framadate | Création de sondage - lien admin - ',
'owner_list' => 'Framadate | Vos sondages créés',
'expiration' => 'Framadate | Notice d\'expiration du sondage ' . $config[ 'poll' ]->getTitle(),
'creation_comment' => 'Framadate | Commentaire de "' . $config[ 'owner' ]->getPseudo() . '" - sondage ' . $config[ 'poll' ]->getTitle(),
'creation_vote' => 'Framadate | Vote de "' . $config[ 'owner' ]->getPseudo() . '" - sondage ' . $config[ 'poll' ]->getTitle(),
];
// Create a message
$htmlbody = $this->renderView(
$config[ 'email_template' ],
$config
);
$message = ( new Swift_Message( $config[ 'title' ] ) )
->setContentType( "text/html" )
->setCharset( 'UTF-8' )
->setFrom( [ 'ne-pas-repondre@framadate-api.cipherbliss.com' ] )
->setTo( [ $config[ 'owner' ]->getEmail() ] )
->setBody( $htmlbody, 'text/html' );
// Send the message
$numSent = $this->mail_service->send( $message );
$this->numSent = $numSent;
return 1;
}
/**
* send created polls to an owner
*
* @param Owner $owner
*
* @return int|void
* @throws Exception
*/
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 Owner $foundOwner
* @param Poll|null $poll
*
*/
public function sendCreationMailAction( Owner $foundOwner, Poll $poll = null ) {
// anti spam , limit to every minute TODO
$config = [
'owner' => $foundOwner,
'from' => 'ne-pas-repondre@framadate-api.cipherbliss.com',
'poll' => $poll,
'title' => 'Création de sondage - ' . ( $poll ? $poll->getTitle() : $poll ),
'email_template' => 'emails/creation-mail.html.twig',
];
return $this->sendMailWithVars( $config );
}
/**
* @param Owner $owner
* @param $comment
*
* @return int
*/
public function sendCommentNotificationAction( Owner $owner, $comment ) {
$config = [
'owner' => $owner,
'comment' => $comment,
'poll' => $comment->getPoll(),
'title' => 'Framadate | Commentaire de "' . $owner->getPseudo() . '" - sondage ' . $comment->getPoll()->getTitle(),
'email_template' => 'emails/comment-notification.html.twig',
];
$this->sendMailWithVars( $config );
return 1;
}
/**
* @param Owner $owner
* @param $stackOfVotes
*
* @return int
*/
public function sendVoteNotificationAction( Owner $owner, $stackOfVotes ) {
$config = [
'owner' => $owner,
'comment' => $stackOfVotes,
'poll' => $stackOfVotes->getPoll(),
'title' => 'Framadate | Vote de "' . $owner->getPseudo() . '" - sondage ' . $stackOfVotes->getPoll()->getTitle(),
'email_template' => 'emails/vote-notification.html.twig',
];
$this->sendMailWithVars( $config );
return 1;
}
}

26
src/Controller/MigrationController.php

@ -0,0 +1,26 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class MigrationController extends AbstractController
{
#[Route('/migration', name: 'migration')]
public function index(): Response
{
// TODO
// get env vars
// check uniq key is good
// fetch old Database
// gather objects
// create new polls
// success
// failure notice
return $this->render('migration/index.html.twig', [
'controller_name' => 'MigrationController',
]);
}
}

18
src/Controller/OwnerController.php

@ -0,0 +1,18 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class OwnerController extends AbstractController
{
#[Route('/owner', name: 'owner')]
public function index(): Response
{
return $this->render('owner/index.html.twig', [
'controller_name' => 'OwnerController',
]);
}
}

18
src/Controller/VoteController.php

@ -0,0 +1,18 @@
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class VoteController extends AbstractController
{
#[Route('/vote', name: 'vote')]
public function index(): Response
{
return $this->render('vote/index.html.twig', [
'controller_name' => 'VoteController',
]);
}
}

0
src/Entity/.gitignore vendored

145
src/Entity/Choice.php

@ -0,0 +1,145 @@
<?php
namespace App\Entity;
use DateTime;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
/**
* one poll choice, could be a text or a date
* @ORM\Entity(repositoryClass="App\Repository\ChoiceRepository")
* @Serializer\ExclusionPolicy("all")
*/
class Choice {
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
* @Serializer\Expose()
*/
public $id;
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Serializer\Type("string")
* @Serializer\Expose()
*/
public $name;
/**
* @ORM\Column(type="string", length=1024, nullable=true)
* @Serializer\Type("string")
* @Serializer\Expose()
*/
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")
*/
public $votes;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Poll", inversedBy="choices", cascade={"persist"})
* @Serializer\Type("App\Entity\Poll")
*/
private $poll;
public function __construct( $optionalName = null ) {
$this->poll = new ArrayCollection();
$this->votes = new ArrayCollection();
$this->setDateTime( new DateTime() );
if ( $optionalName ) {
$this->setName( $optionalName );
}
}
public function display() {
return [
'id' => $this->getId(),
'date' => $this->getDateTime(),
'text' => $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 getName(): ?string {
return $this->name;
}
public function setName( ?string $name ): self {
$this->name = $name;
return $this;
}
public function getPoll(): ?Poll {
return $this->poll;
}
public function setPoll( ?Poll $poll ): self {
$this->poll = $poll;
return $this;
}
/**
* @return Collection|Vote[]
*/
public function getVotes(): Collection {
return $this->votes;
}
public function addVote( Vote $vote ): self {
if ( ! $this->votes->contains( $vote ) ) {
$this->votes[] = $vote;
$vote->setChoice( $this );
}
return $this;
}
public function removeVote( Vote $vote ): self {
if ( $this->votes->contains( $vote ) ) {
$this->votes->removeElement( $vote );
// set the owning side to null (unless already changed)
if ( $vote->getChoice() === $this ) {
$vote->setChoice( null );
}
}
return $this;
}
public function getUrl(): ?string {
return $this->url;
}
public function setUrl( ?string $url ): self {
$this->url = $url;
return $this;
}
}

103
src/Entity/Comment.php

@ -0,0 +1,103 @@
<?php
namespace App\Entity;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
/**
* @ORM\Entity(repositoryClass="App\Repository\CommentRepository")
* @Serializer\ExclusionPolicy("all")
*/
class Comment {
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Owner", inversedBy="comments")
* @Serializer\Type("App\Entity\Owner")
* @Serializer\Expose()
*/
private $owner;
/**
* @ORM\Column(type="text")
* @Serializer\Type("string")
* @Serializer\Expose()
*/
private $text;
/**
* @ORM\Column(type="datetime")
* @Serializer\Type("datetime")
* @Serializer\Expose()
*/
private $createdAt;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Poll", inversedBy="comments")
*/
private $poll;
function display() {
return [
'id' => $this->getId(),
'text' => $this->getText(),
'pseudo' => $this->getOwner()->getPseudo(),
'date' => $this->getCreatedAt(),
];
}
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;
}
public function setText( string $text ): self {
$this->text = $text;
return $this;
}
public function getCreatedAt(): ?DateTimeInterface {
return $this->createdAt;
}
public function setCreatedAt( DateTimeInterface $createdAt ): self {
$this->createdAt = $createdAt;
return $this;
}
public function getPoll(): ?Poll {
return $this->poll;
}
public function setPoll( ?Poll $poll ): self {
$this->poll = $poll;
return $this;
}
}

228
src/Entity/Owner.php

@ -0,0 +1,228 @@
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
/**
* @ORM\Entity(repositoryClass="App\Repository\OwnerRepository")
*/
class Owner {
/**
* @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\OneToMany(targetEntity="App\Entity\Poll", mappedBy="owner",cascade={"persist","remove"},orphanRemoval=true)
* @Serializer\Type("App\Entity\Poll")
*/
private $polls;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="owner", cascade={"persist","remove"},orphanRemoval=true)
*/
private $comments;
/**
* @ORM\OneToMany(targetEntity="App\Entity\StackOfVotes", mappedBy="owner", cascade={"persist","remove"},orphanRemoval=true)
*/
private $stackOfVotes;
/**
* @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)
*/
private $requestedPollsDate;
public function __construct() {
$this->polls = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->stackOfVotes = new ArrayCollection();
$this->setCreatedAt( new \DateTime() );
$this->setModifierToken( uniqid() );
}
public function getId(): ?int {
return $this->id;
}
public function getEmail(): ?string {
return $this->email;
}
public function setEmail( string $email ): self {
$this->email = $email;
return $this;
}
public function getPseudo(): ?string {
return $this->pseudo;
}
public function setPseudo( string $pseudo ): self {
$this->pseudo = $pseudo;
return $this;
}
/**
* @return Collection|Poll[]
*/
public function getPolls(): Collection {
return $this->polls;
}
public function addPoll( Poll $poll ): self {
if ( ! $this->polls->contains( $poll ) ) {
$this->polls[] = $poll;
$poll->setOwner( $this );
}
return $this;
}
public function removePoll( Poll $poll ): self {
if ( $this->polls->contains( $poll ) ) {
$this->polls->removeElement( $poll );
// set the owning side to null (unless already changed)
if ( $poll->getOwner() === $this ) {
$poll->setOwner( null );
}
}
return $this;
}
/**
* @return Collection|Comment[]
*/
public function getComments(): Collection {
return $this->comments;
}
public function addText( Comment $text ): self {
if ( ! $this->comments->contains( $text ) ) {
$this->comments[] = $text;
$text->setOwner( $this );
}
return $this;
}
public function removeText( Comment $text ): self {
if ( $this->comments->contains( $text ) ) {
$this->comments->removeElement( $text );
// set the owning side to null (unless already changed)
if ( $text->getOwner() === $this ) {
$text->setOwner( null );
}
}
return $this;
}
/**
* @return Collection|StackOfVotes[]
*/
public function getStackOfVotes(): Collection {
return $this->stackOfVotes;
}
public function addStackOfVote( StackOfVotes $stackOfVote ): self {
if ( ! $this->stackOfVotes->contains( $stackOfVote ) ) {
$this->stackOfVotes[] = $stackOfVote;
$stackOfVote->setOwner( $this );
}
return $this;
}
public function removeStackOfVote( StackOfVotes $stackOfVote ): self {
if ( $this->stackOfVotes->contains( $stackOfVote ) ) {
$this->stackOfVotes->removeElement( $stackOfVote );
// set the owning side to null (unless already changed)
if ( $stackOfVote->getOwner() === $this ) {
$stackOfVote->setOwner( null );
}
}
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;
$comment->setOwner( $this );
}
return $this;
}
public function removeComment( Comment $comment ): self {
if ( $this->comments->contains( $comment ) ) {
$this->comments->removeElement( $comment );
// set the owning side to null (unless already changed)
if ( $comment->getOwner() === $this ) {
$comment->setOwner( null );
}
}
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;
}
public function setRequestedPollsDate( $requestedPollsDate ): self {
$this->requestedPollsDate = $requestedPollsDate;
return $this;
}
}

606
src/Entity/Poll.php

@ -0,0 +1,606 @@
<?php
namespace App\Entity;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
/**
* @ORM\Entity(repositoryClass="App\Repository\PollRepository")
* @Serializer\ExclusionPolicy("all")
*/
class Poll {
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
* @Serializer\Expose()
* @Serializer\Type("integer")
*/
public $id;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Expose()
* @Serializer\Type("string")
*/
public $title;
/**
* @ORM\Column(type="string", length=255, nullable=true)
* @Serializer\Expose()
* @Serializer\Type("string")
*/
public $customUrl;
/**
* @ORM\Column(type="string", length=1000)
* @Serializer\Expose()
* @Serializer\Type("string")
*/
public $description;
/**
* @ORM\Column(type="datetime" , options={"default"="CURRENT_TIMESTAMP"})
* @Serializer\Expose()
*/
public $creationDate;
/**
* @ORM\Column(type="datetime")
* @Serializer\Expose()
*/
public $expiracyDate;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Owner", inversedBy="polls",cascade={"persist"})
* @ORM\JoinColumn(nullable=false)
* @Serializer\Type("App\Entity\Owner")
* @Serializer\Expose()
*/
public $owner;
/**
* kind of poll, text or date
* @ORM\Column(type="string", length=255)
* @Serializer\Type("string")
* @Serializer\Expose()
*/
public $kind = 'text';
/**
* array of possible answer to each choice, by default: "yes" or nothing only.
* could be also "yes", "maybe", "no". extensible to anything
* @ORM\Column(type="array")
* @Serializer\Type("array")
* @Serializer\Expose()
*/
public $allowedAnswers;
/**
* kind of way the people can modify the poll
* everybody - can modify votes
* self - one can only modify its own vote
* nobody - no one can modify the votes (excepted admin), pray to have it right at first
* @ORM\Column(type="string", length=255)
* @Serializer\Type("string")
* @Serializer\Expose()
*/
public $modificationPolicy = 'nobody';
/**
* send a mail on a new comment
* @ORM\Column(type="boolean", nullable=true)
* @Serializer\Type("boolean")
* @Serializer\Expose()
*/
public $mailOnComment;
/**
* send a mail on a new vote
* @ORM\Column(type="boolean", nullable=true)
* @Serializer\Type("boolean")
* @Serializer\Expose()
*/
public $mailOnVote;
/**
* hide publicly results
* @ORM\Column(type="boolean", nullable=true)
* @Serializer\Type("boolean")
* @Serializer\Expose()
*/
public $hideResults;
/**
* show publicly results even if there is a password to access the vote
* @ORM\Column(type="boolean", nullable=true)
* @Serializer\Type("boolean")
* @Serializer\Expose()
*/
public $showResultEvenIfPasswords;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Vote", mappedBy="poll", orphanRemoval=true, cascade={"persist", "remove"})
* @Serializer\Type("ArrayCollection<App\Entity\Vote>")
* @Serializer\Expose()
*/
public $votes;
/**
* @ORM\OneToMany(targetEntity="App\Entity\StackOfVotes", mappedBy="poll", orphanRemoval=true, cascade={"persist", "remove"})
* @Serializer\Expose()
*/
public $stacksOfVotes;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Choice", mappedBy="poll", orphanRemoval=true, cascade={"persist", "remove"})
* @Serializer\Expose()
* @Serializer\Type("ArrayCollection<App\Entity\Choice>")
*/
public $choices;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="poll", orphanRemoval=true, cascade={"persist", "remove"})
* @Serializer\Expose()
* @Serializer\Type("ArrayCollection<App\Entity\Comment>")
*/
public $comments;
/**
* vote restricted by a password in md5 format
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $password;
/**
* used to allow administration
* @ORM\Column(type="string", length=255)
* @Serializer\Type("string")
*/
private $adminKey;
/**
* number of days from now for default expiracy date