Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,458 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ApiBundle\Controller;
|
||||
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\ApiBundle\Model\ClientModel;
|
||||
use Mautic\CoreBundle\Controller\AbstractStandardFormController;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Factory\PageHelperFactoryInterface;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Service\FlashBag;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\FormBundle\Helper\FormFieldHelper;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use OAuth2\OAuth2;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormFactoryInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
|
||||
class ClientController extends AbstractStandardFormController
|
||||
{
|
||||
public function __construct(
|
||||
private ClientModel $clientModel,
|
||||
FormFactoryInterface $formFactory,
|
||||
FormFieldHelper $fieldHelper,
|
||||
ManagerRegistry $doctrine,
|
||||
ModelFactory $modelFactory,
|
||||
UserHelper $userHelper,
|
||||
CoreParametersHelper $coreParametersHelper,
|
||||
EventDispatcherInterface $dispatcher,
|
||||
Translator $translator,
|
||||
FlashBag $flashBag,
|
||||
RequestStack $requestStack,
|
||||
CorePermissions $security,
|
||||
) {
|
||||
parent::__construct($formFactory, $fieldHelper, $doctrine, $modelFactory, $userHelper, $coreParametersHelper, $dispatcher, $translator, $flashBag, $requestStack, $security);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate's default client list.
|
||||
*
|
||||
* @param int $page
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function indexAction(Request $request, PageHelperFactoryInterface $pageHelperFactory, $page = 1)
|
||||
{
|
||||
if (!$this->security->isGranted('api:clients:view')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$this->setListFilters();
|
||||
|
||||
$pageHelper= $pageHelperFactory->make('mautic.api.client', $page);
|
||||
$limit = $pageHelper->getLimit();
|
||||
$start = $pageHelper->getStart();
|
||||
$orderBy = $request->getSession()->get('mautic.api.client.orderby', 'c.name');
|
||||
$orderByDir= $request->getSession()->get('mautic.api.client.orderbydir', 'ASC');
|
||||
$filter = $request->get('search', $request->getSession()->get('mautic.api.client.filter', ''));
|
||||
$apiMode = $request->get('api_mode', $request->getSession()->get('mautic.api.client.filter.api_mode', 'oauth2'));
|
||||
$request->getSession()->set('mautic.api.client.filter.api_mode', $apiMode);
|
||||
$request->getSession()->set('mautic.api.client.filter', $filter);
|
||||
|
||||
$clients = $this->clientModel->getEntities(
|
||||
[
|
||||
'start' => $start,
|
||||
'limit' => $limit,
|
||||
'filter' => $filter,
|
||||
'orderBy' => $orderBy,
|
||||
'orderByDir' => $orderByDir,
|
||||
]
|
||||
);
|
||||
|
||||
$count = count($clients);
|
||||
if ($count && $count < ($start + 1)) {
|
||||
$lastPage = $pageHelper->countPage($count);
|
||||
$returnUrl = $this->generateUrl('mautic_client_index', ['page' => $lastPage]);
|
||||
$pageHelper->rememberPage($lastPage);
|
||||
|
||||
return $this->postActionRedirect(
|
||||
[
|
||||
'returnUrl' => $returnUrl,
|
||||
'viewParameters' => ['page' => $lastPage],
|
||||
'contentTemplate' => 'Mautic\ApiBundle\Controller\ClientController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => 'mautic_client_index',
|
||||
'mauticContent' => 'client',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$pageHelper->rememberPage($page);
|
||||
|
||||
// filters
|
||||
$filters = [];
|
||||
|
||||
// api options
|
||||
$apiOptions = [];
|
||||
$apiOptions['oauth2'] = 'OAuth 2';
|
||||
$filters['api_mode'] = [
|
||||
'values' => [$apiMode],
|
||||
'options' => $apiOptions,
|
||||
];
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'items' => $clients,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'permissions' => [
|
||||
'create' => $this->security->isGranted('api:clients:create'),
|
||||
'edit' => $this->security->isGranted('api:clients:editother'),
|
||||
'delete' => $this->security->isGranted('api:clients:deleteother'),
|
||||
],
|
||||
'tmpl' => $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index',
|
||||
'searchValue' => $filter,
|
||||
'filters' => $filters,
|
||||
],
|
||||
'contentTemplate' => '@MauticApi/Client/list.html.twig',
|
||||
'passthroughVars' => [
|
||||
'route' => $this->generateUrl('mautic_client_index', ['page' => $page]),
|
||||
'mauticContent' => 'client',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function authorizedClientsAction(TokenStorageInterface $tokenStorage): Response
|
||||
{
|
||||
$me = $tokenStorage->getToken()->getUser();
|
||||
\assert($me instanceof User);
|
||||
$clients = $this->clientModel->getUserClients($me);
|
||||
|
||||
return $this->render('@MauticApi/Client/authorized.html.twig', ['clients' => $clients]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $clientId
|
||||
*/
|
||||
public function revokeAction(Request $request, $clientId): Response
|
||||
{
|
||||
$success = 0;
|
||||
$flashes = [];
|
||||
|
||||
if ('POST' == $request->getMethod()) {
|
||||
$client = $this->clientModel->getEntity($clientId);
|
||||
|
||||
if (null === $client) {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.api.client.error.notfound',
|
||||
'msgVars' => ['%id%' => $clientId],
|
||||
];
|
||||
} else {
|
||||
$name = $client->getName();
|
||||
|
||||
$this->clientModel->revokeAccess($client);
|
||||
|
||||
$flashes[] = [
|
||||
'type' => 'notice',
|
||||
'msg' => 'mautic.api.client.notice.revoked',
|
||||
'msgVars' => [
|
||||
'%name%' => $name,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->postActionRedirect(
|
||||
[
|
||||
'returnUrl' => $this->generateUrl('mautic_user_account'),
|
||||
'contentTemplate' => 'Mautic\UserBundle\Controller\ProfileController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'success' => $success,
|
||||
],
|
||||
'flashes' => $flashes,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $objectId
|
||||
*
|
||||
* @return array|JsonResponse|RedirectResponse|Response
|
||||
*/
|
||||
public function newAction(Request $request, $objectId = 0)
|
||||
{
|
||||
if (!$this->security->isGranted('api:clients:create')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$apiMode = (0 === $objectId) ? $request->getSession()->get('mautic.client.filter.api_mode', 'oauth2') : $objectId;
|
||||
$request->getSession()->set('mautic.client.filter.api_mode', $apiMode);
|
||||
|
||||
$this->clientModel->setApiMode($apiMode);
|
||||
|
||||
// retrieve the entity
|
||||
$client = $this->clientModel->getEntity();
|
||||
|
||||
// set the return URL for post actions
|
||||
$returnUrl = $this->generateUrl('mautic_client_index');
|
||||
|
||||
// get the user form factory
|
||||
$action = $this->generateUrl('mautic_client_action', ['objectAction' => 'new']);
|
||||
$form = $this->clientModel->createForm($client, $this->formFactory, $action);
|
||||
|
||||
// remove the client id and secret fields as they'll be auto generated
|
||||
$form->remove('randomId');
|
||||
$form->remove('secret');
|
||||
$form->remove('publicId');
|
||||
$form->remove('consumerKey');
|
||||
$form->remove('consumerSecret');
|
||||
|
||||
// /Check for a submitted form and process it
|
||||
if ('POST' == $request->getMethod()) {
|
||||
$valid = false;
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
if ($valid = $this->isFormValid($form)) {
|
||||
// form is valid so process the data
|
||||
// If the admin is creating API credentials, enable 'Client Credential' grant type
|
||||
/** @var User $user */
|
||||
$user = $this->getUser();
|
||||
if (ClientModel::API_MODE_OAUTH2 == $apiMode && $user->getRole()->isAdmin()) {
|
||||
$client->addGrantType(OAuth2::GRANT_TYPE_CLIENT_CREDENTIALS);
|
||||
}
|
||||
$client->setRole($user->getRole());
|
||||
$this->clientModel->saveEntity($client);
|
||||
$this->addFlashMessage(
|
||||
'mautic.api.client.notice.created',
|
||||
[
|
||||
'%name%' => $client->getName(),
|
||||
'%clientId%' => $client->getPublicId(),
|
||||
'%clientSecret%' => $client->getSecret(),
|
||||
'%url%' => $this->generateUrl(
|
||||
'mautic_client_action',
|
||||
[
|
||||
'objectAction' => 'edit',
|
||||
'objectId' => $client->getId(),
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($cancelled || ($valid && $this->getFormButton($form, ['buttons', 'save'])->isClicked())) {
|
||||
return $this->postActionRedirect(
|
||||
[
|
||||
'returnUrl' => $returnUrl,
|
||||
'contentTemplate' => 'Mautic\ApiBundle\Controller\ClientController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_client_index',
|
||||
'mauticContent' => 'client',
|
||||
],
|
||||
]
|
||||
);
|
||||
} elseif ($valid && !$cancelled) {
|
||||
return $this->editAction($request, $client->getId(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
'tmpl' => $request->get('tmpl', 'form'),
|
||||
],
|
||||
'contentTemplate' => '@MauticApi/Client/form.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_client_new',
|
||||
'route' => $action,
|
||||
'mauticContent' => 'client',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates edit form and processes post data.
|
||||
*
|
||||
* @param int $objectId
|
||||
* @param bool $ignorePost
|
||||
*
|
||||
* @return JsonResponse|RedirectResponse|Response
|
||||
*/
|
||||
public function editAction(Request $request, $objectId, $ignorePost = false)
|
||||
{
|
||||
if (!$this->security->isGranted('api:clients:editother')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$client = $this->clientModel->getEntity($objectId);
|
||||
$returnUrl = $this->generateUrl('mautic_client_index');
|
||||
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'contentTemplate' => 'Mautic\ApiBundle\Controller\ClientController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_client_index',
|
||||
'mauticContent' => 'client',
|
||||
],
|
||||
];
|
||||
|
||||
// client not found
|
||||
if (null === $client) {
|
||||
return $this->postActionRedirect(
|
||||
array_merge(
|
||||
$postActionVars,
|
||||
[
|
||||
'flashes' => [
|
||||
[
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.api.client.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
],
|
||||
],
|
||||
]
|
||||
)
|
||||
);
|
||||
} elseif ($this->clientModel->isLocked($client)) {
|
||||
// deny access if the entity is locked
|
||||
return $this->isLocked($postActionVars, $client, 'api.client');
|
||||
}
|
||||
|
||||
$action = $this->generateUrl('mautic_client_action', ['objectAction' => 'edit', 'objectId' => $objectId]);
|
||||
$form = $this->clientModel->createForm($client, $this->formFactory, $action);
|
||||
|
||||
// remove api_mode field
|
||||
$form->remove('api_mode');
|
||||
|
||||
// /Check for a submitted form and process it
|
||||
if (!$ignorePost && 'POST' == $request->getMethod()) {
|
||||
if (!$cancelled = $this->isFormCancelled($form)) {
|
||||
if ($valid = $this->isFormValid($form)) {
|
||||
// form is valid so process the data
|
||||
$this->clientModel->saveEntity($client, $this->getFormButton($form, ['buttons', 'save'])->isClicked());
|
||||
$this->addFlashMessage(
|
||||
'mautic.core.notice.updated',
|
||||
[
|
||||
'%name%' => $client->getName(),
|
||||
'%menu_link%' => 'mautic_client_index',
|
||||
'%url%' => $this->generateUrl(
|
||||
'mautic_client_action',
|
||||
[
|
||||
'objectAction' => 'edit',
|
||||
'objectId' => $client->getId(),
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
if ($this->getFormButton($form, ['buttons', 'save'])->isClicked()) {
|
||||
return $this->postActionRedirect($postActionVars);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// unlock the entity
|
||||
$this->clientModel->unlockEntity($client);
|
||||
|
||||
return $this->postActionRedirect($postActionVars);
|
||||
}
|
||||
} else {
|
||||
// lock the entity
|
||||
$this->clientModel->lockEntity($client);
|
||||
}
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
'tmpl' => $request->get('tmpl', 'form'),
|
||||
],
|
||||
'contentTemplate' => '@MauticApi/Client/form.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_client_index',
|
||||
'route' => $action,
|
||||
'mauticContent' => 'client',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the entity.
|
||||
*
|
||||
* @param int $objectId
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function deleteAction(Request $request, $objectId)
|
||||
{
|
||||
if (!$this->security->isGranted('api:clients:delete')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$returnUrl = $this->generateUrl('mautic_client_index');
|
||||
$success = 0;
|
||||
$flashes = [];
|
||||
|
||||
$postActionVars = [
|
||||
'returnUrl' => $returnUrl,
|
||||
'contentTemplate' => 'Mautic\ApiBundle\Controller\ClientController::indexAction',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_client_index',
|
||||
'success' => $success,
|
||||
'mauticContent' => 'client',
|
||||
],
|
||||
];
|
||||
|
||||
if ('POST' === $request->getMethod()) {
|
||||
$entity = $this->clientModel->getEntity($objectId);
|
||||
if (null === $entity) {
|
||||
$flashes[] = [
|
||||
'type' => 'error',
|
||||
'msg' => 'mautic.api.client.error.notfound',
|
||||
'msgVars' => ['%id%' => $objectId],
|
||||
];
|
||||
} elseif ($this->clientModel->isLocked($entity)) {
|
||||
// deny access if the entity is locked
|
||||
return $this->isLocked($postActionVars, $entity, 'api.client');
|
||||
} else {
|
||||
$this->clientModel->deleteEntity($entity);
|
||||
$name = $entity->getName();
|
||||
$flashes[] = [
|
||||
'type' => 'notice',
|
||||
'msg' => 'mautic.core.notice.deleted',
|
||||
'msgVars' => [
|
||||
'%name%' => $name,
|
||||
'%id%' => $objectId,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->postActionRedirect(
|
||||
array_merge(
|
||||
$postActionVars,
|
||||
[
|
||||
'flashes' => $flashes,
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function getModelName(): string
|
||||
{
|
||||
return 'api.client';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ApiBundle\Controller;
|
||||
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\ApiBundle\ApiEvents;
|
||||
use Mautic\ApiBundle\Event\ApiEntityEvent;
|
||||
use Mautic\ApiBundle\Helper\EntityResultHelper;
|
||||
use Mautic\CategoryBundle\Entity\Category;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Helper\AppVersion;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\InputHelper;
|
||||
use Mautic\CoreBundle\Model\FormModel;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
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\Routing\RouterInterface;
|
||||
|
||||
/**
|
||||
* @template E of object
|
||||
*
|
||||
* @extends FetchCommonApiController<E>
|
||||
*/
|
||||
class CommonApiController extends FetchCommonApiController
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $dataInputMasks = [];
|
||||
|
||||
/**
|
||||
* Model object for processing the entity.
|
||||
*
|
||||
* @var FormModel<E>|null
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $routeParams = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $entityRequestParameters = [];
|
||||
|
||||
public function __construct(
|
||||
CorePermissions $security,
|
||||
Translator $translator,
|
||||
EntityResultHelper $entityResultHelper,
|
||||
protected RouterInterface $router,
|
||||
protected FormFactoryInterface $formFactory,
|
||||
AppVersion $appVersion,
|
||||
RequestStack $requestStack,
|
||||
ManagerRegistry $doctrine,
|
||||
ModelFactory $modelFactory,
|
||||
EventDispatcherInterface $dispatcher,
|
||||
CoreParametersHelper $coreParametersHelper,
|
||||
) {
|
||||
parent::__construct($security, $translator, $entityResultHelper, $appVersion, $requestStack, $doctrine, $modelFactory, $dispatcher, $coreParametersHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a batch of entities.
|
||||
*
|
||||
* @return array|Response
|
||||
*/
|
||||
public function deleteEntitiesAction(Request $request)
|
||||
{
|
||||
$parameters = $request->query->all();
|
||||
|
||||
$valid = $this->validateBatchPayload($parameters);
|
||||
if ($valid instanceof Response) {
|
||||
return $valid;
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
$entities = $this->getBatchEntities($parameters, $errors, true);
|
||||
$this->inBatchMode = true;
|
||||
|
||||
// Generate the view before deleting so that the IDs are still populated before Doctrine removes them
|
||||
$payload = [$this->entityNameMulti => $entities];
|
||||
$view = $this->view($payload, Response::HTTP_OK);
|
||||
$this->setSerializationContext($view);
|
||||
$response = $this->handleView($view);
|
||||
|
||||
foreach ($entities as $key => $entity) {
|
||||
if (null === $entity || !$entity->getId()) {
|
||||
$this->setBatchError($key, 'mautic.core.error.notfound', Response::HTTP_NOT_FOUND, $errors, $entities, $entity);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->checkEntityAccess($entity, 'delete')) {
|
||||
$this->setBatchError($key, 'mautic.core.error.accessdenied', Response::HTTP_FORBIDDEN, $errors, $entities, $entity);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->model->deleteEntity($entity);
|
||||
$this->doctrine->getManager()->detach($entity);
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
$content = json_decode($response->getContent(), true);
|
||||
$content['errors'] = $errors;
|
||||
$response->setContent(json_encode($content));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an entity.
|
||||
*
|
||||
* @param int $id Entity ID
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function deleteEntityAction($id)
|
||||
{
|
||||
$entity = $this->model->getEntity($id);
|
||||
if (null !== $entity) {
|
||||
if (!$this->checkEntityAccess($entity, 'delete')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$this->model->deleteEntity($entity);
|
||||
|
||||
$this->preSerializeEntity($entity);
|
||||
$view = $this->view([$this->entityNameOne => $entity], Response::HTTP_OK);
|
||||
$this->setSerializationContext($view);
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
|
||||
return $this->notFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a batch of entities.
|
||||
*
|
||||
* @return array|Response
|
||||
*/
|
||||
public function editEntitiesAction(Request $request)
|
||||
{
|
||||
$parameters = $request->request->all();
|
||||
|
||||
$valid = $this->validateBatchPayload($parameters);
|
||||
if ($valid instanceof Response) {
|
||||
return $valid;
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
$statusCodes = [];
|
||||
$entities = $this->getBatchEntities($parameters, $errors);
|
||||
|
||||
foreach ($parameters as $key => $params) {
|
||||
$method = $request->getMethod();
|
||||
$entity = $entities[$key] ?? null;
|
||||
|
||||
$statusCode = Response::HTTP_OK;
|
||||
if (null === $entity || !$entity->getId()) {
|
||||
if ('PATCH' === $method) {
|
||||
// PATCH requires that an entity exists
|
||||
$this->setBatchError($key, 'mautic.core.error.notfound', Response::HTTP_NOT_FOUND, $errors, $entities, $entity);
|
||||
$statusCodes[$key] = Response::HTTP_NOT_FOUND;
|
||||
continue;
|
||||
}
|
||||
|
||||
// PUT can create a new entity if it doesn't exist
|
||||
$entity = $this->model->getEntity();
|
||||
if (!$this->checkEntityAccess($entity, 'create')) {
|
||||
$this->setBatchError($key, 'mautic.core.error.accessdenied', Response::HTTP_FORBIDDEN, $errors, $entities, $entity);
|
||||
$statusCodes[$key] = Response::HTTP_FORBIDDEN;
|
||||
continue;
|
||||
}
|
||||
|
||||
$statusCode = Response::HTTP_CREATED;
|
||||
}
|
||||
|
||||
if (!$this->checkEntityAccess($entity, 'edit')) {
|
||||
$this->setBatchError($key, 'mautic.core.error.accessdenied', Response::HTTP_FORBIDDEN, $errors, $entities, $entity);
|
||||
$statusCodes[$key] = Response::HTTP_FORBIDDEN;
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->processBatchForm($request, $key, $entity, $params, $method, $errors, $entities);
|
||||
|
||||
if (isset($errors[$key])) {
|
||||
$statusCodes[$key] = $errors[$key]['code'];
|
||||
} else {
|
||||
$statusCodes[$key] = $statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
$payload = [
|
||||
$this->entityNameMulti => $entities,
|
||||
'statusCodes' => $statusCodes,
|
||||
];
|
||||
|
||||
if (!empty($errors)) {
|
||||
$payload['errors'] = $errors;
|
||||
}
|
||||
|
||||
$view = $this->view($payload, Response::HTTP_OK);
|
||||
$this->setSerializationContext($view);
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits an existing entity or creates one on PUT if it doesn't exist.
|
||||
*
|
||||
* @param int $id Entity ID
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function editEntityAction(Request $request, $id)
|
||||
{
|
||||
$entity = $this->model->getEntity($id);
|
||||
$parameters = $request->request->all();
|
||||
$method = $request->getMethod();
|
||||
|
||||
if (null === $entity || !$entity->getId()) {
|
||||
if ('PATCH' === $method) {
|
||||
// PATCH requires that an entity exists
|
||||
return $this->notFound();
|
||||
}
|
||||
|
||||
// PUT can create a new entity if it doesn't exist
|
||||
$entity = $this->model->getEntity();
|
||||
if (!$this->checkEntityAccess($entity, 'create')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->checkEntityAccess($entity, 'edit')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
return $this->processForm($request, $entity, $parameters, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a batch of new entities.
|
||||
*
|
||||
* @return array|Response
|
||||
*/
|
||||
public function newEntitiesAction(Request $request)
|
||||
{
|
||||
$entity = $this->model->getEntity();
|
||||
|
||||
if (!$this->checkEntityAccess($entity, 'create')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$parameters = $request->request->all();
|
||||
|
||||
$valid = $this->validateBatchPayload($parameters);
|
||||
if ($valid instanceof Response) {
|
||||
return $valid;
|
||||
}
|
||||
|
||||
$this->inBatchMode = true;
|
||||
$entities = [];
|
||||
$errors = [];
|
||||
$statusCodes = [];
|
||||
foreach ($parameters as $key => $params) {
|
||||
// Can be new or an existing on based on params
|
||||
$entity = $this->getNewEntity($params);
|
||||
$entityExists = false;
|
||||
$method = 'POST';
|
||||
if ($entity->getId()) {
|
||||
$entityExists = true;
|
||||
$method = 'PATCH';
|
||||
if (!$this->checkEntityAccess($entity, 'edit')) {
|
||||
$this->setBatchError($key, 'mautic.core.error.accessdenied', Response::HTTP_FORBIDDEN, $errors, $entities, $entity);
|
||||
$statusCodes[$key] = Response::HTTP_FORBIDDEN;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$this->processBatchForm($request, $key, $entity, $params, $method, $errors, $entities);
|
||||
|
||||
if (isset($errors[$key])) {
|
||||
$statusCodes[$key] = $errors[$key]['code'];
|
||||
} elseif ($entityExists) {
|
||||
$statusCodes[$key] = Response::HTTP_OK;
|
||||
} else {
|
||||
$statusCodes[$key] = Response::HTTP_CREATED;
|
||||
}
|
||||
}
|
||||
|
||||
$payload = [
|
||||
$this->entityNameMulti => $entities,
|
||||
'statusCodes' => $statusCodes,
|
||||
];
|
||||
|
||||
if (!empty($errors)) {
|
||||
$payload['errors'] = $errors;
|
||||
}
|
||||
|
||||
$view = $this->view($payload, Response::HTTP_CREATED);
|
||||
$this->setSerializationContext($view);
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entity.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function newEntityAction(Request $request)
|
||||
{
|
||||
$parameters = $request->request->all();
|
||||
$entity = $this->getNewEntity($parameters);
|
||||
|
||||
if (!$this->checkEntityAccess($entity, 'create')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
return $this->processForm($request, $entity, $parameters, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FormInterface<mixed>
|
||||
*/
|
||||
protected function createEntityForm($entity): FormInterface
|
||||
{
|
||||
return $this->model->createForm(
|
||||
$entity,
|
||||
$this->formFactory,
|
||||
null,
|
||||
array_merge(
|
||||
[
|
||||
'csrf_protection' => false,
|
||||
'allow_extra_fields' => true,
|
||||
],
|
||||
$this->getEntityFormOptions()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives child controllers opportunity to analyze and do whatever to an entity before populating the form.
|
||||
*
|
||||
* @param string $action
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function prePopulateForm(&$entity, $parameters, $action = 'edit')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Give the controller an opportunity to process the entity before persisting.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function preSaveEntity(&$entity, $form, $parameters, $action = 'edit')
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert posted parameters into what the form needs in order to successfully bind.
|
||||
*
|
||||
* @param mixed[] $parameters
|
||||
* @param object $entity
|
||||
* @param string $action
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function prepareParametersForBinding(Request $request, $parameters, $entity, $action)
|
||||
{
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
protected function processBatchForm(Request $request, $key, $entity, $params, $method, &$errors, &$entities)
|
||||
{
|
||||
$this->inBatchMode = true;
|
||||
$formResponse = $this->processForm($request, $entity, $params, $method);
|
||||
if ($formResponse instanceof Response) {
|
||||
if (!$formResponse instanceof RedirectResponse) {
|
||||
// Assume an error
|
||||
$this->setBatchError(
|
||||
$key,
|
||||
InputHelper::string($formResponse->getContent()),
|
||||
$formResponse->getStatusCode(),
|
||||
$errors,
|
||||
$entities,
|
||||
$entity
|
||||
);
|
||||
}
|
||||
} elseif (is_object($formResponse) && $formResponse::class === $entity::class) {
|
||||
// Success
|
||||
$entities[$key] = $formResponse;
|
||||
} elseif (is_array($formResponse) && isset($formResponse['code'], $formResponse['message'])) {
|
||||
// There was an error
|
||||
$errors[$key] = $formResponse;
|
||||
}
|
||||
|
||||
$lastEntityIndex = -1;
|
||||
foreach ($entities as $index => $moreEntities) {
|
||||
if ($moreEntities !== $entity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lastEntityIndex = $index;
|
||||
}
|
||||
|
||||
if (-1 === $lastEntityIndex || $lastEntityIndex === $key) {
|
||||
$this->detachEntity($entity);
|
||||
}
|
||||
|
||||
$this->inBatchMode = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes API Form.
|
||||
*
|
||||
* @param array<mixed>|null $parameters
|
||||
* @param string $method
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function processForm(Request $request, $entity, $parameters = null, $method = 'PUT')
|
||||
{
|
||||
$categoryId = null;
|
||||
|
||||
if (null === $parameters) {
|
||||
// get from request
|
||||
$parameters = $request->request->all();
|
||||
}
|
||||
|
||||
// Store the original parameters from the request so that callbacks can have access to them as needed
|
||||
$this->entityRequestParameters = $parameters;
|
||||
|
||||
// unset the ID in the parameters if set as this will cause the form to fail
|
||||
if (isset($parameters['id'])) {
|
||||
unset($parameters['id']);
|
||||
}
|
||||
|
||||
// is an entity being updated or created?
|
||||
if ($entity->getId()) {
|
||||
$statusCode = Response::HTTP_OK;
|
||||
$action = 'edit';
|
||||
} else {
|
||||
$statusCode = Response::HTTP_CREATED;
|
||||
$action = 'new';
|
||||
|
||||
// All the properties have to be defined in order for validation to work
|
||||
// Bug reported https://github.com/symfony/symfony/issues/19788
|
||||
$defaultProperties = $this->getEntityDefaultProperties($entity);
|
||||
$parameters = array_merge($defaultProperties, $parameters);
|
||||
}
|
||||
|
||||
// Check if user has access to publish
|
||||
if (
|
||||
(
|
||||
array_key_exists('isPublished', $parameters)
|
||||
|| array_key_exists('publishUp', $parameters)
|
||||
|| array_key_exists('publishDown', $parameters)
|
||||
)
|
||||
&& $this->security->checkPermissionExists($this->permissionBase.':publish')) {
|
||||
if ($this->security->checkPermissionExists($this->permissionBase.':publishown')) {
|
||||
if (!$this->checkEntityAccess($entity, 'publish')) {
|
||||
if ('new' === $action) {
|
||||
$parameters['isPublished'] = 0;
|
||||
unset($parameters['publishUp'], $parameters['publishDown']);
|
||||
} else {
|
||||
unset($parameters['isPublished'], $parameters['publishUp'], $parameters['publishDown']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$form = $this->createEntityForm($entity);
|
||||
$submitParams = $this->prepareParametersForBinding($request, $parameters, $entity, $action);
|
||||
|
||||
if ($submitParams instanceof Response) {
|
||||
return $submitParams;
|
||||
}
|
||||
|
||||
// Remove category from the payload because it will cause form validation error.
|
||||
if (isset($submitParams['category'])) {
|
||||
$categoryId = (int) $submitParams['category'];
|
||||
unset($submitParams['category']);
|
||||
}
|
||||
|
||||
$this->prepareParametersFromRequest($form, $submitParams, $entity, $this->dataInputMasks);
|
||||
|
||||
$form->submit($submitParams, 'PATCH' !== $method);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->setCategory($entity, $categoryId);
|
||||
$preSaveError = $this->preSaveEntity($entity, $form, $submitParams, $action);
|
||||
|
||||
if ($preSaveError instanceof Response) {
|
||||
return $preSaveError;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->dispatcher->hasListeners(ApiEvents::API_ON_ENTITY_PRE_SAVE)) {
|
||||
$this->dispatcher->dispatch(new ApiEntityEvent($entity, $this->entityRequestParameters, $request), ApiEvents::API_ON_ENTITY_PRE_SAVE);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return $this->returnError($e->getMessage(), $e->getCode());
|
||||
}
|
||||
|
||||
$statusCode = $this->saveEntity($entity, $statusCode);
|
||||
|
||||
$headers = [];
|
||||
// return the newly created entities location if applicable
|
||||
if (in_array($statusCode, [Response::HTTP_CREATED, Response::HTTP_ACCEPTED])) {
|
||||
$route = (null !== $this->router->getRouteCollection()->get('mautic_api_'.$this->entityNameMulti.'_getone'))
|
||||
? 'mautic_api_'.$this->entityNameMulti.'_getone' : 'mautic_api_get'.$this->entityNameOne;
|
||||
$headers['Location'] = $this->generateUrl(
|
||||
$route,
|
||||
array_merge(['id' => $entity->getId()], $this->routeParams),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if ($this->dispatcher->hasListeners(ApiEvents::API_ON_ENTITY_POST_SAVE)) {
|
||||
$this->dispatcher->dispatch(new ApiEntityEvent($entity, $this->entityRequestParameters, $request), ApiEvents::API_ON_ENTITY_POST_SAVE);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return $this->returnError($e->getMessage(), $e->getCode());
|
||||
}
|
||||
|
||||
$this->preSerializeEntity($entity, $action);
|
||||
|
||||
if ($this->inBatchMode) {
|
||||
return $entity;
|
||||
} else {
|
||||
$view = $this->view([$this->entityNameOne => $entity], $statusCode, $headers);
|
||||
}
|
||||
|
||||
$this->setSerializationContext($view);
|
||||
} else {
|
||||
$formErrors = $this->getFormErrorMessages($form);
|
||||
$formErrorCodes = $this->getFormErrorCodes($form);
|
||||
$msg = $this->getFormErrorMessage($formErrors);
|
||||
|
||||
if (!$msg) {
|
||||
$msg = $this->translator->trans('mautic.core.error.badrequest', [], 'flashes');
|
||||
}
|
||||
|
||||
$responseCode = in_array(Response::HTTP_UNPROCESSABLE_ENTITY, $formErrorCodes) ? Response::HTTP_UNPROCESSABLE_ENTITY : Response::HTTP_BAD_REQUEST;
|
||||
|
||||
return $this->returnError($msg, $responseCode, $formErrors);
|
||||
}
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
|
||||
protected function saveEntity($entity, int $statusCode): int
|
||||
{
|
||||
$this->model->saveEntity($entity);
|
||||
|
||||
return $statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $entity
|
||||
* @param int $categoryId
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function setCategory($entity, $categoryId)
|
||||
{
|
||||
if (!empty($categoryId) && method_exists($entity, 'setCategory')) {
|
||||
$category = $this->doctrine->getManager()->find(Category::class, $categoryId);
|
||||
|
||||
if (null === $category) {
|
||||
throw new \UnexpectedValueException("Category $categoryId does not exist");
|
||||
}
|
||||
|
||||
$entity->setCategory($category);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity not to be detached in case of Lead Batch API.
|
||||
*/
|
||||
protected function detachEntity(object $entity): void
|
||||
{
|
||||
$this->doctrine->getManager()->detach($entity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,773 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ApiBundle\Controller;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Tools\Pagination\Paginator;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use FOS\RestBundle\Controller\AbstractFOSRestController;
|
||||
use FOS\RestBundle\View\View;
|
||||
use JMS\Serializer\Exclusion\ExclusionStrategyInterface;
|
||||
use Mautic\ApiBundle\ApiEvents;
|
||||
use Mautic\ApiBundle\Event\ApiInitializeEvent;
|
||||
use Mautic\ApiBundle\Event\ApiSerializationContextEvent;
|
||||
use Mautic\ApiBundle\Helper\BatchIdToEntityHelper;
|
||||
use Mautic\ApiBundle\Helper\EntityResultHelper;
|
||||
use Mautic\ApiBundle\Serializer\Exclusion\ParentChildrenExclusionStrategy;
|
||||
use Mautic\ApiBundle\Serializer\Exclusion\PublishDetailsExclusionStrategy;
|
||||
use Mautic\CoreBundle\Controller\FormErrorMessagesTrait;
|
||||
use Mautic\CoreBundle\Controller\MauticController;
|
||||
use Mautic\CoreBundle\Entity\FormEntity;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Form\RequestTrait;
|
||||
use Mautic\CoreBundle\Helper\AppVersion;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\InputHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Model\AbstractCommonModel;
|
||||
use Mautic\CoreBundle\Model\MauticModelInterface;
|
||||
use Mautic\CoreBundle\Security\Exception\PermissionException;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ContainerBagInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* @template E of object
|
||||
*/
|
||||
class FetchCommonApiController extends AbstractFOSRestController implements MauticController
|
||||
{
|
||||
use RequestTrait;
|
||||
use FormErrorMessagesTrait;
|
||||
|
||||
/**
|
||||
* If set to true, serializer will not return null values.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $customSelectRequested = false;
|
||||
|
||||
/**
|
||||
* Class for the entity.
|
||||
*
|
||||
* @var class-string<E>
|
||||
*/
|
||||
protected $entityClass;
|
||||
|
||||
/**
|
||||
* Key to return for entity lists.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityNameMulti;
|
||||
|
||||
/**
|
||||
* Key to return for a single entity.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityNameOne;
|
||||
|
||||
/**
|
||||
* Custom JMS strategies to add to the view's context.
|
||||
*
|
||||
* @var array<int, ExclusionStrategyInterface>
|
||||
*/
|
||||
protected $exclusionStrategies = [];
|
||||
|
||||
/**
|
||||
* Pass to the model's getEntities() method.
|
||||
*
|
||||
* @var array<mixed>
|
||||
*/
|
||||
protected $extraGetEntitiesArguments = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $inBatchMode = false;
|
||||
|
||||
/**
|
||||
* Used to set default filters for entity lists such as restricting to owning user.
|
||||
*
|
||||
* @var array<array<string, mixed>>
|
||||
*/
|
||||
protected $listFilters = [];
|
||||
|
||||
/**
|
||||
* Model object for processing the entity.
|
||||
*
|
||||
* @var AbstractCommonModel<E>|null
|
||||
*/
|
||||
protected $model;
|
||||
|
||||
/**
|
||||
* The level parent/children should stop loading if applicable.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $parentChildrenLevelDepth = 3;
|
||||
|
||||
/**
|
||||
* Permission base for the entity such as page:pages.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $permissionBase;
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $serializerGroups = [];
|
||||
|
||||
/**
|
||||
* @var Translator
|
||||
*/
|
||||
protected $translator;
|
||||
|
||||
protected ContainerBagInterface $parametersContainer;
|
||||
|
||||
/**
|
||||
* @param ModelFactory<E> $modelFactory
|
||||
*/
|
||||
public function __construct(
|
||||
protected CorePermissions $security,
|
||||
Translator $translator,
|
||||
protected EntityResultHelper $entityResultHelper,
|
||||
private AppVersion $appVersion,
|
||||
private RequestStack $requestStack,
|
||||
protected ManagerRegistry $doctrine,
|
||||
protected ModelFactory $modelFactory,
|
||||
protected EventDispatcherInterface $dispatcher,
|
||||
protected CoreParametersHelper $coreParametersHelper,
|
||||
) {
|
||||
$this->translator = $translator;
|
||||
|
||||
if (null !== $this->model && !$this->permissionBase && method_exists($this->model, 'getPermissionBase')) {
|
||||
$this->permissionBase = $this->model->getPermissionBase();
|
||||
}
|
||||
|
||||
$event = new ApiInitializeEvent(
|
||||
(string) $this->entityClass,
|
||||
$this->serializerGroups,
|
||||
$this->exclusionStrategies,
|
||||
);
|
||||
$this->dispatcher->dispatch($event);
|
||||
|
||||
$this->serializerGroups = $event->getSerializerGroups();
|
||||
$this->exclusionStrategies = $event->getExclusionStrategies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a list of entities as defined by the API URL.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getEntitiesAction(Request $request, UserHelper $userHelper)
|
||||
{
|
||||
$repo = $this->model->getRepository();
|
||||
$tableAlias = $repo->getTableAlias();
|
||||
$publishedOnly = $request->get('published', 0);
|
||||
$minimal = $request->get('minimal', 0);
|
||||
|
||||
try {
|
||||
if (!$this->security->isGranted($this->permissionBase.':view')) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
} catch (PermissionException $e) {
|
||||
return $this->accessDenied($e->getMessage());
|
||||
}
|
||||
|
||||
if ($this->security->checkPermissionExists($this->permissionBase.':viewother')
|
||||
&& !$this->security->isGranted($this->permissionBase.':viewother')
|
||||
&& null !== $user = $userHelper->getUser()
|
||||
) {
|
||||
$this->listFilters[] = [
|
||||
'column' => $tableAlias.'.createdBy',
|
||||
'expr' => 'eq',
|
||||
'value' => $user->getId(),
|
||||
];
|
||||
}
|
||||
|
||||
if ($publishedOnly) {
|
||||
$this->listFilters[] = [
|
||||
'column' => $tableAlias.'.isPublished',
|
||||
'expr' => 'eq',
|
||||
'value' => true,
|
||||
];
|
||||
}
|
||||
|
||||
if ($minimal) {
|
||||
if (isset($this->serializerGroups[0])) {
|
||||
$this->serializerGroups[0] = str_replace('Details', 'List', $this->serializerGroups[0]);
|
||||
}
|
||||
}
|
||||
|
||||
$args = array_merge(
|
||||
[
|
||||
'start' => $request->query->get('start', 0),
|
||||
'limit' => $request->query->get('limit', $this->coreParametersHelper->get('default_pagelimit')),
|
||||
'filter' => [
|
||||
'string' => $request->query->get('search', ''),
|
||||
'force' => $this->listFilters,
|
||||
],
|
||||
'orderBy' => $this->addAliasIfNotPresent($request->query->get('orderBy', ''), $tableAlias),
|
||||
'orderByDir' => $request->query->get('orderByDir', 'ASC'),
|
||||
'withTotalCount' => true, // for repositories that break free of Paginator
|
||||
],
|
||||
$this->extraGetEntitiesArguments
|
||||
);
|
||||
|
||||
if ($select = InputHelper::cleanArray($request->query->all()['select'] ?? $request->request->all()['select'] ?? [])) {
|
||||
$args['select'] = $select;
|
||||
$this->customSelectRequested = true;
|
||||
}
|
||||
|
||||
if ($where = $this->getWhereFromRequest($request)) {
|
||||
$args['filter']['where'] = $where;
|
||||
}
|
||||
|
||||
if ($order = $this->getOrderFromRequest($request)) {
|
||||
$args['filter']['order'] = $order;
|
||||
}
|
||||
|
||||
if ($totalCountTtl = $this->getTotalCountTtl()) {
|
||||
$args['totalCountTtl'] = $totalCountTtl;
|
||||
}
|
||||
|
||||
$results = $this->model->getEntities($args);
|
||||
|
||||
[$entities, $totalCount] = $this->prepareEntitiesForView($results);
|
||||
|
||||
$view = $this->view(
|
||||
[
|
||||
'total' => $totalCount,
|
||||
$this->entityNameMulti => $entities,
|
||||
],
|
||||
Response::HTTP_OK
|
||||
);
|
||||
$this->setSerializationContext($view);
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes and returns an array of where statements from the request.
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
protected function getWhereFromRequest(Request $request)
|
||||
{
|
||||
$where = $request->query->all()['where'] ?? [];
|
||||
|
||||
$this->sanitizeWhereClauseArrayFromRequest($where);
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes and returns an array of ORDER statements from the request.
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
protected function getOrderFromRequest(Request $request): array
|
||||
{
|
||||
return InputHelper::cleanArray($request->query->all()['order'] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the repository alias to the column name if it doesn't exist.
|
||||
*
|
||||
* @return string $column name with alias prefix
|
||||
*/
|
||||
protected function addAliasIfNotPresent(string $columns, string $alias): string
|
||||
{
|
||||
if (!$columns) {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
$columns = explode(',', trim($columns));
|
||||
$prefix = $alias.'.';
|
||||
|
||||
array_walk(
|
||||
$columns,
|
||||
function (&$column, $key, $prefix): void {
|
||||
$column = trim($column);
|
||||
if (1 === count(explode('.', $column))) {
|
||||
$column = $prefix.$column;
|
||||
}
|
||||
},
|
||||
$prefix
|
||||
);
|
||||
|
||||
return implode(',', $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains a specific entity as defined by the API URL.
|
||||
*
|
||||
* @param int $id Entity ID
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getEntityAction(Request $request, $id)
|
||||
{
|
||||
$args = [];
|
||||
if ($select = InputHelper::cleanArray($request->get('select', []))) {
|
||||
$args['select'] = $select;
|
||||
$this->customSelectRequested = true;
|
||||
}
|
||||
|
||||
if (!empty($args)) {
|
||||
$args['id'] = $id;
|
||||
$entity = $this->model->getEntity($args);
|
||||
} else {
|
||||
$entity = $this->model->getEntity($id);
|
||||
}
|
||||
|
||||
if (!$entity instanceof $this->entityClass) {
|
||||
return $this->notFound();
|
||||
}
|
||||
|
||||
if (!$this->checkEntityAccess($entity)) {
|
||||
return $this->accessDenied();
|
||||
}
|
||||
|
||||
$this->preSerializeEntity($entity);
|
||||
$view = $this->view([$this->entityNameOne => $entity], Response::HTTP_OK);
|
||||
$this->setSerializationContext($view);
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new entity from provided params.
|
||||
*
|
||||
* @param array<mixed> $params
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getNewEntity(array $params)
|
||||
{
|
||||
return $this->model->getEntity();
|
||||
}
|
||||
|
||||
public function getCurrentRequest(): Request
|
||||
{
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
if (null === $request) {
|
||||
throw new \RuntimeException('Request is not set.');
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for notFound method. It's used in the LeadAccessTrait.
|
||||
*
|
||||
* @param array<mixed> $args
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function postActionRedirect(array $args = [])
|
||||
{
|
||||
return $this->notFound('mautic.contact.error.notfound');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 403 Access Denied.
|
||||
*
|
||||
* @param string $msg
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function accessDenied($msg = 'mautic.core.error.accessdenied')
|
||||
{
|
||||
return $this->returnError($msg, Response::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
protected function addExclusionStrategy(ExclusionStrategyInterface $strategy): void
|
||||
{
|
||||
$this->exclusionStrategies[] = $strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 400 Bad Request.
|
||||
*
|
||||
* @param string $msg
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function badRequest($msg = 'mautic.core.error.badrequest')
|
||||
{
|
||||
return $this->returnError($msg, Response::HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if user has permission to access retrieved entity.
|
||||
*
|
||||
* @param FormEntity $entity
|
||||
* @param string $action view|create|edit|publish|delete
|
||||
*
|
||||
* @return bool|Response
|
||||
*/
|
||||
protected function checkEntityAccess($entity, $action = 'view')
|
||||
{
|
||||
$ownPerm = "{$this->permissionBase}:{$action}own";
|
||||
$otherPerm = "{$this->permissionBase}:{$action}other";
|
||||
|
||||
if ('publish' === $action) {
|
||||
return $this->security->hasPublishAccessForEntity($entity, $ownPerm, $otherPerm);
|
||||
}
|
||||
|
||||
if ('create' !== $action && is_object($entity) && method_exists($entity, 'getCreatedBy')) {
|
||||
$owner = (method_exists($entity, 'getPermissionUser')) ? $entity->getPermissionUser() : $entity->getCreatedBy();
|
||||
|
||||
return $this->security->hasEntityAccess($ownPerm, $otherPerm, $owner);
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->security->isGranted("{$this->permissionBase}:{$action}");
|
||||
} catch (PermissionException $e) {
|
||||
return $this->accessDenied($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $parameters
|
||||
* @param mixed[] $errors
|
||||
* @param bool $prepareForSerialization
|
||||
* @param string $requestIdColumn
|
||||
* @param MauticModelInterface|null $model
|
||||
* @param bool $returnWithOriginalKeys
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected function getBatchEntities($parameters, &$errors, $prepareForSerialization = false, $requestIdColumn = 'id', $model = null, $returnWithOriginalKeys = true): array
|
||||
{
|
||||
$idHelper = new BatchIdToEntityHelper($parameters, $requestIdColumn);
|
||||
|
||||
if (!$idHelper->hasIds()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var AbstractCommonModel<object> $model */
|
||||
$model = $model ?: $this->model;
|
||||
$entities = $model->getEntities(
|
||||
[
|
||||
'filter' => [
|
||||
'force' => [
|
||||
[
|
||||
'column' => $model->getRepository()->getTableAlias().'.id',
|
||||
'expr' => 'in',
|
||||
'value' => $idHelper->getIds(),
|
||||
],
|
||||
],
|
||||
],
|
||||
'ignore_paginator' => true,
|
||||
]
|
||||
);
|
||||
// It must be associative because the order of entities has changed
|
||||
$idHelper->setIsAssociative(true);
|
||||
|
||||
[$entities, $total] = $prepareForSerialization
|
||||
?
|
||||
$this->prepareEntitiesForView($entities)
|
||||
:
|
||||
$this->prepareEntityResultsToArray($entities);
|
||||
|
||||
// Set errors
|
||||
if ($idHelper->hasErrors()) {
|
||||
foreach ($idHelper->getErrors() as $key => $error) {
|
||||
$this->setBatchError($key, $error, Response::HTTP_BAD_REQUEST, $errors);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the response with matching keys from the request
|
||||
if ($returnWithOriginalKeys) {
|
||||
if ($entities instanceof \ArrayObject) {
|
||||
$entities = $entities->getArrayCopy();
|
||||
}
|
||||
|
||||
return $idHelper->orderByOriginalKey($entities);
|
||||
}
|
||||
|
||||
// Return the response with IDs as keys (default behavior)
|
||||
$return = [];
|
||||
foreach ($entities as $entity) {
|
||||
$return[$entity->getId()] = $entity;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default properties of an entity and parents.
|
||||
*
|
||||
* @phpstan-param E $entity
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
protected function getEntityDefaultProperties(object $entity): array
|
||||
{
|
||||
$class = $entity::class;
|
||||
$chain = array_reverse(class_parents($entity), true) + [$class => $class];
|
||||
$defaultValues = [];
|
||||
|
||||
$classMetdata = new ClassMetadata($class);
|
||||
foreach ($chain as $class) {
|
||||
if (method_exists($class, 'loadMetadata')) {
|
||||
$class::loadMetadata($classMetdata);
|
||||
}
|
||||
$defaultValues += (new \ReflectionClass($class))->getDefaultProperties();
|
||||
}
|
||||
|
||||
// These are the mapped columns
|
||||
$fields = $classMetdata->getFieldNames();
|
||||
|
||||
// Merge values in with $fields
|
||||
$properties = [];
|
||||
foreach ($fields as $field) {
|
||||
$properties[$field] = $defaultValues[$field];
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append options to the form.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function getEntityFormOptions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a model instance from the service container.
|
||||
*
|
||||
* @return AbstractCommonModel<E>
|
||||
*/
|
||||
protected function getModel(string $modelNameKey): AbstractCommonModel
|
||||
{
|
||||
return $this->modelFactory->getModel($modelNameKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 404 Not Found.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function notFound(string $msg = 'mautic.core.error.notfound')
|
||||
{
|
||||
return $this->returnError($msg, Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives child controllers opportunity to analyze and do whatever to an entity before going through serializer.
|
||||
*
|
||||
* @phpstan-param E $entity
|
||||
*/
|
||||
protected function preSerializeEntity(object $entity, string $action = 'view'): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares entities returned from repository getEntities().
|
||||
*
|
||||
* @param array<mixed>|Paginator<E> $results
|
||||
*
|
||||
* @return array{0: array<mixed>|\ArrayObject<int,mixed>, 1: int}
|
||||
*/
|
||||
protected function prepareEntitiesForView($results): array
|
||||
{
|
||||
return $this->prepareEntityResultsToArray(
|
||||
$results,
|
||||
function ($entity): void {
|
||||
$this->preSerializeEntity($entity);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed>|Paginator<E> $results
|
||||
* @param callable|null $callback
|
||||
*
|
||||
* @return array{0: array<mixed>|\ArrayObject<int,mixed>, 1: int}
|
||||
*/
|
||||
protected function prepareEntityResultsToArray($results, $callback = null): array
|
||||
{
|
||||
if (is_array($results) && isset($results['count'])) {
|
||||
$totalCount = $results['count'];
|
||||
$results = $results['results'];
|
||||
} else {
|
||||
$totalCount = count($results);
|
||||
}
|
||||
|
||||
$entities = $this->entityResultHelper->getArray($results, $callback);
|
||||
|
||||
return [$entities, $totalCount];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an error.
|
||||
*
|
||||
* @param array<mixed> $details
|
||||
*
|
||||
* @return Response|array<string, array<mixed>|int|string|null>
|
||||
*/
|
||||
protected function returnError(string $msg, int $code = Response::HTTP_INTERNAL_SERVER_ERROR, array $details = [])
|
||||
{
|
||||
if ($this->translator->hasId($msg, 'flashes')) {
|
||||
$msg = $this->translator->trans($msg, [], 'flashes');
|
||||
} elseif ($this->translator->hasId($msg, 'messages')) {
|
||||
$msg = $this->translator->trans($msg, [], 'messages');
|
||||
}
|
||||
|
||||
$error = [
|
||||
'code' => $code,
|
||||
'message' => $msg,
|
||||
'details' => $details,
|
||||
'type' => null,
|
||||
];
|
||||
|
||||
if ($this->inBatchMode) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
$view = $this->view(
|
||||
[
|
||||
'errors' => [
|
||||
$error,
|
||||
],
|
||||
],
|
||||
$code
|
||||
);
|
||||
|
||||
return $this->handleView($view);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $where
|
||||
*/
|
||||
protected function sanitizeWhereClauseArrayFromRequest(array &$where): void
|
||||
{
|
||||
foreach ($where as $key => $statement) {
|
||||
if (isset($statement['internal'])) {
|
||||
unset($where[$key]);
|
||||
} elseif (in_array($statement['expr'], ['andX', 'orX'])) {
|
||||
$this->sanitizeWhereClauseArrayFromRequest($statement['val']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array<string|int>> $errors
|
||||
* @param array<int, object|null> $entities
|
||||
*
|
||||
* @phpstan-param E|null $entity
|
||||
* @phpstan-param array<int, E|null> $entities
|
||||
*/
|
||||
protected function setBatchError(int $key, string $msg, int $code, array &$errors, array &$entities = [], ?object $entity = null): void
|
||||
{
|
||||
unset($entities[$key]);
|
||||
if ($entity) {
|
||||
$this->doctrine->getManager()->detach($entity);
|
||||
}
|
||||
|
||||
$errors[$key] = [
|
||||
'message' => $this->translator->hasId($msg, 'flashes') ? $this->translator->trans($msg, [], 'flashes') : $msg,
|
||||
'code' => $code,
|
||||
'type' => 'api',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set serialization groups and exclusion strategies.
|
||||
*/
|
||||
protected function setSerializationContext(View $view): void
|
||||
{
|
||||
$context = $view->getContext();
|
||||
|
||||
if ($this->dispatcher->hasListeners(ApiEvents::API_PRE_SERIALIZATION_CONTEXT)) {
|
||||
$apiSerializationContextEvent = new ApiSerializationContextEvent($context, $this->getCurrentRequest());
|
||||
$this->dispatcher->dispatch($apiSerializationContextEvent, ApiEvents::API_PRE_SERIALIZATION_CONTEXT);
|
||||
$context = $apiSerializationContextEvent->getContext();
|
||||
}
|
||||
|
||||
if (!empty($this->serializerGroups)) {
|
||||
$context->setGroups($this->serializerGroups);
|
||||
}
|
||||
|
||||
// Only include FormEntity properties for the top level entity and not the associated entities
|
||||
$context->addExclusionStrategy(
|
||||
new PublishDetailsExclusionStrategy()
|
||||
);
|
||||
|
||||
// Only include first level of children/parents
|
||||
if ($this->parentChildrenLevelDepth) {
|
||||
$context->addExclusionStrategy(
|
||||
new ParentChildrenExclusionStrategy($this->parentChildrenLevelDepth)
|
||||
);
|
||||
}
|
||||
|
||||
// Add custom exclusion strategies
|
||||
foreach ($this->exclusionStrategies as $strategy) {
|
||||
$context->addExclusionStrategy($strategy);
|
||||
}
|
||||
|
||||
// Include null values if a custom select has not been given
|
||||
if (!$this->customSelectRequested) {
|
||||
$context->setSerializeNull(true);
|
||||
}
|
||||
|
||||
if ($this->dispatcher->hasListeners(ApiEvents::API_POST_SERIALIZATION_CONTEXT)) {
|
||||
$apiSerializationContextEvent = new ApiSerializationContextEvent($context, $this->getCurrentRequest());
|
||||
$this->dispatcher->dispatch($apiSerializationContextEvent, ApiEvents::API_POST_SERIALIZATION_CONTEXT);
|
||||
$context = $apiSerializationContextEvent->getContext();
|
||||
}
|
||||
|
||||
$view->setContext($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $parameters
|
||||
*
|
||||
* @return array<string, array<mixed>|int|string|null>|bool|Response
|
||||
*/
|
||||
protected function validateBatchPayload(array $parameters)
|
||||
{
|
||||
$batchLimit = (int) $this->coreParametersHelper->get('api_batch_max_limit', 200);
|
||||
if (count($parameters) > $batchLimit) {
|
||||
return $this->returnError($this->translator->trans('mautic.api.call.batch_exception', ['%limit%' => $batchLimit]));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|null $data
|
||||
* @param array<string, string|int> $headers
|
||||
*/
|
||||
protected function view($data = null, ?int $statusCode = null, array $headers = []): View
|
||||
{
|
||||
if ($data instanceof Paginator) {
|
||||
// Get iterator out of Paginator class so that the entities are properly serialized by the serializer
|
||||
$data = iterator_to_array($data->getIterator(), true);
|
||||
}
|
||||
|
||||
$headers['Mautic-Version'] = $this->appVersion->getVersion();
|
||||
|
||||
return parent::view($data, $statusCode, $headers);
|
||||
}
|
||||
|
||||
protected function getTotalCountTtl(): ?int
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ApiBundle\Controller\oAuth2;
|
||||
|
||||
use FOS\OAuthServerBundle\Form\Handler\AuthorizeFormHandler;
|
||||
use FOS\OAuthServerBundle\Model\ClientManagerInterface;
|
||||
use OAuth2\OAuth2;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Error\SyntaxError;
|
||||
|
||||
class AuthorizeController extends \FOS\OAuthServerBundle\Controller\AuthorizeController
|
||||
{
|
||||
private TokenStorageInterface $tokenStorage;
|
||||
|
||||
/**
|
||||
* This constructor must be duplicated from the extended class so our custom code could access the properties.
|
||||
*/
|
||||
public function __construct(
|
||||
RequestStack $requestStack,
|
||||
Form $authorizeForm,
|
||||
OAuth2 $oAuth2Server,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
UrlGeneratorInterface $router,
|
||||
ClientManagerInterface $clientManager,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
) {
|
||||
parent::__construct(
|
||||
$requestStack,
|
||||
$authorizeForm,
|
||||
$oAuth2Server,
|
||||
$tokenStorage,
|
||||
$router,
|
||||
$clientManager,
|
||||
$eventDispatcher
|
||||
);
|
||||
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string , mixed> $data Various data to be passed to the twig template
|
||||
*
|
||||
* @throws LoaderError
|
||||
* @throws RuntimeError
|
||||
* @throws SyntaxError
|
||||
*/
|
||||
protected function renderAuthorize(array $data, Environment $twig): Response
|
||||
{
|
||||
$response = $twig->render(
|
||||
'@MauticApi/Authorize/oAuth2/authorize.html.twig',
|
||||
$data
|
||||
);
|
||||
|
||||
return new Response($response);
|
||||
}
|
||||
|
||||
public function authorizeAction(Request $request, AuthorizeFormHandler $formHandler, Environment $twig): Response
|
||||
{
|
||||
// The parent bundle does not care about token being empty.
|
||||
if (null === $this->tokenStorage->getToken()) {
|
||||
throw new AccessDeniedException('This user does not have access to this section. No token.');
|
||||
}
|
||||
|
||||
return parent::authorizeAction($request, $formHandler, $twig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\ApiBundle\Controller\oAuth2;
|
||||
|
||||
use Mautic\CoreBundle\Controller\CommonController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Exception;
|
||||
use Symfony\Component\Security\Http\SecurityRequestAttributes;
|
||||
|
||||
class SecurityController extends CommonController
|
||||
{
|
||||
public function loginAction(Request $request): Response
|
||||
{
|
||||
$session = $request->getSession();
|
||||
|
||||
// get the login error if there is one
|
||||
if ($request->attributes->has(SecurityRequestAttributes::AUTHENTICATION_ERROR)) {
|
||||
$error = $request->attributes->get(SecurityRequestAttributes::AUTHENTICATION_ERROR);
|
||||
} else {
|
||||
$error = $session->get(SecurityRequestAttributes::AUTHENTICATION_ERROR);
|
||||
$session->remove(SecurityRequestAttributes::AUTHENTICATION_ERROR);
|
||||
}
|
||||
if (!empty($error)) {
|
||||
if ($error instanceof Exception\BadCredentialsException) {
|
||||
$msg = 'mautic.user.auth.error.invalidlogin';
|
||||
} else {
|
||||
$msg = $error->getMessage();
|
||||
}
|
||||
$this->addFlashMessage($msg, [], 'error', null, false);
|
||||
}
|
||||
|
||||
if ($session->has('_security.target_path')) {
|
||||
if (str_contains($session->get('_security.target_path'), $this->generateUrl('fos_oauth_server_authorize'))) {
|
||||
$session->set('_fos_oauth_server.ensure_logout', true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
'@MauticApi/Security/login.html.twig',
|
||||
[
|
||||
'last_username' => $session->get(SecurityRequestAttributes::LAST_USERNAME),
|
||||
'route' => 'mautic_oauth2_server_auth_login_check',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function loginCheckAction(): Response
|
||||
{
|
||||
return new Response('', 400);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user