]> Raphaël G. Git Repositories - airbundle/commitdiff
Add snippet feature
authorRaphaël Gertz <git@rapsys.eu>
Mon, 28 Dec 2020 07:15:29 +0000 (08:15 +0100)
committerRaphaël Gertz <git@rapsys.eu>
Mon, 28 Dec 2020 07:15:29 +0000 (08:15 +0100)
Controller/SnippetController.php [new file with mode: 0644]
Entity/Snippet.php [new file with mode: 0644]
Form/SnippetType.php [new file with mode: 0644]
Repository/SnippetRepository.php [new file with mode: 0644]
Resources/config/doctrine/Snippet.orm.yml [new file with mode: 0644]
Resources/views/snippet/add.html.twig [new file with mode: 0644]
Resources/views/snippet/edit.html.twig [new file with mode: 0644]

diff --git a/Controller/SnippetController.php b/Controller/SnippetController.php
new file mode 100644 (file)
index 0000000..6335b1f
--- /dev/null
@@ -0,0 +1,270 @@
+<?php
+
+namespace Rapsys\AirBundle\Controller;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Rapsys\AirBundle\Entity\Location;
+use Rapsys\AirBundle\Entity\Snippet;
+use Rapsys\AirBundle\Entity\User;
+
+class SnippetController extends DefaultController {
+       /**
+        * Add snippet
+        *
+        * @desc Persist snippet in database
+        *
+        * @param Request $request The request instance
+        *
+        * @return Response The rendered view or redirection
+        *
+        * @throws \RuntimeException When user has not at least guest role
+        */
+       public function add(Request $request) {
+               //Prevent non-guest to access here
+               $this->denyAccessUnlessGranted('ROLE_GUEST', null, $this->translator->trans('Unable to access this page without role %role%!', ['%role%' => $this->translator->trans('Guest')]));
+
+               //Create ApplicationType form
+               $form = $this->createForm('Rapsys\AirBundle\Form\SnippetType', null, [
+                       //Set the action
+                       'action' => $this->generateUrl('rapsys_air_snippet_add'),
+                       //Set the form attribute
+                       'attr' => []
+               ]);
+
+               //Refill the fields in case of invalid form
+               $form->handleRequest($request);
+
+               //Prevent creating snippet for other user unless admin
+               if ($form->get('user')->getData() !== $this->getUser()) {
+                       //Prevent non-admin to access here
+                       $this->denyAccessUnlessGranted('ROLE_ADMIN', null, $this->translator->trans('Unable to access this page without role %role%!', ['%role%' => $this->translator->trans('Admin')]));
+               }
+
+               //Handle invalid form
+               if (!$form->isSubmitted() || !$form->isValid()) {
+                       //Set section
+                       $section = $this->translator->trans('Snippet add');
+
+                       //Set title
+                       $title = $this->translator->trans($this->config['site']['title']).' - '.$section;
+
+                       //Render the view
+                       return $this->render('@RapsysAir/snippet/add.html.twig', ['title' => $title, 'section' => $section, 'form' => $form->createView()]+$this->context);
+               }
+
+               //Get doctrine
+               $doctrine = $this->getDoctrine();
+
+               //Get manager
+               $manager = $doctrine->getManager();
+
+               //Get snippet
+               $snippet = $form->getData();
+
+               //Set created
+               $snippet->setCreated(new \DateTime('now'));
+
+               //Set updated
+               $snippet->setUpdated(new \DateTime('now'));
+
+               //Queue snippet save
+               $manager->persist($snippet);
+
+               //Flush to get the ids
+               $manager->flush();
+
+               //Add notice
+               $this->addFlash('notice', $this->translator->trans('Snippet in %locale% %location% for %user% created', ['%locale%' => $snippet->getLocale(), '%location%' => $this->translator->trans('at '.$snippet->getLocation()), '%user%' => $snippet->getUser()]));
+
+               //Extract and process referer
+               if ($referer = $request->headers->get('referer')) {
+                       //Create referer request instance
+                       $req = Request::create($referer);
+
+                       //Get referer path
+                       $path = $req->getPathInfo();
+
+                       //Get referer query string
+                       $query = $req->getQueryString();
+
+                       //Remove script name
+                       $path = str_replace($request->getScriptName(), '', $path);
+
+                       //Try with referer path
+                       try {
+                               //Save old context
+                               $oldContext = $this->router->getContext();
+
+                               //Force clean context
+                               //XXX: prevent MethodNotAllowedException because current context method is POST in onevendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php+42
+                               $this->router->setContext(new RequestContext());
+
+                               //Retrieve route matching path
+                               $route = $this->router->match($path);
+
+                               //Reset context
+                               $this->router->setContext($oldContext);
+
+                               //Clear old context
+                               unset($oldContext);
+
+                               //Extract name
+                               $name = $route['_route'];
+
+                               //Remove route and controller from route defaults
+                               unset($route['_route'], $route['_controller']);
+
+                               //Check if snippet view route
+                               if ($name == 'rapsys_air_organizer_view' && !empty($route['id'])) {
+                                       //Replace id
+                                       $route['id'] = $snippet->getUser()->getId();
+                               //Other routes
+                               } else {
+                                       //Set snippet
+                                       $route['snippet'] = $snippet->getId();
+                               }
+
+                               //Generate url
+                               return $this->redirectToRoute($name, $route);
+                       //No route matched
+                       } catch(MethodNotAllowedException|ResourceNotFoundException $e) {
+                               //Unset referer to fallback to default route
+                               unset($referer);
+                       }
+               }
+
+               //Redirect to cleanup the form
+               return $this->redirectToRoute('rapsys_air', ['snippet' => $snippet->getId()]);
+       }
+
+       /**
+        * Edit snippet
+        *
+        * @desc Persist snippet in database
+        *
+        * @param Request $request The request instance
+        *
+        * @return Response The rendered view or redirection
+        *
+        * @throws \RuntimeException When user has not at least guest role
+        */
+       public function edit(Request $request, $id) {
+               //Prevent non-guest to access here
+               $this->denyAccessUnlessGranted('ROLE_GUEST', null, $this->translator->trans('Unable to access this page without role %role%!', ['%role%' => $this->translator->trans('Guest')]));
+
+               //Get doctrine
+               $doctrine = $this->getDoctrine();
+
+               //Get snippet
+               if (empty($snippet = $doctrine->getRepository(Snippet::class)->findOneById($id))) {
+                       throw $this->createNotFoundException($this->translator->trans('Unable to find snippet: %id%', ['%id%' => $id]));
+               }
+
+               //Create ApplicationType form
+               $form = $this->createForm('Rapsys\AirBundle\Form\SnippetType', $snippet, [
+                       //Set the action
+                       'action' => $this->generateUrl('rapsys_air_snippet_edit', ['id' => $id]),
+                       //Set the form attribute
+                       'attr' => []
+               ]);
+
+               //Refill the fields in case of invalid form
+               $form->handleRequest($request);
+
+               //Prevent creating snippet for other user unless admin
+               if ($form->get('user')->getData() !== $this->getUser()) {
+                       //Prevent non-admin to access here
+                       $this->denyAccessUnlessGranted('ROLE_ADMIN', null, $this->translator->trans('Unable to access this page without role %role%!', ['%role%' => $this->translator->trans('Admin')]));
+               }
+
+               //Handle invalid form
+               if (!$form->isSubmitted() || !$form->isValid()) {
+                       //Set section
+                       $section = $this->translator->trans('Snippet %id%', ['%id%' => $id]);
+
+                       //Set title
+                       $title = $this->translator->trans($this->config['site']['title']).' - '.$section;
+
+                       //Render the view
+                       return $this->render('@RapsysAir/snippet/edit.html.twig', ['id' => $id, 'title' => $title, 'section' => $section, 'form' => $form->createView()]+$this->context);
+               }
+
+               //Get manager
+               $manager = $doctrine->getManager();
+
+               //Set updated
+               $snippet->setUpdated(new \DateTime('now'));
+
+               //Queue snippet save
+               $manager->persist($snippet);
+
+               //Flush to get the ids
+               $manager->flush();
+
+               //Add notice
+               $this->addFlash('notice', $this->translator->trans('Snippet %id% in %locale% %location% for %user% updated', ['%id%' => $id, '%locale%' => $snippet->getLocale(), '%location%' => $this->translator->trans('at '.$snippet->getLocation()), '%user%' => $snippet->getUser()]));
+
+               //Extract and process referer
+               if ($referer = $request->headers->get('referer')) {
+                       //Create referer request instance
+                       $req = Request::create($referer);
+
+                       //Get referer path
+                       $path = $req->getPathInfo();
+
+                       //Get referer query string
+                       $query = $req->getQueryString();
+
+                       //Remove script name
+                       $path = str_replace($request->getScriptName(), '', $path);
+
+                       //Try with referer path
+                       try {
+                               //Save old context
+                               $oldContext = $this->router->getContext();
+
+                               //Force clean context
+                               //XXX: prevent MethodNotAllowedException because current context method is POST in onevendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php+42
+                               $this->router->setContext(new RequestContext());
+
+                               //Retrieve route matching path
+                               $route = $this->router->match($path);
+
+                               //Reset context
+                               $this->router->setContext($oldContext);
+
+                               //Clear old context
+                               unset($oldContext);
+
+                               //Extract name
+                               $name = $route['_route'];
+
+                               //Remove route and controller from route defaults
+                               unset($route['_route'], $route['_controller']);
+
+                               //Check if snippet view route
+                               if ($name == 'rapsys_air_organizer_view' && !empty($route['id'])) {
+                                       //Replace id
+                                       $route['id'] = $snippet->getUser()->getId();
+                               //Other routes
+                               } else {
+                                       //Set snippet
+                                       $route['snippet'] = $snippet->getId();
+                               }
+
+                               //Generate url
+                               return $this->redirectToRoute($name, $route);
+                       //No route matched
+                       } catch(MethodNotAllowedException|ResourceNotFoundException $e) {
+                               //Unset referer to fallback to default route
+                               unset($referer);
+                       }
+               }
+
+               //Redirect to cleanup the form
+               return $this->redirectToRoute('rapsys_air', ['snippet' => $snippet->getId()]);
+       }
+}
diff --git a/Entity/Snippet.php b/Entity/Snippet.php
new file mode 100644 (file)
index 0000000..2aa9d3b
--- /dev/null
@@ -0,0 +1,175 @@
+<?php
+
+namespace Rapsys\AirBundle\Entity;
+
+use Rapsys\AirBundle\Entity\Location;
+use Rapsys\AirBundle\Entity\User;
+
+/**
+ * Snippet
+ */
+class Snippet {
+       /**
+        * @var integer
+        */
+       private $id;
+
+       /**
+        * @var string
+        */
+       protected $locale;
+
+       /**
+        * @var text
+        */
+       protected $description;
+
+       /**
+        * @var \DateTime
+        */
+       protected $created;
+
+       /**
+        * @var \DateTime
+        */
+       protected $updated;
+
+       /**
+        * @var \Rapsys\UserBundle\Entity\Location
+        */
+       protected $location;
+
+       /**
+        * @var \Rapsys\UserBundle\Entity\User
+        */
+       protected $user;
+
+       /**
+        * Get id
+        *
+        * @return integer
+        */
+       public function getId() {
+               return $this->id;
+       }
+
+       /**
+        * Set locale
+        *
+        * @param string $locale
+        *
+        * @return Snippet
+        */
+       public function setLocale($locale) {
+               $this->locale = $locale;
+
+               return $this;
+       }
+
+       /**
+        * Get locale
+        *
+        * @return string
+        */
+       public function getLocale() {
+               return $this->locale;
+       }
+
+       /**
+        * Set description
+        *
+        * @param string $description
+        *
+        * @return Snippet
+        */
+       public function setDescription($description) {
+               $this->description = $description;
+
+               return $this;
+       }
+
+       /**
+        * Get description
+        *
+        * @return string
+        */
+       public function getDescription() {
+               return $this->description;
+       }
+
+       /**
+        * Set created
+        *
+        * @param \DateTime $created
+        *
+        * @return User
+        */
+       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 User
+        */
+       public function setUpdated($updated) {
+               $this->updated = $updated;
+
+               return $this;
+       }
+
+       /**
+        * Get updated
+        *
+        * @return \DateTime
+        */
+       public function getUpdated() {
+               return $this->updated;
+       }
+
+       /**
+        * Set location
+        */
+       public function setLocation(Location $location) {
+               $this->location = $location;
+
+               return $this;
+       }
+
+       /**
+        * Get location
+        */
+       public function getLocation() {
+               return $this->location;
+       }
+
+       /**
+        * Set user
+        */
+       public function setUser(User $user) {
+               $this->user = $user;
+
+               return $this;
+       }
+
+       /**
+        * Get user
+        */
+       public function getUser() {
+               return $this->user;
+       }
+}
diff --git a/Form/SnippetType.php b/Form/SnippetType.php
new file mode 100644 (file)
index 0000000..9c21171
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+
+namespace Rapsys\AirBundle\Form;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+#use Symfony\Bridge\Doctrine\Form\Type\EntityType;
+use Symfony\Component\Form\Extension\Core\Type\HiddenType;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Validator\Constraints\NotBlank;
+use Rapsys\AirBundle\Form\Extension\Type\HiddenEntityType;
+use Rapsys\AirBundle\Entity\Location;
+use Rapsys\AirBundle\Entity\User;
+use Rapsys\AirBundle\Entity\Snippet;
+
+class SnippetType extends AbstractType {
+       /**
+        * {@inheritdoc}
+        */
+       public function buildForm(FormBuilderInterface $builder, array $options) {
+               return $builder
+                       ->add('locale', HiddenType::class, ['required' => true])
+                       ->add('location', HiddenEntityType::class, ['required' => true])
+                       ->add('user', HiddenEntityType::class, ['required' => true])
+                       ->add('description', TextareaType::class, ['attr' => ['placeholder' => 'Your description', 'cols' => 50, 'rows' => 15], 'constraints' => [new NotBlank(['message' => 'Please provide your description'])], 'required' => true])
+                       ->add('submit', SubmitType::class, ['label' => 'Send', 'attr' => ['class' => 'submit']]);
+                       #->add('delete', SubmitType::class, ['label' => 'Remove', 'attr' => ['class' => 'submit']]);
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function configureOptions(OptionsResolver $resolver) {
+               $resolver->setDefaults(['data_class' => Snippet::class, 'error_bubbling' => true, 'location' => null, 'user' => null]);
+               $resolver->setAllowedTypes('location', [Location::class, 'null']);
+               $resolver->setAllowedTypes('user', [User::class, 'null']);
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function getName() {
+               return 'snippet_form';
+       }
+}
diff --git a/Repository/SnippetRepository.php b/Repository/SnippetRepository.php
new file mode 100644 (file)
index 0000000..a1d696b
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+namespace Rapsys\AirBundle\Repository;
+
+use Symfony\Component\Translation\TranslatorInterface;
+use Doctrine\ORM\Query\ResultSetMapping;
+
+/**
+ * SnippetRepository
+ */
+class SnippetRepository extends \Doctrine\ORM\EntityRepository {
+       /**
+        * Find snippets by locale and user id
+        *
+        * @param string $locale The locale
+        * @param User|int $user The user
+        * @return array The snippets or empty array
+        */
+       public function findByLocaleUserId($locale, $user) {
+               //Fetch snippets
+               $ret = $this->getEntityManager()
+                       ->createQuery('SELECT s FROM RapsysAirBundle:Snippet s WHERE s.locale = :locale and s.user = :user')
+                       ->setParameter('locale', $locale)
+                       ->setParameter('user', $user)
+                       ->getResult();
+
+               //Send result
+               return $ret;
+       }
+}
diff --git a/Resources/config/doctrine/Snippet.orm.yml b/Resources/config/doctrine/Snippet.orm.yml
new file mode 100644 (file)
index 0000000..a6e9165
--- /dev/null
@@ -0,0 +1,35 @@
+Rapsys\AirBundle\Entity\Snippet:
+    type: entity
+    repositoryClass: Rapsys\AirBundle\Repository\SnippetRepository
+    table: snippets
+    id:
+        id:
+            type: integer
+            generator:
+                strategy: AUTO
+            options:
+                unsigned: true
+    fields:
+        locale:
+            type: string
+            length: 2
+        description:
+            type: text
+        created:
+            type: datetime
+        updated:
+            type: datetime
+    manyToOne:
+        location:
+            targetEntity: Rapsys\AirBundle\Entity\Location
+            inversedBy: snippets
+        user:
+            targetEntity: Rapsys\AirBundle\Entity\User
+            inversedBy: snippets
+    indexes:
+        #XXX: may be used in SnippetRepository::findByLocaleUserId
+        locale_user:
+            columns: [ locale, user_id ]
+    uniqueConstraints:
+        locale_location_user:
+            columns: [ locale, location_id, user_id ]
diff --git a/Resources/views/snippet/add.html.twig b/Resources/views/snippet/add.html.twig
new file mode 100644 (file)
index 0000000..75bf356
--- /dev/null
@@ -0,0 +1,20 @@
+{% extends '@RapsysAir/body.html.twig' %}
+{% block content %}
+       <section id="form">
+               <h2>{{ section }}</h2>
+               {{ form_start(form) }}
+                       <div>
+                               {{ form_row(form.description) }}
+
+                               {{ form_row(form.submit) }}
+
+                               {% if form.delete is defined %}
+                                       {{ form_row(form.delete) }}
+                               {% endif %}
+                       </div>
+
+                       {# render csrf token etc .#}
+                       <footer style="display:none">{{ form_rest(form) }}</footer>
+               {{ form_end(form) }}
+       </section>
+{% endblock %}
diff --git a/Resources/views/snippet/edit.html.twig b/Resources/views/snippet/edit.html.twig
new file mode 100644 (file)
index 0000000..75bf356
--- /dev/null
@@ -0,0 +1,20 @@
+{% extends '@RapsysAir/body.html.twig' %}
+{% block content %}
+       <section id="form">
+               <h2>{{ section }}</h2>
+               {{ form_start(form) }}
+                       <div>
+                               {{ form_row(form.description) }}
+
+                               {{ form_row(form.submit) }}
+
+                               {% if form.delete is defined %}
+                                       {{ form_row(form.delete) }}
+                               {% endif %}
+                       </div>
+
+                       {# render csrf token etc .#}
+                       <footer style="display:none">{{ form_rest(form) }}</footer>
+               {{ form_end(form) }}
+       </section>
+{% endblock %}