vendor/sulu/sulu/src/Sulu/Component/Content/SmartContent/PageDataProvider.php line 251

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Sulu.
  4.  *
  5.  * (c) Sulu GmbH
  6.  *
  7.  * This source file is subject to the MIT license that is bundled
  8.  * with this source code in the file LICENSE.
  9.  */
  10. namespace Sulu\Component\Content\SmartContent;
  11. use PHPCR\ItemNotFoundException;
  12. use PHPCR\SessionInterface;
  13. use ProxyManager\Factory\LazyLoadingValueHolderFactory;
  14. use ProxyManager\Proxy\LazyLoadingInterface;
  15. use Sulu\Bundle\AdminBundle\Metadata\FormMetadata\TypedFormMetadata;
  16. use Sulu\Bundle\AdminBundle\Metadata\MetadataProviderInterface;
  17. use Sulu\Bundle\PageBundle\Admin\PageAdmin;
  18. use Sulu\Bundle\PageBundle\Document\PageDocument;
  19. use Sulu\Bundle\WebsiteBundle\ReferenceStore\ReferenceStoreInterface;
  20. use Sulu\Component\Content\Compat\PropertyParameter;
  21. use Sulu\Component\Content\Query\ContentQueryBuilderInterface;
  22. use Sulu\Component\Content\Query\ContentQueryExecutorInterface;
  23. use Sulu\Component\DocumentManager\DocumentManagerInterface;
  24. use Sulu\Component\Security\Authentication\UserInterface;
  25. use Sulu\Component\Security\Authorization\PermissionTypes;
  26. use Sulu\Component\SmartContent\ArrayAccessItem;
  27. use Sulu\Component\SmartContent\Configuration\Builder;
  28. use Sulu\Component\SmartContent\Configuration\ProviderConfigurationInterface;
  29. use Sulu\Component\SmartContent\DataProviderAliasInterface;
  30. use Sulu\Component\SmartContent\DataProviderInterface;
  31. use Sulu\Component\SmartContent\DataProviderResult;
  32. use Sulu\Component\SmartContent\DatasourceItem;
  33. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  34. /**
  35.  * DataProvider for content.
  36.  */
  37. class PageDataProvider implements DataProviderInterfaceDataProviderAliasInterface
  38. {
  39.     /**
  40.      * @var ContentQueryBuilderInterface
  41.      */
  42.     private $contentQueryBuilder;
  43.     /**
  44.      * @var ContentQueryExecutorInterface
  45.      */
  46.     private $contentQueryExecutor;
  47.     /**
  48.      * @var DocumentManagerInterface
  49.      */
  50.     private $documentManager;
  51.     /**
  52.      * @var ProviderConfigurationInterface
  53.      */
  54.     private $configuration;
  55.     /**
  56.      * @var LazyLoadingValueHolderFactory
  57.      */
  58.     private $proxyFactory;
  59.     /**
  60.      * @var SessionInterface
  61.      */
  62.     private $session;
  63.     /**
  64.      * @var ReferenceStoreInterface
  65.      */
  66.     private $referenceStore;
  67.     /**
  68.      * @var bool
  69.      */
  70.     private $showDrafts;
  71.     /**
  72.      * @var array
  73.      */
  74.     private $permissions;
  75.     /*
  76.      * @var bool
  77.      */
  78.     private $hasAudienceTargeting;
  79.     /**
  80.      * @var MetadataProviderInterface|null
  81.      */
  82.     private $formMetadataProvider;
  83.     /**
  84.      * @var TokenStorageInterface|null
  85.      */
  86.     private $tokenStorage;
  87.     /**
  88.      * @var array
  89.      */
  90.     private $enabledTwigAttributes = [];
  91.     public function __construct(
  92.         ContentQueryBuilderInterface $contentQueryBuilder,
  93.         ContentQueryExecutorInterface $contentQueryExecutor,
  94.         DocumentManagerInterface $documentManager,
  95.         LazyLoadingValueHolderFactory $proxyFactory,
  96.         SessionInterface $session,
  97.         ReferenceStoreInterface $referenceStore,
  98.         $showDrafts,
  99.         $permissions,
  100.         bool $hasAudienceTargeting false,
  101.         MetadataProviderInterface $formMetadataProvider null,
  102.         TokenStorageInterface $tokenStorage null,
  103.         array $enabledTwigAttributes = [
  104.             'path' => true,
  105.         ]
  106.     ) {
  107.         $this->contentQueryBuilder $contentQueryBuilder;
  108.         $this->contentQueryExecutor $contentQueryExecutor;
  109.         $this->documentManager $documentManager;
  110.         $this->proxyFactory $proxyFactory;
  111.         $this->session $session;
  112.         $this->referenceStore $referenceStore;
  113.         $this->showDrafts $showDrafts;
  114.         $this->permissions $permissions;
  115.         $this->hasAudienceTargeting $hasAudienceTargeting;
  116.         $this->formMetadataProvider $formMetadataProvider;
  117.         $this->tokenStorage $tokenStorage;
  118.         $this->enabledTwigAttributes $enabledTwigAttributes;
  119.         if (!$formMetadataProvider) {
  120.             @\trigger_error('The usage of the "PageDataProvider" without setting the "FormMetadataProvider" is deprecated. Please inject the "FormMetadataProvider".'\E_USER_DEPRECATED);
  121.         }
  122.         if ($enabledTwigAttributes['path'] ?? true) {
  123.             @\trigger_error('Enabling the "path" parameter is deprecated since sulu/sulu 2.3.'\E_USER_DEPRECATED);
  124.         }
  125.     }
  126.     public function getConfiguration()
  127.     {
  128.         if (!$this->configuration) {
  129.             return $this->initConfiguration();
  130.         }
  131.         return $this->configuration;
  132.     }
  133.     /**
  134.      * Initiate configuration.
  135.      *
  136.      * @return ProviderConfigurationInterface
  137.      */
  138.     private function initConfiguration()
  139.     {
  140.         $builder Builder::create()
  141.             ->enableTags()
  142.             ->enableCategories()
  143.             ->enableLimit()
  144.             ->enablePagination()
  145.             ->enablePresentAs()
  146.             ->enableDatasource('pages''pages''column_list')
  147.             ->enableSorting(
  148.                 [
  149.                     ['column' => null'title' => 'sulu_admin.default'],
  150.                     ['column' => 'title''title' => 'sulu_admin.title'],
  151.                     ['column' => 'published''title' => 'sulu_admin.published'],
  152.                     ['column' => 'created''title' => 'sulu_admin.created'],
  153.                     ['column' => 'changed''title' => 'sulu_admin.changed'],
  154.                     ['column' => 'authored''title' => 'sulu_admin.authored'],
  155.                 ]
  156.             )
  157.             ->enableTypes($this->getTypes())
  158.             ->enableView(PageAdmin::EDIT_FORM_VIEW, ['id' => 'id''webspace' => 'webspace']);
  159.         if ($this->hasAudienceTargeting) {
  160.             $builder->enableAudienceTargeting();
  161.         }
  162.         $this->configuration $builder->getConfiguration();
  163.         return $this->configuration;
  164.     }
  165.     public function getDefaultPropertyParameter()
  166.     {
  167.         return [
  168.             'properties' => new PropertyParameter('properties', [], 'collection'),
  169.         ];
  170.     }
  171.     public function resolveDatasource($datasource, array $propertyParameter, array $options)
  172.     {
  173.         $properties \array_key_exists('properties'$propertyParameter) ?
  174.             $propertyParameter['properties']->getValue() : [];
  175.         $this->contentQueryBuilder->init(
  176.             [
  177.                 'ids' => [$datasource],
  178.                 'properties' => $properties,
  179.                 'published' => false,
  180.             ]
  181.         );
  182.         $result $this->contentQueryExecutor->execute(
  183.             $options['webspaceKey'],
  184.             [$options['locale']],
  185.             $this->contentQueryBuilder,
  186.             true,
  187.             -1,
  188.             1,
  189.             0,
  190.             false
  191.         );
  192.         if (=== \count($result)) {
  193.             return;
  194.         }
  195.         return new DatasourceItem($result[0]['id'], $result[0]['title'], $result[0]['url'] ?? null);
  196.     }
  197.     public function resolveDataItems(
  198.         array $filters,
  199.         array $propertyParameter,
  200.         array $options = [],
  201.         $limit null,
  202.         $page 1,
  203.         $pageSize null
  204.     ) {
  205.         list($items$hasNextPage) = $this->resolveFilters(
  206.             $filters,
  207.             $propertyParameter,
  208.             $options,
  209.             $limit,
  210.             $page,
  211.             $pageSize
  212.         );
  213.         $items $this->decorateDataItems($items$options['locale']);
  214.         return new DataProviderResult($items$hasNextPage);
  215.     }
  216.     public function resolveResourceItems(
  217.         array $filters,
  218.         array $propertyParameter,
  219.         array $options = [],
  220.         $limit null,
  221.         $page 1,
  222.         $pageSize null
  223.     ) {
  224.         list($items$hasNextPage) = $this->resolveFilters(
  225.             $filters,
  226.             $propertyParameter,
  227.             $options,
  228.             $limit,
  229.             $page,
  230.             $pageSize
  231.         );
  232.         $items $this->decorateResourceItems($items$options['locale']);
  233.         return new DataProviderResult($items$hasNextPage);
  234.     }
  235.     /**
  236.      * Resolves filters.
  237.      */
  238.     private function resolveFilters(
  239.         array $filters,
  240.         array $propertyParameter,
  241.         array $options = [],
  242.         $limit null,
  243.         $page 1,
  244.         $pageSize null
  245.     ) {
  246.         $emptyFilterResult = [[], false];
  247.         if (!\array_key_exists('dataSource'$filters)
  248.             || null === $filters['dataSource']
  249.             || '' === $filters['dataSource']
  250.             || (null !== $limit && $limit 1)
  251.         ) {
  252.             return $emptyFilterResult;
  253.         }
  254.         try {
  255.             $this->session->getNodeByIdentifier($filters['dataSource']);
  256.         } catch (ItemNotFoundException $e) {
  257.             return $emptyFilterResult;
  258.         }
  259.         $properties \array_key_exists('properties'$propertyParameter) ?
  260.             $propertyParameter['properties']->getValue() : [];
  261.         $excluded = isset($filters['excluded']) ? $filters['excluded'] : [];
  262.         if (\array_key_exists('exclude_duplicates'$propertyParameter)
  263.             && $propertyParameter['exclude_duplicates']->getValue()
  264.         ) {
  265.             $excluded \array_merge($excluded$this->referenceStore->getAll());
  266.         }
  267.         $this->contentQueryBuilder->init(
  268.             [
  269.                 'config' => $filters,
  270.                 'properties' => $properties,
  271.                 'excluded' => $excluded,
  272.                 'published' => !$this->showDrafts,
  273.             ]
  274.         );
  275.         $hasNextPage false;
  276.         if (null !== $pageSize) {
  277.             $result $this->loadPaginated($options$limit$page$pageSize);
  278.             $hasNextPage = (\count($result) > $pageSize);
  279.             $items \array_splice($result0$pageSize);
  280.         } else {
  281.             $items $this->load($options$limit);
  282.         }
  283.         return [$items$hasNextPage];
  284.     }
  285.     /**
  286.      * Load paginated data.
  287.      *
  288.      * @param int $limit
  289.      * @param int $page
  290.      * @param int $pageSize
  291.      *
  292.      * @return array
  293.      */
  294.     private function loadPaginated(array $options$limit$page$pageSize)
  295.     {
  296.         $pageSize \intval($pageSize);
  297.         $offset = ($page 1) * $pageSize;
  298.         $position $pageSize $page;
  299.         if (null !== $limit && $position >= $limit) {
  300.             $pageSize $limit $offset;
  301.             $loadLimit $pageSize;
  302.         } else {
  303.             $loadLimit $pageSize 1;
  304.         }
  305.         return $this->contentQueryExecutor->execute(
  306.             $options['webspaceKey'],
  307.             [$options['locale']],
  308.             $this->contentQueryBuilder,
  309.             true,
  310.             -1,
  311.             $loadLimit,
  312.             $offset,
  313.             false,
  314.             $this->permissions[PermissionTypes::VIEW]
  315.         );
  316.     }
  317.     /**
  318.      * Load data.
  319.      *
  320.      * @param int $limit
  321.      *
  322.      * @return array
  323.      */
  324.     private function load(array $options$limit)
  325.     {
  326.         return $this->contentQueryExecutor->execute(
  327.             $options['webspaceKey'],
  328.             [$options['locale']],
  329.             $this->contentQueryBuilder,
  330.             true,
  331.             -1,
  332.             $limit,
  333.             null,
  334.             false,
  335.             $this->permissions[PermissionTypes::VIEW]
  336.         );
  337.     }
  338.     /**
  339.      * Decorates result with item class.
  340.      *
  341.      * @param string $locale
  342.      *
  343.      * @return ContentDataItem[]
  344.      */
  345.     private function decorateDataItems(array $data$locale)
  346.     {
  347.         return \array_map(
  348.             function($item) use ($locale) {
  349.                 return new ContentDataItem($item$this->getResource($item['id'], $locale));
  350.             },
  351.             $data
  352.         );
  353.     }
  354.     /**
  355.      * Decorates result with item class.
  356.      *
  357.      * @param string $locale
  358.      *
  359.      * @return ArrayAccessItem[]
  360.      */
  361.     private function decorateResourceItems(array $data$locale)
  362.     {
  363.         return \array_map(
  364.             function($item) use ($locale) {
  365.                 $this->referenceStore->add($item['id']);
  366.                 if (!($this->enabledTwigAttributes['path'] ?? true)) {
  367.                     unset($item['path']);
  368.                 }
  369.                 return new ArrayAccessItem($item['id'], $item$this->getResource($item['id'], $locale));
  370.             },
  371.             $data
  372.         );
  373.     }
  374.     /**
  375.      * Returns Proxy Document for uuid.
  376.      *
  377.      * @param string $uuid
  378.      * @param string $locale
  379.      *
  380.      * @return object
  381.      */
  382.     private function getResource($uuid$locale)
  383.     {
  384.         return $this->proxyFactory->createProxy(
  385.             PageDocument::class,
  386.             function(
  387.                 &$wrappedObject,
  388.                 LazyLoadingInterface $proxy,
  389.                 $method,
  390.                 array $parameters,
  391.                 &$initializer
  392.             ) use ($uuid$locale) {
  393.                 $initializer null;
  394.                 $wrappedObject $this->documentManager->find($uuid$locale);
  395.                 return true;
  396.             }
  397.         );
  398.     }
  399.     /**
  400.      * @return array<int, array<string, string>>
  401.      */
  402.     private function getTypes(): array
  403.     {
  404.         $types = [];
  405.         if ($this->tokenStorage && null !== $this->tokenStorage->getToken() && $this->formMetadataProvider) {
  406.             $user $this->tokenStorage->getToken()->getUser();
  407.             if (!$user instanceof UserInterface) {
  408.                 return $types;
  409.             }
  410.             /** @var TypedFormMetadata $metadata */
  411.             $metadata $this->formMetadataProvider->getMetadata('page'$user->getLocale(), []);
  412.             foreach ($metadata->getForms() as $form) {
  413.                 $types[] = ['type' => $form->getName(), 'title' => $form->getTitle()];
  414.             }
  415.         }
  416.         return $types;
  417.     }
  418.     public function getAlias()
  419.     {
  420.         return 'content';
  421.     }
  422. }