<?php
namespace App\Controller;
use App\Component\ViewLayer\Views\DocHtmlView;
use App\Component\ViewLayer\Views\AjaxHtmlView;
use App\Component\ViewLayer\Views\JsonView;
use App\Component\ViewLayer\Views\TwimlView;
use App\Contracts\Service\CustomServiceSubscriberTrait;
use App\Util\Permissions;
use App\Security\Core\Authorization\AuthorizationTester;
use Cms\CoreBundle\Model\Contexts\GlobalContext;
use Cms\CoreBundle\Service\ContextManager;
use Cms\CoreBundle\Service\LocaleManager;
use Cms\CoreBundle\Util\Doctrine\EntityManager;
use Cms\LogBundle\Service\LoggingService;
use Cms\TenantBundle\Entity\Tenant;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use SessionHandlerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController as SymfonyAbstractController;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Profiler\Profiler;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Twig\Environment;
use Twilio\TwiML\TwiML;
/**
* Class AbstractController
* @package App\Controller
*/
abstract class AbstractController extends SymfonyAbstractController
{
use CustomServiceSubscriberTrait {
CustomServiceSubscriberTrait::getSubscribedServices as resolveSubscribedServices;
}
/**
* When working with a request, will do the work necessary to process the incoming form data and validate the form.
* Returns whether the form is properly submitted and if it is valid.
*
* @param string|array<string> $attributes
* @return bool
*/
protected function maybeGranted($attributes): bool
{
return $this->container->get(AuthorizationTester::class)->maybeGranted($attributes);
}
/**
* @param string|array<string> $attributes
* @param string $message
* @return void
*/
protected function denyAccessUnlessMaybeGranted($attributes, string $message = 'Access Denied.'): void
{
if (!$this->maybeGranted($attributes)) {
$exception = $this->createAccessDeniedException($message);
$exception->setAttributes($attributes);
throw $exception;
}
}
/**
* @param FormInterface $form
* @param Request|null $request
* @return bool
*/
protected function handleForm(
FormInterface $form,
?Request $request = null
): bool {
$form->handleRequest($request ?? $this->getRequest());
return ($form->isSubmitted() && $form->isValid());
}
/**
* When working with raw form data (not in a request), will do the work of processing the form against that data.
*
* @param FormInterface $form
* @param array $data
* @return bool
*/
protected function submitForm(
FormInterface $form,
array $data = []
): bool {
$form->submit($data);
return ($form->isSubmitted() && $form->isValid());
}
/**
* Determines whether the request is an AJAX request based on headers.
*
* @param Request|null $request
* @return bool
*/
protected function isAjax(?Request $request = null): bool
{
return ($request ?? $this->getRequest())->isXmlHttpRequest();
}
/**
* @param Request|null $request
* @return void
*/
protected function enforceAjax(?Request $request = null): void
{
if (!$this->isAjax($request)) {
throw $this->createNotFoundException();
}
}
/**
* @param FormInterface $form
* @param Request|null $request
* @return bool
*/
protected function handleSearch(
FormInterface $form,
?Request $request = null
): bool {
if (!$request) {
$request = $this->getRequest();
}
$data = array_filter(
($form->getName())
? $request->query->get(
$form->getName(),
[]
)
: $request->query->all()
);
if ($data) {
$form->submit(
$data,
false
);
}
return ($form->isSubmitted() && $form->isValid());
}
/**
* @return LoggingService|object
*/
protected function getLoggingService(): LoggingService
{
return $this->get(__METHOD__);
}
/**
* @return LocaleManager|object
*/
protected function getLocaleManager(): LocaleManager
{
return $this->get(__METHOD__);
}
/**
* @return SessionInterface|object
*/
protected function getSession(): SessionInterface
{
return $this->get(__METHOD__);
}
/**
* @return Request
*/
public function getRequest(): Request
{
return $this->getRequestStack()->getCurrentRequest();
}
/**
* @return RequestStack|object
*/
protected function getRequestStack(): RequestStack
{
return $this->get(RequestStack::class);
}
/**
* @return EntityManager|object
*/
protected function getEntityManager(): EntityManager
{
// TODO: need to figure out how to get this into the doctrine registry...
return $this->get(EntityManager::class);
}
/**
* @param object|string $class
* @return EntityRepository
*/
protected function getRepository($class): EntityRepository
{
if (is_object($class)) {
$class = get_class($class);
}
return $this->getEntityManager()->getRepository($class);
}
/**
* @return string
*/
protected function getEnvironment(): string
{
return $this->getParameter('kernel.environment');
}
/**
* @return ContextManager|object
*/
protected function getContextManager(): ContextManager
{
return $this->get(ContextManager::class);
}
/**
* @return GlobalContext
*/
protected function getGlobalContext(): GlobalContext
{
return $this->getContextManager()->getGlobalContext();
}
/**
* @return Tenant|null
*/
protected function getTenant(): ?Tenant
{
return $this->getGlobalContext()->getTenant();
}
/**
* @return Environment
*/
protected function getTwig(): Environment
{
return $this->get(Environment::class);
}
/**
* @return FormFactoryInterface
*/
protected function getFormFactory(): FormFactoryInterface
{
return $this->get(FormFactoryInterface::class);
}
/**
* @return RouterInterface
*/
protected function getRouter(): RouterInterface
{
return $this->get(RouterInterface::class);
}
/**
* @return TranslatorInterface
*/
protected function getTranslator(): TranslatorInterface
{
return $this->get(TranslatorInterface::class);
}
/**
* @param int $status
* @return Response
*/
protected function resp(int $status = Response::HTTP_OK): Response
{
return new Response($status);
}
/**
* @param array $parameters
* @param string|null $template
* @return DocHtmlView
*/
protected function html(array $parameters = [], ?string $template = null): DocHtmlView
{
return new DocHtmlView($parameters, $template);
}
/**
* @param array $parameters
* @param string|null $template
* @return AjaxHtmlView
*/
protected function ajax(array $parameters = [], ?string $template = null): AjaxHtmlView
{
return new AjaxHtmlView($parameters, $template);
}
/**
* @param TwiML $twiml
* @return TwimlView
*/
protected function twiml(TwiML $twiml): TwimlView
{
return new TwimlView($twiml);
}
/**
* @param mixed $data
* @param int $status
* @param array $headers
* @return JsonView
*/
protected function jsonView($data, int $status = 200, array $headers = []): JsonView
{
$view = new JsonView($data);
$view->getResponse()->setStatusCode($status);
if ($headers) {
$view->getResponse()->headers->replace($headers);
}
return $view;
}
/**
* @param string $name
* @param mixed $data
* @param array $options
* @return FormBuilderInterface
*/
public function createNamedBuilder(string $name, $data = null, array $options = []): FormBuilderInterface
{
return $this->getFormFactory()->createNamedBuilder($name ?? '', FormType::class, $data, $options);
}
/**
* @param string|null $name
* @param mixed $type
* @param mixed $data
* @param array $options
* @return FormInterface
*/
public function createNamed(?string $name, $type, $data = null, array $options = []): FormInterface
{
return $this->getFormFactory()->createNamed($name, $type, $data, $options);
}
/**
* @param RedirectResponse $redirect
* @param Request|null $request
* @return RedirectResponse
*/
public function jumpOrRedirect(RedirectResponse $redirect, ?Request $request = null): RedirectResponse
{
if ( ! $request) {
$request = $this->getRequest();
}
if ($request->query->has('jump')) {
return new RedirectResponse($request->query->get('jump'));
}
return $redirect;
}
/**
* @param string $route
* @param array $parameters
* @param int $status
* @return RedirectResponse
*/
public function jumpOrRedirectToRoute(string $route, array $parameters = [], int $status = Response::HTTP_FOUND): RedirectResponse
{
return $this->jumpOrRedirect($this->redirectToRoute(
$route,
$parameters,
$status
));
}
/**
* @param string $route
* @param array $parameters
* @return string
*/
public function generateAbsoluteUrl(string $route, array $parameters = []): string
{
return $this->generateUrl($route, $parameters, UrlGeneratorInterface::ABSOLUTE_URL);
}
/**
* @param string $route
* @param array $parameters
* @return Request
*/
public function subrequest(string $route, array $parameters = []): Request
{
$request = Request::create($this->generateAbsoluteUrl($route, $parameters));
$params = $this->getRouter()->matchRequest($request);
$request->attributes->add($params);
unset($params['_route'], $params['_controller']);
$request->attributes->set('_route_params', $params);
return $request;
}
/**
* @return void
*/
protected function disableProfiling(): void
{
if ($this->has('profiler')) {
$this->get('profiler')->disable();
}
}
/**
* @return void
*/
protected function closeSession(): void
{
// speed some things up by closing the session
// symfony will block concurrency when a session is opened
// public-facing pages should not need to use a session
// so, closing it when needed should speed up the system a bit in certain places
$this->get('session.handler')->close();
}
/**
* @param string $name
* @return string
*/
protected function locateResource(string $name): string
{
return $this->get('kernel')->locateResource($name);
}
/**
* {@inheritDoc}
*/
protected function isGranted($attribute, $subject = null): bool
{
return parent::isGranted(
Permissions::attributesExpression($attribute),
$subject,
);
}
/**
* {@inheritDoc}
*/
public static function getSubscribedServices(): array
{
return array_merge(
parent::getSubscribedServices(),
[
'kernel' => '?kernel',
'profiler' => '?profiler',
'translator' => '?'.TranslatorInterface::class,
'session.handler' => '?'.SessionHandlerInterface::class,
],
// TODO: doing this to alias the core stuff being brought in, probably not needed in later versions
[
RouterInterface::class => '?'.RouterInterface::class,
RequestStack::class => '?'.RequestStack::class,
HttpKernelInterface::class => '?'.HttpKernelInterface::class,
SerializerInterface::class => '?'.SerializerInterface::class,
SessionInterface::class => '?'.SessionInterface::class,
AuthorizationCheckerInterface::class => '?'.AuthorizationCheckerInterface::class,
Environment::class => '?'.Environment::class,
ManagerRegistry::class => '?'.ManagerRegistry::class,
FormFactoryInterface::class => '?'.FormFactoryInterface::class,
TokenStorageInterface::class => '?'.TokenStorageInterface::class,
CsrfTokenManagerInterface::class => '?'.CsrfTokenManagerInterface::class,
ParameterBagInterface::class => '?'.ParameterBagInterface::class,
TranslatorInterface::class => '?'.TranslatorInterface::class,
],
// injecting things we use commonly
[
EntityManager::class => EntityManager::class,
ContextManager::class => ContextManager::class,
LoggingService::class => LoggingService::class,
AuthorizationTester::class => AuthorizationTester::class,
],
// inject things found in the controllers methods
self::resolveSubscribedServices(),
);
}
}