add flex reciepes in base folder

This commit is contained in:
Tykayn 2021-04-08 16:41:54 +02:00 committed by tykayn
parent da66dc88fb
commit 0a15f364a2
83 changed files with 7663 additions and 6 deletions

42
.env
View File

@ -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

View File

@ -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"
}
}

3943
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -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],
];

View File

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

View File

@ -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']

View File

@ -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

View File

@ -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%'

View File

@ -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 ] }

View File

@ -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"

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -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)%

View File

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

View File

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

View File

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

View File

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

13
doc/database_creation.md Normal file
View File

@ -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 Executable file
View File

@ -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 Executable file
View File

@ -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 Normal file
View File

@ -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 Executable file
View File

@ -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
```

View File

@ -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 Executable file
View File

@ -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 Executable file
View File

@ -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 Executable file
View File

@ -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 Normal file
View File

View File

@ -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',
]);
}
}

View File

@ -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',
]);
}
}

View File

@ -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;
}
}

View File

@ -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',
]);
}
}

View File

@ -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',
]);
}
}

View File

@ -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 Normal file
View File

145
src/Entity/Choice.php Executable file
View File

@ -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 Executable file
View File

@ -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 Executable file
View File

@ -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 Executable file
View File

@ -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
* @var int
* @Serializer\Expose()
*/
public $defaultExpiracyDaysFromNow = 60;
private $maxChoicesLimit = 25;
public function computeAnswers() {
// counts each number of answer for this choice
$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(),
'score' => 0,
'yes' => [
'count' => 0,
'people' => [],
],
'maybe' => [
'count' => 0,
'people' => [],
],
'no' => [
'count' => 0,
'people' => [],
],
];
}
$computedArray[ $choice_id ][ $answer ][ 'count' ] ++;
$computedArray[ $choice_id ][ $answer ][ 'people' ][] = $stack_of_vote->getOwner()->getPseudo();
if ( $answer == 'yes' ) {
$computedArray[ $choice_id ][ 'score' ] += 1;
} elseif ( $answer == 'maybe' ) {
$computedArray[ $choice_id ][ 'score' ] += 0.5;
}
// compare with max value
if ( $computedArray[ $choice_id ][ 'score' ] > $maxScore ) {
$maxScore = $computedArray[ $choice_id ][ 'score' ];
}
}
}
return [
'counts' => $computedArray,
'maxScore' => $maxScore,
];
}
public function display() {
return [
'poll' => $this,
'answers' => $this->computeAnswers(),
];
}
public function __construct() {
$this->votes = new ArrayCollection();
$this->stacksOfVotes = new ArrayCollection();
$this->choices = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->initiate();
}
private function initiate() {
$this->adminKey = $this->generateAdminKey();
$this->setCreationDate( new \DateTime() );
$this->setExpiracyDate( $this->addDaysToDate(
new \DateTime(),
$this->defaultExpiracyDaysFromNow
) );
$this->setAllowedAnswers( [ 'yes' ] );
}
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
*/
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 );
}
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;
}
public function addDaysToDate( \DateTime $date, int $days ) {
$st = strtotime( $date->getTimestamp() . ' + ' . $days . ' days' );
return new \DateTime( $st );
}
public function getId(): ?int {
return $this->id;
}
public function getTitle(): ?string {
return $this->title;
}
public function setTitle( string $title ): self {
$this->title = $title;
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;
}
public function setDescription( string $description ): self {
$this->description = $description;
return $this;
}
public function getKind(): ?string {
return $this->kind;
}
public function setKind( string $kind ): self {
$this->kind = $kind;
return $this;
}
public function getCustomUrl(): ?string {
return $this->customUrl;
}
public function setCustomUrl( string $customUrl ): self {
$this->customUrl = $customUrl;
return $this;
}
public function getPassword(): ?string {
return $this->password;
}
public function setPassword( string $password ): self {
$this->password = md5( $password );
return $this;
}
public function getModificationPolicy(): ?string {
return $this->modificationPolicy;
}
public function setModificationPolicy( string $modificationPolicy ): self {
$this->modificationPolicy = $modificationPolicy;
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;
}
public function setHideResults( bool $hideResults ): self {
$this->hideResults = $hideResults;
return $this;
}
public function getShowResultEvenIfPasswords(): ?bool {
return $this->showResultEvenIfPasswords;
}
public function setShowResultEvenIfPasswords( bool $showResultEvenIfPasswords ): self {
$this->showResultEvenIfPasswords = $showResultEvenIfPasswords;
return $this;
}
/**
* @return Collection|Comment[]
*/
public function getComments(): Collection {
return $this->comments;
}
public function addComment( Comment $comment ): self {
if ( ! $this->comments->contains( $comment ) ) {
$this->comments[] = $comment;
$comment->setPoll( $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->getPoll() === $this ) {
$comment->setPoll( null );
}
}
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;
$stackOfVote->setPoll( $this );
}
return $this;
}
public function removeStackOfVote( StackOfVotes $stackOfVote ): self {
if ( $this->stacksOfVotes->contains( $stackOfVote ) ) {
$this->stacksOfVotes->removeElement( $stackOfVote );
// set the owning side to null (unless already changed)
if ( $stackOfVote->getPoll() === $this ) {
$stackOfVote->setPoll( null );
}
}
return $this;
}
public function getExpiracyDate(): ?\DateTimeInterface {
return $this->expiracyDate;
}
public function addVote( Vote $vote ): self {
if ( ! $this->votes->contains( $vote ) ) {
$this->votes[] = $vote;
$vote->setPoll( $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->getPoll() === $this ) {
$vote->setPoll( null );
}
}
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();
$newChoice->setName( $text );
$this->addChoice( $newChoice );
}
return $this;
}
public function getAllowedAnswers(): ?array {
return $this->allowedAnswers;
}
public function setAllowedAnswers( array $allowedAnswers ): self {
$this->allowedAnswers = $allowedAnswers;
return $this;
}
public function addStacksOfVote( StackOfVotes $stacksOfVote ): self {
if ( ! $this->stacksOfVotes->contains( $stacksOfVote ) ) {
$this->stacksOfVotes[] = $stacksOfVote;
$stacksOfVote->setPoll( $this );
}
return $this;
}
public function removeStacksOfVote( StackOfVotes $stacksOfVote ): self {
if ( $this->stacksOfVotes->contains( $stacksOfVote ) ) {
$this->stacksOfVotes->removeElement( $stacksOfVote );
// set the owning side to null (unless already changed)
if ( $stacksOfVote->getPoll() === $this ) {
$stacksOfVote->setPoll( null );
}
}
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 );
// set the owning side to null (unless already changed)
if ( $choice->getPoll() === $this ) {
$choice->setPoll( null );
}
}
return $this;
}
}

145
src/Entity/StackOfVotes.php Executable file
View File

@ -0,0 +1,145 @@
<?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;
/**
* contains the votes for one answer to a poll
* @ORM\Entity(repositoryClass="App\Repository\StackOfVotesRepository")
* @ORM\HasLifecycleCallbacks()
* @Serializer\ExclusionPolicy("all")
*/
class StackOfVotes {
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
* @Serializer\Type("string")
* @Serializer\Expose()
*/
public $pseudo;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Vote", mappedBy="stacksOfVotes", cascade={"persist","remove"})
* @Serializer\Expose()
*/
public $votes;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Poll", inversedBy="stacksOfVotes", cascade={"persist"})
*/
private $poll;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Owner", inversedBy="stackOfVotes", cascade={"persist"})
* @Serializer\Expose()
*/
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(),
];
}
foreach ( $this->getVotes() as $vote ) {
$tab[ 'votes' ][ $vote->getChoice()->getId() ] = $vote->display();
$tab[ 'votes' ][ $vote->getChoice()->getId() ][ 'stack_id' ] = $this->getId();
$tab[ 'pseudo' ] = $this->getOwner()->getPseudo();
$tab[ 'creation_date' ] = $vote->getCreationDate();
}
return $tab;
}
/**
* @ORM\PrePersist
*/
public function prePersist() {
$this->setPseudo( $this->getOwner()->getPseudo() );
}
public function __construct() {
$this->votes = new ArrayCollection();
}
public function getId(): ?int {
return $this->id;
}
/**
* @return Collection|poll[]
*/
public function getVotes(): Collection {
return $this->votes;
}
public function addVote( Vote $vote ): self {
if ( ! $this->votes->contains( $vote ) ) {
$vote->setPoll( $this->getPoll() );
$this->votes[] = $vote;
$vote->setStacksOfVotes( $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->getStacksOfVotes() === $this ) {
$vote->setStacksOfVotes( null );
}
}
return $this;
}
public function getPseudo(): ?string {
return $this->pseudo;
}
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;
return $this;
}
}

124
src/Entity/Vote.php Executable file
View File

@ -0,0 +1,124 @@
<?php
namespace App\Entity;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as Serializer;
/**
* @ORM\Entity(repositoryClass="App\Repository\VoteRepository")
* @Serializer\ExclusionPolicy("all")
*/
class Vote {
/**
* 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
* @ORM\Column(type="string", length=255, nullable=true)
* @Serializer\Type("string")
* @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)
* @Serializer\Type("App\Entity\choice")
* @Serializer\Expose()
*/
public $choice;
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
* @Serializer\Type("integer")
* @Serializer\Expose()
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Poll", inversedBy="votes", cascade={"persist"})
* @ORM\JoinColumn(nullable=false)
*/
private $poll;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\StackOfVotes", inversedBy="votes", cascade={"persist"})
* @ORM\JoinColumn(nullable=false)
* @Serializer\Type("App\Entity\StackOfVotes")
*/
private $stacksOfVotes;
public function display() {
return [
'id' => $this->getId(),
'value' => $this->getValue(),
'choice_id' => $this->getChoice()->getId(),
'text' => $this->getChoice()->getName(),
];
}
public function __construct() {
$this->setCreationDate( new \DateTime() );
}
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;
}
public function setChoice( ?Choice $choice ): self {
$this->choice = $choice;
return $this;
}
public function getValue(): ?string {
return $this->value;
}
public function setValue( ?string $value ): self {
$this->value = $value;
return $this;
}
public function getCreationDate(): ?DateTimeInterface {
return $this->creationDate;
}
public function setCreationDate( DateTimeInterface $creationDate ): self {
$this->creationDate = $creationDate;
return $this;
}
public function getStacksOfVotes(): ?StackOfVotes {
return $this->stacksOfVotes;
}
public function setStacksOfVotes( ?StackOfVotes $stacksOfVotes ): self {
$this->stacksOfVotes = $stacksOfVotes;
return $this;
}
}

12
src/Entity/timedTrait.php Executable file
View File

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

0
src/Repository/.gitignore vendored Normal file
View File

View File

@ -0,0 +1,50 @@
<?php
namespace App\Repository;
use App\Entity\Choice;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method Choice|null find($id, $lockMode = null, $lockVersion = null)
* @method Choice|null findOneBy(array $criteria, array $orderBy = null)
* @method Choice[] findAll()
* @method Choice[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class ChoiceRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Choice::class);
}
// /**
// * @return Choice[] Returns an array of Choice objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('c')
->andWhere('c.exampleField = :val')
->setParameter('val', $value)
->orderBy('c.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Choice
{
return $this->createQueryBuilder('c')
->andWhere('c.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Repository;
use App\Entity\Comment;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method Comment|null find($id, $lockMode = null, $lockVersion = null)
* @method Comment|null findOneBy(array $criteria, array $orderBy = null)
* @method Comment[] findAll()
* @method Comment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class CommentRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Comment::class);
}
// /**
// * @return Comment[] Returns an array of Comment objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('c')
->andWhere('c.exampleField = :val')
->setParameter('val', $value)
->orderBy('c.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Comment
{
return $this->createQueryBuilder('c')
->andWhere('c.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Repository;
use App\Entity\Owner;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method Owner|null find($id, $lockMode = null, $lockVersion = null)
* @method Owner|null findOneBy(array $criteria, array $orderBy = null)
* @method Owner[] findAll()
* @method Owner[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class OwnerRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Owner::class);
}
// /**
// * @return Owner[] Returns an array of Owner objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('o')
->andWhere('o.exampleField = :val')
->setParameter('val', $value)
->orderBy('o.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Owner
{
return $this->createQueryBuilder('o')
->andWhere('o.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,52 @@
<?php
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)
* @method Poll|null findOneBy(array $criteria, array $orderBy = null)
* @method Poll[] findAll()
* @method Poll[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class PollRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry
$registry)
{
parent::__construct($registry, Poll::class);
}
// /**
// * @return Poll[] Returns an array of Poll objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->orderBy('p.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Poll
{
return $this->createQueryBuilder('p')
->andWhere('p.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Repository;
use App\Entity\StackOfVotes;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method StackOfVotes|null find($id, $lockMode = null, $lockVersion = null)
* @method StackOfVotes|null findOneBy(array $criteria, array $orderBy = null)
* @method StackOfVotes[] findAll()
* @method StackOfVotes[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class StackOfVotesRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, StackOfVotes::class);
}
// /**
// * @return StackOfVotes[] Returns an array of StackOfVotes objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('s')
->andWhere('s.exampleField = :val')
->setParameter('val', $value)
->orderBy('s.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?StackOfVotes
{
return $this->createQueryBuilder('s')
->andWhere('s.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Repository;
use App\Entity\Vote;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method Vote|null find($id, $lockMode = null, $lockVersion = null)
* @method Vote|null findOneBy(array $criteria, array $orderBy = null)
* @method Vote[] findAll()
* @method Vote[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class VoteRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Vote::class);
}
// /**
// * @return Vote[] Returns an array of Vote objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('v')
->andWhere('v.exampleField = :val')
->setParameter('val', $value)
->orderBy('v.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Vote
{
return $this->createQueryBuilder('v')
->andWhere('v.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}

View File

@ -1,4 +1,140 @@
{
"composer/package-versions-deprecated": {
"version": "1.11.99.1"
},
"doctrine/annotations": {
"version": "1.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.0",
"ref": "a2759dd6123694c8d901d0ec80006e044c2e6457"
},
"files": [
"config/routes/annotations.yaml"
]
},
"doctrine/cache": {
"version": "1.10.2"
},
"doctrine/collections": {
"version": "1.6.7"
},
"doctrine/common": {
"version": "3.1.2"
},
"doctrine/dbal": {
"version": "2.13.0"
},
"doctrine/deprecations": {
"version": "v0.5.3"
},
"doctrine/doctrine-bundle": {
"version": "2.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "2.3",
"ref": "9777bf185961283a450b6237281132935025fe04"
},
"files": [
"config/packages/doctrine.yaml",
"config/packages/prod/doctrine.yaml",
"config/packages/test/doctrine.yaml",
"src/Entity/.gitignore",
"src/Repository/.gitignore"
]
},
"doctrine/doctrine-migrations-bundle": {
"version": "3.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "3.1",
"ref": "ee609429c9ee23e22d6fa5728211768f51ed2818"
},
"files": [
"config/packages/doctrine_migrations.yaml",
"migrations/.gitignore"
]
},
"doctrine/event-manager": {
"version": "1.1.1"
},
"doctrine/inflector": {
"version": "2.0.3"
},
"doctrine/instantiator": {
"version": "1.4.0"
},
"doctrine/lexer": {
"version": "1.2.1"
},
"doctrine/migrations": {
"version": "3.1.1"
},
"doctrine/orm": {
"version": "2.8.4"
},
"doctrine/persistence": {
"version": "2.1.0"
},
"doctrine/sql-formatter": {
"version": "1.1.1"
},
"egulias/email-validator": {
"version": "3.1.1"
},
"friendsofphp/proxy-manager-lts": {
"version": "v1.0.3"
},
"friendsofsymfony/rest-bundle": {
"version": "2.2",
"recipe": {
"repo": "github.com/symfony/recipes-contrib",
"branch": "master",
"version": "2.2",
"ref": "cad41ef93d6150067ae2bb3c7fd729492dff6f0a"
},
"files": [
"config/packages/fos_rest.yaml"
]
},
"jms/metadata": {
"version": "2.5.0"
},
"jms/serializer": {
"version": "3.12.2"
},
"jms/serializer-bundle": {
"version": "3.0",
"recipe": {
"repo": "github.com/symfony/recipes-contrib",
"branch": "master",
"version": "3.0",
"ref": "384cec52df45f3bfd46a09930d6960a58872b268"
},
"files": [
"config/packages/dev/jms_serializer.yaml",
"config/packages/jms_serializer.yaml",
"config/packages/prod/jms_serializer.yaml"
]
},
"laminas/laminas-code": {
"version": "4.1.0"
},
"laminas/laminas-eventmanager": {
"version": "3.3.1"
},
"laminas/laminas-zendframework-bridge": {
"version": "1.2.0"
},
"nikic/php-parser": {
"version": "v4.10.4"
},
"phpstan/phpdoc-parser": {
"version": "0.4.14"
},
"psr/cache": {
"version": "2.0.0"
},
@ -11,6 +147,21 @@
"psr/log": {
"version": "1.1.3"
},
"sensio/framework-extra-bundle": {
"version": "5.2",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "5.2",
"ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b"
},
"files": [
"config/packages/sensio_framework_extra.yaml"
]
},
"swiftmailer/swiftmailer": {
"version": "v6.2.7"
},
"symfony/cache": {
"version": "v5.2.6"
},
@ -38,6 +189,9 @@
"symfony/deprecation-contracts": {
"version": "v2.2.0"
},
"symfony/doctrine-bridge": {
"version": "v5.2.6"
},
"symfony/dotenv": {
"version": "v5.2.4"
},
@ -68,6 +222,9 @@
".env"
]
},
"symfony/form": {
"version": "v5.2.6"
},
"symfony/framework-bundle": {
"version": "5.2",
"recipe": {
@ -97,9 +254,33 @@
"symfony/http-kernel": {
"version": "v5.2.6"
},
"symfony/intl": {
"version": "v5.2.4"
},
"symfony/maker-bundle": {
"version": "1.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "1.0",
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
}
},
"symfony/options-resolver": {
"version": "v5.2.4"
},
"symfony/orm-pack": {
"version": "v2.1.0"
},
"symfony/polyfill-intl-grapheme": {
"version": "v1.22.1"
},
"symfony/polyfill-intl-icu": {
"version": "v1.22.1"
},
"symfony/polyfill-intl-idn": {
"version": "v1.22.1"
},
"symfony/polyfill-intl-normalizer": {
"version": "v1.22.1"
},
@ -112,6 +293,15 @@
"symfony/polyfill-php80": {
"version": "v1.22.1"
},
"symfony/property-access": {
"version": "v5.2.4"
},
"symfony/property-info": {
"version": "v5.2.4"
},
"symfony/proxy-manager-bridge": {
"version": "v5.2.4"
},
"symfony/routing": {
"version": "5.1",
"recipe": {
@ -126,12 +316,52 @@
"config/routes.yaml"
]
},
"symfony/security-core": {
"version": "v5.2.6"
},
"symfony/service-contracts": {
"version": "v2.2.0"
},
"symfony/stopwatch": {
"version": "v5.2.4"
},
"symfony/string": {
"version": "v5.2.6"
},
"symfony/swiftmailer-bundle": {
"version": "2.5",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "2.5",
"ref": "ae4d22af30bbd484506bc1817c5a3ef72c855b93"
},
"files": [
"config/packages/dev/swiftmailer.yaml",
"config/packages/swiftmailer.yaml",
"config/packages/test/swiftmailer.yaml"
]
},
"symfony/translation-contracts": {
"version": "v2.3.0"
},
"symfony/twig-bridge": {
"version": "v5.2.6"
},
"symfony/twig-bundle": {
"version": "5.0",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "master",
"version": "5.0",
"ref": "fab9149bbaa4d5eca054ed93f9e1b66cc500895d"
},
"files": [
"config/packages/test/twig.yaml",
"config/packages/twig.yaml",
"templates/base.html.twig"
]
},
"symfony/var-dumper": {
"version": "v5.2.6"
},
@ -140,5 +370,14 @@
},
"symfony/yaml": {
"version": "v5.2.5"
},
"twig/twig": {
"version": "v3.3.0"
},
"willdurand/jsonp-callback-validator": {
"version": "v1.1.0"
},
"willdurand/negotiation": {
"version": "3.0.0"
}
}

View File

@ -0,0 +1,20 @@
{% extends 'base.html.twig' %}
{% block title %}Hello AdminController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code><a href="{{ '/var/www/html/sf5/labase/src/Controller/AdminController.php'|file_link(0) }}">src/Controller/AdminController.php</a></code></li>
<li>Your template at <code><a href="{{ '/var/www/html/sf5/labase/templates/admin/index.html.twig'|file_link(0) }}">templates/admin/index.html.twig</a></code></li>
</ul>
</div>
{% endblock %}

35
templates/base.html.twig Executable file
View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<base href="/">
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<link href="favicon.ico" rel="icon" type="image/x-icon"/>
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link rel="stylesheet" href="styles.css" crossorigin="anonymous">
<title>{% block title %}Framdate{% endblock %}</title>
{# {% block stylesheets %}#}
{# <link rel="stylesheet" href="{{ asset('build/vendors~app.css') }}">#}
{# <link rel="stylesheet" href="{{ asset('build/app.css') }}">#}
{# {% endblock %}#}
</head>
<body>
{% include 'split/header.html.twig' %}
{% block outerBody %}
<div class="container">
{% block body %}
{% endblock %}
</div>
{% endblock %}
{% include 'split/footer.html.twig' %}
{# {% block javascripts %} #}
{# <script href="{{ asset('build/vendors~app.js') }}"></script> #}
{# <script href="{{ asset('build/app.js') }}"></script> #}
{# {% endblock %} #}
</body>
</html>

View File

@ -0,0 +1,20 @@
{% extends 'base.html.twig' %}
{% block title %}Hello DefaultController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code><a href="{{ '/var/www/html/sf5/labase/src/Controller/DefaultController.php'|file_link(0) }}">src/Controller/DefaultController.php</a></code></li>
<li>Your template at <code><a href="{{ '/var/www/html/sf5/labase/templates/default/index.html.twig'|file_link(0) }}">templates/default/index.html.twig</a></code></li>
</ul>
</div>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% block title %}
{% if title is defined %}
<h1>{{ title }}</h1>
{% else %}
<h1>Framadate - email</h1>
{% endif %}
<hr>
{% endblock %}
{% block stylesheets %}{% endblock %}
{% block content %}
{% endblock %}
{% block footer %}
{% include 'emails/footer.html.twig' %}
{% endblock %}

View File

@ -0,0 +1,64 @@
<!doctype html>
<html lang="en">
<head>
</head>
<body>
<div class="email">
<header>
{% block title %}
{% if title is defined %}
<h1>{{ title }}</h1>
{% else %}
<h1>Framadate - email</h1>
{% endif %}
<hr>
{% endblock %}
{% block stylesheets %}{% endblock %}
</header>
<main>
{% block content %}
{% endblock %}
</main>
<footer>
{% block footer %}
{% include 'emails/footer.html.twig' %}
{% endblock %}
</footer>
</div>
<style type="text/css">
html, body, main, header, footer, div {
font-family: "Open Sans", "Helvetica Neue", sans-serif;
}
.email {
padding: 1em;
}
.footer-content {
margin-top: 1em;
background: #f2dff2;
}
ul {
border-left: 3px solid #f2dff2;
}
a {
padding: 1em;
border-radius: 3px;
background: #ceb4f5;
color: #201b27;
margin: 0.5rem 0;
display: inline-block;
}
h1, h2, h3 {
color: #3c334a;
}
</style>
</body>
</html>

View File

@ -0,0 +1,25 @@
{#[Framadate][Réservé à l'auteur] Sondage: TESSSSSSSSSST#}
{% extends 'email-base.html.twig' %}
{% block content %}
<h1>
Ce message ne doit PAS être diffusé aux sondés. Il est réservé à l'auteur du sondage.
</h1>
<br>
<h2>
Vous pouvez modifier ce sondage à l'adresse suivante :
</h2>
<br>
{% include 'emails/partial/admin_link.html.twig' %}
<br>
Pour partager votre sondage aux participants, utilisez son lien d'accès public que vous avez reçu dans un autre email.
<br>
{% if poll.password %}
Ce sondage est protégé par un mot de passe, n'oubliez pas de le communiquer à vos participants.
Vous pouvez changer ce mot de passe via l'administration.
{% else %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,22 @@
{#[Framadate] Notification d'un sondage : TESSSSSSSSSST#}
{% extends 'email-base.html.twig' %}
{% block content %}
<strong>
{{ owner.pseudo }}
</strong>
vient de rédiger un commentaire.
<blockquote style="background: #dedede; padding: 1em 2em;">
<i>
{% autoescape %}
{{ comment.text }}
{% endautoescape %}
</i>
</blockquote>
<br>
Vous pouvez retrouver votre sondage avec le lien suivant :
<br>
{% include 'emails/partial/admin_link.html.twig' %}
<br>
{% include 'emails/partial/public_link.html.twig' %}
{% endblock %}

View File

@ -0,0 +1,30 @@
{#[Framadate][Pour diffusion aux sondés] Sondage: TESSSSSSSSSST#}
{% extends 'email-base.html.twig' %}
{% block content %}
Suite à la création de votre sondage {{ title }} vous recevez deux emails afin de ne pas transmettre par erreur aux sondés le lien d'administration de votre sondage.
<br>
Ce mail est le premier, comportant le message qui doit être envoyé aux sondés.<br>
Vous pouvez maintenant transmettre ce message à toutes les personnes susceptibles de participer au vote.<br>
<br>
<fieldset>
<blockquote>
<i>
{{ owner.pseudo }} ( {{ owner.email }} ) vient de créer un sondage intitulé : " <strong>{{ title }}
"</strong>.
<br>
<br>
Merci de bien vouloir participer au sondage à l'adresse suivante :<br>
</i>
{% include 'emails/partial/public_link.html.twig' %}
</blockquote>
</fieldset>
{% endblock %}

View File

@ -0,0 +1,15 @@
{#[Framadate][expiration] Sondage: TESSSSSSSSSST#}
{% extends 'email-base.html.twig' %}
{% block content %}
Ce sondage va bientôt expirer dans 1 jour, <br>
<blockquote>
le {{ poll.expiracyDate | date('D Y-m-d') }}
</blockquote>
il ne sera plus possible d'y voter. <br>
Dans 31 jours il sera supprimé.<br>
Vous pouvez exporter ses données à tout moment en vous rendant à ce lien pour l'administrer:
<br>
{% include 'emails/partial/admin_link.html.twig' %}
{% endblock %}

View File

@ -0,0 +1,24 @@
<div class="footer-content" style="text-align:center; padding: 1em;">
Framadate est un logiciel libre, tout le monde peut
<a href="https://framateam.org/ux-framatrucs/channels/framadate">
l'améliorer.
</a>
<br>
Merci de votre confiance.
<br>
<a href="{{ BASE_URL }}">
Framadate {{ BASE_URL }}
</a>
<br>
<a href="https://framagit.org/framasoft/framadate/funky-framadate-front">
Sources du client Front end,
</a>
<a href="https://framagit.org/framasoft/framadate/framadate">
API back end.
</a>
<a href="https://framagit.org/framasoft/framadate/funky-framadate-front/-/wikis/home">
Documentation
</a>
</div>

View File

@ -0,0 +1,14 @@
{#[Framadate] Participation au sondage : TESSSSSSSSSST#}
{% extends 'email-base.html.twig' %}
{% block content %}
<strong>
{{ poll.owner.pseudo }} ,
{{ poll.owner.email }} , </strong>
vient de modifier votre sondage accessible au lien suivant:
<br>
{% include 'emails/partial/admin_link.html.twig' %}
<br>
lien public:
<br>
{% include 'emails/partial/public_link.html.twig' %}
{% endblock %}

View File

@ -0,0 +1,26 @@
{#[Framadate][Réservé à l'auteur] Sondage: TESSSSSSSSSST#}
{% extends 'email-base.html.twig' %}
{% block content %}
<h2>
Voici la liste des {{ owner.polls|length }} sondages
<a href="{{ BASE_URL }}">
Framadate
</a>
que vous avez créé.
</h2>
<div>
Si vous n'avez pas demandé à recevoir cet email, veuillez en informer les administrateurs du site Framadate.
</div>
<hr>
<ul style="list-style-type: none">
{% for poll in owner.polls %}
<li class="poll-element" style="border: solid 1px #ccc; padding: 1em; margin-top: 1em;">
{% include 'emails/partial/poll.html.twig' %}
</li>
{% endfor %}
</ul>
{% endblock %}

View File

@ -0,0 +1,2 @@
<a href="{{ BASE_URL }}/#/poll/{{ poll.id }}/admin/{{ poll.adminKey }}">{{ BASE_URL }}
/admin/{{ poll.adminKey }}</a>

View File

@ -0,0 +1,39 @@
<div class="poll">
<h1>
Sondage {{ poll.title }}
</h1>
<br>
<div class="creation">
créé le {{ poll.creationDate| date('Y m d ') }}
</div>
<div class="creation">
expirera le {{ poll.expiracyDate| date('Y m d ') }}
</div>
<span class="votes-count">
{{ poll.stacksOfVotes |length }} votes
</span>
<span class="votes-count">
{{ poll.comments |length }} commentaires
</span>
{% if poll.password |length %}
(accès avec mot de passe)
{% else %}
{% endif %}
<br>
<div class="public">
<span>
lien à donner aux votants:
</span>
{% include 'emails/partial/public_link.html.twig' %}
</div>
<div class="admin">
<span>
administration:
</span>
{% include 'emails/partial/admin_link.html.twig' %}
</div>
</div>

View File

@ -0,0 +1,11 @@
<span class="public-link">
{% if poll.customUrl |length %}
<a href="{{ BASE_URL }}/#/vote/poll/key/{{ poll.customUrl }}">
{{ BASE_URL }}/#/vote/poll/key/{{ poll.customUrl }}
</a>
{% else %}
<a href="{{ BASE_URL }}/#/vote/poll/id/{{ poll.id }}">
{{ BASE_URL }}/#/vote/poll/id/{{ poll.id }}
</a>
{% endif %}
</span>

View File

@ -0,0 +1,13 @@
{#[Framadate] Notification d'un sondage : TESSSSSSSSSST#}
{% extends 'email-base.html.twig' %}
{% block content %}
<strong>
{{ owner.pseudo }}
</strong>
vient de voter au sondage.
<br>
Vous pouvez retrouver votre sondage avec le lien suivant :
{% include 'emails/partial/admin_link.html.twig' %}
{% include 'emails/partial/public_link.html.twig' %}
{% endblock %}

67
templates/index.html.twig Executable file
View File

@ -0,0 +1,67 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Framadate</title>
<base href="/">
<meta
content="width=device-width, initial-scale=1"
name="viewport">
<link
href="favicon.ico"
rel="icon"
type="image/x-icon">
<link
rel="stylesheet"
href="assets/css/bootstrap-reboot-4.3.1.css">
<link
rel="stylesheet"
href="styles.css"
crossorigin="anonymous">
</head>
<body>
<app-root></app-root>
<footer id="main-footer" class="text-center">
<a href="https://framagit.org/tykayn/date-poll-api">
Sources
<i class="fa fa-gitlab"></i>
</a>
<a href="mailto:contact@cipherbliss.com">
Contact
<i class="fa fa-envelope"></i>
</a>
</footer>
<script
src="runtime-es2018.js"
crossorigin="anonymous"
type="module"></script>
<script
src="runtime-es5.js"
crossorigin="anonymous"
nomodule
defer></script>
<script
src="polyfills-es5.js"
crossorigin="anonymous"
nomodule
defer></script>
<script
src="polyfills-es2018.js"
crossorigin="anonymous"
type="module"></script>
<script
src="scripts.js"
crossorigin="anonymous"
defer></script>
<script
src="main-es2018.js"
crossorigin="anonymous"
type="module"></script>
<script
src="main-es5.js"
crossorigin="anonymous"
nomodule
defer></script>
</body>
</html>

View File

@ -0,0 +1,20 @@
{% extends 'base.html.twig' %}
{% block title %}Hello MigrationController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code><a href="{{ '/var/www/html/sf5/labase/src/Controller/MigrationController.php'|file_link(0) }}">src/Controller/MigrationController.php</a></code></li>
<li>Your template at <code><a href="{{ '/var/www/html/sf5/labase/templates/migration/index.html.twig'|file_link(0) }}">templates/migration/index.html.twig</a></code></li>
</ul>
</div>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends 'base.html.twig' %}
{% block title %}Hello OwnerController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code><a href="{{ '/var/www/html/sf5/labase/src/Controller/OwnerController.php'|file_link(0) }}">src/Controller/OwnerController.php</a></code></li>
<li>Your template at <code><a href="{{ '/var/www/html/sf5/labase/templates/owner/index.html.twig'|file_link(0) }}">templates/owner/index.html.twig</a></code></li>
</ul>
</div>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends 'base.html.twig' %}
{% block outerBody %}
<h1>Bienvenue à la maison!</h1>
<section class="home">
<app-root></app-root>
<script src="runtime.js" crossorigin="anonymous" defer></script>
<script src="es5-polyfills.js" crossorigin="anonymous" nomodule defer></script>
<script src="other-polyfills.js" crossorigin="anonymous" defer></script>
<script src="scripts.js" crossorigin="anonymous" defer></script>
<script src="main.js" crossorigin="anonymous" defer></script>
</section>
{% endblock %}

View File

@ -0,0 +1,5 @@
{% extends 'base.html.twig' %}
{% block outerBody %}
<h1>Bienvenue à la maison!</h1>
{% endblock %}

View File

@ -0,0 +1,5 @@
<form method="post" action="{{ path('poll_delete', {'id': poll.id}) }}" onsubmit="return confirm('Are you sure you want to delete this item?');">
<input type="hidden" name="_method" value="DELETE">
<input type="hidden" name="_token" value="{{ csrf_token('delete' ~ poll.id) }}">
<button class="btn">Delete</button>
</form>

View File

@ -0,0 +1,4 @@
{{ form_start(form) }}
{{ form_widget(form) }}
<button class="btn">{{ button_label|default('Save') }}</button>
{{ form_end(form) }}

View File

@ -0,0 +1,13 @@
{% extends 'base.html.twig' %}
{% block title %}Edit Poll{% endblock %}
{% block body %}
<h1>Edit Poll</h1>
{{ include('poll/_form.html.twig', {'button_label': 'Update'}) }}
<a href="{{ path('poll_index') }}">back to list</a>
{{ include('poll/_delete_form.html.twig') }}
{% endblock %}

View File

@ -0,0 +1,65 @@
{% extends 'base.html.twig' %}
{% block title %}Poll index{% endblock %}
{% block body %}
<h1>Poll index</h1>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Title</th>
<th>CustomUrl</th>
<th>Description</th>
<th>CreationDate</th>
<th>ExpiracyDate</th>
<th>Kind</th>
<th>AllowedAnswers</th>
<th>ModificationPolicy</th>
<th>MailOnComment</th>
<th>MailOnVote</th>
<th>HideResults</th>
<th>ShowResultEvenIfPasswords</th>
<th>Password</th>
<th>AdminKey</th>
<th>actions</th>
</tr>
</thead>
<tbody>
{% for poll in polls %}
<tr>
<td>{{ poll.id }}</td>
<td>{{ poll.title }}</td>
<td>{{ poll.customUrl }}</td>
<td>{{ poll.description }}</td>
<td>{{ poll.creationDate ? poll.creationDate|date('Y-m-d H:i:s') : '' }}</td>
<td>{{ poll.expiracyDate ? poll.expiracyDate|date('Y-m-d H:i:s') : '' }}</td>
<td>{{ poll.kind }}</td>
<td>{{ poll.allowedAnswers ? poll.allowedAnswers|join(', ') : '' }}</td>
<td>{{ poll.modificationPolicy }}</td>
<td>{{ poll.mailOnComment ? 'Yes' : 'No' }}</td>
<td>{{ poll.mailOnVote ? 'Yes' : 'No' }}</td>
<td>{{ poll.hideResults ? 'Yes' : 'No' }}</td>
<td>{{ poll.showResultEvenIfPasswords ? 'Yes' : 'No' }}</td>
<td>
{% if poll.password %}
(***) oui, caché
{% endif %}
</td>
<td>{{ poll.adminKey }}</td>
<td>
<a href="{{ path('poll_show', {'id': poll.id}) }}">show</a>
<a href="{{ path('poll_edit', {'id': poll.id}) }}">edit</a>
</td>
</tr>
{% else %}
<tr>
<td colspan="16">no records found</td>
</tr>
{% endfor %}
</tbody>
</table>
<a href="{{ path('poll_new') }}">Create new</a>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends 'base.html.twig' %}
{% block title %}New Poll{% endblock %}
{% block body %}
<h1>Create new Poll</h1>
{{ include('poll/_form.html.twig') }}
<a href="{{ path('poll_index') }}">back to list</a>
{% endblock %}

View File

@ -0,0 +1,78 @@
{% extends 'base.html.twig' %}
{% block title %}Poll{% endblock %}
{% block body %}
<h1>Poll</h1>
<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>{{ poll.id }}</td>
</tr>
<tr>
<th>Title</th>
<td>{{ poll.title }}</td>
</tr>
<tr>
<th>CustomUrl</th>
<td>{{ poll.customUrl }}</td>
</tr>
<tr>
<th>Description</th>
<td>{{ poll.description }}</td>
</tr>
<tr>
<th>CreationDate</th>
<td>{{ poll.creationDate ? poll.creationDate|date('Y-m-d H:i:s') : '' }}</td>
</tr>
<tr>
<th>ExpiracyDate</th>
<td>{{ poll.expiracyDate ? poll.expiracyDate|date('Y-m-d H:i:s') : '' }}</td>
</tr>
<tr>
<th>Kind</th>
<td>{{ poll.kind }}</td>
</tr>
<tr>
<th>AllowedAnswers</th>
<td>{{ poll.allowedAnswers ? poll.allowedAnswers|join(', ') : '' }}</td>
</tr>
<tr>
<th>ModificationPolicy</th>
<td>{{ poll.modificationPolicy }}</td>
</tr>
<tr>
<th>MailOnComment</th>
<td>{{ poll.mailOnComment ? 'Yes' : 'No' }}</td>
</tr>
<tr>
<th>MailOnVote</th>
<td>{{ poll.mailOnVote ? 'Yes' : 'No' }}</td>
</tr>
<tr>
<th>HideResults</th>
<td>{{ poll.hideResults ? 'Yes' : 'No' }}</td>
</tr>
<tr>
<th>ShowResultEvenIfPasswords</th>
<td>{{ poll.showResultEvenIfPasswords ? 'Yes' : 'No' }}</td>
</tr>
<tr>
<th>Password</th>
<td>{{ poll.password }}</td>
</tr>
<tr>
<th>AdminKey</th>
<td>{{ poll.adminKey }}</td>
</tr>
</tbody>
</table>
<a href="{{ path('poll_index') }}">back to list</a>
<a href="{{ path('poll_edit', {'id': poll.id}) }}">edit</a>
{{ include('poll/_delete_form.html.twig') }}
{% endblock %}

View File

@ -0,0 +1,14 @@
{% block footer %}
<footer>
<nav>
<ul>
<li>
<a href="/">
<i class="fa fa-home"></i>
</a>
</li>
</ul>
</nav>
</footer>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% block header %}
<header class="bg-purple-300 p-4 block">
<div class="container">
<nav>
<ul>
<li>
<a class="btn button rounded bg-purple-200 p-2" href="/">
<i class="fa fa-home"></i>
<img src="{{ WEBSITE_LOGO }}" alt="logo">
{{ WEBSITE_NAME }}
</a>
</li>
</ul>
</nav>
</div>
</header>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends 'base.html.twig' %}
{% block title %}Hello VoteController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
This friendly message is coming from:
<ul>
<li>Your controller at <code><a href="{{ '/var/www/html/sf5/labase/src/Controller/VoteController.php'|file_link(0) }}">src/Controller/VoteController.php</a></code></li>
<li>Your template at <code><a href="{{ '/var/www/html/sf5/labase/templates/vote/index.html.twig'|file_link(0) }}">templates/vote/index.html.twig</a></code></li>
</ul>
</div>
{% endblock %}

131
translations/en.json Executable file
View File

@ -0,0 +1,131 @@
{
"Language": "Language",
"Title": "Translation example",
"Intro": "Hello I am {{name}}, I am {{age}} years old.",
"home": {
"title": "home's title"
},
"config": {
"demo": "this is a demo",
"demo_other": "this is an other demo",
"continue": "Continue",
"perfect": "That's perfect",
"title": "Create a poll",
"letsgo": "Lets go!",
"description": "Shedule appointments with your friends or your family, or create a pool with text, pictures or links… A real survey!",
"find_my_polls": "Where are my polls ?",
"find_helper": "I am looking for polls linked to my email",
"find_button": "Find my polls"
},
"creation": {
"title": "To begin with",
"want": "I want to create a poll",
"kind": {
"classic": "classic",
"date": "special dates"
},
"choose_title": "Whom title will be",
"choose_title_placeholder": "title",
"name": "I can also specify my name if i want",
"name_placeholder": "my name",
"description": "and the description would be",
"description_placeholder": "summary of the poll's purpose"
},
"dates": {
"title": "Config especially for the dates",
"hours_different": "I want to put",
"hours_each_day": "slots for each day",
"multiple": {
"identical": "the same",
"different": "possibly different"
},
"add": "Add a date choice",
"add_time": "Add a schedule proposal",
"empty": "Empty",
"count_dates": "choices of dates",
"count_time": "choices of schedules",
"add_interval": "Add a date interval",
"interval_propose": "I want to suggest all the dates from",
"interval_span": "to",
"interval_button": "Add these",
"interval_button_dates": "dates"
},
"choices": {
"title": "Write the proposals",
"helper": "You can use markdown syntax",
"answer_preset_1": "Demo answer 1",
"answer_preset_2": "answer 2",
"answer_preset_3": "and D, the answer D",
"add": "Add",
"continue": "Let's see how it goes"
},
"resume": {
"title": "And that's all for us!",
"admins": "Admin side",
"users": "Respondent side",
"links_mail": "Receive links by email"
},
"visibility": {
"top_txt": "A few settings...",
"title": "Visibility of answers",
"visibility_want": "I want that",
"visibility_link": "anybody having the link",
"visibility_nobody": "nobody",
"visibility_see": "can see answers to the pool.",
"votes": "Votes",
"votes_possible": "Possible answers will be",
"votes_possible_single": "only \"yes\"",
"votes_possible_normal": "\"yes\" or \"maybe\"",
"votes_possible_full": "\"yes\" or \"maybe\" or \"no\"",
"archiving": "Polled people will be able to vote until",
"archiving_start": "They",
"archiving_can": "will",
"archiving_can_not": "will not",
"archiving_end": "be able to edit",
"modfiy_their": "their vote",
"modfiy_everyone": "all the votes",
"access": "Access to the poll",
"access_url": "Pooled people will be able to access it via this address:",
"access_url_key": "URL",
"see_pass": "see",
"access_instructions": "may contain letters, numbers and dashes",
"access_want": "I",
"access_want_yes": "want",
"access_want_no": "do not need",
"access_protect": "it to be password-protected.",
"validate_btn": "Create this poll!"
},
"admin": {
"choose_title": "The pool title is",
"description": "and its description is",
"info_section_title": "Pool informations",
"settings_section_title": "Settings",
"votes_deletion_desc": "To start over from scratch, I can delete them all",
"votes_deletion_btn": "Delete all the votes",
"comments_deletion_title": "Comments",
"comments_deletion_desc": "If I wish, I can delete all the comments",
"comments_deletion_btn": "Delete all the comments",
"archiving_title": "Archiving",
"archiving_desc": "This poll will no longer be editable from",
"deletion": "Delete all",
"deletion_desc": "In the case you want do delete everything, this button is for you:",
"deletion_btn": "Delete the poll",
"link": "Links to access the survey",
"link_admin": "Administrator's side",
"link_admin_desc": "To access the poll and all its settings",
"link_admin_btn": "To see the poll as an administrator",
"copy_link": "Copy the link",
"polled_people": "Polled side",
"polled_people_desc": "To access the poll and all its settings",
"polled_people_btn": "See the poll",
"email_links": "Receive the links by email",
"email_links_desc": "To be sure to find these links, they can be sent to you by email",
"email_links_key": "email",
"email_links_btn": "Send the links to the poll"
},
"pollGraphic": {
"choiceColorblind": "I am",
"choiceNotColorblind": "I am not",
"colorblindText": "colorblind."
}
}

131
translations/fr.json Executable file
View File

@ -0,0 +1,131 @@
{
"Language": "Langue",
"Title": "Exemple de traduction",
"Intro": "Bonjour je m'appelle {{name}}, j'ai {{age}} ans.",
"home": {
"title": "le titre de la home"
},
"config": {
"demo": "C'est une démo traduite en Français",
"demo_other": "C'est une <strong>autre</strong> démo traduite en Français",
"continue": "Continuer",
"perfect": "C'est parfait",
"title": "Créer un sondage",
"letsgo": "C'est parti !",
"description": "Planifiez des rendez-vous avec vos amis ou votre famille ou créez un sondage avec du texte, des images ou des liens… un sondage quoi !",
"find_my_polls": "Mes sondages",
"find_helper": "Je cherche d'autres sondages qui correspondent à mon mail",
"find_button": "Retrouver mes sondages"
},
"creation": {
"title": "Créer un sondage",
"want": "Je veux créer un sondage",
"kind": {
"classic": "classique",
"date": "spécial dates"
},
"choose_title": "Dont le titre sera",
"choose_title_placeholder": "titre",
"name": "Je peux aussi préciser mon nom si je le souhaite",
"name_placeholder": "mon nom",
"description": "et la description serait",
"description_placeholder": "description"
},
"dates": {
"title": "Config spécialement pour les dates",
"hours_different": "Je souhaite mettre des créneaux horaires",
"hours_each_day": "pour chaque journée",
"multiple": {
"identical": "identiques",
"different": "possiblement différents"
},
"add": "Ajouter une plage de dates",
"add_time": "Ajouter une plage horaire",
"empty": "Vider",
"count_dates": "choix de dates",
"count_time": "choix de plages horaires",
"add_interval": "Ajouter un intervalle de dates",
"interval_propose": "Je souhaite proposer pour mon sondage toutes les dates entre le",
"interval_span": "et le",
"interval_button": "Ajouter ces",
"interval_button_dates": "dates"
},
"choices": {
"title": "Choisir les propositions",
"helper": "vous pouvez utiliser la syntaxe markdown ",
"answer_preset_1": "réponse de démo 1",
"answer_preset_2": "réponse 2",
"answer_preset_3": "la réponse D",
"add": "Ajouter",
"continue": "Voyons ce que ça donne"
},
"resume": {
"title": "Et c'est tout pour nous !",
"admins": "Côté administrateur-ice-eux",
"users": "Côté sondés",
"links_mail": "Recevoir les liens par e-mail"
},
"visibility": {
"top_txt": "Un peu de paramétrage…",
"title": "Visibilité des réponses",
"visibility_want": "Je veux que",
"visibility_link": "tous ceux qui ont le lien puissent",
"visibility_nobody": "seulement moi puisse",
"visibility_see": "voir les réponses au sondage.",
"votes": "Votes",
"votes_possible": "Les réponses possibles seront :",
"votes_possible_single": "Uniquement « oui »",
"votes_possible_normal":  oui » ou « peut-être »",
"votes_possible_full":  oui » ou « peut-être » ou « non »",
"archiving": "Les personnes sondées pourront voter jusquau",
"archiving_start": "Elles",
"archiving_can": "auront",
"archiving_can_not": "n'auront pas",
"archiving_end": "la possibilité de modifier",
"modfiy_their": "leur vote",
"modfiy_everyone": "tous les votes",
"access": "Accès au sondage",
"access_url": "Les votants pourront y accéder via cette adresse :",
"access_url_key": "URL",
"see_pass": "voir",
"access_instructions": "peut contenir des lettres, chiffres et des tirets",
"access_want": "Je",
"access_want_yes": "souhaite",
"access_want_no": "n'ai pas besoin",
"access_protect": "quil soit protégé par un mot de passe.",
"validate_btn": "Créer ce sondage !"
},
"admin": {
"choose_title": "Le titre du sondage est",
"description": "et sa description :",
"info_section_title": "Informations du sondage",
"settings_section_title": "Paramètres",
"votes_deletion_desc": "Pour repartir à zéro, je peux tous les supprimer :",
"votes_deletion_btn": "Supprimer tous les votes",
"comments_deletion_title": "Commentaires",
"comments_deletion_desc": "Si je le souhaite, je peux supprimer l'ensemble des commentaires",
"comments_deletion_btn": "Supprimer tous les commentaires",
"archiving_title": "Archivage",
"archiving_desc": "Ce sondage ne sera plus éditable à partir du",
"deletion": "Tout supprimer",
"deletion_desc": "Dans le cas où vous voudriez tout supprimer, ce bouton est fait pour vous :",
"deletion_btn": "Supprimer le sondage",
"link": "Liens daccès au sondage",
"link_admin": "Coté administrateur·ice",
"link_admin_desc": "Pour accéder au sondage et à tous ses paramètres :",
"link_admin_btn": "Voir le sondage coté administrateur·ice",
"copy_link": "Copier le lien",
"polled_people": "Côté sondé·es",
"polled_people_desc": "Pour accéder au sondage et à tous ses paramètres :",
"polled_people_btn": "Voir le sondage",
"email_links": "Recevoir les liens par mail",
"email_links_desc": "Pour être sûr de retrouver ces liens, nous pouvons vous les envoyer sur votre boîte mail.",
"email_links_key": "mail",
"email_links_btn": "Envoyer les liens du sondage"
},
"pollGraphic": {
"choiceColorblind": "Je suis",
"choiceNotColorblind": "Je ne suis pas",
"colorblindText": "daltonien."
}
}