PageRenderTime 58ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/Admin/AbstractAdmin.php

http://github.com/sonata-project/SonataAdminBundle
PHP | 3234 lines | 2798 code | 88 blank | 348 comment | 10 complexity | efa0736c1dec5e7723a89caddf908a18 MD5 | raw file
Possible License(s): JSON, Apache-2.0, MIT
  1. <?php
  2. /*
  3. * This file is part of the Sonata Project package.
  4. *
  5. * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Sonata\AdminBundle\Admin;
  11. use Doctrine\Common\Util\ClassUtils;
  12. use Knp\Menu\FactoryInterface as MenuFactoryInterface;
  13. use Knp\Menu\ItemInterface;
  14. use Knp\Menu\ItemInterface as MenuItemInterface;
  15. use Sonata\AdminBundle\Builder\DatagridBuilderInterface;
  16. use Sonata\AdminBundle\Builder\FormContractorInterface;
  17. use Sonata\AdminBundle\Builder\ListBuilderInterface;
  18. use Sonata\AdminBundle\Builder\RouteBuilderInterface;
  19. use Sonata\AdminBundle\Builder\ShowBuilderInterface;
  20. use Sonata\AdminBundle\Datagrid\DatagridInterface;
  21. use Sonata\AdminBundle\Datagrid\DatagridMapper;
  22. use Sonata\AdminBundle\Datagrid\ListMapper;
  23. use Sonata\AdminBundle\Datagrid\Pager;
  24. use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
  25. use Sonata\AdminBundle\Form\FormMapper;
  26. use Sonata\AdminBundle\Model\ModelManagerInterface;
  27. use Sonata\AdminBundle\Route\RouteCollection;
  28. use Sonata\AdminBundle\Route\RouteGeneratorInterface;
  29. use Sonata\AdminBundle\Security\Handler\AclSecurityHandlerInterface;
  30. use Sonata\AdminBundle\Security\Handler\SecurityHandlerInterface;
  31. use Sonata\AdminBundle\Show\ShowMapper;
  32. use Sonata\AdminBundle\Translator\LabelTranslatorStrategyInterface;
  33. use Sonata\CoreBundle\Model\Metadata;
  34. use Sonata\CoreBundle\Validator\Constraints\InlineConstraint;
  35. use Sonata\CoreBundle\Validator\ErrorElement;
  36. use Symfony\Component\Form\Form;
  37. use Symfony\Component\Form\FormBuilderInterface;
  38. use Symfony\Component\HttpFoundation\Request;
  39. use Symfony\Component\PropertyAccess\PropertyPath;
  40. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  41. use Symfony\Component\Security\Acl\Model\DomainObjectInterface;
  42. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  43. use Symfony\Component\Translation\TranslatorInterface;
  44. use Symfony\Component\Validator\Validator\ValidatorInterface;
  45. use Symfony\Component\Validator\ValidatorInterface as LegacyValidatorInterface;
  46. /**
  47. * @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
  48. */
  49. abstract class AbstractAdmin implements AdminInterface, DomainObjectInterface
  50. {
  51. const CONTEXT_MENU = 'menu';
  52. const CONTEXT_DASHBOARD = 'dashboard';
  53. const CLASS_REGEX =
  54. '@
  55. (?:([A-Za-z0-9]*)\\\)? # vendor name / app name
  56. (Bundle\\\)? # optional bundle directory
  57. ([A-Za-z0-9]+?)(?:Bundle)?\\\ # bundle name, with optional suffix
  58. (
  59. Entity|Document|Model|PHPCR|CouchDocument|Phpcr|
  60. Doctrine\\\Orm|Doctrine\\\Phpcr|Doctrine\\\MongoDB|Doctrine\\\CouchDB
  61. )\\\(.*)@x';
  62. const MOSAIC_ICON_CLASS = 'fa fa-th-large fa-fw';
  63. /**
  64. * The list FieldDescription constructed from the configureListField method.
  65. *
  66. * @var array
  67. */
  68. protected $listFieldDescriptions = array();
  69. /**
  70. * The show FieldDescription constructed from the configureShowFields method.
  71. *
  72. * @var array
  73. */
  74. protected $showFieldDescriptions = array();
  75. /**
  76. * The list FieldDescription constructed from the configureFormField method.
  77. *
  78. * @var array
  79. */
  80. protected $formFieldDescriptions = array();
  81. /**
  82. * The filter FieldDescription constructed from the configureFilterField method.
  83. *
  84. * @var array
  85. */
  86. protected $filterFieldDescriptions = array();
  87. /**
  88. * The number of result to display in the list.
  89. *
  90. * @var int
  91. */
  92. protected $maxPerPage = 32;
  93. /**
  94. * The maximum number of page numbers to display in the list.
  95. *
  96. * @var int
  97. */
  98. protected $maxPageLinks = 25;
  99. /**
  100. * The base route name used to generate the routing information.
  101. *
  102. * @var string
  103. */
  104. protected $baseRouteName;
  105. /**
  106. * The base route pattern used to generate the routing information.
  107. *
  108. * @var string
  109. */
  110. protected $baseRoutePattern;
  111. /**
  112. * The base name controller used to generate the routing information.
  113. *
  114. * @var string
  115. */
  116. protected $baseControllerName;
  117. /**
  118. * The label class name (used in the title/breadcrumb ...).
  119. *
  120. * @var string
  121. */
  122. protected $classnameLabel;
  123. /**
  124. * The translation domain to be used to translate messages.
  125. *
  126. * @var string
  127. */
  128. protected $translationDomain = 'messages';
  129. /**
  130. * Options to set to the form (ie, validation_groups).
  131. *
  132. * @var array
  133. */
  134. protected $formOptions = array();
  135. /**
  136. * Default values to the datagrid.
  137. *
  138. * @var array
  139. */
  140. protected $datagridValues = array(
  141. '_page' => 1,
  142. '_per_page' => 32,
  143. );
  144. /**
  145. * Predefined per page options.
  146. *
  147. * @var array
  148. */
  149. protected $perPageOptions = array(16, 32, 64, 128, 192);
  150. /**
  151. * Pager type.
  152. *
  153. * @var string
  154. */
  155. protected $pagerType = Pager::TYPE_DEFAULT;
  156. /**
  157. * The code related to the admin.
  158. *
  159. * @var string
  160. */
  161. protected $code;
  162. /**
  163. * The label.
  164. *
  165. * @var string
  166. */
  167. protected $label;
  168. /**
  169. * Whether or not to persist the filters in the session.
  170. *
  171. * @var bool
  172. */
  173. protected $persistFilters = false;
  174. /**
  175. * Array of routes related to this admin.
  176. *
  177. * @var RouteCollection
  178. */
  179. protected $routes;
  180. /**
  181. * The subject only set in edit/update/create mode.
  182. *
  183. * @var object
  184. */
  185. protected $subject;
  186. /**
  187. * Define a Collection of child admin, ie /admin/order/{id}/order-element/{childId}.
  188. *
  189. * @var array
  190. */
  191. protected $children = array();
  192. /**
  193. * Reference the parent collection.
  194. *
  195. * @var AdminInterface|null
  196. */
  197. protected $parent = null;
  198. /**
  199. * The base code route refer to the prefix used to generate the route name.
  200. *
  201. * @var string
  202. */
  203. protected $baseCodeRoute = '';
  204. /**
  205. * The related field reflection, ie if OrderElement is linked to Order,
  206. * then the $parentReflectionProperty must be the ReflectionProperty of
  207. * the order (OrderElement::$order).
  208. *
  209. * @var \ReflectionProperty
  210. */
  211. protected $parentAssociationMapping = null;
  212. /**
  213. * Reference the parent FieldDescription related to this admin
  214. * only set for FieldDescription which is associated to an Sub Admin instance.
  215. *
  216. * @var FieldDescriptionInterface
  217. */
  218. protected $parentFieldDescription;
  219. /**
  220. * If true then the current admin is part of the nested admin set (from the url).
  221. *
  222. * @var bool
  223. */
  224. protected $currentChild = false;
  225. /**
  226. * The uniqid is used to avoid clashing with 2 admin related to the code
  227. * ie: a Block linked to a Block.
  228. *
  229. * @var string
  230. */
  231. protected $uniqid;
  232. /**
  233. * The Entity or Document manager.
  234. *
  235. * @var ModelManagerInterface
  236. */
  237. protected $modelManager;
  238. /**
  239. * The current request object.
  240. *
  241. * @var \Symfony\Component\HttpFoundation\Request
  242. */
  243. protected $request;
  244. /**
  245. * The translator component.
  246. *
  247. * @var \Symfony\Component\Translation\TranslatorInterface
  248. */
  249. protected $translator;
  250. /**
  251. * The related form contractor.
  252. *
  253. * @var FormContractorInterface
  254. */
  255. protected $formContractor;
  256. /**
  257. * The related list builder.
  258. *
  259. * @var ListBuilderInterface
  260. */
  261. protected $listBuilder;
  262. /**
  263. * The related view builder.
  264. *
  265. * @var ShowBuilderInterface
  266. */
  267. protected $showBuilder;
  268. /**
  269. * The related datagrid builder.
  270. *
  271. * @var DatagridBuilderInterface
  272. */
  273. protected $datagridBuilder;
  274. /**
  275. * @var RouteBuilderInterface
  276. */
  277. protected $routeBuilder;
  278. /**
  279. * The datagrid instance.
  280. *
  281. * @var \Sonata\AdminBundle\Datagrid\DatagridInterface
  282. */
  283. protected $datagrid;
  284. /**
  285. * The router instance.
  286. *
  287. * @var RouteGeneratorInterface
  288. */
  289. protected $routeGenerator;
  290. /**
  291. * The generated breadcrumbs.
  292. *
  293. * NEXT_MAJOR : remove this property
  294. *
  295. * @var array
  296. */
  297. protected $breadcrumbs = array();
  298. /**
  299. * @var SecurityHandlerInterface
  300. */
  301. protected $securityHandler = null;
  302. /**
  303. * @var ValidatorInterface|LegacyValidatorInterface
  304. */
  305. protected $validator = null;
  306. /**
  307. * The configuration pool.
  308. *
  309. * @var Pool
  310. */
  311. protected $configurationPool;
  312. /**
  313. * @var MenuItemInterface
  314. */
  315. protected $menu;
  316. /**
  317. * @var MenuFactoryInterface
  318. */
  319. protected $menuFactory;
  320. /**
  321. * @var array
  322. */
  323. protected $loaded = array(
  324. 'view_fields' => false,
  325. 'view_groups' => false,
  326. 'routes' => false,
  327. 'tab_menu' => false,
  328. );
  329. /**
  330. * @var array
  331. */
  332. protected $formTheme = array();
  333. /**
  334. * @var array
  335. */
  336. protected $filterTheme = array();
  337. /**
  338. * @var array
  339. */
  340. protected $templates = array();
  341. /**
  342. * @var AdminExtensionInterface[]
  343. */
  344. protected $extensions = array();
  345. /**
  346. * @var LabelTranslatorStrategyInterface
  347. */
  348. protected $labelTranslatorStrategy;
  349. /**
  350. * Setting to true will enable preview mode for
  351. * the entity and show a preview button in the
  352. * edit/create forms.
  353. *
  354. * @var bool
  355. */
  356. protected $supportsPreviewMode = false;
  357. /**
  358. * Roles and permissions per role.
  359. *
  360. * @var array [role] => array([permission], [permission])
  361. */
  362. protected $securityInformation = array();
  363. protected $cacheIsGranted = array();
  364. /**
  365. * Action list for the search result.
  366. *
  367. * @var string[]
  368. */
  369. protected $searchResultActions = array('edit', 'show');
  370. protected $listModes = array(
  371. 'list' => array(
  372. 'class' => 'fa fa-list fa-fw',
  373. ),
  374. 'mosaic' => array(
  375. 'class' => self::MOSAIC_ICON_CLASS,
  376. ),
  377. );
  378. /**
  379. * The Access mapping.
  380. *
  381. * @var array
  382. */
  383. protected $accessMapping = array();
  384. /**
  385. * The class name managed by the admin class.
  386. *
  387. * @var string
  388. */
  389. private $class;
  390. /**
  391. * The subclasses supported by the admin class.
  392. *
  393. * @var array
  394. */
  395. private $subClasses = array();
  396. /**
  397. * The list collection.
  398. *
  399. * @var array
  400. */
  401. private $list;
  402. /**
  403. * @var FieldDescriptionCollection
  404. */
  405. private $show;
  406. /**
  407. * @var Form
  408. */
  409. private $form;
  410. /**
  411. * @var DatagridInterface
  412. */
  413. private $filter;
  414. /**
  415. * The cached base route name.
  416. *
  417. * @var string
  418. */
  419. private $cachedBaseRouteName;
  420. /**
  421. * The cached base route pattern.
  422. *
  423. * @var string
  424. */
  425. private $cachedBaseRoutePattern;
  426. /**
  427. * The form group disposition.
  428. *
  429. * @var array|bool
  430. */
  431. private $formGroups = false;
  432. /**
  433. * The form tabs disposition.
  434. *
  435. * @var array|bool
  436. */
  437. private $formTabs = false;
  438. /**
  439. * The view group disposition.
  440. *
  441. * @var array|bool
  442. */
  443. private $showGroups = false;
  444. /**
  445. * The view tab disposition.
  446. *
  447. * @var array|bool
  448. */
  449. private $showTabs = false;
  450. /**
  451. * The manager type to use for the admin.
  452. *
  453. * @var string
  454. */
  455. private $managerType;
  456. /**
  457. * The breadcrumbsBuilder component.
  458. *
  459. * @var BreadcrumbsBuilderInterface
  460. */
  461. private $breadcrumbsBuilder;
  462. /**
  463. * @param string $code
  464. * @param string $class
  465. * @param string $baseControllerName
  466. */
  467. public function __construct($code, $class, $baseControllerName)
  468. {
  469. $this->code = $code;
  470. $this->class = $class;
  471. $this->baseControllerName = $baseControllerName;
  472. $this->predefinePerPageOptions();
  473. $this->datagridValues['_per_page'] = $this->maxPerPage;
  474. }
  475. /**
  476. * {@inheritdoc}
  477. */
  478. public function getExportFormats()
  479. {
  480. return array(
  481. 'json', 'xml', 'csv', 'xls',
  482. );
  483. }
  484. /**
  485. * @return array
  486. */
  487. public function getExportFields()
  488. {
  489. $fields = $this->getModelManager()->getExportFields($this->getClass());
  490. foreach ($this->getExtensions() as $extension) {
  491. if (method_exists($extension, 'configureExportFields')) {
  492. $fields = $extension->configureExportFields($this, $fields);
  493. }
  494. }
  495. return $fields;
  496. }
  497. /**
  498. * {@inheritdoc}
  499. */
  500. public function getDataSourceIterator()
  501. {
  502. $datagrid = $this->getDatagrid();
  503. $datagrid->buildPager();
  504. $fields = array();
  505. foreach ($this->getExportFields() as $key => $field) {
  506. $label = $this->getTranslationLabel($field, 'export', 'label');
  507. $transLabel = $this->trans($label);
  508. // NEXT_MAJOR: Remove this hack, because all field labels will be translated with the major release
  509. // No translation key exists
  510. if ($transLabel == $label) {
  511. $fields[$key] = $field;
  512. } else {
  513. $fields[$transLabel] = $field;
  514. }
  515. }
  516. return $this->getModelManager()->getDataSourceIterator($datagrid, $fields);
  517. }
  518. /**
  519. * {@inheritdoc}
  520. */
  521. public function validate(ErrorElement $errorElement, $object)
  522. {
  523. }
  524. /**
  525. * define custom variable.
  526. */
  527. public function initialize()
  528. {
  529. if (!$this->classnameLabel) {
  530. $this->classnameLabel = substr($this->getClass(), strrpos($this->getClass(), '\\') + 1);
  531. }
  532. $this->baseCodeRoute = $this->getCode();
  533. $this->configure();
  534. }
  535. /**
  536. * {@inheritdoc}
  537. */
  538. public function configure()
  539. {
  540. }
  541. /**
  542. * {@inheritdoc}
  543. */
  544. public function update($object)
  545. {
  546. $this->preUpdate($object);
  547. foreach ($this->extensions as $extension) {
  548. $extension->preUpdate($this, $object);
  549. }
  550. $result = $this->getModelManager()->update($object);
  551. // BC compatibility
  552. if (null !== $result) {
  553. $object = $result;
  554. }
  555. $this->postUpdate($object);
  556. foreach ($this->extensions as $extension) {
  557. $extension->postUpdate($this, $object);
  558. }
  559. return $object;
  560. }
  561. /**
  562. * {@inheritdoc}
  563. */
  564. public function create($object)
  565. {
  566. $this->prePersist($object);
  567. foreach ($this->extensions as $extension) {
  568. $extension->prePersist($this, $object);
  569. }
  570. $result = $this->getModelManager()->create($object);
  571. // BC compatibility
  572. if (null !== $result) {
  573. $object = $result;
  574. }
  575. $this->postPersist($object);
  576. foreach ($this->extensions as $extension) {
  577. $extension->postPersist($this, $object);
  578. }
  579. $this->createObjectSecurity($object);
  580. return $object;
  581. }
  582. /**
  583. * {@inheritdoc}
  584. */
  585. public function delete($object)
  586. {
  587. $this->preRemove($object);
  588. foreach ($this->extensions as $extension) {
  589. $extension->preRemove($this, $object);
  590. }
  591. $this->getSecurityHandler()->deleteObjectSecurity($this, $object);
  592. $this->getModelManager()->delete($object);
  593. $this->postRemove($object);
  594. foreach ($this->extensions as $extension) {
  595. $extension->postRemove($this, $object);
  596. }
  597. }
  598. /**
  599. * {@inheritdoc}
  600. */
  601. public function preValidate($object)
  602. {
  603. }
  604. /**
  605. * {@inheritdoc}
  606. */
  607. public function preUpdate($object)
  608. {
  609. }
  610. /**
  611. * {@inheritdoc}
  612. */
  613. public function postUpdate($object)
  614. {
  615. }
  616. /**
  617. * {@inheritdoc}
  618. */
  619. public function prePersist($object)
  620. {
  621. }
  622. /**
  623. * {@inheritdoc}
  624. */
  625. public function postPersist($object)
  626. {
  627. }
  628. /**
  629. * {@inheritdoc}
  630. */
  631. public function preRemove($object)
  632. {
  633. }
  634. /**
  635. * {@inheritdoc}
  636. */
  637. public function postRemove($object)
  638. {
  639. }
  640. /**
  641. * {@inheritdoc}
  642. */
  643. public function preBatchAction($actionName, ProxyQueryInterface $query, array &$idx, $allElements)
  644. {
  645. }
  646. /**
  647. * {@inheritdoc}
  648. */
  649. public function getFilterParameters()
  650. {
  651. $parameters = array();
  652. // build the values array
  653. if ($this->hasRequest()) {
  654. $filters = $this->request->query->get('filter', array());
  655. // if persisting filters, save filters to session, or pull them out of session if no new filters set
  656. if ($this->persistFilters) {
  657. if ($filters == array() && $this->request->query->get('filters') != 'reset') {
  658. $filters = $this->request->getSession()->get($this->getCode().'.filter.parameters', array());
  659. } else {
  660. $this->request->getSession()->set($this->getCode().'.filter.parameters', $filters);
  661. }
  662. }
  663. $parameters = array_merge(
  664. $this->getModelManager()->getDefaultSortValues($this->getClass()),
  665. $this->datagridValues,
  666. $filters
  667. );
  668. if (!$this->determinedPerPageValue($parameters['_per_page'])) {
  669. $parameters['_per_page'] = $this->maxPerPage;
  670. }
  671. // always force the parent value
  672. if ($this->isChild() && $this->getParentAssociationMapping()) {
  673. $name = str_replace('.', '__', $this->getParentAssociationMapping());
  674. $parameters[$name] = array('value' => $this->request->get($this->getParent()->getIdParameter()));
  675. }
  676. }
  677. return $parameters;
  678. }
  679. /**
  680. * {@inheritdoc}
  681. */
  682. public function buildDatagrid()
  683. {
  684. if ($this->datagrid) {
  685. return;
  686. }
  687. $filterParameters = $this->getFilterParameters();
  688. // transform _sort_by from a string to a FieldDescriptionInterface for the datagrid.
  689. if (isset($filterParameters['_sort_by']) && is_string($filterParameters['_sort_by'])) {
  690. if ($this->hasListFieldDescription($filterParameters['_sort_by'])) {
  691. $filterParameters['_sort_by'] = $this->getListFieldDescription($filterParameters['_sort_by']);
  692. } else {
  693. $filterParameters['_sort_by'] = $this->getModelManager()->getNewFieldDescriptionInstance(
  694. $this->getClass(),
  695. $filterParameters['_sort_by'],
  696. array()
  697. );
  698. $this->getListBuilder()->buildField(null, $filterParameters['_sort_by'], $this);
  699. }
  700. }
  701. // initialize the datagrid
  702. $this->datagrid = $this->getDatagridBuilder()->getBaseDatagrid($this, $filterParameters);
  703. $this->datagrid->getPager()->setMaxPageLinks($this->maxPageLinks);
  704. $mapper = new DatagridMapper($this->getDatagridBuilder(), $this->datagrid, $this);
  705. // build the datagrid filter
  706. $this->configureDatagridFilters($mapper);
  707. // ok, try to limit to add parent filter
  708. if ($this->isChild() && $this->getParentAssociationMapping() && !$mapper->has($this->getParentAssociationMapping())) {
  709. $mapper->add($this->getParentAssociationMapping(), null, array(
  710. 'show_filter' => false,
  711. 'label' => false,
  712. 'field_type' => 'sonata_type_model_hidden',
  713. 'field_options' => array(
  714. 'model_manager' => $this->getModelManager(),
  715. ),
  716. 'operator_type' => 'hidden',
  717. ), null, null, array(
  718. 'admin_code' => $this->getParent()->getCode(),
  719. ));
  720. }
  721. foreach ($this->getExtensions() as $extension) {
  722. $extension->configureDatagridFilters($mapper);
  723. }
  724. }
  725. /**
  726. * Returns the name of the parent related field, so the field can be use to set the default
  727. * value (ie the parent object) or to filter the object.
  728. *
  729. * @return string the name of the parent related field
  730. */
  731. public function getParentAssociationMapping()
  732. {
  733. return $this->parentAssociationMapping;
  734. }
  735. /**
  736. * Returns the baseRoutePattern used to generate the routing information.
  737. *
  738. * @throws \RuntimeException
  739. *
  740. * @return string the baseRoutePattern used to generate the routing information
  741. */
  742. public function getBaseRoutePattern()
  743. {
  744. if (null !== $this->cachedBaseRoutePattern) {
  745. return $this->cachedBaseRoutePattern;
  746. }
  747. if ($this->isChild()) { // the admin class is a child, prefix it with the parent route pattern
  748. if (!$this->baseRoutePattern) {
  749. preg_match(self::CLASS_REGEX, $this->class, $matches);
  750. if (!$matches) {
  751. throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', get_class($this)));
  752. }
  753. }
  754. $this->cachedBaseRoutePattern = sprintf('%s/{id}/%s',
  755. $this->getParent()->getBaseRoutePattern(),
  756. $this->baseRoutePattern ?: $this->urlize($matches[5], '-')
  757. );
  758. } elseif ($this->baseRoutePattern) {
  759. $this->cachedBaseRoutePattern = $this->baseRoutePattern;
  760. } else {
  761. preg_match(self::CLASS_REGEX, $this->class, $matches);
  762. if (!$matches) {
  763. throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', get_class($this)));
  764. }
  765. $this->cachedBaseRoutePattern = sprintf('/%s%s/%s',
  766. empty($matches[1]) ? '' : $this->urlize($matches[1], '-').'/',
  767. $this->urlize($matches[3], '-'),
  768. $this->urlize($matches[5], '-')
  769. );
  770. }
  771. return $this->cachedBaseRoutePattern;
  772. }
  773. /**
  774. * Returns the baseRouteName used to generate the routing information.
  775. *
  776. * @throws \RuntimeException
  777. *
  778. * @return string the baseRouteName used to generate the routing information
  779. */
  780. public function getBaseRouteName()
  781. {
  782. if (null !== $this->cachedBaseRouteName) {
  783. return $this->cachedBaseRouteName;
  784. }
  785. if ($this->isChild()) { // the admin class is a child, prefix it with the parent route name
  786. if (!$this->baseRouteName) {
  787. preg_match(self::CLASS_REGEX, $this->class, $matches);
  788. if (!$matches) {
  789. throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', get_class($this)));
  790. }
  791. }
  792. $this->cachedBaseRouteName = sprintf('%s_%s',
  793. $this->getParent()->getBaseRouteName(),
  794. $this->baseRouteName ?: $this->urlize($matches[5])
  795. );
  796. } elseif ($this->baseRouteName) {
  797. $this->cachedBaseRouteName = $this->baseRouteName;
  798. } else {
  799. preg_match(self::CLASS_REGEX, $this->class, $matches);
  800. if (!$matches) {
  801. throw new \RuntimeException(sprintf('Cannot automatically determine base route name, please define a default `baseRouteName` value for the admin class `%s`', get_class($this)));
  802. }
  803. $this->cachedBaseRouteName = sprintf('admin_%s%s_%s',
  804. empty($matches[1]) ? '' : $this->urlize($matches[1]).'_',
  805. $this->urlize($matches[3]),
  806. $this->urlize($matches[5])
  807. );
  808. }
  809. return $this->cachedBaseRouteName;
  810. }
  811. /**
  812. * urlize the given word.
  813. *
  814. * @param string $word
  815. * @param string $sep the separator
  816. *
  817. * @return string
  818. */
  819. public function urlize($word, $sep = '_')
  820. {
  821. return strtolower(preg_replace('/[^a-z0-9_]/i', $sep.'$1', $word));
  822. }
  823. /**
  824. * {@inheritdoc}
  825. */
  826. public function getClass()
  827. {
  828. // see https://github.com/sonata-project/SonataCoreBundle/commit/247eeb0a7ca7211142e101754769d70bc402a5b4
  829. if ($this->hasSubject() && is_object($this->getSubject())) {
  830. return ClassUtils::getClass($this->getSubject());
  831. }
  832. if (!$this->hasActiveSubClass()) {
  833. if (count($this->getSubClasses()) > 0) {
  834. $subject = $this->getSubject();
  835. if ($subject && is_object($subject)) {
  836. return ClassUtils::getClass($subject);
  837. }
  838. }
  839. return $this->class;
  840. }
  841. if ($this->getParentFieldDescription() && $this->hasActiveSubClass()) {
  842. throw new \RuntimeException('Feature not implemented: an embedded admin cannot have subclass');
  843. }
  844. $subClass = $this->getRequest()->query->get('subclass');
  845. return $this->getSubClass($subClass);
  846. }
  847. /**
  848. * {@inheritdoc}
  849. */
  850. public function getSubClasses()
  851. {
  852. return $this->subClasses;
  853. }
  854. /**
  855. * {@inheritdoc}
  856. */
  857. public function addSubClass($subClass)
  858. {
  859. if (!in_array($subClass, $this->subClasses)) {
  860. $this->subClasses[] = $subClass;
  861. }
  862. }
  863. /**
  864. * {@inheritdoc}
  865. */
  866. public function setSubClasses(array $subClasses)
  867. {
  868. $this->subClasses = $subClasses;
  869. }
  870. /**
  871. * {@inheritdoc}
  872. */
  873. public function hasSubClass($name)
  874. {
  875. return isset($this->subClasses[$name]);
  876. }
  877. /**
  878. * {@inheritdoc}
  879. */
  880. public function hasActiveSubClass()
  881. {
  882. if (count($this->subClasses) > 0 && $this->request) {
  883. return null !== $this->getRequest()->query->get('subclass');
  884. }
  885. return false;
  886. }
  887. /**
  888. * {@inheritdoc}
  889. */
  890. public function getActiveSubClass()
  891. {
  892. if (!$this->hasActiveSubClass()) {
  893. return;
  894. }
  895. return $this->getClass();
  896. }
  897. /**
  898. * {@inheritdoc}
  899. */
  900. public function getActiveSubclassCode()
  901. {
  902. if (!$this->hasActiveSubClass()) {
  903. return;
  904. }
  905. $subClass = $this->getRequest()->query->get('subclass');
  906. if (!$this->hasSubClass($subClass)) {
  907. return;
  908. }
  909. return $subClass;
  910. }
  911. /**
  912. * {@inheritdoc}
  913. */
  914. public function getBatchActions()
  915. {
  916. $actions = array();
  917. if ($this->hasRoute('delete') && $this->isGranted('DELETE')) {
  918. $actions['delete'] = array(
  919. 'label' => 'action_delete',
  920. 'translation_domain' => 'SonataAdminBundle',
  921. 'ask_confirmation' => true, // by default always true
  922. );
  923. }
  924. $actions = $this->configureBatchActions($actions);
  925. foreach ($this->getExtensions() as $extension) {
  926. // TODO: remove method check in next major release
  927. if (method_exists($extension, 'configureBatchActions')) {
  928. $actions = $extension->configureBatchActions($this, $actions);
  929. }
  930. }
  931. return $actions;
  932. }
  933. /**
  934. * {@inheritdoc}
  935. */
  936. public function getRoutes()
  937. {
  938. $this->buildRoutes();
  939. return $this->routes;
  940. }
  941. /**
  942. * {@inheritdoc}
  943. */
  944. public function getRouterIdParameter()
  945. {
  946. return $this->isChild() ? '{childId}' : '{id}';
  947. }
  948. /**
  949. * {@inheritdoc}
  950. */
  951. public function getIdParameter()
  952. {
  953. return $this->isChild() ? 'childId' : 'id';
  954. }
  955. /**
  956. * {@inheritdoc}
  957. */
  958. public function hasRoute($name)
  959. {
  960. if (!$this->routeGenerator) {
  961. throw new \RuntimeException('RouteGenerator cannot be null');
  962. }
  963. return $this->routeGenerator->hasAdminRoute($this, $name);
  964. }
  965. /**
  966. * {@inheritdoc}
  967. */
  968. public function isCurrentRoute($name, $adminCode = null)
  969. {
  970. if (!$this->hasRequest()) {
  971. return false;
  972. }
  973. $request = $this->getRequest();
  974. $route = $request->get('_route');
  975. if ($adminCode) {
  976. $admin = $this->getConfigurationPool()->getAdminByAdminCode($adminCode);
  977. } else {
  978. $admin = $this;
  979. }
  980. if (!$admin) {
  981. return false;
  982. }
  983. return ($admin->getBaseRouteName().'_'.$name) == $route;
  984. }
  985. /**
  986. * {@inheritdoc}
  987. */
  988. public function generateObjectUrl($name, $object, array $parameters = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
  989. {
  990. $parameters['id'] = $this->getUrlsafeIdentifier($object);
  991. return $this->generateUrl($name, $parameters, $absolute);
  992. }
  993. /**
  994. * {@inheritdoc}
  995. */
  996. public function generateUrl($name, array $parameters = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
  997. {
  998. return $this->routeGenerator->generateUrl($this, $name, $parameters, $absolute);
  999. }
  1000. /**
  1001. * {@inheritdoc}
  1002. */
  1003. public function generateMenuUrl($name, array $parameters = array(), $absolute = UrlGeneratorInterface::ABSOLUTE_PATH)
  1004. {
  1005. return $this->routeGenerator->generateMenuUrl($this, $name, $parameters, $absolute);
  1006. }
  1007. /**
  1008. * @param array $templates
  1009. */
  1010. public function setTemplates(array $templates)
  1011. {
  1012. $this->templates = $templates;
  1013. }
  1014. /**
  1015. * @param string $name
  1016. * @param string $template
  1017. */
  1018. public function setTemplate($name, $template)
  1019. {
  1020. $this->templates[$name] = $template;
  1021. }
  1022. /**
  1023. * @return array
  1024. */
  1025. public function getTemplates()
  1026. {
  1027. return $this->templates;
  1028. }
  1029. /**
  1030. * {@inheritdoc}
  1031. */
  1032. public function getTemplate($name)
  1033. {
  1034. if (isset($this->templates[$name])) {
  1035. return $this->templates[$name];
  1036. }
  1037. }
  1038. /**
  1039. * {@inheritdoc}
  1040. */
  1041. public function getNewInstance()
  1042. {
  1043. $object = $this->getModelManager()->getModelInstance($this->getClass());
  1044. foreach ($this->getExtensions() as $extension) {
  1045. $extension->alterNewInstance($this, $object);
  1046. }
  1047. return $object;
  1048. }
  1049. /**
  1050. * {@inheritdoc}
  1051. */
  1052. public function getFormBuilder()
  1053. {
  1054. $this->formOptions['data_class'] = $this->getClass();
  1055. $formBuilder = $this->getFormContractor()->getFormBuilder(
  1056. $this->getUniqid(),
  1057. $this->formOptions
  1058. );
  1059. $this->defineFormBuilder($formBuilder);
  1060. return $formBuilder;
  1061. }
  1062. /**
  1063. * This method is being called by the main admin class and the child class,
  1064. * the getFormBuilder is only call by the main admin class.
  1065. *
  1066. * @param FormBuilderInterface $formBuilder
  1067. */
  1068. public function defineFormBuilder(FormBuilderInterface $formBuilder)
  1069. {
  1070. $mapper = new FormMapper($this->getFormContractor(), $formBuilder, $this);
  1071. $this->configureFormFields($mapper);
  1072. foreach ($this->getExtensions() as $extension) {
  1073. $extension->configureFormFields($mapper);
  1074. }
  1075. $this->attachInlineValidator();
  1076. }
  1077. /**
  1078. * {@inheritdoc}
  1079. */
  1080. public function attachAdminClass(FieldDescriptionInterface $fieldDescription)
  1081. {
  1082. $pool = $this->getConfigurationPool();
  1083. $adminCode = $fieldDescription->getOption('admin_code');
  1084. if ($adminCode !== null) {
  1085. $admin = $pool->getAdminByAdminCode($adminCode);
  1086. } else {
  1087. $admin = $pool->getAdminByClass($fieldDescription->getTargetEntity());
  1088. }
  1089. if (!$admin) {
  1090. return;
  1091. }
  1092. if ($this->hasRequest()) {
  1093. $admin->setRequest($this->getRequest());
  1094. }
  1095. $fieldDescription->setAssociationAdmin($admin);
  1096. }
  1097. /**
  1098. * {@inheritdoc}
  1099. */
  1100. public function getObject($id)
  1101. {
  1102. $object = $this->getModelManager()->find($this->getClass(), $id);
  1103. foreach ($this->getExtensions() as $extension) {
  1104. $extension->alterObject($this, $object);
  1105. }
  1106. return $object;
  1107. }
  1108. /**
  1109. * {@inheritdoc}
  1110. */
  1111. public function getForm()
  1112. {
  1113. $this->buildForm();
  1114. return $this->form;
  1115. }
  1116. /**
  1117. * {@inheritdoc}
  1118. */
  1119. public function getList()
  1120. {
  1121. $this->buildList();
  1122. return $this->list;
  1123. }
  1124. /**
  1125. * {@inheritdoc}
  1126. */
  1127. public function createQuery($context = 'list')
  1128. {
  1129. if (func_num_args() > 0) {
  1130. @trigger_error(
  1131. 'The $context argument of '.__METHOD__.' is deprecated since 3.3, to be removed in 4.0.',
  1132. E_USER_DEPRECATED
  1133. );
  1134. }
  1135. $query = $this->getModelManager()->createQuery($this->class);
  1136. foreach ($this->extensions as $extension) {
  1137. $extension->configureQuery($this, $query, $context);
  1138. }
  1139. return $query;
  1140. }
  1141. /**
  1142. * {@inheritdoc}
  1143. */
  1144. public function getDatagrid()
  1145. {
  1146. $this->buildDatagrid();
  1147. return $this->datagrid;
  1148. }
  1149. /**
  1150. * {@inheritdoc}
  1151. */
  1152. public function buildTabMenu($action, AdminInterface $childAdmin = null)
  1153. {
  1154. if ($this->loaded['tab_menu']) {
  1155. return;
  1156. }
  1157. $this->loaded['tab_menu'] = true;
  1158. $menu = $this->menuFactory->createItem('root');
  1159. $menu->setChildrenAttribute('class', 'nav navbar-nav');
  1160. $menu->setExtra('translation_domain', $this->translationDomain);
  1161. // Prevents BC break with KnpMenuBundle v1.x
  1162. if (method_exists($menu, 'setCurrentUri')) {
  1163. $menu->setCurrentUri($this->getRequest()->getBaseUrl().$this->getRequest()->getPathInfo());
  1164. }
  1165. $this->configureTabMenu($menu, $action, $childAdmin);
  1166. foreach ($this->getExtensions() as $extension) {
  1167. $extension->configureTabMenu($this, $menu, $action, $childAdmin);
  1168. }
  1169. $this->menu = $menu;
  1170. }
  1171. /**
  1172. * {@inheritdoc}
  1173. */
  1174. public function buildSideMenu($action, AdminInterface $childAdmin = null)
  1175. {
  1176. return $this->buildTabMenu($action, $childAdmin);
  1177. }
  1178. /**
  1179. * @param string $action
  1180. * @param AdminInterface $childAdmin
  1181. *
  1182. * @return ItemInterface
  1183. */
  1184. public function getSideMenu($action, AdminInterface $childAdmin = null)
  1185. {
  1186. if ($this->isChild()) {
  1187. return $this->getParent()->getSideMenu($action, $this);
  1188. }
  1189. $this->buildSideMenu($action, $childAdmin);
  1190. return $this->menu;
  1191. }
  1192. /**
  1193. * Returns the root code.
  1194. *
  1195. * @return string the root code
  1196. */
  1197. public function getRootCode()
  1198. {
  1199. return $this->getRoot()->getCode();
  1200. }
  1201. /**
  1202. * Returns the master admin.
  1203. *
  1204. * @return Admin the root admin class
  1205. */
  1206. public function getRoot()
  1207. {
  1208. $parentFieldDescription = $this->getParentFieldDescription();
  1209. if (!$parentFieldDescription) {
  1210. return $this;
  1211. }
  1212. return $parentFieldDescription->getAdmin()->getRoot();
  1213. }
  1214. /**
  1215. * {@inheritdoc}
  1216. */
  1217. public function setBaseControllerName($baseControllerName)
  1218. {
  1219. $this->baseControllerName = $baseControllerName;
  1220. }
  1221. /**
  1222. * {@inheritdoc}
  1223. */
  1224. public function getBaseControllerName()
  1225. {
  1226. return $this->baseControllerName;
  1227. }
  1228. /**
  1229. * @param string $label
  1230. */
  1231. public function setLabel($label)
  1232. {
  1233. $this->label = $label;
  1234. }
  1235. /**
  1236. * {@inheritdoc}
  1237. */
  1238. public function getLabel()
  1239. {
  1240. return $this->label;
  1241. }
  1242. /**
  1243. * @param bool $persist
  1244. */
  1245. public function setPersistFilters($persist)
  1246. {
  1247. $this->persistFilters = $persist;
  1248. }
  1249. /**
  1250. * @param int $maxPerPage
  1251. */
  1252. public function setMaxPerPage($maxPerPage)
  1253. {
  1254. $this->maxPerPage = $maxPerPage;
  1255. }
  1256. /**
  1257. * @return int
  1258. */
  1259. public function getMaxPerPage()
  1260. {
  1261. return $this->maxPerPage;
  1262. }
  1263. /**
  1264. * @param int $maxPageLinks
  1265. */
  1266. public function setMaxPageLinks($maxPageLinks)
  1267. {
  1268. $this->maxPageLinks = $maxPageLinks;
  1269. }
  1270. /**
  1271. * @return int
  1272. */
  1273. public function getMaxPageLinks()
  1274. {
  1275. return $this->maxPageLinks;
  1276. }
  1277. /**
  1278. * {@inheritdoc}
  1279. */
  1280. public function getFormGroups()
  1281. {
  1282. return $this->formGroups;
  1283. }
  1284. /**
  1285. * {@inheritdoc}
  1286. */
  1287. public function setFormGroups(array $formGroups)
  1288. {
  1289. $this->formGroups = $formGroups;
  1290. }
  1291. /**
  1292. * {@inheritdoc}
  1293. */
  1294. public function removeFieldFromFormGroup($key)
  1295. {
  1296. foreach ($this->formGroups as $name => $formGroup) {
  1297. unset($this->formGroups[$name]['fields'][$key]);
  1298. if (empty($this->formGroups[$name]['fields'])) {
  1299. unset($this->formGroups[$name]);
  1300. }
  1301. }
  1302. }
  1303. /**
  1304. * @param array $group
  1305. * @param array $keys
  1306. */
  1307. public function reorderFormGroup($group, array $keys)
  1308. {
  1309. $formGroups = $this->getFormGroups();
  1310. $formGroups[$group]['fields'] = array_merge(array_flip($keys), $formGroups[$group]['fields']);
  1311. $this->setFormGroups($formGroups);
  1312. }
  1313. /**
  1314. * {@inheritdoc}
  1315. */
  1316. public function getFormTabs()
  1317. {
  1318. return $this->formTabs;
  1319. }
  1320. /**
  1321. * {@inheritdoc}
  1322. */
  1323. public function setFormTabs(array $formTabs)
  1324. {
  1325. $this->formTabs = $formTabs;
  1326. }
  1327. /**
  1328. * {@inheritdoc}
  1329. */
  1330. public function getShowTabs()
  1331. {
  1332. return $this->showTabs;
  1333. }
  1334. /**
  1335. * {@inheritdoc}
  1336. */
  1337. public function setShowTabs(array $showTabs)
  1338. {
  1339. $this->showTabs = $showTabs;
  1340. }
  1341. /**
  1342. * {@inheritdoc}
  1343. */
  1344. public function getShowGroups()
  1345. {
  1346. return $this->showGroups;
  1347. }
  1348. /**
  1349. * {@inheritdoc}
  1350. */
  1351. public function setShowGroups(array $showGroups)
  1352. {
  1353. $this->showGroups = $showGroups;
  1354. }
  1355. /**
  1356. * {@inheritdoc}
  1357. */
  1358. public function reorderShowGroup($group, array $keys)
  1359. {
  1360. $showGroups = $this->getShowGroups();
  1361. $showGroups[$group]['fields'] = array_merge(array_flip($keys), $showGroups[$group]['fields']);
  1362. $this->setShowGroups($showGroups);
  1363. }
  1364. /**
  1365. * {@inheritdoc}
  1366. */
  1367. public function setParentFieldDescription(FieldDescriptionInterface $parentFieldDescription)
  1368. {
  1369. $this->parentFieldDescription = $parentFieldDescription;
  1370. }
  1371. /**
  1372. * {@inheritdoc}
  1373. */
  1374. public function getParentFieldDescription()
  1375. {
  1376. return $this->parentFieldDescription;
  1377. }
  1378. /**
  1379. * {@inheritdoc}
  1380. */
  1381. public function hasParentFieldDescription()
  1382. {
  1383. return $this->parentFieldDescription instanceof FieldDescriptionInterface;
  1384. }
  1385. /**
  1386. * {@inheritdoc}
  1387. */
  1388. public function setSubject($subject)
  1389. {
  1390. if (is_object($subject) && !is_a($subject, $this->class, true)) {
  1391. $message = <<<'EOT'
  1392. You are trying to set entity an instance of "%s",
  1393. which is not the one registered with this admin class ("%s").
  1394. This is deprecated since 3.x and will no longer be supported in 4.0.
  1395. EOT;
  1396. trigger_error(
  1397. sprintf($message, get_class($subject), $this->class),
  1398. E_USER_DEPRECATED
  1399. ); // NEXT_MAJOR : throw an exception instead
  1400. }
  1401. $this->subject = $subject;
  1402. }
  1403. /**
  1404. * {@inheritdoc}
  1405. */
  1406. public function getSubject()
  1407. {
  1408. if ($this->subject === null && $this->request) {
  1409. $id = $this->request->get($this->getIdParameter());
  1410. $this->subject = $this->getModelManager()->find($this->class, $id);
  1411. }
  1412. return $this->subject;
  1413. }
  1414. /**
  1415. * {@inheritdoc}
  1416. */
  1417. public function hasSubject()
  1418. {
  1419. return $this->subject != null;
  1420. }
  1421. /**
  1422. * {@inheritdoc}
  1423. */
  1424. public function getFormFieldDescriptions()
  1425. {
  1426. $this->buildForm();
  1427. return $this->formFieldDescriptions;
  1428. }
  1429. /**
  1430. * {@inheritdoc}
  1431. */
  1432. public function getFormFieldDescription($name)
  1433. {
  1434. return $this->hasFormFieldDescription($name) ? $this->formFieldDescriptions[$name] : null;
  1435. }
  1436. /**
  1437. * Returns true if the admin has a FieldDescription with the given $name.
  1438. *
  1439. * @param string $name
  1440. *
  1441. * @return bool
  1442. */
  1443. public function hasFormFieldDescription($name)
  1444. {
  1445. return array_key_exists($name, $this->formFieldDescriptions) ? true : false;
  1446. }
  1447. /**
  1448. * {@inheritdoc}
  1449. */
  1450. public function addFormFieldDescription($name, FieldDescriptionInterface $fieldDescription)
  1451. {
  1452. $this->formFieldDescriptions[$name] = $fieldDescription;
  1453. }
  1454. /**
  1455. * remove a FieldDescription.
  1456. *
  1457. * @param string $name
  1458. */
  1459. public function removeFormFieldDescription($name)
  1460. {
  1461. unset($this->formFieldDescriptions[$name]);
  1462. }
  1463. /**
  1464. * build and return the collection of form FieldDescription.
  1465. *
  1466. * @return array collection of form FieldDescription
  1467. */
  1468. public function getShowFieldDescriptions()
  1469. {
  1470. $this->buildShow();
  1471. return $this->showFieldDescriptions;
  1472. }
  1473. /**
  1474. * Returns the form FieldDescription with the given $name.
  1475. *
  1476. * @param string $name
  1477. *
  1478. * @return FieldDescriptionInterface
  1479. */
  1480. public function getShowFieldDescription($name)
  1481. {
  1482. $this->buildShow();
  1483. return $this->hasShowFieldDescription($name) ? $this->showFieldDescriptions[$name] : null;
  1484. }
  1485. /**
  1486. * {@inheritdoc}
  1487. */
  1488. public function hasShowFieldDescription($name)
  1489. {
  1490. return array_key_exists($name, $this->showFieldDescriptions);
  1491. }
  1492. /**
  1493. * {@inheritdoc}
  1494. */
  1495. public function addShowFieldDescription($name, FieldDescriptionInterface $fieldDescription)
  1496. {
  1497. $this->showFieldDescriptions[$name] = $fieldDescription;
  1498. }
  1499. /**
  1500. * {@inheritdoc}
  1501. */
  1502. public function removeShowFieldDescription($name)
  1503. {
  1504. unset($this->showFieldDescriptions[$name]);
  1505. }
  1506. /**
  1507. * {@inheritdoc}
  1508. */
  1509. public function getListFieldDescriptions()
  1510. {
  1511. $this->buildList();
  1512. return $this->listFieldDescriptions;
  1513. }
  1514. /**
  1515. * {@inheritdoc}
  1516. */
  1517. public function getListFieldDescription($name)
  1518. {
  1519. return $this->hasListFieldDescription($name) ? $this->listFieldDescriptions[$name] : null;
  1520. }
  1521. /**
  1522. * {@inheritdoc}
  1523. */
  1524. public function hasListFieldDescription($name)
  1525. {
  1526. $this->buildList();
  1527. return array_key_exists($name, $this->listFieldDescriptions) ? true : false;
  1528. }
  1529. /**
  1530. * {@inheritdoc}
  1531. */
  1532. public function addListFieldDescription($name, FieldDescriptionInterface $fieldDescription)
  1533. {
  1534. $this->listFieldDescriptions[$name] = $fieldDescription;
  1535. }
  1536. /**
  1537. * {@inheritdoc}
  1538. */
  1539. public function removeListFieldDescription($name)
  1540. {
  1541. unset($this->listFieldDescriptions[$name]);
  1542. }
  1543. /**
  1544. * {@inheritdoc}
  1545. */
  1546. public function getFilterFieldDescription($name)
  1547. {
  1548. return $this->hasFilterFieldDescription($name) ? $this->filterFieldDescriptions[$name] : null;
  1549. }
  1550. /**
  1551. * {@inheritdoc}
  1552. */
  1553. public function hasFilterFieldDescription($name)
  1554. {
  1555. return array_key_exists($name, $this->filterFieldDescriptions) ? true : false;
  1556. }
  1557. /**
  1558. * {@inheritdoc}
  1559. */
  1560. public function addFilterFieldDescription($name, FieldDescriptionInterface $fieldDescription)
  1561. {
  1562. $this->filterFieldDescriptions[$name] = $fieldDescription;
  1563. }
  1564. /**
  1565. * {@inheritdoc}
  1566. */
  1567. public function removeFilterFieldDescription($name)
  1568. {
  1569. unset($this->filterFieldDescriptions[$name]);
  1570. }
  1571. /**
  1572. * {@inheritdoc}
  1573. */
  1574. public function getFilterFieldDescriptions()
  1575. {
  1576. $this->buildDatagrid();
  1577. return $this->filterFieldDescriptions;
  1578. }
  1579. /**
  1580. * {@inheritdoc}
  1581. */
  1582. public function addChild(AdminInterface $child)
  1583. {
  1584. $this->children[$child->getCode()] = $child;
  1585. $child->setBaseCodeRoute($this->getCode().'|'.$child->getCode());
  1586. $child->setParent($this);
  1587. }
  1588. /**
  1589. * {@inheritdoc}
  1590. */
  1591. public function hasChild($code)
  1592. {
  1593. return isset($this->children[$code]);
  1594. }
  1595. /**
  1596. * {@inheritdoc}
  1597. */
  1598. public function getChildren()
  1599. {
  1600. return $this->children;
  1601. }
  1602. /**
  1603. * {@inheritdoc}
  1604. */
  1605. public function getChild($code)
  1606. {
  1607. return $this->hasChild($code) ? $this->children[$code] : null;
  1608. }
  1609. /**
  1610. * {@inheritdoc}
  1611. */
  1612. public function setParent(AdminInterface $parent)
  1613. {
  1614. $this->parent = $parent;
  1615. }
  1616. /**
  1617. * {@inheritdoc}
  1618. */
  1619. public function getParent()
  1620. {
  1621. return $this->parent;
  1622. }
  1623. /**
  1624. * {@inheritdoc}
  1625. */
  1626. public function isChild()
  1627. {
  1628. return $this->parent instanceof AdminInterface;
  1629. }
  1630. /**
  1631. * Returns true if the admin has children, false otherwise.
  1632. *
  1633. * @return bool if the admin has children
  1634. */
  1635. public function hasChildren()
  1636. {
  1637. return count($this->children) > 0;
  1638. }
  1639. /**
  1640. * {@inheritdoc}
  1641. */
  1642. public function setUniqid($uniqid)
  1643. {
  1644. $this->uniqid = $uniqid;
  1645. }
  1646. /**
  1647. * {@inheritdoc}
  1648. */
  1649. public function getUniqid()
  1650. {
  1651. if (!$this->uniqid) {
  1652. $this->uniqid = 's'.uniqid();
  1653. }
  1654. return $this->uniqid;
  1655. }
  1656. /**
  1657. * Returns the classname label.
  1658. *
  1659. * @return string the classname label
  1660. */
  1661. public function getClassnameLabel()
  1662. {
  1663. return $this->classnameLabel;
  1664. }
  1665. /**
  1666. * {@inheritdoc}
  1667. */
  1668. public function getPersistentParameters()
  1669. {
  1670. $parameters = array();
  1671. foreach ($this->getExtensions() as $extension) {
  1672. $params = $extension->getPersistentParameters($this);
  1673. if (!is_array($params)) {
  1674. throw new \RuntimeException(sprintf('The %s::getPersistentParameters must return an array', get_class($extension)));
  1675. }
  1676. $parameters = array_merge($parameters, $params);
  1677. }
  1678. return $parameters;
  1679. }
  1680. /**
  1681. * @param string $name
  1682. *
  1683. * @return null|mixed
  1684. */
  1685. public function getPersistentParameter($name)
  1686. {
  1687. $parameters = $this->getPersistentParameters();
  1688. return isset($parameters[$name]) ? $parameters[$name] : null;
  1689. }
  1690. /**
  1691. * {@inheritdoc}
  1692. */
  1693. public function getBreadcrumbs($action)
  1694. {
  1695. @trigger_error(
  1696. 'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
  1697. ' Use Sonata\AdminBundle\Admin\BreadcrumbsBuilder::getBreadcrumbs instead.',
  1698. E_USER_DEPRECATED
  1699. );
  1700. return $this->getBreadcrumbsBuilder()->getBreadcrumbs($this, $action);
  1701. }
  1702. /**
  1703. * Generates the breadcrumbs array.
  1704. *
  1705. * Note: the method will be called by the top admin instance (parent => child)
  1706. *
  1707. * @param string $action
  1708. * @param ItemInterface|null $menu
  1709. *
  1710. * @return array
  1711. */
  1712. public function buildBreadcrumbs($action, MenuItemInterface $menu = null)
  1713. {
  1714. @trigger_error(
  1715. 'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.',
  1716. E_USER_DEPRECATED
  1717. );
  1718. if (isset($this->breadcrumbs[$action])) {
  1719. return $this->breadcrumbs[$action];
  1720. }
  1721. return $this->breadcrumbs[$action] = $this->getBreadcrumbsBuilder()
  1722. ->buildBreadcrumbs($this, $action, $menu);
  1723. }
  1724. /**
  1725. * NEXT_MAJOR : remove this method.
  1726. *
  1727. * @return BreadcrumbsBuilderInterface
  1728. */
  1729. final public function getBreadcrumbsBuilder()
  1730. {
  1731. @trigger_error(
  1732. 'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
  1733. ' Use the sonata.admin.breadcrumbs_builder service instead.',
  1734. E_USER_DEPRECATED
  1735. );
  1736. if ($this->breadcrumbsBuilder === null) {
  1737. $this->breadcrumbsBuilder = new BreadcrumbsBuilder();
  1738. }
  1739. return $this->breadcrumbsBuilder;
  1740. }
  1741. /**
  1742. * NEXT_MAJOR : remove this method.
  1743. *
  1744. * @param BreadcrumbsBuilderInterface
  1745. *
  1746. * @return AbstractAdmin
  1747. */
  1748. final public function setBreadcrumbsBuilder(BreadcrumbsBuilderInterface $value)
  1749. {
  1750. @trigger_error(
  1751. 'The '.__METHOD__.' method is deprecated since version 3.2 and will be removed in 4.0.'.
  1752. ' Use the sonata.admin.breadcrumbs_builder service instead.',
  1753. E_USER_DEPRECATED
  1754. );
  1755. $this->breadcrumbsBuilder = $value;
  1756. return $this;
  1757. }
  1758. /**
  1759. * {@inheritdoc}
  1760. */
  1761. public function setCurrentChild($currentChild)
  1762. {
  1763. $this->currentChild = $currentChild;
  1764. }
  1765. /**
  1766. * {@inheritdoc}
  1767. */
  1768. public function getCurrentChild()
  1769. {
  1770. return $this->currentChild;
  1771. }
  1772. /**
  1773. * Returns the current child admin instance.
  1774. *
  1775. * @return AdminInterface|null the current child admin instance
  1776. */
  1777. public function getCurrentChildAdmin()
  1778. {
  1779. foreach ($this->children as $children) {
  1780. if ($children->getCurrentChild()) {
  1781. return $children;
  1782. }
  1783. }
  1784. return;
  1785. }
  1786. /**
  1787. * {@inheritdoc}
  1788. */
  1789. public function trans($id, array $parameters = array(), $domain = null, $locale = null)
  1790. {
  1791. $domain = $domain ?: $this->getTranslationDomain();
  1792. if (!$this->translator) {
  1793. return $id;
  1794. }
  1795. return $this->translator->trans($id, $parameters, $domain, $locale);
  1796. }
  1797. /**
  1798. * Translate a message id.
  1799. *
  1800. * @param string $id
  1801. * @param int $count
  1802. * @param array $parameters
  1803. * @param string|null $domain
  1804. * @param string|null $locale
  1805. *
  1806. * @return string the translated string
  1807. */
  1808. public function transChoice($id, $count, array $parameters = array(), $domain = null, $locale = null)
  1809. {
  1810. $domain = $domain ?: $this->getTranslationDomain();
  1811. if (!$this->translator) {
  1812. return $id;
  1813. }
  1814. return $this->translator->transChoice($id, $count, $parameters, $domain, $locale);
  1815. }
  1816. /**
  1817. * {@inheritdoc}
  1818. */
  1819. public function setTranslationDomain($translationDomain)
  1820. {
  1821. $this->translationDomain = $translationDomain;
  1822. }
  1823. /**
  1824. * {@inheritdoc}
  1825. */
  1826. public function getTranslationDomain()
  1827. {
  1828. return $this->translationDomain;
  1829. }
  1830. /**
  1831. * {@inheritdoc}
  1832. */
  1833. public function setTranslator(TranslatorInterface $translator)
  1834. {
  1835. $this->translator = $translator;
  1836. }
  1837. /**
  1838. * {@inheritdoc}
  1839. */
  1840. public function getTranslator()
  1841. {
  1842. return $this->translator;
  1843. }
  1844. /**
  1845. * {@inheritdoc}
  1846. */
  1847. public function getTranslationLabel($label, $context = '', $type = '')
  1848. {
  1849. return $this->getLabelTranslatorStrategy()->getLabel($label, $context, $type);
  1850. }
  1851. /**
  1852. * {@inheritdoc}
  1853. */
  1854. public function setRequest(Request $request)
  1855. {
  1856. $this->request = $request;
  1857. foreach ($this->getChildren() as $children) {
  1858. $children->setRequest($request);
  1859. }
  1860. }
  1861. /**
  1862. * {@inheritdoc}
  1863. */
  1864. public function getRequest()
  1865. {
  1866. if (!$this->request) {
  1867. throw new \RuntimeException('The Request object has not been set');
  1868. }
  1869. return $this->request;
  1870. }
  1871. /**
  1872. * {@inheritdoc}
  1873. */
  1874. public function hasRequest()
  1875. {
  1876. return $this->request !== null;
  1877. }
  1878. /**
  1879. * {@inheritdoc}
  1880. */
  1881. public function setFormContractor(FormContractorInterface $formBuilder)
  1882. {
  1883. $this->formContractor = $formBuilder;
  1884. }
  1885. /**
  1886. * @return FormContractorInterface
  1887. */
  1888. public function getFormContractor()
  1889. {
  1890. return $this->formContractor;
  1891. }
  1892. /**
  1893. * {@inheritdoc}
  1894. */
  1895. public function setDatagridBuilder(DatagridBuilderInterface $datagridBuilder)
  1896. {
  1897. $this->datagridBuilder = $datagridBuilder;
  1898. }
  1899. /**
  1900. * {@inheritdoc}
  1901. */
  1902. public function getDatagridBuilder()
  1903. {
  1904. return $this->datagridBuilder;
  1905. }
  1906. /**
  1907. * {@inheritdoc}
  1908. */
  1909. public function setListBuilder(ListBuilderInterface $listBuilder)
  1910. {
  1911. $this->listBuilder = $listBuilder;
  1912. }
  1913. /**
  1914. * {@inheritdoc}
  1915. */
  1916. public function getListBuilder()
  1917. {
  1918. return $this->listBuilder;
  1919. }
  1920. /**
  1921. * @param ShowBuilderInterface $showBuilder
  1922. */
  1923. public function setShowBuilder(ShowBuilderInterface $showBuilder)
  1924. {
  1925. $this->showBuilder = $showBuilder;
  1926. }
  1927. /**
  1928. * @return ShowBuilderInterface
  1929. */
  1930. public function getShowBuilder()
  1931. {
  1932. return $this->showBuilder;
  1933. }
  1934. /**
  1935. * {@inheritdoc}
  1936. */
  1937. public function setConfigurationPool(Pool $configurationPool)
  1938. {
  1939. $this->configurationPool = $configurationPool;
  1940. }
  1941. /**
  1942. * @return Pool
  1943. */
  1944. public function getConfigurationPool()
  1945. {
  1946. return $this->configurationPool;
  1947. }
  1948. /**
  1949. * {@inheritdoc}
  1950. */
  1951. public function setRouteGenerator(RouteGeneratorInterface $routeGenerator)
  1952. {
  1953. $this->routeGenerator = $routeGenerator;
  1954. }
  1955. /**
  1956. * @return RouteGeneratorInterface
  1957. */
  1958. public function getRouteGenerator()
  1959. {
  1960. return $this->routeGenerator;
  1961. }
  1962. /**
  1963. * {@inheritdoc}
  1964. */
  1965. public function getCode()
  1966. {
  1967. return $this->code;
  1968. }
  1969. /**
  1970. * @param string $baseCodeRoute
  1971. */
  1972. public function setBaseCodeRoute($baseCodeRoute)
  1973. {
  1974. $this->baseCodeRoute = $baseCodeRoute;
  1975. }
  1976. /**
  1977. * {@inheritdoc}
  1978. */
  1979. public function getBaseCodeRoute()
  1980. {
  1981. return $this->baseCodeRoute;
  1982. }
  1983. /**
  1984. * {@inheritdoc}
  1985. */
  1986. public function getModelManager()
  1987. {
  1988. return $this->modelManager;
  1989. }
  1990. /**
  1991. * @param ModelManagerInterface $modelManager
  1992. */
  1993. public function setModelManager(ModelManagerInterface $modelManager)
  1994. {
  1995. $this->modelManager = $modelManager;
  1996. }
  1997. /**
  1998. * {@inheritdoc}
  1999. */
  2000. public function getManagerType()
  2001. {
  2002. return $this->managerType;
  2003. }
  2004. /**
  2005. * @param string $type
  2006. */
  2007. public function setManagerType($type)
  2008. {
  2009. $this->managerType = $type;
  2010. }
  2011. /**
  2012. * {@inheritdoc}
  2013. */
  2014. public function getObjectIdentifier()
  2015. {
  2016. return $this->getCode();
  2017. }
  2018. /**
  2019. * Set the roles and permissions per role.
  2020. *
  2021. * @param array $information
  2022. */
  2023. public function setSecurityInformation(array $information)
  2024. {
  2025. $this->securityInformation = $information;
  2026. }
  2027. /**
  2028. * {@inheritdoc}
  2029. */
  2030. public function getSecurityInformation()
  2031. {
  2032. return $this->securityInformation;
  2033. }
  2034. /**
  2035. * Return the list of permissions the user should have in order to display the admin.
  2036. *
  2037. * @param string $context
  2038. *
  2039. * @return array
  2040. */
  2041. public function getPermissionsShow($context)
  2042. {
  2043. switch ($context) {
  2044. case self::CONTEXT_DASHBOARD:
  2045. case self::CONTEXT_MENU:
  2046. default:
  2047. return array('LIST');
  2048. }
  2049. }
  2050. /**
  2051. * {@inheritdoc}
  2052. */
  2053. public function showIn($context)
  2054. {
  2055. switch ($context) {
  2056. case self::CONTEXT_DASHBOARD:
  2057. case self::CONTEXT_MENU:
  2058. default:
  2059. return $this->isGranted($this->getPermissionsShow($context));
  2060. }
  2061. }
  2062. /**
  2063. * {@inheritdoc}
  2064. */
  2065. public function createObjectSecurity($object)
  2066. {
  2067. $this->getSecurityHandler()->createObjectSecurity($this, $object);
  2068. }
  2069. /**
  2070. * {@inheritdoc}
  2071. */
  2072. public function setSecurityHandler(SecurityHandlerInterface $securityHandler)
  2073. {
  2074. $this->securityHandler = $securityHandler;
  2075. }
  2076. /**
  2077. * {@inheritdoc}
  2078. */
  2079. public function getSecurityHandler()
  2080. {
  2081. return $this->securityHandler;
  2082. }
  2083. /**
  2084. * {@inheritdoc}
  2085. */
  2086. public function isGranted($name, $object = null)
  2087. {
  2088. $key = md5(json_encode($name).($object ? '/'.spl_object_hash($object) : ''));
  2089. if (!array_key_exists($key, $this->cacheIsGranted)) {
  2090. $this->cacheIsGranted[$key] = $this->securityHandler->isGranted($this, $name, $object ?: $this);
  2091. }
  2092. return $this->cacheIsGranted[$key];
  2093. }
  2094. /**
  2095. * {@inheritdoc}
  2096. */
  2097. public function getUrlsafeIdentifier($entity)
  2098. {
  2099. return $this->getModelManager()->getUrlsafeIdentifier($entity);
  2100. }
  2101. /**
  2102. * {@inheritdoc}
  2103. */
  2104. public function getNormalizedIdentifier($entity)
  2105. {
  2106. return $this->getModelManager()->getNormalizedIdentifier($entity);
  2107. }
  2108. /**
  2109. * {@inheritdoc}
  2110. */
  2111. public function id($entity)
  2112. {
  2113. return $this->getNormalizedIdentifier($entity);
  2114. }
  2115. /**
  2116. * {@inheritdoc}
  2117. */
  2118. public function setValidator($validator)
  2119. {
  2120. // TODO: Remove it when bumping requirements to SF 2.5+
  2121. if (!$validator instanceof ValidatorInterface && !$validator instanceof LegacyValidatorInterface) {
  2122. throw new \InvalidArgumentException('Argument 1 must be an instance of Symfony\Component\Validator\Validator\ValidatorInterface or Symfony\Component\Validator\ValidatorInterface');
  2123. }
  2124. $this->validator = $validator;
  2125. }
  2126. /**
  2127. * {@inheritdoc}
  2128. */
  2129. public function getValidator()
  2130. {
  2131. return $this->validator;
  2132. }
  2133. /**
  2134. * {@inheritdoc}
  2135. */
  2136. public function getShow()
  2137. {
  2138. $this->buildShow();
  2139. return $this->show;
  2140. }
  2141. /**
  2142. * {@inheritdoc}
  2143. */
  2144. public function setFormTheme(array $formTheme)
  2145. {
  2146. $this->formTheme = $formTheme;
  2147. }
  2148. /**
  2149. * {@inheritdoc}
  2150. */
  2151. public function getFormTheme()
  2152. {
  2153. return $this->formTheme;
  2154. }
  2155. /**
  2156. * {@inheritdoc}
  2157. */
  2158. public function setFilterTheme(array $filterTheme)
  2159. {
  2160. $this->filterTheme = $filterTheme;
  2161. }
  2162. /**
  2163. * {@inheritdoc}
  2164. */
  2165. public function getFilterTheme()
  2166. {
  2167. return $this->filterTheme;
  2168. }
  2169. /**
  2170. * {@inheritdoc}
  2171. */
  2172. public function addExtension(AdminExtensionInterface $extension)
  2173. {
  2174. $this->extensions[] = $extension;
  2175. }
  2176. /**
  2177. * {@inheritdoc}
  2178. */
  2179. public function getExtensions()
  2180. {
  2181. return $this->extensions;
  2182. }
  2183. /**
  2184. * {@inheritdoc}
  2185. */
  2186. public function setMenuFactory(MenuFactoryInterface $menuFactory)
  2187. {
  2188. $this->menuFactory = $menuFactory;
  2189. }
  2190. /**
  2191. * {@inheritdoc}
  2192. */
  2193. public function getMenuFactory()
  2194. {
  2195. return $this->menuFactory;
  2196. }
  2197. /**
  2198. * {@inheritdoc}
  2199. */
  2200. public function setRouteBuilder(RouteBuilderInterface $routeBuilder)
  2201. {
  2202. $this->routeBuilder = $routeBuilder;
  2203. }
  2204. /**
  2205. * {@inheritdoc}
  2206. */
  2207. public function getRouteBuilder()
  2208. {
  2209. return $this->routeBuilder;
  2210. }
  2211. /**
  2212. * {@inheritdoc}
  2213. */
  2214. public function toString($object)
  2215. {
  2216. if (!is_object($object)) {
  2217. return '';
  2218. }
  2219. if (method_exists($object, '__toString') && null !== $object->__toString()) {
  2220. return (string) $object;
  2221. }
  2222. return sprintf('%s:%s', ClassUtils::getClass($object), spl_object_hash($object));
  2223. }
  2224. /**
  2225. * {@inheritdoc}
  2226. */
  2227. public function setLabelTranslatorStrategy(LabelTranslatorStrategyInterface $labelTranslatorStrategy)
  2228. {
  2229. $this->labelTranslatorStrategy = $labelTranslatorStrategy;
  2230. }
  2231. /**
  2232. * {@inheritdoc}
  2233. */
  2234. public function getLabelTranslatorStrategy()
  2235. {
  2236. return $this->labelTranslatorStrategy;
  2237. }
  2238. /**
  2239. * {@inheritdoc}
  2240. */
  2241. public function supportsPreviewMode()
  2242. {
  2243. return $this->supportsPreviewMode;
  2244. }
  2245. /**
  2246. * Set custom per page options.
  2247. *
  2248. * @param array $options
  2249. */
  2250. public function setPerPageOptions(array $options)
  2251. {
  2252. $this->perPageOptions = $options;
  2253. }
  2254. /**
  2255. * Returns predefined per page options.
  2256. *
  2257. * @return array
  2258. */
  2259. public function getPerPageOptions()
  2260. {
  2261. return $this->perPageOptions;
  2262. }
  2263. /**
  2264. * Set pager type.
  2265. *
  2266. * @param string $pagerType
  2267. */
  2268. public function setPagerType($pagerType)
  2269. {
  2270. $this->pagerType = $pagerType;
  2271. }
  2272. /**
  2273. * Get pager type.
  2274. *
  2275. * @return string
  2276. */
  2277. public function getPagerType()
  2278. {
  2279. return $this->pagerType;
  2280. }
  2281. /**
  2282. * Returns true if the per page value is allowed, false otherwise.
  2283. *
  2284. * @param int $perPage
  2285. *
  2286. * @return bool
  2287. */
  2288. public function determinedPerPageValue($perPage)
  2289. {
  2290. return in_array($perPage, $this->perPageOptions);
  2291. }
  2292. /**
  2293. * {@inheritdoc}
  2294. */
  2295. public function isAclEnabled()
  2296. {
  2297. return $this->getSecurityHandler() instanceof AclSecurityHandlerInterface;
  2298. }
  2299. /**
  2300. * {@inheritdoc}
  2301. */
  2302. public function getObjectMetadata($object)
  2303. {
  2304. return new Metadata($this->toString($object));
  2305. }
  2306. /**
  2307. * {@inheritdoc}
  2308. */
  2309. public function getListModes()
  2310. {
  2311. return $this->listModes;
  2312. }
  2313. /**
  2314. * {@inheritdoc}
  2315. */
  2316. public function setListMode($mode)
  2317. {
  2318. if (!$this->hasRequest()) {
  2319. throw new \RuntimeException(sprintf('No request attached to the current admin: %s', $this->getCode()));
  2320. }
  2321. $this->getRequest()->getSession()->set(sprintf('%s.list_mode', $this->getCode()), $mode);
  2322. }
  2323. /**
  2324. * {@inheritdoc}
  2325. */
  2326. public function getListMode()
  2327. {
  2328. if (!$this->hasRequest()) {
  2329. return 'list';
  2330. }
  2331. return $this->getRequest()->getSession()->get(sprintf('%s.list_mode', $this->getCode()), 'list');
  2332. }
  2333. /**
  2334. * {@inheritdoc}
  2335. */
  2336. public function getAccessMapping()
  2337. {
  2338. return $this->accessMapping;
  2339. }
  2340. /**
  2341. * {@inheritdoc}
  2342. */
  2343. public function checkAccess($action, $object = null)
  2344. {
  2345. $access = $this->getAccess();
  2346. if (!array_key_exists($action, $access)) {
  2347. 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));
  2348. }
  2349. if (!is_array($access[$action])) {
  2350. $access[$action] = array($access[$action]);
  2351. }
  2352. foreach ($access[$action] as $role) {
  2353. if (false === $this->isGranted($role, $object)) {
  2354. throw new AccessDeniedException(sprintf('Access Denied to the action %s and role %s', $action, $role));
  2355. }
  2356. }
  2357. }
  2358. /**
  2359. * Hook to handle access authorization, without throw Exception.
  2360. *
  2361. * @param string $action
  2362. * @param object $object
  2363. *
  2364. * @return bool
  2365. */
  2366. public function hasAccess($action, $object = null)
  2367. {
  2368. $access = $this->getAccess();
  2369. if (!array_key_exists($action, $access)) {
  2370. return false;
  2371. }
  2372. if (!is_array($access[$action])) {
  2373. $access[$action] = array($access[$action]);
  2374. }
  2375. foreach ($access[$action] as $role) {
  2376. if (false === $this->isGranted($role, $object)) {
  2377. return false;
  2378. }
  2379. }
  2380. return true;
  2381. }
  2382. /**
  2383. * {@inheritdoc}
  2384. */
  2385. public function configureActionButtons($action, $object = null)
  2386. {
  2387. $list = array();
  2388. if (in_array($action, array('tree', 'show', 'edit', 'delete', 'list', 'batch')) && $this->hasAccess('create')) {
  2389. $list['create'] = array(
  2390. 'template' => 'SonataAdminBundle:Button:create_button.html.twig',
  2391. );
  2392. }
  2393. if (in_array($action, array('show', 'delete', 'acl', 'history')) && $this->canAccessObject('edit', $object)) {
  2394. $list['edit'] = array(
  2395. 'template' => 'SonataAdminBundle:Button:edit_button.html.twig',
  2396. );
  2397. }
  2398. if (in_array($action, array('show', 'edit', 'acl')) && $this->canAccessObject('history', $object)) {
  2399. $list['history'] = array(
  2400. 'template' => 'SonataAdminBundle:Button:history_button.html.twig',
  2401. );
  2402. }
  2403. if (in_array($action, array('edit', 'history'))
  2404. && $this->isAclEnabled()
  2405. && $this->canAccessObject('acl', $object)
  2406. ) {
  2407. $list['acl'] = array(
  2408. 'template' => 'SonataAdminBundle:Button:acl_button.html.twig',
  2409. );
  2410. }
  2411. if (in_array($action, array('edit', 'history', 'acl'))
  2412. && $this->canAccessObject('show', $object)
  2413. && count($this->getShow()) > 0
  2414. ) {
  2415. $list['show'] = array(
  2416. 'template' => 'SonataAdminBundle:Button:show_button.html.twig',
  2417. );
  2418. }
  2419. if (in_array($action, array('show', 'edit', 'delete', 'acl', 'batch')) && $this->hasAccess('list')) {
  2420. $list['list'] = array(
  2421. 'template' => 'SonataAdminBundle:Button:list_button.html.twig',
  2422. );
  2423. }
  2424. return $list;
  2425. }
  2426. /**
  2427. * @param string $action
  2428. * @param mixed $object
  2429. *
  2430. * @return array
  2431. */
  2432. public function getActionButtons($action, $object = null)
  2433. {
  2434. $list = $this->configureActionButtons($action, $object);
  2435. foreach ($this->getExtensions() as $extension) {
  2436. // TODO: remove method check in next major release
  2437. if (method_exists($extension, 'configureActionButtons')) {
  2438. $list = $extension->configureActionButtons($this, $list, $action, $object);
  2439. }
  2440. }
  2441. return $list;
  2442. }
  2443. /**
  2444. * Get the list of actions that can be accessed directly from the dashboard.
  2445. *
  2446. * @return array
  2447. */
  2448. public function getDashboardActions()
  2449. {
  2450. $actions = array();
  2451. if ($this->hasRoute('create') && $this->isGranted('CREATE')) {
  2452. $actions['create'] = array(
  2453. 'label' => 'link_add',
  2454. 'translation_domain' => 'SonataAdminBundle',
  2455. 'template' => 'SonataAdminBundle:CRUD:dashboard__action_create.html.twig',
  2456. 'url' => $this->generateUrl('create'),
  2457. 'icon' => 'plus-circle',
  2458. );
  2459. }
  2460. if ($this->hasRoute('list') && $this->isGranted('LIST')) {
  2461. $actions['list'] = array(
  2462. 'label' => 'link_list',
  2463. 'translation_domain' => 'SonataAdminBundle',
  2464. 'url' => $this->generateUrl('list'),
  2465. 'icon' => 'list',
  2466. );
  2467. }
  2468. return $actions;
  2469. }
  2470. /**
  2471. * Setting to true will enable mosaic button for the admin screen.
  2472. * Setting to false will hide mosaic button for the admin screen.
  2473. *
  2474. * @param bool $isShown
  2475. */
  2476. final public function showMosaicButton($isShown)
  2477. {
  2478. if ($isShown) {
  2479. $this->listModes['mosaic'] = array('class' => self::MOSAIC_ICON_CLASS);
  2480. } else {
  2481. unset($this->listModes['mosaic']);
  2482. }
  2483. }
  2484. /**
  2485. * @param FormMapper $form
  2486. */
  2487. final public function getSearchResultLink($object)
  2488. {
  2489. foreach ($this->searchResultActions as $action) {
  2490. if ($this->hasRoute($action) && $this->hasAccess($action, $object)) {
  2491. return $this->generateObjectUrl($action, $object);
  2492. }
  2493. }
  2494. return;
  2495. }
  2496. /**
  2497. * {@inheritdoc}
  2498. */
  2499. protected function configureFormFields(FormMapper $form)
  2500. {
  2501. }
  2502. /**
  2503. * @param ListMapper $list
  2504. */
  2505. protected function configureListFields(ListMapper $list)
  2506. {
  2507. }
  2508. /**
  2509. * @param DatagridMapper $filter
  2510. */
  2511. protected function configureDatagridFilters(DatagridMapper $filter)
  2512. {
  2513. }
  2514. /**
  2515. * @param ShowMapper $show
  2516. */
  2517. protected function configureShowFields(ShowMapper $show)
  2518. {
  2519. }
  2520. /**
  2521. * @param RouteCollection $collection
  2522. */
  2523. protected function configureRoutes(RouteCollection $collection)
  2524. {
  2525. }
  2526. /**
  2527. * Allows you to customize batch actions.
  2528. *
  2529. * @param array $actions List of actions
  2530. *
  2531. * @return array
  2532. */
  2533. protected function configureBatchActions($actions)
  2534. {
  2535. return $actions;
  2536. }
  2537. /**
  2538. * DEPRECATED: Use configureTabMenu instead.
  2539. *
  2540. * @param MenuItemInterface $menu
  2541. * @param $action
  2542. * @param AdminInterface $childAdmin
  2543. *
  2544. * @return mixed
  2545. *
  2546. * @deprecated Use configureTabMenu instead
  2547. */
  2548. protected function configureSideMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
  2549. {
  2550. }
  2551. /**
  2552. * Configures the tab menu in your admin.
  2553. *
  2554. * @param MenuItemInterface $menu
  2555. * @param string $action
  2556. * @param AdminInterface $childAdmin
  2557. *
  2558. * @return mixed
  2559. */
  2560. protected function configureTabMenu(MenuItemInterface $menu, $action, AdminInterface $childAdmin = null)
  2561. {
  2562. // Use configureSideMenu not to mess with previous overrides
  2563. // TODO remove once deprecation period is over
  2564. $this->configureSideMenu($menu, $action, $childAdmin);
  2565. }
  2566. /**
  2567. * build the view FieldDescription array.
  2568. */
  2569. protected function buildShow()
  2570. {
  2571. if ($this->show) {
  2572. return;
  2573. }
  2574. $this->show = new FieldDescriptionCollection();
  2575. $mapper = new ShowMapper($this->showBuilder, $this->show, $this);
  2576. $this->configureShowFields($mapper);
  2577. foreach ($this->getExtensions() as $extension) {
  2578. $extension->configureShowFields($mapper);
  2579. }
  2580. }
  2581. /**
  2582. * build the list FieldDescription array.
  2583. */
  2584. protected function buildList()
  2585. {
  2586. if ($this->list) {
  2587. return;
  2588. }
  2589. $this->list = $this->getListBuilder()->getBaseList();
  2590. $mapper = new ListMapper($this->getListBuilder(), $this->list, $this);
  2591. if (count($this->getBatchActions()) > 0) {
  2592. $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance($this->getClass(), 'batch', array(
  2593. 'label' => 'batch',
  2594. 'code' => '_batch',
  2595. 'sortable' => false,
  2596. 'virtual_field' => true,
  2597. ));
  2598. $fieldDescription->setAdmin($this);
  2599. $fieldDescription->setTemplate($this->getTemplate('batch'));
  2600. $mapper->add($fieldDescription, 'batch');
  2601. }
  2602. $this->configureListFields($mapper);
  2603. foreach ($this->getExtensions() as $extension) {
  2604. $extension->configureListFields($mapper);
  2605. }
  2606. if ($this->hasRequest() && $this->getRequest()->isXmlHttpRequest()) {
  2607. $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance($this->getClass(), 'select', array(
  2608. 'label' => false,
  2609. 'code' => '_select',
  2610. 'sortable' => false,
  2611. 'virtual_field' => false,
  2612. ));
  2613. $fieldDescription->setAdmin($this);
  2614. $fieldDescription->setTemplate($this->getTemplate('select'));
  2615. $mapper->add($fieldDescription, 'select');
  2616. }
  2617. }
  2618. /**
  2619. * Build the form FieldDescription collection.
  2620. */
  2621. protected function buildForm()
  2622. {
  2623. if ($this->form) {
  2624. return;
  2625. }
  2626. // append parent object if any
  2627. // todo : clean the way the Admin class can retrieve set the object
  2628. if ($this->isChild() && $this->getParentAssociationMapping()) {
  2629. $parent = $this->getParent()->getObject($this->request->get($this->getParent()->getIdParameter()));
  2630. $propertyAccessor = $this->getConfigurationPool()->getPropertyAccessor();
  2631. $propertyPath = new PropertyPath($this->getParentAssociationMapping());
  2632. $object = $this->getSubject();
  2633. $value = $propertyAccessor->getValue($object, $propertyPath);
  2634. if (is_array($value) || ($value instanceof \Traversable && $value instanceof \ArrayAccess)) {
  2635. $value[] = $parent;
  2636. $propertyAccessor->setValue($object, $propertyPath, $value);
  2637. } else {
  2638. $propertyAccessor->setValue($object, $propertyPath, $parent);
  2639. }
  2640. }
  2641. $this->form = $this->getFormBuilder()->getForm();
  2642. }
  2643. /**
  2644. * Gets the subclass corresponding to the given name.
  2645. *
  2646. * @param string $name The name of the sub class
  2647. *
  2648. * @return string the subclass
  2649. */
  2650. protected function getSubClass($name)
  2651. {
  2652. if ($this->hasSubClass($name)) {
  2653. return $this->subClasses[$name];
  2654. }
  2655. throw new \RuntimeException(sprintf('Unable to find the subclass `%s` for admin `%s`', $name, get_class($this)));
  2656. }
  2657. /**
  2658. * Attach the inline validator to the model metadata, this must be done once per admin.
  2659. */
  2660. protected function attachInlineValidator()
  2661. {
  2662. $admin = $this;
  2663. // add the custom inline validation option
  2664. // TODO: Remove conditional method when bumping requirements to SF 2.5+
  2665. if (method_exists($this->validator, 'getMetadataFor')) {
  2666. $metadata = $this->validator->getMetadataFor($this->getClass());
  2667. } else {
  2668. $metadata = $this->validator->getMetadataFactory()->getMetadataFor($this->getClass());
  2669. }
  2670. $metadata->addConstraint(new InlineConstraint(array(
  2671. 'service' => $this,
  2672. 'method' => function (ErrorElement $errorElement, $object) use ($admin) {
  2673. /* @var \Sonata\AdminBundle\Admin\AdminInterface $admin */
  2674. // This avoid the main validation to be cascaded to children
  2675. // The problem occurs when a model Page has a collection of Page as property
  2676. if ($admin->hasSubject() && spl_object_hash($object) !== spl_object_hash($admin->getSubject())) {
  2677. return;
  2678. }
  2679. $admin->validate($errorElement, $object);
  2680. foreach ($admin->getExtensions() as $extension) {
  2681. $extension->validate($admin, $errorElement, $object);
  2682. }
  2683. },
  2684. 'serializingWarning' => true,
  2685. )));
  2686. }
  2687. /**
  2688. * Predefine per page options.
  2689. */
  2690. protected function predefinePerPageOptions()
  2691. {
  2692. array_unshift($this->perPageOptions, $this->maxPerPage);
  2693. $this->perPageOptions = array_unique($this->perPageOptions);
  2694. sort($this->perPageOptions);
  2695. }
  2696. /**
  2697. * Return list routes with permissions name.
  2698. *
  2699. * @return array
  2700. */
  2701. protected function getAccess()
  2702. {
  2703. $access = array_merge(array(
  2704. 'acl' => 'MASTER',
  2705. 'export' => 'EXPORT',
  2706. 'historyCompareRevisions' => 'EDIT',
  2707. 'historyViewRevision' => 'EDIT',
  2708. 'history' => 'EDIT',
  2709. 'edit' => 'EDIT',
  2710. 'show' => 'VIEW',
  2711. 'create' => 'CREATE',
  2712. 'delete' => 'DELETE',
  2713. 'batchDelete' => 'DELETE',
  2714. 'list' => 'LIST',
  2715. ), $this->getAccessMapping());
  2716. foreach ($this->extensions as $extension) {
  2717. // TODO: remove method check in next major release
  2718. if (method_exists($extension, 'getAccessMapping')) {
  2719. $access = array_merge($access, $extension->getAccessMapping($this));
  2720. }
  2721. }
  2722. return $access;
  2723. }
  2724. /**
  2725. * Build all the related urls to the current admin.
  2726. */
  2727. private function buildRoutes()
  2728. {
  2729. if ($this->loaded['routes']) {
  2730. return;
  2731. }
  2732. $this->loaded['routes'] = true;
  2733. $this->routes = new RouteCollection(
  2734. $this->getBaseCodeRoute(),
  2735. $this->getBaseRouteName(),
  2736. $this->getBaseRoutePattern(),
  2737. $this->getBaseControllerName()
  2738. );
  2739. $this->routeBuilder->build($this, $this->routes);
  2740. $this->configureRoutes($this->routes);
  2741. foreach ($this->getExtensions() as $extension) {
  2742. $extension->configureRoutes($this, $this->routes);
  2743. }
  2744. }
  2745. /**
  2746. * Check object existence and access, without throw Exception.
  2747. *
  2748. * @param string $action
  2749. * @param object $object
  2750. *
  2751. * @return bool
  2752. */
  2753. private function canAccessObject($action, $object)
  2754. {
  2755. return $object && $this->id($object) && $this->hasAccess($action, $object);
  2756. }
  2757. }