Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
<?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\Route;
use Sonata\AdminBundle\Admin\Pool;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\RouteCollection as SymfonyRouteCollection;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class AdminPoolLoader extends Loader
{
const ROUTE_TYPE_NAME = 'sonata_admin';
/**
* @var Pool
*/
protected $pool;
/**
* @var array
*/
protected $adminServiceIds = [];
/**
* @var ContainerInterface
*/
protected $container;
/**
* @param Pool $pool
* @param array $adminServiceIds
* @param ContainerInterface $container
*/
public function __construct(Pool $pool, array $adminServiceIds, ContainerInterface $container)
{
$this->pool = $pool;
$this->adminServiceIds = $adminServiceIds;
$this->container = $container;
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
return $type === self::ROUTE_TYPE_NAME;
}
/**
* {@inheritdoc}
*/
public function load($resource, $type = null)
{
$collection = new SymfonyRouteCollection();
foreach ($this->adminServiceIds as $id) {
$admin = $this->pool->getInstance($id);
foreach ($admin->getRoutes()->getElements() as $code => $route) {
$collection->add($route->getDefault('_sonata_name'), $route);
}
$reflection = new \ReflectionObject($admin);
if (file_exists($reflection->getFileName())) {
$collection->addResource(new FileResource($reflection->getFileName()));
}
}
$reflection = new \ReflectionObject($this->container);
if (file_exists($reflection->getFileName())) {
$collection->addResource(new FileResource($reflection->getFileName()));
}
return $collection;
}
}

View File

@@ -0,0 +1,182 @@
<?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\Route;
use Sonata\AdminBundle\Admin\AdminInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class DefaultRouteGenerator implements RouteGeneratorInterface
{
/**
* @var RouterInterface
*/
private $router;
/**
* @var RoutesCache
*/
private $cache;
/**
* @var array
*/
private $caches = [];
/**
* @var string[]
*/
private $loaded = [];
/**
* @param RouterInterface $router
* @param RoutesCache $cache
*/
public function __construct(RouterInterface $router, RoutesCache $cache)
{
$this->router = $router;
$this->cache = $cache;
}
/**
* {@inheritdoc}
*/
public function generate($name, array $parameters = [], $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
{
return $this->router->generate($name, $parameters, $absolute);
}
/**
* {@inheritdoc}
*/
public function generateUrl(AdminInterface $admin, $name, array $parameters = [], $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
{
$arrayRoute = $this->generateMenuUrl($admin, $name, $parameters, $absolute);
return $this->router->generate($arrayRoute['route'], $arrayRoute['routeParameters'], $arrayRoute['routeAbsolute']);
}
/**
* {@inheritdoc}
*/
public function generateMenuUrl(AdminInterface $admin, $name, array $parameters = [], $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
{
// if the admin is a child we automatically append the parent's id
if ($admin->isChild() && $admin->hasRequest()) {
// twig template does not accept variable hash key ... so cannot use admin.idparameter ...
// switch value
if (isset($parameters['id'])) {
$parameters[$admin->getIdParameter()] = $parameters['id'];
unset($parameters['id']);
}
for ($parentAdmin = $admin->getParent(); null !== $parentAdmin; $parentAdmin = $parentAdmin->getParent()) {
$parameters[$parentAdmin->getIdParameter()] = $admin->getRequest()->attributes->get($parentAdmin->getIdParameter());
}
}
// if the admin is linked to a parent FieldDescription (ie, embedded widget)
if ($admin->hasParentFieldDescription()) {
// merge link parameter if any provided by the parent field
$parameters = array_merge($parameters, $admin->getParentFieldDescription()->getOption('link_parameters', []));
$parameters['uniqid'] = $admin->getUniqid();
$parameters['code'] = $admin->getCode();
$parameters['pcode'] = $admin->getParentFieldDescription()->getAdmin()->getCode();
$parameters['puniqid'] = $admin->getParentFieldDescription()->getAdmin()->getUniqid();
}
if ($name == 'update' || substr($name, -7) == '|update') {
$parameters['uniqid'] = $admin->getUniqid();
$parameters['code'] = $admin->getCode();
}
// allows to define persistent parameters
if ($admin->hasRequest()) {
$parameters = array_merge($admin->getPersistentParameters(), $parameters);
}
$code = $this->getCode($admin, $name);
if (!array_key_exists($code, $this->caches)) {
throw new \RuntimeException(sprintf('unable to find the route `%s`', $code));
}
return [
'route' => $this->caches[$code],
'routeParameters' => $parameters,
'routeAbsolute' => $absolute,
];
}
/**
* {@inheritdoc}
*/
public function hasAdminRoute(AdminInterface $admin, $name)
{
return array_key_exists($this->getCode($admin, $name), $this->caches);
}
/**
* @param AdminInterface $admin
* @param string $name
*
* @return string
*/
private function getCode(AdminInterface $admin, $name)
{
$this->loadCache($admin);
// someone provide the fullname
if (!$admin->isChild() && array_key_exists($name, $this->caches)) {
return $name;
}
// NEXT_MAJOR: Uncomment the following line.
// $codePrefix = $admin->getBaseCodeRoute();
// NEXT_MAJOR: Remove next 5 lines.
$codePrefix = $admin->getCode();
if ($admin->isChild()) {
$codePrefix = $admin->getBaseCodeRoute();
}
// someone provide a code, so it is a child
if (strpos($name, '.')) {
return $codePrefix.'|'.$name;
}
return $codePrefix.'.'.$name;
}
/**
* @param AdminInterface $admin
*/
private function loadCache(AdminInterface $admin)
{
if ($admin->isChild()) {
$this->loadCache($admin->getParent());
} else {
if (in_array($admin->getCode(), $this->loaded)) {
return;
}
$this->caches = array_merge($this->cache->load($admin), $this->caches);
$this->loaded[] = $admin->getCode();
}
}
}

View File

@@ -0,0 +1,65 @@
<?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\Route;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Builder\RouteBuilderInterface;
use Sonata\AdminBundle\Model\AuditManagerInterface;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class PathInfoBuilder implements RouteBuilderInterface
{
/**
* @var AuditManagerInterface
*/
protected $manager;
/**
* @param AuditManagerInterface $manager
*/
public function __construct(AuditManagerInterface $manager)
{
$this->manager = $manager;
}
/**
* @param AdminInterface $admin
* @param RouteCollection $collection
*/
public function build(AdminInterface $admin, RouteCollection $collection)
{
$collection->add('list');
$collection->add('create');
$collection->add('batch');
$collection->add('edit', $admin->getRouterIdParameter().'/edit');
$collection->add('delete', $admin->getRouterIdParameter().'/delete');
$collection->add('show', $admin->getRouterIdParameter().'/show');
$collection->add('export');
if ($this->manager->hasReader($admin->getClass())) {
$collection->add('history', $admin->getRouterIdParameter().'/history');
$collection->add('history_view_revision', $admin->getRouterIdParameter().'/history/{revision}/view');
$collection->add('history_compare_revisions', $admin->getRouterIdParameter().'/history/{base_revision}/{compare_revision}/compare');
}
if ($admin->isAclEnabled()) {
$collection->add('acl', $admin->getRouterIdParameter().'/acl');
}
// add children urls
foreach ($admin->getChildren() as $children) {
$collection->addCollection($children->getRoutes());
}
}
}

View File

@@ -0,0 +1,70 @@
<?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\Route;
use Sonata\AdminBundle\Admin\AdminInterface;
use Sonata\AdminBundle\Builder\RouteBuilderInterface;
use Sonata\AdminBundle\Model\AuditManagerInterface;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class QueryStringBuilder implements RouteBuilderInterface
{
/**
* @var AuditManagerInterface
*/
protected $manager;
/**
* @param AuditManagerInterface $manager
*/
public function __construct(AuditManagerInterface $manager)
{
$this->manager = $manager;
}
/**
* @param AdminInterface $admin
* @param RouteCollection $collection
*/
public function build(AdminInterface $admin, RouteCollection $collection)
{
$collection->add('list');
$collection->add('create');
$collection->add('batch');
$collection->add('edit');
$collection->add('delete');
$collection->add('show');
$collection->add('export');
if ($this->manager->hasReader($admin->getClass())) {
$collection->add('history', '/audit-history');
$collection->add('history_view_revision', '/audit-history-view');
$collection->add('history_compare_revisions', '/audit-history-compare');
}
if ($admin->isAclEnabled()) {
$collection->add('acl', $admin->getRouterIdParameter().'/acl');
}
// an admin can have only one level of nested child
if ($admin->getParent()) {
return;
}
// add children urls
foreach ($admin->getChildren() as $children) {
$collection->addCollection($children->getRoutes());
}
}
}

View File

@@ -0,0 +1,288 @@
<?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\Route;
use Symfony\Component\Routing\Route;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class RouteCollection
{
/**
* @var Route[]
*/
protected $elements = [];
/**
* @var string
*/
protected $baseCodeRoute;
/**
* @var string
*/
protected $baseRouteName;
/**
* @var string
*/
protected $baseControllerName;
/**
* @var string
*/
protected $baseRoutePattern;
/**
* @param string $baseCodeRoute
* @param string $baseRouteName
* @param string $baseRoutePattern
* @param string $baseControllerName
*/
public function __construct($baseCodeRoute, $baseRouteName, $baseRoutePattern, $baseControllerName)
{
$this->baseCodeRoute = $baseCodeRoute;
$this->baseRouteName = $baseRouteName;
$this->baseRoutePattern = $baseRoutePattern;
$this->baseControllerName = $baseControllerName;
}
/**
* Add route.
*
* @param string $name Name
* @param string $pattern Pattern (will be automatically combined with @see $this->baseRoutePattern and $name
* @param array $defaults Defaults
* @param array $requirements Requirements
* @param array $options Options
* @param string $host Host
* @param array $schemes Schemes
* @param array $methods Methods
* @param string $condition Condition
*
* @return RouteCollection
*/
public function add($name, $pattern = null, array $defaults = [], array $requirements = [], array $options = [], $host = '', array $schemes = [], array $methods = [], $condition = '')
{
$pattern = $this->baseRoutePattern.'/'.($pattern ?: $name);
$code = $this->getCode($name);
$routeName = $this->baseRouteName.'_'.$name;
if (!isset($defaults['_controller'])) {
$defaults['_controller'] = $this->baseControllerName.':'.$this->actionify($code);
}
if (!isset($defaults['_sonata_admin'])) {
$defaults['_sonata_admin'] = $this->baseCodeRoute;
}
$defaults['_sonata_name'] = $routeName;
$this->elements[$this->getCode($name)] = function () use (
$pattern, $defaults, $requirements, $options, $host, $schemes, $methods, $condition) {
return new Route($pattern, $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
};
return $this;
}
/**
* @param string $name
*
* @return string
*/
public function getCode($name)
{
if (strrpos($name, '.') !== false) {
return $name;
}
return $this->baseCodeRoute.'.'.$name;
}
/**
* @param RouteCollection $collection
*
* @return RouteCollection
*/
public function addCollection(RouteCollection $collection)
{
foreach ($collection->getElements() as $code => $route) {
$this->elements[$code] = $route;
}
return $this;
}
/**
* @return Route[]
*/
public function getElements()
{
foreach ($this->elements as $name => $element) {
$this->elements[$name] = $this->resolve($element);
}
return $this->elements;
}
/**
* @param string $name
*
* @return bool
*/
public function has($name)
{
return array_key_exists($this->getCode($name), $this->elements);
}
/**
* @param string $name
*
* @throws \InvalidArgumentException
*
* @return Route
*/
public function get($name)
{
if ($this->has($name)) {
$code = $this->getCode($name);
$this->elements[$code] = $this->resolve($this->elements[$code]);
return $this->elements[$code];
}
throw new \InvalidArgumentException(sprintf('Element "%s" does not exist.', $name));
}
/**
* @param string $name
*
* @return RouteCollection
*/
public function remove($name)
{
unset($this->elements[$this->getCode($name)]);
return $this;
}
/**
* Remove all routes except routes in $routeList.
*
* @param string[]|string $routeList
*
* @return RouteCollection
*/
public function clearExcept($routeList)
{
if (!is_array($routeList)) {
$routeList = [$routeList];
}
$routeCodeList = [];
foreach ($routeList as $name) {
$routeCodeList[] = $this->getCode($name);
}
$elements = $this->elements;
foreach ($elements as $key => $element) {
if (!in_array($key, $routeCodeList)) {
unset($this->elements[$key]);
}
}
return $this;
}
/**
* Remove all routes.
*
* @return RouteCollection
*/
public function clear()
{
$this->elements = [];
return $this;
}
/**
* Convert a word in to the format for a symfony action action_name => actionName.
*
* @param string $action Word to actionify
*
* @return string Actionified word
*/
public function actionify($action)
{
if (($pos = strrpos($action, '.')) !== false) {
$action = substr($action, $pos + 1);
}
// if this is a service rather than just a controller name, the suffix
// Action is not automatically appended to the method name
if (strpos($this->baseControllerName, ':') === false) {
$action .= 'Action';
}
return lcfirst(str_replace(' ', '', ucwords(strtr($action, '_-', ' '))));
}
/**
* @return string
*/
public function getBaseCodeRoute()
{
return $this->baseCodeRoute;
}
/**
* @return string
*/
public function getBaseControllerName()
{
return $this->baseControllerName;
}
/**
* @return string
*/
public function getBaseRouteName()
{
return $this->baseRouteName;
}
/**
* @return string
*/
public function getBaseRoutePattern()
{
return $this->baseRoutePattern;
}
/**
* @param $element
*
* @return Route
*/
private function resolve($element)
{
if (is_callable($element)) {
return call_user_func($element);
}
return $element;
}
}

View File

@@ -0,0 +1,57 @@
<?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\Route;
use Sonata\AdminBundle\Admin\AdminInterface;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
interface RouteGeneratorInterface
{
/**
* @param AdminInterface $admin
* @param string $name
* @param array $parameters
* @param bool $absolute
*
* @return string
*/
public function generateUrl(AdminInterface $admin, $name, array $parameters = [], $absolute = false);
/**
* @param AdminInterface $admin
* @param string $name
* @param array $parameters
* @param bool $absolute
*
* @return array
*/
public function generateMenuUrl(AdminInterface $admin, $name, array $parameters = [], $absolute = false);
/**
* @param string $name
* @param array $parameters
* @param bool $absolute
*
* @return string
*/
public function generate($name, array $parameters = [], $absolute = false);
/**
* @param AdminInterface $admin
* @param string $name
*
* @return bool
*/
public function hasAdminRoute(AdminInterface $admin, $name);
}

View File

@@ -0,0 +1,86 @@
<?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\Route;
use Sonata\AdminBundle\Admin\AdminInterface;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Resource\FileResource;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class RoutesCache
{
/**
* @var string
*/
protected $cacheFolder;
/**
* @var bool
*/
protected $debug;
/**
* @param string $cacheFolder
* @param bool $debug
*/
public function __construct($cacheFolder, $debug)
{
$this->cacheFolder = $cacheFolder;
$this->debug = $debug;
}
/**
* @param AdminInterface $admin
*
* @return mixed
*
* @throws \RuntimeException
*/
public function load(AdminInterface $admin)
{
$filename = $this->cacheFolder.'/route_'.md5($admin->getCode());
$cache = new ConfigCache($filename, $this->debug);
if (!$cache->isFresh()) {
$resources = [];
$routes = [];
$reflection = new \ReflectionObject($admin);
if (file_exists($reflection->getFileName())) {
$resources[] = new FileResource($reflection->getFileName());
}
if (!$admin->getRoutes()) {
throw new \RuntimeException('Invalid data type, AdminInterface::getRoutes must return a RouteCollection');
}
foreach ($admin->getRoutes()->getElements() as $code => $route) {
$routes[$code] = $route->getDefault('_sonata_name');
}
if (!is_array($admin->getExtensions())) {
throw new \RuntimeException('extensions must be an array');
}
foreach ($admin->getExtensions() as $extension) {
$reflection = new \ReflectionObject($extension);
$resources[] = new FileResource($reflection->getFileName());
}
$cache->write(serialize($routes), $resources);
}
return unserialize(file_get_contents($filename));
}
}

View File

@@ -0,0 +1,59 @@
<?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\Route;
use Sonata\AdminBundle\Admin\Pool;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
/**
* @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
*/
class RoutesCacheWarmUp implements CacheWarmerInterface
{
/**
* @var RoutesCache
*/
protected $cache;
/**
* @var Pool
*/
protected $pool;
/**
* @param RoutesCache $cache
* @param Pool $pool
*/
public function __construct(RoutesCache $cache, Pool $pool)
{
$this->cache = $cache;
$this->pool = $pool;
}
/**
* {@inheritdoc}
*/
public function isOptional()
{
return true;
}
/**
* {@inheritdoc}
*/
public function warmUp($cacheDir)
{
foreach ($this->pool->getAdminServiceIds() as $id) {
$this->cache->load($this->pool->getInstance($id));
}
}
}