From: Raphaël Gertz 
Date: Fri, 25 Oct 2019 14:33:24 +0000 (+0200)
Subject: Initial import
X-Git-Tag: 0.0.1~2
X-Git-Url: https://git.rapsys.eu/.gitweb.cgi/airbundle/commitdiff_plain/f110b8ba20232e0ceeb67390f8e672431868d32d?ds=inline
Initial import
---
f110b8ba20232e0ceeb67390f8e672431868d32d
diff --git a/Controller/DefaultController.php b/Controller/DefaultController.php
new file mode 100644
index 0000000..31d4b89
--- /dev/null
+++ b/Controller/DefaultController.php
@@ -0,0 +1,305 @@
+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
index 0000000..cfaaa62
--- /dev/null
+++ b/DataFixtures/AirFixtures.php
@@ -0,0 +1,246 @@
+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/RapsysAirExtension.php b/DependencyInjection/RapsysAirExtension.php
new file mode 100644
index 0000000..5a2bdff
--- /dev/null
+++ b/DependencyInjection/RapsysAirExtension.php
@@ -0,0 +1,46 @@
+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
index 0000000..c157132
--- /dev/null
+++ b/Entity/Application.php
@@ -0,0 +1,187 @@
+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
index 0000000..3ccf6ee
--- /dev/null
+++ b/Entity/Group.php
@@ -0,0 +1,6 @@
+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
index 0000000..72d1f33
--- /dev/null
+++ b/Entity/Session.php
@@ -0,0 +1,281 @@
+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
index 0000000..03bfd3f
--- /dev/null
+++ b/Entity/Slot.php
@@ -0,0 +1,191 @@
+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
index 0000000..54a6a60
--- /dev/null
+++ b/Entity/Title.php
@@ -0,0 +1,7 @@
+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
index 0000000..7b2fb53
--- /dev/null
+++ b/Entity/Vote.php
@@ -0,0 +1,141 @@
+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
index 0000000..8439d29
--- /dev/null
+++ b/Form/ApplicationType.php
@@ -0,0 +1,39 @@
+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
index 0000000..e9a5ba1
--- /dev/null
+++ b/Form/ContactType.php
@@ -0,0 +1,40 @@
+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
index 0000000..06f1b37
--- /dev/null
+++ b/RapsysAirBundle.php
@@ -0,0 +1,9 @@
+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
index 0000000..e295bfd
--- /dev/null
+++ b/Repository/SessionRepository.php
@@ -0,0 +1,45 @@
+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
index 0000000..aaf461f
--- /dev/null
+++ b/Resources/config/doctrine/Application.orm.yml
@@ -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
index 0000000..57f70a9
--- /dev/null
+++ b/Resources/config/doctrine/Group.orm.yml
@@ -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
index 0000000..86d86db
--- /dev/null
+++ b/Resources/config/doctrine/Location.orm.yml
@@ -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
index 0000000..9c89adb
--- /dev/null
+++ b/Resources/config/doctrine/Session.orm.yml
@@ -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
index 0000000..c4ff511
--- /dev/null
+++ b/Resources/config/doctrine/Slot.orm.yml
@@ -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
index 0000000..d68aa60
--- /dev/null
+++ b/Resources/config/doctrine/Title.orm.yml
@@ -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
index 0000000..4df938e
--- /dev/null
+++ b/Resources/config/doctrine/User.orm.yml
@@ -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
index 0000000..ee8e2ea
--- /dev/null
+++ b/Resources/config/doctrine/Vote.orm.yml
@@ -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
index 0000000..dd63f57
--- /dev/null
+++ b/Resources/config/routing.yml
@@ -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
index 0000000..9f2f696
--- /dev/null
+++ b/Resources/config/services.yml
@@ -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
index 0000000..c59ac50
--- /dev/null
+++ b/Resources/public/css/reset.css
@@ -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
index 0000000..5477726
--- /dev/null
+++ b/Resources/public/css/screen.css
@@ -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
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
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
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
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
index 0000000..1e196c6
--- /dev/null
+++ b/Resources/public/svg/favicon.svg
@@ -0,0 +1,95 @@
+
+
+
+
diff --git a/Resources/public/svg/logo.svg b/Resources/public/svg/logo.svg
new file mode 100644
index 0000000..159107f
--- /dev/null
+++ b/Resources/public/svg/logo.svg
@@ -0,0 +1,228 @@
+
+
+
+
diff --git a/Resources/public/xcf/openair.xcf b/Resources/public/xcf/openair.xcf
new file mode 100644
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
index 0000000..40c37fe
--- /dev/null
+++ b/Resources/translations/messages.fr.yml
@@ -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: %url%': 'Merci de nous avoir rejoint, pour récupérer votre compte vous pouvez suivre ce lien : %url%'
+'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: %url%': 'Votre mot de passe a été changé, pour récupérer votre compte vous pouvez suivre ce lien : %url%'
+'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
index 0000000..e945217
--- /dev/null
+++ b/Resources/translations/validators.fr.yml
@@ -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
index 0000000..687e217
--- /dev/null
+++ b/Resources/views/admin/index.html.twig
@@ -0,0 +1,57 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+
+	{% trans %}Dashboard{% endtrans %}
+	
+		{% if calendar is defined and calendar %}
+			
+				
+					
+						{% for date, day in calendar %}
+							| + +							{% if loop.index % 7 == 0 and not loop.last %}
+{{ day.title }}+								{% if day.sessions is not empty %}
+									
+								{% endif %}
+ | 
+								
+							{% endif %}
+						{% endfor %}
+					
+				
+			
+		{% endif %}
+		
+		{{ form_start(form) }}
+
+			
+
+			
+				{{ form_row(form.location) }}
+
+				{{ form_row(form.date) }}
+
+				{{ form_row(form.slot) }}
+			
+
+			{{ form_row(form.submit) }}
+
+			{# Render CSRF token etc .#}
+			
+
+		{{ form_end(form) }}
+		
+	
{{ section }}
+	
+		
+			
+				- {% trans %}Location{% endtrans %}
+- {{ session.location.getTitle() }}+
+			
+				- {% trans %}Date{% endtrans %}
+- {{ session.date.format('Y-m-d') }}+
+			
+				- {% trans %}Slot{% endtrans %}
+- {{ session.slot.getTitle() }}+
+			
+				- {% trans %}Application{% endtrans %}
+				{% if session.application is null %}
+- {% trans %}None{% endtrans %}+				{% else %}
+
- {{ session.application.getTitle() }}+				{% endif %}
+
+		
+	
+						
+							{% for message in messages %}
+								- {{ message }}+							{% endfor %}
+
+					
+        {%- if form is rootform -%}
+            {{ form_errors(form) }}
+        {%- endif -%}
+        {{- block('form_rows') -}}
+        {{- form_rest(form) -}}
+    
+{%- 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 -%}
+    
+{%- 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 -%}
+    
+    {%- for child in form %}
+        {{- form_widget(child) -}}
+        {{- form_label(child, null, {translation_domain: choice_translation_domain}) -}}
+    {% endfor -%}
+    
+{%- 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 -%}
+    
+{%- endblock choice_widget_collapsed -%}
+
+{%- block choice_widget_options -%}
+    {% for group_label, choice in options %}
+        {%- if choice is iterable -%}
+            
+        {%- else -%}
+            
+        {%- endif -%}
+    {% endfor %}
+{%- endblock choice_widget_options -%}
+
+{%- block checkbox_widget -%}
+    
+{%- endblock checkbox_widget -%}
+
+{%- block radio_widget -%}
+    
+{%- endblock radio_widget -%}
+
+{%- block datetime_widget -%}
+    {% if widget == 'single_text' %}
+        {{- block('form_widget_simple') -}}
+    {%- else -%}
+        
+            {{- form_errors(form.date) -}}
+            {{- form_errors(form.time) -}}
+            {{- form_widget(form.date) -}}
+            {{- form_widget(form.time) -}}
+        
+    {%- endif -%}
+{%- endblock datetime_widget -%}
+
+{%- block date_widget -%}
+    {%- if widget == 'single_text' -%}
+        {{ block('form_widget_simple') }}
+    {%- else -%}
+        
+            {{- date_pattern|replace({
+                '{{ year }}':  form_widget(form.year),
+                '{{ month }}': form_widget(form.month),
+                '{{ day }}':   form_widget(form.day),
+            })|raw -}}
+        
+    {%- endif -%}
+{%- endblock date_widget -%}
+
+{%- block time_widget -%}
+    {%- if widget == 'single_text' -%}
+        {{ block('form_widget_simple') }}
+    {%- else -%}
+        {%- set vars = widget == 'text' ? { 'attr': { 'size': 1 }} : {} -%}
+        
+            {{ form_widget(form.hour, vars) }}{% if with_minutes %}:{{ form_widget(form.minute, vars) }}{% endif %}{% if with_seconds %}:{{ form_widget(form.second, vars) }}{% endif %}
+        
+    {%- endif -%}
+{%- endblock time_widget -%}
+
+{%- block dateinterval_widget -%}
+    {%- if widget == 'single_text' -%}
+        {{- block('form_widget_simple') -}}
+    {%- else -%}
+        
+            {{- form_errors(form) -}}
+            
+                
+                    
+                        {%- if with_years %}| {{ form_label(form.years) }}{% endif -%}
+                        {%- if with_months %} | {{ form_label(form.months) }}{% endif -%}
+                        {%- if with_weeks %} | {{ form_label(form.weeks) }}{% endif -%}
+                        {%- if with_days %} | {{ form_label(form.days) }}{% endif -%}
+                        {%- if with_hours %} | {{ form_label(form.hours) }}{% endif -%}
+                        {%- if with_minutes %} | {{ form_label(form.minutes) }}{% endif -%}
+                        {%- if with_seconds %} | {{ form_label(form.seconds) }}{% endif -%}
+ | 
+                
+                
+                    
+                        {%- if with_years %}| {{ form_widget(form.years) }}{% endif -%}
+                        {%- if with_months %} | {{ form_widget(form.months) }}{% endif -%}
+                        {%- if with_weeks %} | {{ form_widget(form.weeks) }}{% endif -%}
+                        {%- if with_days %} | {{ form_widget(form.days) }}{% endif -%}
+                        {%- if with_hours %} | {{ form_widget(form.hours) }}{% endif -%}
+                        {%- if with_minutes %} | {{ form_widget(form.minutes) }}{% endif -%}
+                        {%- if with_seconds %} | {{ form_widget(form.seconds) }}{% endif -%}
+ | 
+                
+            
+            {%- if with_invert %}{{ form_widget(form.invert) }}{% endif -%}
+        
+        {{- form_label(form) -}}
+        
+            {{- form_widget(form) -}}
+            {%- if errors|length > 0 -%}
+                
+                    {{- form_errors(form) -}}
+                
+            {%- endif -%}
+        
+    
+        {{- form_widget(form) -}}
+    
+{%- 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 -%}
+    
+{%- endblock form_end -%}
+
+{%- block form_errors -%}
+    {%- if errors|length > 0 -%}
+    
+        {%- for error in errors -%}
+            - {{ error.message }}+        {%- endfor -%}
+
+    {%- 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 -%}
+            
+        {%- 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
index 0000000..d680b2d
--- /dev/null
+++ b/Resources/views/form/login.html.twig
@@ -0,0 +1,32 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+	
+{% endblock %}
diff --git a/Resources/views/form/recover.html.twig b/Resources/views/form/recover.html.twig
new file mode 100644
index 0000000..63f948d
--- /dev/null
+++ b/Resources/views/form/recover.html.twig
@@ -0,0 +1,28 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+	
+{% endblock %}
diff --git a/Resources/views/form/recover_mail.html.twig b/Resources/views/form/recover_mail.html.twig
new file mode 100644
index 0000000..74b2dd7
--- /dev/null
+++ b/Resources/views/form/recover_mail.html.twig
@@ -0,0 +1,30 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+	
+{% endblock %}
diff --git a/Resources/views/form/register.html.twig b/Resources/views/form/register.html.twig
new file mode 100644
index 0000000..ca69b5e
--- /dev/null
+++ b/Resources/views/form/register.html.twig
@@ -0,0 +1,42 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+	
+{% endblock %}
diff --git a/Resources/views/mail/contact.html.twig b/Resources/views/mail/contact.html.twig
new file mode 100644
index 0000000..7fe13ff
--- /dev/null
+++ b/Resources/views/mail/contact.html.twig
@@ -0,0 +1,43 @@
+
+
+
+	
+	
+	{{ subject }}
+	
+
+
+
+		
+			| + | + | 
+	
+	
+		
+			| + | + +{% trans %}Hi,{% endtrans %} {{ contact_name }}+{% trans %}Subject:{% endtrans %} {{ subject }}+{{ message|nl2br }}+ | + | 
+	
+	
+
+
diff --git a/Resources/views/mail/generic.html.twig b/Resources/views/mail/generic.html.twig
new file mode 100644
index 0000000..5e6da39
--- /dev/null
+++ b/Resources/views/mail/generic.html.twig
@@ -0,0 +1,49 @@
+
+
+
+	
+	
+	{{ title }}
+	
+
+
+	
+		
+			| + | + | 
+	
+	
+		
+			| + | + +{{ subtitle }}+				{% if subject %}
+{% trans %}Subject:{% endtrans %} {{ subject }}+				{% endif %}
+				{% if raw is defined %}
+{{ raw|raw }}+				{% else %}
+ {{ message|nl2br }}+				{% endif %}
+ | + | 
+	
+	
+
+
diff --git a/Resources/views/mail/register.html.twig b/Resources/views/mail/register.html.twig
new file mode 100644
index 0000000..8a6c0d3
--- /dev/null
+++ b/Resources/views/mail/register.html.twig
@@ -0,0 +1,43 @@
+
+
+
+	
+	
+	{{ 'Welcome to %title%'|trans({'%title%': title}) }}
+	
+
+
+	
+		
+			| + | + | 
+	
+	
+		
+			| + | + +{% trans %}Hi,{% endtrans %} {{ contact_name }}+{% trans %}Subject:{% endtrans %} {{ 'Welcome to %title%'|trans({'%title%': title|trans}) }}+{{ 'Thanks so much for joining us, from now you can reserver your %title% spaces.'|trans({'%title%': title|trans}) }}+ | + | 
+	
+	
+
+
diff --git a/Resources/views/page/index.html.twig b/Resources/views/page/index.html.twig
new file mode 100644
index 0000000..f0997a7
--- /dev/null
+++ b/Resources/views/page/index.html.twig
@@ -0,0 +1,7 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+
+	
+	{% trans %}Outdoor space reservation system{% endtrans %}
+
+{% endblock %}
diff --git a/Tests/Controller/DefaultControllerTest.php b/Tests/Controller/DefaultControllerTest.php
new file mode 100644
index 0000000..fed9e42
--- /dev/null
+++ b/Tests/Controller/DefaultControllerTest.php
@@ -0,0 +1,17 @@
+request('GET', '/');
+
+        $this->assertContains('Hello World', $client->getResponse()->getContent());
+    }
+}
diff --git a/Twig/Base64Extension.php b/Twig/Base64Extension.php
new file mode 100644
index 0000000..8272989
--- /dev/null
+++ b/Twig/Base64Extension.php
@@ -0,0 +1,12 @@
+ array('type' => BBCODE_TYPE_ROOT),
+							'code' => array(
+								'type' => BBCODE_TYPE_OPTARG,
+								'open_tag' => '',
+								'close_tag' => '
',
+								'default_arg' => '{CONTENT}'
+							),
+							'ul' => array(
+								'type' => BBCODE_TYPE_NOARG,
+								'open_tag' => '',
+								'childs' => 'li'
+							),
+							'li' => array(
+								'type' => BBCODE_TYPE_NOARG,
+								'open_tag' => '',
+								'close_tag' => '',
+								'parent' => 'ul',
+								'childs' => 'url'
+							),
+							'url' => array(
+								'type' => BBCODE_TYPE_OPTARG,
+								'open_tag' => '',
+								'close_tag' => '',
+								'default_arg' => '{CONTENT}',
+								'parent' => 'p,li'
+							)
+						)
+					);
+					$text = nl2br(bbcode_parse($ctx, htmlspecialchars($text)));
+					if (preg_match_all('#\]*\>(.*?)\
#s', $text, $matches) && !empty($matches[1])) {
+						foreach($matches[1] as $string) {
+							$text = str_replace($string, str_replace('
', '', $string), $text);
+						}
+					}
+					if (preg_match_all('#\#s', $text, $matches) && !empty($matches[1])) {
+						foreach($matches[1] as $string) {
+							$text = str_replace($string, str_replace('
', '', $string), $text);
+						}
+					}
+					$text = preg_replace(
+						array('#(
(\r?\n?))*(
(\r?\n?))*#', '#(
(\r?\n?))*
(
(\r?\n?))*#', '#(
(\r?\n?)){2,}#'),
+						array('
\2\2', '
\2
\2', '
\2'),
+						$text
+					);
+					return $text;
+				},
+				array('is_safe' => array('html'))
+			)
+		);
+	}
+}
diff --git a/Twig/FileGetContentsExtension.php b/Twig/FileGetContentsExtension.php
new file mode 100644
index 0000000..ac4ab2a
--- /dev/null
+++ b/Twig/FileGetContentsExtension.php
@@ -0,0 +1,11 @@
+