vendor/pimcore/portal-engine/src/EventSubscriber/StatisticsSubscriber.php line 379

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under following license:
  6.  * - Pimcore Commercial License (PCL)
  7.  *
  8.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  9.  *  @license    http://www.pimcore.org/license     PCL
  10.  */
  11. namespace Pimcore\Bundle\PortalEngineBundle\EventSubscriber;
  12. use Carbon\Carbon;
  13. use ONGR\ElasticsearchDSL\Query\Compound\BoolQuery;
  14. use ONGR\ElasticsearchDSL\Query\TermLevel\RangeQuery;
  15. use ONGR\ElasticsearchDSL\Query\TermLevel\TermQuery;
  16. use ONGR\ElasticsearchDSL\Search;
  17. use Pimcore\Bundle\PortalEngineBundle\Enum\ElasticSearchFields;
  18. use Pimcore\Bundle\PortalEngineBundle\Enum\Statistics;
  19. use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\AssetConfig;
  20. use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\DataObjectConfig;
  21. use Pimcore\Bundle\PortalEngineBundle\Service\DataPool\DataPoolConfigService;
  22. use Pimcore\Bundle\PortalEngineBundle\Service\Element\NameExtractorService;
  23. use Pimcore\Bundle\PortalEngineBundle\Service\Element\UrlExtractorService;
  24. use Pimcore\Bundle\PortalEngineBundle\Service\PortalConfig\PortalConfigService;
  25. use Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\Asset;
  26. use Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\DataObject;
  27. use Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\ElasticSearchConfigService;
  28. use Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\Search\PreConditionService;
  29. use Pimcore\Bundle\PortalEngineBundle\Service\Security\PermissionService;
  30. use Pimcore\Bundle\PortalEngineBundle\Service\Security\SecurityService;
  31. use Pimcore\Bundle\StatisticsExplorerBundle\Events\DataFilterModificationEvent;
  32. use Pimcore\Bundle\StatisticsExplorerBundle\Events\DataResultEvent;
  33. use Pimcore\Bundle\StatisticsExplorerBundle\Events\StatisticsServiceInitEvent;
  34. use Pimcore\Bundle\StatisticsExplorerBundle\Events\TableRenderEvent;
  35. use Pimcore\Bundle\StatisticsExplorerBundle\Model\StatisticsResult;
  36. use Pimcore\Bundle\StatisticsExplorerBundle\StatisticsStorageAdapter\ElasticsearchAdapter;
  37. use Pimcore\Bundle\StatisticsExplorerBundle\StatisticsStorageAdapter\StatisticsStorageAdapterInterface;
  38. use Pimcore\Bundle\StatisticsExplorerBundle\StatisticsStorageAdapter\Worker\ElasticsearchListWorker;
  39. use Pimcore\Bundle\StatisticsExplorerBundle\StatisticsStorageAdapter\Worker\ElasticsearchStatisticWorker;
  40. use Pimcore\Bundle\StatisticsExplorerBundle\Tools\ElasticsearchClientFactory;
  41. use Pimcore\Model\DataObject\ClassDefinition;
  42. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  43. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  44. class StatisticsSubscriber implements EventSubscriberInterface
  45. {
  46.     /**
  47.      * @var DataObject\Search\WorkspaceService
  48.      */
  49.     protected $dataObjectWorkspaceService;
  50.     /**
  51.      * @var Asset\Search\WorkspaceService
  52.      */
  53.     protected $assetWorkspaceService;
  54.     /**
  55.      * @var DataPoolConfigService $dataPoolConfigService
  56.      */
  57.     protected $dataPoolConfigService;
  58.     /**
  59.      * @var PermissionService
  60.      */
  61.     protected $permissionService;
  62.     /**
  63.      * @var SecurityService
  64.      */
  65.     protected $securityService;
  66.     /**
  67.      * @var PreConditionService
  68.      */
  69.     protected $preConditionService;
  70.     /**
  71.      * @var PortalConfigService
  72.      */
  73.     protected $portalConfigService;
  74.     /**
  75.      * @var ElasticsearchClientFactory
  76.      */
  77.     protected $elasticsearchClientFactory;
  78.     /**
  79.      * @var ElasticsearchStatisticWorker
  80.      */
  81.     protected $statisticWorker;
  82.     /**
  83.      * @var ElasticsearchListWorker
  84.      */
  85.     protected $listWorker;
  86.     /**
  87.      * @var EventDispatcherInterface
  88.      */
  89.     protected $eventDispatcher;
  90.     /**
  91.      * @var ElasticSearchConfigService
  92.      */
  93.     protected $elasticSearchConfigService;
  94.     /**
  95.      * @var NameExtractorService
  96.      */
  97.     protected $nameExtractorService;
  98.     /**
  99.      * @var UrlExtractorService
  100.      */
  101.     protected $urlExtractorService;
  102.     /**
  103.      * StatisticsSubscriber constructor.
  104.      *
  105.      * @param DataObject\Search\WorkspaceService $dataObjectWorkspaceService
  106.      * @param Asset\Search\WorkspaceService $assetWorkspaceService
  107.      * @param DataPoolConfigService $dataPoolConfigService
  108.      * @param PermissionService $permissionService
  109.      * @param SecurityService $securityService
  110.      * @param PreConditionService $preConditionService
  111.      * @param PortalConfigService $portalConfigService
  112.      * @param ElasticsearchClientFactory $elasticsearchClientFactory
  113.      * @param ElasticsearchStatisticWorker $statisticWorker
  114.      * @param ElasticsearchListWorker $listWorker
  115.      * @param EventDispatcherInterface $eventDispatcher
  116.      * @param ElasticSearchConfigService $elasticSearchConfigService
  117.      * @param NameExtractorService $nameExtractorService
  118.      * @param UrlExtractorService $urlExtractorService
  119.      */
  120.     public function __construct(
  121.         DataObject\Search\WorkspaceService $dataObjectWorkspaceService,
  122.         Asset\Search\WorkspaceService $assetWorkspaceService,
  123.         DataPoolConfigService $dataPoolConfigService,
  124.         PermissionService $permissionService,
  125.         SecurityService $securityService,
  126.         PreConditionService $preConditionService,
  127.         PortalConfigService $portalConfigService,
  128.         ElasticsearchClientFactory $elasticsearchClientFactory,
  129.         ElasticsearchStatisticWorker $statisticWorker,
  130.         ElasticsearchListWorker $listWorker,
  131.         EventDispatcherInterface $eventDispatcher,
  132.         ElasticSearchConfigService $elasticSearchConfigService,
  133.         NameExtractorService $nameExtractorService,
  134.         UrlExtractorService $urlExtractorService
  135.     ) {
  136.         $this->dataObjectWorkspaceService $dataObjectWorkspaceService;
  137.         $this->assetWorkspaceService $assetWorkspaceService;
  138.         $this->dataPoolConfigService $dataPoolConfigService;
  139.         $this->permissionService $permissionService;
  140.         $this->securityService $securityService;
  141.         $this->preConditionService $preConditionService;
  142.         $this->portalConfigService $portalConfigService;
  143.         $this->elasticsearchClientFactory $elasticsearchClientFactory;
  144.         $this->statisticWorker $statisticWorker;
  145.         $this->listWorker $listWorker;
  146.         $this->eventDispatcher $eventDispatcher;
  147.         $this->elasticSearchConfigService $elasticSearchConfigService;
  148.         $this->nameExtractorService $nameExtractorService;
  149.         $this->urlExtractorService $urlExtractorService;
  150.     }
  151.     /**
  152.      * @return array
  153.      */
  154.     public static function getSubscribedEvents()
  155.     {
  156.         return [
  157.             DataFilterModificationEvent::class => 'modifyStatisticsFilter',
  158.             DataResultEvent::class => 'modifyDataResult',
  159.             StatisticsServiceInitEvent::class => 'addDataObjectClassSources',
  160.             TableRenderEvent::class => 'renderAssetListing'
  161.         ];
  162.     }
  163.     /**
  164.      * @param DataFilterModificationEvent $event
  165.      *
  166.      * @throws \Exception
  167.      */
  168.     public function modifyStatisticsFilter(DataFilterModificationEvent $event)
  169.     {
  170.         if ($event->getConfiguration() && in_array($event->getConfiguration()->getName(), Statistics::ASSET_STATISTICS)) {
  171.             $this->applyAssetDataPoolFilter($event);
  172.         } elseif ($event->getConfiguration() && in_array($event->getConfiguration()->getName(), Statistics::DATA_OBJECT_STATISTICS)) {
  173.             $this->applyDataObjectDataPoolFilter($event);
  174.         } elseif ($event->getConfiguration() && $event->getConfiguration()->getName() === Statistics::ALL_LOGINS_LAST_SIX_MONTHS) {
  175.             $this->applyLoginsFilter($event);
  176.         }
  177.         if ($event->getConfiguration() && in_array($event->getConfiguration()->getName(), Statistics::ADD_6_MONTHS_CONDITION)) {
  178.             $this->addLast6MonthsFilter($event);
  179.         }
  180.         if ($event->getConfiguration() && in_array($event->getConfiguration()->getName(), Statistics::ADD_USER_CONDITION)) {
  181.             $this->addUserFilter($event);
  182.         }
  183.     }
  184.     public function modifyDataResult(DataResultEvent $event)
  185.     {
  186.         if ($event->getConfiguration() && $event->getConfiguration()->getName() === Statistics::ASSET_STORAGE_BY_TYPES) {
  187.             $statisticResult $event->getStatisticsResult();
  188.             $data $statisticResult->getData();
  189.             foreach ($data as $key => $row) {
  190.                 $data[$key]['value'] = ceil($row['value'] / 1000 1000);
  191.                 $data[$key]['label'] = str_replace(' system_fields.fileSize sum'''$row['label']);
  192.             }
  193.             $statisticResult = new StatisticsResult(
  194.                 $data,
  195.                 $statisticResult->getColumnHeaders(),
  196.                 $statisticResult->getRowHeaders()
  197.             );
  198.             $event->setStatisticsResult($statisticResult);
  199.         }
  200.         if ($event->getConfiguration() && in_array($event->getConfiguration()->getName(), Statistics::FORMAT_TIMESTAMP_STATISTICS)) {
  201.             $statisticResult $event->getStatisticsResult();
  202.             $data $statisticResult->getData();
  203.             foreach ($data as $key => $row) {
  204.                 $data[$key]['timestamp'] = date('Y-m-d H:i:s'strtotime($row['timestamp']));
  205.             }
  206.             $statisticResult = new StatisticsResult(
  207.                 $data,
  208.                 $statisticResult->getColumnHeaders(),
  209.                 $statisticResult->getRowHeaders()
  210.             );
  211.             $event->setStatisticsResult($statisticResult);
  212.         }
  213.     }
  214.     /**
  215.      * @param DataFilterModificationEvent $event
  216.      *
  217.      * @throws \Exception
  218.      */
  219.     protected function applyAssetDataPoolFilter(DataFilterModificationEvent $event)
  220.     {
  221.         $boolQuery = new BoolQuery();
  222.         foreach ($this->dataPoolConfigService->getDataPoolConfigsFromSite() as $dataPoolConfig) {
  223.             if ($dataPoolConfig instanceof AssetConfig) {
  224.                 $this->dataPoolConfigService->setCurrentDataPoolConfig($dataPoolConfig);
  225.                 $mustQuery = new BoolQuery();
  226.                 $mustQuery->add($this->assetWorkspaceService->getElasticSearchWorkspaceQuery(), BoolQuery::MUST);
  227.                 $search = new Search();
  228.                 $this->preConditionService->applyElasticSearchPreConditions($search);
  229.                 /* @phpstan-ignore-next-line */
  230.                 if ($search->getQueries()) {
  231.                     $mustQuery->add($search->getQueries(), BoolQuery::MUST);
  232.                 }
  233.                 $boolQuery->add($mustQueryBoolQuery::SHOULD);
  234.             }
  235.         }
  236.         $excludeFolderQuery = new TermQuery(ElasticSearchFields::SYSTEM_FIELDS '.' ElasticSearchFields::SYSTEM_FIELDS_TYPE'folder');
  237.         $boolQuery->add($excludeFolderQueryBoolQuery::MUST_NOT);
  238.         $event->setFilter($boolQuery->toArray());
  239.     }
  240.     /**
  241.      * @param DataFilterModificationEvent $event
  242.      *
  243.      * @throws \Exception
  244.      */
  245.     protected function applyDataObjectDataPoolFilter(DataFilterModificationEvent $event)
  246.     {
  247.         $boolQuery = new BoolQuery();
  248.         foreach ($this->dataPoolConfigService->getDataPoolConfigsFromSite() as $dataPoolConfig) {
  249.             if ($dataPoolConfig instanceof DataObjectConfig) {
  250.                 $this->dataPoolConfigService->setCurrentDataPoolConfig($dataPoolConfig);
  251.                 $mustQuery = new BoolQuery();
  252.                 $mustQuery->add($this->dataObjectWorkspaceService->getElasticSearchWorkspaceQuery(), BoolQuery::MUST);
  253.                 $classDefinition ClassDefinition::getById($dataPoolConfig->getDataObjectClass());
  254.                 $className $classDefinition $classDefinition->getName() : '-1';
  255.                 $classNameQuery = new TermQuery(ElasticSearchFields::SYSTEM_FIELDS '.' ElasticSearchFields::SYSTEM_FIELDS_CLASS_NAME '.keyword'$className);
  256.                 $mustQuery->add($classNameQueryBoolQuery::MUST);
  257.                 $search = new Search();
  258.                 $this->preConditionService->applyElasticSearchPreConditions($search);
  259.                 /* @phpstan-ignore-next-line */
  260.                 if ($search->getQueries()) {
  261.                     $mustQuery->add($search->getQueries(), BoolQuery::MUST);
  262.                 }
  263.                 $boolQuery->add($mustQueryBoolQuery::SHOULD);
  264.             }
  265.         }
  266.         $event->setFilter($boolQuery->toArray());
  267.     }
  268.     /**
  269.      * @param DataFilterModificationEvent $event
  270.      *
  271.      * @throws \Exception
  272.      */
  273.     protected function applyLoginsFilter(DataFilterModificationEvent $event)
  274.     {
  275.         $filter = new TermQuery('portalId'$this->portalConfigService->getCurrentPortalConfig()->getPortalId()); // @phpstan-ignore-line
  276.         $event->setFilter($filter->toArray());
  277.     }
  278.     protected function addLast6MonthsFilter(DataFilterModificationEvent $event)
  279.     {
  280.         $timestampQuery = new RangeQuery(
  281.             'timestamp', [
  282.                 RangeQuery::GTE => Carbon::createFromTimestamp(strtotime(date('Y-m-1')))->subMonths(6)->format(
  283.                     'Y-m-d\TH:i:sO'
  284.                 ),
  285.             ]
  286.         );
  287.         $event->setFilter([
  288.            'bool' => [
  289.                'must' => [
  290.                    $timestampQuery->toArray(),
  291.                    $event->getFilter()
  292.                ]
  293.            ]
  294.         ]);
  295.     }
  296.     protected function addUserFilter(DataFilterModificationEvent $event)
  297.     {
  298.         $timestampQuery = new TermQuery(
  299.             'userId'$this->securityService->getPortalUser()->getId()
  300.         );
  301.         $event->setFilter([
  302.            'bool' => [
  303.                'must' => [
  304.                    $timestampQuery->toArray(),
  305.                    $event->getFilter()
  306.                ]
  307.            ]
  308.         ]);
  309.     }
  310.     public function addDataObjectClassSources(StatisticsServiceInitEvent $event)
  311.     {
  312.         if (!$this->portalConfigService->isPortalEngineSite()) {
  313.             return;
  314.         }
  315.         $statisticsService $event->getStatisticsService();
  316.         $dataPools $this->dataPoolConfigService->getDataPoolConfigsFromSite();
  317.         $classIds = [];
  318.         foreach ($dataPools as $dataPool) {
  319.             if ($dataPool instanceof DataObjectConfig) {
  320.                 $classIds[] = $dataPool->getDataObjectClass();
  321.             }
  322.         }
  323.         $classes = [];
  324.         foreach (array_unique($classIds) as $classId) {
  325.             $classes[] = ClassDefinition::getById($classId);
  326.         }
  327.         foreach ($classes as $class) {
  328.             $indexName $this->elasticSearchConfigService->getIndexName($class->getName());
  329.             $label 'DataObject `' $class->getName() . '`';
  330.             $adapter = new ElasticsearchAdapter($this->elasticsearchClientFactory$indexName$this->statisticWorker$this->listWorker$this->eventDispatcher$label);
  331.             $statisticsService->addDataSourceAdapter('pimcoreportalengine_' $class->getId(), 'portal'$adapter);
  332.         }
  333.     }
  334.     public function renderAssetListing(TableRenderEvent $event)
  335.     {
  336.         if ($event->getConfiguration() && in_array($event->getConfiguration()->getName(), Statistics::RENDER_AS_ASSET_TABLE)) {
  337.             $params $event->getParameters();
  338.             $newParams = [];
  339.             if ($event->getStatisticsMode() === StatisticsStorageAdapterInterface::STATISTICS_MODE_LIST) {
  340.                 if (($params['columnHeaders']['0']['value'] ?? null) === 'system_fields.id') {
  341.                     $newParams = [
  342.                         'labels' => ['asset' => 'Asset''timestamp' => 'Timestamp'],
  343.                         'data' => []
  344.                     ];
  345.                     foreach ($params['data'] as $dataEntry) {
  346.                         $asset \Pimcore\Model\Asset::getById($dataEntry['system_fields.id']);
  347.                         if (empty($asset)) {
  348.                             $dataEntry['asset_name'] = $dataEntry['path'];
  349.                             $dataEntry['asset_url'] = false;
  350.                         } else {
  351.                             $dataEntry['asset_name'] = $this->nameExtractorService->extractName($asset);
  352.                             $dataEntry['asset_url'] = $this->urlExtractorService->extractUrl($asset);
  353.                         }
  354.                         $newParams['data'][] = $dataEntry;
  355.                     }
  356.                 }
  357.             } else {
  358.                 $newParams = [
  359.                     'labels' => ['asset' => 'Asset''result' => 'Result'],
  360.                     'data' => []
  361.                 ];
  362.                 foreach ($params['rowHeaders'] as $row) {
  363.                     $dataEntry = [];
  364.                     $rowHeaders $row['rowHeaders'];
  365.                     $asset \Pimcore\Model\Asset::getById($rowHeaders[0]['value']);
  366.                     if (empty($asset)) {
  367.                         $dataEntry['asset_name'] = $rowHeaders[1]['value'];
  368.                         $dataEntry['asset_url'] = false;
  369.                     } else {
  370.                         $dataEntry['asset_name'] = $this->nameExtractorService->extractName($asset);
  371.                         $dataEntry['asset_url'] = $this->urlExtractorService->extractUrl($asset);
  372.                     }
  373.                     $dataEntry['result'] = $params['data'][$row['dataKey']]['value'] ?? '-';
  374.                     $newParams['data'][] = $dataEntry;
  375.                 }
  376.             }
  377.             $event->setParameters($newParams);
  378.             $event->setTemplate('@PimcorePortalEngine/statistic_explorer/asset-data-list.html.twig');
  379.         }
  380.     }
  381. }