diff --git a/app/Resources/views/maintenance.html.twig b/app/Resources/views/maintenance.html.twig new file mode 100644 index 0000000..6655e80 --- /dev/null +++ b/app/Resources/views/maintenance.html.twig @@ -0,0 +1,39 @@ +{% extends 'base.html.twig' %} + +{% block body %} +

+ We are sorry, but we are in maintenance mode. +

+

+ We are busy upgrading the system and will be back shortly.
+ Thank you for your patience! +

+ +

+ +

+ + + +{% endblock %} +{% block javascripts %} + +{% endblock %} \ No newline at end of file diff --git a/app/Resources/views/off_canvas.html.twig b/app/Resources/views/off_canvas.html.twig index 189f180..ab623f8 100644 --- a/app/Resources/views/off_canvas.html.twig +++ b/app/Resources/views/off_canvas.html.twig @@ -8,6 +8,11 @@
  • Our Story
  • RSVP
  • Gallery
  • + {% if is_granted('IS_AUTHENTICATED_FULLY') %} +
  • + Upload Photo +
  • + {% endif %}
  • Location
  • Get In Touch
  • diff --git a/app/config/config.yml b/app/config/config.yml index c3df565..0b0799f 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -13,6 +13,7 @@ parameters: google.application_name: 'doughnut-wedding' katrina.email: katrina.a.johnson@gmail.com eric.email: sikofitt@gmail.com + maintenance: false framework: cache: app: cache.adapter.redis diff --git a/app/config/services.yml b/app/config/services.yml index 6b7946e..8aca29e 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -38,7 +38,11 @@ services: class: \Google_Service_Storage factory: ['Sikofitt\DoughnutWeddingBundle\Factory\GoogleCloudStorageServiceFactory', createGoogleCloudService] arguments: ['%google.credentials_file%', '%google.client_id%', '%google.application_name%'] - + doughnutwedding.event.check_maintenance_mode: + class: Sikofitt\DoughnutWeddingBundle\EventListener\CheckForMaintenanceModeListenerEvent + arguments: ['@service_container', '@security.token_storage'] + tags: + - { name: kernel.event_listener, event: kernel.request } doughnutwedding.event.check_email_on_reset_event: class: Sikofitt\DoughnutWeddingBundle\EventListener\CheckThatEmailAndUserNameExistOnResetRequest arguments: ['@router', '@templating.engine.twig'] diff --git a/src/Sikofitt/DoughnutWeddingBundle/Controller/GalleryController.php b/src/Sikofitt/DoughnutWeddingBundle/Controller/GalleryController.php index 4714354..62ce298 100644 --- a/src/Sikofitt/DoughnutWeddingBundle/Controller/GalleryController.php +++ b/src/Sikofitt/DoughnutWeddingBundle/Controller/GalleryController.php @@ -22,19 +22,28 @@ namespace Sikofitt\DoughnutWeddingBundle\Controller; use Pagerfanta\Adapter\ArrayAdapter; use Pagerfanta\Pagerfanta; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; -use Sikofitt\DoughnutWeddingBundle\Entity\Image; -use Sikofitt\DoughnutWeddingBundle\Entity\ImageComment; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\{ + Method, + Route +}; +use Sikofitt\DoughnutWeddingBundle\Entity\{ + Image, + ImageComment, + User +}; use Sikofitt\DoughnutWeddingBundle\Form\GalleryUploadType; use Symfony\Bundle\FrameworkBundle\Controller\Controller; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\{ + JsonResponse, + RedirectResponse, + Request, + Response +}; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Security\Csrf\CsrfToken; +use Symfony\Component\Security\{ + Core\Authentication\Token\TokenInterface, + Csrf\CsrfToken +}; /** * Class ImageController. @@ -47,9 +56,12 @@ class GalleryController extends Controller * @Route("/") * @Method({"GET"}) */ - public function indexAction(Request $request) : Response + public function indexAction(Request $request): Response { - $images = $this->get('doctrine.orm.default_entity_manager')->getRepository('SikofittDoughnutWeddingBundle:Image')->findAll(); + $images = $this + ->get('doctrine.orm.entity_manager') + ->getRepository('SikofittDoughnutWeddingBundle:Image') + ->findAllSortedByUpdatedAtDesc(); $chunks = array_chunk($images, 12); $adapter = new ArrayAdapter($images); @@ -72,7 +84,10 @@ class GalleryController extends Controller */ public function pageAction(Request $request, int $number) { - $images = $this->get('doctrine.orm.default_entity_manager')->getRepository('SikofittDoughnutWeddingBundle:Image')->findAll(); + $images = $this + ->get('doctrine.orm.entity_manager') + ->getRepository('SikofittDoughnutWeddingBundle:Image') + ->findAllSortedByUpdatedAtDesc(); $adapter = new ArrayAdapter($images); $pager = new Pagerfanta($adapter); @@ -80,7 +95,10 @@ class GalleryController extends Controller $pager->setMaxPerPage(12); $pager->setCurrentPage($number); - return $this->render('SikofittDoughnutWeddingBundle:Image:index.html.twig', ['images' => $chunks[$number - 1], 'pager' => $pager]); + return $this->render('SikofittDoughnutWeddingBundle:Image:index.html.twig', [ + 'images' => $chunks[$number - 1] ?? $images, + 'pager' => $pager, + ]); } /** @@ -105,9 +123,19 @@ class GalleryController extends Controller $em->persist($image); $em->flush(); - $baseName = $image->getImageFile()->getBasename('.'.$image->getImageFile()->getExtension()); + $baseName = $image + ->getImageFile() + ->getBasename( + sprintf('.%s', $image->getImageFile()->getExtension()) + ); - return new RedirectResponse($this->get('router')->generate('view_gallery_image_by_hash', ['imageHash' => $baseName])); + return new RedirectResponse( + $this + ->get('router') + ->generate('view_gallery_image_by_hash', [ + 'imageHash' => $baseName, + ]) + ); } return $this->render('SikofittDoughnutWeddingBundle:Image:upload.html.twig', [ @@ -122,20 +150,34 @@ class GalleryController extends Controller public function ajaxCommentAction(Request $request): JsonResponse { if (false === $request->getSession()->has('_security_secured_area')) { - return new JsonResponse(['status' => 403, 'message' => 'You must be logged in to post a comment'], 403); + return new JsonResponse([ + 'status' => 403, + 'message' => 'You must be logged in to post a comment', + ], 403); } + /** @var \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $data */ - $data = unserialize($request->getSession()->get('_security_secured_area'), [TokenInterface::class]); + $data = unserialize( + $request->getSession()->get('_security_secured_area'), [ + TokenInterface::class, + ]); $tokenManager = $this->get('security.csrf.token_manager'); $fileName = $request->request->get('file'); - $token = $request->request->get('_token'); $csrfToken = new CsrfToken($fileName, $token); if (false === $tokenManager->isTokenValid($csrfToken)) { return new JsonResponse(['status' => Response::HTTP_EXPECTATION_FAILED, 'message' => 'Invalid Token'], Response::HTTP_EXPECTATION_FAILED); } + + if (false === $data->getUser() instanceof User) { + return new JsonResponse([ + 'status' => 403, + 'message' => 'You must be logged in to post a comment', + ], 403); + } + $em = $this->get('doctrine.orm.entity_manager'); $user = $em ->getRepository('SikofittDoughnutWeddingBundle:User') @@ -155,6 +197,7 @@ class GalleryController extends Controller ->setIsChild(false) ->setCreated(new \DateTime('now')) ->setChildComments(null); + $em->persist($newComment); $em->flush(); @@ -187,13 +230,17 @@ class GalleryController extends Controller ->get('doctrine.orm.entity_manager') ->createQueryBuilder() ->select('i') - ->from('Sikofitt\DoughnutWeddingBundle\Entity\Image', 'i') + ->from(Image::class, 'i') ->where('i.imageName LIKE :imageHash') ->setParameter('imageHash', $imageHash.'%') - ->getQuery()->getOneOrNullResult(); + ->getQuery() + ->getOneOrNullResult(); + if (null !== $image) { - return $this->render('@SikofittDoughnutWedding/Image/view_image.html.twig', - ['image' => $image]); + return $this + ->render('@SikofittDoughnutWedding/Image/view_image.html.twig', [ + 'image' => $image, + ]); } throw new NotFoundHttpException(); @@ -232,6 +279,8 @@ class GalleryController extends Controller * * @param \Symfony\Component\HttpFoundation\Request $request * @param string $category + * @param int $number + * The current page * * @return Response */ diff --git a/src/Sikofitt/DoughnutWeddingBundle/Entity/Image.php b/src/Sikofitt/DoughnutWeddingBundle/Entity/Image.php index bb29904..11f5372 100644 --- a/src/Sikofitt/DoughnutWeddingBundle/Entity/Image.php +++ b/src/Sikofitt/DoughnutWeddingBundle/Entity/Image.php @@ -313,6 +313,11 @@ class Image */ public function setTags($tags): Image { + foreach ($tags as $key => $value) { + if (null === $value || '' === $value) { + unset($tags[$key]); + } + } $this->tags = $tags; return $this; @@ -321,10 +326,18 @@ class Image /** * Get tags. * - * @return array + * @return array|string */ public function getTags() { + if (true === is_array($this->tags)) { + foreach ((array) $this->tags as $key => $value) { + if (null === $value || '' === $value) { + unset($this->tags[$key]); + } + } + } + return $this->tags; } @@ -335,7 +348,7 @@ class Image * * @return Image */ - public function setCategory($category) + public function setCategory($category): ?Image { $this->category = $category; @@ -347,7 +360,7 @@ class Image * * @return string */ - public function getCategory() + public function getCategory(): ?string { return $this->category; } diff --git a/src/Sikofitt/DoughnutWeddingBundle/EventListener/CheckForMaintenanceModeListenerEvent.php b/src/Sikofitt/DoughnutWeddingBundle/EventListener/CheckForMaintenanceModeListenerEvent.php new file mode 100644 index 0000000..ed94de7 --- /dev/null +++ b/src/Sikofitt/DoughnutWeddingBundle/EventListener/CheckForMaintenanceModeListenerEvent.php @@ -0,0 +1,67 @@ +. + */ + +namespace Sikofitt\DoughnutWeddingBundle\EventListener; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; + + +class CheckForMaintenanceModeListenerEvent +{ + private $container; + private $token; + + public function __construct(ContainerInterface $container, TokenStorageInterface $token) + { + $this->container = $container; + $this->token = $token; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $maintenance = (bool) $this->container->getParameter('maintenance'); + + if (null === $maintenance || false === $maintenance) { + return; + } + + $authToken = $this->token->getToken(); + if ( + null !== $authToken && + true === $this->container + ->get('security.authorization_checker') + ->isGranted('ROLE_ADMIN', $authToken) + ) { + $event + ->getRequest() + ->getSession() + ->getFlashBag() + ->set('warning', 'Maintenance mode is on.'); + + return; + } + + $event->setResponse(new Response($this->container->get('twig') + ->render('maintenance.html.twig'))); + } +} diff --git a/src/Sikofitt/DoughnutWeddingBundle/Form/GalleryUploadType.php b/src/Sikofitt/DoughnutWeddingBundle/Form/GalleryUploadType.php index 5bc32db..3b21258 100644 --- a/src/Sikofitt/DoughnutWeddingBundle/Form/GalleryUploadType.php +++ b/src/Sikofitt/DoughnutWeddingBundle/Form/GalleryUploadType.php @@ -25,56 +25,69 @@ use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Validator\Constraints as Assert; use Vich\UploaderBundle\Form\Type\VichImageType; class GalleryUploadType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { + $image = new Assert\Image(); + $builder->add('imageFile', VichImageType::class, [ 'label_attr' => [ - 'class' => 'uk-form-label uk-hidden', + 'class' => 'uk-form-label uk-visible@m', + 'id' => 'file-field-label', + 'style' => 'margin-right:14px;', ], + 'attr' => [ 'class' => 'uk-padding-small', + 'accept' => '.jpg,.jpeg,.png,.gif,.tiff', + ], + 'constraints' => [ + new Assert\Image(), ], ]) - ->add('description', TextareaType::class, [ - 'required' => false, - 'label' => 'Description of image.', - 'attr' => [ - 'rows' => 4, - 'class' => 'uk-textarea uk-form-large uk-padding-small', - ], - 'label_attr' => [ - 'class' => 'uk-form-label', - ], - ]) - ->add('tags', CollectionType::class, [ - 'required' => false, - 'entry_type' => TextType::class, - 'entry_options' => [ + ->add('description', TextareaType::class, [ + 'required' => false, + 'label' => 'Description of image.', 'attr' => [ - 'class' => 'uk-input uk-form-large uk-width-medium', + 'rows' => 4, + 'class' => 'uk-textarea uk-form-large uk-padding-small uk-margin-small-bottom', + 'placeholder' => 'Description of image', ], - ], - 'allow_add' => true, - 'allow_delete' => true, - 'label_attr' => [ - 'class' => 'uk-form-label', - ], - 'attr' => [ - 'class' => 'uk-input uk-form-large uk-padding-small', - ], - ]) - ->add('category', TextType::class, [ - 'required' => false, - 'label_attr' => [ - 'class' => 'uk-form-label', - ], - 'attr' => [ - 'class' => 'uk-input uk-form-large uk-padding-small', - ], - ]); + 'label_attr' => [ + 'class' => 'uk-form-label uk-visible@m', + ], + ]) + ->add('tags', CollectionType::class, [ + 'required' => false, + 'entry_type' => TextType::class, + 'entry_options' => [ + 'attr' => [ + 'class' => 'uk-input uk-form-large uk-width-medium uk-margin-small-bottom', + 'placeholder' => 'Enter tag', + ], + ], + 'allow_add' => true, + 'allow_delete' => true, + 'label_attr' => [ + 'class' => 'uk-form-label uk-visible@m', + ], + 'attr' => [ + 'class' => 'uk-input uk-form-large uk-padding-small', + ], + ]) + ->add('category', TextType::class, [ + 'required' => false, + 'label_attr' => [ + 'class' => 'uk-form-label uk-visible@m', + ], + 'attr' => [ + 'class' => 'uk-input uk-form-large uk-padding-small uk-margin-small-bottom', + 'placeholder' => 'Add category', + ], + ]); } } diff --git a/src/Sikofitt/DoughnutWeddingBundle/Repository/ImageRepository.php b/src/Sikofitt/DoughnutWeddingBundle/Repository/ImageRepository.php index f135f55..ccfe9ee 100644 --- a/src/Sikofitt/DoughnutWeddingBundle/Repository/ImageRepository.php +++ b/src/Sikofitt/DoughnutWeddingBundle/Repository/ImageRepository.php @@ -32,41 +32,61 @@ use Sikofitt\DoughnutWeddingBundle\Entity\Image; class ImageRepository extends EntityRepository { /** - * @param string $tag - * - * @return array - */ - public function findImageByTag(string $tag): array - { - $images = $this->getEntityManager() - ->createQueryBuilder() - ->select('i') - ->from(Image::class, 'i') - ->where('i.tags LIKE :tag') - ->setParameter('tag', '%'.$tag.'%') - ->getQuery()->getResult(); - /** - * @var int $key - * @var Image $image - */ - foreach ($images as $key => $image) { - if (false === in_array($tag, $image->getTags(), true)) { - unset($images[$key]); - } - } + * @return array|null + */ + public function findAllSortedByUpdatedAtDesc(): ?array + { + return $this + ->getEntityManager() + ->createQueryBuilder() + ->select('i') + ->from(Image::class, 'i') + ->orderBy('i.updatedAt', 'DESC') + ->getQuery() + ->getResult(); + } - return $images; + /** + * @param string $tag + * + * @return array + */ + public function findImageByTag(string $tag): array + { + $images = $this->getEntityManager() + ->createQueryBuilder() + ->select('i') + ->from(Image::class, 'i') + ->orderBy('i.updatedAt', 'DESC') + ->where('i.tags LIKE :tag') + ->setParameter('tag', '%'.$tag.'%') + ->getQuery() + ->getResult(); + + /** + * @var int $key + * @var Image $image + */ + foreach ($images as $key => $image) { + if (false === in_array($tag, $image->getTags(), true)) { + unset($images[$key]); + } } + return $images; + } + public function findImageByCategory(string $category): array { return $this - ->getEntityManager() - ->createQueryBuilder() - ->select('i') - ->from(Image::class, 'i') - ->where('i.category =:category') - ->setParameter('category', $category) - ->getQuery()->getResult(); + ->getEntityManager() + ->createQueryBuilder() + ->select('i') + ->from(Image::class, 'i') + ->orderBy('i.updatedAt', 'DESC') + ->where('i.category =:category') + ->setParameter('category', $category) + ->getQuery() + ->getResult(); } } diff --git a/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/index.html.twig b/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/index.html.twig index 268ae5c..4acb318 100644 --- a/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/index.html.twig +++ b/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/index.html.twig @@ -24,7 +24,11 @@
    -
    +
    @@ -45,7 +49,9 @@
    • View more like this
    • {% for tag in image.tags %} + {% if tag is not null and tag is not empty %} {{ tag|capitalize }} + {% endif %} {% endfor %}
    {% endif %} @@ -146,6 +152,9 @@ } }); + jQuery('.comment-scroll').on('click', function(event) { + jQuery('#modal-full-'+jQuery(this).attr('data-id')).animate({scrollTop: jQuery('#comment-panel-'+jQuery(this).attr('data-id')).offset().top}, 1000); + }); }); {% endblock javascripts %} \ No newline at end of file diff --git a/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/upload.html.twig b/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/upload.html.twig index 3def15e..dbf2405 100644 --- a/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/upload.html.twig +++ b/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/upload.html.twig @@ -2,14 +2,20 @@ {% block title %}Gallery Upload{% endblock title %} {% block body %} -

    Upload an Image to the Gallery

    +

    Upload an Image to the Gallery

    {{ form_start(form, {'attr': { 'class': 'uk-form uk-form-horizontal'}}) }} - {{ form_label(form.imageFile) }} -
    - {{ form_errors(form.imageFile) }} - {{ form_widget(form.imageFile) }} -
    + +
    + {{ form_label(form.imageFile) }} +
    + {{ form_errors(form.imageFile) }} + {{ form_widget(form.imageFile) }} + +
    +
    +
    + {{ form_label(form.description) }}
    {{ form_errors(form.description) }} @@ -34,11 +40,10 @@ Add Tag
    + +
    - - - - +
    {{ form_rest(form) }} {{ form_end(form) }} {% endblock body %} @@ -48,6 +53,10 @@ var tagCount = '{{ form.tags|length }}'; jQuery(document).ready(function() { + jQuery('form').on('change', 'input[type=file]', function(event) { + jQuery('#file-select').text(this.files.item(0).name); + }); + jQuery('#add-tag').click(function(e) { e.preventDefault(); @@ -64,6 +73,9 @@ // create a new list element and add it to the list var newLi = jQuery('
  • ').html(newWidget); newLi.appendTo(tagList); + var input = jQuery('#gallery_upload_tags_'+(tagCount -1)); + input.focus(); + }); }) diff --git a/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/view_image.html.twig b/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/view_image.html.twig index dc87871..cc35656 100644 --- a/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/view_image.html.twig +++ b/src/Sikofitt/DoughnutWeddingBundle/Resources/views/Image/view_image.html.twig @@ -38,7 +38,9 @@
    • View more like this
    • {% for tag in image.tags %} + {% if tag is not null and tag is not empty %} {{ tag|capitalize }} + {% endif %} {% endfor %}
    {% endif %}