<?php
declare(strict_types=1);
/*
* 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\Admin;
use Doctrine\Common\Util\ClassUtils;
use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;
use Sonata\AdminBundle\Builder\DatagridBuilderInterface;
use Sonata\AdminBundle\Builder\FormContractorInterface;
use Sonata\AdminBundle\Builder\ListBuilderInterface;
use Sonata\AdminBundle\Builder\RouteBuilderInterface;
use Sonata\AdminBundle\Builder\ShowBuilderInterface;
use Sonata\AdminBundle\Datagrid\DatagridInterface;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\Pager;
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
use Sonata\AdminBundle\Filter\Persister\FilterPersisterInterface;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Form\Type\ModelHiddenType;
use Sonata\AdminBundle\Model\ModelManagerInterface;
use Sonata\AdminBundle\Object\Metadata;
use Sonata\AdminBundle\Route\RouteCollection;
use Sonata\AdminBundle\Route\RouteGeneratorInterface;
use Sonata\AdminBundle\Security\Handler\AclSecurityHandlerInterface;
use Sonata\AdminBundle\Security\Handler\SecurityHandlerInterface;
use Sonata\AdminBundle\Show\ShowMapper;
use Sonata\AdminBundle\Templating\MutableTemplateRegistryInterface;
use Sonata\AdminBundle\Translator\LabelTranslatorStrategyInterface;
use Sonata\Form\Validator\Constraints\InlineConstraint;
use Sonata\Form\Validator\ErrorElement;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface as RoutingUrlGeneratorInterface;
use Symfony\Component\Security\Acl\Model\DomainObjectInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Translation\TranslatorInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
abstract class AbstractAdmin implements AdminInterface, DomainObjectInterface, AdminTreeInterface
{
public const CONTEXT_MENU = 'menu';
public const CONTEXT_DASHBOARD = 'dashboard';
public const CLASS_REGEX =
'@
(?:([A-Za-z0-9]*)\\\)? # vendor name / app name
(Bundle\\\)? # optional bundle directory
([A-Za-z0-9]+?)(?:Bundle)?\\\ # bundle name, with optional suffix
(
Entity|Document|Model|PHPCR|CouchDocument|Phpcr|
Doctrine\\\Orm|Doctrine\\\Phpcr|Doctrine\\\MongoDB|Doctrine\\\CouchDB
)\\\(.*)@x';
public const MOSAIC_ICON_CLASS = 'fa fa-th-large fa-fw';
/**
* The list FieldDescription constructed from the configureListField method.
*
* @var array
*/
protected $listFieldDescriptions = [];
/**
* The show FieldDescription constructed from the configureShowFields method.
*
* @var array
*/
protected $showFieldDescriptions = [];
/**
* The list FieldDescription constructed from the configureFormField method.
*
* @var array
*/
protected $formFieldDescriptions = [];
/**
* The filter FieldDescription constructed from the configureFilterField method.
*
* @var array
*/
protected $filterFieldDescriptions = [];
/**
* The number of result to display in the list.
*
* @var int
*/
protected $maxPerPage = 32;
/**
* The maximum number of page numbers to display in the list.
*
* @var int
*/
protected $maxPageLinks = 25;
/**
* The base route name used to generate the routing information.
*
* @var string
*/
protected $baseRouteName;
/**
* The base route pattern used to generate the routing information.
*
* @var string
*/
protected $baseRoutePattern;
/**
* The base name controller used to generate the routing information.
*
* @var string
*/
protected $baseControllerName;
/**
* The label class name (used in the title/breadcrumb ...).
*
* @var string
*/
protected $classnameLabel;
/**
* The translation domain to be used to translate messages.
*
* @var string
*/
protected $translationDomain = 'messages';
/**
* Options to set to the form (ie, validation_groups).
*
* @var array
*/
protected $formOptions = [];
/**
* Default values to the datagrid.
*
* @var array
*/
protected $datagridValues = [
'_page' => 1,
'_per_page' => 32,
];
/**
* Predefined per page options.
*
* @var array
*/
protected $perPageOptions = [16, 32, 64, 128, 256];
/**
* Pager type.
*
* @var string
*/
protected $pagerType = Pager::TYPE_DEFAULT;
/**
* The code related to the admin.
*
* @var string
*/
protected $code;
/**
* The label.
*
* @var string
*/
protected $label;
/**
* Whether or not to persist the filters in the session.
*
* NEXT_MAJOR: remove this property
*
* @var bool
*
* @deprecated since sonata-project/admin-bundle 3.34, to be removed in 4.0.
*/
protected $persistFilters = false;
/**
* Array of routes related to this admin.
*
* @var RouteCollection
*/
protected $routes;
/**
* The subject only set in edit/update/create mode.
*
* @var object|null
*/
protected $subject;
/**
* Define a Collection of child admin, ie /admin/order/{id}/order-element/{childId}.
*
* @var array
*/
protected $children = [];
/**
* Reference the parent admin.
*
* @var AdminInterface|null
*/
protected $parent;
/**
* The base code route refer to the prefix used to generate the route name.
*
* NEXT_MAJOR: remove this attribute.
*
* @deprecated This attribute is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0
*
* @var string
*/
protected $baseCodeRoute = '';
/**
* NEXT_MAJOR: should be default array and private.
*
* @var string|array
*/
protected $parentAssociationMapping;
/**
* Reference the parent FieldDescription related to this admin
* only set for FieldDescription which is associated to an Sub Admin instance.
*
* @var FieldDescriptionInterface
*/
protected $parentFieldDescription;
/**
* If true then the current admin is part of the nested admin set (from the url).
*
* @var bool
*/
protected $currentChild = false;
/**
* The uniqid is used to avoid clashing with 2 admin related to the code
* ie: a Block linked to a Block.
*
* @var string
*/
protected $uniqid;
/**
* The Entity or Document manager.
*
* @var ModelManagerInterface
*/
protected $modelManager;
/**
* The current request object.
*
* @var Request|null
*/
protected $request;
/**
* The translator component.
*
* NEXT_MAJOR: remove this property
*
* @var \Symfony\Component\Translation\TranslatorInterface
*
* @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
*/
protected $translator;
/**
* The related form contractor.
*
* @var FormContractorInterface
*/
protected $formContractor;
/**
* The related list builder.
*
* @var ListBuilderInterface
*/
protected $listBuilder;
/**
* The related view builder.
*
* @var ShowBuilderInterface
*/
protected $showBuilder;
/**
* The related datagrid builder.
*
* @var DatagridBuilderInterface
*/
protected $datagridBuilder;
/**
* @var RouteBuilderInterface
*/
protected $routeBuilder;
/**
* The datagrid instance.
*
* @var DatagridInterface|null
*/
protected $datagrid;
/**
* The router instance.
*
* @var RouteGeneratorInterface|null
*/
protected $routeGenerator;
/**
* The generated breadcrumbs.
*
* NEXT_MAJOR : remove this property
*
* @var array
*/
protected $breadcrumbs = [];
/**
* @var SecurityHandlerInterface
*/
protected $securityHandler;
/**
* @var ValidatorInterface
*/
protected $validator;
/**
* The configuration pool.
*
* @var Pool
*/
protected $configurationPool;
/**
* @var ItemInterface
*/
protected $menu;
/**
* @var FactoryInterface
*/
protected $menuFactory;
/**
* @var array<string, bool>
*/
protected $loaded = [
'view_fields' => false,
'view_groups' => false,
'routes' => false,
'tab_menu' => false,
];
/**
* @var string[]
*/
protected $formTheme = [];
/**
* @var string[]
*/
protected $filterTheme = [];
/**
* @var array<string, string>
*
* @deprecated since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
*/
protected $templates = [];
/**
* @var AdminExtensionInterface[]
*/
protected $extensions = [];
/**
* @var LabelTranslatorStrategyInterface
*/
protected $labelTranslatorStrategy;
/**
* Setting to true will enable preview mode for
* the entity and show a preview button in the
* edit/create forms.
*
* @var bool
*/
protected $supportsPreviewMode = false;
/**
* Roles and permissions per role.
*
* @var array 'role' => ['permission', 'permission']
*/
protected $securityInformation = [];
protected $cacheIsGranted = [];
/**
* Action list for the search result.
*
* @var string[]
*/
protected $searchResultActions = ['edit', 'show'];
protected $listModes = [
'list' => [
'class' => 'fa fa-list fa-fw',
],
'mosaic' => [
'class' => self::MOSAIC_ICON_CLASS,
],
];
/**
* The Access mapping.
*
* @var array [action1 => requiredRole1, action2 => [requiredRole2, requiredRole3]]
*/
protected $accessMapping = [];
/**
* @var MutableTemplateRegistryInterface
*/
private $templateRegistry;
/**
* The class name managed by the admin class.
*
* @var string
*/
private $class;
/**
* The subclasses supported by the admin class.
*
* @var array<string, string>
*/
private $subClasses = [];
/**
* The list collection.
*
* @var FieldDescriptionCollection
*/
private $list;
/**
* @var FieldDescriptionCollection|null
*/
private $show;
/**
* @var Form|null
*/
private $form;
/**
* The cached base route name.
*
* @var string
*/
private $cachedBaseRouteName;
/**
* The cached base route pattern.
*
* @var string
*/
private $cachedBaseRoutePattern;
/**
* The form group disposition.
*
* NEXT_MAJOR: must have `[]` as default value and remove the possibility to
* hold boolean values.
*
* @var array|bool
*/
private $formGroups = false;
/**
* The form tabs disposition.
*
* NEXT_MAJOR: must have `[]` as default value and remove the possibility to
* hold boolean values.
*
* @var array|bool
*/
private $formTabs = false;
/**
* The view group disposition.
*
* NEXT_MAJOR: must have `[]` as default value and remove the possibility to
* hold boolean values.
*
* @var array|bool
*/
private $showGroups = false;
/**
* The view tab disposition.
*
* NEXT_MAJOR: must have `[]` as default value and remove the possibility to
* hold boolean values.
*
* @var array|bool
*/
private $showTabs = false;
/**
* The manager type to use for the admin.
*
* @var string
*/
private $managerType;
/**
* The breadcrumbsBuilder component.
*
* @var BreadcrumbsBuilderInterface
*/
private $breadcrumbsBuilder;
/**
* Component responsible for persisting filters.
*
* @var FilterPersisterInterface|null
*/
private $filterPersister;
/**
* @param string $code
* @param string $class
* @param string|null $baseControllerName
*/
public function __construct($code, $class, $baseControllerName = null)
{
if (!\is_string($code)) {
@trigger_error(sprintf(
'Passing other type than string as argument 1 for method %s() is deprecated since sonata-project/admin-bundle 3.65. It will accept only string in version 4.0.',
__METHOD__
), E_USER_DEPRECATED);
}
$this->code = $code;
if (!\is_string($class)) {
@trigger_error(sprintf(
'Passing other type than string as argument 2 for method %s() is deprecated since sonata-project/admin-bundle 3.65. It will accept only string in version 4.0.',
__METHOD__
), E_USER_DEPRECATED);
}
$this->class = $class;
if (null !== $baseControllerName && !\is_string($baseControllerName)) {
@trigger_error(sprintf(
'Passing other type than string or null as argument 3 for method %s() is deprecated since sonata-project/admin-bundle 3.65. It will accept only string and null in version 4.0.',
__METHOD__
), E_USER_DEPRECATED);
}
$this->baseControllerName = $baseControllerName;
$this->predefinePerPageOptions();
$this->datagridValues['_per_page'] = $this->maxPerPage;
}
/**
* {@inheritdoc}
*/
public function getExportFormats()
{
return [
'json', 'xml', 'csv', 'xls',
];
}
/**
* @return array
*/
public function getExportFields()
{
$fields = $this->getModelManager()->getExportFields($this->getClass());
foreach ($this->getExtensions() as $extension) {
if (method_exists($extension, 'configureExportFields')) {
$fields = $extension->configureExportFields($this, $fields);
}
}
return $fields;
}
public function getDataSourceIterator()
{
$datagrid = $this->getDatagrid();
$datagrid->buildPager();
$fields = [];
foreach ($this->getExportFields() as $key => $field) {
$label = $this->getTranslationLabel($field, 'export', 'label');
$transLabel = $this->trans($label);
// NEXT_MAJOR: Remove this hack, because all field labels will be translated with the major release
// No translation key exists
if ($transLabel === $label) {
$fields[$key] = $field;
} else {
$fields[$transLabel] = $field;
}
}
return $this->getModelManager()->getDataSourceIterator($datagrid, $fields);
}
public function validate(ErrorElement $errorElement, $object)
{
}
/**
* define custom variable.
*/
public function initialize()
{
if (!$this->classnameLabel) {
/* NEXT_MAJOR: remove cast to string, null is not supposed to be
supported but was documented as such */
$this->classnameLabel = substr(
(string) $this->getClass(),
strrpos((string) $this->getClass(), '\\') + 1
);
}
// NEXT_MAJOR: Remove this line.
$this->baseCodeRoute = $this->getCode();
$this->configure();
}
public function configure()
{
}
public function update($object)
{
$this->preUpdate($object);
foreach ($this->extensions as $extension) {
$extension->preUpdate($this, $object);
}
$result = $this->getModelManager()->update($object);
// BC compatibility
if (null !== $result) {
$object = $result;
}
$this->postUpdate($object);
foreach ($this->extensions as $extension) {
$extension->postUpdate($this, $object);
}
return $object;
}
public function create($object)
{
$this->prePersist($object);
foreach ($this->extensions as $extension) {
$extension->prePersist($this, $object);
}
$result = $this->getModelManager()->create($object);
// BC compatibility
if (null !== $result) {
$object = $result;
}
$this->postPersist($object);
foreach ($this->extensions as $extension) {
$extension->postPersist($this, $object);
}
$this->createObjectSecurity($object);
return $object;
}
public function delete($object)
{
$this->preRemove($object);
foreach ($this->extensions as $extension) {
$extension->preRemove($this, $object);
}
$this->getSecurityHandler()->deleteObjectSecurity($this, $object);
$this->getModelManager()->delete($object);
$this->postRemove($object);
foreach ($this->extensions as $extension) {
$extension->postRemove($this, $object);
}
}
/**
* @param object $object
*/
public function preValidate($object)
{
}
public function preUpdate($object)
{
}
public function postUpdate($object)
{
}
public function prePersist($object)
{
}
public function postPersist($object)
{
}
public function preRemove($object)
{
}
public function postRemove($object)
{
}
public function preBatchAction($actionName, ProxyQueryInterface $query, array &$idx, $allElements)
{
}
public function getFilterParameters()
{
$parameters = [];
// build the values array
if ($this->hasRequest()) {
$filters = $this->request->query->get('filter', []);
if (isset($filters['_page'])) {
$filters['_page'] = (int) $filters['_page'];
}
if (isset($filters['_per_page'])) {
$filters['_per_page'] = (int) $filters['_per_page'];
}
// if filter persistence is configured
// NEXT_MAJOR: remove `$this->persistFilters !== false` from the condition
if (false !== $this->persistFilters && null !== $this->filterPersister) {
// if reset filters is asked, remove from storage
if ('reset' === $this->request->query->get('filters')) {
$this->filterPersister->reset($this->getCode());
}
// if no filters, fetch from storage
// otherwise save to storage
if (empty($filters)) {
$filters = $this->filterPersister->get($this->getCode());
} else {
$this->filterPersister->set($this->getCode(), $filters);
}
}
$parameters = array_merge(
$this->getModelManager()->getDefaultSortValues($this->getClass()),
$this->datagridValues,
$this->getDefaultFilterValues(),
$filters
);
if (!$this->determinedPerPageValue($parameters['_per_page'])) {
$parameters['_per_page'] = $this->maxPerPage;
}
// always force the parent value
if ($this->isChild() && $this->getParentAssociationMapping()) {
$name = str_replace('.', '__', $this->getParentAssociationMapping());
$parameters[$name] = ['value' => $this->request->get($this->getParent()->getIdParameter())];
}
}
return $parameters;
}
public function buildDatagrid()
{
if ($this->datagrid) {
return;
}
$filterParameters = $this->getFilterParameters();
// transform _sort_by from a string to a FieldDescriptionInterface for the datagrid.
if (isset($filterParameters['_sort_by']) && \is_string($filterParameters['_sort_by'])) {
if ($this->hasListFieldDescription($filterParameters['_sort_by'])) {
$filterParameters['_sort_by'] = $this->getListFieldDescription($filterParameters['_sort_by']);
} else {
$filterParameters['_sort_by'] = $this->getModelManager()->getNewFieldDescriptionInstance(
$this->getClass(),
$filterParameters['_sort_by'],
[]
);
$this->getListBuilder()->buildField(null, $filterParameters['_sort_by'], $this);
}
}
// initialize the datagrid
$this->datagrid = $this->getDatagridBuilder()->getBaseDatagrid($this, $filterParameters);
$this->datagrid->getPager()->setMaxPageLinks($this->maxPageLinks);
$mapper = new DatagridMapper($this->getDatagridBuilder(), $this->datagrid, $this);
// build the datagrid filter
$this->configureDatagridFilters($mapper);
// ok, try to limit to add parent filter
if ($this->isChild() && $this->getParentAssociationMapping() && !$mapper->has($this->getParentAssociationMapping())) {
$mapper->add($this->getParentAssociationMapping(), null, [
'show_filter' => false,
'label' => false,
'field_type' => ModelHiddenType::class,
'field_options' => [
'model_manager' => $this->getModelManager(),
],
'operator_type' => HiddenType::class,
], null, null, [
'admin_code' => $this->getParent()->getCode(),
]);
}
foreach ($this->getExtensions() as $extension) {
$extension->configureDatagridFilters($mapper);
}
}
/**
* Returns the name of the parent related field, so the field can be use to set the default
* value (ie the parent object) or to filter the object.
*
* @throws \InvalidArgumentException
*
* @return string|null
*/
public function getParentAssociationMapping()
{
// NEXT_MAJOR: remove array check
if (\is_array($this->parentAssociationMapping) && $this->isChild()) {
$parent = $this->getParent()->getCode();
if (\array_key_exists($parent, $this->parentAssociationMapping)) {
return $this->parentAssociationMapping[$parent];
}
throw new \InvalidArgumentException(sprintf(
"There's no association between %s and %s.",
$this->getCode(),
$this->getParent()->getCode()
));
}
// NEXT_MAJOR: remove this line
return $this->parentAssociationMapping;
}
/**
* @param string $code
* @param string $value
*/
final public function addParentAssociationMapping($code, $value)
{
$this->parentAssociationMapping[$code] = $value;
}
/**
* Returns the baseRoutePattern used to generate the routing information.
*
* @throws \RuntimeException
*
* @return string the baseRoutePattern used to generate the routing information
*/
public function getBaseRoutePattern()
{
if (null !== $this->cachedBaseRoutePattern) {
return $this->cachedBaseRoutePattern;
}
if ($this->isChild()) { // the admin class is a child, prefix it with the parent route pattern
$baseRoutePattern = $this->baseRoutePattern;
if (!$this->baseRoutePattern) {
preg_match(self::CLASS_REGEX, $this->class, $matches);
if (!$matches) {
throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', static::class));
}
$baseRoutePattern = $this->urlize($matches[5], '-');
}
$this->cachedBaseRoutePattern = sprintf(
'%s/%s/%s',
$this->getParent()->getBaseRoutePattern(),
$this->getParent()->getRouterIdParameter(),
$baseRoutePattern
);
} elseif ($this->baseRoutePattern) {
$this->cachedBaseRoutePattern = $this->baseRoutePattern;
} else {
preg_match(self::CLASS_REGEX, $this->class, $matches);
if (!$matches) {
throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', static::class));
}
$this->cachedBaseRoutePattern = sprintf(
'/%s%s/%s',
empty($matches[1]) ? '' : $this->urlize($matches[1], '-').'/',
$this->urlize($matches[3], '-'),
$this->urlize($matches[5], '-')
);
}
return $this->cachedBaseRoutePattern;
}
/**
* Returns the baseRouteName used to generate the routing information.
*
* @throws \RuntimeException
*
* @return string the baseRouteName used to generate the routing information
*/
public function getBaseRouteName()
{
if (null !== $this->cachedBaseRouteName) {
return $this->cachedBaseRouteName;
}
if ($this->isChild()) { // the admin class is a child, prefix it with the parent route name
$baseRouteName = $this->baseRouteName;
if (!$this->baseRouteName) {
preg_match(self::CLASS_REGEX, $this->class, $matches);
if (!$matches) {
throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', static::class));
}
$baseRouteName = $this->urlize($matches[5]);
}
$this->cachedBaseRouteName = sprintf(
'%s_%s',
$this->getParent()->getBaseRouteName(),
$baseRouteName
);
} elseif ($this->baseRouteName) {
$this->cachedBaseRouteName = $this->baseRouteName;
} else {
preg_match(self::CLASS_REGEX, $this->class, $matches);
if (!$matches) {
throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', static::class));
}
$this->cachedBaseRouteName = sprintf(
'admin_%s%s_%s',
empty($matches[1]) ? '' : $this->urlize($matches[1]).'_',
$this->urlize($matches[3]),
$this->urlize($matches[5])
);
}
return $this->cachedBaseRouteName;
}
/**
* urlize the given word.
*
* @param string $word
* @param string $sep the separator
*
* @return string
*/
public function urlize($word, $sep = '_')
{
return strtolower(preg_replace('/[^a-z0-9_]/i', $sep.'$1', $word));
}
public function getClass()
{
if ($this->hasActiveSubClass()) {
if ($this->hasParentFieldDescription()) {
throw new \RuntimeException('Feature not implemented: an embedded admin cannot have subclass');
}
$subClass = $this->getRequest()->query->get('subclass');
if (!$this->hasSubClass($subClass)) {
throw new \RuntimeException(sprintf('Subclass "%s" is not defined.', $subClass));
}
return $this->getSubClass($subClass);
}
// see https://github.com/sonata-project/SonataCoreBundle/commit/247eeb0a7ca7211142e101754769d70bc402a5b4
if ($this->subject && \is_object($this->subject)) {
return ClassUtils::getClass($this->subject);
}
return $this->class;
}
public function getSubClasses()
{
return $this->subClasses;
}
/**
* NEXT_MAJOR: remove this method.
*/
public function addSubClass($subClass)
{
@trigger_error(sprintf(
'Method "%s" is deprecated since sonata-project/admin-bundle 3.30 and will be removed in 4.0.',
__METHOD__
), E_USER_DEPRECATED);
if (!\in_array($subClass, $this->subClasses, true)) {
$this->subClasses[] = $subClass;
}
}
public function setSubClasses(array $subClasses)
{
$this->subClasses = $subClasses;
}
public function hasSubClass($name)
{
return isset($this->subClasses[$name]);
}
public function hasActiveSubClass()
{
if (\count($this->subClasses) > 0 && $this->request) {
return null !== $this->getRequest()->query->get('subclass');
}
return false;
}
public function getActiveSubClass()
{
if (!$this->hasActiveSubClass()) {
@trigger_error(sprintf(
'Calling %s() when there is no active subclass is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0. '.
'Use %s::hasActiveSubClass() to know if there is an active subclass.',
__METHOD__,
__CLASS__
), E_USER_DEPRECATED);
// NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
// throw new \LogicException(sprintf(
// 'Admin "%s" has no active subclass.',
// static::class
// ));
return null;
}
return $this->getSubClass($this->getActiveSubclassCode());
}
public function getActiveSubclassCode()
{
if (!$this->hasActiveSubClass()) {
@trigger_error(sprintf(
'Calling %s() when there is no active subclass is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0. '.
'Use %s::hasActiveSubClass() to know if there is an active subclass.',
__METHOD__,
__CLASS__
), E_USER_DEPRECATED);
// NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
// throw new \LogicException(sprintf(
// 'Admin "%s" has no active subclass.',
// static::class
// ));
return null;
}
$subClass = $this->getRequest()->query->get('subclass');
if (!$this->hasSubClass($subClass)) {
@trigger_error(sprintf(
'Calling %s() when there is no active subclass is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0. '.
'Use %s::hasActiveSubClass() to know if there is an active subclass.',
__METHOD__,
__CLASS__
), E_USER_DEPRECATED);
// NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare string as return type
// throw new \LogicException(sprintf(
// 'Admin "%s" has no active subclass.',
// static::class
// ));
return null;
}
return $subClass;
}
public function getBatchActions()
{
$actions = [];
if ($this->hasRoute('delete') && $this->hasAccess('delete')) {
$actions['delete'] = [
'label' => 'action_delete',
'translation_domain' => 'SonataAdminBundle',
'ask_confirmation' => true, // by default always true
];
}
$actions = $this->configureBatchActions($actions);
foreach ($this->getExtensions() as $extension) {
// NEXT_MAJOR: remove method check
if (method_exists($extension, 'configureBatchActions')) {
$actions = $extension->configureBatchActions($this, $actions);
}
}
foreach ($actions as $name => &$action) {
if (!\array_key_exists('label', $action)) {
$action['label'] = $this->getTranslationLabel($name, 'batch', 'label');
}
if (!\array_key_exists('translation_domain', $action)) {
$action['translation_domain'] = $this->getTranslationDomain();
}
}
return $actions;
}
public function getRoutes()
{
$this->buildRoutes();
return $this->routes;
}
public function getRouterIdParameter()
{
return '{'.$this->getIdParameter().'}';
}
public function getIdParameter()
{
$parameter = 'id';
for ($i = 0; $i < $this->getChildDepth(); ++$i) {
$parameter = 'child'.ucfirst($parameter);
}
return $parameter;
}
public function hasRoute($name)
{
if (!$this->routeGenerator) {
throw new \RuntimeException('RouteGenerator cannot be null');
}
return $this->routeGenerator->hasAdminRoute($this, $name);
}
/**
* @param string $name
* @param string|null $adminCode
*
* @return bool
*/
public function isCurrentRoute($name, $adminCode = null)
{
if (!$this->hasRequest()) {
return false;
}
$request = $this->getRequest();
$route = $request->get('_route');
if ($adminCode) {
$admin = $this->getConfigurationPool()->getAdminByAdminCode($adminCode);
} else {
$admin = $this;
}
if (!$admin) {
return false;
}
return ($admin->getBaseRouteName().'_'.$name) === $route;
}
public function generateObjectUrl($name, $object, array $parameters = [], $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
{
$parameters['id'] = $this->getUrlSafeIdentifier($object);
return $this->generateUrl($name, $parameters, $referenceType);
}
public function generateUrl($name, array $parameters = [], $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
{
return $this->routeGenerator->generateUrl($this, $name, $parameters, $referenceType);
}
public function generateMenuUrl($name, array $parameters = [], $referenceType = RoutingUrlGeneratorInterface::ABSOLUTE_PATH)
{
return $this->routeGenerator->generateMenuUrl($this, $name, $parameters, $referenceType);
}
final public function setTemplateRegistry(MutableTemplateRegistryInterface $templateRegistry)
{
$this->templateRegistry = $templateRegistry;
}
/**
* @param array<string, string> $templates
*/
public function setTemplates(array $templates)
{
// NEXT_MAJOR: Remove this line
$this->templates = $templates;
$this->getTemplateRegistry()->setTemplates($templates);
}
/**
* @param string $name
* @param string $template
*/
public function setTemplate($name, $template)
{
// NEXT_MAJOR: Remove this line
$this->templates[$name] = $template;
$this->getTemplateRegistry()->setTemplate($name, $template);
}
/**
* @deprecated since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
*
* @return array<string, string>
*/
public function getTemplates()
{
return $this->getTemplateRegistry()->getTemplates();
}
/**
* @deprecated since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead
*
* @param string $name
*
* @return string|null
*/
public function getTemplate($name)
{
return $this->getTemplateRegistry()->getTemplate($name);
}
public function getNewInstance()
{
$object = $this->getModelManager()->getModelInstance($this->getClass());
foreach ($this->getExtensions() as $extension) {
$extension->alterNewInstance($this, $object);
}
return $object;
}
public function getFormBuilder()
{
$this->formOptions['data_class'] = $this->getClass();
$formBuilder = $this->getFormContractor()->getFormBuilder(
$this->getUniqid(),
$this->formOptions
);
$this->defineFormBuilder($formBuilder);
return $formBuilder;
}
/**
* This method is being called by the main admin class and the child class,
* the getFormBuilder is only call by the main admin class.
*/
public function defineFormBuilder(FormBuilderInterface $formBuilder)
{
if (!$this->hasSubject()) {
@trigger_error(sprintf(
'Calling %s() when there is no subject is deprecated since sonata-project/admin-bundle 3.65 and will throw an exception in 4.0. '.
'Use %s::setSubject() to set the subject.',
__METHOD__,
__CLASS__
), E_USER_DEPRECATED);
// NEXT_MAJOR : remove the previous `trigger_error()` call and uncomment the following exception
// throw new \LogicException(sprintf(
// 'Admin "%s" has no subject.',
// static::class
// ));
}
$mapper = new FormMapper($this->getFormContractor(), $formBuilder, $this);
$this->configureFormFields($mapper);
foreach ($this->getExtensions() as $extension) {
$extension->configureFormFields($mapper);
}
$this->attachInlineValidator();
}
public function attachAdminClass(FieldDescriptionInterface $fieldDescription)
{
$pool = $this->getConfigurationPool();
$adminCode = $fieldDescription->getOption('admin_code');
if (null !== $adminCode) {
$admin = $pool->getAdminByAdminCode($adminCode);
} else {
$admin = $pool->getAdminByClass($fieldDescription->getTargetEntity());
}
if (!$admin) {
return;
}
if ($this->hasRequest()) {
$admin->setRequest($this->getRequest());
}
$fieldDescription->setAssociationAdmin($admin);
}
public function getObject($id)
{
$object = $this->getModelManager()->find($this->getClass(), $id);
foreach ($this->getExtensions() as $extension) {
$extension->alterObject($this, $object);
}
return $object;
}
public function getForm()
{
$this->buildForm();
return $this->form;
}
public function getList()
{
$this->buildList();
return $this->list;
}
/**
* @final since sonata-project/admin-bundle 3.63.0
*/
public function createQuery($context = 'list')
{
if (\func_num_args() > 0) {
@trigger_error(
'The $context argument of '.__METHOD__.' is deprecated since 3.3, to be removed in 4.0.',
E_USER_DEPRECATED
);
}
$query = $this->getModelManager()->createQuery($this->getClass());
$query = $this->configureQuery($query);
foreach ($this->extensions as $extension) {
$extension->configureQuery($this, $query, $context);
}
return $query;
}
public function getDatagrid()
{
$this->buildDatagrid();
return $this->datagrid;
}
public function buildTabMenu($action, ?AdminInterface $childAdmin = null)
{
if ($this->loaded['tab_menu']) {
return $this->menu;
}
$this->loaded['tab_menu'] = true;
$menu = $this->menuFactory->createItem('root');
$menu->setChildrenAttribute('class', 'nav navbar-nav');
$menu->setExtra('translation_domain', $this->translationDomain);
// Prevents BC break with KnpMenuBundle v1.x
if (method_exists($menu, 'setCurrentUri')) {
$menu->setCurrentUri($this->getRequest()->getBaseUrl().$this->getRequest()->getPathInfo());
}
$this->configureTabMenu($menu, $action, $childAdmin);
foreach ($this->getExtensions() as $extension) {
$extension->configureTabMenu($this, $menu, $action, $childAdmin);
}
$this->menu = $menu;
return $this->menu;
}
public function buildSideMenu($action, ?AdminInterface $childAdmin = null)
{
return $this->buildTabMenu($action, $childAdmin);
}
/**
* @param string $action
*
* @return ItemInterface
*/
public function getSideMenu($action, ?AdminInterface $childAdmin = null)
{
if ($this->isChild()) {
return $this->getParent()->getSideMenu($action, $this);
}
$this->buildSideMenu($action, $childAdmin);
return $this->menu;
}
/**
* Returns the root code.
*
* @return string the root code
*/
public function getRootCode()
{
return $this->getRoot()->getCode();
}
/**
* Returns the master admin.
*
* @return AbstractAdmin the root admin class
*/
public function getRoot()
{
if (!$this->hasParentFieldDescription()) {
return $this;
}
return $this->getParentFieldDescription()->getAdmin()->getRoot();
}
public function setBaseControllerName($baseControllerName)
{
$this->baseControllerName = $baseControllerName;
}
public function getBaseControllerName()
{
return $this->baseControllerName;
}
/**
* @param string $label
*/
public function setLabel($label)
{
$this->label = $label;
}
public function getLabel()
{
return $this->label;
}
/**
* @param bool $persist
*
* NEXT_MAJOR: remove this method
*
* @deprecated since sonata-project/admin-bundle 3.34, to be removed in 4.0.
*/
public function setPersistFilters($persist)
{
@trigger_error(
'The '.__METHOD__.' method is deprecated since version 3.34 and will be removed in 4.0.',
E_USER_DEPRECATED
);
$this->persistFilters = $persist;
}
public function setFilterPersister(?FilterPersisterInterface $filterPersister = null)
{
$this->filterPersister = $filterPersister;
// NEXT_MAJOR remove the deprecated property will be removed. Needed for persisted filter condition.
$this->persistFilters = true;
}
/**
* @param int $maxPerPage
*/
public function setMaxPerPage($maxPerPage)
{
$this->maxPerPage = $maxPerPage;
}
/**
* @return int
*/
public function getMaxPerPage()
{
return $this->maxPerPage;
}
/**
* @param int $maxPageLinks
*/
public function setMaxPageLinks($maxPageLinks)
{
$this->maxPageLinks = $maxPageLinks;
}
/**
* @return int
*/
public function getMaxPageLinks()
{
return $this->maxPageLinks;
}
public function getFormGroups()
{
if (!\is_array($this->formGroups) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
@trigger_error(sprintf(
'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
__METHOD__
), E_USER_DEPRECATED);
}
return $this->formGroups;
}
public function setFormGroups(array $formGroups)
{
$this->formGroups = $formGroups;
}
public function removeFieldFromFormGroup($key)
{
foreach ($this->formGroups as $name => $formGroup) {
unset($this->formGroups[$name]['fields'][$key]);
if (empty($this->formGroups[$name]['fields'])) {
unset($this->formGroups[$name]);
}
}
}
/**
* @param string $group
*/
public function reorderFormGroup($group, array $keys)
{
// NEXT_MAJOR: Remove the argument "sonata_deprecation_mute" in the following call.
$formGroups = $this->getFormGroups('sonata_deprecation_mute');
$formGroups[$group]['fields'] = array_merge(array_flip($keys), $formGroups[$group]['fields']);
$this->setFormGroups($formGroups);
}
public function getFormTabs()
{
if (!\is_array($this->formTabs) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
@trigger_error(sprintf(
'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
__METHOD__
), E_USER_DEPRECATED);
}
return $this->formTabs;
}
public function setFormTabs(array $formTabs)
{
$this->formTabs = $formTabs;
}
public function getShowTabs()
{
if (!\is_array($this->showTabs) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
@trigger_error(sprintf(
'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
__METHOD__
), E_USER_DEPRECATED);
}
return $this->showTabs;
}
public function setShowTabs(array $showTabs)
{
$this->showTabs = $showTabs;
}
public function getShowGroups()
{
if (!\is_array($this->showGroups) && 'sonata_deprecation_mute' !== (\func_get_args()[0] ?? null)) {
@trigger_error(sprintf(
'Returning other type than array in method %s() is deprecated since sonata-project/admin-bundle 3.65. It will return only array in version 4.0.',
__METHOD__
), E_USER_DEPRECATED);
}
return $this->showGroups;
}
public function setShowGroups(array $showGroups)
{
$this->showGroups = $showGroups;
}
public function reorderShowGroup($group, array $keys)
{
// NEXT_MAJOR: Remove the argument "sonata_deprecation_mute" in the following call.
$showGroups = $this->getShowGroups('sonata_deprecation_mute');
$showGroups[$group]['fields'] = array_merge(array_flip($keys), $showGroups[$group]['fields']);
$this->setShowGroups($showGroups);
}
public function setParentFieldDescription(FieldDescriptionInterface $parentFieldDescription)
{
$this->parentFieldDescription = $parentFieldDescription;
}
public function getParentFieldDescription()
{
if (!$this->hasParentFieldDescription()) {
@trigger_error(sprintf(
'Calling %s() when there is no parent field description is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
'Use %s::hasParentFieldDescription() to know if there is a parent field description.',
__METHOD__,
__CLASS__
), E_USER_DEPRECATED);
// NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
// throw new \LogicException(sprintf(
// 'Admin "%s" has no parent field description.',
// static::class
// ));
return null;
}
return $this->parentFieldDescription;
}
public function hasParentFieldDescription()
{
return $this->parentFieldDescription instanceof FieldDescriptionInterface;
}
public function setSubject($subject)
{
if (\is_object($subject) && !is_a($subject, $this->getClass(), true)) {
$message = <<<'EOT'
You are trying to set entity an instance of "%s",
which is not the one registered with this admin class ("%s").
This is deprecated since 3.5 and will no longer be supported in 4.0.
EOT;
@trigger_error(
sprintf($message, \get_class($subject), $this->getClass()),
E_USER_DEPRECATED
); // NEXT_MAJOR : throw an exception instead
}
$this->subject = $subject;
}
public function getSubject()
{
if (!$this->hasSubject()) {
@trigger_error(sprintf(
'Calling %s() when there is no subject is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
'Use %s::hasSubject() to know if there is a subject.',
__METHOD__,
__CLASS__
), E_USER_DEPRECATED);
// NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and update the return type
// throw new \LogicException(sprintf(
// 'Admin "%s" has no subject.',
// static::class
// ));
return null;
}
return $this->subject;
}
public function hasSubject()
{
if (null === $this->subject && $this->hasRequest() && !$this->hasParentFieldDescription()) {
$id = $this->request->get($this->getIdParameter());
if (null !== $id) {
$this->subject = $this->getObject($id);
}
}
return null !== $this->subject;
}
public function getFormFieldDescriptions()
{
$this->buildForm();
return $this->formFieldDescriptions;
}
public function getFormFieldDescription($name)
{
return $this->hasFormFieldDescription($name) ? $this->formFieldDescriptions[$name] : null;
}
/**
* Returns true if the admin has a FieldDescription with the given $name.
*
* @param string $name
*
* @return bool
*/
public function hasFormFieldDescription($name)
{
return \array_key_exists($name, $this->formFieldDescriptions) ? true : false;
}
public function addFormFieldDescription($name, FieldDescriptionInterface $fieldDescription)
{
$this->formFieldDescriptions[$name] = $fieldDescription;
}
/**
* remove a FieldDescription.
*
* @param string $name
*/
public function removeFormFieldDescription($name)
{
unset($this->formFieldDescriptions[$name]);
}
/**
* build and return the collection of form FieldDescription.
*
* @return array collection of form FieldDescription
*/
public function getShowFieldDescriptions()
{
$this->buildShow();
return $this->showFieldDescriptions;
}
/**
* Returns the form FieldDescription with the given $name.
*
* @param string $name
*
* @return FieldDescriptionInterface
*/
public function getShowFieldDescription($name)
{
$this->buildShow();
return $this->hasShowFieldDescription($name) ? $this->showFieldDescriptions[$name] : null;
}
public function hasShowFieldDescription($name)
{
return \array_key_exists($name, $this->showFieldDescriptions);
}
public function addShowFieldDescription($name, FieldDescriptionInterface $fieldDescription)
{
$this->showFieldDescriptions[$name] = $fieldDescription;
}
public function removeShowFieldDescription($name)
{
unset($this->showFieldDescriptions[$name]);
}
public function getListFieldDescriptions()
{
$this->buildList();
return $this->listFieldDescriptions;
}
public function getListFieldDescription($name)
{
if (!$this->hasListFieldDescription($name)) {
@trigger_error(sprintf(
'Calling %s() when there is no list field description is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
'Use %s::hasListFieldDescription(\'%s\') to know if there is a list field description.',
__METHOD__,
__CLASS__,
$name
), E_USER_DEPRECATED);
// NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FieldDescriptionInterface as return type
// throw new \LogicException(sprintf(
// 'Admin "%s" has no list field description for %s.',
// static::class,
// $name
// ));
return null;
}
return $this->listFieldDescriptions[$name];
}
public function hasListFieldDescription($name)
{
$this->buildList();
return \array_key_exists($name, $this->listFieldDescriptions) ? true : false;
}
public function addListFieldDescription($name, FieldDescriptionInterface $fieldDescription)
{
$this->listFieldDescriptions[$name] = $fieldDescription;
}
public function removeListFieldDescription($name)
{
unset($this->listFieldDescriptions[$name]);
}
public function getFilterFieldDescription($name)
{
return $this->hasFilterFieldDescription($name) ? $this->filterFieldDescriptions[$name] : null;
}
public function hasFilterFieldDescription($name)
{
return \array_key_exists($name, $this->filterFieldDescriptions) ? true : false;
}
public function addFilterFieldDescription($name, FieldDescriptionInterface $fieldDescription)
{
$this->filterFieldDescriptions[$name] = $fieldDescription;
}
public function removeFilterFieldDescription($name)
{
unset($this->filterFieldDescriptions[$name]);
}
public function getFilterFieldDescriptions()
{
$this->buildDatagrid();
return $this->filterFieldDescriptions;
}
public function addChild(AdminInterface $child)
{
$parentAdmin = $this;
while ($parentAdmin->isChild() && $parentAdmin->getCode() !== $child->getCode()) {
$parentAdmin = $parentAdmin->getParent();
}
if ($parentAdmin->getCode() === $child->getCode()) {
throw new \RuntimeException(sprintf(
'Circular reference detected! The child admin `%s` is already in the parent tree of the `%s` admin.',
$child->getCode(),
$this->getCode()
));
}
$this->children[$child->getCode()] = $child;
$child->setParent($this);
// NEXT_MAJOR: remove $args and add $field parameter to this function on next Major
$args = \func_get_args();
if (isset($args[1])) {
$child->addParentAssociationMapping($this->getCode(), $args[1]);
} else {
@trigger_error(
'Calling "addChild" without second argument is deprecated since'
.' sonata-project/admin-bundle 3.35 and will not be allowed in 4.0.',
E_USER_DEPRECATED
);
}
}
public function hasChild($code)
{
return isset($this->children[$code]);
}
public function getChildren()
{
return $this->children;
}
public function getChild($code)
{
return $this->hasChild($code) ? $this->children[$code] : null;
}
public function setParent(AdminInterface $parent)
{
$this->parent = $parent;
}
public function getParent()
{
if (!$this->isChild()) {
@trigger_error(sprintf(
'Calling %s() when there is no parent is deprecated since sonata-project/admin-bundle 3.66 and will throw an exception in 4.0. '.
'Use %s::isChild() to know if there is a parent.',
__METHOD__,
__CLASS__
), E_USER_DEPRECATED);
// NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare AdminInterface as return type
// throw new \LogicException(sprintf(
// 'Admin "%s" has no parent.',
// static::class
// ));
return null;
}
return $this->parent;
}
final public function getRootAncestor()
{
$parent = $this;
while ($parent->isChild()) {
$parent = $parent->getParent();
}
return $parent;
}
final public function getChildDepth()
{
$parent = $this;
$depth = 0;
while ($parent->isChild()) {
$parent = $parent->getParent();
++$depth;
}
return $depth;
}
final public function getCurrentLeafChildAdmin()
{
$child = $this->getCurrentChildAdmin();
if (null === $child) {
return null;
}
for ($c = $child; null !== $c; $c = $child->getCurrentChildAdmin()) {
$child = $c;
}
return $child;
}
public function isChild()
{
return $this->parent instanceof AdminInterface;
}
/**
* Returns true if the admin has children, false otherwise.
*
* @return bool if the admin has children
*/
public function hasChildren()
{
return \count($this->children) > 0;
}
public function setUniqid($uniqid)
{
$this->uniqid = $uniqid;
}
public function getUniqid()
{
if (!$this->uniqid) {
$this->uniqid = 's'.uniqid();
}
return $this->uniqid;
}
/**
* Returns the classname label.
*
* @return string the classname label
*/
public function getClassnameLabel()
{
return $this->classnameLabel;
}
public function getPersistentParameters()
{
$parameters = [];
foreach ($this->getExtensions() as $extension) {
$params = $extension->getPersistentParameters($this);
if (!\is_array($params)) {
throw new \RuntimeException(sprintf('The %s::getPersistentParameters must return an array', \get_class($extension)));
}
$parameters = array_merge($parameters, $params);
}
return $parameters;
}
/**
* @param string $name
*
* @return mixed|null
*/
public function getPersistentParameter($name)
{
$parameters = $this->getPersistentParameters();
return $parameters[$name] ?? null;
}
public function getBreadcrumbs($action)
{
@trigger_error(
'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
' Use Sonata\AdminBundle\Admin\BreadcrumbsBuilder::getBreadcrumbs instead.',
E_USER_DEPRECATED
);
return $this->getBreadcrumbsBuilder()->getBreadcrumbs($this, $action);
}
/**
* Generates the breadcrumbs array.
*
* Note: the method will be called by the top admin instance (parent => child)
*
* @param string $action
*
* @return array
*/
public function buildBreadcrumbs($action, ?ItemInterface $menu = null)
{
@trigger_error(
'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.',
E_USER_DEPRECATED
);
if (isset($this->breadcrumbs[$action])) {
return $this->breadcrumbs[$action];
}
return $this->breadcrumbs[$action] = $this->getBreadcrumbsBuilder()
->buildBreadcrumbs($this, $action, $menu);
}
/**
* NEXT_MAJOR : remove this method.
*
* @return BreadcrumbsBuilderInterface
*/
final public function getBreadcrumbsBuilder()
{
@trigger_error(
'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
' Use the sonata.admin.breadcrumbs_builder service instead.',
E_USER_DEPRECATED
);
if (null === $this->breadcrumbsBuilder) {
$this->breadcrumbsBuilder = new BreadcrumbsBuilder(
$this->getConfigurationPool()->getContainer()->getParameter('sonata.admin.configuration.breadcrumbs')
);
}
return $this->breadcrumbsBuilder;
}
/**
* NEXT_MAJOR : remove this method.
*
* @return AbstractAdmin
*/
final public function setBreadcrumbsBuilder(BreadcrumbsBuilderInterface $value)
{
@trigger_error(
'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
' Use the sonata.admin.breadcrumbs_builder service instead.',
E_USER_DEPRECATED
);
$this->breadcrumbsBuilder = $value;
return $this;
}
public function setCurrentChild($currentChild)
{
$this->currentChild = $currentChild;
}
/**
* NEXT_MAJOR: Remove this method.
*
* @deprecated since sonata-project/admin-bundle 3.65, to be removed in 4.0
*/
public function getCurrentChild()
{
@trigger_error(
sprintf(
'The %s() method is deprecated since version 3.65 and will be removed in 4.0. Use %s::isCurrentChild() instead.',
__METHOD__,
__CLASS__
),
E_USER_DEPRECATED
);
return $this->currentChild;
}
public function isCurrentChild(): bool
{
return $this->currentChild;
}
/**
* Returns the current child admin instance.
*
* @return AdminInterface|null the current child admin instance
*/
public function getCurrentChildAdmin()
{
foreach ($this->children as $children) {
if ($children->isCurrentChild()) {
return $children;
}
}
return null;
}
public function trans($id, array $parameters = [], $domain = null, $locale = null)
{
@trigger_error(
'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
E_USER_DEPRECATED
);
$domain = $domain ?: $this->getTranslationDomain();
return $this->translator->trans($id, $parameters, $domain, $locale);
}
/**
* Translate a message id.
*
* NEXT_MAJOR: remove this method
*
* @param string $id
* @param int $count
* @param string|null $domain
* @param string|null $locale
*
* @return string the translated string
*
* @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
*/
public function transChoice($id, $count, array $parameters = [], $domain = null, $locale = null)
{
@trigger_error(
'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
E_USER_DEPRECATED
);
$domain = $domain ?: $this->getTranslationDomain();
return $this->translator->transChoice($id, $count, $parameters, $domain, $locale);
}
public function setTranslationDomain($translationDomain)
{
$this->translationDomain = $translationDomain;
}
public function getTranslationDomain()
{
return $this->translationDomain;
}
/**
* {@inheritdoc}
*
* NEXT_MAJOR: remove this method
*
* @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
*/
public function setTranslator(TranslatorInterface $translator)
{
$args = \func_get_args();
if (isset($args[1]) && $args[1]) {
@trigger_error(
'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
E_USER_DEPRECATED
);
}
$this->translator = $translator;
}
/**
* {@inheritdoc}
*
* NEXT_MAJOR: remove this method
*
* @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0
*/
public function getTranslator()
{
@trigger_error(
'The '.__METHOD__.' method is deprecated since version 3.9 and will be removed in 4.0.',
E_USER_DEPRECATED
);
return $this->translator;
}
public function getTranslationLabel($label, $context = '', $type = '')
{
return $this->getLabelTranslatorStrategy()->getLabel($label, $context, $type);
}
public function setRequest(Request $request)
{
$this->request = $request;
foreach ($this->getChildren() as $children) {
$children->setRequest($request);
}
}
public function getRequest()
{
if (!$this->request) {
// NEXT_MAJOR: Throw \LogicException instead.
throw new \RuntimeException('The Request object has not been set');
}
return $this->request;
}
public function hasRequest()
{
return null !== $this->request;
}
public function setFormContractor(FormContractorInterface $formBuilder)
{
$this->formContractor = $formBuilder;
}
/**
* @return FormContractorInterface
*/
public function getFormContractor()
{
return $this->formContractor;
}
public function setDatagridBuilder(DatagridBuilderInterface $datagridBuilder)
{
$this->datagridBuilder = $datagridBuilder;
}
public function getDatagridBuilder()
{
return $this->datagridBuilder;
}
public function setListBuilder(ListBuilderInterface $listBuilder)
{
$this->listBuilder = $listBuilder;
}
public function getListBuilder()
{
return $this->listBuilder;
}
public function setShowBuilder(ShowBuilderInterface $showBuilder)
{
$this->showBuilder = $showBuilder;
}
/**
* @return ShowBuilderInterface
*/
public function getShowBuilder()
{
return $this->showBuilder;
}
public function setConfigurationPool(Pool $configurationPool)
{
$this->configurationPool = $configurationPool;
}
/**
* @return Pool
*/
public function getConfigurationPool()
{
return $this->configurationPool;
}
public function setRouteGenerator(RouteGeneratorInterface $routeGenerator)
{
$this->routeGenerator = $routeGenerator;
}
/**
* @return RouteGeneratorInterface
*/
public function getRouteGenerator()
{
return $this->routeGenerator;
}
public function getCode()
{
return $this->code;
}
/**
* NEXT_MAJOR: Remove this function.
*
* @deprecated This method is deprecated since sonata-project/admin-bundle 3.24 and will be removed in 4.0
*
* @param string $baseCodeRoute
*/
public function setBaseCodeRoute($baseCodeRoute)
{
@trigger_error(
'The '.__METHOD__.' is deprecated since 3.24 and will be removed in 4.0.',
E_USER_DEPRECATED
);
$this->baseCodeRoute = $baseCodeRoute;
}
public function getBaseCodeRoute()
{
// NEXT_MAJOR: Uncomment the following lines.
// if ($this->isChild()) {
// return $this->getParent()->getBaseCodeRoute().'|'.$this->getCode();
// }
//
// return $this->getCode();
// NEXT_MAJOR: Remove all the code below.
if ($this->isChild()) {
$parentCode = $this->getParent()->getCode();
if ($this->getParent()->isChild()) {
$parentCode = $this->getParent()->getBaseCodeRoute();
}
return $parentCode.'|'.$this->getCode();
}
return $this->baseCodeRoute;
}
public function getModelManager()
{
return $this->modelManager;
}
public function setModelManager(ModelManagerInterface $modelManager)
{
$this->modelManager = $modelManager;
}
public function getManagerType()
{
return $this->managerType;
}
/**
* @param string $type
*/
public function setManagerType($type)
{
$this->managerType = $type;
}
public function getObjectIdentifier()
{
return $this->getCode();
}
/**
* Set the roles and permissions per role.
*/
public function setSecurityInformation(array $information)
{
$this->securityInformation = $information;
}
public function getSecurityInformation()
{
return $this->securityInformation;
}
/**
* Return the list of permissions the user should have in order to display the admin.
*
* @param string $context
*
* @return array
*/
public function getPermissionsShow($context)
{
switch ($context) {
case self::CONTEXT_DASHBOARD:
case self::CONTEXT_MENU:
default:
return ['LIST'];
}
}
public function showIn($context)
{
switch ($context) {
case self::CONTEXT_DASHBOARD:
case self::CONTEXT_MENU:
default:
return $this->isGranted($this->getPermissionsShow($context));
}
}
public function createObjectSecurity($object)
{
$this->getSecurityHandler()->createObjectSecurity($this, $object);
}
public function setSecurityHandler(SecurityHandlerInterface $securityHandler)
{
$this->securityHandler = $securityHandler;
}
public function getSecurityHandler()
{
return $this->securityHandler;
}
public function isGranted($name, $object = null)
{
$objectRef = $object ? '/'.spl_object_hash($object).'#'.$this->id($object) : '';
$key = md5(json_encode($name).$objectRef);
if (!\array_key_exists($key, $this->cacheIsGranted)) {
$this->cacheIsGranted[$key] = $this->securityHandler->isGranted($this, $name, $object ?: $this);
}
return $this->cacheIsGranted[$key];
}
public function getUrlSafeIdentifier($entity)
{
return $this->getModelManager()->getUrlSafeIdentifier($entity);
}
public function getNormalizedIdentifier($entity)
{
return $this->getModelManager()->getNormalizedIdentifier($entity);
}
public function id($entity)
{
return $this->getNormalizedIdentifier($entity);
}
public function setValidator($validator)
{
// NEXT_MAJOR: Move ValidatorInterface check to method signature
if (!$validator instanceof ValidatorInterface) {
throw new \InvalidArgumentException(
'Argument 1 must be an instance of Symfony\Component\Validator\Validator\ValidatorInterface'
);
}
$this->validator = $validator;
}
public function getValidator()
{
return $this->validator;
}
public function getShow()
{
$this->buildShow();
return $this->show;
}
public function setFormTheme(array $formTheme)
{
$this->formTheme = $formTheme;
}
public function getFormTheme()
{
return $this->formTheme;
}
public function setFilterTheme(array $filterTheme)
{
$this->filterTheme = $filterTheme;
}
public function getFilterTheme()
{
return $this->filterTheme;
}
public function addExtension(AdminExtensionInterface $extension)
{
$this->extensions[] = $extension;
}
public function getExtensions()
{
return $this->extensions;
}
public function setMenuFactory(FactoryInterface $menuFactory)
{
$this->menuFactory = $menuFactory;
}
public function getMenuFactory()
{
return $this->menuFactory;
}
public function setRouteBuilder(RouteBuilderInterface $routeBuilder)
{
$this->routeBuilder = $routeBuilder;
}
public function getRouteBuilder()
{
return $this->routeBuilder;
}
public function toString($object)
{
if (!\is_object($object)) {
return '';
}
if (method_exists($object, '__toString') && null !== $object->__toString()) {
return (string) $object;
}
return sprintf('%s:%s', ClassUtils::getClass($object), spl_object_hash($object));
}
public function setLabelTranslatorStrategy(LabelTranslatorStrategyInterface $labelTranslatorStrategy)
{
$this->labelTranslatorStrategy = $labelTranslatorStrategy;
}
public function getLabelTranslatorStrategy()
{
return $this->labelTranslatorStrategy;
}
public function supportsPreviewMode()
{
return $this->supportsPreviewMode;
}
/**
* Set custom per page options.
*/
public function setPerPageOptions(array $options)
{
$this->perPageOptions = $options;
}
/**
* Returns predefined per page options.
*
* @return array
*/
public function getPerPageOptions()
{
return $this->perPageOptions;
}
/**
* Set pager type.
*
* @param string $pagerType
*/
public function setPagerType($pagerType)
{
$this->pagerType = $pagerType;
}
/**
* Get pager type.
*
* @return string
*/
public function getPagerType()
{
return $this->pagerType;
}
/**
* Returns true if the per page value is allowed, false otherwise.
*
* @param int $perPage
*
* @return bool
*/
public function determinedPerPageValue($perPage)
{
return \in_array($perPage, $this->perPageOptions, true);
}
public function isAclEnabled()
{
return $this->getSecurityHandler() instanceof AclSecurityHandlerInterface;
}
public function getObjectMetadata($object)
{
return new Metadata($this->toString($object));
}
public function getListModes()
{
return $this->listModes;
}
public function setListMode($mode)
{
if (!$this->hasRequest()) {
throw new \RuntimeException(sprintf('No request attached to the current admin: %s', $this->getCode()));
}
$this->getRequest()->getSession()->set(sprintf('%s.list_mode', $this->getCode()), $mode);
}
public function getListMode()
{
if (!$this->hasRequest()) {
return 'list';
}
return $this->getRequest()->getSession()->get(sprintf('%s.list_mode', $this->getCode()), 'list');
}
public function getAccessMapping()
{
return $this->accessMapping;
}
public function checkAccess($action, $object = null)
{
$access = $this->getAccess();
if (!\array_key_exists($action, $access)) {
throw new \InvalidArgumentException(sprintf(
'Action "%s" could not be found in access mapping.'
.' Please make sure your action is defined into your admin class accessMapping property.',
$action
));
}
if (!\is_array($access[$action])) {
$access[$action] = [$access[$action]];
}
foreach ($access[$action] as $role) {
if (false === $this->isGranted($role, $object)) {
throw new AccessDeniedException(sprintf('Access Denied to the action %s and role %s', $action, $role));
}
}
}
/**
* Hook to handle access authorization, without throw Exception.
*
* @param string $action
* @param object $object
*
* @return bool
*/
public function hasAccess($action, $object = null)
{
$access = $this->getAccess();
if (!\array_key_exists($action, $access)) {
return false;
}
if (!\is_array($access[$action])) {
$access[$action] = [$access[$action]];
}
foreach ($access[$action] as $role) {
if (false === $this->isGranted($role, $object)) {
return false;
}
}
return true;
}
/**
* @param string $action
* @param object|null $object
*
* @return array
*/
public function configureActionButtons($action, $object = null)
{
$list = [];
if (\in_array($action, ['tree', 'show', 'edit', 'delete', 'list', 'batch'], true)
&& $this->hasAccess('create')
&& $this->hasRoute('create')
) {
$list['create'] = [
// NEXT_MAJOR: Remove this line and use commented line below it instead
'template' => $this->getTemplate('button_create'),
// 'template' => $this->getTemplateRegistry()->getTemplate('button_create'),
];
}
if (\in_array($action, ['show', 'delete', 'acl', 'history'], true)
&& $this->canAccessObject('edit', $object)
&& $this->hasRoute('edit')
) {
$list['edit'] = [
// NEXT_MAJOR: Remove this line and use commented line below it instead
'template' => $this->getTemplate('button_edit'),
//'template' => $this->getTemplateRegistry()->getTemplate('button_edit'),
];
}
if (\in_array($action, ['show', 'edit', 'acl'], true)
&& $this->canAccessObject('history', $object)
&& $this->hasRoute('history')
) {
$list['history'] = [
// NEXT_MAJOR: Remove this line and use commented line below it instead
'template' => $this->getTemplate('button_history'),
// 'template' => $this->getTemplateRegistry()->getTemplate('button_history'),
];
}
if (\in_array($action, ['edit', 'history'], true)
&& $this->isAclEnabled()
&& $this->canAccessObject('acl', $object)
&& $this->hasRoute('acl')
) {
$list['acl'] = [
// NEXT_MAJOR: Remove this line and use commented line below it instead
'template' => $this->getTemplate('button_acl'),
// 'template' => $this->getTemplateRegistry()->getTemplate('button_acl'),
];
}
if (\in_array($action, ['edit', 'history', 'acl'], true)
&& $this->canAccessObject('show', $object)
&& \count($this->getShow()) > 0
&& $this->hasRoute('show')
) {
$list['show'] = [
// NEXT_MAJOR: Remove this line and use commented line below it instead
'template' => $this->getTemplate('button_show'),
// 'template' => $this->getTemplateRegistry()->getTemplate('button_show'),
];
}
if (\in_array($action, ['show', 'edit', 'delete', 'acl', 'batch'], true)
&& $this->hasAccess('list')
&& $this->hasRoute('list')
) {
$list['list'] = [
// NEXT_MAJOR: Remove this line and use commented line below it instead
'template' => $this->getTemplate('button_list'),
// 'template' => $this->getTemplateRegistry()->getTemplate('button_list'),
];
}
return $list;
}
/**
* @param string $action
* @param object $object
*
* @return array
*/
public function getActionButtons($action, $object = null)
{
$list = $this->configureActionButtons($action, $object);
foreach ($this->getExtensions() as $extension) {
// NEXT_MAJOR: remove method check
if (method_exists($extension, 'configureActionButtons')) {
$list = $extension->configureActionButtons($this, $list, $action, $object);
}
}
return $list;
}
/**
* Get the list of actions that can be accessed directly from the dashboard.
*
* @return array
*/
public function getDashboardActions()
{
$actions = [];
if ($this->hasRoute('create') && $this->hasAccess('create')) {
$actions['create'] = [
'label' => 'link_add',
'translation_domain' => 'SonataAdminBundle',
// NEXT_MAJOR: Remove this line and use commented line below it instead
'template' => $this->getTemplate('action_create'),
// 'template' => $this->getTemplateRegistry()->getTemplate('action_create'),
'url' => $this->generateUrl('create'),
'icon' => 'plus-circle',
];
}
if ($this->hasRoute('list') && $this->hasAccess('list')) {
$actions['list'] = [
'label' => 'link_list',
'translation_domain' => 'SonataAdminBundle',
'url' => $this->generateUrl('list'),
'icon' => 'list',
];
}
return $actions;
}
/**
* Setting to true will enable mosaic button for the admin screen.
* Setting to false will hide mosaic button for the admin screen.
*
* @param bool $isShown
*/
final public function showMosaicButton($isShown)
{
if ($isShown) {
$this->listModes['mosaic'] = ['class' => static::MOSAIC_ICON_CLASS];
} else {
unset($this->listModes['mosaic']);
}
}
/**
* @param object $object
*/
final public function getSearchResultLink($object)
{
foreach ($this->searchResultActions as $action) {
if ($this->hasRoute($action) && $this->hasAccess($action, $object)) {
return $this->generateObjectUrl($action, $object);
}
}
return null;
}
/**
* Checks if a filter type is set to a default value.
*
* @param string $name
*
* @return bool
*/
final public function isDefaultFilter($name)
{
$filter = $this->getFilterParameters();
$default = $this->getDefaultFilterValues();
if (!\array_key_exists($name, $filter) || !\array_key_exists($name, $default)) {
return false;
}
return $filter[$name] === $default[$name];
}
/**
* Check object existence and access, without throw Exception.
*
* @param string $action
* @param object $object
*
* @return bool
*/
public function canAccessObject($action, $object)
{
return $object && $this->id($object) && $this->hasAccess($action, $object);
}
protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface
{
return $query;
}
/**
* @return MutableTemplateRegistryInterface
*/
final protected function getTemplateRegistry()
{
return $this->templateRegistry;
}
/**
* Returns a list of default filters.
*
* @return array
*/
final protected function getDefaultFilterValues()
{
$defaultFilterValues = [];
$this->configureDefaultFilterValues($defaultFilterValues);
foreach ($this->getExtensions() as $extension) {
// NEXT_MAJOR: remove method check
if (method_exists($extension, 'configureDefaultFilterValues')) {
$extension->configureDefaultFilterValues($this, $defaultFilterValues);
}
}
return $defaultFilterValues;
}
protected function configureFormFields(FormMapper $form)
{
}
protected function configureListFields(ListMapper $list)
{
}
protected function configureDatagridFilters(DatagridMapper $filter)
{
}
protected function configureShowFields(ShowMapper $show)
{
}
protected function configureRoutes(RouteCollection $collection)
{
}
/**
* Allows you to customize batch actions.
*
* @param array $actions List of actions
*
* @return array
*/
protected function configureBatchActions($actions)
{
return $actions;
}
/**
* NEXT_MAJOR: remove this method.
*
* @deprecated Use configureTabMenu instead
*/
protected function configureSideMenu(ItemInterface $menu, $action, ?AdminInterface $childAdmin = null)
{
}
/**
* Configures the tab menu in your admin.
*
* @param string $action
*/
protected function configureTabMenu(ItemInterface $menu, $action, ?AdminInterface $childAdmin = null)
{
// Use configureSideMenu not to mess with previous overrides
// NEXT_MAJOR: remove this line
$this->configureSideMenu($menu, $action, $childAdmin);
}
/**
* build the view FieldDescription array.
*/
protected function buildShow()
{
if ($this->show) {
return;
}
$this->show = new FieldDescriptionCollection();
$mapper = new ShowMapper($this->showBuilder, $this->show, $this);
$this->configureShowFields($mapper);
foreach ($this->getExtensions() as $extension) {
$extension->configureShowFields($mapper);
}
}
/**
* build the list FieldDescription array.
*/
protected function buildList()
{
if ($this->list) {
return;
}
$this->list = $this->getListBuilder()->getBaseList();
$mapper = new ListMapper($this->getListBuilder(), $this->list, $this);
if (\count($this->getBatchActions()) > 0 && $this->hasRequest() && !$this->getRequest()->isXmlHttpRequest()) {
$fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
$this->getClass(),
'batch',
[
'label' => 'batch',
'code' => '_batch',
'sortable' => false,
'virtual_field' => true,
]
);
$fieldDescription->setAdmin($this);
// NEXT_MAJOR: Remove this line and use commented line below it instead
$fieldDescription->setTemplate($this->getTemplate('batch'));
// $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('batch'));
$mapper->add($fieldDescription, 'batch');
}
$this->configureListFields($mapper);
foreach ($this->getExtensions() as $extension) {
$extension->configureListFields($mapper);
}
if ($this->hasRequest() && $this->getRequest()->isXmlHttpRequest()) {
$fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance(
$this->getClass(),
'select',
[
'label' => false,
'code' => '_select',
'sortable' => false,
'virtual_field' => false,
]
);
$fieldDescription->setAdmin($this);
// NEXT_MAJOR: Remove this line and use commented line below it instead
$fieldDescription->setTemplate($this->getTemplate('select'));
// $fieldDescription->setTemplate($this->getTemplateRegistry()->getTemplate('select'));
$mapper->add($fieldDescription, 'select');
}
}
/**
* Build the form FieldDescription collection.
*/
protected function buildForm()
{
if ($this->form) {
return;
}
// append parent object if any
// todo : clean the way the Admin class can retrieve set the object
if ($this->isChild() && $this->getParentAssociationMapping()) {
$parent = $this->getParent()->getObject($this->request->get($this->getParent()->getIdParameter()));
$propertyAccessor = $this->getConfigurationPool()->getPropertyAccessor();
$propertyPath = new PropertyPath($this->getParentAssociationMapping());
$object = $this->getSubject();
$value = $propertyAccessor->getValue($object, $propertyPath);
if (\is_array($value) || $value instanceof \ArrayAccess) {
$value[] = $parent;
$propertyAccessor->setValue($object, $propertyPath, $value);
} else {
$propertyAccessor->setValue($object, $propertyPath, $parent);
}
}
$formBuilder = $this->getFormBuilder();
$formBuilder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
$this->preValidate($event->getData());
}, 100);
$this->form = $formBuilder->getForm();
}
/**
* Gets the subclass corresponding to the given name.
*
* @param string $name The name of the sub class
*
* @return string the subclass
*/
protected function getSubClass($name)
{
if ($this->hasSubClass($name)) {
return $this->subClasses[$name];
}
// NEXT_MAJOR: Throw \LogicException instead.
throw new \RuntimeException(sprintf(
'Unable to find the subclass `%s` for admin `%s`',
$name,
static::class
));
}
/**
* Attach the inline validator to the model metadata, this must be done once per admin.
*/
protected function attachInlineValidator()
{
$admin = $this;
// add the custom inline validation option
$metadata = $this->validator->getMetadataFor($this->getClass());
$metadata->addConstraint(new InlineConstraint([
'service' => $this,
'method' => static function (ErrorElement $errorElement, $object) use ($admin) {
/* @var \Sonata\AdminBundle\Admin\AdminInterface $admin */
// This avoid the main validation to be cascaded to children
// The problem occurs when a model Page has a collection of Page as property
if ($admin->hasSubject() && spl_object_hash($object) !== spl_object_hash($admin->getSubject())) {
return;
}
$admin->validate($errorElement, $object);
foreach ($admin->getExtensions() as $extension) {
$extension->validate($admin, $errorElement, $object);
}
},
'serializingWarning' => true,
]));
}
/**
* Predefine per page options.
*/
protected function predefinePerPageOptions()
{
array_unshift($this->perPageOptions, $this->maxPerPage);
$this->perPageOptions = array_unique($this->perPageOptions);
sort($this->perPageOptions);
}
/**
* Return list routes with permissions name.
*
* @return array<string, string>
*/
protected function getAccess()
{
$access = array_merge([
'acl' => 'MASTER',
'export' => 'EXPORT',
'historyCompareRevisions' => 'EDIT',
'historyViewRevision' => 'EDIT',
'history' => 'EDIT',
'edit' => 'EDIT',
'show' => 'VIEW',
'create' => 'CREATE',
'delete' => 'DELETE',
'batchDelete' => 'DELETE',
'list' => 'LIST',
], $this->getAccessMapping());
foreach ($this->extensions as $extension) {
// NEXT_MAJOR: remove method check
if (method_exists($extension, 'getAccessMapping')) {
$access = array_merge($access, $extension->getAccessMapping($this));
}
}
return $access;
}
/**
* Configures a list of default filters.
*/
protected function configureDefaultFilterValues(array &$filterValues)
{
}
/**
* Build all the related urls to the current admin.
*/
private function buildRoutes(): void
{
if ($this->loaded['routes']) {
return;
}
$this->loaded['routes'] = true;
$this->routes = new RouteCollection(
$this->getBaseCodeRoute(),
$this->getBaseRouteName(),
$this->getBaseRoutePattern(),
$this->getBaseControllerName()
);
$this->routeBuilder->build($this, $this->routes);
$this->configureRoutes($this->routes);
foreach ($this->getExtensions() as $extension) {
$extension->configureRoutes($this, $this->routes);
}
}
}
class_exists(\Sonata\Form\Validator\ErrorElement::class);