Files
Chamilo/vendor/sonata-project/admin-bundle/Controller/CRUDController.php
2025-04-10 12:24:57 +02:00

1427 lines
45 KiB
PHP

<?php
/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Sonata\AdminBundle\Controller;
use Doctrine\Common\Inflector\Inflector;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Exception\LockException;
use Sonata\AdminBundle\Exception\ModelManagerException;
use Sonata\AdminBundle\Util\AdminObjectAclData;
use Sonata\AdminBundle\Util\AdminObjectAclManipulator;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Csrf\CsrfToken;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class CRUDController extends Controller
{
/**
* The related Admin class.
*
* @var AdminInterface
*/
protected $admin;
/**
* Sets the Container associated with this Controller.
*
* @param ContainerInterface $container A ContainerInterface instance
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
$this->configure();
}
/**
* {@inheritdoc}
*/
public function render($view, array $parameters = [], Response $response = null)
{
if (!$this->isXmlHttpRequest()) {
$parameters['breadcrumbs_builder'] = $this->get('sonata.admin.breadcrumbs_builder');
}
$parameters['admin'] = isset($parameters['admin']) ?
$parameters['admin'] :
$this->admin;
$parameters['base_template'] = isset($parameters['base_template']) ?
$parameters['base_template'] :
$this->getBaseTemplate();
$parameters['admin_pool'] = $this->get('sonata.admin.pool');
return parent::render($view, $parameters, $response);
}
/**
* List action.
*
* @return Response
*
* @throws AccessDeniedException If access is not granted
*/
public function listAction()
{
$request = $this->getRequest();
$this->admin->checkAccess('list');
$preResponse = $this->preList($request);
if ($preResponse !== null) {
return $preResponse;
}
if ($listMode = $request->get('_list_mode')) {
$this->admin->setListMode($listMode);
}
$datagrid = $this->admin->getDatagrid();
$formView = $datagrid->getForm()->createView();
// set the theme for the current Admin Form
$this->setFormTheme($formView, $this->admin->getFilterTheme());
return $this->render($this->admin->getTemplate('list'), [
'action' => 'list',
'form' => $formView,
'datagrid' => $datagrid,
'csrf_token' => $this->getCsrfToken('sonata.batch'),
'export_formats' => $this->has('sonata.admin.admin_exporter') ?
$this->get('sonata.admin.admin_exporter')->getAvailableFormats($this->admin) :
$this->admin->getExportFormats(),
], null);
}
/**
* Execute a batch delete.
*
* @param ProxyQueryInterface $query
*
* @return RedirectResponse
*
* @throws AccessDeniedException If access is not granted
*/
public function batchActionDelete(ProxyQueryInterface $query)
{
$this->admin->checkAccess('batchDelete');
$modelManager = $this->admin->getModelManager();
try {
$modelManager->batchDelete($this->admin->getClass(), $query);
$this->addFlash(
'sonata_flash_success',
$this->trans('flash_batch_delete_success', [], 'SonataAdminBundle')
);
} catch (ModelManagerException $e) {
$this->handleModelManagerException($e);
$this->addFlash(
'sonata_flash_error',
$this->trans('flash_batch_delete_error', [], 'SonataAdminBundle')
);
}
return new RedirectResponse($this->admin->generateUrl(
'list',
['filter' => $this->admin->getFilterParameters()]
));
}
/**
* Delete action.
*
* @param int|string|null $id
*
* @return Response|RedirectResponse
*
* @throws NotFoundHttpException If the object does not exist
* @throws AccessDeniedException If access is not granted
*/
public function deleteAction($id)
{
$request = $this->getRequest();
$id = $request->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
}
$this->admin->checkAccess('delete', $object);
$preResponse = $this->preDelete($request, $object);
if ($preResponse !== null) {
return $preResponse;
}
if ($this->getRestMethod() == 'DELETE') {
// check the csrf token
$this->validateCsrfToken('sonata.delete');
$objectName = $this->admin->toString($object);
try {
$this->admin->delete($object);
if ($this->isXmlHttpRequest()) {
return $this->renderJson(['result' => 'ok'], 200, []);
}
$this->addFlash(
'sonata_flash_success',
$this->trans(
'flash_delete_success',
['%name%' => $this->escapeHtml($objectName)],
'SonataAdminBundle'
)
);
} catch (ModelManagerException $e) {
$this->handleModelManagerException($e);
if ($this->isXmlHttpRequest()) {
return $this->renderJson(['result' => 'error'], 200, []);
}
$this->addFlash(
'sonata_flash_error',
$this->trans(
'flash_delete_error',
['%name%' => $this->escapeHtml($objectName)],
'SonataAdminBundle'
)
);
}
return $this->redirectTo($object);
}
return $this->render($this->admin->getTemplate('delete'), [
'object' => $object,
'action' => 'delete',
'csrf_token' => $this->getCsrfToken('sonata.delete'),
], null);
}
/**
* Edit action.
*
* @param int|string|null $id
*
* @return Response|RedirectResponse
*
* @throws NotFoundHttpException If the object does not exist
* @throws AccessDeniedException If access is not granted
*/
public function editAction($id = null)
{
$request = $this->getRequest();
// the key used to lookup the template
$templateKey = 'edit';
$id = $request->get($this->admin->getIdParameter());
$existingObject = $this->admin->getObject($id);
if (!$existingObject) {
throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
}
$this->admin->checkAccess('edit', $existingObject);
$preResponse = $this->preEdit($request, $existingObject);
if ($preResponse !== null) {
return $preResponse;
}
$this->admin->setSubject($existingObject);
$objectId = $this->admin->getNormalizedIdentifier($existingObject);
/** @var $form Form */
$form = $this->admin->getForm();
$form->setData($existingObject);
$form->handleRequest($request);
if ($form->isSubmitted()) {
//TODO: remove this check for 4.0
if (method_exists($this->admin, 'preValidate')) {
$this->admin->preValidate($existingObject);
}
$isFormValid = $form->isValid();
// persist if the form was valid and if in preview mode the preview was approved
if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
$submittedObject = $form->getData();
$this->admin->setSubject($submittedObject);
try {
$existingObject = $this->admin->update($submittedObject);
if ($this->isXmlHttpRequest()) {
return $this->renderJson([
'result' => 'ok',
'objectId' => $objectId,
'objectName' => $this->escapeHtml($this->admin->toString($existingObject)),
], 200, []);
}
$this->addFlash(
'sonata_flash_success',
$this->trans(
'flash_edit_success',
['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
'SonataAdminBundle'
)
);
// redirect to edit mode
return $this->redirectTo($existingObject);
} catch (ModelManagerException $e) {
$this->handleModelManagerException($e);
$isFormValid = false;
} catch (LockException $e) {
$this->addFlash('sonata_flash_error', $this->trans('flash_lock_error', [
'%name%' => $this->escapeHtml($this->admin->toString($existingObject)),
'%link_start%' => '<a href="'.$this->admin->generateObjectUrl('edit', $existingObject).'">',
'%link_end%' => '</a>',
], 'SonataAdminBundle'));
}
}
// show an error message if the form failed validation
if (!$isFormValid) {
if (!$this->isXmlHttpRequest()) {
$this->addFlash(
'sonata_flash_error',
$this->trans(
'flash_edit_error',
['%name%' => $this->escapeHtml($this->admin->toString($existingObject))],
'SonataAdminBundle'
)
);
}
} elseif ($this->isPreviewRequested()) {
// enable the preview template if the form was valid and preview was requested
$templateKey = 'preview';
$this->admin->getShow();
}
}
$formView = $form->createView();
// set the theme for the current Admin Form
$this->setFormTheme($formView, $this->admin->getFormTheme());
return $this->render($this->admin->getTemplate($templateKey), [
'action' => 'edit',
'form' => $formView,
'object' => $existingObject,
'objectId' => $objectId,
], null);
}
/**
* Batch action.
*
* @return Response|RedirectResponse
*
* @throws NotFoundHttpException If the HTTP method is not POST
* @throws \RuntimeException If the batch action is not defined
*/
public function batchAction()
{
$request = $this->getRequest();
$restMethod = $this->getRestMethod();
if ('POST' !== $restMethod) {
throw $this->createNotFoundException(sprintf('Invalid request type "%s", POST expected', $restMethod));
}
// check the csrf token
$this->validateCsrfToken('sonata.batch');
$confirmation = $request->get('confirmation', false);
if ($data = json_decode($request->get('data'), true)) {
$action = $data['action'];
$idx = $data['idx'];
$allElements = $data['all_elements'];
$request->request->replace(array_merge($request->request->all(), $data));
} else {
$request->request->set('idx', $request->get('idx', []));
$request->request->set('all_elements', $request->get('all_elements', false));
$action = $request->get('action');
$idx = $request->get('idx');
$allElements = $request->get('all_elements');
$data = $request->request->all();
unset($data['_sonata_csrf_token']);
}
// NEXT_MAJOR: Remove reflection check.
$reflector = new \ReflectionMethod($this->admin, 'getBatchActions');
if ($reflector->getDeclaringClass()->getName() === get_class($this->admin)) {
@trigger_error('Override Sonata\AdminBundle\Admin\AbstractAdmin::getBatchActions method'
.' is deprecated since version 3.2.'
.' Use Sonata\AdminBundle\Admin\AbstractAdmin::configureBatchActions instead.'
.' The method will be final in 4.0.', E_USER_DEPRECATED
);
}
$batchActions = $this->admin->getBatchActions();
if (!array_key_exists($action, $batchActions)) {
throw new \RuntimeException(sprintf('The `%s` batch action is not defined', $action));
}
$camelizedAction = Inflector::classify($action);
$isRelevantAction = sprintf('batchAction%sIsRelevant', $camelizedAction);
if (method_exists($this, $isRelevantAction)) {
$nonRelevantMessage = call_user_func([$this, $isRelevantAction], $idx, $allElements, $request);
} else {
$nonRelevantMessage = count($idx) != 0 || $allElements; // at least one item is selected
}
if (!$nonRelevantMessage) { // default non relevant message (if false of null)
$nonRelevantMessage = 'flash_batch_empty';
}
$datagrid = $this->admin->getDatagrid();
$datagrid->buildPager();
if (true !== $nonRelevantMessage) {
$this->addFlash(
'sonata_flash_info',
$this->trans($nonRelevantMessage, [], 'SonataAdminBundle')
);
return new RedirectResponse(
$this->admin->generateUrl(
'list',
['filter' => $this->admin->getFilterParameters()]
)
);
}
$askConfirmation = isset($batchActions[$action]['ask_confirmation']) ?
$batchActions[$action]['ask_confirmation'] :
true;
if ($askConfirmation && $confirmation != 'ok') {
$actionLabel = $batchActions[$action]['label'];
$batchTranslationDomain = isset($batchActions[$action]['translation_domain']) ?
$batchActions[$action]['translation_domain'] :
$this->admin->getTranslationDomain();
$formView = $datagrid->getForm()->createView();
$this->setFormTheme($formView, $this->admin->getFilterTheme());
return $this->render($this->admin->getTemplate('batch_confirmation'), [
'action' => 'list',
'action_label' => $actionLabel,
'batch_translation_domain' => $batchTranslationDomain,
'datagrid' => $datagrid,
'form' => $formView,
'data' => $data,
'csrf_token' => $this->getCsrfToken('sonata.batch'),
], null);
}
// execute the action, batchActionXxxxx
$finalAction = sprintf('batchAction%s', $camelizedAction);
if (!is_callable([$this, $finalAction])) {
throw new \RuntimeException(sprintf('A `%s::%s` method must be callable', get_class($this), $finalAction));
}
$query = $datagrid->getQuery();
$query->setFirstResult(null);
$query->setMaxResults(null);
$this->admin->preBatchAction($action, $query, $idx, $allElements);
if (count($idx) > 0) {
$this->admin->getModelManager()->addIdentifiersToQuery($this->admin->getClass(), $query, $idx);
} elseif (!$allElements) {
$query = null;
}
return call_user_func([$this, $finalAction], $query, $request);
}
/**
* Create action.
*
* @return Response
*
* @throws AccessDeniedException If access is not granted
*/
public function createAction()
{
$request = $this->getRequest();
// the key used to lookup the template
$templateKey = 'edit';
$this->admin->checkAccess('create');
$class = new \ReflectionClass($this->admin->hasActiveSubClass() ? $this->admin->getActiveSubClass() : $this->admin->getClass());
if ($class->isAbstract()) {
return $this->render(
'SonataAdminBundle:CRUD:select_subclass.html.twig',
[
'base_template' => $this->getBaseTemplate(),
'admin' => $this->admin,
'action' => 'create',
],
null,
$request
);
}
$newObject = $this->admin->getNewInstance();
$preResponse = $this->preCreate($request, $newObject);
if ($preResponse !== null) {
return $preResponse;
}
$this->admin->setSubject($newObject);
/** @var $form \Symfony\Component\Form\Form */
$form = $this->admin->getForm();
$form->setData($newObject);
$form->handleRequest($request);
if ($form->isSubmitted()) {
//TODO: remove this check for 4.0
if (method_exists($this->admin, 'preValidate')) {
$this->admin->preValidate($newObject);
}
$isFormValid = $form->isValid();
// persist if the form was valid and if in preview mode the preview was approved
if ($isFormValid && (!$this->isInPreviewMode() || $this->isPreviewApproved())) {
$submittedObject = $form->getData();
$this->admin->setSubject($submittedObject);
$this->admin->checkAccess('create', $submittedObject);
try {
$newObject = $this->admin->create($submittedObject);
if ($this->isXmlHttpRequest()) {
return $this->renderJson([
'result' => 'ok',
'objectId' => $this->admin->getNormalizedIdentifier($newObject),
], 200, []);
}
$this->addFlash(
'sonata_flash_success',
$this->trans(
'flash_create_success',
['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
'SonataAdminBundle'
)
);
// redirect to edit mode
return $this->redirectTo($newObject);
} catch (ModelManagerException $e) {
$this->handleModelManagerException($e);
$isFormValid = false;
}
}
// show an error message if the form failed validation
if (!$isFormValid) {
if (!$this->isXmlHttpRequest()) {
$this->addFlash(
'sonata_flash_error',
$this->trans(
'flash_create_error',
['%name%' => $this->escapeHtml($this->admin->toString($newObject))],
'SonataAdminBundle'
)
);
}
} elseif ($this->isPreviewRequested()) {
// pick the preview template if the form was valid and preview was requested
$templateKey = 'preview';
$this->admin->getShow();
}
}
$formView = $form->createView();
// set the theme for the current Admin Form
$this->setFormTheme($formView, $this->admin->getFormTheme());
return $this->render($this->admin->getTemplate($templateKey), [
'action' => 'create',
'form' => $formView,
'object' => $newObject,
'objectId' => null,
], null);
}
/**
* Show action.
*
* @param int|string|null $id
*
* @return Response
*
* @throws NotFoundHttpException If the object does not exist
* @throws AccessDeniedException If access is not granted
*/
public function showAction($id = null)
{
$request = $this->getRequest();
$id = $request->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
}
$this->admin->checkAccess('show', $object);
$preResponse = $this->preShow($request, $object);
if ($preResponse !== null) {
return $preResponse;
}
$this->admin->setSubject($object);
return $this->render($this->admin->getTemplate('show'), [
'action' => 'show',
'object' => $object,
'elements' => $this->admin->getShow(),
], null);
}
/**
* Show history revisions for object.
*
* @param int|string|null $id
*
* @return Response
*
* @throws AccessDeniedException If access is not granted
* @throws NotFoundHttpException If the object does not exist or the audit reader is not available
*/
public function historyAction($id = null)
{
$request = $this->getRequest();
$id = $request->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
}
$this->admin->checkAccess('history', $object);
$manager = $this->get('sonata.admin.audit.manager');
if (!$manager->hasReader($this->admin->getClass())) {
throw $this->createNotFoundException(
sprintf(
'unable to find the audit reader for class : %s',
$this->admin->getClass()
)
);
}
$reader = $manager->getReader($this->admin->getClass());
$revisions = $reader->findRevisions($this->admin->getClass(), $id);
return $this->render($this->admin->getTemplate('history'), [
'action' => 'history',
'object' => $object,
'revisions' => $revisions,
'currentRevision' => $revisions ? current($revisions) : false,
], null);
}
/**
* View history revision of object.
*
* @param int|string|null $id
* @param string|null $revision
*
* @return Response
*
* @throws AccessDeniedException If access is not granted
* @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
*/
public function historyViewRevisionAction($id = null, $revision = null)
{
$request = $this->getRequest();
$id = $request->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
}
$this->admin->checkAccess('historyViewRevision', $object);
$manager = $this->get('sonata.admin.audit.manager');
if (!$manager->hasReader($this->admin->getClass())) {
throw $this->createNotFoundException(
sprintf(
'unable to find the audit reader for class : %s',
$this->admin->getClass()
)
);
}
$reader = $manager->getReader($this->admin->getClass());
// retrieve the revisioned object
$object = $reader->find($this->admin->getClass(), $id, $revision);
if (!$object) {
throw $this->createNotFoundException(
sprintf(
'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
$id,
$revision,
$this->admin->getClass()
)
);
}
$this->admin->setSubject($object);
return $this->render($this->admin->getTemplate('show'), [
'action' => 'show',
'object' => $object,
'elements' => $this->admin->getShow(),
], null);
}
/**
* Compare history revisions of object.
*
* @param int|string|null $id
* @param int|string|null $base_revision
* @param int|string|null $compare_revision
*
* @return Response
*
* @throws AccessDeniedException If access is not granted
* @throws NotFoundHttpException If the object or revision does not exist or the audit reader is not available
*/
public function historyCompareRevisionsAction($id = null, $base_revision = null, $compare_revision = null)
{
$request = $this->getRequest();
$this->admin->checkAccess('historyCompareRevisions');
$id = $request->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
}
$manager = $this->get('sonata.admin.audit.manager');
if (!$manager->hasReader($this->admin->getClass())) {
throw $this->createNotFoundException(
sprintf(
'unable to find the audit reader for class : %s',
$this->admin->getClass()
)
);
}
$reader = $manager->getReader($this->admin->getClass());
// retrieve the base revision
$base_object = $reader->find($this->admin->getClass(), $id, $base_revision);
if (!$base_object) {
throw $this->createNotFoundException(
sprintf(
'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
$id,
$base_revision,
$this->admin->getClass()
)
);
}
// retrieve the compare revision
$compare_object = $reader->find($this->admin->getClass(), $id, $compare_revision);
if (!$compare_object) {
throw $this->createNotFoundException(
sprintf(
'unable to find the targeted object `%s` from the revision `%s` with classname : `%s`',
$id,
$compare_revision,
$this->admin->getClass()
)
);
}
$this->admin->setSubject($base_object);
return $this->render($this->admin->getTemplate('show_compare'), [
'action' => 'show',
'object' => $base_object,
'object_compare' => $compare_object,
'elements' => $this->admin->getShow(),
], null);
}
/**
* Export data to specified format.
*
* @param Request $request
*
* @return Response
*
* @throws AccessDeniedException If access is not granted
* @throws \RuntimeException If the export format is invalid
*/
public function exportAction(Request $request)
{
$this->admin->checkAccess('export');
$format = $request->get('format');
// NEXT_MAJOR: remove the check
if (!$this->has('sonata.admin.admin_exporter')) {
@trigger_error(
'Not registering the exporter bundle is deprecated since version 3.14.'
.' You must register it to be able to use the export action in 4.0.',
E_USER_DEPRECATED
);
$allowedExportFormats = (array) $this->admin->getExportFormats();
$class = $this->admin->getClass();
$filename = sprintf(
'export_%s_%s.%s',
strtolower(substr($class, strripos($class, '\\') + 1)),
date('Y_m_d_H_i_s', strtotime('now')),
$format
);
$exporter = $this->get('sonata.admin.exporter');
} else {
$adminExporter = $this->get('sonata.admin.admin_exporter');
$allowedExportFormats = $adminExporter->getAvailableFormats($this->admin);
$filename = $adminExporter->getExportFilename($this->admin, $format);
$exporter = $this->get('sonata.exporter.exporter');
}
if (!in_array($format, $allowedExportFormats)) {
throw new \RuntimeException(
sprintf(
'Export in format `%s` is not allowed for class: `%s`. Allowed formats are: `%s`',
$format,
$this->admin->getClass(),
implode(', ', $allowedExportFormats)
)
);
}
return $exporter->getResponse(
$format,
$filename,
$this->admin->getDataSourceIterator()
);
}
/**
* Returns the Response object associated to the acl action.
*
* @param int|string|null $id
*
* @return Response|RedirectResponse
*
* @throws AccessDeniedException If access is not granted
* @throws NotFoundHttpException If the object does not exist or the ACL is not enabled
*/
public function aclAction($id = null)
{
$request = $this->getRequest();
if (!$this->admin->isAclEnabled()) {
throw $this->createNotFoundException('ACL are not enabled for this admin');
}
$id = $request->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw $this->createNotFoundException(sprintf('unable to find the object with id: %s', $id));
}
$this->admin->checkAccess('acl', $object);
$this->admin->setSubject($object);
$aclUsers = $this->getAclUsers();
$aclRoles = $this->getAclRoles();
$adminObjectAclManipulator = $this->get('sonata.admin.object.manipulator.acl.admin');
$adminObjectAclData = new AdminObjectAclData(
$this->admin,
$object,
$aclUsers,
$adminObjectAclManipulator->getMaskBuilderClass(),
$aclRoles
);
$aclUsersForm = $adminObjectAclManipulator->createAclUsersForm($adminObjectAclData);
$aclRolesForm = $adminObjectAclManipulator->createAclRolesForm($adminObjectAclData);
if ($request->getMethod() === 'POST') {
if ($request->request->has(AdminObjectAclManipulator::ACL_USERS_FORM_NAME)) {
$form = $aclUsersForm;
$updateMethod = 'updateAclUsers';
} elseif ($request->request->has(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME)) {
$form = $aclRolesForm;
$updateMethod = 'updateAclRoles';
}
if (isset($form)) {
$form->handleRequest($request);
if ($form->isValid()) {
$adminObjectAclManipulator->$updateMethod($adminObjectAclData);
$this->addFlash(
'sonata_flash_success',
$this->trans('flash_acl_edit_success', [], 'SonataAdminBundle')
);
return new RedirectResponse($this->admin->generateObjectUrl('acl', $object));
}
}
}
return $this->render($this->admin->getTemplate('acl'), [
'action' => 'acl',
'permissions' => $adminObjectAclData->getUserPermissions(),
'object' => $object,
'users' => $aclUsers,
'roles' => $aclRoles,
'aclUsersForm' => $aclUsersForm->createView(),
'aclRolesForm' => $aclRolesForm->createView(),
], null);
}
/**
* @return Request
*/
public function getRequest()
{
if ($this->container->has('request_stack')) {
return $this->container->get('request_stack')->getCurrentRequest();
}
return $this->container->get('request');
}
/**
* Render JSON.
*
* @param mixed $data
* @param int $status
* @param array $headers
*
* @return Response with json encoded data
*/
protected function renderJson($data, $status = 200, $headers = [])
{
return new JsonResponse($data, $status, $headers);
}
/**
* Returns true if the request is a XMLHttpRequest.
*
* @return bool True if the request is an XMLHttpRequest, false otherwise
*/
protected function isXmlHttpRequest()
{
$request = $this->getRequest();
return $request->isXmlHttpRequest() || $request->get('_xml_http_request');
}
/**
* Returns the correct RESTful verb, given either by the request itself or
* via the "_method" parameter.
*
* @return string HTTP method, either
*/
protected function getRestMethod()
{
$request = $this->getRequest();
if (Request::getHttpMethodParameterOverride() || !$request->request->has('_method')) {
return $request->getMethod();
}
return $request->request->get('_method');
}
/**
* Contextualize the admin class depends on the current request.
*
* @throws \RuntimeException
*/
protected function configure()
{
$request = $this->getRequest();
$adminCode = $request->get('_sonata_admin');
if (!$adminCode) {
throw new \RuntimeException(sprintf(
'There is no `_sonata_admin` defined for the controller `%s` and the current route `%s`',
get_class($this),
$request->get('_route')
));
}
$this->admin = $this->container->get('sonata.admin.pool')->getAdminByAdminCode($adminCode);
if (!$this->admin) {
throw new \RuntimeException(sprintf(
'Unable to find the admin class related to the current controller (%s)',
get_class($this)
));
}
$rootAdmin = $this->admin;
while ($rootAdmin->isChild()) {
$rootAdmin->setCurrentChild(true);
$rootAdmin = $rootAdmin->getParent();
}
$rootAdmin->setRequest($request);
if ($request->get('uniqid')) {
$this->admin->setUniqid($request->get('uniqid'));
}
}
/**
* Proxy for the logger service of the container.
* If no such service is found, a NullLogger is returned.
*
* @return LoggerInterface
*/
protected function getLogger()
{
if ($this->container->has('logger')) {
return $this->container->get('logger');
}
return new NullLogger();
}
/**
* Returns the base template name.
*
* @return string The template name
*/
protected function getBaseTemplate()
{
if ($this->isXmlHttpRequest()) {
return $this->admin->getTemplate('ajax');
}
return $this->admin->getTemplate('layout');
}
/**
* @param \Exception $e
*
* @throws \Exception
*/
protected function handleModelManagerException(\Exception $e)
{
if ($this->get('kernel')->isDebug()) {
throw $e;
}
$context = ['exception' => $e];
if ($e->getPrevious()) {
$context['previous_exception_message'] = $e->getPrevious()->getMessage();
}
$this->getLogger()->error($e->getMessage(), $context);
}
/**
* Redirect the user depend on this choice.
*
* @param object $object
*
* @return RedirectResponse
*/
protected function redirectTo($object)
{
$request = $this->getRequest();
$url = false;
if (null !== $request->get('btn_update_and_list')) {
$url = $this->admin->generateUrl('list');
}
if (null !== $request->get('btn_create_and_list')) {
$url = $this->admin->generateUrl('list');
}
if (null !== $request->get('btn_create_and_create')) {
$params = [];
if ($this->admin->hasActiveSubClass()) {
$params['subclass'] = $request->get('subclass');
}
$url = $this->admin->generateUrl('create', $params);
}
if ($this->getRestMethod() === 'DELETE') {
$url = $this->admin->generateUrl('list');
}
if (!$url) {
foreach (['edit', 'show'] as $route) {
if ($this->admin->hasRoute($route) && $this->admin->hasAccess($route, $object)) {
$url = $this->admin->generateObjectUrl($route, $object);
break;
}
}
}
if (!$url) {
$url = $this->admin->generateUrl('list');
}
return new RedirectResponse($url);
}
/**
* Returns true if the preview is requested to be shown.
*
* @return bool
*/
protected function isPreviewRequested()
{
$request = $this->getRequest();
return $request->get('btn_preview') !== null;
}
/**
* Returns true if the preview has been approved.
*
* @return bool
*/
protected function isPreviewApproved()
{
$request = $this->getRequest();
return $request->get('btn_preview_approve') !== null;
}
/**
* Returns true if the request is in the preview workflow.
*
* That means either a preview is requested or the preview has already been shown
* and it got approved/declined.
*
* @return bool
*/
protected function isInPreviewMode()
{
return $this->admin->supportsPreviewMode()
&& ($this->isPreviewRequested()
|| $this->isPreviewApproved()
|| $this->isPreviewDeclined());
}
/**
* Returns true if the preview has been declined.
*
* @return bool
*/
protected function isPreviewDeclined()
{
$request = $this->getRequest();
return $request->get('btn_preview_decline') !== null;
}
/**
* Gets ACL users.
*
* @return \Traversable
*/
protected function getAclUsers()
{
$aclUsers = [];
$userManagerServiceName = $this->container->getParameter('sonata.admin.security.acl_user_manager');
if ($userManagerServiceName !== null && $this->has($userManagerServiceName)) {
$userManager = $this->get($userManagerServiceName);
if (method_exists($userManager, 'findUsers')) {
$aclUsers = $userManager->findUsers();
}
}
return is_array($aclUsers) ? new \ArrayIterator($aclUsers) : $aclUsers;
}
/**
* Gets ACL roles.
*
* @return \Traversable
*/
protected function getAclRoles()
{
$aclRoles = [];
$roleHierarchy = $this->container->getParameter('security.role_hierarchy.roles');
$pool = $this->container->get('sonata.admin.pool');
foreach ($pool->getAdminServiceIds() as $id) {
try {
$admin = $pool->getInstance($id);
} catch (\Exception $e) {
continue;
}
$baseRole = $admin->getSecurityHandler()->getBaseRole($admin);
foreach ($admin->getSecurityInformation() as $role => $permissions) {
$role = sprintf($baseRole, $role);
$aclRoles[] = $role;
}
}
foreach ($roleHierarchy as $name => $roles) {
$aclRoles[] = $name;
$aclRoles = array_merge($aclRoles, $roles);
}
$aclRoles = array_unique($aclRoles);
return is_array($aclRoles) ? new \ArrayIterator($aclRoles) : $aclRoles;
}
/**
* Adds a flash message for type.
*
* @param string $type
* @param string $message
*
* @TODO Remove this method when bumping requirements to Symfony >= 2.6
*/
protected function addFlash($type, $message)
{
if (method_exists('Symfony\Bundle\FrameworkBundle\Controller\Controller', 'addFlash')) {
parent::addFlash($type, $message);
} else {
$this->get('session')
->getFlashBag()
->add($type, $message);
}
}
/**
* Validate CSRF token for action without form.
*
* @param string $intention
*
* @throws HttpException
*/
protected function validateCsrfToken($intention)
{
$request = $this->getRequest();
$token = $request->request->get('_sonata_csrf_token', false);
if ($this->container->has('security.csrf.token_manager')) { // SF3.0
$valid = $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($intention, $token));
} elseif ($this->container->has('form.csrf_provider')) { // < SF3.0
$valid = $this->container->get('form.csrf_provider')->isCsrfTokenValid($intention, $token);
} else {
return;
}
if (!$valid) {
throw new HttpException(400, 'The csrf token is not valid, CSRF attack?');
}
}
/**
* Escape string for html output.
*
* @param string $s
*
* @return string
*/
protected function escapeHtml($s)
{
return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
/**
* Get CSRF token.
*
* @param string $intention
*
* @return string|false
*/
protected function getCsrfToken($intention)
{
if ($this->container->has('security.csrf.token_manager')) {
return $this->container->get('security.csrf.token_manager')->getToken($intention)->getValue();
}
// TODO: Remove it when bumping requirements to SF 2.4+
if ($this->container->has('form.csrf_provider')) {
return $this->container->get('form.csrf_provider')->generateCsrfToken($intention);
}
return false;
}
/**
* This method can be overloaded in your custom CRUD controller.
* It's called from createAction.
*
* @param Request $request
* @param mixed $object
*
* @return Response|null
*/
protected function preCreate(Request $request, $object)
{
}
/**
* This method can be overloaded in your custom CRUD controller.
* It's called from editAction.
*
* @param Request $request
* @param mixed $object
*
* @return Response|null
*/
protected function preEdit(Request $request, $object)
{
}
/**
* This method can be overloaded in your custom CRUD controller.
* It's called from deleteAction.
*
* @param Request $request
* @param mixed $object
*
* @return Response|null
*/
protected function preDelete(Request $request, $object)
{
}
/**
* This method can be overloaded in your custom CRUD controller.
* It's called from showAction.
*
* @param Request $request
* @param mixed $object
*
* @return Response|null
*/
protected function preShow(Request $request, $object)
{
}
/**
* This method can be overloaded in your custom CRUD controller.
* It's called from listAction.
*
* @param Request $request
*
* @return Response|null
*/
protected function preList(Request $request)
{
}
/**
* Translate a message id.
*
* @param string $id
* @param array $parameters
* @param string $domain
* @param string $locale
*
* @return string translated string
*/
final protected function trans($id, array $parameters = [], $domain = null, $locale = null)
{
$domain = $domain ?: $this->admin->getTranslationDomain();
return $this->get('translator')->trans($id, $parameters, $domain, $locale);
}
/**
* Sets the admin form theme to form view. Used for compatibility between Symfony versions.
*
* @param FormView $formView
* @param string $theme
*/
private function setFormTheme(FormView $formView, $theme)
{
$twig = $this->get('twig');
try {
$twig
->getRuntime('Symfony\Bridge\Twig\Form\TwigRenderer')
->setTheme($formView, $theme);
} catch (\Twig_Error_Runtime $e) {
// BC for Symfony < 3.2 where this runtime not exists
$twig
->getExtension('Symfony\Bridge\Twig\Extension\FormExtension')
->renderer
->setTheme($formView, $theme);
}
}
}