vendor/pimcore/advanced-object-search/src/Controller/AdminController.php line 563

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace AdvancedObjectSearchBundle\Controller;
  15. use AdvancedObjectSearchBundle\Event\AdvancedObjectSearchEvents;
  16. use AdvancedObjectSearchBundle\Event\FilterListingEvent;
  17. use AdvancedObjectSearchBundle\Model\SavedSearch;
  18. use AdvancedObjectSearchBundle\Service;
  19. use Pimcore\Bundle\AdminBundle\Helper\QueryParams;
  20. use Pimcore\Controller\Traits\JsonHelperTrait;
  21. use Pimcore\Controller\UserAwareController;
  22. use Pimcore\Db;
  23. use Pimcore\Model\DataObject;
  24. use Pimcore\Model\DataObject\Listing;
  25. use Pimcore\Model\DataObject\Service as DataObjectService;
  26. use Pimcore\Tool;
  27. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  28. use Symfony\Component\HttpFoundation\JsonResponse;
  29. use Symfony\Component\HttpFoundation\Request;
  30. use Symfony\Component\HttpFoundation\Response;
  31. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
  32. use Symfony\Component\Routing\Annotation\Route;
  33. /**
  34.  * Class AdminController
  35.  *
  36.  * @Route("/admin")
  37.  */
  38. class AdminController extends UserAwareController
  39. {
  40.     use JsonHelperTrait;
  41.     /**
  42.      * @Route("/get-fields")
  43.      */
  44.     public function getFieldsAction(Request $requestService $service): JsonResponse
  45.     {
  46.         $type strip_tags($request->get('type'));
  47.         $allowInheritance false;
  48.         switch ($type) {
  49.             case 'class':
  50.                 $classId strip_tags($request->get('class_id'));
  51.                 $definition DataObject\ClassDefinition::getById($classId);
  52.                 $allowInheritance $definition->getAllowInherit();
  53.                 break;
  54.             case 'fieldcollection':
  55.                 $key strip_tags($request->get('key'));
  56.                 $definition DataObject\Fieldcollection\Definition::getByKey($key);
  57.                 break;
  58.             case 'objectbrick':
  59.                 $key strip_tags($request->get('key'));
  60.                 $definition DataObject\Objectbrick\Definition::getByKey($key);
  61.                 $classId strip_tags($request->get('class_id'));
  62.                 $classDefinition DataObject\ClassDefinition::getById($classId);
  63.                 $allowInheritance $classDefinition->getAllowInherit();
  64.                 break;
  65.             default:
  66.                 throw new \Exception("Invalid type '$type''");
  67.         }
  68.         $fieldSelectionInformationEntries $service->getFieldSelectionInformationForClassDefinition($definition$allowInheritance);
  69.         $fields = [];
  70.         foreach ($fieldSelectionInformationEntries as $entry) {
  71.             $fields[] = $entry->toArray();
  72.         }
  73.         return $this->jsonResponse(['data' => $fields]);
  74.     }
  75.     /**
  76.      * @Route("/grid-proxy")
  77.      */
  78.     public function gridProxyAction(Request $requestService $serviceEventDispatcherInterface $eventDispatcher): JsonResponse Response
  79.     {
  80.         $requestedLanguage $request->get('language');
  81.         if ($requestedLanguage) {
  82.             if ($requestedLanguage != 'default') {
  83.                 $request->setLocale($requestedLanguage);
  84.             }
  85.         } else {
  86.             $requestedLanguage $request->getLocale();
  87.         }
  88.         if ($request->get('data')) {
  89.             return $this->forward('Pimcore\Bundle\AdminBundle\Controller\Admin\DataObject\DataObjectController:gridProxyAction', [], $request->query->all());
  90.         } else {
  91.             // get list of objects
  92.             $class DataObject\ClassDefinition::getById($request->get('classId'));
  93.             $className $class->getName();
  94.             $fields = [];
  95.             if ($request->get('fields')) {
  96.                 $fields $request->get('fields');
  97.             }
  98.             $start 0;
  99.             $limit 20;
  100.             if ($request->get('limit')) {
  101.                 $limit $request->get('limit');
  102.             }
  103.             if ($request->get('start')) {
  104.                 $start $request->get('start');
  105.             }
  106.             $listClass '\\Pimcore\\Model\\DataObject\\' ucfirst($className) . '\\Listing';
  107.             //get ID list from ES Service
  108.             $data json_decode($request->get('filter'), true);
  109.             $results $service->doFilter($data['classId'], $data['conditions']['filters'], $data['conditions']['fulltextSearchTerm'], $start$limit);
  110.             $total $service->extractTotalCountFromResult($results);
  111.             $ids $service->extractIdsFromResult($results);
  112.             /**
  113.              * @var Listing $list
  114.              */
  115.             $list = new $listClass();
  116.             $list->setObjectTypes(['object''folder''variant']);
  117.             $conditionFilters = [];
  118.             // this is necessary to properly reference the columns from main query in the workspaces related sub-query
  119.             $listingTableName $list->getDao()->getTableName();
  120.             $idField $listingTableName '.' DataObjectService::getVersionDependentDatabaseColumnName('id');
  121.             $keyColumn $listingTableName '.' DataObjectService::getVersionDependentDatabaseColumnName('key');
  122.             $pathColumn $listingTableName '.' DataObjectService::getVersionDependentDatabaseColumnName('path');
  123.             if (!$this->getPimcoreUser()->isAdmin()) {
  124.                 $userIds $this->getPimcoreUser()->getRoles();
  125.                 $userIds[] = $this->getPimcoreUser()->getId();
  126.                 $conditionFilters[] = ' (
  127.                                                     (select list from users_workspaces_object where userId in (' implode(','$userIds) . ') and LOCATE(CONCAT('$pathColumn .', ' $keyColumn '),cpath)=1  ORDER BY LENGTH(cpath) DESC LIMIT 1)=1
  128.                                                     OR
  129.                                                     (select list from users_workspaces_object where userId in (' implode(','$userIds) . ') and LOCATE(cpath,CONCAT('$pathColumn .', ' $keyColumn '))=1  ORDER BY LENGTH(cpath) DESC LIMIT 1)=1
  130.                                                  )';
  131.             }
  132.             if (!empty($ids)) {
  133.                 $conditionFilters[] = $idField ' IN (' implode(','$ids) . ')';
  134.                 $list->setOrderKey(' FIELD(' $idField ', ' implode(','$ids) . ')'false);
  135.             } else {
  136.                 $conditionFilters[] = '1=2';
  137.             }
  138.             $list->setCondition(implode(' AND '$conditionFilters));
  139.             /* @phpstan-ignore-next-line */
  140.             $eventDispatcher->dispatch(new FilterListingEvent($list), AdvancedObjectSearchEvents::LISTING_FILER);
  141.             $list->load();
  142.             $objects = [];
  143.             foreach ($list->getObjects() as $object) {
  144.                 $o DataObject\Service::gridObjectData($object$fields$requestedLanguage);
  145.                 $objects[] = $o;
  146.             }
  147.             return $this->jsonResponse(['data' => $objects'success' => true'total' => $total]);
  148.         }
  149.     }
  150.     /**
  151.      * @Route("/get-batch-jobs")
  152.      */
  153.     public function getBatchJobsAction(Request $requestService $service): JsonResponse
  154.     {
  155.         if ($request->get('language')) {
  156.             $request->setLocale($request->get('language'));
  157.         }
  158.         $class DataObject\ClassDefinition::getById($request->get('classId'));
  159.         //get ID list from ES Service
  160.         $data json_decode($request->get('filter'), true);
  161.         $results $service->doFilter($data['classId'], $data['conditions']['filters'] ?? [], $data['conditions']['fulltextSearchTerm'] ?? [], null9999);
  162.         $ids $service->extractIdsFromResult($results);
  163.         $className $class->getName();
  164.         $listClass '\\Pimcore\\Model\\DataObject\\' ucfirst($className) . '\\Listing';
  165.         $list = new $listClass();
  166.         $list->setObjectTypes(['object''folder''variant']);
  167.         $idField DataObjectService::getVersionDependentDatabaseColumnName('id');
  168.         $list->setCondition($idField ' IN (' implode(','$ids) . ')');
  169.         $list->setOrderKey(' FIELD('$idField .', ' implode(','$ids) . ')'false);
  170.         if ($request->get('objecttype')) {
  171.             $list->setObjectTypes([$request->get('objecttype')]);
  172.         }
  173.         $jobs $list->loadIdList();
  174.         return $this->jsonResponse(['success' => true'jobs' => $jobs]);
  175.     }
  176.     protected function getCsvFile(string $fileHandle): string
  177.     {
  178.         return $fileHandle '.csv';
  179.     }
  180.     /**
  181.      * @Route("/get-export-jobs")
  182.      */
  183.     public function getExportJobsAction(Request $requestService $service): JsonResponse
  184.     {
  185.         if ($request->get('language')) {
  186.             $request->setLocale($request->get('language'));
  187.         }
  188.         $data json_decode($request->get('filter'), true);
  189.         if (empty($ids $request->get('ids'false))) {
  190.             $results $service->doFilter(
  191.                 $data['classId'],
  192.                 $data['conditions']['filters'],
  193.                 $data['conditions']['fulltextSearchTerm'],
  194.                 0,
  195.                 9999 // elastic search cannot export more results than 9999 in one request
  196.             );
  197.             //get ID list from ES Service
  198.             $ids $service->extractIdsFromResult($results);
  199.         }
  200.         $jobs array_chunk($ids20);
  201.         $fileHandle uniqid('export-');
  202.         $storage Tool\Storage::get('temp');
  203.         $storage->write($this->getCsvFile($fileHandle), '');
  204.         return $this->jsonResponse(['success' => true'jobs' => $jobs'fileHandle' => $fileHandle]);
  205.     }
  206.     /**
  207.      * @Route("/save")
  208.      */
  209.     public function saveAction(Request $request): JsonResponse
  210.     {
  211.         $data $request->get('data');
  212.         $data json_decode($data);
  213.         $id = (intval($request->get('id')));
  214.         if ($id) {
  215.             $savedSearch SavedSearch::getById($id);
  216.         } else {
  217.             $savedSearch = new SavedSearch();
  218.             $savedSearch->setOwner($this->getPimcoreUser());
  219.         }
  220.         $savedSearch->setName($data->settings->name);
  221.         $savedSearch->setDescription($data->settings->description);
  222.         $savedSearch->setCategory($data->settings->category);
  223.         $savedSearch->setSharedUserIds(array_merge($data->settings->shared_users$data->settings->shared_roles));
  224.         $savedSearch->setShareGlobally($this->getPimcoreUser()->isAdmin() && $data->settings->share_globally);
  225.         $config = ['classId' => $data->classId'gridConfig' => $data->gridConfig'conditions' => $data->conditions];
  226.         $savedSearch->setConfig(json_encode($config));
  227.         $savedSearch->save();
  228.         return $this->jsonResponse(['success' => true'id' => $savedSearch->getId()]);
  229.     }
  230.     /**
  231.      * @Route("/delete")
  232.      */
  233.     public function deleteAction(Request $request): JsonResponse
  234.     {
  235.         $id intval($request->get('id'));
  236.         $savedSearch SavedSearch::getById($id);
  237.         if ($savedSearch) {
  238.             $savedSearch->delete();
  239.             return $this->jsonResponse(['success' => true'id' => $savedSearch->getId()]);
  240.         }
  241.         return $this->jsonResponse(['success' => false'message' => "Saved Search with $id not found."]);
  242.     }
  243.     /**
  244.      * @Route("/find")
  245.      */
  246.     public function findAction(Request $request): JsonResponse
  247.     {
  248.         $user $this->getPimcoreUser();
  249.         $query $request->get('query');
  250.         if ($query == '*') {
  251.             $query '';
  252.         }
  253.         $query str_replace('%''*'$query);
  254.         $offset intval($request->get('start'));
  255.         $limit intval($request->get('limit'));
  256.         $offset $offset $offset 0;
  257.         $limit $limit $limit 50;
  258.         $db Db::get();
  259.         $searcherList = new SavedSearch\Listing();
  260.         $conditionParts = [];
  261.         $conditionParams = [];
  262.         //filter for current user
  263.         $userIds = [$user->getId()];
  264.         $userIds array_merge($userIds$user->getRoles());
  265.         $userIds implode('|'$userIds);
  266.         $conditionParts[] = '(shareGlobally = 1 OR ownerId = ? OR sharedUserIds REGEXP ?)';
  267.         $conditionParams[] = $user->getId();
  268.         $conditionParams[] = ',(' $userIds '),';
  269.         //filter for query
  270.         if (!empty($query)) {
  271.             $conditionParts[] = sprintf('(%s LIKE ? OR %s LIKE ? OR %s LIKE ?)',
  272.                 $db->quoteIdentifier('name'),
  273.                 $db->quoteIdentifier('description'),
  274.                 $db->quoteIdentifier('category')
  275.             );
  276.             $conditionParams[] = '%' $query '%';
  277.             $conditionParams[] = '%' $query '%';
  278.             $conditionParams[] = '%' $query '%';
  279.         }
  280.         $condition implode(' AND '$conditionParts);
  281.         $searcherList->setCondition($condition$conditionParams);
  282.         $searcherList->setOffset($offset);
  283.         $searcherList->setLimit($limit);
  284.         $sortingSettings = [];
  285.         if (class_exists(QueryParams::class)) {
  286.             $sortingSettings QueryParams::extractSortingSettings(array_merge($request->request->all(), $request->query->all()));
  287.         }
  288.         if ($sortingSettings['orderKey'] ?? false) {
  289.             $searcherList->setOrderKey($sortingSettings['orderKey']);
  290.         }
  291.         if ($sortingSettings['order'] ?? false) {
  292.             $searcherList->setOrder($sortingSettings['order']);
  293.         }
  294.         $results = []; //$searcherList->load();
  295.         foreach ($searcherList->load() as $result) {
  296.             $results[] = [
  297.                 'id' => $result->getId(),
  298.                 'name' => $result->getName(),
  299.                 'description' => $result->getDescription(),
  300.                 'category' => $result->getCategory(),
  301.                 'owner' => $result->getOwner() ? $result->getOwner()->getUsername() . ' (' $result->getOwner()->getFirstname() . ' ' $result->getOwner()->getLastName() . ')' '',
  302.                 'ownerId' => $result->getOwnerId()
  303.             ];
  304.         }
  305.         // only get the real total-count when the limit parameter is given otherwise use the default limit
  306.         if ($request->get('limit')) {
  307.             $totalMatches $searcherList->getTotalCount();
  308.         } else {
  309.             $totalMatches count($results);
  310.         }
  311.         return $this->jsonResponse(['data' => $results'success' => true'total' => $totalMatches]);
  312.     }
  313.     /**
  314.      * @Route("/load-search")
  315.      */
  316.     public function loadSearchAction(Request $request): JsonResponse
  317.     {
  318.         $id intval($request->get('id'));
  319.         $savedSearch SavedSearch::getById($id);
  320.         if ($savedSearch) {
  321.             $config json_decode($savedSearch->getConfig(), true);
  322.             $classDefinition DataObject\ClassDefinition::getById($config['classId']);
  323.             if (!empty($config['gridConfig']['columns'])) {
  324.                 $helperColumns = [];
  325.                 foreach ($config['gridConfig']['columns'] as &$column) {
  326.                     if (!($column['isOperator'] ?? false)) {
  327.                         $fieldDefinition $classDefinition->getFieldDefinition($column['key']);
  328.                         if ($fieldDefinition) {
  329.                             $width $column['layout']['width'] ?? null;
  330.                             $column['layout'] = json_decode(json_encode($fieldDefinition), true);
  331.                             if ($width) {
  332.                                 $column['layout']['width'] = $width;
  333.                             }
  334.                         }
  335.                     }
  336.                     if (!DataObject\Service::isHelperGridColumnConfig($column['key'])) {
  337.                         continue;
  338.                     }
  339.                     // columnconfig has to be a stdclass
  340.                     $helperColumns[$column['key']] = json_decode(json_encode($column));
  341.                 }
  342.                 // store the saved search columns in the session, otherwise they won't work
  343.                 Tool\Session::useBag($request->getSession(), function (AttributeBagInterface $session) use ($helperColumns) {
  344.                     $existingColumns $session->get('helpercolumns', []);
  345.                     $helperColumns array_merge($existingColumns$helperColumns);
  346.                     $session->set('helpercolumns'$helperColumns);
  347.                 }, 'pimcore_gridconfig');
  348.             }
  349.             return $this->jsonResponse([
  350.                 'id' => $savedSearch->getId(),
  351.                 'classId' => $config['classId'],
  352.                 'settings' => [
  353.                     'name' => $savedSearch->getName(),
  354.                     'description' => $savedSearch->getDescription(),
  355.                     'category' => $savedSearch->getCategory(),
  356.                     'sharedUserIds' => $savedSearch->getSharedUserIds(),
  357.                     'shareGlobally' => $savedSearch->getShareGlobally(),
  358.                     'isOwner' => $savedSearch->getOwnerId() == $this->getPimcoreUser()->getId(),
  359.                     'hasShortCut' => $savedSearch->isInShortCutsForUser($this->getPimcoreUser())
  360.                 ],
  361.                 'conditions' => $config['conditions'],
  362.                 'gridConfig' => $config['gridConfig']
  363.             ]);
  364.         }
  365.         return $this->jsonResponse(['success' => false'message' => "Saved Search with $id not found."]);
  366.     }
  367.     /**
  368.      * @Route("/load-short-cuts")
  369.      */
  370.     public function loadShortCutsAction(Request $request): JsonResponse
  371.     {
  372.         $list = new SavedSearch\Listing();
  373.         $list->setCondition(
  374.             '(shareGlobally = ? OR ownerId = ? OR sharedUserIds LIKE ?) AND shortCutUserIds LIKE ?',
  375.             [
  376.                 true,
  377.                 $this->getPimcoreUser()->getId(),
  378.                 '%,' $this->getPimcoreUser()->getId() . ',%',
  379.                 '%,' $this->getPimcoreUser()->getId() . ',%'
  380.             ]
  381.         );
  382.         $list->load();
  383.         $entries = [];
  384.         foreach ($list->getSavedSearches() as $entry) {
  385.             $entries[] = [
  386.                 'id' => $entry->getId(),
  387.                 'name' => $entry->getName()
  388.             ];
  389.         }
  390.         return $this->jsonResponse(['entries' => $entries]);
  391.     }
  392.     /**
  393.      * @Route("/toggle-short-cut")
  394.      */
  395.     public function toggleShortCutAction(Request $request): JsonResponse
  396.     {
  397.         $id intval($request->get('id'));
  398.         $savedSearch SavedSearch::getById($id);
  399.         if ($savedSearch) {
  400.             $user $this->getPimcoreUser();
  401.             if ($savedSearch->isInShortCutsForUser($user)) {
  402.                 $savedSearch->removeShortCutForUser($user);
  403.             } else {
  404.                 $savedSearch->addShortCutForUser($user);
  405.             }
  406.             $savedSearch->save();
  407.             return $this->jsonResponse(['success' => 'true''hasShortCut' => $savedSearch->isInShortCutsForUser($user)]);
  408.         }
  409.         return $this->jsonResponse(['success' => 'false']);
  410.     }
  411.     /**
  412.      * @Route("/get-users")
  413.      */
  414.     public function getUsersAction(Request $request): JsonResponse
  415.     {
  416.         $users = [];
  417.         // condition for users with groups having DAM permission
  418.         $condition = [];
  419.         $rolesList = new \Pimcore\Model\User\Role\Listing();
  420.         $rolesList->addConditionParam("CONCAT(',', permissions, ',') LIKE ?"'%,bundle_advancedsearch_search,%');
  421.         $rolesList->load();
  422.         $roles $rolesList->getRoles();
  423.         foreach ($roles as $role) {
  424.             $condition[] = "CONCAT(',', roles, ',') LIKE '%," $role->getId() . ",%'";
  425.         }
  426.         // get available user
  427.         $list = new \Pimcore\Model\User\Listing();
  428.         $condition[] = 'admin = 1';
  429.         $list->addConditionParam("((CONCAT(',', permissions, ',') LIKE ? ) OR " implode(' OR '$condition) . ')''%,bundle_advancedsearch_search,%');
  430.         $list->addConditionParam('id != ?'$this->getPimcoreUser()->getId());
  431.         $list->load();
  432.         $userList $list->getUsers();
  433.         foreach ($userList as $user) {
  434.             $users[] = [
  435.                 'id' => $user->getId(),
  436.                 'label' => $user->getName()
  437.             ];
  438.         }
  439.         return $this->jsonResponse(['success' => true'total' => count($users), 'data' => $users]);
  440.     }
  441.     /**
  442.      * @Route("/get-roles")
  443.      */
  444.     public function getRolesAction(): JsonResponse
  445.     {
  446.         $roles = [];
  447.         $rolesList = new \Pimcore\Model\User\Role\Listing();
  448.         $rolesList->setCondition('type = "role"');
  449.         $rolesList->addConditionParam("CONCAT(',', permissions, ',') LIKE ?"'%,bundle_advancedsearch_search,%');
  450.         $rolesList->load();
  451.         foreach ($rolesList->getRoles() as $role) {
  452.             $roles[] = [
  453.                 'id' => $role->getId(),
  454.                 'label' => $role->getName()
  455.             ];
  456.         }
  457.         return $this->jsonResponse(['success' => true'total' => count($roles), 'data' => $roles]);
  458.     }
  459.     /**
  460.      * @Route("/check-index-status")
  461.      */
  462.     public function checkIndexStatusAction(Request $requestService $service): JsonResponse
  463.     {
  464.         return $this->jsonResponse(['indexUptodate' => $service->updateQueueEmpty()]);
  465.     }
  466. }