]> Raphaël G. Git Repositories - airbundle/commitdiff
Initial import
authorRaphaël Gertz <git@rapsys.eu>
Fri, 25 Oct 2019 14:33:24 +0000 (16:33 +0200)
committerRaphaël Gertz <git@rapsys.eu>
Fri, 25 Oct 2019 14:33:24 +0000 (16:33 +0200)
56 files changed:
Controller/DefaultController.php [new file with mode: 0644]
DataFixtures/AirFixtures.php [new file with mode: 0644]
DependencyInjection/Configuration.php [new file with mode: 0644]
DependencyInjection/RapsysAirExtension.php [new file with mode: 0644]
Entity/Application.php [new file with mode: 0644]
Entity/Group.php [new file with mode: 0644]
Entity/Location.php [new file with mode: 0644]
Entity/Session.php [new file with mode: 0644]
Entity/Slot.php [new file with mode: 0644]
Entity/Title.php [new file with mode: 0644]
Entity/User.php [new file with mode: 0644]
Entity/Vote.php [new file with mode: 0644]
Form/ApplicationType.php [new file with mode: 0644]
Form/ContactType.php [new file with mode: 0644]
RapsysAirBundle.php [new file with mode: 0644]
Repository/ApplicationRepository.php [new file with mode: 0644]
Repository/SessionRepository.php [new file with mode: 0644]
Resources/config/doctrine/Application.orm.yml [new file with mode: 0644]
Resources/config/doctrine/Group.orm.yml [new file with mode: 0644]
Resources/config/doctrine/Location.orm.yml [new file with mode: 0644]
Resources/config/doctrine/Session.orm.yml [new file with mode: 0644]
Resources/config/doctrine/Slot.orm.yml [new file with mode: 0644]
Resources/config/doctrine/Title.orm.yml [new file with mode: 0644]
Resources/config/doctrine/User.orm.yml [new file with mode: 0644]
Resources/config/doctrine/Vote.orm.yml [new file with mode: 0644]
Resources/config/routing.yml [new file with mode: 0644]
Resources/config/services.yml [new file with mode: 0644]
Resources/public/css/reset.css [new file with mode: 0644]
Resources/public/css/screen.css [new file with mode: 0644]
Resources/public/ico/favicon.ico [new file with mode: 0644]
Resources/public/png/apple.png [new file with mode: 0644]
Resources/public/png/favicon.png [new file with mode: 0644]
Resources/public/png/logo.png [new file with mode: 0644]
Resources/public/svg/favicon.svg [new file with mode: 0644]
Resources/public/svg/logo.svg [new file with mode: 0644]
Resources/public/xcf/openair.xcf [new file with mode: 0644]
Resources/translations/messages.fr.yml [new file with mode: 0644]
Resources/translations/validators.fr.yml [new file with mode: 0644]
Resources/views/admin/index.html.twig [new file with mode: 0644]
Resources/views/admin/index.html.twig.toto [new file with mode: 0644]
Resources/views/admin/session.html.twig [new file with mode: 0644]
Resources/views/base.html.twig [new file with mode: 0644]
Resources/views/form/contact.html.twig [new file with mode: 0644]
Resources/views/form/form_div_layout.html.twig [new file with mode: 0644]
Resources/views/form/login.html.twig [new file with mode: 0644]
Resources/views/form/recover.html.twig [new file with mode: 0644]
Resources/views/form/recover_mail.html.twig [new file with mode: 0644]
Resources/views/form/register.html.twig [new file with mode: 0644]
Resources/views/mail/contact.html.twig [new file with mode: 0644]
Resources/views/mail/generic.html.twig [new file with mode: 0644]
Resources/views/mail/register.html.twig [new file with mode: 0644]
Resources/views/page/index.html.twig [new file with mode: 0644]
Tests/Controller/DefaultControllerTest.php [new file with mode: 0644]
Twig/Base64Extension.php [new file with mode: 0644]
Twig/Bb2htmlExtension.php [new file with mode: 0644]
Twig/FileGetContentsExtension.php [new file with mode: 0644]

diff --git a/Controller/DefaultController.php b/Controller/DefaultController.php
new file mode 100644 (file)
index 0000000..31d4b89
--- /dev/null
@@ -0,0 +1,305 @@
+<?php
+
+namespace Rapsys\AirBundle\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Rapsys\AirBundle\Entity\Session;
+use Rapsys\AirBundle\Entity\Application;
+use Symfony\Component\Form\FormError;
+
+class DefaultController extends Controller {
+       public function contactAction(Request $request) {
+               //Get translator
+               $trans = $this->get('translator');
+
+               //Set section
+               $section = $trans->trans('Contact');
+
+               //Set title
+               $title = $section.' - '.$trans->trans($this->getParameter('rapsys_air.title'));
+
+               //Create the form according to the FormType created previously.
+               //And give the proper parameters
+               $form = $this->createForm('Rapsys\AirBundle\Form\ContactType', null, [
+                       // To set the action use $this->generateUrl('route_identifier')
+                       'action' => $this->generateUrl('rapsys_air_contact'),
+                       'method' => 'POST'
+               ]);
+
+               if ($request->isMethod('POST')) {
+                       // Refill the fields in case the form is not valid.
+                       $form->handleRequest($request);
+
+                       if ($form->isValid()) {
+                               //Get data
+                               $data = $form->getData();
+
+                               //Get contact name
+                               $contactName = $this->getParameter('rapsys_air.contact_name');
+
+                               //Get contact mail
+                               $contactMail = $this->getParameter('rapsys_air.contact_mail');
+
+                               //Get logo
+                               $logo = $this->getParameter('rapsys_air.logo');
+
+                               //Get title
+                               $title = $trans->trans($this->getParameter('rapsys_air.title'));
+
+                               //Get subtitle
+                               $subtitle = $trans->trans('Hi,').' '.$contactName;
+
+                               $message = \Swift_Message::newInstance()
+                                       ->setSubject($data['subject'])
+                                       ->setFrom([$data['mail'] => $data['name']])
+                                       ->setTo([$contactMail => $contactName])
+                                       ->setBody($data['message'])
+                                       ->addPart(
+                                               $this->renderView(
+                                                       '@RapsysAir/mail/generic.html.twig',
+                                                       [
+                                                               'logo' => $logo,
+                                                               'title' => $title,
+                                                               'subtitle' => $subtitle,
+                                                               'home' => $this->get('router')->generate('rapsys_air_homepage', [], UrlGeneratorInterface::ABSOLUTE_URL),
+                                                               'subject' => $data['subject'],
+                                                               'contact_name' => $contactName,
+                                                               'message' => strip_tags($data['message'])
+                                                       ]
+                                               ),
+                                               'text/html'
+                                       );
+                               //Send message
+                               if ($this->get('mailer')->send($message)) {
+                                       //Redirect to cleanup the form
+                                       return $this->redirectToRoute('rapsys_air_contact', ['sent' => 1]);
+                               }
+                       }
+               }
+
+               //Render template
+               return $this->render('@RapsysAir/form/contact.html.twig', ['title' => $title, 'section' => $section, 'form' => $form->createView(), 'sent' => $request->query->get('sent', 0)]);
+       }
+
+       public function indexAction() {
+               //Get translator
+               $trans = $this->get('translator');
+
+               //Set section
+               $section = $trans->trans('Index');
+
+               //Set title
+               $title = $section.' - '.$trans->trans($this->getParameter('rapsys_air.title'));
+
+               return $this->render('@RapsysAir/page/index.html.twig', ['title' => $title, 'section' => $section]);
+       }
+
+       public function adminAction(Request $request) {
+               $this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');
+
+               //Get translator
+               $trans = $this->get('translator');
+
+               //Set section
+               $section = $trans->trans('Admin');
+
+               //Set title
+               $title = $section.' - '.$trans->trans($this->getParameter('rapsys_air.title'));
+
+               //Create the form according to the FormType created previously.
+               //And give the proper parameters
+               $form = $this->createForm('Rapsys\AirBundle\Form\ApplicationType', null, [
+                       // To set the action use $this->generateUrl('route_identifier')
+                       'action' => $this->generateUrl('rapsys_air_admin'),
+                       'method' => 'POST',
+                       'attr' => [ 'class' => 'form_col' ]
+               ]);
+
+               //Get doctrine
+               $doctrine = $this->getDoctrine();
+
+               //Handle request
+               if ($request->isMethod('POST')) {
+                       // Refill the fields in case the form is not valid.
+                       $form->handleRequest($request);
+
+                       if ($form->isValid()) {
+                               //Get data
+                               $data = $form->getData();
+
+                               //Get manager
+                               $manager = $doctrine->getManager();
+
+                               //Protect session fetching
+                               try {
+                                       $session = $doctrine->getRepository(Session::class)->findOneByLocationSlotDate($data['location'], $data['slot'], $data['date']);
+                               //Catch no session case
+                               } catch (\Doctrine\ORM\NoResultException $e) {
+                                       //Create the session
+                                       $session = new Session();
+                                       $session->setLocation($data['location']);
+                                       $session->setSlot($data['slot']);
+                                       $session->setDate($data['date']);
+                                       $session->setCreated(new \DateTime('now'));
+                                       $session->setUpdated(new \DateTime('now'));
+                                       $manager->persist($session);
+                                       //Flush to get the ids
+                                       #$manager->flush();
+                               }
+
+                               //Init application
+                               $application = false;
+
+                               //Protect application fetching
+                               try {
+                                       //TODO: handle admin case where we provide a user in extra
+                                       $application = $doctrine->getRepository(Application::class)->findOneBySessionUser($session, $this->getUser());
+
+                                       //Add error message to mail field
+                                       $form->get('slot')->addError(new FormError($trans->trans('Application already exists')));
+                               //Catch no application cases
+                               //XXX: combine these catch when php 7.1 is available
+                               } catch (\Doctrine\ORM\NoResultException $e) {
+                               //Catch invalid argument because session is not already persisted
+                               } catch(\Doctrine\ORM\ORMInvalidArgumentException $e) {
+                               }
+
+                               //Create new application if none found
+                               if (!$application) {
+                                       //Create the application
+                                       $application = new Application();
+                                       $application->setSession($session);
+                                       //TODO: handle admin case where we provide a user in extra
+                                       $application->setUser($this->getUser());
+                                       $application->setCreated(new \DateTime('now'));
+                                       $application->setUpdated(new \DateTime('now'));
+                                       $manager->persist($application);
+
+                                       //Flush to get the ids
+                                       $manager->flush();
+
+                                       //Add notice in flash message
+                                       $this->addFlash('notice', $trans->trans('Application request the %date% for %location% on the slot %slot% saved', ['%location%' => $data['location']->getTitle(), '%slot%' => $data['slot']->getTitle(), '%date%' => $data['date']->format('Y-m-d')]));
+
+                                       //Redirect to cleanup the form
+                                       return $this->redirectToRoute('rapsys_air_admin');
+                               }
+                       }
+               }
+
+               //Compute period
+               $period = new \DatePeriod(
+                       //Start from first monday of week
+                       new \DateTime('Monday this week'),
+                       //Iterate on each day
+                       new \DateInterval('P1D'),
+                       //End with next sunday and 4 weeks
+                       new \DateTime('Monday this week + 5 week')
+               );
+
+               //Fetch sessions
+               $sessions = $doctrine->getRepository(Session::class)->findByDatePeriod($period);
+
+               //Init calendar
+               $calendar = [];
+               
+               //Init month
+               $month = null;
+
+               //Iterate on each day
+               foreach($period as $date) {
+                       //Init day in calendar
+                       $calendar[$Ymd = $date->format('Ymd')] = [
+                               'title' => $date->format('d'),
+                               'class' => [],
+                               'sessions' => []
+                       ];
+                       //Append month for first day of month
+                       if ($month != $date->format('m')) {
+                               $month = $date->format('m');
+                               $calendar[$Ymd]['title'] .= '/'.$month;
+                       }
+                       //Deal with today
+                       if ($date->format('U') == ($today = strtotime('today'))) {
+                               $calendar[$Ymd]['title'] .= '/'.$month;
+                               $calendar[$Ymd]['current'] = true;
+                               $calendar[$Ymd]['class'][] =  'current';
+                       }
+                       //Disable passed days
+                       if ($date->format('U') < $today) {
+                               $calendar[$Ymd]['disabled'] = true;
+                               $calendar[$Ymd]['class'][] =  'disabled';
+                       }
+                       //Set next month days
+                       if ($date->format('m') > date('m')) {
+                               $calendar[$Ymd]['next'] = true;
+                               $calendar[$Ymd]['class'][] =  'next';
+                       }
+                       //Iterate on each session to find the one of the day
+                       foreach($sessions as $session) {
+                               if (($sessionYmd = $session->getDate()->format('Ymd')) == $Ymd) {
+                                       //Count number of application
+                                       $count = count($session->getApplications());
+
+                                       //Compute classes
+                                       $class = [];
+                                       if ($session->getApplication()) {
+                                               $class[] = 'granted';
+                                       } elseif ($count == 0) {
+                                               $class[] = 'orphaned';
+                                       } elseif ($count > 1) {
+                                               $class[] = 'disputed';
+                                       } else {
+                                               $class[] = 'pending';
+                                       }
+
+                                       //Add the session
+                                       $calendar[$Ymd]['sessions'][$session->getSlot()->getId().$session->getLocation()->getId()] = [
+                                               'id' => $session->getId(),
+                                               'title' => ($count > 1?'['.$count.'] ':'').$session->getSlot()->getTitle().' '.$session->getLocation()->getTitle(),
+                                               'class' => $class
+                                       ];
+                               }
+                       }
+
+                       //Sort sessions
+                       ksort($calendar[$Ymd]['sessions']);
+               }
+
+               return $this->render('@RapsysAir/admin/index.html.twig', ['title' => $title, 'section' => $section, 'form' => $form->createView(), 'calendar' => $calendar]);
+       }
+
+       public function sessionAction(Request $request, $id) {
+               /*header('Content-Type: text/plain');
+               var_dump($calendar);
+               exit;*/
+
+               //Get translator
+               $trans = $this->get('translator');
+
+               //Set section
+               $section = $trans->trans('Session %id%', ['%id%' => $id]);
+
+               //Set title
+               $title = $section.' - '.$trans->trans($this->getParameter('rapsys_air.title'));
+
+               //Create the form according to the FormType created previously.
+               //And give the proper parameters
+               /*$form = $this->createForm('Rapsys\AirBundle\Form\ApplicationType', null, [
+                       // To set the action use $this->generateUrl('route_identifier')
+                       'action' => $this->generateUrl('rapsys_air_admin'),
+                       'method' => 'POST',
+                       'attr' => [ 'class' => 'form_col' ]
+               ]);*/
+
+               //Get doctrine
+               $doctrine = $this->getDoctrine();
+
+               //Fetch session
+               $session = $doctrine->getRepository(Session::class)->findOneById($id);
+
+               return $this->render('@RapsysAir/admin/session.html.twig', ['title' => $title, 'section' => $section, /*'form' => $form->createView(),*/ 'session' => $session]);
+       }
+}
diff --git a/DataFixtures/AirFixtures.php b/DataFixtures/AirFixtures.php
new file mode 100644 (file)
index 0000000..cfaaa62
--- /dev/null
@@ -0,0 +1,246 @@
+<?php
+
+namespace Rapsys\AirBundle\DataFixtures;
+
+use Rapsys\AirBundle\Entity\Title;
+use Rapsys\AirBundle\Entity\Group;
+use Rapsys\AirBundle\Entity\User;
+use Rapsys\AirBundle\Entity\Location;
+use Rapsys\AirBundle\Entity\Slot;
+
+class AirFixtures extends \Doctrine\Bundle\FixturesBundle\Fixture implements \Symfony\Component\DependencyInjection\ContainerAwareInterface {
+       /**
+        * @var ContainerInterface
+        */
+       private $container;
+
+       public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null)
+       {
+               $this->container = $container;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public function load(\Doctrine\Common\Persistence\ObjectManager $manager) {
+               $encoder = $this->container->get('security.password_encoder');
+
+               //Title tree
+               $titleTree = array(
+                       'M.' => 'Monsieur',
+                       'Mlle' => 'Mademoiselle',
+                       'Mme' => 'Madame'
+               );
+
+               //Create titles
+               $titles = array();
+               foreach($titleTree as $shortData => $titleData) {
+                       $title = new Title();
+                       $title->setShort($shortData);
+                       $title->setTitle($titleData);
+                       $title->setCreated(new \DateTime('now'));
+                       $title->setUpdated(new \DateTime('now'));
+                       $manager->persist($title);
+                       $titles[$shortData] = $title;
+                       unset($title);
+               }
+
+               //Group tree
+               $groupTree = array(
+                       'ROLE_USER',
+                       'ROLE_ADMIN',
+                       'ROLE_SUPER'
+               );
+
+               //Create groups
+               $groups = array();
+               foreach($groupTree as $groupData) {
+                       $group = new Group($groupData);
+                       $group->setCreated(new \DateTime('now'));
+                       $group->setUpdated(new \DateTime('now'));
+                       $manager->persist($group);
+                       $groups[$groupData] = $group;
+                       unset($group);
+               }
+
+               //Flush to get the ids
+               $manager->flush();
+
+               //User tree
+               $userTree = array(
+                       array(
+                               'short' => 'M.',
+                               'group' => 'ROLE_SUPER',
+                               'mail' => 'airlibre@rapsys.eu',
+                               'pseudonym' => 'Rapsys',
+                               'forename' => 'Raphaël',
+                               'surname' => 'Gertz',
+                               'password' => 'test'
+                       ),
+                       array(
+                               'short' => 'M.',
+                               'group' => 'ROLE_ADMIN',
+                               'mail' => 'rannou402@orange.fr',
+                               'pseudonym' => 'Mitch',
+                               'forename' => 'Michel',
+                               'surname' => 'Rannou',
+                               'password' => 'test'
+                       ),
+                       array(
+                               'short' => 'Mlle',
+                               'group' => 'ROLE_ADMIN',
+                               'mail' => 'roxmaps@gmail.com',
+                               'pseudonym' => 'Roxana',
+                               'forename' => 'Roxana',
+                               'surname' => 'Prado',
+                               'password' => 'test'
+                       ),
+                       array(
+                               'short' => 'M.',
+                               'group' => 'ROLE_ADMIN',
+                               'mail' => 'majid.ghedjatti@gmail.com',
+                               'pseudonym' => 'El Guerrillero',
+                               'forename' => 'Majid',
+                               'surname' => 'Ghedjatti',
+                               'password' => 'test'
+                       ),
+                       array(
+                               'short' => 'M.',
+                               'group' => 'ROLE_ADMIN',
+                               'mail' => 'denis.courvoisier@wanadoo.fr',
+                               'pseudonym' => 'Sined',
+                               'forename' => 'Denis',
+                               'surname' => 'Courvoisier',
+                               'password' => 'test'
+                       ),
+                       array(
+                               'short' => 'M.',
+                               'group' => 'ROLE_ADMIN',
+                               'mail' => 'kastango13@gmail.com',
+                               'pseudonym' => 'Kastrat',
+                               'forename' => 'Kastrat',
+                               'surname' => 'Hasaj',
+                               'password' => 'test'
+                       ),
+               );
+
+               //Create users
+               $users = array();
+               foreach($userTree as $userData) {
+                       $user = new User();
+                       $user->setMail($userData['mail']);
+                       $user->setPseudonym($userData['pseudonym']);
+                       $user->setForename($userData['forename']);
+                       $user->setSurname($userData['surname']);
+                       $user->setPassword($encoder->encodePassword($user, $userData['password']));
+                       $user->setActive(true);
+                       $user->setTitle($titles[$userData['short']]);
+                       $user->addGroup($groups[$userData['group']]);
+                       $user->setCreated(new \DateTime('now'));
+                       $user->setUpdated(new \DateTime('now'));
+                       $manager->persist($user);
+                       $users[] = $user;
+                       unset($user);
+               }
+
+               //Flush to get the ids
+               $manager->flush();
+
+               //Location tree
+               $locationTree = [
+                       [
+                               'title' => 'Esplanade du Trocadéro',
+                               'address' => '1 Avenue Hussein 1er de Jordanie',
+                               #75016 pour meteo-france, accuweather supporte 75116
+                               'zipcode' => '75116',
+                               'city' => 'Paris',
+                               'latitude' => 48.8619,
+                               'longitude' => 2.2888
+                       ],
+                       [
+                               'title' => 'Opéra Garnier',
+                               'address' => 'Place de l\'Opéra',
+                               'zipcode' => '75009',
+                               'city' => 'Paris',
+                               'latitude' => 48.871365,
+                               'longitude' => 2.332026
+                       ],
+                       [
+                               'title' => 'Marché Saint Honoré',
+                               'address' => '1 Passage des Jacobins',
+                               'zipcode' => '75001',
+                               'city' => 'Paris',
+                               'latitude' => 48.8668,
+                               'longitude' => 2.331659
+                       ],
+                       [
+                               'title' => 'Jardin Tino-Rossi',
+                               'address' => '2 Quai Saint-Bernard',
+                               'zipcode' => '75005',
+                               'city' => 'Paris',
+                               'latitude' => 48.847736,
+                               'longitude' => 2.360953
+                       ],
+                       [
+                               'title' => 'Palais de Tokyo',
+                               'address' => '13 Avenue du Président Wilson',
+                               'zipcode' => '75116',
+                               'city' => 'Paris',
+                               'latitude' => 48.864567,
+                               'longitude' => 2.296892
+                       ]
+               ];
+
+               //Create locations
+               $locations = array();
+               foreach($locationTree as $locationData) {
+                       $location = new Location();
+                       $location->setTitle($locationData['title']);
+                       $location->setAddress($locationData['address']);
+                       $location->setZipcode($locationData['zipcode']);
+                       $location->setCity($locationData['city']);
+                       $location->setLatitude($locationData['latitude']);
+                       $location->setLongitude($locationData['longitude']);
+                       $location->setCreated(new \DateTime('now'));
+                       $location->setUpdated(new \DateTime('now'));
+                       $manager->persist($location);
+                       $locations[$locationData['title']] = $location;
+                       unset($location);
+               }
+
+               //Flush to get the ids
+               $manager->flush();
+
+               //Slot tree
+               $slotTree = [
+                       [
+                               'begin' => '14:00:00 UTC',
+                               'end' => '19:00:00 UTC'
+                       ],
+                       [
+                               'begin' => '19:00:00 UTC',
+                               'end' => '23:00:00 UTC'
+                       ],
+                       [
+                               'begin' => '23:00:00 UTC',
+                               'end' => '02:00:00 UTC'
+                       ]
+               ];
+
+               //Create slots
+               $slots = array();
+               foreach($slotTree as $slotData) {
+                       $slot = new Slot();
+                       $slot->setBegin(new \DateTime($slotData['begin']));
+                       $slot->setEnd(new \DateTime($slotData['end']));
+                       $slot->setCreated(new \DateTime('now'));
+                       $slot->setUpdated(new \DateTime('now'));
+                       $manager->persist($slot);
+                       $slots[$slot->getId()] = $slot;
+                       unset($slot);
+               }
+
+               //Flush to get the ids
+               $manager->flush();
+       }
+}
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
new file mode 100644 (file)
index 0000000..13d24a7
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+
+namespace Rapsys\AirBundle\DependencyInjection;
+
+use Symfony\Component\Config\Definition\Builder\TreeBuilder;
+use Symfony\Component\Config\Definition\ConfigurationInterface;
+
+/**
+ * This is the class that validates and merges configuration from your app/config files.
+ *
+ * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/configuration.html}
+ */
+class Configuration implements ConfigurationInterface {
+    /**
+     * {@inheritdoc}
+     */
+    public function getConfigTreeBuilder() {
+        $treeBuilder = new TreeBuilder();
+
+        // Here you should define the parameters that are allowed to
+        // configure your bundle. See the documentation linked above for
+        // more information on that topic.
+               //Set defaults
+               $defaults = [
+                       'logo' => 'bundles/rapsysair/png/logo.png',
+                       'title' => 'Open Air',
+                       'contact_name' => 'Raphaël Gertz',
+                       'contact_mail' => 'airlibre@rapsys.eu',
+               ];
+
+               //Here we define the parameters that are allowed to configure the bundle.
+               //TODO: see https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php for default value and description
+               //TODO: see http://symfony.com/doc/current/components/config/definition.html
+               //XXX: use bin/console config:dump-reference to dump class infos
+
+               //Here we define the parameters that are allowed to configure the bundle.
+               $treeBuilder
+                       //Parameters
+                       ->root('parameters')
+                               ->addDefaultsIfNotSet()
+                               ->children()
+                                       ->arrayNode('rapsys_air')
+                                               ->addDefaultsIfNotSet()
+                                               ->children()
+                                                       ->scalarNode('logo')->defaultValue($defaults['logo'])->treatNullLike($defaults['logo'])->isRequired()->end()
+                                                       ->scalarNode('title')->defaultValue($defaults['title'])->treatNullLike($defaults['title'])->isRequired()->end()
+                                                       ->scalarNode('contact_name')->defaultValue($defaults['contact_name'])->treatNullLike($defaults['contact_name'])->isRequired()->end()
+                                                       ->scalarNode('contact_mail')->defaultValue($defaults['contact_mail'])->treatNullLike($defaults['contact_mail'])->isRequired()->end()
+                                               ->end()
+                                       ->end()
+                               ->end()
+                       ->end();
+
+        return $treeBuilder;
+    }
+}
diff --git a/DependencyInjection/RapsysAirExtension.php b/DependencyInjection/RapsysAirExtension.php
new file mode 100644 (file)
index 0000000..5a2bdff
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+namespace Rapsys\AirBundle\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\HttpKernel\DependencyInjection\Extension;
+use Symfony\Component\DependencyInjection\Loader;
+
+/**
+ * This is the class that loads and manages your bundle configuration.
+ *
+ * @link http://symfony.com/doc/current/cookbook/bundles/extension.html
+ */
+class RapsysAirExtension extends Extension {
+       /**
+        * {@inheritdoc}
+        */
+       public function load(array $configs, ContainerBuilder $container) {
+               $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
+               $loader->load('services.yml');
+
+               $configuration = new Configuration();
+               $config = $this->processConfiguration($configuration, $configs);
+
+               //Set default config in parameter
+               if (!$container->hasParameter($alias = $this->getAlias())) {
+                       $container->setParameter($alias, $config[$alias]);
+               } else {
+                       $config[$alias] = $container->getParameter($alias);
+               }
+
+               //Transform the one level tree in flat parameters
+               foreach($config[$alias] as $k => $v) {
+                       //Set is as parameters
+                       $container->setParameter($alias.'.'.$k, $v);
+               }
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function getAlias() {
+               return 'rapsys_air';
+       }
+}
diff --git a/Entity/Application.php b/Entity/Application.php
new file mode 100644 (file)
index 0000000..c157132
--- /dev/null
@@ -0,0 +1,187 @@
+<?php
+
+namespace Rapsys\AirBundle\Entity;
+
+/**
+ * Application
+ */
+class Application
+{
+    /**
+     * @var integer
+     */
+    private $id;
+
+    /**
+     * @var \DateTime
+     */
+    private $created;
+
+    /**
+     * @var \DateTime
+     */
+    private $updated;
+
+    /**
+     * @var \Doctrine\Common\Collections\Collection
+     */
+    private $votes;
+
+    /**
+     * @var \Rapsys\AirBundle\Entity\Session
+     */
+    private $session;
+
+    /**
+     * @var \Rapsys\AirBundle\Entity\User
+     */
+    private $user;
+
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
+        $this->votes = new \Doctrine\Common\Collections\ArrayCollection();
+    }
+
+    /**
+     * Get id
+     *
+     * @return integer
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Set created
+     *
+     * @param \DateTime $created
+     *
+     * @return Application
+     */
+    public function setCreated($created)
+    {
+        $this->created = $created;
+
+        return $this;
+    }
+
+    /**
+     * Get created
+     *
+     * @return \DateTime
+     */
+    public function getCreated()
+    {
+        return $this->created;
+    }
+
+    /**
+     * Set updated
+     *
+     * @param \DateTime $updated
+     *
+     * @return Application
+     */
+    public function setUpdated($updated)
+    {
+        $this->updated = $updated;
+
+        return $this;
+    }
+
+    /**
+     * Get updated
+     *
+     * @return \DateTime
+     */
+    public function getUpdated()
+    {
+        return $this->updated;
+    }
+
+    /**
+     * Add vote
+     *
+     * @param \Rapsys\AirBundle\Entity\Vote $vote
+     *
+     * @return Application
+     */
+    public function addVote(\Rapsys\AirBundle\Entity\Vote $vote)
+    {
+        $this->votes[] = $vote;
+
+        return $this;
+    }
+
+    /**
+     * Remove vote
+     *
+     * @param \Rapsys\AirBundle\Entity\Vote $vote
+     */
+    public function removeVote(\Rapsys\AirBundle\Entity\Vote $vote)
+    {
+        $this->votes->removeElement($vote);
+    }
+
+    /**
+     * Get votes
+     *
+     * @return \Doctrine\Common\Collections\Collection
+     */
+    public function getVotes()
+    {
+        return $this->votes;
+    }
+
+    /**
+     * Set session
+     *
+     * @param \Rapsys\AirBundle\Entity\Session $session
+     *
+     * @return Application
+     */
+    public function setSession(\Rapsys\AirBundle\Entity\Session $session = null)
+    {
+        $this->session = $session;
+
+        return $this;
+    }
+
+    /**
+     * Get session
+     *
+     * @return \Rapsys\AirBundle\Entity\Session
+     */
+    public function getSession()
+    {
+        return $this->session;
+    }
+
+    /**
+     * Set user
+     *
+     * @param \Rapsys\AirBundle\Entity\User $user
+     *
+     * @return Application
+     */
+    public function setUser(\Rapsys\AirBundle\Entity\User $user = null)
+    {
+        $this->user = $user;
+
+        return $this;
+    }
+
+    /**
+     * Get user
+     *
+     * @return \Rapsys\AirBundle\Entity\User
+     */
+    public function getUser()
+    {
+        return $this->user;
+    }
+}
diff --git a/Entity/Group.php b/Entity/Group.php
new file mode 100644 (file)
index 0000000..3ccf6ee
--- /dev/null
@@ -0,0 +1,6 @@
+<?php
+
+// src/Rapsys/AirBundle/Entity/Group.php
+namespace Rapsys\AirBundle\Entity;
+
+class Group extends \Rapsys\UserBundle\Entity\Group {}
diff --git a/Entity/Location.php b/Entity/Location.php
new file mode 100644 (file)
index 0000000..4e9ceaf
--- /dev/null
@@ -0,0 +1,304 @@
+<?php
+
+namespace Rapsys\AirBundle\Entity;
+
+/**
+ * Location
+ */
+class Location
+{
+    /**
+     * @var integer
+     */
+    private $id;
+
+    /**
+     * @var string
+     */
+    private $title;
+
+    /**
+     * @var string
+     */
+    private $address;
+
+    /**
+     * @var string
+     */
+    private $zipcode;
+
+    /**
+     * @var string
+     */
+    private $city;
+
+    /**
+     * @var string
+     */
+    private $latitude;
+
+    /**
+     * @var string
+     */
+    private $longitude;
+
+    /**
+     * @var \DateTime
+     */
+    private $created;
+
+    /**
+     * @var \DateTime
+     */
+    private $updated;
+
+    /**
+     * @var \Doctrine\Common\Collections\Collection
+     */
+    private $sessions;
+
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
+        $this->sessions = new \Doctrine\Common\Collections\ArrayCollection();
+    }
+
+    /**
+     * Get id
+     *
+     * @return integer
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Set title
+     *
+     * @param string $title
+     *
+     * @return Location
+     */
+    public function setTitle($title)
+    {
+        $this->title = $title;
+
+        return $this;
+    }
+
+    /**
+     * Get title
+     *
+     * @return string
+     */
+    public function getTitle()
+    {
+        return $this->title;
+    }
+
+    /**
+     * Set address
+     *
+     * @param string $address
+     *
+     * @return Location
+     */
+    public function setAddress($address)
+    {
+        $this->address = $address;
+
+        return $this;
+    }
+
+    /**
+     * Get address
+     *
+     * @return string
+     */
+    public function getAddress()
+    {
+        return $this->address;
+    }
+
+    /**
+     * Set zipcode
+     *
+     * @param string $zipcode
+     *
+     * @return Location
+     */
+    public function setZipcode($zipcode)
+    {
+        $this->zipcode = $zipcode;
+
+        return $this;
+    }
+
+    /**
+     * Get zipcode
+     *
+     * @return string
+     */
+    public function getZipcode()
+    {
+        return $this->zipcode;
+    }
+
+    /**
+     * Set city
+     *
+     * @param string $city
+     *
+     * @return Location
+     */
+    public function setCity($city)
+    {
+        $this->city = $city;
+
+        return $this;
+    }
+
+    /**
+     * Get city
+     *
+     * @return string
+     */
+    public function getCity()
+    {
+        return $this->city;
+    }
+
+    /**
+     * Set latitude
+     *
+     * @param string $latitude
+     *
+     * @return Location
+     */
+    public function setLatitude($latitude)
+    {
+        $this->latitude = $latitude;
+
+        return $this;
+    }
+
+    /**
+     * Get latitude
+     *
+     * @return string
+     */
+    public function getLatitude()
+    {
+        return $this->latitude;
+    }
+
+    /**
+     * Set longitude
+     *
+     * @param string $longitude
+     *
+     * @return Location
+     */
+    public function setLongitude($longitude)
+    {
+        $this->longitude = $longitude;
+
+        return $this;
+    }
+
+    /**
+     * Get longitude
+     *
+     * @return string
+     */
+    public function getLongitude()
+    {
+        return $this->longitude;
+    }
+
+    /**
+     * Set created
+     *
+     * @param \DateTime $created
+     *
+     * @return Location
+     */
+    public function setCreated($created)
+    {
+        $this->created = $created;
+
+        return $this;
+    }
+
+    /**
+     * Get created
+     *
+     * @return \DateTime
+     */
+    public function getCreated()
+    {
+        return $this->created;
+    }
+
+    /**
+     * Set updated
+     *
+     * @param \DateTime $updated
+     *
+     * @return Location
+     */
+    public function setUpdated($updated)
+    {
+        $this->updated = $updated;
+
+        return $this;
+    }
+
+    /**
+     * Get updated
+     *
+     * @return \DateTime
+     */
+    public function getUpdated()
+    {
+        return $this->updated;
+    }
+
+    /**
+     * Add session
+     *
+     * @param \Rapsys\AirBundle\Entity\Session $session
+     *
+     * @return Location
+     */
+    public function addSession(\Rapsys\AirBundle\Entity\Session $session)
+    {
+        $this->sessions[] = $session;
+
+        return $this;
+    }
+
+    /**
+     * Remove session
+     *
+     * @param \Rapsys\AirBundle\Entity\Session $session
+     */
+    public function removeSession(\Rapsys\AirBundle\Entity\Session $session)
+    {
+        $this->sessions->removeElement($session);
+    }
+
+    /**
+     * Get sessions
+     *
+     * @return \Doctrine\Common\Collections\Collection
+     */
+    public function getSessions()
+    {
+        return $this->sessions;
+    }
+}
+
diff --git a/Entity/Session.php b/Entity/Session.php
new file mode 100644 (file)
index 0000000..72d1f33
--- /dev/null
@@ -0,0 +1,281 @@
+<?php
+
+namespace Rapsys\AirBundle\Entity;
+
+/**
+ * Session
+ */
+class Session {
+       /**
+        * @var integer
+        */
+       private $id;
+
+       /**
+        * @var \DateTime
+        */
+       private $date;
+
+       /**
+        * @var \DateTime
+        */
+       private $begin;
+
+       /**
+        * @var \DateTime
+        */
+       private $end;
+
+       /**
+        * @var \DateTime
+        */
+       private $created;
+
+       /**
+        * @var \DateTime
+        */
+       private $updated;
+
+       /**
+        * @var \Doctrine\Common\Collections\Collection
+        */
+       private $applications;
+
+       /**
+        * @var \Rapsys\AirBundle\Entity\Location
+        */
+       private $location;
+
+       /**
+        * @var \Rapsys\AirBundle\Entity\Application
+        */
+       private $application;
+
+       /**
+        * Constructor
+        */
+       public function __construct() {
+               $this->applications = new \Doctrine\Common\Collections\ArrayCollection();
+       }
+
+       /**
+        * Get id
+        *
+        * @return integer
+        */
+       public function getId() {
+               return $this->id;
+       }
+
+       /**
+        * Set date
+        *
+        * @param \DateTime $date
+        *
+        * @return Session
+        */
+       public function setDate($date) {
+               $this->date = $date;
+
+               return $this;
+       }
+
+       /**
+        * Get date
+        *
+        * @return \DateTime
+        */
+       public function getDate() {
+               return $this->date;
+       }
+
+       /**
+        * Set begin
+        *
+        * @param \DateTime $begin
+        *
+        * @return Session
+        */
+       public function setBegin($begin) {
+               $this->begin = $begin;
+
+               return $this;
+       }
+
+       /**
+        * Get begin
+        *
+        * @return \DateTime
+        */
+       public function getBegin() {
+               return $this->begin;
+       }
+
+       /**
+        * Set end
+        *
+        * @param \DateTime $end
+        *
+        * @return Session
+        */
+       public function setEnd($end) {
+               $this->end = $end;
+
+               return $this;
+       }
+
+       /**
+        * Get end
+        *
+        * @return \DateTime
+        */
+       public function getEnd() {
+               return $this->end;
+       }
+
+       /**
+        * Set created
+        *
+        * @param \DateTime $created
+        *
+        * @return Session
+        */
+       public function setCreated($created) {
+               $this->created = $created;
+
+               return $this;
+       }
+
+       /**
+        * Get created
+        *
+        * @return \DateTime
+        */
+       public function getCreated() {
+               return $this->created;
+       }
+
+       /**
+        * Set updated
+        *
+        * @param \DateTime $updated
+        *
+        * @return Session
+        */
+       public function setUpdated($updated) {
+               $this->updated = $updated;
+
+               return $this;
+       }
+
+       /**
+        * Get updated
+        *
+        * @return \DateTime
+        */
+       public function getUpdated() {
+               return $this->updated;
+       }
+
+       /**
+        * Add application
+        *
+        * @param \Rapsys\AirBundle\Entity\Application $application
+        *
+        * @return Session
+        */
+       public function addApplication(\Rapsys\AirBundle\Entity\Application $application) {
+               $this->applications[] = $application;
+
+               return $this;
+       }
+
+       /**
+        * Remove application
+        *
+        * @param \Rapsys\AirBundle\Entity\Application $application
+        */
+       public function removeApplication(\Rapsys\AirBundle\Entity\Application $application) {
+               $this->applications->removeElement($application);
+       }
+
+       /**
+        * Get applications
+        *
+        * @return \Doctrine\Common\Collections\Collection
+        */
+       public function getApplications() {
+               return $this->applications;
+       }
+
+       /**
+        * Set location
+        *
+        * @param \Rapsys\AirBundle\Entity\Location $location
+        *
+        * @return Session
+        */
+       public function setLocation(\Rapsys\AirBundle\Entity\Location $location = null) {
+               $this->location = $location;
+
+               return $this;
+       }
+
+       /**
+        * Get location
+        *
+        * @return \Rapsys\AirBundle\Entity\Location
+        */
+       public function getLocation() {
+               return $this->location;
+       }
+       /**
+        * @var \Rapsys\AirBundle\Entity\Slot
+        */
+       private $slot;
+
+
+       /**
+        * Set slot
+        *
+        * @param \Rapsys\AirBundle\Entity\Slot $slot
+        *
+        * @return Session
+        */
+       public function setSlot(\Rapsys\AirBundle\Entity\Slot $slot = null) {
+               $this->slot = $slot;
+
+               return $this;
+       }
+
+       /**
+        * Get slot
+        *
+        * @return \Rapsys\AirBundle\Entity\Slot
+        */
+       public function getSlot() {
+               return $this->slot;
+       }
+
+       /**
+        * Set application
+        *
+        * @param \Rapsys\AirBundle\Entity\Application $application
+        *
+        * @return Session
+        */
+       public function setApplication(\Rapsys\AirBundle\Entity\Application $application = null) {
+               $this->application = $application;
+
+               return $this;
+       }
+
+       /**
+        * Get application
+        *
+        * @return \Rapsys\AirBundle\Entity\Application
+        */
+       public function getApplication() {
+               return $this->application;
+       }
+}
diff --git a/Entity/Slot.php b/Entity/Slot.php
new file mode 100644 (file)
index 0000000..03bfd3f
--- /dev/null
@@ -0,0 +1,191 @@
+<?php
+
+namespace Rapsys\AirBundle\Entity;
+
+/**
+ * Slot
+ */
+class Slot
+{
+    /**
+     * @var integer
+     */
+    private $id;
+
+    /**
+     * @var \DateTime
+     */
+    private $begin;
+
+    /**
+     * @var \DateTime
+     */
+    private $end;
+
+    /**
+     * @var \DateTime
+     */
+    private $created;
+
+    /**
+     * @var \DateTime
+     */
+    private $updated;
+
+    /**
+     * @var \Doctrine\Common\Collections\Collection
+     */
+    private $sessions;
+
+    /**
+     * Constructor
+     */
+    public function __construct()
+    {
+        $this->sessions = new \Doctrine\Common\Collections\ArrayCollection();
+    }
+
+    /**
+     * Get id
+     *
+     * @return integer
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Set begin
+     *
+     * @param \DateTime $begin
+     *
+     * @return Slot
+     */
+    public function setBegin($begin)
+    {
+        $this->begin = $begin;
+
+        return $this;
+    }
+
+    /**
+     * Get begin
+     *
+     * @return \DateTime
+     */
+    public function getBegin()
+    {
+        return $this->begin;
+    }
+
+    /**
+     * Set end
+     *
+     * @param \DateTime $end
+     *
+     * @return Slot
+     */
+    public function setEnd($end)
+    {
+        $this->end = $end;
+
+        return $this;
+    }
+
+    /**
+     * Get end
+     *
+     * @return \DateTime
+     */
+    public function getEnd()
+    {
+        return $this->end;
+    }
+
+    /**
+     * Set created
+     *
+     * @param \DateTime $created
+     *
+     * @return Slot
+     */
+    public function setCreated($created)
+    {
+        $this->created = $created;
+
+        return $this;
+    }
+
+    /**
+     * Get created
+     *
+     * @return \DateTime
+     */
+    public function getCreated()
+    {
+        return $this->created;
+    }
+
+    /**
+     * Set updated
+     *
+     * @param \DateTime $updated
+     *
+     * @return Slot
+     */
+    public function setUpdated($updated)
+    {
+        $this->updated = $updated;
+
+        return $this;
+    }
+
+    /**
+     * Get updated
+     *
+     * @return \DateTime
+     */
+    public function getUpdated()
+    {
+        return $this->updated;
+    }
+
+    /**
+     * Add session
+     *
+     * @param \Rapsys\AirBundle\Entity\Session $session
+     *
+     * @return Slot
+     */
+    public function addSession(\Rapsys\AirBundle\Entity\Session $session)
+    {
+        $this->sessions[] = $session;
+
+        return $this;
+    }
+
+    /**
+     * Remove session
+     *
+     * @param \Rapsys\AirBundle\Entity\Session $session
+     */
+    public function removeSession(\Rapsys\AirBundle\Entity\Session $session)
+    {
+        $this->sessions->removeElement($session);
+    }
+
+    /**
+     * Get sessions
+     *
+     * @return \Doctrine\Common\Collections\Collection
+     */
+    public function getSessions()
+    {
+        return $this->sessions;
+    }
+
+    public function getTitle() {
+           return $this->begin->format('H:i').'-'.$this->end->format('H:i');
+    }
+}
diff --git a/Entity/Title.php b/Entity/Title.php
new file mode 100644 (file)
index 0000000..54a6a60
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+// src/Rapsys/AirBundle/Entity/Title.php
+namespace Rapsys\AirBundle\Entity;
+
+class Title extends \Rapsys\UserBundle\Entity\Title {
+}
diff --git a/Entity/User.php b/Entity/User.php
new file mode 100644 (file)
index 0000000..473fd07
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+
+// src/Rapsys/AirBundle/Entity/User.php
+namespace Rapsys\AirBundle\Entity;
+
+class User extends \Rapsys\UserBundle\Entity\User {
+       /**
+        * @var \Doctrine\Common\Collections\Collection
+        */
+       private $votes;
+
+       /**
+        * @var \Doctrine\Common\Collections\Collection
+        */
+       private $applications;
+
+       /**
+        * Constructor
+        */
+       public function __construct() {
+               parent::__construct();
+       }
+
+       /**
+        * Add vote
+        *
+        * @param \Rapsys\AirBundle\Entity\Vote $vote
+        *
+        * @return User
+        */
+       public function addVote(\Rapsys\AirBundle\Entity\Vote $vote) {
+               $this->votes[] = $vote;
+
+               return $this;
+       }
+
+       /**
+        * Remove vote
+        *
+        * @param \Rapsys\AirBundle\Entity\Vote $vote
+        */
+       public function removeVote(\Rapsys\AirBundle\Entity\Vote $vote) {
+               $this->votes->removeElement($vote);
+       }
+
+       /**
+        * Get votes
+        *
+        * @return \Doctrine\Common\Collections\Collection
+        */
+       public function getVotes() {
+               return $this->votes;
+       }
+
+       /**
+        * Add application
+        *
+        * @param \Rapsys\AirBundle\Entity\Application $application
+        *
+        * @return User
+        */
+       public function addApplication(\Rapsys\AirBundle\Entity\Application $application) {
+               $this->applications[] = $application;
+
+               return $this;
+       }
+
+       /**
+        * Remove application
+        *
+        * @param \Rapsys\AirBundle\Entity\Application $application
+        */
+       public function removeApplication(\Rapsys\AirBundle\Entity\Application $application) {
+               $this->applications->removeElement($application);
+       }
+
+       /**
+        * Get applications
+        *
+        * @return \Doctrine\Common\Collections\Collection
+        */
+       public function getApplications() {
+               return $this->applications;
+       }
+}
diff --git a/Entity/Vote.php b/Entity/Vote.php
new file mode 100644 (file)
index 0000000..7b2fb53
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+
+namespace Rapsys\AirBundle\Entity;
+
+/**
+ * Vote
+ */
+class Vote
+{
+    /**
+     * @var integer
+     */
+    private $id;
+
+    /**
+     * @var \DateTime
+     */
+    private $created;
+
+    /**
+     * @var \DateTime
+     */
+    private $updated;
+
+    /**
+     * @var \Rapsys\AirBundle\Entity\Application
+     */
+    private $application;
+
+    /**
+     * @var \Rapsys\AirBundle\Entity\User
+     */
+    private $user;
+
+
+    /**
+     * Get id
+     *
+     * @return integer
+     */
+    public function getId()
+    {
+        return $this->id;
+    }
+
+    /**
+     * Set created
+     *
+     * @param \DateTime $created
+     *
+     * @return Vote
+     */
+    public function setCreated($created)
+    {
+        $this->created = $created;
+
+        return $this;
+    }
+
+    /**
+     * Get created
+     *
+     * @return \DateTime
+     */
+    public function getCreated()
+    {
+        return $this->created;
+    }
+
+    /**
+     * Set updated
+     *
+     * @param \DateTime $updated
+     *
+     * @return Vote
+     */
+    public function setUpdated($updated)
+    {
+        $this->updated = $updated;
+
+        return $this;
+    }
+
+    /**
+     * Get updated
+     *
+     * @return \DateTime
+     */
+    public function getUpdated()
+    {
+        return $this->updated;
+    }
+
+    /**
+     * Set application
+     *
+     * @param \Rapsys\AirBundle\Entity\Application $application
+     *
+     * @return Vote
+     */
+    public function setApplication(\Rapsys\AirBundle\Entity\Application $application = null)
+    {
+        $this->application = $application;
+
+        return $this;
+    }
+
+    /**
+     * Get application
+     *
+     * @return \Rapsys\AirBundle\Entity\Application
+     */
+    public function getApplication()
+    {
+        return $this->application;
+    }
+
+    /**
+     * Set user
+     *
+     * @param \Rapsys\AirBundle\Entity\User $user
+     *
+     * @return Vote
+     */
+    public function setUser(\Rapsys\AirBundle\Entity\User $user = null)
+    {
+        $this->user = $user;
+
+        return $this;
+    }
+
+    /**
+     * Get user
+     *
+     * @return \Rapsys\AirBundle\Entity\User
+     */
+    public function getUser()
+    {
+        return $this->user;
+    }
+}
diff --git a/Form/ApplicationType.php b/Form/ApplicationType.php
new file mode 100644 (file)
index 0000000..8439d29
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+namespace Rapsys\AirBundle\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Bridge\Doctrine\Form\Type\EntityType;
+use Symfony\Component\Form\Extension\Core\Type\DateType;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Validator\Constraints\Date;
+use Symfony\Component\Validator\Constraints\NotBlank;
+
+class ApplicationType extends AbstractType {
+       /**
+        * {@inheritdoc}
+        */
+       public function buildForm(FormBuilderInterface $builder, array $options) {
+               return $builder
+                       ->add('location', EntityType::class, array('class' => 'RapsysAirBundle:Location', 'choice_label' => 'title', 'attr' => array('placeholder' => 'Your location'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your location')))))
+                       ->add('date', DateType::class, array('attr' => [ 'placeholder' => 'Your date', 'class' => 'date' ], 'html5' => true, 'input' => 'datetime', 'data' => new \DateTime('+7 day'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your date')), new Date(array('message' => 'Your date doesn\'t seems to be valid')))))
+                       ->add('slot', EntityType::class, array('class' => 'RapsysAirBundle:Slot', 'choice_label' => 'title', 'attr' => array('placeholder' => 'Your slot'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your slot')))))
+                       ->add('submit', SubmitType::class, array('label' => 'Send', 'attr' => array('class' => 'submit')));
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function configureOptions(OptionsResolver $resolver) {
+               $resolver->setDefaults(['error_bubbling' => true]);
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function getName() {
+               return 'rapsys_air_application';
+       }
+}
diff --git a/Form/ContactType.php b/Form/ContactType.php
new file mode 100644 (file)
index 0000000..e9a5ba1
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+
+namespace Rapsys\AirBundle\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+use Symfony\Component\Form\Extension\Core\Type\EmailType;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Validator\Constraints\Email;
+use Symfony\Component\Validator\Constraints\NotBlank;
+
+class ContactType extends AbstractType {
+       /**
+        * {@inheritdoc}
+        */
+       public function buildForm(FormBuilderInterface $builder, array $options) {
+               return $builder->add('name', TextType::class, array('attr' => array('placeholder' => 'Your name'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your name')))))
+                       ->add('subject', TextType::class, array('attr' => array('placeholder' => 'Subject'), 'constraints' => array(new NotBlank(array('message' => 'Please provide your subject')))))
+                       ->add('mail', EmailType::class, array('attr' => array('placeholder' => 'Your mail address'), 'constraints' => array(new NotBlank(array('message' => 'Please provide a valid mail')), new Email(array('message' => 'Your mail doesn\'t seems to be valid')))))
+                       ->add('message', TextareaType::class, array('attr' => array('placeholder' => 'Your message here', 'cols' => 50, 'rows' => 15), 'constraints' => array(new NotBlank(array('message' => 'Please provide your message')))))
+                       ->add('submit', SubmitType::class, array('label' => 'Send', 'attr' => array('class' => 'submit')));
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function configureOptions(OptionsResolver $resolver) {
+               $resolver->setDefaults(['error_bubbling' => true]);
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function getName() {
+               return 'contact_form';
+       }
+}
diff --git a/RapsysAirBundle.php b/RapsysAirBundle.php
new file mode 100644 (file)
index 0000000..06f1b37
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+namespace Rapsys\AirBundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+class RapsysAirBundle extends Bundle
+{
+}
diff --git a/Repository/ApplicationRepository.php b/Repository/ApplicationRepository.php
new file mode 100644 (file)
index 0000000..3112d14
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+
+namespace Rapsys\AirBundle\Repository;
+
+/**
+ * ApplicationRepository
+ */
+class ApplicationRepository extends \Doctrine\ORM\EntityRepository {
+       /**
+        * Find session by session and user
+        *
+        * @param $session The session
+        * @param $user The user
+        */
+       public function findOneBySessionUser($session, $user) {
+               //Fetch article
+               $ret = $this->getEntityManager()
+                       ->createQuery('SELECT a FROM RapsysAirBundle:Application a WHERE (a.session = :session AND a.user = :user)')
+                       ->setParameter('session', $session)
+                       ->setParameter('user', $user)
+                       ->getSingleResult();
+
+               //Send result
+               return $ret;
+       }
+}
diff --git a/Repository/SessionRepository.php b/Repository/SessionRepository.php
new file mode 100644 (file)
index 0000000..e295bfd
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+namespace Rapsys\AirBundle\Repository;
+
+/**
+ * SessionRepository
+ */
+class SessionRepository extends \Doctrine\ORM\EntityRepository {
+       /**
+        * Find session by location, slot and date
+        *
+        * @param $location The location
+        * @param $slot The slot
+        * @param $date The datetime
+        */
+       public function findOneByLocationSlotDate($location, $slot, $date) {
+               //Fetch session
+               $ret = $this->getEntityManager()
+                       ->createQuery('SELECT s FROM RapsysAirBundle:Session s WHERE (s.location = :location AND s.slot = :slot AND s.date = :date)')
+                       ->setParameter('location', $location)
+                       ->setParameter('slot', $slot)
+                       ->setParameter('date', $date)
+                       ->getSingleResult();
+
+               //Send result
+               return $ret;
+       }
+
+       /**
+        * Find sessions by date period
+        *
+        * @param $period The date period
+        */
+       public function findByDatePeriod($period) {
+               //Fetch sessions
+               $ret = $this->getEntityManager()
+                       ->createQuery('SELECT s FROM RapsysAirBundle:Session s WHERE s.date BETWEEN :begin AND :end')
+                       ->setParameter('begin', $period->getStartDate())
+                       ->setParameter('end', $period->getEndDate())
+                       ->getResult();
+
+               //Send result
+               return $ret;
+       }
+}
diff --git a/Resources/config/doctrine/Application.orm.yml b/Resources/config/doctrine/Application.orm.yml
new file mode 100644 (file)
index 0000000..aaf461f
--- /dev/null
@@ -0,0 +1,27 @@
+Rapsys\AirBundle\Entity\Application:
+    type: entity
+    repositoryClass: Rapsys\AirBundle\Repository\ApplicationRepository
+    table: applications
+    id:
+        id:
+            type: integer
+            generator: 
+                strategy: AUTO
+            options:
+                unsigned: true
+    fields:
+        created:
+            type: datetime
+        updated:
+            type: datetime
+    manyToOne:
+        session:
+            targetEntity: Rapsys\AirBundle\Entity\Session
+            inversedBy: applications
+        user:
+            targetEntity: Rapsys\AirBundle\Entity\User
+            inversedBy: applications
+    oneToMany:
+        votes:
+            targetEntity: Rapsys\AirBundle\Entity\Vote
+            mappedBy: application
diff --git a/Resources/config/doctrine/Group.orm.yml b/Resources/config/doctrine/Group.orm.yml
new file mode 100644 (file)
index 0000000..57f70a9
--- /dev/null
@@ -0,0 +1,8 @@
+Rapsys\AirBundle\Entity\Group:
+    type: entity
+    #repositoryClass: Rapsys\AirBundle\Repository\GroupRepository
+    table: groups
+    manyToMany:
+        users:
+            targetEntity: Rapsys\AirBundle\Entity\User
+            mappedBy: groups
diff --git a/Resources/config/doctrine/Location.orm.yml b/Resources/config/doctrine/Location.orm.yml
new file mode 100644 (file)
index 0000000..86d86db
--- /dev/null
@@ -0,0 +1,69 @@
+Rapsys\AirBundle\Entity\Location:
+    type: entity
+    #repositoryClass: Rapsys\AirBundle\Repository\LocationRepository
+    table: locations
+    id:
+        id:
+            type: integer
+            generator: 
+                strategy: AUTO
+            options:
+                unsigned: true
+    fields:
+        title:
+            type: string
+            length: 24
+        address:
+            type: string
+            length: 32
+        zipcode:
+            type: string
+            length: 5
+        city:
+            type: string
+            length: 64
+        latitude:
+            type: decimal
+            precision: 8
+            scale: 6
+        longitude:
+            type: decimal
+            precision: 9
+            scale: 6
+        created:
+            type: datetime
+        updated:
+            type: datetime
+    oneToMany:
+        sessions:
+            targetEntity: Rapsys\AirBundle\Entity\Session
+            mappedBy: location
+
+#        manyToOne:
+#                title:
+#                        targetEntity: Rapsys\UserBundle\Entity\Title
+#                        inversedBy: users
+#        manyToMany:
+#                groups:
+#                        targetEntity: Rapsys\UserBundle\Entity\Group
+#                        inversedBy: users
+#                        joinTable:
+#                                name: groups_users
+#
+#        manyToOne:
+#                site:
+#                        targetEntity: Rapsys\BlogBundle\Entity\Site
+#                        inversedBy: articles
+#                author:
+#                        targetEntity: Rapsys\BlogBundle\Entity\Author
+#                        inversedBy: articles
+#        manyToMany:
+#                keywords:
+#                        targetEntity: Rapsys\BlogBundle\Entity\Keyword
+#                        inversedBy: articles
+#                        joinTable:
+#                                name: articles_keywords
+#        oneToMany:
+#                article_translations:
+#                        targetEntity: Rapsys\BlogBundle\Entity\ArticleTranslation
+#                        mappedBy: article
diff --git a/Resources/config/doctrine/Session.orm.yml b/Resources/config/doctrine/Session.orm.yml
new file mode 100644 (file)
index 0000000..9c89adb
--- /dev/null
@@ -0,0 +1,41 @@
+Rapsys\AirBundle\Entity\Session:
+    type: entity
+    repositoryClass: Rapsys\AirBundle\Repository\SessionRepository
+    table: sessions
+    id:
+        id:
+            type: integer
+            generator: 
+                strategy: AUTO
+            options:
+                unsigned: true
+    fields:
+        date:
+            type: date
+        begin:
+            type: time
+            nullable: true
+        end:
+            type: time
+            nullable: true
+        created:
+            type: datetime
+        updated:
+            type: datetime
+    oneToOne:
+        application:
+            targetEntity: Rapsys\AirBundle\Entity\Application
+    manyToOne:
+        location:
+            targetEntity: Rapsys\AirBundle\Entity\Location
+            inversedBy: sessions
+        slot:
+            targetEntity: Rapsys\AirBundle\Entity\Slot
+            inversedBy: sessions
+    oneToMany:
+        applications:
+            targetEntity: Rapsys\AirBundle\Entity\Application
+            mappedBy: session
+    uniqueConstraints:
+        date_location_slot:
+            columns: [ date, location_id, slot_id ]
diff --git a/Resources/config/doctrine/Slot.orm.yml b/Resources/config/doctrine/Slot.orm.yml
new file mode 100644 (file)
index 0000000..c4ff511
--- /dev/null
@@ -0,0 +1,24 @@
+Rapsys\AirBundle\Entity\Slot:
+    type: entity
+    #repositoryClass: Rapsys\AirBundle\Repository\SlotRepository
+    table: slots
+    id:
+        id:
+            type: integer
+            generator: 
+                strategy: AUTO
+            options:
+                unsigned: true
+    fields:
+        begin:
+            type: time
+        end:
+            type: time
+        created:
+            type: datetime
+        updated:
+            type: datetime
+    oneToMany:
+        sessions:
+            targetEntity: Rapsys\AirBundle\Entity\Session
+            mappedBy: slot
diff --git a/Resources/config/doctrine/Title.orm.yml b/Resources/config/doctrine/Title.orm.yml
new file mode 100644 (file)
index 0000000..d68aa60
--- /dev/null
@@ -0,0 +1,8 @@
+Rapsys\AirBundle\Entity\Title:
+    type: entity
+    #repositoryClass: Rapsys\AirBundle\Repository\TitleRepository
+    table: titles
+    oneToMany:
+        users:
+            targetEntity: User
+            mappedBy: title
diff --git a/Resources/config/doctrine/User.orm.yml b/Resources/config/doctrine/User.orm.yml
new file mode 100644 (file)
index 0000000..4df938e
--- /dev/null
@@ -0,0 +1,27 @@
+Rapsys\AirBundle\Entity\User:
+    type: entity
+    #repositoryClass: Rapsys\AirBundle\Repository\UserRepository
+    table: users
+    oneToMany:
+        votes:
+            targetEntity: Rapsys\AirBundle\Entity\Vote
+            mappedBy: user
+        applications:
+            targetEntity: Rapsys\AirBundle\Entity\Application
+            mappedBy: user
+#    manyToMany:
+#        groups:
+#            targetEntity: Group
+#            inversedBy: users
+#            joinTable:
+#                name: groups_users
+    associationOverride:
+        groups:
+            joinTable:
+                name: groups_users
+                joinColumns:
+                    id:
+                        name: user_id
+                inverseJoinColumns:
+                    id:
+                        name: group_id
diff --git a/Resources/config/doctrine/Vote.orm.yml b/Resources/config/doctrine/Vote.orm.yml
new file mode 100644 (file)
index 0000000..ee8e2ea
--- /dev/null
@@ -0,0 +1,23 @@
+Rapsys\AirBundle\Entity\Vote:
+    type: entity
+    #repositoryClass: Rapsys\AirBundle\Repository\VoteRepository
+    table: votes
+    id:
+        id:
+            type: integer
+            generator: 
+                strategy: AUTO
+            options:
+                unsigned: true
+    fields:
+        created:
+            type: datetime
+        updated:
+            type: datetime
+    manyToOne:
+        application:
+            targetEntity: Rapsys\AirBundle\Entity\Application
+            inversedBy: votes
+        user:
+            targetEntity: Rapsys\AirBundle\Entity\User
+            inversedBy: votes
diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml
new file mode 100644 (file)
index 0000000..dd63f57
--- /dev/null
@@ -0,0 +1,17 @@
+rapsys_air_homepage:
+    path:     /
+    defaults: { _controller: RapsysAirBundle:Default:index }
+
+rapsys_air_admin:
+    path:     /admin
+    defaults: { _controller: RapsysAirBundle:Default:admin }
+
+rapsys_air_contact:
+    path:     /contact
+    defaults: { _controller: RapsysAirBundle:Default:contact }
+
+rapsys_air_session:
+    path:     /admin/session/{id}
+    defaults: { _controller: RapsysAirBundle:Default:session }
+    requirements:
+        id: '\d+'
diff --git a/Resources/config/services.yml b/Resources/config/services.yml
new file mode 100644 (file)
index 0000000..9f2f696
--- /dev/null
@@ -0,0 +1,15 @@
+services:
+    rapsys_air.twig.file_get_contents:
+        class: Rapsys\AirBundle\Twig\FileGetContentsExtension
+        tags: [ twig.extension ]
+    rapsys_air.twig.base64:
+        class: Rapsys\AirBundle\Twig\Base64Extension
+        tags: [ twig.extension ]
+    rapsys_air.twig.bb2html:
+        class: Rapsys\AirBundle\Twig\Bb2htmlExtension
+        tags: [ twig.extension ]
+    Rapsys\AirBundle\DataFixtures\AirFixtures:
+        tags: [ doctrine.fixture.orm ]
+#    rapsys_air.example:
+#        class: Rapsys\AirBundle\Example
+#        arguments: ["@service_id", "plain_value", "%parameter%"]
diff --git a/Resources/public/css/reset.css b/Resources/public/css/reset.css
new file mode 100644 (file)
index 0000000..c59ac50
--- /dev/null
@@ -0,0 +1,45 @@
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center, dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed, 
+figure, figcaption, footer, header, hgroup, 
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video, input, textarea, button, select, option {
+       margin: 0;
+       padding: 0;
+       border: 0;
+       font-size: 100%;
+       font: inherit;
+       vertical-align: baseline;
+}
+
+article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
+       display: block;
+}
+
+body {
+       line-height: 1;
+}
+
+ol, ul {
+       list-style: none;
+}
+
+blockquote, q {
+       quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after {
+       content: '';
+       content: none;
+}
+
+table {
+       border-collapse: collapse;
+       border-spacing: 0;
+}
diff --git a/Resources/public/css/screen.css b/Resources/public/css/screen.css
new file mode 100644 (file)
index 0000000..5477726
--- /dev/null
@@ -0,0 +1,473 @@
+/* Reset link */
+a {
+       text-decoration: none;
+       color: #066;
+}
+
+a:hover {
+       text-decoration: underline solid #00c3f9;
+}
+
+h1::first-letter,
+h2::first-letter,
+h3::first-letter,
+h4::first-letter,
+h5::first-letter,
+h6::first-letter,
+a::first-letter {
+       color: #00c3f9;
+}
+
+/* Default styling */
+h1 {
+       font-size: 2rem;
+       margin: 1.34rem 0;
+}
+
+h2 {
+       font-size: 1.5rem;
+       margin: 1.245rem 0;
+}
+
+h3 {
+       font-size: 1.17rem;
+       margin: 1.17rem 0;
+}
+
+h4 {
+       font-size: 1rem;
+       margin: 1.33rem 0;
+}
+
+h5 {
+       font-size: .83rem;
+       margin: 1.386rem 0;
+}
+
+h6 {
+       font-size: .67rem;
+       margin: 1.561rem 0;
+}
+
+p {
+}
+
+body {
+       display: flex;
+       flex-flow: column wrap;
+       color: #066;
+}
+
+/* Header */
+#header {
+       border: .1rem solid #00c3f9;
+       border-top: 0;
+       border-radius: 0 0 .5rem .5rem;
+       margin: .5rem;
+       margin-top: 0;
+       padding: .5rem;
+       left: 0;
+       right: 0;
+       display: flex;
+       justify-content: space-between;
+       min-width: 180px;
+}
+
+#header h1 {
+       order: 0;
+       padding: 0;
+       margin: 0;
+       vertical-align: middle;
+       white-space: nowrap;
+}
+
+#header h1 a {
+       display: flex;
+}
+
+/*#header h2 {
+       order: 1;
+       font-size: 1.5rem;
+       margin: 0;
+       white-space: nowrap;
+}
+
+#header h2:before {
+       content: ">\00a0";
+}*/
+
+#header nav {
+       order: 3;
+       margin-left: auto;
+       display: flex;
+       flex-direction: row;
+       flex-wrap: wrap;
+       align-items: center;
+       justify-content: flex-end;
+}
+
+#header nav h2 {
+       display: none;
+}
+
+#header nav a {
+       text-align: center;
+       border-radius: .25rem;
+       padding: .375rem .5rem .25rem .5rem;
+       margin: 0 0 .1rem .5rem;
+       border: .1rem solid #00c3f9;
+       font-weight: bold;
+       background-color: #cff;
+}
+
+/* Message */
+div.error::before,
+div.error::after {
+       content: "⚠";
+       line-height: 100%;
+       margin: auto 0;
+}
+
+div.error {
+       display: flex;
+       flex-direction: row;
+       justify-content: space-between;
+       border: .05rem solid #c33333;
+       background-color: #f9c3c3;
+       color: #c33333;
+       font-size: .9rem;
+       padding: .2rem;
+       border-radius: .2rem;
+       text-align: center;
+}
+
+div.flash {
+       margin: 0 .5rem .5rem;
+}
+
+/*ul#error::before,
+ul#error::after,
+ul.error::before,
+ul.error::after,
+ul#notice::before,
+ul#notice::after {
+       content: "⚠";
+       padding: .1rem .5rem 0 .5rem;
+       position: absolute;
+}
+
+ul#error::before,
+ul#error::after {
+       padding-top: .2rem;
+}
+
+ul#notice::before,
+ul#notice::after {
+       content: "ℹ";
+       padding-top: .3rem;
+}
+
+ul#notice::after,
+ul#error::after {
+       margin-left: 37.8rem;
+}
+
+ul.error::after {
+       margin-left: 34rem;
+}
+
+ul#notice,
+ul#error,
+ul.error {
+       display: flex;
+       flex-direction: column;
+       justify-content: center;
+       box-sizing: border-box;
+       text-align: center;
+       border: .05rem solid #c33333;
+       background-color: #f9c3c3;
+       color: #c33333;
+       font-size: .9rem;
+       padding: .2rem;
+       border-radius: .2rem;
+       width: 40rem;
+       margin: 0 auto .5rem auto;
+}
+
+ul.error {
+       width: 36rem;
+       margin: .2rem 0 0 0;
+}
+
+ul#notice {
+       border: .05rem solid #3333c3;
+       background-color: #c3c3f9;
+       color: #3333c3;
+}
+
+ul.error {
+       margin-top: .2rem;
+}*/
+
+/* Content */
+#form,
+#content,
+#dashboard {
+       border: .1rem solid #00c3f9;
+       border-radius: .5rem;
+       margin: .5rem;
+       margin-top: 0;
+       overflow: hidden;
+       padding: .5rem;
+}
+
+section h2 {
+       background-color: #cff;
+       border-bottom: .1rem solid #00c3f9;
+       margin: -.5rem -.5rem .5rem -.5rem;
+       padding: .5rem;
+       padding-bottom: .4rem;
+}
+
+/* Form */
+form {
+       display: flex;
+       flex-direction: column;
+       border: .05rem solid #00c3f9;
+       border-radius: .2rem;
+       padding: .5rem;
+}
+
+form section {
+       margin-bottom: 1rem;
+}
+
+form section:only-child,
+form section:last-child,
+form section:last-of-type {
+       margin-bottom: .5rem;
+}
+
+form section section,
+form section section:only-child,
+form section section:last-child,
+form section section:last-of-type {
+       width: 50%;
+       margin-bottom: 0;
+}
+
+form div {
+       display: flex;
+       flex-direction: row;
+       justify-content: space-around;
+       margin-bottom: .5rem;
+}
+
+form div:only-child,
+form div:last-child,
+form div:last-of-type {
+       margin-bottom: 0;
+}
+
+form section div.error {
+       margin: 0 1rem;
+}
+
+form section section div.error {
+       margin: .25rem 0 0 0;
+}
+
+label {
+       min-width: 5rem;
+       font-size: .9rem;
+       padding: .2rem 0;
+       text-align: right;
+       white-space: nowrap;
+}
+
+button,
+input,
+select,
+textarea {
+       box-sizing: border-box;
+       width: 100%;
+       padding: .1rem;
+       border: .05rem solid #00c3f9;
+       border-radius: .2rem;
+       font-size: .8rem;
+       color: #066;
+}
+
+button.submit {
+       width: 25%;
+       min-width: 8rem;
+       margin: 0 auto;
+       padding: .2rem .1rem;
+}
+
+/* Vertical form */
+.form_col {
+       margin-left: .5rem;
+       width: 10rem;
+}
+
+.form_col div {
+       flex-direction: column;
+}
+
+.form_col div.error {
+       flex-direction: row;
+}
+
+.form_col label {
+       text-align: center;
+}
+
+.form_col section section,
+.form_col section section:only-child,
+.form_col section section:last-child,
+.form_col section section:last-of-type {
+       width: auto;
+}
+
+.form_col div.date {
+       flex-direction: row;
+       justify-content: space-between;
+}
+
+/* Dashboard */
+#dashboard .panel {
+       display: flex;
+       flex-direction: row;
+       flex-wrap: wrap;
+       place-content: space-between;
+}
+
+#dashboard .grid {
+       display: table;
+       border: .05rem solid #00c3f9;
+       flex-grow: 1;
+       border-radius: .2rem; /* marche pas sur chrome */
+       table-layout: fixed;    
+       width: calc(100% - 12rem);
+       border-collapse: collapse;
+}
+
+#dashboard .cell {
+       display: table-cell;
+       text-align: left;
+       border: .05rem solid #00c3f9;
+       font-size: initial;
+       height: 8rem;
+}
+
+#dashboard dl.cell {
+       height: 3rem;
+}
+
+#dashboard .cell h3 {
+       font-size: 1rem;
+       padding: .25rem;
+       margin: 0;
+}
+
+#dashboard .cell dd {
+       text-align: center;
+}
+
+#dashboard .seventh {
+       width: calc(100% / 7);
+}
+
+#dashboard .disabled {
+       color: #acc;
+       background-color: #bee;
+}
+
+#dashboard .current {
+       background-color: #cff;
+}
+
+#dashboard .next {
+       background-color: #eff;
+}
+
+#dashboard .session {
+       border-radius: .2rem;
+       border: .1rem solid #00c3f9;
+       font-size: .8rem;
+       padding: .2rem;
+       margin: 0 .1rem .1rem .1rem;
+       overflow-x: hidden;
+       white-space: nowrap;
+       text-overflow: ellipsis;
+}
+
+#dashboard .pending {
+       background-color: #ccc;
+}
+
+#dashboard .granted {
+       background-color: #cff;
+       /*background-color: #33b679;
+       border-color: #33b679;*/
+}
+
+#dashboard .disputed {
+       background-color: #fcc;
+}
+
+#dashboard .orphaned {
+       background-color: #fc9;
+}
+
+/* Footer */
+#footer {
+       border: .1rem solid #00c3f9;
+       border-radius: .5rem;
+       margin: .5rem;
+       margin-top: 0;
+       padding: .5rem;
+       text-align: center;
+       font-size: .8rem;
+       display: flex;
+       justify-content: space-between;
+       background-color: #cff;
+}
+
+#footer details {
+       font-weight: bold;
+}
+
+#footer summary::after {
+       display: none;
+}
+
+#footer summary::-webkit-details-marker {
+       display: none;
+}
+
+/* viewport responsive hack */
+@media ( max-width: 650px ) {
+       #header {
+               flex-wrap: wrap;
+       }
+
+       #dashboard .panel {
+               place-content: center;
+               flex-direction: column;
+       }
+
+       #dashboard .grid {
+               width: 100%;
+       }
+
+       #dashboard div.grid {
+               display: grid;
+       }
+
+       .form_col {
+               margin: .5rem auto 0 auto;
+       }
+}
+
diff --git a/Resources/public/ico/favicon.ico b/Resources/public/ico/favicon.ico
new file mode 100644 (file)
index 0000000..fb66588
Binary files /dev/null and b/Resources/public/ico/favicon.ico differ
diff --git a/Resources/public/png/apple.png b/Resources/public/png/apple.png
new file mode 100644 (file)
index 0000000..da559cf
Binary files /dev/null and b/Resources/public/png/apple.png differ
diff --git a/Resources/public/png/favicon.png b/Resources/public/png/favicon.png
new file mode 100644 (file)
index 0000000..85f7b97
Binary files /dev/null and b/Resources/public/png/favicon.png differ
diff --git a/Resources/public/png/logo.png b/Resources/public/png/logo.png
new file mode 100644 (file)
index 0000000..b57be8c
Binary files /dev/null and b/Resources/public/png/logo.png differ
diff --git a/Resources/public/svg/favicon.svg b/Resources/public/svg/favicon.svg
new file mode 100644 (file)
index 0000000..1e196c6
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 210 297"
+   version="1.1"
+   id="svg4570"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"
+   sodipodi:docname="openair.logo.svg">
+  <title
+     id="title5131">Open Air Logo</title>
+  <defs
+     id="defs4564">
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5174"
+       id="radialGradient5140"
+       cx="3.5708413"
+       cy="290.94611"
+       fx="3.5708413"
+       fy="290.94611"
+       r="3.5708413"
+       gradientTransform="matrix(0.96030384,0.85677319,-2.1146358,1.886651,616.04931,-259.20651)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5174">
+      <stop
+         id="stop5166"
+         offset="0"
+         style="stop-color:#00c3f9;stop-opacity:0" />
+      <stop
+         style="stop-color:#00c3f9;stop-opacity:0.33"
+         offset="0.40000001"
+         id="stop5168" />
+      <stop
+         id="stop5172"
+         offset="1"
+         style="stop-color:#00c3f9;stop-opacity:1;" />
+    </linearGradient>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="15.839192"
+     inkscape:cx="19.689661"
+     inkscape:cy="10.360728"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="2560"
+     inkscape:window-height="1537"
+     inkscape:window-x="-4"
+     inkscape:window-y="-4"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata4567">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>Open Air Logo</dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       inkscape:connector-curvature="0"
+       id="path5017"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:url(#radialGradient5140);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.11197994;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       d="m 4.2394596,289.42973 c -0.7893188,-7.7e-4 -1.6132823,0.29881 -2.1442353,0.89641 -0.5268689,0.5976 -0.790304,1.41203 -0.790304,2.44326 0,1.02758 0.2634351,1.84019 0.790304,2.43778 0.530953,0.5976 1.2456986,0.89641 2.1442353,0.89641 0.8985367,0 1.6091981,-0.29881 2.1319828,-0.89641 0.5268689,-0.59759 0.790304,-1.4102 0.790304,-2.43778 0,-1.03123 -0.2634351,-1.84566 -0.790304,-2.44326 -0.5227847,-0.5976 -1.3463617,-0.89563 -2.1319828,-0.89641 z m 0,-0.8964 c 1.2824569,0 2.3076057,0.38444 3.0754463,1.1533 0.7678406,0.76523 1.1517608,1.7928 1.1517608,3.08277 0,1.2863 -0.3839202,2.3139 -1.1517608,3.08275 C 6.5470653,296.61738 5.5219165,297 4.2394596,297 2.9529184,297 1.9236856,296.61738 1.1517607,295.85215 0.38392027,295.08693 0,294.05936 0,292.7694 c 0,-1.28997 0.38392027,-2.31754 1.1517607,-3.08277 0.7719249,-0.76886 1.8011577,-1.1533 3.0876989,-1.1533 z"
+       sodipodi:nodetypes="acscscscascscscscs"
+       inkscape:export-xdpi="100.34"
+       inkscape:export-ydpi="100.34" />
+  </g>
+</svg>
diff --git a/Resources/public/svg/logo.svg b/Resources/public/svg/logo.svg
new file mode 100644 (file)
index 0000000..159107f
--- /dev/null
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="210mm"
+   height="297mm"
+   viewBox="0 0 210 297"
+   version="1.1"
+   id="svg8"
+   sodipodi:docname="logo.svg"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)">
+  <defs
+     id="defs2">
+    <linearGradient
+       inkscape:collect="always"
+       id="linearGradient5174">
+      <stop
+         id="stop5166"
+         offset="0"
+         style="stop-color:#00c3f9;stop-opacity:0" />
+      <stop
+         style="stop-color:#00c3f9;stop-opacity:0.33"
+         offset="0.40000001"
+         id="stop5168" />
+      <stop
+         id="stop5172"
+         offset="1"
+         style="stop-color:#00c3f9;stop-opacity:1;" />
+    </linearGradient>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath18">
+      <path
+         d="M 3044.85,0 C 1363.23,0 0,203.461 0,454.441 0,705.41 1363.23,908.859 3044.85,908.859 4726.47,908.859 6089.69,705.41 6089.69,454.441 6089.69,203.461 4726.47,0 3044.85,0"
+         id="path20" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath30">
+      <path
+         d="M 0,3832 0,0 l 6089.69,0 0,3832 L 0,3832 Z"
+         id="path32" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath80">
+      <path
+         d="m 2788.36,2246.6 -256.48,0 c -3.06,0.66 -19.07,4.34 -19.07,4.34 -17.41,4.1 -33.17,8.18 -48.17,12.44 l -6.49,1.93 c -15.5,4.52 -30.45,9.28 -44.44,14.09 l -4.55,1.52 c -13.92,4.85 -28.16,10.26 -43.53,16.52 l -8.06,3.3 c -14.03,5.88 -27.59,11.99 -40.31,18.1 l -6.54,3.26 c -14.56,7.15 -27.3,13.83 -39.59,20.71 -11.57,6.57 -23.65,13.86 -36.94,22.42 l -5.91,3.79 c -11.33,7.43 -22.51,15.21 -33.21,23.16 l -5.56,4.17 c -11.98,9.07 -22.38,17.36 -31.8,25.43 l -1.19,1.03 c -9.72,8.35 -19.3,17.09 -29.29,26.74 l -3.52,3.35 c -8.91,8.76 -17.92,18.14 -26.74,27.89 l -4.06,4.54 c -9.19,10.33 -17.29,19.98 -24.77,29.48 l -1.68,2.18 c -8.07,10.36 -15.78,20.86 -22.89,31.21 l -1.03,1.46 c -6.93,10.11 -13.69,20.71 -21.23,33.28 l -1.86,3.11 c -6.58,11.16 -12.75,22.33 -18.31,33.13 l -1.27,2.53 c -11.68,22.94 -22.2,46.97 -31.59,72.27 l -0.64,1.7 c -4.41,12.01 -8.53,24.3 -12.26,36.52 l -0.6,1.99 c -11.33,37.51 -19.82,76.79 -25.31,117.56 -0.35,2.6 -0.55,5.25 -0.88,7.85 30.2,9.7 62.33,15.04 95.71,15.04 9.99,0 20.46,-0.6 32,-1.82 l 15.43,-1.64 1.44,15.45 c 15.75,169.23 79.48,321.07 167.3,405.86 -19.6,-51.67 -34.76,-105.27 -45.24,-160 -18.26,-95.59 -21.4,-190.06 -9.27,-280.74 5.48,-40.77 13.97,-80.05 25.31,-117.56 l 0.59,-1.99 c 3.73,-12.22 7.86,-24.51 12.27,-36.52 l 0.63,-1.7 c 9.39,-25.3 19.91,-49.33 31.6,-72.27 l 1.26,-2.53 c 5.56,-10.8 11.74,-21.97 18.31,-33.13 l 1.87,-3.11 c 7.53,-12.57 14.3,-23.17 21.22,-33.28 l 1.04,-1.46 c 7.11,-10.35 14.82,-20.85 22.89,-31.21 l 1.67,-2.18 c 7.49,-9.5 15.58,-19.15 24.77,-29.48 l 4.06,-4.54 c 8.82,-9.75 17.83,-19.13 26.75,-27.89 l 3.51,-3.35 c 9.99,-9.65 19.58,-18.39 29.29,-26.74 l 1.19,-1.03 c 9.42,-8.07 19.83,-16.36 31.8,-25.43 l 5.56,-4.17 c 10.71,-7.95 21.89,-15.73 33.21,-23.16 l 5.91,-3.79 c 13.29,-8.56 25.37,-15.85 36.95,-22.42 12.28,-6.88 25.03,-13.56 39.59,-20.71 l 6.53,-3.26 c 12.72,-6.11 26.28,-12.22 40.31,-18.1 l 8.06,-3.3 c 15.38,-6.26 29.61,-11.67 43.53,-16.52 l 4.55,-1.52 c 13.99,-4.81 28.95,-9.57 44.45,-14.09 l 6.47,-1.93 c 15.01,-4.26 30.77,-8.34 48.18,-12.44 0,0 16.02,-3.68 19.07,-4.34"
+         id="path82" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath92">
+      <path
+         d="m 4078.74,2296.83 c -70.64,7.85 -137.5,18.24 -200.68,31.03 150.52,95.28 244.88,262.64 244.88,443.38 0,245.43 -173.93,461.07 -413.56,512.75 l -7.73,1.66 -3.11,7.27 c -86.59,201.73 -254.36,351.23 -455.56,417.29 11.47,19.88 20.58,32.95 25.61,37.54 299.13,-1.39 568.3,-179.6 686.42,-454.83 l 3.12,-7.27 7.73,-1.66 c 239.62,-51.68 413.56,-267.32 413.56,-512.75 0,-202.21 -117.9,-387.97 -300.68,-474.41"
+         id="path94" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath104">
+      <path
+         d="m 3549.34,2246.6 -62.19,0 c -87.81,0 -171.3,4.24 -250.34,12.59 -23.74,5.79 -47.08,12.04 -69.74,19.01 -13.3,4.1 -26.3,8.41 -39.2,12.89 l -1.92,0.68 c -12.67,4.47 -25.13,9.11 -37.23,13.88 l -1.87,0.74 c -12.34,4.9 -24.42,9.97 -36.99,15.52 -116.98,51.84 -215.06,122.74 -292.07,211.34 -7.01,8.09 -13.8,16.3 -20.4,24.61 l -1.27,1.6 c -6.44,8.15 -12.67,16.4 -18.71,24.74 l -1.44,1.99 c -5.99,8.35 -11.81,16.78 -17.36,25.22 l -1.17,1.8 c -5.69,8.69 -11.2,17.48 -16.94,27.12 -47.66,80.21 -80.73,170.1 -98.31,267.17 l -0.3,1.63 c -1.73,9.67 -3.29,19.39 -4.89,30.48 l -0.24,1.59 c -1.34,9.5 -2.52,19 -3.58,28.53 l -0.38,3.54 c -1,9.47 -1.86,18.96 -2.58,28.51 l -0.25,3.42 c -0.68,9.61 -1.23,19.25 -1.63,28.84 l -0.08,2.66 c -0.4,9.98 -0.65,19.98 -0.73,30.57 -0.65,93.21 11.69,187.94 36.97,282.57 2.61,9.73 5.36,19.4 8.22,29.04 l 0.48,1.61 c 2.86,9.55 5.84,19.05 8.94,28.51 l 0.51,1.54 c 0.69,2.07 1.41,4.17 2.11,6.24 131.82,235.2 366.62,359.44 498.49,391.61 -19.1,-17.91 -37.78,-37.4 -55.88,-58.3 -74,-85.49 -135.6,-192.8 -178.13,-310.32 -3.67,-10.2 -6.95,-19.7 -10.11,-29.23 l -0.5,-1.54 c -3.11,-9.46 -6.09,-18.96 -8.95,-28.51 l -0.48,-1.61 c -2.87,-9.64 -5.61,-19.31 -8.22,-29.04 -25.28,-94.63 -37.62,-189.36 -36.96,-282.57 0.08,-10.59 0.32,-20.59 0.72,-30.57 l 0.09,-2.66 c 0.39,-9.59 0.95,-19.23 1.62,-28.84 l 0.25,-3.42 c 0.72,-9.55 1.59,-19.04 2.59,-28.51 l 0.38,-3.54 c 1.05,-9.53 2.23,-19.03 3.57,-28.53 l 0.24,-1.59 c 1.61,-11.09 3.16,-20.81 4.89,-30.48 l 0.31,-1.63 c 17.56,-97.07 50.65,-186.96 98.31,-267.17 5.74,-9.64 11.25,-18.43 16.93,-27.12 l 1.17,-1.8 c 5.56,-8.44 11.37,-16.87 17.37,-25.22 l 1.43,-1.99 c 6.05,-8.34 12.28,-16.59 18.72,-24.74 l 1.27,-1.6 c 6.59,-8.31 13.39,-16.52 20.4,-24.61 76.99,-88.6 175.08,-159.5 292.06,-211.34 12.56,-5.55 24.66,-10.62 36.99,-15.52 l 1.87,-0.74 c 12.11,-4.77 24.55,-9.41 37.25,-13.88 l 1.91,-0.68 c 12.89,-4.48 25.91,-8.79 39.21,-12.89 40.2,-12.38 82.15,-22.92 125.77,-31.6"
+         id="path106" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath18-8">
+      <path
+         d="M 3044.85,0 C 1363.23,0 0,203.461 0,454.441 0,705.41 1363.23,908.859 3044.85,908.859 4726.47,908.859 6089.69,705.41 6089.69,454.441 6089.69,203.461 4726.47,0 3044.85,0"
+         id="path20-1" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath30-2">
+      <path
+         d="M 0,3832 0,0 l 6089.69,0 0,3832 L 0,3832 Z"
+         id="path32-0" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath80-2">
+      <path
+         d="m 2788.36,2246.6 -256.48,0 c -3.06,0.66 -19.07,4.34 -19.07,4.34 -17.41,4.1 -33.17,8.18 -48.17,12.44 l -6.49,1.93 c -15.5,4.52 -30.45,9.28 -44.44,14.09 l -4.55,1.52 c -13.92,4.85 -28.16,10.26 -43.53,16.52 l -8.06,3.3 c -14.03,5.88 -27.59,11.99 -40.31,18.1 l -6.54,3.26 c -14.56,7.15 -27.3,13.83 -39.59,20.71 -11.57,6.57 -23.65,13.86 -36.94,22.42 l -5.91,3.79 c -11.33,7.43 -22.51,15.21 -33.21,23.16 l -5.56,4.17 c -11.98,9.07 -22.38,17.36 -31.8,25.43 l -1.19,1.03 c -9.72,8.35 -19.3,17.09 -29.29,26.74 l -3.52,3.35 c -8.91,8.76 -17.92,18.14 -26.74,27.89 l -4.06,4.54 c -9.19,10.33 -17.29,19.98 -24.77,29.48 l -1.68,2.18 c -8.07,10.36 -15.78,20.86 -22.89,31.21 l -1.03,1.46 c -6.93,10.11 -13.69,20.71 -21.23,33.28 l -1.86,3.11 c -6.58,11.16 -12.75,22.33 -18.31,33.13 l -1.27,2.53 c -11.68,22.94 -22.2,46.97 -31.59,72.27 l -0.64,1.7 c -4.41,12.01 -8.53,24.3 -12.26,36.52 l -0.6,1.99 c -11.33,37.51 -19.82,76.79 -25.31,117.56 -0.35,2.6 -0.55,5.25 -0.88,7.85 30.2,9.7 62.33,15.04 95.71,15.04 9.99,0 20.46,-0.6 32,-1.82 l 15.43,-1.64 1.44,15.45 c 15.75,169.23 79.48,321.07 167.3,405.86 -19.6,-51.67 -34.76,-105.27 -45.24,-160 -18.26,-95.59 -21.4,-190.06 -9.27,-280.74 5.48,-40.77 13.97,-80.05 25.31,-117.56 l 0.59,-1.99 c 3.73,-12.22 7.86,-24.51 12.27,-36.52 l 0.63,-1.7 c 9.39,-25.3 19.91,-49.33 31.6,-72.27 l 1.26,-2.53 c 5.56,-10.8 11.74,-21.97 18.31,-33.13 l 1.87,-3.11 c 7.53,-12.57 14.3,-23.17 21.22,-33.28 l 1.04,-1.46 c 7.11,-10.35 14.82,-20.85 22.89,-31.21 l 1.67,-2.18 c 7.49,-9.5 15.58,-19.15 24.77,-29.48 l 4.06,-4.54 c 8.82,-9.75 17.83,-19.13 26.75,-27.89 l 3.51,-3.35 c 9.99,-9.65 19.58,-18.39 29.29,-26.74 l 1.19,-1.03 c 9.42,-8.07 19.83,-16.36 31.8,-25.43 l 5.56,-4.17 c 10.71,-7.95 21.89,-15.73 33.21,-23.16 l 5.91,-3.79 c 13.29,-8.56 25.37,-15.85 36.95,-22.42 12.28,-6.88 25.03,-13.56 39.59,-20.71 l 6.53,-3.26 c 12.72,-6.11 26.28,-12.22 40.31,-18.1 l 8.06,-3.3 c 15.38,-6.26 29.61,-11.67 43.53,-16.52 l 4.55,-1.52 c 13.99,-4.81 28.95,-9.57 44.45,-14.09 l 6.47,-1.93 c 15.01,-4.26 30.77,-8.34 48.18,-12.44 0,0 16.02,-3.68 19.07,-4.34"
+         id="path82-9" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath92-4">
+      <path
+         d="m 4078.74,2296.83 c -70.64,7.85 -137.5,18.24 -200.68,31.03 150.52,95.28 244.88,262.64 244.88,443.38 0,245.43 -173.93,461.07 -413.56,512.75 l -7.73,1.66 -3.11,7.27 c -86.59,201.73 -254.36,351.23 -455.56,417.29 11.47,19.88 20.58,32.95 25.61,37.54 299.13,-1.39 568.3,-179.6 686.42,-454.83 l 3.12,-7.27 7.73,-1.66 c 239.62,-51.68 413.56,-267.32 413.56,-512.75 0,-202.21 -117.9,-387.97 -300.68,-474.41"
+         id="path94-6" />
+    </clipPath>
+    <clipPath
+       clipPathUnits="userSpaceOnUse"
+       id="clipPath104-6">
+      <path
+         d="m 3549.34,2246.6 -62.19,0 c -87.81,0 -171.3,4.24 -250.34,12.59 -23.74,5.79 -47.08,12.04 -69.74,19.01 -13.3,4.1 -26.3,8.41 -39.2,12.89 l -1.92,0.68 c -12.67,4.47 -25.13,9.11 -37.23,13.88 l -1.87,0.74 c -12.34,4.9 -24.42,9.97 -36.99,15.52 -116.98,51.84 -215.06,122.74 -292.07,211.34 -7.01,8.09 -13.8,16.3 -20.4,24.61 l -1.27,1.6 c -6.44,8.15 -12.67,16.4 -18.71,24.74 l -1.44,1.99 c -5.99,8.35 -11.81,16.78 -17.36,25.22 l -1.17,1.8 c -5.69,8.69 -11.2,17.48 -16.94,27.12 -47.66,80.21 -80.73,170.1 -98.31,267.17 l -0.3,1.63 c -1.73,9.67 -3.29,19.39 -4.89,30.48 l -0.24,1.59 c -1.34,9.5 -2.52,19 -3.58,28.53 l -0.38,3.54 c -1,9.47 -1.86,18.96 -2.58,28.51 l -0.25,3.42 c -0.68,9.61 -1.23,19.25 -1.63,28.84 l -0.08,2.66 c -0.4,9.98 -0.65,19.98 -0.73,30.57 -0.65,93.21 11.69,187.94 36.97,282.57 2.61,9.73 5.36,19.4 8.22,29.04 l 0.48,1.61 c 2.86,9.55 5.84,19.05 8.94,28.51 l 0.51,1.54 c 0.69,2.07 1.41,4.17 2.11,6.24 131.82,235.2 366.62,359.44 498.49,391.61 -19.1,-17.91 -37.78,-37.4 -55.88,-58.3 -74,-85.49 -135.6,-192.8 -178.13,-310.32 -3.67,-10.2 -6.95,-19.7 -10.11,-29.23 l -0.5,-1.54 c -3.11,-9.46 -6.09,-18.96 -8.95,-28.51 l -0.48,-1.61 c -2.87,-9.64 -5.61,-19.31 -8.22,-29.04 -25.28,-94.63 -37.62,-189.36 -36.96,-282.57 0.08,-10.59 0.32,-20.59 0.72,-30.57 l 0.09,-2.66 c 0.39,-9.59 0.95,-19.23 1.62,-28.84 l 0.25,-3.42 c 0.72,-9.55 1.59,-19.04 2.59,-28.51 l 0.38,-3.54 c 1.05,-9.53 2.23,-19.03 3.57,-28.53 l 0.24,-1.59 c 1.61,-11.09 3.16,-20.81 4.89,-30.48 l 0.31,-1.63 c 17.56,-97.07 50.65,-186.96 98.31,-267.17 5.74,-9.64 11.25,-18.43 16.93,-27.12 l 1.17,-1.8 c 5.56,-8.44 11.37,-16.87 17.37,-25.22 l 1.43,-1.99 c 6.05,-8.34 12.28,-16.59 18.72,-24.74 l 1.27,-1.6 c 6.59,-8.31 13.39,-16.52 20.4,-24.61 76.99,-88.6 175.08,-159.5 292.06,-211.34 12.56,-5.55 24.66,-10.62 36.99,-15.52 l 1.87,-0.74 c 12.11,-4.77 24.55,-9.41 37.25,-13.88 l 1.91,-0.68 c 12.89,-4.48 25.91,-8.79 39.21,-12.89 40.2,-12.38 82.15,-22.92 125.77,-31.6"
+         id="path106-1" />
+    </clipPath>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5174"
+       id="linearGradient5075"
+       x1="1.018965"
+       y1="285.02298"
+       x2="7.7508168"
+       y2="285.02298"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5174"
+       id="radialGradient5140"
+       cx="3.5708413"
+       cy="290.94611"
+       fx="3.5708413"
+       fy="290.94611"
+       r="3.5708413"
+       gradientTransform="matrix(0.81002191,0.81002303,-1.7837077,1.7837053,519.6412,-230.90846)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient5174"
+       id="radialGradient5202"
+       cx="34.65731"
+       cy="290.9346"
+       fx="34.65731"
+       fy="290.9346"
+       r="3.534668"
+       gradientTransform="matrix(1,0,0,1.0913743,0,-26.583936)"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8"
+     inkscape:cx="33.962344"
+     inkscape:cy="136.63132"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1666"
+     inkscape:window-height="1294"
+     inkscape:window-x="436"
+     inkscape:window-y="107"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Calque 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#006666;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
+       x="7.7265034"
+       y="294.79858"
+       id="text4105"
+       inkscape:export-xdpi="99.20594"
+       inkscape:export-ydpi="99.20594"><tspan
+         sodipodi:role="line"
+         id="tspan4103"
+         x="7.7265034"
+         y="294.79858"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;stroke-width:0.26458332;fill:#006666;fill-opacity:1;">pen</tspan></text>
+    <path
+       inkscape:connector-curvature="0"
+       id="path5017"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:1.25;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:url(#radialGradient5140);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
+       d="m 3.576009,287.79126 c -0.6657944,-7.3e-4 -1.3608126,0.2825 -1.808675,0.84749 -0.4444173,0.565 -0.666626,1.33498 -0.666626,2.30994 0,0.97152 0.2222087,1.73978 0.666626,2.30477 0.4478624,0.56499 1.0507541,0.84749 1.808675,0.84749 0.757921,0 1.3573676,-0.2825 1.7983398,-0.84749 0.4444173,-0.56499 0.666626,-1.33325 0.666626,-2.30477 0,-0.97496 -0.2222087,-1.74494 -0.666626,-2.30994 -0.4409722,-0.56499 -1.1356634,-0.84676 -1.7983398,-0.84749 z m 0,-0.84749 c 1.08176,0 1.946479,0.36346 2.5941569,1.09037 0.6476779,0.72347 0.9715169,1.69498 0.9715169,2.91455 0,1.21612 -0.323839,2.18764 -0.9715169,2.91455 -0.6476779,0.72347 -1.5123969,1.08521 -2.5941569,1.08521 -1.085205,0 -1.953369,-0.36174 -2.60449208,-1.08521 C 0.32383899,293.13977 2.1886081e-8,292.16826 2.1886081e-8,290.94869 2.1886081e-8,289.72912 0.32383899,288.75761 0.97151692,288.03414 1.62264,287.30723 2.490804,286.94377 3.576009,286.94377 Z"
+       sodipodi:nodetypes="acscscscascscscscs"
+       inkscape:export-xdpi="99.20594"
+       inkscape:export-ydpi="99.20594" />
+    <rect
+       style="fill:url(#linearGradient5075);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.1;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:0, 1.09999998;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill"
+       id="rect5067"
+       width="6.7318516"
+       height="1.3363476"
+       x="1.018965"
+       y="284.3548" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:url(#radialGradient5202);fill-opacity:1.0;stroke:none;stroke-width:0.26458332"
+       x="31.039961"
+       y="294.79224"
+       id="text5152"
+       inkscape:export-xdpi="99.20594"
+       inkscape:export-ydpi="99.20594"><tspan
+         sodipodi:role="line"
+         id="tspan5150"
+         x="31.039961"
+         y="294.79224"
+         style="fill:url(#radialGradient5202);fill-opacity:1.0;stroke-width:0.26458332">A</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#006666;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
+       x="38.277328"
+       y="294.79953"
+       id="text5156"
+       inkscape:export-xdpi="99.20594"
+       inkscape:export-ydpi="99.20594"><tspan
+         sodipodi:role="line"
+         id="tspan5154"
+         x="38.277328"
+         y="294.79953"
+         style="fill:#006666;fill-opacity:1;stroke-width:0.26458332;">ir</tspan></text>
+  </g>
+</svg>
diff --git a/Resources/public/xcf/openair.xcf b/Resources/public/xcf/openair.xcf
new file mode 100644 (file)
index 0000000..ebe102b
Binary files /dev/null and b/Resources/public/xcf/openair.xcf differ
diff --git a/Resources/translations/messages.fr.yml b/Resources/translations/messages.fr.yml
new file mode 100644 (file)
index 0000000..40c37fe
--- /dev/null
@@ -0,0 +1,60 @@
+'Outdoor space reservation system': 'Système de réservation d''espace en plein air'
+'Open Air': 'Air libre'
+Navigation: Navigation
+About: 'À propos'
+Index: Accueil
+Admin: Administration
+Contact: Contact
+'Raphaël Gertz all rights reserved': 'Raphaël Gertz tous droits réservés'
+'Copyright 2018': 'Droit d''auteur 2018'
+Send: Envoyer
+Subject: Sujet
+'Your name': 'Votre nom'
+'Your mail address': 'Votre courriel'
+'Your message has been sent': 'Votre message a bien été envoyé'
+'Your message here': 'Votre message ici'
+'Your password': 'Votre mot de passe'
+Logout: Déconnection
+Register: Enregistrement
+Login: Identification
+Recover: Récupération
+Dashboard: Tableau de bord
+'Hi, %name%': 'Salut, %name%'
+'Subject:': 'Sujet :'
+'Name': 'Nom'
+'Message': 'Message'
+'Title': 'Civilité'
+'Your title': 'Votre civilité'
+'Forename': 'Prénom'
+'Your forename': 'Votre prénom'
+'Surname': 'Nom'
+'Your surname': 'Votre nom'
+'Pseudonym': 'Pseudonyme'
+'Your pseudonym': 'Votre pseudonyme'
+'Mail': 'Courriel'
+'Password': 'Mot de passe'
+'Confirm password': 'Confirmation'
+'Your password confirmation': 'Votre confirmation de mot de passe'
+'Welcome to %title%': 'Bienvenue sur %title%'
+'Thanks so much for joining us, from now on, you can reserve your %title% spaces.': 'Merci de nous avoir rejoint, à partir de maintenant, vous pouvez réserver vos espaces à l''%title%.'
+'Your account has been created': 'Votre compte a bien été créé'
+'Invalid credentials.': 'Identifiants invalides'
+'Thanks so much for joining us, to recover your account you can follow this link: <a href="%url%">%url%</a>': 'Merci de nous avoir rejoint, pour récupérer votre compte vous pouvez suivre ce lien : <a href="%url%">%url%</a>'
+'Your password is updated and an account recover message has been sent': 'Votre mot de passe est modifié et un message de récupération de compte a été envoyé'
+'Unable to find account': 'Impossible de trouver le compte'
+'Account already exists: %mail%': 'Le compte existe déjà : %mail%'
+'Your recover account message has been sent': 'Votre message de récupération de compte a été envoyé'
+'To recover your account click here': 'Pour récupérer votre compte cliquez ici'
+'Recover account on %title%': 'Récupération de compte sur %title%'
+'Account recovered on %title%': 'Compte récupéré sur %title%'
+'Your account password has been changed, to recover your account you can follow this link: <a href="%url%">%url%</a>': 'Votre mot de passe a été changé, pour récupérer votre compte vous pouvez suivre ce lien : <a href="%url%">%url%</a>'
+'Authentication request could not be processed due to a system problem.': 'Authentification impossible suite à un problème système'
+Location: Emplacement
+Your location: Votre emplacement
+Date: Date
+Your date: Votre date
+Slot: Créneau
+Your slot: Votre créneau
+'Application request the %date% for %location% on the slot %slot% saved': 'Réservation le %date% pour %location% sur le créneau %slot% enregistrée'
+#'Application request the %date% for %location% on the slot %slot% already exists': 'Réservation le %date% pour %location% sur le créneau %slot% déjà existante'
+'Application already exists': 'Réservation déjà existante'
diff --git a/Resources/translations/validators.fr.yml b/Resources/translations/validators.fr.yml
new file mode 100644 (file)
index 0000000..e945217
--- /dev/null
@@ -0,0 +1,11 @@
+'Please provide your name': 'Veuillez fournir votre nom'
+'Please provide your mail': 'Veuillez fournir votre courriel'
+'Please provide a valid mail': 'Veuillez fournir un courriel valide'
+'Please provide your pseudonym': 'Veuillez fournir votre pseudonyme'
+'Please provide your forename': 'Veuillez fournir votre prénom'
+'Please provide your surname': 'Veuillez fournir votre nom'
+'Please provide your password': 'Veuillez fournir votre mot de passe'
+'Please provide your subject': 'Veuillez fournir votre sujet'
+'Please provide your message': 'Veuillez fournir votre message'
+'The password and confirmation must match': 'Le mot de passe doit correspondre à la confirmation'
+'Your mail doesn''t seems to be valid': 'Votre courriel ne semble pas valable'
diff --git a/Resources/views/admin/index.html.twig b/Resources/views/admin/index.html.twig
new file mode 100644 (file)
index 0000000..687e217
--- /dev/null
@@ -0,0 +1,57 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+<section id="dashboard">
+       <h2>{% trans %}Dashboard{% endtrans %}</h2>
+       <div class="panel">
+               {% if calendar is defined and calendar %}
+                       <table class="grid">
+                               <tbody>
+                                       <tr>
+                                               {% for date, day in calendar %}
+                                                       <td class="{{ ['cell', 'seventh']|merge(day.class)|join(' ') }}">
+                                                               <h3>{{ day.title }}</h3>
+                                                               {% if day.sessions is not empty %}
+                                                                       <ul>
+                                                                               {% for session in day.sessions %}
+                                                                                       <li class="{{ ['session']|merge(session.class)|join(' ') }}">
+                                                                                               <a href="{{ path('rapsys_air_session', {'id': session.id}) }}" title="{{ session.title }}">{{ session.title }}</a>
+                                                                                       </li>
+                                                                               {% endfor %}
+                                                                       </ul>
+                                                               {% endif %}
+                                                       </td>
+                                                       {% if loop.index % 7 == 0 and not loop.last %}
+                                                               </tr>
+                                                               <tr>
+                                                       {% endif %}
+                                               {% endfor %}
+                                       </tr>
+                               </tbody>
+                       </table>
+               {% endif %}
+               <div>
+               {{ form_start(form) }}
+
+                       <header>{{ form_errors(form) }}</header>
+
+                       <section>
+                               {{ form_row(form.location) }}
+
+                               {{ form_row(form.date) }}
+
+                               {{ form_row(form.slot) }}
+                       </section>
+
+                       {{ form_row(form.submit) }}
+
+                       {# Render CSRF token etc .#}
+                       <footer style="display:none">
+                               {{ form_rest(form) }}
+                       </footer>
+
+               {{ form_end(form) }}
+               </div>
+       </div>
+       {# dump(calendar) #}
+</section>
+{% endblock %}
diff --git a/Resources/views/admin/index.html.twig.toto b/Resources/views/admin/index.html.twig.toto
new file mode 100644 (file)
index 0000000..d529910
--- /dev/null
@@ -0,0 +1 @@
+|length > 21 ? session.title|slice(0, 21) ~ '...' : session.title
diff --git a/Resources/views/admin/session.html.twig b/Resources/views/admin/session.html.twig
new file mode 100644 (file)
index 0000000..ef61643
--- /dev/null
@@ -0,0 +1,31 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+<section id="dashboard">
+       <h2>{{ section }}</h2>
+       <div class="panel">
+               <div class="grid">
+                       <dl class="cell">
+                               <dt><h3>{% trans %}Location{% endtrans %}</h3></dt>
+                               <dd>{{ session.location.getTitle() }}</dd>
+                       </dl>
+                       <dl class="cell">
+                               <dt><h3>{% trans %}Date{% endtrans %}</h3></dt>
+                               <dd>{{ session.date.format('Y-m-d') }}</dd>
+                       </dl>
+                       <dl class="cell">
+                               <dt><h3>{% trans %}Slot{% endtrans %}</h3></dt>
+                               <dd>{{ session.slot.getTitle() }}</dd>
+                       </dl>
+                       <dl class="cell">
+                               <dt><h3>{% trans %}Application{% endtrans %}</h3></dt>
+                               {% if session.application is null %}
+                                       <dd>{% trans %}None{% endtrans %}</dd>
+                               {% else %}
+                                       <dd>{{ session.application.getTitle() }}</dd>
+                               {% endif %}
+                       </dl>
+               </div>
+       </div>
+       {{ dump(session) }}
+</section>
+{% endblock %}
diff --git a/Resources/views/base.html.twig b/Resources/views/base.html.twig
new file mode 100644 (file)
index 0000000..a95e21a
--- /dev/null
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8" />
+               <title>{% block title %}{{ title }}{% endblock %}</title>
+               <meta name="viewport" content="width=device-width, initial-scale=1" />
+               {% block stylesheet %}
+                       {% stylesheet '//fonts.googleapis.com/css?family=Irish+Grover' '//fonts.googleapis.com/css?family=La+Belle+Aurore' '@RapsysAirBundle/Resources/public/css/{reset,screen}.css' %}
+                               <link href="{{ asset_url }}" type="text/css" rel="stylesheet" />
+                       {% endstylesheet %}
+               {% endblock %}
+               <!--{% block javascript %}
+                       {% javascript '@RapsysAirBundle/Resources/public/js/*.js' %}
+                               <script src="{{ asset_url }}"></script>
+                       {% endjavascript %}
+               {% endblock %}-->
+               <link rel="icon" type="image/png" href="{{ asset('bundles/rapsysair/png/favicon.png') }}" sizes="192x192" />
+               <link rel="shortcut icon" type="image/x-icon" href="{{ asset('bundles/rapsysair/ico/favicon.ico') }}" />
+       </head>
+       {% block body %}
+               <body>
+                       {% block header %}
+                               <header id="header">
+                                       {% block header_title %}
+                                               {% block blog_title %}<h1><a href="{{ path('rapsys_air_homepage') }}"><img src="{{ asset('bundles/rapsysair/png/logo.png') }}" alt="{% trans %}Open Air{% endtrans %}" /></a></h1>{% endblock %}
+                                               {% block header_nav %}
+                                                       <nav>
+                                                               <h2>{% trans %}Navigation{% endtrans %}</h2>
+                                                               <a href="{{ path('rapsys_air_admin') }}">{% trans %}Admin{% endtrans %}</a>
+                                                               <a href="{{ path('rapsys_air_contact') }}">{% trans %}Contact{% endtrans %}</a>
+                                                               {% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
+                                                                       <a href="{{ path('rapsys_user_logout') }}">{% trans %}Logout{% endtrans %}</a>
+                                                               {% else %}
+                                                                       <a href="{{ path('rapsys_user_register') }}">{% trans %}Register{% endtrans %}</a>
+                                                               {% endif %}
+                                                       </nav>
+                                               {% endblock %}
+                                               {% block blog_subtitle %}{% endblock %}
+                                               {% block blog_tagline %}
+                                                       {% if tags is defined and tags %}
+                                                               <ul>
+                                                                       {% for id, tag in tags %}
+                                                                               <li><h2><a href="#">Tag</a></h2></li>
+                                                                       {% endfor %}
+                                                               </ul>
+                                                       {% endif %}
+                                               {% endblock %}
+                                       {% endblock %}
+                               </header>
+                       {% endblock %}
+
+                       {# pass an array argument to get the messages of those types (['warning', 'error']) #}
+                       {% for label, messages in app.flashes %}
+                               {% if messages %}
+                                       <div class="flash {{label}}">
+                                               <ul>
+                                                       {% for message in messages %}
+                                                               <li>{{ message }}</li>
+                                                       {% endfor %}
+                                               </ul>
+                                       </div>
+                               {% endif %}
+                       {% endfor %}
+
+                       {% block sidebar %}<aside id="sidebar"></aside>{% endblock %}
+
+                       {% block content %}<section id="content"></section>{% endblock %}
+
+                       {% block footer %}
+                               <footer id="footer">
+                                       <span>&nbsp;</span>
+                                       <details><summary>{% trans %}Raphaël Gertz all rights reserved{% endtrans %}</summary><span>{% trans %}Copyright 2018{% endtrans %}.</span></details>
+                                       <span>&nbsp;</span>
+                               </footer>
+                       {% endblock %}
+               </body>
+       {% endblock %}
+</html>
diff --git a/Resources/views/form/contact.html.twig b/Resources/views/form/contact.html.twig
new file mode 100644 (file)
index 0000000..b75b91b
--- /dev/null
@@ -0,0 +1,34 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+       <section id="form">
+               <h2><a href="{{ path('rapsys_air_contact') }}">{{ section }}</a></h2>
+               {% if sent %}
+                       <p>{% trans %}Your message has been sent{% endtrans %}</p>
+               {% else %}
+                       <div>
+                               {{ form_start(form) }}
+
+                                       <header>{{ form_errors(form) }}</header>
+
+                                       <section>
+                                               {{ form_row(form.subject) }}
+
+                                               {{ form_row(form.name) }}
+
+                                               {{ form_row(form.mail) }}
+
+                                               {{ form_row(form.message) }}
+                                       </section>
+
+                                       {{ form_row(form.submit) }}
+
+                                       {# Render CSRF token etc .#}
+                                       <footer style="display:none">
+                                               {{ form_rest(form) }}
+                                       </footer>
+
+                               {{ form_end(form) }}
+                       </div>
+               {% endif %}
+       </section>
+{% endblock %}
diff --git a/Resources/views/form/form_div_layout.html.twig b/Resources/views/form/form_div_layout.html.twig
new file mode 100644 (file)
index 0000000..7f73c51
--- /dev/null
@@ -0,0 +1,417 @@
+{# Widgets #}
+
+{%- block form_widget -%}
+    {% if compound %}
+        {{- block('form_widget_compound') -}}
+    {% else %}
+        {{- block('form_widget_simple') -}}
+    {% endif %}
+{%- endblock form_widget -%}
+
+{%- block form_widget_simple -%}
+    {%- set type = type|default('text') -%}
+    <input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
+{%- endblock form_widget_simple -%}
+
+{%- block form_widget_compound -%}
+    <div {{ block('widget_container_attributes') }}>
+        {%- if form is rootform -%}
+            {{ form_errors(form) }}
+        {%- endif -%}
+        {{- block('form_rows') -}}
+        {{- form_rest(form) -}}
+    </div>
+{%- endblock form_widget_compound -%}
+
+{%- block collection_widget -%}
+    {% if prototype is defined %}
+        {%- set attr = attr|merge({'data-prototype': form_row(prototype) }) -%}
+    {% endif %}
+    {{- block('form_widget') -}}
+{%- endblock collection_widget -%}
+
+{%- block textarea_widget -%}
+    <textarea {{ block('widget_attributes') }}>{{ value }}</textarea>
+{%- endblock textarea_widget -%}
+
+{%- block choice_widget -%}
+    {% if expanded %}
+        {{- block('choice_widget_expanded') -}}
+    {% else %}
+        {{- block('choice_widget_collapsed') -}}
+    {% endif %}
+{%- endblock choice_widget -%}
+
+{%- block choice_widget_expanded -%}
+    <div {{ block('widget_container_attributes') }}>
+    {%- for child in form %}
+        {{- form_widget(child) -}}
+        {{- form_label(child, null, {translation_domain: choice_translation_domain}) -}}
+    {% endfor -%}
+    </div>
+{%- endblock choice_widget_expanded -%}
+
+{%- block choice_widget_collapsed -%}
+    {%- if required and placeholder is none and not placeholder_in_choices and not multiple and (attr.size is not defined or attr.size <= 1) -%}
+        {% set required = false %}
+    {%- endif -%}
+    <select {{ block('widget_attributes') }}{% if multiple %} multiple="multiple"{% endif %}>
+        {%- if placeholder is not none -%}
+            <option value=""{% if required and value is empty %} selected="selected"{% endif %}>{{ placeholder != '' ? (translation_domain is same as(false) ? placeholder : placeholder|trans({}, translation_domain)) }}</option>
+        {%- endif -%}
+        {%- if preferred_choices|length > 0 -%}
+            {% set options = preferred_choices %}
+            {{- block('choice_widget_options') -}}
+            {%- if choices|length > 0 and separator is not none -%}
+                <option disabled="disabled">{{ separator }}</option>
+            {%- endif -%}
+        {%- endif -%}
+        {%- set options = choices -%}
+        {{- block('choice_widget_options') -}}
+    </select>
+{%- endblock choice_widget_collapsed -%}
+
+{%- block choice_widget_options -%}
+    {% for group_label, choice in options %}
+        {%- if choice is iterable -%}
+            <optgroup label="{{ choice_translation_domain is same as(false) ? group_label : group_label|trans({}, choice_translation_domain) }}">
+                {% set options = choice %}
+                {{- block('choice_widget_options') -}}
+            </optgroup>
+        {%- else -%}
+            <option value="{{ choice.value }}"{% if choice.attr %}{% with { attr: choice.attr } %}{{ block('attributes') }}{% endwith %}{% endif %}{% if choice is selectedchoice(value) %} selected="selected"{% endif %}>{{ choice_translation_domain is same as(false) ? choice.label : choice.label|trans({}, choice_translation_domain) }}</option>
+        {%- endif -%}
+    {% endfor %}
+{%- endblock choice_widget_options -%}
+
+{%- block checkbox_widget -%}
+    <input type="checkbox" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
+{%- endblock checkbox_widget -%}
+
+{%- block radio_widget -%}
+    <input type="radio" {{ block('widget_attributes') }}{% if value is defined %} value="{{ value }}"{% endif %}{% if checked %} checked="checked"{% endif %} />
+{%- endblock radio_widget -%}
+
+{%- block datetime_widget -%}
+    {% if widget == 'single_text' %}
+        {{- block('form_widget_simple') -}}
+    {%- else -%}
+        <div {{ block('widget_container_attributes') }}>
+            {{- form_errors(form.date) -}}
+            {{- form_errors(form.time) -}}
+            {{- form_widget(form.date) -}}
+            {{- form_widget(form.time) -}}
+        </div>
+    {%- endif -%}
+{%- endblock datetime_widget -%}
+
+{%- block date_widget -%}
+    {%- if widget == 'single_text' -%}
+        {{ block('form_widget_simple') }}
+    {%- else -%}
+        <div {{ block('widget_container_attributes') }}>
+            {{- date_pattern|replace({
+                '{{ year }}':  form_widget(form.year),
+                '{{ month }}': form_widget(form.month),
+                '{{ day }}':   form_widget(form.day),
+            })|raw -}}
+        </div>
+    {%- endif -%}
+{%- endblock date_widget -%}
+
+{%- block time_widget -%}
+    {%- if widget == 'single_text' -%}
+        {{ block('form_widget_simple') }}
+    {%- else -%}
+        {%- set vars = widget == 'text' ? { 'attr': { 'size': 1 }} : {} -%}
+        <div {{ block('widget_container_attributes') }}>
+            {{ form_widget(form.hour, vars) }}{% if with_minutes %}:{{ form_widget(form.minute, vars) }}{% endif %}{% if with_seconds %}:{{ form_widget(form.second, vars) }}{% endif %}
+        </div>
+    {%- endif -%}
+{%- endblock time_widget -%}
+
+{%- block dateinterval_widget -%}
+    {%- if widget == 'single_text' -%}
+        {{- block('form_widget_simple') -}}
+    {%- else -%}
+        <div {{ block('widget_container_attributes') }}>
+            {{- form_errors(form) -}}
+            <table class="{{ table_class|default('') }}" role="presentation">
+                <thead>
+                    <tr>
+                        {%- if with_years %}<th>{{ form_label(form.years) }}</th>{% endif -%}
+                        {%- if with_months %}<th>{{ form_label(form.months) }}</th>{% endif -%}
+                        {%- if with_weeks %}<th>{{ form_label(form.weeks) }}</th>{% endif -%}
+                        {%- if with_days %}<th>{{ form_label(form.days) }}</th>{% endif -%}
+                        {%- if with_hours %}<th>{{ form_label(form.hours) }}</th>{% endif -%}
+                        {%- if with_minutes %}<th>{{ form_label(form.minutes) }}</th>{% endif -%}
+                        {%- if with_seconds %}<th>{{ form_label(form.seconds) }}</th>{% endif -%}
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr>
+                        {%- if with_years %}<td>{{ form_widget(form.years) }}</td>{% endif -%}
+                        {%- if with_months %}<td>{{ form_widget(form.months) }}</td>{% endif -%}
+                        {%- if with_weeks %}<td>{{ form_widget(form.weeks) }}</td>{% endif -%}
+                        {%- if with_days %}<td>{{ form_widget(form.days) }}</td>{% endif -%}
+                        {%- if with_hours %}<td>{{ form_widget(form.hours) }}</td>{% endif -%}
+                        {%- if with_minutes %}<td>{{ form_widget(form.minutes) }}</td>{% endif -%}
+                        {%- if with_seconds %}<td>{{ form_widget(form.seconds) }}</td>{% endif -%}
+                    </tr>
+                </tbody>
+            </table>
+            {%- if with_invert %}{{ form_widget(form.invert) }}{% endif -%}
+        </div>
+    {%- endif -%}
+{%- endblock dateinterval_widget -%}
+
+{%- block number_widget -%}
+    {# type="number" doesn't work with floats #}
+    {%- set type = type|default('text') -%}
+    {{ block('form_widget_simple') }}
+{%- endblock number_widget -%}
+
+{%- block integer_widget -%}
+    {%- set type = type|default('number') -%}
+    {{ block('form_widget_simple') }}
+{%- endblock integer_widget -%}
+
+{%- block money_widget -%}
+    {{ money_pattern|replace({ '{{ widget }}': block('form_widget_simple') })|raw }}
+{%- endblock money_widget -%}
+
+{%- block url_widget -%}
+    {%- set type = type|default('url') -%}
+    {{ block('form_widget_simple') }}
+{%- endblock url_widget -%}
+
+{%- block search_widget -%}
+    {%- set type = type|default('search') -%}
+    {{ block('form_widget_simple') }}
+{%- endblock search_widget -%}
+
+{%- block percent_widget -%}
+    {%- set type = type|default('text') -%}
+    {{ block('form_widget_simple') }} %
+{%- endblock percent_widget -%}
+
+{%- block password_widget -%}
+    {%- set type = type|default('password') -%}
+    {{ block('form_widget_simple') }}
+{%- endblock password_widget -%}
+
+{%- block hidden_widget -%}
+    {%- set type = type|default('hidden') -%}
+    {{ block('form_widget_simple') }}
+{%- endblock hidden_widget -%}
+
+{%- block email_widget -%}
+    {%- set type = type|default('email') -%}
+    {{ block('form_widget_simple') }}
+{%- endblock email_widget -%}
+
+{%- block range_widget -%}
+    {% set type = type|default('range') %}
+    {{- block('form_widget_simple') -}}
+{%- endblock range_widget %}
+
+{%- block button_widget -%}
+    {%- if label is empty -%}
+        {%- if label_format is not empty -%}
+            {% set label = label_format|replace({
+                '%name%': name,
+                '%id%': id,
+            }) %}
+        {%- elseif label is same as(false) -%}
+            {% set translation_domain = false %}
+        {%- else -%}
+            {% set label = name|humanize %}
+        {%- endif -%}
+    {%- endif -%}
+    <button type="{{ type|default('button') }}" {{ block('button_attributes') }}>{{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}</button>
+{%- endblock button_widget -%}
+
+{%- block submit_widget -%}
+    {%- set type = type|default('submit') -%}
+    {{ block('button_widget') }}
+{%- endblock submit_widget -%}
+
+{%- block reset_widget -%}
+    {%- set type = type|default('reset') -%}
+    {{ block('button_widget') }}
+{%- endblock reset_widget -%}
+
+{%- block tel_widget -%}
+    {%- set type = type|default('tel') -%}
+    {{ block('form_widget_simple') }}
+{%- endblock tel_widget -%}
+
+{%- block color_widget -%}
+    {%- set type = type|default('color') -%}
+    {{ block('form_widget_simple') }}
+{%- endblock color_widget -%}
+
+{# Labels #}
+
+{%- block form_label -%}
+    {% if label is not same as(false) -%}
+        {% if not compound -%}
+            {% set label_attr = label_attr|merge({'for': id}) %}
+        {%- endif -%}
+        {% if required -%}
+            {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ ' required')|trim}) %}
+        {%- endif -%}
+        {% if label is empty -%}
+            {%- if label_format is not empty -%}
+                {% set label = label_format|replace({
+                    '%name%': name,
+                    '%id%': id,
+                }) %}
+            {%- else -%}
+                {% set label = name|humanize %}
+            {%- endif -%}
+        {%- endif -%}
+        <{{ element|default('label') }}{% if label_attr %}{% with { attr: label_attr } %}{{ block('attributes') }}{% endwith %}{% endif %}>
+            {%- if translation_domain is same as(false) -%}
+                {{- label -}}
+            {%- else -%}
+                {{- label|trans({}, translation_domain) -}}
+            {%- endif -%}
+        </{{ element|default('label') }}>
+    {%- endif -%}
+{%- endblock form_label -%}
+
+{%- block button_label -%}{%- endblock -%}
+
+{# Rows #}
+
+{%- block repeated_row -%}
+    {#
+    No need to render the errors here, as all errors are mapped
+    to the first child (see RepeatedTypeValidatorExtension).
+    #}
+    {{- block('form_rows') -}}
+{%- endblock repeated_row -%}
+
+{%- block form_row -%}
+    <div>
+        {{- form_label(form) -}}
+        <section>
+            {{- form_widget(form) -}}
+            {%- if errors|length > 0 -%}
+                <div class="error">
+                    {{- form_errors(form) -}}
+                </div>
+            {%- endif -%}
+        </section>
+    </div>
+{%- endblock form_row -%}
+
+{%- block button_row -%}
+    <div>
+        {{- form_widget(form) -}}
+    </div>
+{%- endblock button_row -%}
+
+{%- block hidden_row -%}
+    {{ form_widget(form) }}
+{%- endblock hidden_row -%}
+
+{# Misc #}
+
+{%- block form -%}
+    {{ form_start(form) }}
+        {{- form_widget(form) -}}
+    {{ form_end(form) }}
+{%- endblock form -%}
+
+{%- block form_start -%}
+    {%- do form.setMethodRendered() -%}
+    {% set method = method|upper %}
+    {%- if method in ["GET", "POST"] -%}
+        {% set form_method = method %}
+    {%- else -%}
+        {% set form_method = "POST" %}
+    {%- endif -%}
+    <form name="{{ name }}" method="{{ form_method|lower }}"{% if action != '' %} action="{{ action }}"{% endif %}{% for attrname, attrvalue in attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}{% if multipart %} enctype="multipart/form-data"{% endif %}>
+    {%- if form_method != method -%}
+        <input type="hidden" name="_method" value="{{ method }}" />
+    {%- endif -%}
+{%- endblock form_start -%}
+
+{%- block form_end -%}
+    {%- if not render_rest is defined or render_rest -%}
+        {{ form_rest(form) }}
+    {%- endif -%}
+    </form>
+{%- endblock form_end -%}
+
+{%- block form_errors -%}
+    {%- if errors|length > 0 -%}
+    <ul>
+        {%- for error in errors -%}
+            <li>{{ error.message }}</li>
+        {%- endfor -%}
+    </ul>
+    {%- endif -%}
+{%- endblock form_errors -%}
+
+{%- block form_rest -%}
+    {% for child in form -%}
+        {% if not child.rendered %}
+            {{- form_row(child) -}}
+        {% endif %}
+    {%- endfor -%}
+
+    {% if not form.methodRendered and form is rootform %}
+        {%- do form.setMethodRendered() -%}
+        {% set method = method|upper %}
+        {%- if method in ["GET", "POST"] -%}
+            {% set form_method = method %}
+        {%- else -%}
+            {% set form_method = "POST" %}
+        {%- endif -%}
+
+        {%- if form_method != method -%}
+            <input type="hidden" name="_method" value="{{ method }}" />
+        {%- endif -%}
+    {% endif -%}
+{% endblock form_rest %}
+
+{# Support #}
+
+{%- block form_rows -%}
+    {% for child in form %}
+        {{- form_row(child) -}}
+    {% endfor %}
+{%- endblock form_rows -%}
+
+{%- block widget_attributes -%}
+    id="{{ id }}" name="{{ full_name }}"
+    {%- if disabled %} disabled="disabled"{% endif -%}
+    {%- if required %} required="required"{% endif -%}
+    {{ block('attributes') }}
+{%- endblock widget_attributes -%}
+
+{%- block widget_container_attributes -%}
+    {%- if id is not empty %}id="{{ id }}"{% endif -%}
+    {{ block('attributes') }}
+{%- endblock widget_container_attributes -%}
+
+{%- block button_attributes -%}
+    id="{{ id }}" name="{{ full_name }}"{% if disabled %} disabled="disabled"{% endif -%}
+    {{ block('attributes') }}
+{%- endblock button_attributes -%}
+
+{% block attributes -%}
+    {%- for attrname, attrvalue in attr -%}
+        {{- " " -}}
+        {%- if attrname in ['placeholder', 'title'] -%}
+            {{- attrname }}="{{ translation_domain is same as(false) ? attrvalue : attrvalue|trans({}, translation_domain) }}"
+        {%- elseif attrvalue is same as(true) -%}
+            {{- attrname }}="{{ attrname }}"
+        {%- elseif attrvalue is not same as(false) -%}
+            {{- attrname }}="{{ attrvalue }}"
+        {%- endif -%}
+    {%- endfor -%}
+{%- endblock attributes -%}
diff --git a/Resources/views/form/login.html.twig b/Resources/views/form/login.html.twig
new file mode 100644 (file)
index 0000000..d680b2d
--- /dev/null
@@ -0,0 +1,32 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+       <section id="form">
+               <h2><a href="{{ path('rapsys_user_login') }}">{% trans %}Login{% endtrans %}</a></h2>
+               <div>
+                       {{ form_start(form) }}
+
+                               <header>{{ form_errors(form) }}</header>
+
+                               <section>
+                                       {{ form_row(form.mail) }}
+
+                                       {{ form_row(form.password) }}
+
+                                       {% if error %}
+                                               <div class="error">
+                                                       <ul><li><a href="{{ path('rapsys_user_recover') }}">{% trans %}To recover your account click here{% endtrans %}</a></li></ul>
+                                               </div>
+                                       {% endif %}
+                               </section>
+
+                               {{ form_row(form.submit) }}
+
+                               {# Render CSRF token etc .#}
+                               <footer style="display:none">
+                                       {{ form_rest(form) }}
+                               </footer>
+
+                       {{ form_end(form) }}
+               </div>
+       </section>
+{% endblock %}
diff --git a/Resources/views/form/recover.html.twig b/Resources/views/form/recover.html.twig
new file mode 100644 (file)
index 0000000..63f948d
--- /dev/null
@@ -0,0 +1,28 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+       <section id="form">
+               <h2><a href="{{ path('rapsys_user_recover') }}">{% trans %}Recover{% endtrans %}</a></h2>
+               {% if sent %}
+                       <p>{% trans %}Your recover account message has been sent{% endtrans %}</p>
+               {% else %}
+                       <div>
+                               {{ form_start(form) }}
+
+                                       <header>{{ form_errors(form) }}</header>
+
+                                       <section>
+                                               {{ form_row(form.mail) }}
+                                       </section>
+
+                                       {{ form_row(form.submit) }}
+
+                                       {# Render CSRF token etc .#}
+                                       <footer style="display:none">
+                                               {{ form_rest(form) }}
+                                       </footer>
+
+                               {{ form_end(form) }}
+                       </div>
+               {% endif %}
+       </section>
+{% endblock %}
diff --git a/Resources/views/form/recover_mail.html.twig b/Resources/views/form/recover_mail.html.twig
new file mode 100644 (file)
index 0000000..74b2dd7
--- /dev/null
@@ -0,0 +1,30 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+       <section id="form">
+               <h2><a href="{{ path('rapsys_user_recover') }}">{% trans %}Recover{% endtrans %}</a></h2>
+               {% if sent %}
+                       <p>{% trans %}Your password is updated and an account recover message has been sent{% endtrans %}</p>
+               {% elseif notfound %}
+                       <p>{% trans %}Unable to find account{% endtrans %}</p>
+               {% else %}
+                       <div>
+                               {{ form_start(form) }}
+
+                                       <header>{{ form_errors(form) }}</header>
+
+                                       <section>
+                                               {{ form_row(form.password) }}
+                                       </section>
+
+                                       {{ form_row(form.submit) }}
+
+                                       {# Render CSRF token etc .#}
+                                       <footer style="display:none">
+                                               {{ form_rest(form) }}
+                                       </footer>
+
+                               {{ form_end(form) }}
+                       </div>
+               {% endif %}
+       </section>
+{% endblock %}
diff --git a/Resources/views/form/register.html.twig b/Resources/views/form/register.html.twig
new file mode 100644 (file)
index 0000000..ca69b5e
--- /dev/null
@@ -0,0 +1,42 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+       <section id="form">
+               <h2>{{ title|trans }}</h2>
+               {% if sent %}
+                       <p>{% trans %}Your account has been created{% endtrans %}</p>
+               {% else %}
+                       <div>
+                               {{ form_start(form) }}
+
+                                       <header>{{ form_errors(form) }}</header>
+
+                                       <section>
+                                               {{ form_row(form.mail) }}
+
+                                               {{ form_row(form.password.first) }}
+
+                                               {{ form_row(form.password.second) }}
+                                       </section>
+
+                                       <section>
+                                               {{ form_row(form.title) }}
+
+                                               {{ form_row(form.forename) }}
+
+                                               {{ form_row(form.surname) }}
+
+                                               {{ form_row(form.pseudonym) }}
+                                       </section>
+
+                                       {{ form_row(form.submit) }}
+
+                                       {# Render CSRF token etc .#}
+                                       <footer style="display:none">
+                                               {{ form_rest(form) }}
+                                       </footer>
+
+                               {{ form_end(form) }}
+                       </div>
+               {% endif %}
+       </section>
+{% endblock %}
diff --git a/Resources/views/mail/contact.html.twig b/Resources/views/mail/contact.html.twig
new file mode 100644 (file)
index 0000000..7fe13ff
--- /dev/null
@@ -0,0 +1,43 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+       <meta name="viewport" content="width=device-width" />
+       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+       <title>{{ subject }}</title>
+       <style>
+               body {background:white;color:#066}
+               a {text-decoration:none;color:#066}
+               a:hover {text-decoration:underline}
+               table {width: 100%;border-radius: .5rem}
+               table.head {border:.1rem solid #00c3f9;text-align:center}
+               table.body {background:white;border:.1rem solid #00c3f9;margin:.5rem 0}
+               table.footer {border:.1rem solid #00c3f9;text-align:center;font-weight:bold}
+               table.footer summary:after {display:none}
+               table.footer summary::-webkit-details-marker {display:none}
+       </style>
+</head>
+<body>
+       <table class="head">
+               <tr>
+                       <td><a href="{{ url }}"><img src="data:image/png;base64, {{ logo|file_get_contents|base64_encode }}" alt="{{ title|trans }}" /></a></td>
+                       <td><h1><a href="{{ url }}">{{ title|trans }}</a></h1></td>
+               </tr>
+       </table>
+       <table class="body">
+               <tr>
+                       <td>&nbsp;</td>
+                       <td>
+                               <h2>{% trans %}Hi,{% endtrans %} {{ contact_name }}</h2>
+                               <h3>{% trans %}Subject:{% endtrans %} {{ subject }}</h3>
+                               <p>{{ message|nl2br }}</p>
+                       </td>
+                       <td>&nbsp;</td>
+               </tr>
+       </table>
+       <table class="footer">
+               <tr>
+                       <td>{% trans %}Raphaël Gertz all rights reserved{% endtrans %} - {% trans %}Copyright 2018{% endtrans %}.</td>
+               </tr>
+       </table>
+</body>
+</html>
diff --git a/Resources/views/mail/generic.html.twig b/Resources/views/mail/generic.html.twig
new file mode 100644 (file)
index 0000000..5e6da39
--- /dev/null
@@ -0,0 +1,49 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+       <meta name="viewport" content="width=device-width" />
+       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+       <title>{{ title }}</title>
+       <style>
+               body {background:white;color:#066}
+               a {text-decoration:none;color:#066}
+               a:hover {text-decoration:underline}
+               table {width: 100%;border-radius: .5rem}
+               table.head {border:.1rem solid #00c3f9;text-align:center}
+               table.body {background:white;border:.1rem solid #00c3f9;margin:.5rem 0}
+               table.footer {border:.1rem solid #00c3f9;text-align:center;font-weight:bold}
+               table.footer summary:after {display:none}
+               table.footer summary::-webkit-details-marker {display:none}
+       </style>
+</head>
+<body>
+       <table class="head">
+               <tr>
+                       <td><a href="{{ home }}"><img src="data:image/png;base64, {{ logo|file_get_contents|base64_encode }}" alt="{{ title }}" /></a></td>
+                       <td><h1><a href="{{ home }}">{{ title }}</a></h1></td>
+               </tr>
+       </table>
+       <table class="body">
+               <tr>
+                       <td>&nbsp;</td>
+                       <td>
+                               <h2>{{ subtitle }}</h2>
+                               {% if subject %}
+                                       <h3>{% trans %}Subject:{% endtrans %} {{ subject }}</h3>
+                               {% endif %}
+                               {% if raw is defined %}
+                                       <p>{{ raw|raw }}</p>
+                               {% else %}
+                                       <p>{{ message|nl2br }}</p>
+                               {% endif %}
+                       </td>
+                       <td>&nbsp;</td>
+               </tr>
+       </table>
+       <table class="footer">
+               <tr>
+                       <td>{% trans %}Raphaël Gertz all rights reserved{% endtrans %} - {% trans %}Copyright 2018{% endtrans %}.</td>
+               </tr>
+       </table>
+</body>
+</html>
diff --git a/Resources/views/mail/register.html.twig b/Resources/views/mail/register.html.twig
new file mode 100644 (file)
index 0000000..8a6c0d3
--- /dev/null
@@ -0,0 +1,43 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+       <meta name="viewport" content="width=device-width" />
+       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+       <title>{{ 'Welcome to %title%'|trans({'%title%': title}) }}</title>
+       <style>
+               body {background:white;color:#066}
+               a {text-decoration:none;color:#066}
+               a:hover {text-decoration:underline}
+               table {width: 100%;border-radius: .5rem}
+               table.head {border:.1rem solid #00c3f9;text-align:center}
+               table.body {background:white;border:.1rem solid #00c3f9;margin:.5rem 0}
+               table.footer {border:.1rem solid #00c3f9;text-align:center;font-weight:bold}
+               table.footer summary:after {display:none}
+               table.footer summary::-webkit-details-marker {display:none}
+       </style>
+</head>
+<body>
+       <table class="head">
+               <tr>
+                       <td><a href="{{ url }}"><img src="data:image/png;base64, {{ logo|file_get_contents|base64_encode }}" alt="{{ title|trans }}" /></a></td>
+                       <td><h1><a href="{{ url }}">{{ title|trans }}</a></h1></td>
+               </tr>
+       </table>
+       <table class="body">
+               <tr>
+                       <td>&nbsp;</td>
+                       <td>
+                               <h2>{% trans %}Hi,{% endtrans %} {{ contact_name }}</h2>
+                               <h3>{% trans %}Subject:{% endtrans %} {{ 'Welcome to %title%'|trans({'%title%': title|trans}) }}</h3>
+                               <p>{{ 'Thanks so much for joining us, from now you can reserver your %title% spaces.'|trans({'%title%': title|trans}) }}</p>
+                       </td>
+                       <td>&nbsp;</td>
+               </tr>
+       </table>
+       <table class="footer">
+               <tr>
+                       <td>{% trans %}Raphaël Gertz all rights reserved{% endtrans %} - {% trans %}Copyright 2018{% endtrans %}.</td>
+               </tr>
+       </table>
+</body>
+</html>
diff --git a/Resources/views/page/index.html.twig b/Resources/views/page/index.html.twig
new file mode 100644 (file)
index 0000000..f0997a7
--- /dev/null
@@ -0,0 +1,7 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+<section id="content">
+       <h2><a href="{{ path('rapsys_air_homepage') }}">{{ section }}</a></h2>
+       <p>{% trans %}Outdoor space reservation system{% endtrans %}</p>
+</section>
+{% endblock %}
diff --git a/Tests/Controller/DefaultControllerTest.php b/Tests/Controller/DefaultControllerTest.php
new file mode 100644 (file)
index 0000000..fed9e42
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+
+namespace Rapsys\AirBundle\Tests\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+
+class DefaultControllerTest extends WebTestCase
+{
+    public function testIndex()
+    {
+        $client = static::createClient();
+
+        $crawler = $client->request('GET', '/');
+
+        $this->assertContains('Hello World', $client->getResponse()->getContent());
+    }
+}
diff --git a/Twig/Base64Extension.php b/Twig/Base64Extension.php
new file mode 100644 (file)
index 0000000..8272989
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+// src/Rapsys/AirBundle/Twig/Base64Extension.php
+namespace Rapsys\AirBundle\Twig;
+
+class Base64Extension extends \Twig_Extension {
+       public function getFilters() {
+               return array(
+                       new \Twig_SimpleFilter('base64_encode', 'base64_encode'),
+                       new \Twig_SimpleFilter('base64_decode', 'base64_decode')
+               );
+       }
+}
diff --git a/Twig/Bb2htmlExtension.php b/Twig/Bb2htmlExtension.php
new file mode 100644 (file)
index 0000000..a9b3c8e
--- /dev/null
@@ -0,0 +1,64 @@
+<?php
+// src/Rapsys/AirBundle/Twig/Bb2htmlExtension.php
+namespace Rapsys\AirBundle\Twig;
+
+class Bb2htmlExtension extends \Twig_Extension {
+       public function getFilters() {
+               return array(
+                       new \Twig\TwigFilter(
+                               'bb2html',
+                               function($text) {
+                                       $ctx = bbcode_create(
+                                               array(
+                                                       '' => array('type' => BBCODE_TYPE_ROOT),
+                                                       'code' => array(
+                                                               'type' => BBCODE_TYPE_OPTARG,
+                                                               'open_tag' => '<pre class="{PARAM}">',
+                                                               'close_tag' => '</pre>',
+                                                               'default_arg' => '{CONTENT}'
+                                                       ),
+                                                       'ul' => array(
+                                                               'type' => BBCODE_TYPE_NOARG,
+                                                               'open_tag' => '<ul>',
+                                                               'close_tag' => '</ul>',
+                                                               'childs' => 'li'
+                                                       ),
+                                                       'li' => array(
+                                                               'type' => BBCODE_TYPE_NOARG,
+                                                               'open_tag' => '<li>',
+                                                               'close_tag' => '</li>',
+                                                               'parent' => 'ul',
+                                                               'childs' => 'url'
+                                                       ),
+                                                       'url' => array(
+                                                               'type' => BBCODE_TYPE_OPTARG,
+                                                               'open_tag' => '<a href="{PARAM}">',
+                                                               'close_tag' => '</a>',
+                                                               'default_arg' => '{CONTENT}',
+                                                               'parent' => 'p,li'
+                                                       )
+                                               )
+                                       );
+                                       $text = nl2br(bbcode_parse($ctx, htmlspecialchars($text)));
+                                       if (preg_match_all('#\<pre[^>]*\>(.*?)\</pre\>#s', $text, $matches) && !empty($matches[1])) {
+                                               foreach($matches[1] as $string) {
+                                                       $text = str_replace($string, str_replace('<br />', '', $string), $text);
+                                               }
+                                       }
+                                       if (preg_match_all('#\<ul[^>]*\>(.*?)\</ul\>#s', $text, $matches) && !empty($matches[1])) {
+                                               foreach($matches[1] as $string) {
+                                                       $text = str_replace($string, str_replace('<br />', '', $string), $text);
+                                               }
+                                       }
+                                       $text = preg_replace(
+                                               array('#(<br />(\r?\n?))*<pre#s', '#</pre>(<br />(\r?\n?))*#', '#(<br />(\r?\n?))*<ul#s', '#</ul>(<br />(\r?\n?))*#', '#(<br />(\r?\n?)){2,}#'),
+                                               array('</p>\2<pre', '</pre>\2<p>', '</p>\2<ul', '</ul>\2<p>', '</p>\2<p>'),
+                                               $text
+                                       );
+                                       return $text;
+                               },
+                               array('is_safe' => array('html'))
+                       )
+               );
+       }
+}
diff --git a/Twig/FileGetContentsExtension.php b/Twig/FileGetContentsExtension.php
new file mode 100644 (file)
index 0000000..ac4ab2a
--- /dev/null
@@ -0,0 +1,11 @@
+<?php
+// src/Rapsys/AirBundle/Twig/FileGetContentsExtension.php
+namespace Rapsys\AirBundle\Twig;
+
+class FileGetContentsExtension extends \Twig_Extension {
+       public function getFilters() {
+               return array(
+                       new \Twig_SimpleFilter('file_get_contents', 'file_get_contents', array(false, null))
+               );
+       }
+}