vendor/pimcore/pimcore/bundles/AdminBundle/Controller/Admin/DataObject/DataObjectController.php line 57

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 Pimcore\Bundle\AdminBundle\Controller\Admin\DataObject;
  15. use Pimcore\Bundle\AdminBundle\Controller\Admin\ElementControllerBase;
  16. use Pimcore\Bundle\AdminBundle\Controller\Traits\AdminStyleTrait;
  17. use Pimcore\Bundle\AdminBundle\Controller\Traits\ApplySchedulerDataTrait;
  18. use Pimcore\Bundle\AdminBundle\Helper\GridHelperService;
  19. use Pimcore\Bundle\AdminBundle\Security\CsrfProtectionHandler;
  20. use Pimcore\Controller\KernelControllerEventInterface;
  21. use Pimcore\Controller\Traits\ElementEditLockHelperTrait;
  22. use Pimcore\Db;
  23. use Pimcore\Event\Admin\ElementAdminStyleEvent;
  24. use Pimcore\Event\AdminEvents;
  25. use Pimcore\Localization\LocaleServiceInterface;
  26. use Pimcore\Logger;
  27. use Pimcore\Model;
  28. use Pimcore\Model\DataObject;
  29. use Pimcore\Model\DataObject\ClassDefinition\Data\ManyToManyObjectRelation;
  30. use Pimcore\Model\DataObject\ClassDefinition\Data\Relations\AbstractRelations;
  31. use Pimcore\Model\DataObject\ClassDefinition\Data\ReverseObjectRelation;
  32. use Pimcore\Model\DataObject\ClassDefinition\PreviewGeneratorInterface;
  33. use Pimcore\Model\Element;
  34. use Pimcore\Model\Element\ElementInterface;
  35. use Pimcore\Model\Schedule\Task;
  36. use Pimcore\Model\Version;
  37. use Pimcore\Tool;
  38. use Symfony\Component\EventDispatcher\GenericEvent;
  39. use Symfony\Component\HttpFoundation\JsonResponse;
  40. use Symfony\Component\HttpFoundation\RedirectResponse;
  41. use Symfony\Component\HttpFoundation\Request;
  42. use Symfony\Component\HttpFoundation\Response;
  43. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
  44. use Symfony\Component\HttpKernel\Event\ControllerEvent;
  45. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  46. use Symfony\Component\Routing\Annotation\Route;
  47. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  48. /**
  49.  * @Route("/object", name="pimcore_admin_dataobject_dataobject_")
  50.  *
  51.  * @internal
  52.  */
  53. class DataObjectController extends ElementControllerBase implements KernelControllerEventInterface
  54. {
  55.     use AdminStyleTrait;
  56.     use ElementEditLockHelperTrait;
  57.     use ApplySchedulerDataTrait;
  58.     use DataObjectActionsTrait;
  59.     /**
  60.      * @var DataObject\Service
  61.      */
  62.     protected DataObject\Service $_objectService;
  63.     /**
  64.      * @var array
  65.      */
  66.     private array $objectData = [];
  67.     /**
  68.      * @var array
  69.      */
  70.     private array $metaData = [];
  71.     private array $classFieldDefinitions = [];
  72.     /**
  73.      * @Route("/tree-get-childs-by-id", name="treegetchildsbyid", methods={"GET"})
  74.      *
  75.      * @param Request $request
  76.      * @param EventDispatcherInterface $eventDispatcher
  77.      *
  78.      * @return JsonResponse
  79.      */
  80.     public function treeGetChildsByIdAction(Request $requestEventDispatcherInterface $eventDispatcher)
  81.     {
  82.         $allParams array_merge($request->request->all(), $request->query->all());
  83.         $filter $request->get('filter');
  84.         $object DataObject::getById((int) $request->get('node'));
  85.         $objectTypes = [DataObject::OBJECT_TYPE_OBJECTDataObject::OBJECT_TYPE_FOLDER];
  86.         $objects = [];
  87.         $cv false;
  88.         $offset $total $limit $filteredTotalCount 0;
  89.         if ($object instanceof DataObject\Concrete) {
  90.             $class $object->getClass();
  91.             if ($class->getShowVariants()) {
  92.                 $objectTypes DataObject::$types;
  93.             }
  94.         }
  95.         if ($object->hasChildren($objectTypes)) {
  96.             $offset = (int)$request->get('start');
  97.             $limit = (int)$request->get('limit'100000000);
  98.             if ($view $request->get('view'false)) {
  99.                 $cv Element\Service::getCustomViewById($request->get('view'));
  100.             }
  101.             if (!is_null($filter)) {
  102.                 if (substr($filter, -1) != '*') {
  103.                     $filter .= '*';
  104.                 }
  105.                 $filter str_replace('*''%'$filter);
  106.                 $limit 100;
  107.             }
  108.             $childrenList = new DataObject\Listing();
  109.             $childrenList->setCondition($this->buildChildrenCondition($object$filter$view));
  110.             $childrenList->setLimit($limit);
  111.             $childrenList->setOffset($offset);
  112.             if ($object->getChildrenSortBy() === 'index') {
  113.                 $childrenList->setOrderKey('objects.o_index ASC'false);
  114.             } else {
  115.                 $childrenList->setOrderKey(
  116.                     sprintf(
  117.                         'CAST(objects.o_%s AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci %s',
  118.                         $object->getChildrenSortBy(), $object->getChildrenSortOrder()
  119.                     ),
  120.                     false
  121.                 );
  122.             }
  123.             $childrenList->setObjectTypes($objectTypes);
  124.             Element\Service::addTreeFilterJoins($cv$childrenList);
  125.             $beforeListLoadEvent = new GenericEvent($this, [
  126.                 'list' => $childrenList,
  127.                 'context' => $allParams,
  128.             ]);
  129.             $eventDispatcher->dispatch($beforeListLoadEventAdminEvents::OBJECT_LIST_BEFORE_LIST_LOAD);
  130.             /** @var DataObject\Listing $childrenList */
  131.             $childrenList $beforeListLoadEvent->getArgument('list');
  132.             $children $childrenList->load();
  133.             $filteredTotalCount $childrenList->getTotalCount();
  134.             foreach ($children as $child) {
  135.                 $objectTreeNode $this->getTreeNodeConfig($child);
  136.                 // this if is obsolete since as long as the change with #11714 about list on line 175-179 are working fine, we already filter the list=1 there
  137.                 if ($objectTreeNode['permissions']['list'] == 1) {
  138.                     $objects[] = $objectTreeNode;
  139.                 }
  140.             }
  141.             //pagination for custom view
  142.             $total $cv
  143.                 $filteredTotalCount
  144.                 $object->getChildAmount(null$this->getAdminUser());
  145.         }
  146.         //Hook for modifying return value - e.g. for changing permissions based on object data
  147.         //data need to wrapped into a container in order to pass parameter to event listeners by reference so that they can change the values
  148.         $event = new GenericEvent($this, [
  149.             'objects' => $objects,
  150.         ]);
  151.         $eventDispatcher->dispatch($eventAdminEvents::OBJECT_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA);
  152.         $objects $event->getArgument('objects');
  153.         if ($limit) {
  154.             return $this->adminJson([
  155.                 'offset' => $offset,
  156.                 'limit' => $limit,
  157.                 'total' => $total,
  158.                 'overflow' => !is_null($filter) && ($filteredTotalCount $limit),
  159.                 'nodes' => $objects,
  160.                 'fromPaging' => (int)$request->get('fromPaging'),
  161.                 'filter' => $request->get('filter') ? $request->get('filter') : '',
  162.                 'inSearch' => (int)$request->get('inSearch'),
  163.             ]);
  164.         }
  165.         return $this->adminJson($objects);
  166.     }
  167.     /**
  168.      * @param DataObject\AbstractObject $object
  169.      * @param string|null $filter
  170.      * @param string|null $view
  171.      *
  172.      * @return string
  173.      */
  174.     private function buildChildrenCondition(DataObject\AbstractObject $object, ?string $filter, ?string $view): string
  175.     {
  176.         $condition "objects.o_parentId = '" $object->getId() . "'";
  177.         // custom views start
  178.         if ($view) {
  179.             $cv Element\Service::getCustomViewById($view);
  180.             if (!empty($cv['classes'])) {
  181.                 $cvConditions = [];
  182.                 $cvClasses $cv['classes'];
  183.                 foreach ($cvClasses as $key => $cvClass) {
  184.                     $cvConditions[] = "objects.o_classId = '" $key "'";
  185.                 }
  186.                 $cvConditions[] = "objects.o_type = 'folder'";
  187.                 $condition .= ' AND (' implode(' OR '$cvConditions) . ')';
  188.             }
  189.         }
  190.         // custom views end
  191.         if (!$this->getAdminUser()->isAdmin()) {
  192.             $userIds $this->getAdminUser()->getRoles();
  193.             $currentUserId $this->getAdminUser()->getId();
  194.             $userIds[] = $currentUserId;
  195.             $inheritedPermission $object->getDao()->isInheritingPermission('list'$userIds);
  196.             $anyAllowedRowOrChildren 'EXISTS(SELECT list FROM users_workspaces_object uwo WHERE userId IN (' implode(','$userIds) . ') AND list=1 AND LOCATE(CONCAT(objects.o_path,objects.o_key),cpath)=1 AND
  197.                 NOT EXISTS(SELECT list FROM users_workspaces_object WHERE userId =' $currentUserId '  AND list=0 AND cpath = uwo.cpath))';
  198.             $isDisallowedCurrentRow 'EXISTS(SELECT list FROM users_workspaces_object WHERE userId IN (' implode(','$userIds) . ')  AND cid = objects.o_id AND list=0)';
  199.             $condition .= ' AND IF(' $anyAllowedRowOrChildren ',1,IF(' $inheritedPermission ', ' $isDisallowedCurrentRow ' = 0, 0)) = 1';
  200.         }
  201.         if (!is_null($filter)) {
  202.             $db Db::get();
  203.             $condition .= ' AND CAST(objects.o_key AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci LIKE ' $db->quote($filter);
  204.         }
  205.         return $condition;
  206.     }
  207.     /**
  208.      * @param ElementInterface $element
  209.      *
  210.      * @return array
  211.      *
  212.      * @throws \Exception
  213.      */
  214.     protected function getTreeNodeConfig($element): array
  215.     {
  216.         /** @var DataObject $child */
  217.         $child $element;
  218.         $tmpObject = [
  219.             'id' => $child->getId(),
  220.             'idx' => (int)$child->getIndex(),
  221.             'key' => $child->getKey(),
  222.             'sortBy' => $child->getChildrenSortBy(),
  223.             'sortOrder' => $child->getChildrenSortOrder(),
  224.             'text' => htmlspecialchars($child->getKey()),
  225.             'type' => $child->getType(),
  226.             'path' => $child->getRealFullPath(),
  227.             'basePath' => $child->getRealPath(),
  228.             'elementType' => 'object',
  229.             'locked' => $child->isLocked(),
  230.             'lockOwner' => $child->getLocked() ? true false,
  231.         ];
  232.         $allowedTypes = [DataObject::OBJECT_TYPE_OBJECTDataObject::OBJECT_TYPE_FOLDER];
  233.         if ($child instanceof DataObject\Concrete && $child->getClass()->getShowVariants()) {
  234.             $allowedTypes[] = DataObject::OBJECT_TYPE_VARIANT;
  235.         }
  236.         $hasChildren $child->getDao()->hasChildren($allowedTypesnull$this->getAdminUser());
  237.         $tmpObject['allowDrop'] = false;
  238.         $tmpObject['isTarget'] = true;
  239.         if ($tmpObject['type'] != DataObject::OBJECT_TYPE_VARIANT) {
  240.             $tmpObject['allowDrop'] = true;
  241.         }
  242.         $tmpObject['allowChildren'] = true;
  243.         $tmpObject['leaf'] = !$hasChildren;
  244.         $tmpObject['cls'] = 'pimcore_class_icon ';
  245.         if ($child instanceof DataObject\Concrete) {
  246.             $tmpObject['published'] = $child->isPublished();
  247.             $tmpObject['className'] = $child->getClass()->getName();
  248.             if (!$child->isPublished()) {
  249.                 $tmpObject['cls'] .= 'pimcore_unpublished ';
  250.             }
  251.             $tmpObject['allowVariants'] = $child->getClass()->getAllowVariants();
  252.         }
  253.         $this->addAdminStyle($childElementAdminStyleEvent::CONTEXT_TREE$tmpObject);
  254.         $tmpObject['expanded'] = !$hasChildren;
  255.         $tmpObject['permissions'] = $child->getUserPermissions($this->getAdminUser());
  256.         if ($child->isLocked()) {
  257.             $tmpObject['cls'] .= 'pimcore_treenode_locked ';
  258.         }
  259.         if ($child->getLocked()) {
  260.             $tmpObject['cls'] .= 'pimcore_treenode_lockOwner ';
  261.         }
  262.         if ($tmpObject['leaf']) {
  263.             $tmpObject['expandable'] = false;
  264.             $tmpObject['leaf'] = false//this is required to allow drag&drop
  265.             $tmpObject['expanded'] = true;
  266.             $tmpObject['loaded'] = true;
  267.         }
  268.         return $tmpObject;
  269.     }
  270.     /**
  271.      * @Route("/get-id-path-paging-info", name="getidpathpaginginfo", methods={"GET"})
  272.      *
  273.      * @param Request $request
  274.      *
  275.      * @return JsonResponse
  276.      */
  277.     public function getIdPathPagingInfoAction(Request $request): JsonResponse
  278.     {
  279.         $path $request->get('path');
  280.         $pathParts explode('/'$path);
  281.         $id = (int) array_pop($pathParts);
  282.         $limit $request->get('limit');
  283.         if (empty($limit)) {
  284.             $limit 30;
  285.         }
  286.         $data = [];
  287.         $targetObject DataObject::getById($id);
  288.         $object $targetObject;
  289.         while ($parent $object->getParent()) {
  290.             $list = new DataObject\Listing();
  291.             $list->setCondition('o_parentId = ?'$parent->getId());
  292.             $list->setUnpublished(true);
  293.             $total $list->getTotalCount();
  294.             $info = [
  295.                 'total' => $total,
  296.             ];
  297.             if ($total $limit) {
  298.                 $idList $list->loadIdList();
  299.                 $position array_search($object->getId(), $idList);
  300.                 $info['position'] = $position 1;
  301.                 $info['page'] = ceil($info['position'] / $limit);
  302.             }
  303.             $data[$parent->getId()] = $info;
  304.             $object $parent;
  305.         }
  306.         return $this->adminJson($data);
  307.     }
  308.     /**
  309.      * @Route("/get", name="get", methods={"GET"})
  310.      *
  311.      * @param Request $request
  312.      * @param EventDispatcherInterface $eventDispatcher
  313.      * @param PreviewGeneratorInterface $defaultPreviewGenerator
  314.      *
  315.      * @return JsonResponse
  316.      *
  317.      * @throws \Exception
  318.      */
  319.     public function getAction(Request $requestEventDispatcherInterface $eventDispatcherPreviewGeneratorInterface $defaultPreviewGenerator): JsonResponse
  320.     {
  321.         $objectId = (int)$request->get('id');
  322.         $objectFromDatabase DataObject\Concrete::getById($objectId);
  323.         if ($objectFromDatabase === null) {
  324.             return $this->adminJson(['success' => false'message' => 'element_not_found'], JsonResponse::HTTP_NOT_FOUND);
  325.         }
  326.         $objectFromDatabase = clone $objectFromDatabase;
  327.         // set the latest available version for editmode
  328.         $draftVersion null;
  329.         $object $this->getLatestVersion($objectFromDatabase$draftVersion);
  330.         // check for lock
  331.         if ($object->isAllowed('save') || $object->isAllowed('publish') || $object->isAllowed('unpublish') || $object->isAllowed('delete')) {
  332.             if (Element\Editlock::isLocked($objectId'object')) {
  333.                 return $this->getEditLockResponse($objectId'object');
  334.             }
  335.             Element\Editlock::lock($request->get('id'), 'object');
  336.         }
  337.         // we need to know if the latest version is published or not (a version), because of lazy loaded fields in $this->getDataForObject()
  338.         $objectFromVersion $object !== $objectFromDatabase;
  339.         if ($object->isAllowed('view')) {
  340.             $objectData = [];
  341.             /** -------------------------------------------------------------
  342.              *   Load some general data from published object (if existing)
  343.              *  ------------------------------------------------------------- */
  344.             $objectData['idPath'] = Element\Service::getIdPath($objectFromDatabase);
  345.             $linkGeneratorReference $objectFromDatabase->getClass()->getLinkGeneratorReference();
  346.             $previewGenerator $objectFromDatabase->getClass()->getPreviewGenerator();
  347.             if (empty($previewGenerator) && !empty($linkGeneratorReference)) {
  348.                 $previewGenerator $defaultPreviewGenerator;
  349.             }
  350.             $objectData['hasPreview'] = false;
  351.             if ($objectFromDatabase->getClass()->getPreviewUrl() || $linkGeneratorReference || $previewGenerator) {
  352.                 $objectData['hasPreview'] = true;
  353.             }
  354.             if ($draftVersion && $objectFromDatabase->getModificationDate() < $draftVersion->getDate()) {
  355.                 $objectData['draft'] = [
  356.                     'id' => $draftVersion->getId(),
  357.                     'modificationDate' => $draftVersion->getDate(),
  358.                     'isAutoSave' => $draftVersion->isAutoSave(),
  359.                 ];
  360.             }
  361.             $objectData['general'] = [];
  362.             $allowedKeys = ['o_published''o_key''o_id''o_creationDate''o_classId''o_className''o_type''o_parentId''o_userOwner'];
  363.             foreach ($objectFromDatabase->getObjectVars() as $key => $value) {
  364.                 if (in_array($key$allowedKeys)) {
  365.                     $objectData['general'][$key] = $value;
  366.                 }
  367.             }
  368.             $objectData['general']['fullpath'] = $objectFromDatabase->getRealFullPath();
  369.             $objectData['general']['o_locked'] = $objectFromDatabase->isLocked();
  370.             $objectData['general']['php'] = [
  371.                 'classes' => array_merge([get_class($objectFromDatabase)], array_values(class_parents($objectFromDatabase))),
  372.                 'interfaces' => array_values(class_implements($objectFromDatabase)),
  373.             ];
  374.             $objectData['general']['allowInheritance'] = $objectFromDatabase->getClass()->getAllowInherit();
  375.             $objectData['general']['allowVariants'] = $objectFromDatabase->getClass()->getAllowVariants();
  376.             $objectData['general']['showVariants'] = $objectFromDatabase->getClass()->getShowVariants();
  377.             $objectData['general']['showAppLoggerTab'] = $objectFromDatabase->getClass()->getShowAppLoggerTab();
  378.             $objectData['general']['showFieldLookup'] = $objectFromDatabase->getClass()->getShowFieldLookup();
  379.             if ($objectFromDatabase instanceof DataObject\Concrete) {
  380.                 $objectData['general']['linkGeneratorReference'] = $linkGeneratorReference;
  381.                 if ($previewGenerator) {
  382.                     $objectData['general']['previewConfig'] = $previewGenerator->getPreviewConfig($objectFromDatabase);
  383.                 }
  384.             }
  385.             $objectData['layout'] = $objectFromDatabase->getClass()->getLayoutDefinitions();
  386.             $objectData['userPermissions'] = $objectFromDatabase->getUserPermissions($this->getAdminUser());
  387.             $objectVersions Element\Service::getSafeVersionInfo($objectFromDatabase->getVersions());
  388.             $objectData['versions'] = array_splice($objectVersions, -11);
  389.             $objectData['scheduledTasks'] = array_map(
  390.                 static function (Task $task) {
  391.                     return $task->getObjectVars();
  392.                 },
  393.                 $objectFromDatabase->getScheduledTasks()
  394.             );
  395.             $objectData['childdata']['id'] = $objectFromDatabase->getId();
  396.             $objectData['childdata']['data']['classes'] = $this->prepareChildClasses($objectFromDatabase->getDao()->getClasses());
  397.             $objectData['childdata']['data']['general'] = $objectData['general'];
  398.             /** -------------------------------------------------------------
  399.              *   Load remaining general data from latest version
  400.              *  ------------------------------------------------------------- */
  401.             $allowedKeys = ['o_modificationDate''o_userModification'];
  402.             foreach ($object->getObjectVars() as $key => $value) {
  403.                 if (in_array($key$allowedKeys)) {
  404.                     $objectData['general'][$key] = $value;
  405.                 }
  406.             }
  407.             $this->getDataForObject($object$objectFromVersion);
  408.             $objectData['data'] = $this->objectData;
  409.             $objectData['metaData'] = $this->metaData;
  410.             $objectData['properties'] = Element\Service::minimizePropertiesForEditmode($object->getProperties());
  411.             // this used for the "this is not a published version" hint
  412.             // and for adding the published icon to version overview
  413.             $objectData['general']['versionDate'] = $objectFromDatabase->getModificationDate();
  414.             $objectData['general']['versionCount'] = $objectFromDatabase->getVersionCount();
  415.             $this->addAdminStyle($objectElementAdminStyleEvent::CONTEXT_EDITOR$objectData['general']);
  416.             $currentLayoutId $request->get('layoutId');
  417.             $validLayouts DataObject\Service::getValidLayouts($object);
  418.             //Fallback if $currentLayoutId is not set or empty string
  419.             //Uses first valid layout instead of admin layout when empty
  420.             $ok false;
  421.             foreach ($validLayouts as $layout) {
  422.                 if ($currentLayoutId == $layout->getId()) {
  423.                     $ok true;
  424.                 }
  425.             }
  426.             if (!$ok) {
  427.                 $currentLayoutId null;
  428.             }
  429.             //main layout has id 0 so we check for is_null()
  430.             if ($currentLayoutId === null && !empty($validLayouts)) {
  431.                 if (count($validLayouts) === 1) {
  432.                     $firstLayout reset($validLayouts);
  433.                     $currentLayoutId $firstLayout->getId();
  434.                 } else {
  435.                     foreach ($validLayouts as $checkDefaultLayout) {
  436.                         if ($checkDefaultLayout->getDefault()) {
  437.                             $currentLayoutId $checkDefaultLayout->getId();
  438.                         }
  439.                     }
  440.                 }
  441.             }
  442.             if ($currentLayoutId === null && count($validLayouts) > 0) {
  443.                 $currentLayoutId reset($validLayouts)->getId();
  444.             }
  445.             if (!empty($validLayouts)) {
  446.                 $objectData['validLayouts'] = [];
  447.                 foreach ($validLayouts as $validLayout) {
  448.                     $objectData['validLayouts'][] = ['id' => $validLayout->getId(), 'name' => $validLayout->getName()];
  449.                 }
  450.                 usort($objectData['validLayouts'], static function ($layoutData1$layoutData2) {
  451.                     if ($layoutData2['id'] === '-1') {
  452.                         return 1;
  453.                     }
  454.                     if ($layoutData1['id'] === '-1') {
  455.                         return -1;
  456.                     }
  457.                     if ($layoutData2['id'] === '0') {
  458.                         return 1;
  459.                     }
  460.                     if ($layoutData1['id'] === '0') {
  461.                         return -1;
  462.                     }
  463.                     return strcasecmp($layoutData1['name'], $layoutData2['name']);
  464.                 });
  465.                 $user Tool\Admin::getCurrentUser();
  466.                 if ($currentLayoutId == -&& $user->isAdmin()) {
  467.                     $layout DataObject\Service::getSuperLayoutDefinition($object);
  468.                     $objectData['layout'] = $layout;
  469.                 } elseif (!empty($currentLayoutId)) {
  470.                     $objectData['layout'] = $validLayouts[$currentLayoutId]->getLayoutDefinitions();
  471.                 }
  472.                 $objectData['currentLayoutId'] = $currentLayoutId;
  473.             }
  474.             //Hook for modifying return value - e.g. for changing permissions based on object data
  475.             //data need to wrapped into a container in order to pass parameter to event listeners by reference so that they can change the values
  476.             $event = new GenericEvent($this, [
  477.                 'data' => $objectData,
  478.                 'object' => $object,
  479.             ]);
  480.             DataObject\Service::enrichLayoutDefinition($objectData['layout'], $object);
  481.             $eventDispatcher->dispatch($eventAdminEvents::OBJECT_GET_PRE_SEND_DATA);
  482.             $data $event->getArgument('data');
  483.             DataObject\Service::removeElementFromSession('object'$object->getId());
  484.             if ($data['layout'] ?? false) {
  485.                 $layoutArray json_decode($this->encodeJson($data['layout']), true);
  486.                 $this->classFieldDefinitions json_decode($this->encodeJson($object->getClass()->getFieldDefinitions()), true);
  487.                 $this->injectValuesForCustomLayout($layoutArray);
  488.                 $data['layout'] = $layoutArray;
  489.             }
  490.             return $this->adminJson($data);
  491.         }
  492.         throw $this->createAccessDeniedHttpException();
  493.     }
  494.     private function injectValuesForCustomLayout(array &$layout): void
  495.     {
  496.         foreach ($layout['children'] as &$child) {
  497.             if ($child['datatype'] === 'layout') {
  498.                 $this->injectValuesForCustomLayout($child);
  499.             } else {
  500.                 foreach ($this->classFieldDefinitions[$child['name']] as $key => $value) {
  501.                     if (array_key_exists($key$child) && ($child[$key] === null || $child[$key] === '' || (is_array($child[$key]) && empty($child[$key])))) {
  502.                         $child[$key] = $value;
  503.                     }
  504.                 }
  505.             }
  506.         }
  507.         //TODO remove in Pimcore 11
  508.         if (isset($layout['childs'])) {
  509.             foreach ($layout['childs'] as &$child) {
  510.                 if ($child['datatype'] === 'layout') {
  511.                     $this->injectValuesForCustomLayout($child);
  512.                 } else {
  513.                     foreach ($this->classFieldDefinitions[$child['name']] as $key => $value) {
  514.                         if (array_key_exists($key$child) && ($child[$key] === null || $child[$key] === '' || (is_array($child[$key]) && empty($child[$key])))) {
  515.                             $child[$key] = $value;
  516.                         }
  517.                     }
  518.                 }
  519.             }
  520.         }
  521.     }
  522.     /**
  523.      * @param DataObject\Concrete $object
  524.      * @param bool $objectFromVersion
  525.      */
  526.     private function getDataForObject(DataObject\Concrete $object$objectFromVersion false)
  527.     {
  528.         foreach ($object->getClass()->getFieldDefinitions(['object' => $object]) as $key => $def) {
  529.             $this->getDataForField($object$key$def$objectFromVersion);
  530.         }
  531.     }
  532.     /**
  533.      * gets recursively attribute data from parent and fills objectData and metaData
  534.      *
  535.      * @param DataObject\Concrete $object
  536.      * @param string $key
  537.      * @param DataObject\ClassDefinition\Data $fielddefinition
  538.      * @param bool $objectFromVersion
  539.      * @param int $level
  540.      */
  541.     private function getDataForField($object$key$fielddefinition$objectFromVersion$level 0)
  542.     {
  543.         $parent DataObject\Service::hasInheritableParentObject($object);
  544.         $getter 'get' ucfirst($key);
  545.         // Editmode optimization for lazy loaded relations (note that this is just for AbstractRelations, not for all
  546.         // LazyLoadingSupportInterface types. It tries to optimize fetching the data needed for the editmode without
  547.         // loading the entire target element.
  548.         // ReverseObjectRelation should go in there anyway (regardless if it a version or not),
  549.         // so that the values can be loaded.
  550.         if (
  551.             (!$objectFromVersion && $fielddefinition instanceof AbstractRelations)
  552.             || $fielddefinition instanceof ReverseObjectRelation
  553.         ) {
  554.             $refId null;
  555.             if ($fielddefinition instanceof ReverseObjectRelation) {
  556.                 $refKey $fielddefinition->getOwnerFieldName();
  557.                 $refClass DataObject\ClassDefinition::getByName($fielddefinition->getOwnerClassName());
  558.                 if ($refClass) {
  559.                     $refId $refClass->getId();
  560.                 }
  561.             } else {
  562.                 $refKey $key;
  563.             }
  564.             $relations $object->getRelationData($refKey, !$fielddefinition instanceof ReverseObjectRelation$refId);
  565.             if ($fielddefinition->supportsInheritance() && empty($relations) && !empty($parent)) {
  566.                 $this->getDataForField($parent$key$fielddefinition$objectFromVersion$level 1);
  567.             } else {
  568.                 $data = [];
  569.                 if ($fielddefinition instanceof DataObject\ClassDefinition\Data\ManyToOneRelation) {
  570.                     if (isset($relations[0])) {
  571.                         $data $relations[0];
  572.                         $data['published'] = (bool)$data['published'];
  573.                     } else {
  574.                         $data null;
  575.                     }
  576.                 } elseif (
  577.                     ($fielddefinition instanceof DataObject\ClassDefinition\Data\OptimizedAdminLoadingInterface && $fielddefinition->isOptimizedAdminLoading())
  578.                     || ($fielddefinition instanceof ManyToManyObjectRelation && !$fielddefinition->getVisibleFields() && !$fielddefinition instanceof DataObject\ClassDefinition\Data\AdvancedManyToManyObjectRelation)
  579.                 ) {
  580.                     foreach ($relations as $rkey => $rel) {
  581.                         $index $rkey 1;
  582.                         $rel['fullpath'] = $rel['path'];
  583.                         $rel['classname'] = $rel['subtype'];
  584.                         $rel['rowId'] = $rel['id'] . AbstractRelations::RELATION_ID_SEPARATOR $index AbstractRelations::RELATION_ID_SEPARATOR $rel['type'];
  585.                         $rel['published'] = (bool)$rel['published'];
  586.                         $data[] = $rel;
  587.                     }
  588.                 } else {
  589.                     $fieldData $object->$getter();
  590.                     $data $fielddefinition->getDataForEditmode($fieldData$object, ['objectFromVersion' => $objectFromVersion]);
  591.                 }
  592.                 $this->objectData[$key] = $data;
  593.                 $this->metaData[$key]['objectid'] = $object->getId();
  594.                 $this->metaData[$key]['inherited'] = $level != 0;
  595.             }
  596.         } else {
  597.             $fieldData $object->$getter();
  598.             $isInheritedValue false;
  599.             if ($fielddefinition instanceof DataObject\ClassDefinition\Data\CalculatedValue) {
  600.                 $fieldData = new DataObject\Data\CalculatedValue($fielddefinition->getName());
  601.                 $fieldData->setContextualData('object'nullnullnullnullnull$fielddefinition);
  602.                 $value $fielddefinition->getDataForEditmode($fieldData$object, ['objectFromVersion' => $objectFromVersion]);
  603.             } else {
  604.                 $value $fielddefinition->getDataForEditmode($fieldData$object, ['objectFromVersion' => $objectFromVersion]);
  605.             }
  606.             // following some exceptions for special data types (localizedfields, objectbricks)
  607.             if ($value && ($fieldData instanceof DataObject\Localizedfield || $fieldData instanceof DataObject\Classificationstore)) {
  608.                 // make sure that the localized field participates in the inheritance detection process
  609.                 $isInheritedValue $value['inherited'];
  610.             }
  611.             if ($fielddefinition instanceof DataObject\ClassDefinition\Data\Objectbricks && is_array($value)) {
  612.                 // make sure that the objectbricks participate in the inheritance detection process
  613.                 foreach ($value as $singleBrickData) {
  614.                     if (!empty($singleBrickData['inherited'])) {
  615.                         $isInheritedValue true;
  616.                     }
  617.                 }
  618.             }
  619.             if ($fielddefinition->isEmpty($fieldData) && !empty($parent)) {
  620.                 $this->getDataForField($parent$key$fielddefinition$objectFromVersion$level 1);
  621.                 // exception for classification store. if there are no items then it is empty by definition.
  622.                 // consequence is that we have to preserve the metadata information
  623.                 // see https://github.com/pimcore/pimcore/issues/9329
  624.                 if ($fielddefinition instanceof DataObject\ClassDefinition\Data\Classificationstore && $level == 0) {
  625.                     $this->objectData[$key]['metaData'] = $value['metaData'] ?? [];
  626.                     $this->objectData[$key]['inherited'] = true;
  627.                 }
  628.             } else {
  629.                 $isInheritedValue $isInheritedValue || ($level != 0);
  630.                 $this->metaData[$key]['objectid'] = $object->getId();
  631.                 $this->objectData[$key] = $value;
  632.                 $this->metaData[$key]['inherited'] = $isInheritedValue;
  633.                 if ($isInheritedValue && !$fielddefinition->isEmpty($fieldData) && !$fielddefinition->supportsInheritance()) {
  634.                     $this->objectData[$key] = null;
  635.                     $this->metaData[$key]['inherited'] = false;
  636.                     $this->metaData[$key]['hasParentValue'] = true;
  637.                 }
  638.             }
  639.         }
  640.     }
  641.     /**
  642.      * @Route("/get-folder", name="getfolder", methods={"GET"})
  643.      *
  644.      * @param Request $request
  645.      * @param EventDispatcherInterface $eventDispatcher
  646.      *
  647.      * @return JsonResponse
  648.      */
  649.     public function getFolderAction(Request $requestEventDispatcherInterface $eventDispatcher)
  650.     {
  651.         $objectId = (int)$request->get('id');
  652.         $object DataObject::getById($objectId);
  653.         if (!$object) {
  654.             throw $this->createNotFoundException();
  655.         }
  656.         if ($object->isAllowed('view')) {
  657.             $objectData = [];
  658.             $objectData['general'] = [];
  659.             $objectData['idPath'] = Element\Service::getIdPath($object);
  660.             $objectData['type'] = $object->getType();
  661.             $allowedKeys = ['o_published''o_key''o_id''o_type''o_path''o_modificationDate''o_creationDate''o_userOwner''o_userModification'];
  662.             foreach ($object->getObjectVars() as $key => $value) {
  663.                 if (strstr($key'o_') && in_array($key$allowedKeys)) {
  664.                     $objectData['general'][$key] = $value;
  665.                 }
  666.             }
  667.             $objectData['general']['fullpath'] = $object->getRealFullPath();
  668.             $objectData['general']['o_locked'] = $object->isLocked();
  669.             $objectData['properties'] = Element\Service::minimizePropertiesForEditmode($object->getProperties());
  670.             $objectData['userPermissions'] = $object->getUserPermissions($this->getAdminUser());
  671.             $objectData['classes'] = $this->prepareChildClasses($object->getDao()->getClasses());
  672.             // grid-config
  673.             $configFile PIMCORE_CONFIGURATION_DIRECTORY '/object/grid/' $object->getId() . '-user_' $this->getAdminUser()->getId() . '.psf';
  674.             if (is_file($configFile)) {
  675.                 $gridConfig Tool\Serialize::unserialize(file_get_contents($configFile));
  676.                 if ($gridConfig) {
  677.                     $selectedClassId $gridConfig['classId'];
  678.                     foreach ($objectData['classes'] as $class) {
  679.                         if ($class['id'] == $selectedClassId) {
  680.                             $objectData['selectedClass'] = $selectedClassId;
  681.                             break;
  682.                         }
  683.                     }
  684.                 }
  685.             }
  686.             //Hook for modifying return value - e.g. for changing permissions based on object data
  687.             //data need to wrapped into a container in order to pass parameter to event listeners by reference so that they can change the values
  688.             $event = new GenericEvent($this, [
  689.                 'data' => $objectData,
  690.                 'object' => $object,
  691.             ]);
  692.             $eventDispatcher->dispatch($eventAdminEvents::OBJECT_GET_PRE_SEND_DATA);
  693.             $objectData $event->getArgument('data');
  694.             return $this->adminJson($objectData);
  695.         }
  696.         throw $this->createAccessDeniedHttpException();
  697.     }
  698.     /**
  699.      * @param DataObject\ClassDefinition[] $classes
  700.      *
  701.      * @return array
  702.      */
  703.     protected function prepareChildClasses(array $classes): array
  704.     {
  705.         $reduced = [];
  706.         foreach ($classes as $class) {
  707.             $reduced[] = [
  708.                 'id' => $class->getId(),
  709.                 'name' => $class->getName(),
  710.                 'inheritance' => $class->getAllowInherit(),
  711.             ];
  712.         }
  713.         return $reduced;
  714.     }
  715.     /**
  716.      * @Route("/add", name="add", methods={"POST"})
  717.      *
  718.      * @param Request $request
  719.      * @param Model\FactoryInterface $modelFactory
  720.      *
  721.      * @return JsonResponse
  722.      */
  723.     public function addAction(Request $requestModel\FactoryInterface $modelFactory): JsonResponse
  724.     {
  725.         $message '';
  726.         $parent DataObject::getById((int) $request->get('parentId'));
  727.         if (!$parent->isAllowed('create')) {
  728.             $message 'prevented adding object because of missing permissions';
  729.             Logger::debug($message);
  730.         }
  731.         $intendedPath $parent->getRealFullPath() . '/' $request->get('key');
  732.         if (DataObject\Service::pathExists($intendedPath)) {
  733.             $message 'prevented creating object because object with same path+key already exists';
  734.             Logger::debug($message);
  735.         }
  736.         //return false if missing permissions or path+key already exists
  737.         if (!empty($message)) {
  738.             return $this->adminJson([
  739.                 'success' => false,
  740.                 'message' => $message,
  741.             ]);
  742.         }
  743.         $className 'Pimcore\\Model\\DataObject\\' ucfirst($request->get('className'));
  744.         /** @var DataObject\Concrete $object */
  745.         $object $modelFactory->build($className);
  746.         $object->setOmitMandatoryCheck(true); // allow to save the object although there are mandatory fields
  747.         $object->setClassId($request->get('classId'));
  748.         if ($request->get('variantViaTree')) {
  749.             $parentId $request->get('parentId');
  750.             $parent DataObject\Concrete::getById($parentId);
  751.             $object->setClassId($parent->getClass()->getId());
  752.         }
  753.         $object->setClassName($request->get('className'));
  754.         $object->setParentId($request->get('parentId'));
  755.         $object->setKey($request->get('key'));
  756.         $object->setCreationDate(time());
  757.         $object->setUserOwner($this->getAdminUser()->getId());
  758.         $object->setUserModification($this->getAdminUser()->getId());
  759.         $object->setPublished(false);
  760.         $objectType $request->get('objecttype');
  761.         if (in_array($objectType, [DataObject::OBJECT_TYPE_OBJECTDataObject::OBJECT_TYPE_VARIANT])) {
  762.             $object->setType($objectType);
  763.         }
  764.         try {
  765.             $object->save();
  766.             $return = [
  767.                 'success' => true,
  768.                 'id' => $object->getId(),
  769.                 'type' => $object->getType(),
  770.                 'message' => $message,
  771.             ];
  772.         } catch (\Exception $e) {
  773.             $return = [
  774.                 'success' => false,
  775.                 'message' => $e->getMessage(),
  776.             ];
  777.         }
  778.         return $this->adminJson($return);
  779.     }
  780.     /**
  781.      * @Route("/add-folder", name="addfolder", methods={"POST"})
  782.      *
  783.      * @param Request $request
  784.      *
  785.      * @return JsonResponse
  786.      */
  787.     public function addFolderAction(Request $request)
  788.     {
  789.         $success false;
  790.         $parent DataObject::getById((int) $request->get('parentId'));
  791.         if ($parent->isAllowed('create')) {
  792.             if (!DataObject\Service::pathExists($parent->getRealFullPath() . '/' $request->get('key'))) {
  793.                 $folder DataObject\Folder::create([
  794.                     'o_parentId' => $request->get('parentId'),
  795.                     'o_creationDate' => time(),
  796.                     'o_userOwner' => $this->getAdminUser()->getId(),
  797.                     'o_userModification' => $this->getAdminUser()->getId(),
  798.                     'o_key' => $request->get('key'),
  799.                     'o_published' => true,
  800.                 ]);
  801.                 try {
  802.                     $folder->save();
  803.                     $success true;
  804.                 } catch (\Exception $e) {
  805.                     return $this->adminJson(['success' => false'message' => $e->getMessage()]);
  806.                 }
  807.             }
  808.         } else {
  809.             Logger::debug('prevented creating object id because of missing permissions');
  810.         }
  811.         return $this->adminJson(['success' => $success]);
  812.     }
  813.     /**
  814.      * @Route("/delete", name="delete", methods={"DELETE"})
  815.      *
  816.      * @param Request $request
  817.      *
  818.      * @return JsonResponse
  819.      *
  820.      * @throws \Exception
  821.      */
  822.     public function deleteAction(Request $request)
  823.     {
  824.         $type $request->get('type');
  825.         if ($type === 'childs') {
  826.             trigger_deprecation(
  827.                 'pimcore/pimcore',
  828.                 '10.4',
  829.                 'Type childs is deprecated. Use children instead'
  830.             );
  831.             $type 'children';
  832.         }
  833.         if ($type === 'children') {
  834.             $parentObject DataObject::getById((int) $request->get('id'));
  835.             $list = new DataObject\Listing();
  836.             $list->setCondition('o_path LIKE ' $list->quote($list->escapeLike($parentObject->getRealFullPath()) . '/%'));
  837.             $list->setLimit((int)$request->get('amount'));
  838.             $list->setOrderKey('LENGTH(o_path)'false);
  839.             $list->setOrder('DESC');
  840.             $deletedItems = [];
  841.             foreach ($list as $object) {
  842.                 $deletedItems[$object->getId()] = $object->getRealFullPath();
  843.                 if ($object->isAllowed('delete') && !$object->isLocked()) {
  844.                     $object->delete();
  845.                 }
  846.             }
  847.             return $this->adminJson(['success' => true'deleted' => $deletedItems]);
  848.         }
  849.         if ($id $request->get('id')) {
  850.             $object DataObject::getById((int) $id);
  851.             if ($object) {
  852.                 if (!$object->isAllowed('delete')) {
  853.                     throw $this->createAccessDeniedHttpException();
  854.                 }
  855.                 if ($object->isLocked()) {
  856.                     return $this->adminJson(['success' => false'message' => 'prevented deleting object, because it is locked: ID: ' $object->getId()]);
  857.                 }
  858.                 $object->delete();
  859.             }
  860.             // return true, even when the object doesn't exist, this can be the case when using batch delete incl. children
  861.             return $this->adminJson(['success' => true]);
  862.         }
  863.         return $this->adminJson(['success' => false]);
  864.     }
  865.     /**
  866.      * @Route("/change-children-sort-by", name="changechildrensortby", methods={"PUT"})
  867.      *
  868.      * @param Request $request
  869.      *
  870.      * @return JsonResponse
  871.      *
  872.      * @throws \Exception
  873.      */
  874.     public function changeChildrenSortByAction(Request $request)
  875.     {
  876.         $object DataObject::getById((int) $request->get('id'));
  877.         if ($object) {
  878.             $sortBy $request->get('sortBy');
  879.             $sortOrder $request->get('childrenSortOrder');
  880.             if (!\in_array($sortOrder, ['ASC''DESC'])) {
  881.                 $sortOrder 'ASC';
  882.             }
  883.             $currentSortBy $object->getChildrenSortBy();
  884.             $object->setChildrenSortBy($sortBy);
  885.             $object->setChildrenSortOrder($sortOrder);
  886.             if ($currentSortBy != $sortBy) {
  887.                 $user Tool\Admin::getCurrentUser();
  888.                 if (!$user->isAdmin() && !$user->isAllowed('objects_sort_method')) {
  889.                     return $this->json(['success' => false'message' => 'Changing the sort method is only allowed for admin users']);
  890.                 }
  891.                 if ($sortBy == 'index') {
  892.                     $this->reindexBasedOnSortOrder($object$sortOrder);
  893.                 }
  894.             }
  895.             $object->save();
  896.             return $this->json(['success' => true]);
  897.         }
  898.         return $this->json(['success' => false'message' => 'Unable to change a sorting way of children items.']);
  899.     }
  900.     /**
  901.      * @Route("/update", name="update", methods={"PUT"})
  902.      *
  903.      * @param Request $request
  904.      *
  905.      * @return JsonResponse
  906.      *
  907.      * @throws \Exception
  908.      */
  909.     public function updateAction(Request $request)
  910.     {
  911.         $values $this->decodeJson($request->get('values'));
  912.         $ids $this->decodeJson($request->get('id'));
  913.         if (is_array($ids)) {
  914.             $return = ['success' => true];
  915.             foreach ($ids as $id) {
  916.                 $object DataObject::getById((int)$id);
  917.                 $return $this->executeUpdateAction($object$values);
  918.                 if (!$return['success']) {
  919.                     return $this->adminJson($return);
  920.                 }
  921.             }
  922.         } else {
  923.             $object DataObject::getById((int)$ids);
  924.             $return $this->executeUpdateAction($object$values);
  925.         }
  926.         return $this->adminJson($return);
  927.     }
  928.     /**
  929.      * @return array{success: bool, message?: string}
  930.      *
  931.      * @throws \Exception
  932.      */
  933.     private function executeUpdateAction(DataObject $objectmixed $values): array
  934.     {
  935.         $success false;
  936.         if ($object instanceof DataObject\Concrete) {
  937.             $object->setOmitMandatoryCheck(true);
  938.         }
  939.         // this prevents the user from renaming, relocating (actions in the tree) if the newest version isn't the published one
  940.         // the reason is that otherwise the content of the newer not published version will be overwritten
  941.         if ($object instanceof DataObject\Concrete) {
  942.             $latestVersion $object->getLatestVersion();
  943.             if ($latestVersion && $latestVersion->getData()->getModificationDate() != $object->getModificationDate()) {
  944.                 return ['success' => false'message' => "You can't rename or relocate if there's a newer not published version"];
  945.             }
  946.         }
  947.         $key $values['key'] ?? null;
  948.         if ($object->isAllowed('settings')) {
  949.             if ($key) {
  950.                 if ($object->isAllowed('rename')) {
  951.                     $object->setKey($key);
  952.                 } elseif ($key !== $object->getKey()) {
  953.                     Logger::debug('prevented renaming object because of missing permissions ');
  954.                 }
  955.             }
  956.             if (!empty($values['parentId'])) {
  957.                 $parent DataObject::getById($values['parentId']);
  958.                 //check if parent is changed
  959.                 if ($object->getParentId() != $parent->getId()) {
  960.                     if (!$parent->isAllowed('create')) {
  961.                         throw new \Exception('Prevented moving object - no create permission on new parent ');
  962.                     }
  963.                     $objectWithSamePath DataObject::getByPath($parent->getRealFullPath() . '/' $object->getKey());
  964.                     if ($objectWithSamePath != null) {
  965.                         return ['success' => false'message' => 'prevented creating object because object with same path+key already exists'];
  966.                     }
  967.                     if ($object->isLocked()) {
  968.                         return ['success' => false'message' => 'prevented moving object, because it is locked: ID: ' $object->getId()];
  969.                     }
  970.                     $object->setParentId($values['parentId']);
  971.                 }
  972.             }
  973.             if (array_key_exists('locked'$values)) {
  974.                 $object->setLocked($values['locked']);
  975.             }
  976.             $object->setModificationDate(time());
  977.             $object->setUserModification($this->getAdminUser()->getId());
  978.             try {
  979.                 $isIndexUpdate = isset($values['indices']);
  980.                 if ($isIndexUpdate) {
  981.                     // Ensure the update sort index is already available in the postUpdate eventListener
  982.                     $indexUpdate is_int($values['indices']) ? $values['indices'] : $values['indices'][$object->getId()];
  983.                     $object->setIndex($indexUpdate);
  984.                 }
  985.                 $object->save();
  986.                 if ($isIndexUpdate) {
  987.                     $this->updateIndexesOfObjectSiblings($object$indexUpdate);
  988.                 }
  989.                 $success true;
  990.             } catch (\Exception $e) {
  991.                 Logger::error((string) $e);
  992.                 return ['success' => false'message' => $e->getMessage()];
  993.             }
  994.         } elseif ($key && $object->isAllowed('rename')) {
  995.             return $this->renameObject($object$key);
  996.         } else {
  997.             Logger::debug('prevented update object because of missing permissions.');
  998.         }
  999.         return ['success' => $success];
  1000.     }
  1001.     private function executeInsideTransaction(callable $fn)
  1002.     {
  1003.         $maxRetries 5;
  1004.         for ($retries 0$retries $maxRetries$retries++) {
  1005.             try {
  1006.                 Db::get()->beginTransaction();
  1007.                 $fn();
  1008.                 Db::get()->commit();
  1009.                 break;
  1010.             } catch (\Exception $e) {
  1011.                 Db::get()->rollBack();
  1012.                 // we try to start the transaction $maxRetries times again (deadlocks, ...)
  1013.                 if ($retries < ($maxRetries 1)) {
  1014.                     $run $retries 1;
  1015.                     $waitTime rand(15) * 100000// microseconds
  1016.                     Logger::warn('Unable to finish transaction (' $run ". run) because of the following reason '" $e->getMessage() . "'. --> Retrying in " $waitTime ' microseconds ... (' . ($run 1) . ' of ' $maxRetries ')');
  1017.                     usleep($waitTime); // wait specified time until we restart the transaction
  1018.                 } else {
  1019.                     // if the transaction still fail after $maxRetries retries, we throw out the exception
  1020.                     Logger::error('Finally giving up restarting the same transaction again and again, last message: ' $e->getMessage());
  1021.                     throw $e;
  1022.                 }
  1023.             }
  1024.         }
  1025.     }
  1026.     /**
  1027.      * @param DataObject\AbstractObject $parentObject
  1028.      * @param string $currentSortOrder
  1029.      */
  1030.     protected function reindexBasedOnSortOrder(DataObject\AbstractObject $parentObjectstring $currentSortOrder)
  1031.     {
  1032.         $fn = function () use ($parentObject$currentSortOrder) {
  1033.             $list = new DataObject\Listing();
  1034.             $db Db::get();
  1035.             $result $db->executeStatement(
  1036.                 'UPDATE '.$list->getDao()->getTableName().' o,
  1037.                     (
  1038.                     SELECT newIndex, o_id FROM (
  1039.                         SELECT @n := @n +1 AS newIndex, o_id
  1040.                         FROM '.$list->getDao()->getTableName().',
  1041.                                 (SELECT @n := -1) variable
  1042.                                  WHERE o_parentId = ? ORDER BY o_key ' $currentSortOrder
  1043.                                .') tmp
  1044.                     ) order_table
  1045.                     SET o.o_index = order_table.newIndex
  1046.                     WHERE o.o_id=order_table.o_id',
  1047.                 [
  1048.                     $parentObject->getId(),
  1049.                 ]
  1050.             );
  1051.             $db Db::get();
  1052.             $children $db->fetchAllAssociative(
  1053.                 'SELECT o_id, o_modificationDate, o_versionCount FROM objects'
  1054.                 .' WHERE o_parentId = ? ORDER BY o_index ASC',
  1055.                 [$parentObject->getId()]
  1056.             );
  1057.             $index 0;
  1058.             foreach ($children as $child) {
  1059.                 $this->updateLatestVersionIndex($child['o_id'], $child['o_modificationDate']);
  1060.                 $index++;
  1061.                 DataObject::clearDependentCacheByObjectId($child['o_id']);
  1062.             }
  1063.         };
  1064.         $this->executeInsideTransaction($fn);
  1065.     }
  1066.     private function updateLatestVersionIndex($objectId$newIndex)
  1067.     {
  1068.         $object DataObject\Concrete::getById($objectId);
  1069.         if (
  1070.             $object &&
  1071.             $object->getType() != DataObject::OBJECT_TYPE_FOLDER &&
  1072.             $latestVersion $object->getLatestVersion()
  1073.         ) {
  1074.             // don't renew references (which means loading the target elements)
  1075.             // Not needed as we just save a new version with the updated index
  1076.             $object $latestVersion->loadData(false);
  1077.             if ($newIndex !== $object->getIndex()) {
  1078.                 $object->setIndex($newIndex);
  1079.             }
  1080.             $latestVersion->save();
  1081.         }
  1082.     }
  1083.     /**
  1084.      * @param DataObject\AbstractObject $updatedObject
  1085.      * @param int $newIndex
  1086.      */
  1087.     protected function updateIndexesOfObjectSiblings(DataObject\AbstractObject $updatedObject$newIndex)
  1088.     {
  1089.         $fn = function () use ($updatedObject$newIndex) {
  1090.             $list = new DataObject\Listing();
  1091.             $updatedObject->saveIndex($newIndex);
  1092.             // The cte and the limit are needed to order the data before the newIndex is set
  1093.             $db Db::get();
  1094.             $db->executeStatement(
  1095.                 'UPDATE '.$list->getDao()->getTableName().' o,
  1096.                     (
  1097.                         SELECT newIndex, o_id
  1098.                         FROM (
  1099.                             With cte As (SELECT o_index, o_id FROM ' $list->getDao()->getTableName() . ' WHERE o_parentId = ? AND o_id != ? AND o_type IN (\''.implode(
  1100.                     "','", [
  1101.                         DataObject::OBJECT_TYPE_OBJECT,
  1102.                         DataObject::OBJECT_TYPE_VARIANT,
  1103.                         DataObject::OBJECT_TYPE_FOLDER,
  1104.                     ]
  1105.                 ).'\') ORDER BY o_index LIMIT '$updatedObject->getParent()->getChildAmount([
  1106.                             DataObject::OBJECT_TYPE_OBJECT,
  1107.                             DataObject::OBJECT_TYPE_VARIANT,
  1108.                             DataObject::OBJECT_TYPE_FOLDER,
  1109.                         ]) .')
  1110.                             SELECT @n := IF(@n = ? - 1,@n + 2,@n + 1) AS newIndex, o_id
  1111.                             FROM cte,
  1112.                             (SELECT @n := -1) variable
  1113.                         ) tmp
  1114.                     ) order_table
  1115.                     SET o.o_index = order_table.newIndex
  1116.                     WHERE o.o_id=order_table.o_id',
  1117.                 [
  1118.                     $updatedObject->getParentId(),
  1119.                     $updatedObject->getId(),
  1120.                     $newIndex,
  1121.                 ]
  1122.             );
  1123.             $siblings $db->fetchAllAssociative(
  1124.                 'SELECT o_id, o_modificationDate, o_versionCount, o_key, o_index FROM objects'
  1125.                 ." WHERE o_parentId = ? AND o_id != ? AND o_type IN ('object', 'variant','folder') ORDER BY o_index ASC",
  1126.                 [$updatedObject->getParentId(), $updatedObject->getId()]
  1127.             );
  1128.             $index 0;
  1129.             foreach ($siblings as $sibling) {
  1130.                 if ($index == $newIndex) {
  1131.                     $index++;
  1132.                 }
  1133.                 $this->updateLatestVersionIndex($sibling['o_id'], $index);
  1134.                 $index++;
  1135.                 DataObject::clearDependentCacheByObjectId($sibling['o_id']);
  1136.             }
  1137.         };
  1138.         $this->executeInsideTransaction($fn);
  1139.     }
  1140.     /**
  1141.      * @Route("/save", name="save", methods={"POST", "PUT"})
  1142.      *
  1143.      * @param Request $request
  1144.      *
  1145.      * @return JsonResponse
  1146.      *
  1147.      * @throws \Exception
  1148.      */
  1149.     public function saveAction(Request $request)
  1150.     {
  1151.         $objectFromDatabase DataObject\Concrete::getById((int) $request->get('id'));
  1152.         if (!$objectFromDatabase instanceof DataObject\Concrete) {
  1153.             return $this->adminJson(['success' => false'message' => 'Could not find object']);
  1154.         }
  1155.         // set the latest available version for editmode
  1156.         $object $this->getLatestVersion($objectFromDatabase);
  1157.         $object->setUserModification($this->getAdminUser()->getId());
  1158.         $objectFromVersion $object !== $objectFromDatabase;
  1159.         $originalModificationDate $objectFromVersion $object->getModificationDate() : $objectFromDatabase->getModificationDate();
  1160.         if ($objectFromVersion) {
  1161.             if (method_exists($object'getLocalizedFields')) {
  1162.                 /** @var DataObject\Localizedfield $localizedFields */
  1163.                 $localizedFields $object->getLocalizedFields();
  1164.                 $localizedFields->setLoadedAllLazyData();
  1165.             }
  1166.         }
  1167.         // data
  1168.         $data = [];
  1169.         if ($request->get('data')) {
  1170.             $data $this->decodeJson($request->get('data'));
  1171.             foreach ($data as $key => $value) {
  1172.                 $fd $object->getClass()->getFieldDefinition($key);
  1173.                 if ($fd) {
  1174.                     if ($fd instanceof DataObject\ClassDefinition\Data\Localizedfields) {
  1175.                         $user Tool\Admin::getCurrentUser();
  1176.                         if (!$user->getAdmin()) {
  1177.                             $allowedLanguages DataObject\Service::getLanguagePermissions($object$user'lEdit');
  1178.                             if (!is_null($allowedLanguages)) {
  1179.                                 $allowedLanguages array_keys($allowedLanguages);
  1180.                                 $submittedLanguages array_keys($data[$key]);
  1181.                                 foreach ($submittedLanguages as $submittedLanguage) {
  1182.                                     if (!in_array($submittedLanguage$allowedLanguages)) {
  1183.                                         unset($value[$submittedLanguage]);
  1184.                                     }
  1185.                                 }
  1186.                             }
  1187.                         }
  1188.                     }
  1189.                     if ($fd instanceof ReverseObjectRelation) {
  1190.                         $remoteClass DataObject\ClassDefinition::getByName($fd->getOwnerClassName());
  1191.                         $relations $object->getRelationData($fd->getOwnerFieldName(), false$remoteClass->getId());
  1192.                         $toAdd $this->detectAddedRemoteOwnerRelations($relations$value);
  1193.                         $toDelete $this->detectDeletedRemoteOwnerRelations($relations$value);
  1194.                         if (count($toAdd) > || count($toDelete) > 0) {
  1195.                             $this->processRemoteOwnerRelations($object$toDelete$toAdd$fd->getOwnerFieldName());
  1196.                         }
  1197.                     } else {
  1198.                         $object->setValue($key$fd->getDataFromEditmode($value$object, ['objectFromVersion' => $objectFromVersion]));
  1199.                     }
  1200.                 }
  1201.             }
  1202.         }
  1203.         // general settings
  1204.         // @TODO: IS THIS STILL NECESSARY?
  1205.         if ($request->get('general')) {
  1206.             $general $this->decodeJson($request->get('general'));
  1207.             // do not allow all values to be set, will cause problems (eg. icon)
  1208.             if (is_array($general) && count($general) > 0) {
  1209.                 foreach ($general as $key => $value) {
  1210.                     if (!in_array($key, ['o_id''o_classId''o_className''o_type''icon''o_userOwner''o_userModification''o_modificationDate'])) {
  1211.                         $object->setValue($key$value);
  1212.                     }
  1213.                 }
  1214.             }
  1215.         }
  1216.         $this->assignPropertiesFromEditmode($request$object);
  1217.         $this->applySchedulerDataToElement($request$object);
  1218.         if (($request->get('task') === 'unpublish' && !$object->isAllowed('unpublish')) || ($request->get('task') === 'publish' && !$object->isAllowed('publish'))) {
  1219.             throw $this->createAccessDeniedHttpException();
  1220.         }
  1221.         if ($request->get('task') == 'unpublish') {
  1222.             $object->setPublished(false);
  1223.         }
  1224.         if ($request->get('task') == 'publish') {
  1225.             $object->setPublished(true);
  1226.         }
  1227.         // unpublish and save version is possible without checking mandatory fields
  1228.         if (in_array($request->get('task'), ['unpublish''version''autoSave'])) {
  1229.             $object->setOmitMandatoryCheck(true);
  1230.         }
  1231.         if (($request->get('task') == 'publish') || ($request->get('task') == 'unpublish')) {
  1232.             // disabled for now: see different approach [Elements] Show users who are working on the same element #9381
  1233.             // https://github.com/pimcore/pimcore/issues/9381
  1234.             //            if ($data) {
  1235.             //                if (!$this->performFieldcollectionModificationCheck($request, $object, $originalModificationDate, $data)) {
  1236.             //                    return $this->adminJson(['success' => false, 'message' => 'Could be that someone messed around with the fieldcollection in the meantime. Please reload and try again']);
  1237.             //                }
  1238.             //            }
  1239.             $object->save();
  1240.             $treeData $this->getTreeNodeConfig($object);
  1241.             $newObject DataObject::getById($object->getId(), ['force' => true]);
  1242.             if ($request->get('task') == 'publish') {
  1243.                 $object->deleteAutoSaveVersions($this->getAdminUser()->getId());
  1244.             }
  1245.             return $this->adminJson([
  1246.                 'success' => true,
  1247.                 'general' => ['o_modificationDate' => $object->getModificationDate(),
  1248.                     'versionDate' => $newObject->getModificationDate(),
  1249.                     'versionCount' => $newObject->getVersionCount(),
  1250.                 ],
  1251.                 'treeData' => $treeData,
  1252.             ]);
  1253.         } elseif ($request->get('task') == 'session') {
  1254.             //TODO https://github.com/pimcore/pimcore/issues/9536
  1255.             DataObject\Service::saveElementToSession($object''true);
  1256.             return $this->adminJson(['success' => true]);
  1257.         } elseif ($request->get('task') == 'scheduler') {
  1258.             if ($object->isAllowed('settings')) {
  1259.                 $object->saveScheduledTasks();
  1260.                 return $this->adminJson(['success' => true]);
  1261.             }
  1262.         } elseif ($object->isAllowed('save') || $object->isAllowed('publish')) {
  1263.             $isAutoSave $request->get('task') == 'autoSave';
  1264.             $draftData = [];
  1265.             if ($object->isPublished() || $isAutoSave) {
  1266.                 $version $object->saveVersion(truetruenull$isAutoSave);
  1267.                 $draftData = [
  1268.                     'id' => $version->getId(),
  1269.                     'modificationDate' => $version->getDate(),
  1270.                     'isAutoSave' => $version->isAutoSave(),
  1271.                 ];
  1272.             } else {
  1273.                 $object->save();
  1274.             }
  1275.             if ($request->get('task') == 'version') {
  1276.                 $object->deleteAutoSaveVersions($this->getAdminUser()->getId());
  1277.             }
  1278.             $treeData $this->getTreeNodeConfig($object);
  1279.             $newObject DataObject::getById($object->getId(), ['force' => true]);
  1280.             return $this->adminJson([
  1281.                 'success' => true,
  1282.                 'general' => ['o_modificationDate' => $object->getModificationDate(),
  1283.                     'versionDate' => $newObject->getModificationDate(),
  1284.                     'versionCount' => $newObject->getVersionCount(),
  1285.                 ],
  1286.                 'draft' => $draftData,
  1287.                 'treeData' => $treeData,
  1288.             ]);
  1289.         }
  1290.         throw $this->createAccessDeniedHttpException();
  1291.     }
  1292.     /**
  1293.      * @param Request $request
  1294.      * @param DataObject\Concrete $object
  1295.      * @param int $originalModificationDate
  1296.      * @param array $data
  1297.      *
  1298.      * @return bool
  1299.      *
  1300.      * @throws \Exception
  1301.      */
  1302.     protected function performFieldcollectionModificationCheck(Request $requestDataObject\Concrete $object$originalModificationDate$data)
  1303.     {
  1304.         $modificationDate $request->get('modificationDate');
  1305.         if ($modificationDate != $originalModificationDate) {
  1306.             $fielddefinitions $object->getClass()->getFieldDefinitions();
  1307.             foreach ($fielddefinitions as $fd) {
  1308.                 if ($fd instanceof DataObject\ClassDefinition\Data\Fieldcollections) {
  1309.                     if (isset($data[$fd->getName()])) {
  1310.                         $allowedTypes $fd->getAllowedTypes();
  1311.                         foreach ($allowedTypes as $type) {
  1312.                             /** @var DataObject\Fieldcollection\Definition $fdDef */
  1313.                             $fdDef DataObject\Fieldcollection\Definition::getByKey($type);
  1314.                             $childDefinitions $fdDef->getFieldDefinitions();
  1315.                             foreach ($childDefinitions as $childDef) {
  1316.                                 if ($childDef instanceof DataObject\ClassDefinition\Data\Localizedfields) {
  1317.                                     return false;
  1318.                                 }
  1319.                             }
  1320.                         }
  1321.                     }
  1322.                 }
  1323.             }
  1324.         }
  1325.         return true;
  1326.     }
  1327.     /**
  1328.      * @Route("/save-folder", name="savefolder", methods={"PUT"})
  1329.      *
  1330.      * @param Request $request
  1331.      *
  1332.      * @return JsonResponse
  1333.      */
  1334.     public function saveFolderAction(Request $request)
  1335.     {
  1336.         $object DataObject::getById((int) $request->get('id'));
  1337.         if (!$object) {
  1338.             throw $this->createNotFoundException('Object not found');
  1339.         }
  1340.         if ($object->isAllowed('publish')) {
  1341.             try {
  1342.                 // general settings
  1343.                 $general $this->decodeJson($request->get('general'));
  1344.                 $object->setValues($general);
  1345.                 $object->setUserModification($this->getAdminUser()->getId());
  1346.                 $this->assignPropertiesFromEditmode($request$object);
  1347.                 $object->save();
  1348.                 return $this->adminJson(['success' => true]);
  1349.             } catch (\Exception $e) {
  1350.                 return $this->adminJson(['success' => false'message' => $e->getMessage()]);
  1351.             }
  1352.         }
  1353.         throw $this->createAccessDeniedHttpException();
  1354.     }
  1355.     /**
  1356.      * @param Request $request
  1357.      * @param DataObject\AbstractObject $object
  1358.      */
  1359.     protected function assignPropertiesFromEditmode(Request $request$object)
  1360.     {
  1361.         if ($request->get('properties')) {
  1362.             $properties = [];
  1363.             // assign inherited properties
  1364.             foreach ($object->getProperties() as $p) {
  1365.                 if ($p->isInherited()) {
  1366.                     $properties[$p->getName()] = $p;
  1367.                 }
  1368.             }
  1369.             $propertiesData $this->decodeJson($request->get('properties'));
  1370.             if (is_array($propertiesData)) {
  1371.                 foreach ($propertiesData as $propertyName => $propertyData) {
  1372.                     $value $propertyData['data'];
  1373.                     try {
  1374.                         $property = new Model\Property();
  1375.                         $property->setType($propertyData['type']);
  1376.                         $property->setName($propertyName);
  1377.                         $property->setCtype('object');
  1378.                         $property->setDataFromEditmode($value);
  1379.                         $property->setInheritable($propertyData['inheritable']);
  1380.                         $properties[$propertyName] = $property;
  1381.                     } catch (\Exception $e) {
  1382.                         Logger::err("Can't add " $propertyName ' to object ' $object->getRealFullPath());
  1383.                     }
  1384.                 }
  1385.             }
  1386.             $object->setProperties($properties);
  1387.         }
  1388.     }
  1389.     /**
  1390.      * @Route("/publish-version", name="publishversion", methods={"POST"})
  1391.      *
  1392.      * @param Request $request
  1393.      *
  1394.      * @return JsonResponse
  1395.      */
  1396.     public function publishVersionAction(Request $request)
  1397.     {
  1398.         $id = (int)$request->get('id');
  1399.         $version Model\Version::getById($id);
  1400.         $object $version?->loadData();
  1401.         if (!$object) {
  1402.             throw $this->createNotFoundException('Version with id [' $id "] doesn't exist");
  1403.         }
  1404.         $currentObject DataObject::getById($object->getId());
  1405.         if ($currentObject->isAllowed('publish')) {
  1406.             $object->setPublished(true);
  1407.             $object->setUserModification($this->getAdminUser()->getId());
  1408.             try {
  1409.                 $object->save();
  1410.                 $this->addAdminStyle($objectElementAdminStyleEvent::CONTEXT_TREE$treeData);
  1411.                 return $this->adminJson(
  1412.                     [
  1413.                         'success' => true,
  1414.                         'general' => ['o_modificationDate' => $object->getModificationDate() ],
  1415.                         'treeData' => $treeData, ]
  1416.                 );
  1417.             } catch (\Exception $e) {
  1418.                 return $this->adminJson(['success' => false'message' => $e->getMessage()]);
  1419.             }
  1420.         }
  1421.         throw $this->createAccessDeniedHttpException();
  1422.     }
  1423.     /**
  1424.      * @Route("/preview-version", name="previewversion", methods={"GET"})
  1425.      *
  1426.      * @param Request $request
  1427.      *
  1428.      * @throws \Exception
  1429.      *
  1430.      * @return Response
  1431.      */
  1432.     public function previewVersionAction(Request $request)
  1433.     {
  1434.         DataObject::setDoNotRestoreKeyAndPath(true);
  1435.         $id = (int)$request->get('id');
  1436.         $version Model\Version::getById($id);
  1437.         $object $version?->loadData();
  1438.         if ($object) {
  1439.             if (method_exists($object'getLocalizedFields')) {
  1440.                 /** @var DataObject\Localizedfield $localizedFields */
  1441.                 $localizedFields $object->getLocalizedFields();
  1442.                 $localizedFields->setLoadedAllLazyData();
  1443.             }
  1444.             DataObject::setDoNotRestoreKeyAndPath(false);
  1445.             if ($object->isAllowed('versions')) {
  1446.                 return $this->render('@PimcoreAdmin/Admin/DataObject/DataObject/previewVersion.html.twig',
  1447.                     [
  1448.                         'object' => $object,
  1449.                         'versionNote' => $version->getNote(),
  1450.                         'validLanguages' => Tool::getValidLanguages(),
  1451.                     ]);
  1452.             }
  1453.             throw $this->createAccessDeniedException('Permission denied, version id [' $id ']');
  1454.         }
  1455.         throw $this->createNotFoundException('Version with id [' $id "] doesn't exist");
  1456.     }
  1457.     /**
  1458.      * @Route("/diff-versions/from/{from}/to/{to}", name="diffversions", methods={"GET"})
  1459.      *
  1460.      * @param Request $request
  1461.      * @param int $from
  1462.      * @param int $to
  1463.      *
  1464.      * @return Response
  1465.      *
  1466.      * @throws \Exception
  1467.      */
  1468.     public function diffVersionsAction(Request $request$from$to)
  1469.     {
  1470.         DataObject::setDoNotRestoreKeyAndPath(true);
  1471.         $id1 = (int)$from;
  1472.         $id2 = (int)$to;
  1473.         $version1 Model\Version::getById($id1);
  1474.         $object1 $version1?->loadData();
  1475.         if (!$object1) {
  1476.             throw $this->createNotFoundException('Version with id [' $id1 "] doesn't exist");
  1477.         }
  1478.         if (method_exists($object1'getLocalizedFields')) {
  1479.             /** @var DataObject\Localizedfield $localizedFields1 */
  1480.             $localizedFields1 $object1->getLocalizedFields();
  1481.             $localizedFields1->setLoadedAllLazyData();
  1482.         }
  1483.         $version2 Model\Version::getById($id2);
  1484.         $object2 $version2?->loadData();
  1485.         if (!$object2) {
  1486.             throw $this->createNotFoundException('Version with id [' $id2 "] doesn't exist");
  1487.         }
  1488.         if (method_exists($object2'getLocalizedFields')) {
  1489.             /** @var DataObject\Localizedfield $localizedFields2 */
  1490.             $localizedFields2 $object2->getLocalizedFields();
  1491.             $localizedFields2->setLoadedAllLazyData();
  1492.         }
  1493.         DataObject::setDoNotRestoreKeyAndPath(false);
  1494.         if ($object1->isAllowed('versions') && $object2->isAllowed('versions')) {
  1495.             return $this->render('@PimcoreAdmin/Admin/DataObject/DataObject/diffVersions.html.twig',
  1496.                 [
  1497.                     'object1' => $object1,
  1498.                     'versionNote1' => $version1->getNote(),
  1499.                     'object2' => $object2,
  1500.                     'versionNote2' => $version2->getNote(),
  1501.                     'validLanguages' => Tool::getValidLanguages(),
  1502.                 ]);
  1503.         }
  1504.         throw $this->createAccessDeniedException('Permission denied, version ids [' $id1 ', ' $id2 ']');
  1505.     }
  1506.     /**
  1507.      * @Route("/grid-proxy", name="gridproxy", methods={"GET", "POST", "PUT"})
  1508.      *
  1509.      * @param Request $request
  1510.      * @param EventDispatcherInterface $eventDispatcher
  1511.      * @param GridHelperService $gridHelperService
  1512.      * @param LocaleServiceInterface $localeService
  1513.      * @param CsrfProtectionHandler $csrfProtection
  1514.      *
  1515.      * @return JsonResponse
  1516.      */
  1517.     public function gridProxyAction(
  1518.         Request $request,
  1519.         EventDispatcherInterface $eventDispatcher,
  1520.         GridHelperService $gridHelperService,
  1521.         LocaleServiceInterface $localeService,
  1522.         CsrfProtectionHandler $csrfProtection
  1523.     ): JsonResponse {
  1524.         $allParams array_merge($request->request->all(), $request->query->all());
  1525.         if (isset($allParams['context']) && $allParams['context']) {
  1526.             $allParams['context'] = json_decode($allParams['context'], true);
  1527.         } else {
  1528.             $allParams['context'] = [];
  1529.         }
  1530.         $filterPrepareEvent = new GenericEvent($this, [
  1531.             'requestParams' => $allParams,
  1532.         ]);
  1533.         $eventDispatcher->dispatch($filterPrepareEventAdminEvents::OBJECT_LIST_BEFORE_FILTER_PREPARE);
  1534.         $allParams $filterPrepareEvent->getArgument('requestParams');
  1535.         $csrfProtection->checkCsrfToken($request);
  1536.         $result $this->gridProxy(
  1537.             $allParams,
  1538.             DataObject::OBJECT_TYPE_OBJECT,
  1539.             $request,
  1540.             $eventDispatcher,
  1541.             $gridHelperService,
  1542.             $localeService
  1543.         );
  1544.         return $this->adminJson($result);
  1545.     }
  1546.     /**
  1547.      * @Route("/copy-info", name="copyinfo", methods={"GET"})
  1548.      *
  1549.      * @param Request $request
  1550.      *
  1551.      * @return JsonResponse
  1552.      */
  1553.     public function copyInfoAction(Request $request)
  1554.     {
  1555.         $transactionId time();
  1556.         $pasteJobs = [];
  1557.         Tool\Session::useSession(function (AttributeBagInterface $session) use ($transactionId) {
  1558.             $session->set((string) $transactionId, ['idMapping' => []]);
  1559.         }, 'pimcore_copy');
  1560.         if ($request->get('type') == 'recursive' || $request->get('type') == 'recursive-update-references') {
  1561.             $object DataObject::getById((int) $request->get('sourceId'));
  1562.             // first of all the new parent
  1563.             $pasteJobs[] = [[
  1564.                 'url' => $this->generateUrl('pimcore_admin_dataobject_dataobject_copy'),
  1565.                 'method' => 'POST',
  1566.                 'params' => [
  1567.                     'sourceId' => $request->get('sourceId'),
  1568.                     'targetId' => $request->get('targetId'),
  1569.                     'type' => 'child',
  1570.                     'transactionId' => $transactionId,
  1571.                     'saveParentId' => true,
  1572.                 ],
  1573.             ]];
  1574.             if ($object->hasChildren(DataObject::$types)) {
  1575.                 // get amount of children
  1576.                 $list = new DataObject\Listing();
  1577.                 $list->setCondition('o_path LIKE ' $list->quote($list->escapeLike($object->getRealFullPath()) . '/%'));
  1578.                 $list->setOrderKey('LENGTH(o_path)'false);
  1579.                 $list->setOrder('ASC');
  1580.                 $list->setObjectTypes(DataObject::$types);
  1581.                 $childIds $list->loadIdList();
  1582.                 if (count($childIds) > 0) {
  1583.                     foreach ($childIds as $id) {
  1584.                         $pasteJobs[] = [[
  1585.                             'url' => $this->generateUrl('pimcore_admin_dataobject_dataobject_copy'),
  1586.                             'method' => 'POST',
  1587.                             'params' => [
  1588.                                 'sourceId' => $id,
  1589.                                 'targetParentId' => $request->get('targetId'),
  1590.                                 'sourceParentId' => $request->get('sourceId'),
  1591.                                 'type' => 'child',
  1592.                                 'transactionId' => $transactionId,
  1593.                             ],
  1594.                         ]];
  1595.                     }
  1596.                 }
  1597.                 // add id-rewrite steps
  1598.                 if ($request->get('type') == 'recursive-update-references') {
  1599.                     for ($i 0$i < (count($childIds) + 1); $i++) {
  1600.                         $pasteJobs[] = [[
  1601.                             'url' => $this->generateUrl('pimcore_admin_dataobject_dataobject_copyrewriteids'),
  1602.                             'method' => 'PUT',
  1603.                             'params' => [
  1604.                                 'transactionId' => $transactionId,
  1605.                                 '_dc' => uniqid(),
  1606.                             ],
  1607.                         ]];
  1608.                     }
  1609.                 }
  1610.             }
  1611.         } elseif ($request->get('type') == 'child' || $request->get('type') == 'replace') {
  1612.             // the object itself is the last one
  1613.             $pasteJobs[] = [[
  1614.                 'url' => $this->generateUrl('pimcore_admin_dataobject_dataobject_copy'),
  1615.                 'method' => 'POST',
  1616.                 'params' => [
  1617.                     'sourceId' => $request->get('sourceId'),
  1618.                     'targetId' => $request->get('targetId'),
  1619.                     'type' => $request->get('type'),
  1620.                     'transactionId' => $transactionId,
  1621.                 ],
  1622.             ]];
  1623.         }
  1624.         return $this->adminJson([
  1625.             'pastejobs' => $pasteJobs,
  1626.         ]);
  1627.     }
  1628.     /**
  1629.      * @Route("/copy-rewrite-ids", name="copyrewriteids", methods={"PUT"})
  1630.      *
  1631.      * @param Request $request
  1632.      *
  1633.      * @return JsonResponse
  1634.      *
  1635.      * @throws \Exception
  1636.      */
  1637.     public function copyRewriteIdsAction(Request $request)
  1638.     {
  1639.         $transactionId $request->get('transactionId');
  1640.         $idStore Tool\Session::useSession(function (AttributeBagInterface $session) use ($transactionId) {
  1641.             return $session->get($transactionId);
  1642.         }, 'pimcore_copy');
  1643.         if (!array_key_exists('rewrite-stack'$idStore)) {
  1644.             $idStore['rewrite-stack'] = array_values($idStore['idMapping']);
  1645.         }
  1646.         $id array_shift($idStore['rewrite-stack']);
  1647.         $object DataObject::getById($id);
  1648.         // create rewriteIds() config parameter
  1649.         $rewriteConfig = ['object' => $idStore['idMapping']];
  1650.         $object DataObject\Service::rewriteIds($object$rewriteConfig);
  1651.         $object->setUserModification($this->getAdminUser()->getId());
  1652.         $object->save();
  1653.         // write the store back to the session
  1654.         Tool\Session::useSession(function (AttributeBagInterface $session) use ($transactionId$idStore) {
  1655.             $session->set($transactionId$idStore);
  1656.         }, 'pimcore_copy');
  1657.         return $this->adminJson([
  1658.             'success' => true,
  1659.             'id' => $id,
  1660.         ]);
  1661.     }
  1662.     /**
  1663.      * @Route("/copy", name="copy", methods={"POST"})
  1664.      *
  1665.      * @param Request $request
  1666.      *
  1667.      * @return JsonResponse
  1668.      */
  1669.     public function copyAction(Request $request)
  1670.     {
  1671.         $message '';
  1672.         $sourceId = (int)$request->get('sourceId');
  1673.         $source DataObject::getById($sourceId);
  1674.         $session Tool\Session::get('pimcore_copy');
  1675.         $sessionBag $session->get($request->get('transactionId'));
  1676.         $targetId = (int)$request->get('targetId');
  1677.         if ($request->get('targetParentId')) {
  1678.             $sourceParent DataObject::getById((int) $request->get('sourceParentId'));
  1679.             // this is because the key can get the prefix "_copy" if the target does already exists
  1680.             if ($sessionBag['parentId']) {
  1681.                 $targetParent DataObject::getById($sessionBag['parentId']);
  1682.             } else {
  1683.                 $targetParent DataObject::getById((int) $request->get('targetParentId'));
  1684.             }
  1685.             $targetPath preg_replace('@^' preg_quote($sourceParent->getRealFullPath(), '@') . '@'$targetParent '/'$source->getRealPath());
  1686.             $target DataObject::getByPath($targetPath);
  1687.         } else {
  1688.             $target DataObject::getById($targetId);
  1689.         }
  1690.         if ($target->isAllowed('create')) {
  1691.             $source DataObject::getById($sourceId);
  1692.             if ($source != null) {
  1693.                 if ($source instanceof DataObject\Concrete && $latestVersion $source->getLatestVersion()) {
  1694.                     $source $latestVersion->loadData();
  1695.                     $source->setPublished(false); //as latest version is used which is not published
  1696.                 }
  1697.                 if ($request->get('type') == 'child') {
  1698.                     $newObject $this->_objectService->copyAsChild($target$source);
  1699.                     $sessionBag['idMapping'][(int)$source->getId()] = (int)$newObject->getId();
  1700.                     // this is because the key can get the prefix "_copy" if the target does already exists
  1701.                     if ($request->get('saveParentId')) {
  1702.                         $sessionBag['parentId'] = $newObject->getId();
  1703.                     }
  1704.                 } elseif ($request->get('type') == 'replace') {
  1705.                     $this->_objectService->copyContents($target$source);
  1706.                 }
  1707.                 $session->set($request->get('transactionId'), $sessionBag);
  1708.                 Tool\Session::writeClose();
  1709.                 return $this->adminJson(['success' => true'message' => $message]);
  1710.             } else {
  1711.                 Logger::error("could not execute copy/paste, source object with id [ $sourceId ] not found");
  1712.                 return $this->adminJson(['success' => false'message' => 'source object not found']);
  1713.             }
  1714.         } else {
  1715.             throw $this->createAccessDeniedHttpException();
  1716.         }
  1717.     }
  1718.     /**
  1719.      * @Route("/preview", name="preview", methods={"GET"})
  1720.      *
  1721.      * @param Request $request
  1722.      * @param PreviewGeneratorInterface $defaultPreviewGenerator
  1723.      *
  1724.      * @return Response|RedirectResponse
  1725.      */
  1726.     public function previewAction(Request $requestPreviewGeneratorInterface $defaultPreviewGenerator)
  1727.     {
  1728.         $id $request->get('id');
  1729.         $object DataObject\Service::getElementFromSession('object'$id);
  1730.         if ($object instanceof DataObject\Concrete) {
  1731.             $url $object->getClass()->getPreviewUrl();
  1732.             if ($url) {
  1733.                 // replace named variables
  1734.                 $vars $object->getObjectVars();
  1735.                 foreach ($vars as $key => $value) {
  1736.                     if (!empty($value) && \is_scalar($value)) {
  1737.                         $url str_replace('%' $keyurlencode($value), $url);
  1738.                     } else {
  1739.                         if (strpos($url'%' $key) !== false) {
  1740.                             return new Response('No preview available, please ensure that all fields which are required for the preview are filled correctly.');
  1741.                         }
  1742.                     }
  1743.                 }
  1744.                 $url str_replace('%_locale'$this->getAdminUser()->getLanguage(), $url);
  1745.             } elseif ($previewService $object->getClass()->getPreviewGenerator()) {
  1746.                 $url $previewService->generatePreviewUrl($objectarray_merge(['preview' => true'context' => $this], $request->query->all()));
  1747.             } elseif ($object->getClass()->getLinkGenerator()) {
  1748.                 $parameters = [
  1749.                     'preview' => true,
  1750.                     'context' => $this,
  1751.                 ];
  1752.                 $url $defaultPreviewGenerator->generatePreviewUrl($objectarray_merge($parameters$request->query->all()));
  1753.             }
  1754.             if (!$url) {
  1755.                 throw new NotFoundHttpException('Cannot render preview due to empty URL');
  1756.             }
  1757.             // replace all remaining % signs which are not an url encoded characters
  1758.             $url preg_replace('/%(?![0-9A-F]{2})/i''%25'$url);
  1759.             $urlParts parse_url($url);
  1760.             $redirectParameters array_filter([
  1761.                 'pimcore_object_preview' => $id,
  1762.                 'site' => $request->query->getInt(PreviewGeneratorInterface::PARAMETER_SITE),
  1763.                 'dc' => time(),
  1764.             ]);
  1765.             $redirectUrl $urlParts['path'] . '?' http_build_query($redirectParameters) . (isset($urlParts['query']) ? '&' $urlParts['query'] : '');
  1766.             return $this->redirect($redirectUrl);
  1767.         } else {
  1768.             throw new NotFoundHttpException(sprintf('Expected an object of type "%s", got "%s"'DataObject\Concrete::class, get_debug_type($object)));
  1769.         }
  1770.     }
  1771.     /**
  1772.      * @param  DataObject\Concrete $object
  1773.      * @param  array $toDelete
  1774.      * @param  array $toAdd
  1775.      * @param  string $ownerFieldName
  1776.      */
  1777.     protected function processRemoteOwnerRelations($object$toDelete$toAdd$ownerFieldName)
  1778.     {
  1779.         $getter 'get' ucfirst($ownerFieldName);
  1780.         $setter 'set' ucfirst($ownerFieldName);
  1781.         foreach ($toDelete as $id) {
  1782.             $owner DataObject::getById($id);
  1783.             //TODO: lock ?!
  1784.             if (method_exists($owner$getter)) {
  1785.                 $currentData $owner->$getter();
  1786.                 if (is_array($currentData)) {
  1787.                     for ($i 0$i count($currentData); $i++) {
  1788.                         if ($currentData[$i]->getId() == $object->getId()) {
  1789.                             unset($currentData[$i]);
  1790.                             $owner->$setter($currentData);
  1791.                             break;
  1792.                         }
  1793.                     }
  1794.                 } else {
  1795.                     if ($currentData->getId() == $object->getId()) {
  1796.                         $owner->$setter(null);
  1797.                     }
  1798.                 }
  1799.             }
  1800.             $owner->setUserModification($this->getAdminUser()->getId());
  1801.             $owner->save();
  1802.             Logger::debug('Saved object id [ ' $owner->getId() . ' ] by remote modification through [' $object->getId() . '], Action: deleted [ ' $object->getId() . " ] from [ $ownerFieldName]");
  1803.         }
  1804.         foreach ($toAdd as $id) {
  1805.             $owner DataObject::getById($id);
  1806.             //TODO: lock ?!
  1807.             if (method_exists($owner$getter)) {
  1808.                 $currentData $owner->$getter();
  1809.                 if (is_array($currentData)) {
  1810.                     $currentData[] = $object;
  1811.                 } else {
  1812.                     $currentData $object;
  1813.                 }
  1814.                 $owner->$setter($currentData);
  1815.                 $owner->setUserModification($this->getAdminUser()->getId());
  1816.                 $owner->save();
  1817.                 Logger::debug('Saved object id [ ' $owner->getId() . ' ] by remote modification through [' $object->getId() . '], Action: added [ ' $object->getId() . " ] to [ $ownerFieldName ]");
  1818.             }
  1819.         }
  1820.     }
  1821.     /**
  1822.      * @param  array $relations
  1823.      * @param  array $value
  1824.      *
  1825.      * @return array
  1826.      */
  1827.     protected function detectDeletedRemoteOwnerRelations($relations$value)
  1828.     {
  1829.         $originals = [];
  1830.         $changed = [];
  1831.         foreach ($relations as $r) {
  1832.             $originals[] = $r['dest_id'];
  1833.         }
  1834.         if (is_array($value)) {
  1835.             foreach ($value as $row) {
  1836.                 $changed[] = $row['id'];
  1837.             }
  1838.         }
  1839.         $diff array_diff($originals$changed);
  1840.         return $diff;
  1841.     }
  1842.     /**
  1843.      * @param  array $relations
  1844.      * @param  array $value
  1845.      *
  1846.      * @return array
  1847.      */
  1848.     protected function detectAddedRemoteOwnerRelations($relations$value)
  1849.     {
  1850.         $originals = [];
  1851.         $changed = [];
  1852.         foreach ($relations as $r) {
  1853.             $originals[] = $r['dest_id'];
  1854.         }
  1855.         if (is_array($value)) {
  1856.             foreach ($value as $row) {
  1857.                 $changed[] = $row['id'];
  1858.             }
  1859.         }
  1860.         $diff array_diff($changed$originals);
  1861.         return $diff;
  1862.     }
  1863.     /**
  1864.      * @template T of DataObject\Concrete
  1865.      *
  1866.      * @param T $object
  1867.      * @param null|Version $draftVersion
  1868.      *
  1869.      * @return T
  1870.      */
  1871.     protected function getLatestVersion(DataObject\Concrete $object, &$draftVersion null): ?DataObject\Concrete
  1872.     {
  1873.         $latestVersion $object->getLatestVersion($this->getAdminUser()->getId());
  1874.         if ($latestVersion) {
  1875.             $latestObj $latestVersion->loadData();
  1876.             if ($latestObj instanceof DataObject\Concrete) {
  1877.                 $draftVersion $latestVersion;
  1878.                 return $latestObj;
  1879.             }
  1880.         }
  1881.         return $object;
  1882.     }
  1883.     /**
  1884.      * @param ControllerEvent $event
  1885.      */
  1886.     public function onKernelControllerEvent(ControllerEvent $event)
  1887.     {
  1888.         if (!$event->isMainRequest()) {
  1889.             return;
  1890.         }
  1891.         // check permissions
  1892.         $this->checkPermission('objects');
  1893.         $this->_objectService = new DataObject\Service($this->getAdminUser());
  1894.     }
  1895. }