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/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 %}
+
+ {{ day.title }}
+ {% if day.sessions is not empty %}
+
+ {% endif %}
+ |
+ {% if loop.index % 7 == 0 and not loop.last %}
+
+
+ {% 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) }}
+
+
+ {# dump(calendar) #}
+
+{% endblock %}
diff --git a/Resources/views/admin/index.html.twig.toto b/Resources/views/admin/index.html.twig.toto
new file mode 100644
index 0000000..d529910
--- /dev/null
+++ b/Resources/views/admin/index.html.twig.toto
@@ -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
index 0000000..ef61643
--- /dev/null
+++ b/Resources/views/admin/session.html.twig
@@ -0,0 +1,31 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+
+ {{ 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 %}
+
+
+
+ {{ dump(session) }}
+
+{% endblock %}
diff --git a/Resources/views/base.html.twig b/Resources/views/base.html.twig
new file mode 100644
index 0000000..a95e21a
--- /dev/null
+++ b/Resources/views/base.html.twig
@@ -0,0 +1,78 @@
+
+
+
+
+ {% block title %}{{ title }}{% endblock %}
+
+ {% 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' %}
+
+ {% endstylesheet %}
+ {% endblock %}
+
+
+
+
+ {% block body %}
+
+ {% block header %}
+
+ {% endblock %}
+
+ {# pass an array argument to get the messages of those types (['warning', 'error']) #}
+ {% for label, messages in app.flashes %}
+ {% if messages %}
+
+
+ {% for message in messages %}
+ - {{ message }}
+ {% endfor %}
+
+
+ {% endif %}
+ {% endfor %}
+
+ {% block sidebar %}{% endblock %}
+
+ {% block content %}{% endblock %}
+
+ {% block footer %}
+
+ {% endblock %}
+
+ {% endblock %}
+
diff --git a/Resources/views/form/contact.html.twig b/Resources/views/form/contact.html.twig
new file mode 100644
index 0000000..b75b91b
--- /dev/null
+++ b/Resources/views/form/contact.html.twig
@@ -0,0 +1,34 @@
+{% extends '@RapsysAir/base.html.twig' %}
+{% block content %}
+
+{% 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
index 0000000..7f73c51
--- /dev/null
+++ b/Resources/views/form/form_div_layout.html.twig
@@ -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') -%}
+
+{%- endblock form_widget_simple -%}
+
+{%- block form_widget_compound -%}
+
+ {%- 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 -%}
+
+ {%- 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 -%}
+
+{%- 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 -%}
+
+ {{- form_label(form) -}}
+
+ {{- form_widget(form) -}}
+ {%- if errors|length > 0 -%}
+
+ {{- form_errors(form) -}}
+
+ {%- endif -%}
+
+
+{%- endblock form_row -%}
+
+{%- block button_row -%}
+
+ {{- 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 @@
+