<?php
/**
* digitvision
*
* @category digitvision
* @package Shopware\Plugins\DvsnTicketSystem
* @copyright (c) 2020 digitvision
*/
namespace Dvsn\TicketSystem\Storefront\Controller;
use Dvsn\TicketSystem\Core\System\Ticket\Aggregate\TicketForm\TicketFormEntity;
use Dvsn\TicketSystem\Core\System\Ticket\Aggregate\TicketFormElement\TicketFormElementEntity;
use Dvsn\TicketSystem\Core\System\Ticket\TicketEntity;
use Dvsn\TicketSystem\Service\CaptchaService;
use Dvsn\TicketSystem\Service\Ticket\ElementHandler\Exception\InvalidElementException;
use Dvsn\TicketSystem\Service\Ticket\ElementHandlerFactoryInterface;
use Dvsn\TicketSystem\Service\Ticket\TicketCreatorServiceInterface;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Framework\Adapter\Twig\StringTemplateRenderer;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Core\Framework\Routing\Annotation\LoginRequired;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
use Shopware\Core\SalesChannelRequest;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\Salutation\SalutationCollection;
use Shopware\Core\System\Salutation\SalutationEntity;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Controller\StorefrontController;
use Shopware\Storefront\Page\GenericPageLoaderInterface;
use Shopware\Storefront\Page\Page;
use Symfony\Component\Routing\Annotation\Route;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class TicketSystemController extends StorefrontController
{
/**
* ...
*
* @var EntityRepositoryInterface
*/
protected $ticketRepository;
/**
* ...
*
* @var EntityRepositoryInterface
*/
protected $ticketFormRepository;
/**
* ...
*
* @var EntityRepositoryInterface
*/
protected $ticketHistoryRepository;
/**
* ...
*
* @var SalesChannelRepositoryInterface
*/
private $salutationRepository;
/**
* ...
*
* @var TicketCreatorServiceInterface
*/
private $ticketCreatorService;
/**
* ...
*
* @var SystemConfigService
*/
protected $systemConfigService;
/**
* ...
*
* @var GenericPageLoaderInterface
*/
protected $genericPageLoader;
/**
* ...
*
* @var StringTemplateRenderer
*/
protected $templateRenderer;
/**
* ...
*
* @var ElementHandlerFactoryInterface
*/
protected $elementHandlerFactory;
/**
* ...
*
* @param EntityRepositoryInterface $ticketRepository
* @param EntityRepositoryInterface $ticketFormRepository
* @param EntityRepositoryInterface $ticketHistoryRepository
* @param SalesChannelRepositoryInterface $salutationRepository
* @param TicketCreatorServiceInterface $ticketCreatorService
* @param SystemConfigService $systemConfigService
* @param GenericPageLoaderInterface $genericPageLoader
* @param StringTemplateRenderer $templateRenderer
* @param ElementHandlerFactoryInterface $elementHandlerFactory
*/
public function __construct(
EntityRepositoryInterface $ticketRepository,
EntityRepositoryInterface $ticketFormRepository,
EntityRepositoryInterface $ticketHistoryRepository,
SalesChannelRepositoryInterface $salutationRepository,
TicketCreatorServiceInterface $ticketCreatorService,
SystemConfigService $systemConfigService,
GenericPageLoaderInterface $genericPageLoader,
StringTemplateRenderer $templateRenderer,
ElementHandlerFactoryInterface $elementHandlerFactory
)
{
// set params
$this->ticketRepository = $ticketRepository;
$this->ticketFormRepository = $ticketFormRepository;
$this->ticketHistoryRepository = $ticketHistoryRepository;
$this->salutationRepository = $salutationRepository;
$this->ticketCreatorService = $ticketCreatorService;
$this->systemConfigService = $systemConfigService;
$this->genericPageLoader = $genericPageLoader;
$this->templateRenderer = $templateRenderer;
$this->elementHandlerFactory = $elementHandlerFactory;
}
/**
* ...
*
* @RouteScope(scopes={"storefront"})
* @Route("/dvsn/ticket-system",
* name="frontend.dvsn.ticket-system",
* options={"seo"="false"},
* methods={"GET"}
* )
*
* @param Request $request
* @param RequestDataBag $data
* @param Context $context
* @param SalesChannelContext $salesChannelContext
*
* @return Response
*/
public function index(Request $request, RequestDataBag $data, Context $context, SalesChannelContext $salesChannelContext): Response
{
throw new \Exception('not implemented yet');
}
/**
* ...
*
* @RouteScope(scopes={"storefront"})
* @Route("/dvsn/ticket-system/ticket/{id}",
* name="frontend.dvsn.ticket-system.ticket",
* options={"seo"="false"},
* methods={"GET"}
* )
*
* @param string $id
* @param Request $request
* @param RequestDataBag $data
* @param Context $context
* @param SalesChannelContext $salesChannelContext
*
* @return Response
*/
public function ticket(string $id, Request $request, RequestDataBag $data, Context $context, SalesChannelContext $salesChannelContext): Response
{
// get possible status
$status = (int) $request->query->get('status');
// get the ticket
try {
$ticket = $this->getTicket($id, $salesChannelContext);
} catch (\Exception $exception) {
throw new \Exception('ticket not found');
}
/** @var Page $page */
$page = $this->genericPageLoader->load($request, $salesChannelContext);
// render template
return $this->renderStorefront('@Storefront/storefront/page/dvsn/ticket-system/ticket.html.twig', ['page' => [
'ticket' => $ticket,
'status' => $status,
'config' => $this->systemConfigService->get('DvsnTicketSystem.config'),
'header' => $page->getHeader(),
'footer' => $page->getFooter(),
'salesChannelPaymentMethods' => $page->getSalesChannelPaymentMethods(),
'salesChannelShippingMethods' => $page->getSalesChannelShippingMethods(),
'metaInformation' => $page->getMetaInformation()
]]);
}
/**
* ...
*
* @RouteScope(scopes={"storefront"})
* @Route("/dvsn/ticket-system/create/{id}",
* name="frontend.dvsn.ticket-system.create",
* options={"seo"="false"},
* methods={"GET"}
* )
*
* @param string $id
* @param Request $request
* @param RequestDataBag $data
* @param Context $context
* @param SalesChannelContext $salesChannelContext
*
* @return Response
*/
public function create(string $id, Request $request, RequestDataBag $data, Context $context, SalesChannelContext $salesChannelContext): Response
{
// get the form
try {
$form = $this->getTicketForm($id, $salesChannelContext);
} catch (\Exception $exception) {
throw new \Exception('form not found');
}
// default values
$values = [
'salutationId' => '',
'firstName' => '',
'lastName' => '',
'email' => '',
'phone' => ''
];
// get the customer
$customer = $salesChannelContext->getCustomer();
// do we have one?
if ($customer instanceof CustomerEntity) {
// set the defaults
$values['salutationId'] = $customer->getSalutationId();
$values['firstName'] = $customer->getFirstName();
$values['lastName'] = $customer->getLastName();
$values['email'] = $customer->getEmail();
$values['phone'] = (string) $customer->getActiveBillingAddress()->getPhoneNumber();
}
/** @var TicketFormElementEntity $element */
foreach ($form->getElements() as $element) {
// get key and value
$key = $element->getId();
$value = (string) $element->getContext();
// parse the value
$value = $this->templateRenderer->render(
$value,
[
'salesChannel' => $salesChannelContext->getSalesChannel(),
'customer' => $salesChannelContext->getCustomer(),
],
$context
);
// set it
$values[$key] = $value;
}
// set captcha
$captcha = ($this->systemConfigService->get('DvsnTicketSystem.config.captchaStatus') === true)
? $this->container->get('Dvsn\TicketSystem\Service\CaptchaService')->getBase64()
: '';
/** @var Page $page */
$page = $this->genericPageLoader->load($request, $salesChannelContext);
// render template
return $this->renderStorefront('@Storefront/storefront/page/dvsn/ticket-system/create.html.twig', ['page' => [
'form' => $form,
'values' => $values,
'captcha' => $captcha,
'salutations' => $this->getSalutations($salesChannelContext),
'config' => $this->systemConfigService->get('DvsnTicketSystem.config'),
'header' => $page->getHeader(),
'footer' => $page->getFooter(),
'salesChannelPaymentMethods' => $page->getSalesChannelPaymentMethods(),
'salesChannelShippingMethods' => $page->getSalesChannelShippingMethods(),
'metaInformation' => $page->getMetaInformation()
]]);
}
/**
* ...
*
* @param SalesChannelContext $salesChannelContext
*
* @return array
*/
private function getSalutations(SalesChannelContext $salesChannelContext): array
{
/** @var SalutationCollection $salutations */
$salutations = $this->salutationRepository->search(new Criteria(), $salesChannelContext)->getEntities();
// sort by key
$salutations->sort(function (SalutationEntity $a, SalutationEntity $b) {
return $b->getSalutationKey() <=> $a->getSalutationKey();
});
// we need them in a simple array
$arr = [];
// set every salutation to array
foreach ($salutations as $salutation) {
$arr[] = [
'key' => $salutation->getId(),
'value' => $salutation->getTranslation('displayName')
];
}
// return it
return $arr;
}
/**
* ...
*
* @RouteScope(scopes={"storefront"})
* @Route("/dvsn/ticket-system/create-submit",
* name="frontend.dvsn.ticket-system.create-submit",
* options={"seo"="false"},
* methods={"POST"}
* )
*
* @param string $id
* @param Request $request
* @param RequestDataBag $data
* @param Context $context
* @param SalesChannelContext $salesChannelContext
*
* @return Response
*/
public function createSubmit(Request $request, RequestDataBag $data, Context $context, SalesChannelContext $salesChannelContext): Response
{
// get the form
try {
$form = $this->getTicketForm($data->get('id'), $salesChannelContext);
} catch (\Exception $exception) {
throw new \Exception('form not found');
}
// check captcha
if ($this->systemConfigService->get('DvsnTicketSystem.config.captchaStatus') === true) {
/** @var CaptchaService $captchaService */
$captchaService = $this->container->get('Dvsn\TicketSystem\Service\CaptchaService');
// not valid?
if ($captchaService->isValid($data->get('captcha')) === true) {
// redirect back to ticket form
$this->addFlash('danger', $this->trans('dvsn-ticket-system.create.flash.captcha-error'));
return $this->redirectToRoute('frontend.dvsn.ticket-system.create', ['id' => $data->get('id')]);
}
}
// additional data here
$json = [];
/** @var TicketFormElementEntity $element */
foreach ($form->getElements() as $element) {
// get the value
$value = (string) $data->get('form-elements')->get($element->getId());
// try to handle the element and ignore elements without a handler
// which may be just a title or line-break
try {
// handle it
$value = $this->elementHandlerFactory->get($element->getType())->handle(
$value,
$request,
$form,
$element,
$salesChannelContext
);
// add to json
$json[] = [
'type' => $element->getType(),
'key' => $element->getName(),
'value' => $value
];
} catch (InvalidElementException $exception) {}
}
// create a ticket
$ticket = $this->ticketCreatorService->create(
$form,
$data->get('salutation'),
strip_tags($data->get('firstName')),
strip_tags($data->get('lastName')),
strip_tags($data->get('email')),
strip_tags($data->get('phone')),
strip_tags((string) $data->get('message')),
$json,
($salesChannelContext->getCustomer() instanceof CustomerEntity) ? $salesChannelContext->getCustomer()->getId() : null,
$salesChannelContext->getContext()->getLanguageId(),
$salesChannelContext->getSalesChannel()->getId(),
$request->attributes->get(SalesChannelRequest::ATTRIBUTE_DOMAIN_ID),
null,
$salesChannelContext->getContext()
);
// redirect to ticket
return $this->redirectToRoute('frontend.dvsn.ticket-system.ticket', ['id' => $ticket->getId(), 'status' => 10]);
}
/**
* ...
*
* @RouteScope(scopes={"storefront"})
* @Route("/dvsn/ticket-system/ticket-submit",
* name="frontend.dvsn.ticket-system.ticket-submit",
* options={"seo"="false"},
* methods={"POST"}
* )
*
* @param string $id
* @param Request $request
* @param RequestDataBag $data
* @param Context $context
* @param SalesChannelContext $salesChannelContext
*
* @return Response
*/
public function ticketSubmit(Request $request, RequestDataBag $data, Context $context, SalesChannelContext $salesChannelContext): Response
{
// get params
$id = (string) $data->get('id');
$message = (string) $data->get('message');
$message = strip_tags($message);
// get the ticket
try {
$ticket = $this->getTicket($id, $salesChannelContext);
} catch (\Exception $exception) {
throw new \Exception('ticket not found');
}
// defaults
$preStatusId = null;
$postStatusId = null;
// current status id
$statusId = $ticket->getStatusId();
// did we change it?
if ($ticket->getStatusId() !== $ticket->getAnswerStatusId()) {
// save
$preStatusId = $ticket->getStatusId();
$postStatusId = $ticket->getAnswerStatusId();
$statusId = $ticket->getAnswerStatusId();
}
// force an update for updatedAt for the sorting
$this->ticketRepository->update(
[['id' => $id, 'statusId' => $statusId]],
$context
);
// set a history item
$history = [
'id' => Uuid::randomHex(),
'ticketId' => $id,
'message' => $message,
'customerId' => ($salesChannelContext->getCustomer() instanceof CustomerEntity) ? $salesChannelContext->getCustomer()->getId() : null,
'preStatusId' => $preStatusId,
'postStatusId' => $postStatusId
];
// save it
$this->ticketHistoryRepository->create(
[$history],
$context
);
// return to ticket
return $this->redirectToRoute('frontend.dvsn.ticket-system.ticket', ['id' => $ticket->getId(), 'status' => 20]);
}
/**
* ...
*
* @RouteScope(scopes={"storefront"})
* @Route("/dvsn/ticket-system/list",
* name="frontend.dvsn.ticket-system.list",
* options={"seo"="false"},
* methods={"GET"}
* )
* @LoginRequired()
*
* @param Request $request
* @param RequestDataBag $data
* @param Context $context
* @param SalesChannelContext $salesChannelContext
*
* @return Response
*/
public function list(Request $request, RequestDataBag $data, Context $context, SalesChannelContext $salesChannelContext): Response
{
// get the tickets from the customer
$tickets = $this->getCustomerTickets($salesChannelContext);
/** @var Page $page */
$page = $this->genericPageLoader->load($request, $salesChannelContext);
// render template
return $this->renderStorefront('@Storefront/storefront/page/dvsn/ticket-system/list.html.twig', ['page' => [
'tickets' => $tickets,
'salutations' => $this->getSalutations($salesChannelContext),
'config' => $this->systemConfigService->get('DvsnTicketSystem.config'),
'header' => $page->getHeader(),
'footer' => $page->getFooter(),
'salesChannelPaymentMethods' => $page->getSalesChannelPaymentMethods(),
'salesChannelShippingMethods' => $page->getSalesChannelShippingMethods(),
'metaInformation' => $page->getMetaInformation()
]]);
}
/**
* ...
*
* @param string $id
* @param SalesChannelContext $salesChannelContext
*
* @return TicketFormEntity
*/
private function getTicketForm(string $id, SalesChannelContext $salesChannelContext): TicketFormEntity
{
// find the order
$criteria = new Criteria();
$criteria->addAssociation('elements');
$criteria->addFilter(new EqualsFilter('id', $id));
$criteria->getAssociation('elements')->addSorting(new FieldSorting('position', FieldSorting::ASCENDING));
/** @var OrderEntity $order */
$form = $this->ticketFormRepository->search(
$criteria,
$salesChannelContext->getContext()
)->getEntities()->first();
// return it
return $form;
}
/**
* ...
*
* @param string $id
* @param SalesChannelContext $salesChannelContext
*
* @return TicketEntity
*/
private function getTicket(string $id, SalesChannelContext $salesChannelContext): TicketEntity
{
// find the order
$criteria = new Criteria();
$criteria->addAssociation('status');
$criteria->addAssociation('history');
$criteria->addAssociation('salutation');
$criteria->addFilter(new EqualsFilter('id', $id));
$criteria->getAssociation('history')->addSorting(new FieldSorting('createdAt', FieldSorting::ASCENDING));
$criteria->getAssociation('history')->addAssociation('preStatus');
$criteria->getAssociation('history')->addAssociation('postStatus');
/** @var TicketEntity $ticket */
$ticket = $this->ticketRepository->search(
$criteria,
$salesChannelContext->getContext()
)->getEntities()->first();
// return it
return $ticket;
}
/**
* ...
*
* @param SalesChannelContext $salesChannelContext
*
* @return TicketEntity[]
*/
private function getCustomerTickets(SalesChannelContext $salesChannelContext): array
{
// find the order
$criteria = new Criteria();
$criteria->addAssociation('status');
$criteria->addAssociation('history');
$criteria->addAssociation('salutation');
$criteria->addFilter(new EqualsFilter('customerId', $salesChannelContext->getCustomer()->getId()));
$criteria->getAssociation('history')->addSorting(new FieldSorting('createdAt', FieldSorting::ASCENDING));
$criteria->getAssociation('history')->addAssociation('preStatus');
$criteria->getAssociation('history')->addAssociation('postStatus');
/** @var TicketEntity[] $tickets */
$tickets = $this->ticketRepository->search(
$criteria,
$salesChannelContext->getContext()
)->getElements();
// return it
return $tickets;
}
}