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

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