vendor/pimcore/pimcore/models/Element/Service.php line 511

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Model\Element;
  15. use DeepCopy\DeepCopy;
  16. use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
  17. use DeepCopy\Filter\SetNullFilter;
  18. use DeepCopy\Matcher\PropertyNameMatcher;
  19. use DeepCopy\Matcher\PropertyTypeMatcher;
  20. use Doctrine\Common\Collections\Collection;
  21. use Doctrine\DBAL\Query\QueryBuilder as DoctrineQueryBuilder;
  22. use League\Csv\EscapeFormula;
  23. use Pimcore;
  24. use Pimcore\Db;
  25. use Pimcore\Event\SystemEvents;
  26. use Pimcore\File;
  27. use Pimcore\Logger;
  28. use Pimcore\Model;
  29. use Pimcore\Model\Asset;
  30. use Pimcore\Model\DataObject;
  31. use Pimcore\Model\DataObject\AbstractObject;
  32. use Pimcore\Model\DataObject\ClassDefinition\Data;
  33. use Pimcore\Model\DataObject\Concrete;
  34. use Pimcore\Model\Dependency;
  35. use Pimcore\Model\Document;
  36. use Pimcore\Model\Element\DeepCopy\MarshalMatcher;
  37. use Pimcore\Model\Element\DeepCopy\PimcoreClassDefinitionMatcher;
  38. use Pimcore\Model\Element\DeepCopy\PimcoreClassDefinitionReplaceFilter;
  39. use Pimcore\Model\Element\DeepCopy\UnmarshalMatcher;
  40. use Pimcore\Model\Tool\TmpStore;
  41. use Pimcore\Tool\Serialize;
  42. use Pimcore\Tool\Session;
  43. use Symfony\Component\EventDispatcher\GenericEvent;
  44. use Symfony\Contracts\Translation\TranslatorInterface;
  45. /**
  46.  * @method \Pimcore\Model\Element\Dao getDao()
  47.  */
  48. class Service extends Model\AbstractModel
  49. {
  50.     /**
  51.      * @var EscapeFormula|null
  52.      */
  53.     private static ?EscapeFormula $formatter null;
  54.     /**
  55.      * @internal
  56.      *
  57.      * @param ElementInterface $element
  58.      *
  59.      * @return string
  60.      */
  61.     public static function getIdPath(ElementInterface $element): string
  62.     {
  63.         $path '';
  64.         $elementType self::getElementType($element);
  65.         $parentId $element->getParentId();
  66.         $parentElement self::getElementById($elementType$parentId);
  67.         if ($parentElement) {
  68.             $path self::getIdPath($parentElement);
  69.         }
  70.         $path .= '/' $element->getId();
  71.         return $path;
  72.     }
  73.     /**
  74.      * @internal
  75.      *
  76.      * @param ElementInterface $element
  77.      *
  78.      * @return string
  79.      *
  80.      * @throws \Exception
  81.      */
  82.     public static function getTypePath(ElementInterface $element): string
  83.     {
  84.         $path '';
  85.         $elementType self::getElementType($element);
  86.         $parentId $element->getParentId();
  87.         $parentElement self::getElementById($elementType$parentId);
  88.         if ($parentElement) {
  89.             $path self::getTypePath($parentElement);
  90.         }
  91.         $type $element->getType();
  92.         if ($type !== DataObject::OBJECT_TYPE_FOLDER) {
  93.             if ($element instanceof Document) {
  94.                 $type 'document';
  95.             } elseif ($element instanceof DataObject\AbstractObject) {
  96.                 $type 'object';
  97.             } elseif ($element instanceof Asset) {
  98.                 $type 'asset';
  99.             } else {
  100.                 throw new \Exception('unknown type');
  101.             }
  102.         }
  103.         $path .= '/' $type;
  104.         return $path;
  105.     }
  106.     /**
  107.      * @internal
  108.      *
  109.      * @param ElementInterface $element
  110.      *
  111.      * @return string
  112.      *
  113.      * @throws \Exception
  114.      */
  115.     public static function getSortIndexPath(ElementInterface $element): string
  116.     {
  117.         $path '';
  118.         $elementType self::getElementType($element);
  119.         $parentId $element->getParentId();
  120.         $parentElement self::getElementById($elementType$parentId);
  121.         if ($parentElement) {
  122.             $path self::getSortIndexPath($parentElement);
  123.         }
  124.         $sortIndex method_exists($element'getIndex') ? (int) $element->getIndex() : 0;
  125.         $path .= '/' $sortIndex;
  126.         return $path;
  127.     }
  128.     /**
  129.      * @internal
  130.      *
  131.      * @param array|Model\Listing\AbstractListing $list
  132.      * @param string $idGetter
  133.      *
  134.      * @return int[]
  135.      */
  136.     public static function getIdList($list$idGetter 'getId')
  137.     {
  138.         $ids = [];
  139.         if (is_array($list)) {
  140.             foreach ($list as $entry) {
  141.                 if (is_object($entry) && method_exists($entry$idGetter)) {
  142.                     $ids[] = $entry->$idGetter();
  143.                 } elseif (is_scalar($entry)) {
  144.                     $ids[] = $entry;
  145.                 }
  146.             }
  147.         }
  148.         if ($list instanceof Model\Listing\AbstractListing && method_exists($list'loadIdList')) {
  149.             $ids $list->loadIdList();
  150.         }
  151.         $ids array_unique($ids);
  152.         return $ids;
  153.     }
  154.     /**
  155.      * @internal
  156.      *
  157.      * @param Dependency $d
  158.      *
  159.      * @return array
  160.      */
  161.     public static function getRequiredByDependenciesForFrontend(Dependency $d$offset$limit)
  162.     {
  163.         $dependencies['hasHidden'] = false;
  164.         $dependencies['requiredBy'] = [];
  165.         // requiredBy
  166.         foreach ($d->getRequiredBy($offset$limit) as $r) {
  167.             if ($e self::getDependedElement($r)) {
  168.                 if ($e->isAllowed('list')) {
  169.                     $dependencies['requiredBy'][] = self::getDependencyForFrontend($e);
  170.                 } else {
  171.                     $dependencies['hasHidden'] = true;
  172.                 }
  173.             }
  174.         }
  175.         return $dependencies;
  176.     }
  177.     /**
  178.      * @internal
  179.      *
  180.      * @param Dependency $d
  181.      *
  182.      * @return array
  183.      */
  184.     public static function getRequiresDependenciesForFrontend(Dependency $d$offset$limit)
  185.     {
  186.         $dependencies['hasHidden'] = false;
  187.         $dependencies['requires'] = [];
  188.         // requires
  189.         foreach ($d->getRequires($offset$limit) as $r) {
  190.             if ($e self::getDependedElement($r)) {
  191.                 if ($e->isAllowed('list')) {
  192.                     $dependencies['requires'][] = self::getDependencyForFrontend($e);
  193.                 } else {
  194.                     $dependencies['hasHidden'] = true;
  195.                 }
  196.             }
  197.         }
  198.         return $dependencies;
  199.     }
  200.     /**
  201.      * @param ElementInterface $element
  202.      *
  203.      * @return array
  204.      */
  205.     private static function getDependencyForFrontend($element)
  206.     {
  207.         return [
  208.             'id' => $element->getId(),
  209.             'path' => $element->getRealFullPath(),
  210.             'type' => self::getElementType($element),
  211.             'subtype' => $element->getType(),
  212.             'published' => self::isPublished($element),
  213.         ];
  214.     }
  215.     /**
  216.      * @param array $config
  217.      *
  218.      * @return DataObject\AbstractObject|Document|Asset|null
  219.      */
  220.     private static function getDependedElement($config)
  221.     {
  222.         if ($config['type'] == 'object') {
  223.             return DataObject::getById($config['id']);
  224.         } elseif ($config['type'] == 'asset') {
  225.             return Asset::getById($config['id']);
  226.         } elseif ($config['type'] == 'document') {
  227.             return Document::getById($config['id']);
  228.         }
  229.         return null;
  230.     }
  231.     /**
  232.      * @static
  233.      *
  234.      * @return bool
  235.      */
  236.     public static function doHideUnpublished($element)
  237.     {
  238.         return ($element instanceof AbstractObject && DataObject::doHideUnpublished())
  239.             || ($element instanceof Document && Document::doHideUnpublished());
  240.     }
  241.     /**
  242.      * determines whether an element is published
  243.      *
  244.      * @internal
  245.      *
  246.      * @param  ElementInterface $element
  247.      *
  248.      * @return bool
  249.      */
  250.     public static function isPublished($element null)
  251.     {
  252.         if ($element instanceof ElementInterface) {
  253.             if (method_exists($element'isPublished')) {
  254.                 return $element->isPublished();
  255.             } else {
  256.                 return true;
  257.             }
  258.         }
  259.         return false;
  260.     }
  261.     /**
  262.      * @internal
  263.      *
  264.      * @param array|null $data
  265.      *
  266.      * @return array
  267.      *
  268.      * @throws \Exception
  269.      */
  270.     public static function filterUnpublishedAdvancedElements($data): array
  271.     {
  272.         if (DataObject::doHideUnpublished() && is_array($data)) {
  273.             $publishedList = [];
  274.             $mapping = [];
  275.             foreach ($data as $advancedElement) {
  276.                 if (!$advancedElement instanceof DataObject\Data\ObjectMetadata
  277.                     && !$advancedElement instanceof DataObject\Data\ElementMetadata) {
  278.                     throw new \Exception('only supported for advanced many-to-many (+object) relations');
  279.                 }
  280.                 $elementId null;
  281.                 if ($advancedElement instanceof DataObject\Data\ObjectMetadata) {
  282.                     $elementId $advancedElement->getObjectId();
  283.                     $elementType 'object';
  284.                 } else {
  285.                     $elementId $advancedElement->getElementId();
  286.                     $elementType $advancedElement->getElementType();
  287.                 }
  288.                 if (!$elementId) {
  289.                     continue;
  290.                 }
  291.                 if ($elementType == 'asset') {
  292.                     // there is no published flag for assets
  293.                     continue;
  294.                 }
  295.                 $mapping[$elementType][$elementId] = true;
  296.             }
  297.             $db Db::get();
  298.             $publishedMapping = [];
  299.             // now do the query;
  300.             foreach ($mapping as $elementType => $idList) {
  301.                 $idList array_keys($mapping[$elementType]);
  302.                 switch ($elementType) {
  303.                     case 'document':
  304.                         $idColumn 'id';
  305.                         $publishedColumn 'published';
  306.                         break;
  307.                     case 'object':
  308.                         $idColumn 'o_id';
  309.                         $publishedColumn 'o_published';
  310.                         break;
  311.                     default:
  312.                         throw new \Exception('unknown type');
  313.                 }
  314.                 $query 'SELECT ' $idColumn ' FROM ' $elementType 's WHERE ' $publishedColumn '=1 AND ' $idColumn ' IN (' implode(','$idList) . ');';
  315.                 $publishedIds $db->fetchCol($query);
  316.                 $publishedMapping[$elementType] = $publishedIds;
  317.             }
  318.             foreach ($data as $advancedElement) {
  319.                 $elementId null;
  320.                 if ($advancedElement instanceof DataObject\Data\ObjectMetadata) {
  321.                     $elementId $advancedElement->getObjectId();
  322.                     $elementType 'object';
  323.                 } else {
  324.                     $elementId $advancedElement->getElementId();
  325.                     $elementType $advancedElement->getElementType();
  326.                 }
  327.                 if ($elementType == 'asset') {
  328.                     $publishedList[] = $advancedElement;
  329.                 }
  330.                 if (isset($publishedMapping[$elementType]) && in_array($elementId$publishedMapping[$elementType])) {
  331.                     $publishedList[] = $advancedElement;
  332.                 }
  333.             }
  334.             return $publishedList;
  335.         }
  336.         return is_array($data) ? $data : [];
  337.     }
  338.     /**
  339.      * @param  string $type
  340.      * @param  string $path
  341.      *
  342.      * @return ElementInterface|null
  343.      */
  344.     public static function getElementByPath($type$path)
  345.     {
  346.         $element null;
  347.         if ($type == 'asset') {
  348.             $element Asset::getByPath($path);
  349.         } elseif ($type == 'object') {
  350.             $element DataObject::getByPath($path);
  351.         } elseif ($type == 'document') {
  352.             $element Document::getByPath($path);
  353.         }
  354.         return $element;
  355.     }
  356.     /**
  357.      * @internal
  358.      *
  359.      * @param string|ElementInterface $element
  360.      *
  361.      * @return string
  362.      *
  363.      * @throws \Exception
  364.      */
  365.     public static function getBaseClassNameForElement($element)
  366.     {
  367.         if ($element instanceof ElementInterface) {
  368.             $elementType self::getElementType($element);
  369.         } elseif (is_string($element)) {
  370.             $elementType $element;
  371.         } else {
  372.             throw new \Exception('Wrong type given for getBaseClassNameForElement(), ElementInterface and string are allowed');
  373.         }
  374.         $baseClass ucfirst($elementType);
  375.         if ($elementType == 'object') {
  376.             $baseClass 'DataObject';
  377.         }
  378.         return $baseClass;
  379.     }
  380.     /**
  381.      * @deprecated will be removed in Pimcore 11, use getSafeCopyName() instead
  382.      *
  383.      * @param string $type
  384.      * @param string $sourceKey
  385.      * @param ElementInterface $target
  386.      *
  387.      * @return string
  388.      */
  389.     public static function getSaveCopyName($type$sourceKey$target)
  390.     {
  391.         return self::getSafeCopyName($sourceKey$target);
  392.     }
  393.     /**
  394.      * Returns a uniqe key for the element in the $target-Path (recursive)
  395.      *
  396.      * @return string
  397.      *
  398.      * @param string $sourceKey
  399.      * @param ElementInterface $target
  400.      */
  401.     public static function getSafeCopyName(string $sourceKeyElementInterface $target)
  402.     {
  403.         $type self::getElementType($target);
  404.         if (self::pathExists($target->getRealFullPath() . '/' $sourceKey$type)) {
  405.             // only for assets: add the prefix _copy before the file extension (if exist) not after to that source.jpg will be source_copy.jpg and not source.jpg_copy
  406.             if ($type == 'asset' && $fileExtension File::getFileExtension($sourceKey)) {
  407.                 $sourceKey preg_replace('/\.' $fileExtension '$/i''_copy.' $fileExtension$sourceKey);
  408.             } elseif (preg_match("/_copy(|_\d*)$/"$sourceKey) === 1) {
  409.                 // If key already ends with _copy or copy_N, append a digit to avoid _copy_copy_copy naming
  410.                 $keyParts explode('_'$sourceKey);
  411.                 $counterKey array_key_last($keyParts);
  412.                 if ((int)$keyParts[$counterKey] > 0) {
  413.                     $keyParts[$counterKey] = (int)$keyParts[$counterKey] + 1;
  414.                 } else {
  415.                     $keyParts[] = 1;
  416.                 }
  417.                 $sourceKey implode('_'$keyParts);
  418.             } else {
  419.                 $sourceKey .= '_copy';
  420.             }
  421.             return self::getSafeCopyName($sourceKey$target);
  422.         }
  423.         return $sourceKey;
  424.     }
  425.     /**
  426.      * @param string $path
  427.      * @param string|null $type
  428.      *
  429.      * @return bool
  430.      */
  431.     public static function pathExists($path$type null)
  432.     {
  433.         if ($type == 'asset') {
  434.             return Asset\Service::pathExists($path);
  435.         } elseif ($type == 'document') {
  436.             return Document\Service::pathExists($path);
  437.         } elseif ($type == 'object') {
  438.             return DataObject\Service::pathExists($path);
  439.         }
  440.         return false;
  441.     }
  442.     /**
  443.      * @param  string $type
  444.      * @param  int $id
  445.      * @param  bool $force
  446.      *
  447.      * @return Asset|AbstractObject|Document|null
  448.      */
  449.     public static function getElementById($type$id$force false)
  450.     {
  451.         $element null;
  452.         if ($type === 'asset') {
  453.             $element Asset::getById($id$force);
  454.         } elseif ($type === 'object') {
  455.             $element DataObject::getById($id$force);
  456.         } elseif ($type === 'document') {
  457.             $element Document::getById($id$force);
  458.         }
  459.         return $element;
  460.     }
  461.     /**
  462.      * @static
  463.      *
  464.      * @param ElementInterface $element
  465.      *
  466.      * @return string|null
  467.      */
  468.     public static function getElementType($element): ?string
  469.     {
  470.         if ($element instanceof DataObject\AbstractObject) {
  471.             return 'object';
  472.         }
  473.         if ($element instanceof Document) {
  474.             return 'document';
  475.         }
  476.         if ($element instanceof Asset) {
  477.             return 'asset';
  478.         }
  479.         return null;
  480.     }
  481.     /**
  482.      * @internal
  483.      *
  484.      * @param string $className
  485.      *
  486.      * @return string|null
  487.      */
  488.     public static function getElementTypeByClassName(string $className): ?string
  489.     {
  490.         $className trim($className'\\');
  491.         if (is_a($classNameAbstractObject::class, true)) {
  492.             return 'object';
  493.         }
  494.         if (is_a($classNameAsset::class, true)) {
  495.             return 'asset';
  496.         }
  497.         if (is_a($classNameDocument::class, true)) {
  498.             return 'document';
  499.         }
  500.         return null;
  501.     }
  502.     /**
  503.      * @internal
  504.      *
  505.      * @param ElementInterface $element
  506.      *
  507.      * @return string|null
  508.      */
  509.     public static function getElementHash(ElementInterface $element): ?string
  510.     {
  511.         $elementType self::getElementType($element);
  512.         if ($elementType === null) {
  513.             return null;
  514.         }
  515.         return $elementType '-' $element->getId();
  516.     }
  517.     /**
  518.      * determines the type of an element (object,asset,document)
  519.      *
  520.      * @deprecated use getElementType() instead, will be removed in Pimcore 11
  521.      *
  522.      * @param  ElementInterface $element
  523.      *
  524.      * @return string
  525.      */
  526.     public static function getType($element)
  527.     {
  528.         trigger_deprecation(
  529.             'pimcore/pimcore',
  530.             '10.0',
  531.             'The Service::getType() method is deprecated, use Service::getElementType() instead.'
  532.         );
  533.         return self::getElementType($element);
  534.     }
  535.     /**
  536.      * @internal
  537.      *
  538.      * @param array $props
  539.      *
  540.      * @return array
  541.      */
  542.     public static function minimizePropertiesForEditmode($props)
  543.     {
  544.         $properties = [];
  545.         foreach ($props as $key => $p) {
  546.             //$p = object2array($p);
  547.             $allowedProperties = [
  548.                 'key',
  549.                 'o_key',
  550.                 'filename',
  551.                 'path',
  552.                 'o_path',
  553.                 'id',
  554.                 'o_id',
  555.                 'o_type',
  556.                 'type',
  557.             ];
  558.             if ($p->getData() instanceof Document || $p->getData() instanceof Asset || $p->getData() instanceof DataObject\AbstractObject) {
  559.                 $pa = [];
  560.                 $vars $p->getData()->getObjectVars();
  561.                 foreach ($vars as $k => $value) {
  562.                     if (in_array($k$allowedProperties)) {
  563.                         $pa[$k] = $value;
  564.                     }
  565.                 }
  566.                 // clone it because of caching
  567.                 $tmp = clone $p;
  568.                 $tmp->setData($pa);
  569.                 $properties[$key] = $tmp->getObjectVars();
  570.             } else {
  571.                 $properties[$key] = $p->getObjectVars();
  572.             }
  573.             // add config from predefined properties
  574.             if ($p->getName() && $p->getType()) {
  575.                 $predefined Model\Property\Predefined::getByKey($p->getName());
  576.                 if ($predefined && $predefined->getType() == $p->getType()) {
  577.                     $properties[$key]['config'] = $predefined->getConfig();
  578.                     $properties[$key]['predefinedName'] = $predefined->getName();
  579.                     $properties[$key]['description'] = $predefined->getDescription();
  580.                 }
  581.             }
  582.         }
  583.         return $properties;
  584.     }
  585.     /**
  586.      * @internal
  587.      *
  588.      * @param DataObject|Document|Asset\Folder $target the parent element
  589.      * @param ElementInterface $new the newly inserted child
  590.      */
  591.     protected function updateChildren($target$new)
  592.     {
  593.         //check in case of recursion
  594.         $found false;
  595.         foreach ($target->getChildren() as $child) {
  596.             if ($child->getId() == $new->getId()) {
  597.                 $found true;
  598.                 break;
  599.             }
  600.         }
  601.         if (!$found) {
  602.             $target->setChildren(array_merge($target->getChildren(), [$new]));
  603.         }
  604.     }
  605.     /**
  606.      * @internal
  607.      *
  608.      * @param  ElementInterface $element
  609.      *
  610.      * @return array
  611.      */
  612.     public static function gridElementData(ElementInterface $element)
  613.     {
  614.         $data = [
  615.             'id' => $element->getId(),
  616.             'fullpath' => $element->getRealFullPath(),
  617.             'type' => self::getElementType($element),
  618.             'subtype' => $element->getType(),
  619.             'filename' => $element->getKey(),
  620.             'creationDate' => $element->getCreationDate(),
  621.             'modificationDate' => $element->getModificationDate(),
  622.         ];
  623.         if (method_exists($element'isPublished')) {
  624.             $data['published'] = $element->isPublished();
  625.         } else {
  626.             $data['published'] = true;
  627.         }
  628.         return $data;
  629.     }
  630.     /**
  631.      * find all elements which the user may not list and therefore may never be shown to the user.
  632.      * A user may have custom workspaces and/or may inherit those from their role(s), if any.
  633.      *
  634.      * @internal
  635.      *
  636.      * @param string $type asset|object|document
  637.      * @param Model\User $user
  638.      *
  639.      * @return array{forbidden: array, allowed: array}
  640.      */
  641.     public static function findForbiddenPaths($type$user)
  642.     {
  643.         $db Db::get();
  644.         if ($user->isAdmin()) {
  645.             return ['forbidden' => [], 'allowed' => ['/']];
  646.         }
  647.         $workspaceCids = [];
  648.         $userWorkspaces $db->fetchAll('SELECT cpath, cid, list FROM users_workspaces_' $type ' WHERE userId = ?', [$user->getId()]);
  649.         if ($userWorkspaces) {
  650.             // this collects the array that are on user-level, which have top priority
  651.             foreach ($userWorkspaces as $userWorkspace) {
  652.                 $workspaceCids[] = $userWorkspace['cid'];
  653.             }
  654.         }
  655.         if ($userRoleIds $user->getRoles()) {
  656.             $roleWorkspacesSql 'SELECT cpath, userid, max(list) as list FROM users_workspaces_' $type ' WHERE userId IN (' implode(','$userRoleIds) . ')';
  657.             if ($workspaceCids) {
  658.                 $roleWorkspacesSql .= ' AND cid NOT IN (' implode(','$workspaceCids) . ')';
  659.             }
  660.             $roleWorkspacesSql .= ' GROUP BY cpath';
  661.             $roleWorkspaces $db->fetchAll($roleWorkspacesSql);
  662.         }
  663.         $uniquePaths = [];
  664.         foreach (array_merge($userWorkspaces$roleWorkspaces ?? []) as $workspace) {
  665.             $uniquePaths[$workspace['cpath']] = $workspace['list'];
  666.         }
  667.         ksort($uniquePaths);
  668.         //TODO: above this should be all in one query (eg. instead of ksort, use sql sort) but had difficulties making the `group by` working properly to let user permissions take precedence
  669.         $totalPaths count($uniquePaths);
  670.         $forbidden = [];
  671.         $allowed = [];
  672.         if ($totalPaths 0) {
  673.             $uniquePathsKeys array_keys($uniquePaths);
  674.             for ($index 0$index $totalPaths$index++) {
  675.                 $path $uniquePathsKeys[$index];
  676.                 if ($uniquePaths[$path] == 0) {
  677.                     $forbidden[$path] = [];
  678.                     for ($findIndex $index 1$findIndex $totalPaths$findIndex++) { //NB: the starting index is the last index we got
  679.                         $findPath $uniquePathsKeys[$findIndex];
  680.                         if (str_contains($findPath$path)) { //it means that we found a children
  681.                             if ($uniquePaths[$findPath] == 1) {
  682.                                 array_push($forbidden[$path], $findPath); //adding list=1 children
  683.                             }
  684.                         } else {
  685.                             break;
  686.                         }
  687.                     }
  688.                 } else {
  689.                     $allowed[] = $path;
  690.                 }
  691.             }
  692.         } else {
  693.             $forbidden['/'] = [];
  694.         }
  695.         return ['forbidden' => $forbidden'allowed' => $allowed];
  696.     }
  697.     /**
  698.      * renews all references, for example after unserializing an ElementInterface
  699.      *
  700.      * @internal
  701.      *
  702.      * @param mixed $data
  703.      * @param bool $initial
  704.      * @param string $key
  705.      *
  706.      * @return mixed
  707.      */
  708.     public static function renewReferences($data$initial true$key null)
  709.     {
  710.         if ($data instanceof \__PHP_Incomplete_Class) {
  711.             Logger::err(sprintf('Renew References: Cannot read data (%s) of incomplete class.'is_null($key) ? 'not available' $key));
  712.             return null;
  713.         }
  714.         if (is_array($data)) {
  715.             foreach ($data as $dataKey => &$value) {
  716.                 $value self::renewReferences($valuefalse$dataKey);
  717.             }
  718.             return $data;
  719.         }
  720.         if (is_object($data)) {
  721.             if ($data instanceof ElementInterface && !$initial) {
  722.                 return self::getElementById(self::getElementType($data), $data->getId());
  723.             }
  724.             // if this is the initial element set the correct path and key
  725.             if ($data instanceof ElementInterface && !DataObject\AbstractObject::doNotRestoreKeyAndPath()) {
  726.                 $originalElement self::getElementById(self::getElementType($data), $data->getId());
  727.                 if ($originalElement) {
  728.                     //do not override filename for Assets https://github.com/pimcore/pimcore/issues/8316
  729. //                    if ($data instanceof Asset) {
  730. //                        /** @var Asset $originalElement */
  731. //                        $data->setFilename($originalElement->getFilename());
  732. //                    } else
  733.                     if ($data instanceof Document) {
  734.                         /** @var Document $originalElement */
  735.                         $data->setKey($originalElement->getKey());
  736.                     } elseif ($data instanceof DataObject\AbstractObject) {
  737.                         /** @var AbstractObject $originalElement */
  738.                         $data->setKey($originalElement->getKey());
  739.                     }
  740.                     $data->setPath($originalElement->getRealPath());
  741.                 }
  742.             }
  743.             if ($data instanceof Model\AbstractModel) {
  744.                 $properties $data->getObjectVars();
  745.                 foreach ($properties as $name => $value) {
  746.                     $data->setObjectVar($nameself::renewReferences($valuefalse$name), true);
  747.                 }
  748.             } else {
  749.                 $properties method_exists($data'getObjectVars') ? $data->getObjectVars() : get_object_vars($data);
  750.                 foreach ($properties as $name => $value) {
  751.                     if (method_exists($data'setObjectVar')) {
  752.                         $data->setObjectVar($nameself::renewReferences($valuefalse$name), true);
  753.                     } else {
  754.                         $data->$name self::renewReferences($valuefalse$name);
  755.                     }
  756.                 }
  757.             }
  758.             return $data;
  759.         }
  760.         return $data;
  761.     }
  762.     /**
  763.      * @internal
  764.      *
  765.      * @param string $path
  766.      *
  767.      * @return string
  768.      */
  769.     public static function correctPath(string $path): string
  770.     {
  771.         // remove trailing slash
  772.         if ($path !== '/') {
  773.             $path rtrim($path'/ ');
  774.         }
  775.         // correct wrong path (root-node problem)
  776.         $path str_replace('//''/'$path);
  777.         if (str_contains($path'%')) {
  778.             $path rawurldecode($path);
  779.         }
  780.         return $path;
  781.     }
  782.     /**
  783.      * @internal
  784.      *
  785.      * @param ElementInterface $element
  786.      *
  787.      * @return ElementInterface
  788.      */
  789.     public static function loadAllFields(ElementInterface $element): ElementInterface
  790.     {
  791.         if ($element instanceof Document) {
  792.             Document\Service::loadAllDocumentFields($element);
  793.         } elseif ($element instanceof DataObject\Concrete) {
  794.             DataObject\Service::loadAllObjectFields($element);
  795.         } elseif ($element instanceof Asset) {
  796.             Asset\Service::loadAllFields($element);
  797.         }
  798.         return $element;
  799.     }
  800.     /** Callback for array_filter function.
  801.      * @param string $var value
  802.      *
  803.      * @return bool true if value is accepted
  804.      */
  805.     private static function filterNullValues($var)
  806.     {
  807.         return strlen($var) > 0;
  808.     }
  809.     /**
  810.      * @param string $path
  811.      * @param array $options
  812.      *
  813.      * @return Asset\Folder|Document\Folder|DataObject\Folder
  814.      *
  815.      * @throws \Exception
  816.      */
  817.     public static function createFolderByPath($path$options = [])
  818.     {
  819.         $calledClass = static::class;
  820.         if ($calledClass === __CLASS__) {
  821.             throw new \Exception('This method must be called from a extended class. e.g Asset\\Service, DataObject\\Service, Document\\Service');
  822.         }
  823.         $type str_replace('\Service'''$calledClass);
  824.         $type '\\' ltrim($type'\\');
  825.         $folderType $type '\Folder';
  826.         $lastFolder null;
  827.         $pathsArray = [];
  828.         $parts explode('/'$path);
  829.         $parts array_filter($parts'\\Pimcore\\Model\\Element\\Service::filterNullValues');
  830.         $sanitizedPath '/';
  831.         $itemType self::getElementType(new $type);
  832.         foreach ($parts as $part) {
  833.             $sanitizedPath $sanitizedPath self::getValidKey($part$itemType) . '/';
  834.         }
  835.         if (self::pathExists($sanitizedPath$itemType)) {
  836.             return $type::getByPath($sanitizedPath);
  837.         }
  838.         foreach ($parts as $part) {
  839.             $pathPart $pathsArray[count($pathsArray) - 1] ?? '';
  840.             $pathsArray[] = $pathPart '/' self::getValidKey($part$itemType);
  841.         }
  842.         for ($i 0$i count($pathsArray); $i++) {
  843.             $currentPath $pathsArray[$i];
  844.             if (!self::pathExists($currentPath$itemType)) {
  845.                 $parentFolderPath = ($i == 0) ? '/' $pathsArray[$i 1];
  846.                 $parentFolder $type::getByPath($parentFolderPath);
  847.                 $folder = new $folderType();
  848.                 $folder->setParent($parentFolder);
  849.                 if ($parentFolder) {
  850.                     $folder->setParentId($parentFolder->getId());
  851.                 } else {
  852.                     $folder->setParentId(1);
  853.                 }
  854.                 $key substr($currentPathstrrpos($currentPath'/') + 1strlen($currentPath));
  855.                 if (method_exists($folder'setKey')) {
  856.                     $folder->setKey($key);
  857.                 }
  858.                 if (method_exists($folder'setFilename')) {
  859.                     $folder->setFilename($key);
  860.                 }
  861.                 if (method_exists($folder'setType')) {
  862.                     $folder->setType('folder');
  863.                 }
  864.                 $folder->setPath($currentPath);
  865.                 $folder->setUserModification(0);
  866.                 $folder->setUserOwner(1);
  867.                 $folder->setCreationDate(time());
  868.                 $folder->setModificationDate(time());
  869.                 $folder->setValues($options);
  870.                 $folder->save();
  871.                 $lastFolder $folder;
  872.             }
  873.         }
  874.         return $lastFolder;
  875.     }
  876.     /**
  877.      * Changes the query according to the custom view config
  878.      *
  879.      * @internal
  880.      *
  881.      * @param array $cv
  882.      * @param Model\Asset\Listing|Model\DataObject\Listing|Model\Document\Listing $childsList
  883.      */
  884.     public static function addTreeFilterJoins($cv$childsList)
  885.     {
  886.         if ($cv) {
  887.             $childsList->onCreateQueryBuilder(static function (DoctrineQueryBuilder $select) use ($cv) {
  888.                 $where $cv['where'] ?? null;
  889.                 if ($where) {
  890.                     $select->andWhere($where);
  891.                 }
  892.                 $fromAlias $select->getQueryPart('from')[0]['alias'] ?? $select->getQueryPart('from')[0]['table'] ;
  893.                 $customViewJoins $cv['joins'] ?? null;
  894.                 if ($customViewJoins) {
  895.                     foreach ($customViewJoins as $joinConfig) {
  896.                         $type $joinConfig['type'];
  897.                         $method $type == 'left' || $type == 'right' $method $type 'Join' 'join';
  898.                         $joinAlias array_keys($joinConfig['name']);
  899.                         $joinAlias reset($joinAlias);
  900.                         $joinTable $joinConfig['name'][$joinAlias];
  901.                         $condition $joinConfig['condition'];
  902.                         $columns $joinConfig['columns'];
  903.                         $select->addSelect($columns);
  904.                         $select->$method($fromAlias$joinTable$joinAlias$condition);
  905.                     }
  906.                 }
  907.                 if (!empty($cv['having'])) {
  908.                     $select->having($cv['having']);
  909.                 }
  910.             });
  911.         }
  912.     }
  913.     /**
  914.      * @internal
  915.      *
  916.      * @param string $id
  917.      *
  918.      * @return array|null
  919.      */
  920.     public static function getCustomViewById($id)
  921.     {
  922.         $customViews \Pimcore\CustomView\Config::get();
  923.         if ($customViews) {
  924.             foreach ($customViews as $customView) {
  925.                 if ($customView['id'] == $id) {
  926.                     return $customView;
  927.                 }
  928.             }
  929.         }
  930.         return null;
  931.     }
  932.     /**
  933.      * @param string $key
  934.      * @param string $type
  935.      *
  936.      * @return string
  937.      */
  938.     public static function getValidKey($key$type)
  939.     {
  940.         $event = new GenericEvent(null, [
  941.             'key' => $key,
  942.             'type' => $type,
  943.         ]);
  944.         \Pimcore::getEventDispatcher()->dispatch($eventSystemEvents::SERVICE_PRE_GET_VALID_KEY);
  945.         $key $event->getArgument('key');
  946.         $key trim($key);
  947.         // replace all 4 byte unicode characters
  948.         $key preg_replace('/[\x{10000}-\x{10FFFF}]/u''-'$key);
  949.         // replace slashes with a hyphen
  950.         $key str_replace('/''-'$key);
  951.         if ($type === 'object') {
  952.             $key preg_replace('/[<>]/''-'$key);
  953.         } elseif ($type === 'document') {
  954.             // replace URL reserved characters with a hyphen
  955.             $key preg_replace('/[#\?\*\:\\\\<\>\|"%&@=;\+]/''-'$key);
  956.         } elseif ($type === 'asset') {
  957.             // keys shouldn't start with a "." (=hidden file) *nix operating systems
  958.             // keys shouldn't end with a "." - Windows issue: filesystem API trims automatically . at the end of a folder name (no warning ... et al)
  959.             $key trim($key'. ');
  960.             // windows forbidden filenames + URL reserved characters (at least the ones which are problematic)
  961.             $key preg_replace('/[#\?\*\:\\\\<\>\|"%\+]/''-'$key);
  962.         } else {
  963.             $key ltrim($key'. ');
  964.         }
  965.         $key mb_substr($key0255);
  966.         return $key;
  967.     }
  968.     /**
  969.      * @param string $key
  970.      * @param string $type
  971.      *
  972.      * @return bool
  973.      */
  974.     public static function isValidKey($key$type)
  975.     {
  976.         return self::getValidKey($key$type) == $key;
  977.     }
  978.     /**
  979.      * @param string $path
  980.      * @param string $type
  981.      *
  982.      * @return bool
  983.      */
  984.     public static function isValidPath($path$type)
  985.     {
  986.         $parts explode('/'$path);
  987.         foreach ($parts as $part) {
  988.             if (!self::isValidKey($part$type)) {
  989.                 return false;
  990.             }
  991.         }
  992.         return true;
  993.     }
  994.     /**
  995.      * returns a unique key for an element
  996.      *
  997.      * @param ElementInterface $element
  998.      *
  999.      * @return string|null
  1000.      */
  1001.     public static function getUniqueKey($element)
  1002.     {
  1003.         if ($element instanceof DataObject\AbstractObject) {
  1004.             return DataObject\Service::getUniqueKey($element);
  1005.         }
  1006.         if ($element instanceof Document) {
  1007.             return Document\Service::getUniqueKey($element);
  1008.         }
  1009.         if ($element instanceof Asset) {
  1010.             return Asset\Service::getUniqueKey($element);
  1011.         }
  1012.         return null;
  1013.     }
  1014.     /**
  1015.      * @internal
  1016.      *
  1017.      * @param array $data
  1018.      * @param string $type
  1019.      *
  1020.      * @return array
  1021.      */
  1022.     public static function fixAllowedTypes($data$type)
  1023.     {
  1024.         // this is the new method with Ext.form.MultiSelect
  1025.         if (is_array($data) && count($data)) {
  1026.             $first reset($data);
  1027.             if (!is_array($first)) {
  1028.                 $parts $data;
  1029.                 $data = [];
  1030.                 foreach ($parts as $elementType) {
  1031.                     $data[] = [$type => $elementType];
  1032.                 }
  1033.             } else {
  1034.                 $newList = [];
  1035.                 foreach ($data as $key => $item) {
  1036.                     if ($item) {
  1037.                         if (is_array($item)) {
  1038.                             foreach ($item as $itemKey => $itemValue) {
  1039.                                 if ($itemValue) {
  1040.                                     $newList[$key][$itemKey] = $itemValue;
  1041.                                 }
  1042.                             }
  1043.                         } else {
  1044.                             $newList[$key] = $item;
  1045.                         }
  1046.                     }
  1047.                 }
  1048.                 $data $newList;
  1049.             }
  1050.         }
  1051.         return $data $data : [];
  1052.     }
  1053.     /**
  1054.      * @internal
  1055.      *
  1056.      * @param Model\Version[] $versions
  1057.      *
  1058.      * @return array
  1059.      */
  1060.     public static function getSafeVersionInfo($versions)
  1061.     {
  1062.         $indexMap = [];
  1063.         $result = [];
  1064.         if (is_array($versions)) {
  1065.             foreach ($versions as $versionObj) {
  1066.                 $version = [
  1067.                     'id' => $versionObj->getId(),
  1068.                     'cid' => $versionObj->getCid(),
  1069.                     'ctype' => $versionObj->getCtype(),
  1070.                     'note' => $versionObj->getNote(),
  1071.                     'date' => $versionObj->getDate(),
  1072.                     'public' => $versionObj->getPublic(),
  1073.                     'versionCount' => $versionObj->getVersionCount(),
  1074.                     'autoSave' => $versionObj->isAutoSave(),
  1075.                 ];
  1076.                 $version['user'] = ['name' => '''id' => ''];
  1077.                 if ($user $versionObj->getUser()) {
  1078.                     $version['user'] = [
  1079.                         'name' => $user->getName(),
  1080.                         'id' => $user->getId(),
  1081.                     ];
  1082.                 }
  1083.                 $versionKey $versionObj->getDate() . '-' $versionObj->getVersionCount();
  1084.                 if (!isset($indexMap[$versionKey])) {
  1085.                     $indexMap[$versionKey] = 0;
  1086.                 }
  1087.                 $version['index'] = $indexMap[$versionKey];
  1088.                 $indexMap[$versionKey] = $indexMap[$versionKey] + 1;
  1089.                 $result[] = $version;
  1090.             }
  1091.         }
  1092.         return $result;
  1093.     }
  1094.     /**
  1095.      * @param ElementInterface $element
  1096.      *
  1097.      * @return ElementInterface
  1098.      */
  1099.     public static function cloneMe(ElementInterface $element)
  1100.     {
  1101.         $deepCopy = new \DeepCopy\DeepCopy();
  1102.         $deepCopy->addFilter(new \DeepCopy\Filter\KeepFilter(), new class() implements \DeepCopy\Matcher\Matcher {
  1103.             /**
  1104.              * {@inheritdoc}
  1105.              */
  1106.             public function matches($object$property)
  1107.             {
  1108.                 try {
  1109.                     $reflectionProperty = new \ReflectionProperty($object$property);
  1110.                     $reflectionProperty->setAccessible(true);
  1111.                     $myValue $reflectionProperty->getValue($object);
  1112.                 } catch (\Throwable $e) {
  1113.                     return false;
  1114.                 }
  1115.                 return $myValue instanceof ElementInterface;
  1116.             }
  1117.         });
  1118.         if ($element instanceof Concrete) {
  1119.             $deepCopy->addFilter(
  1120.                 new PimcoreClassDefinitionReplaceFilter(
  1121.                     function (Concrete $objectData $fieldDefinition$property$currentValue) {
  1122.                         if ($fieldDefinition instanceof Data\CustomDataCopyInterface) {
  1123.                             return $fieldDefinition->createDataCopy($object$currentValue);
  1124.                         }
  1125.                         return $currentValue;
  1126.                     }
  1127.                 ),
  1128.                 new PimcoreClassDefinitionMatcher(Data\CustomDataCopyInterface::class)
  1129.             );
  1130.         }
  1131.         $deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('dao'));
  1132.         $deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('resource'));
  1133.         $deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('writeResource'));
  1134.         $deepCopy->addFilter(new \DeepCopy\Filter\Doctrine\DoctrineCollectionFilter(), new \DeepCopy\Matcher\PropertyTypeMatcher(
  1135.             Collection::class
  1136.         ));
  1137.         if ($element instanceof DataObject\Concrete) {
  1138.             DataObject\Service::loadAllObjectFields($element);
  1139.         }
  1140.         $theCopy $deepCopy->copy($element);
  1141.         $theCopy->setId(null);
  1142.         $theCopy->setParent(null);
  1143.         return $theCopy;
  1144.     }
  1145.     /**
  1146.      * @template T
  1147.      *
  1148.      * @param T $properties
  1149.      *
  1150.      * @return T
  1151.      */
  1152.     public static function cloneProperties(mixed $properties): mixed
  1153.     {
  1154.         $deepCopy = new \DeepCopy\DeepCopy();
  1155.         $deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('cid'));
  1156.         $deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('ctype'));
  1157.         $deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('cpath'));
  1158.         return $deepCopy->copy($properties);
  1159.     }
  1160.     /**
  1161.      * @internal
  1162.      *
  1163.      * @param Note $note
  1164.      *
  1165.      * @return array
  1166.      */
  1167.     public static function getNoteData(Note $note)
  1168.     {
  1169.         $cpath '';
  1170.         if ($note->getCid() && $note->getCtype()) {
  1171.             if ($element Service::getElementById($note->getCtype(), $note->getCid())) {
  1172.                 $cpath $element->getRealFullPath();
  1173.             }
  1174.         }
  1175.         $e = [
  1176.             'id' => $note->getId(),
  1177.             'type' => $note->getType(),
  1178.             'cid' => $note->getCid(),
  1179.             'ctype' => $note->getCtype(),
  1180.             'cpath' => $cpath,
  1181.             'date' => $note->getDate(),
  1182.             'title' => Pimcore::getContainer()->get(TranslatorInterface::class)->trans($note->getTitle(), [], 'admin'),
  1183.             'description' => $note->getDescription(),
  1184.         ];
  1185.         // prepare key-values
  1186.         $keyValues = [];
  1187.         if (is_array($note->getData())) {
  1188.             foreach ($note->getData() as $name => $d) {
  1189.                 $type $d['type'];
  1190.                 $data $d['data'];
  1191.                 if ($type == 'document' || $type == 'object' || $type == 'asset') {
  1192.                     if ($d['data'] instanceof ElementInterface) {
  1193.                         $data = [
  1194.                             'id' => $d['data']->getId(),
  1195.                             'path' => $d['data']->getRealFullPath(),
  1196.                             'type' => $d['data']->getType(),
  1197.                         ];
  1198.                     }
  1199.                 } elseif ($type == 'date') {
  1200.                     if (is_object($d['data'])) {
  1201.                         $data $d['data']->getTimestamp();
  1202.                     }
  1203.                 }
  1204.                 $keyValue = [
  1205.                     'type' => $type,
  1206.                     'name' => $name,
  1207.                     'data' => $data,
  1208.                 ];
  1209.                 $keyValues[] = $keyValue;
  1210.             }
  1211.         }
  1212.         $e['data'] = $keyValues;
  1213.         // prepare user data
  1214.         if ($note->getUser()) {
  1215.             $user Model\User::getById($note->getUser());
  1216.             if ($user) {
  1217.                 $e['user'] = [
  1218.                     'id' => $user->getId(),
  1219.                     'name' => $user->getName(),
  1220.                 ];
  1221.             } else {
  1222.                 $e['user'] = '';
  1223.             }
  1224.         }
  1225.         return $e;
  1226.     }
  1227.     /**
  1228.      * @internal
  1229.      *
  1230.      * @param string $type
  1231.      * @param int $elementId
  1232.      * @param null|string $postfix
  1233.      *
  1234.      * @return string
  1235.      */
  1236.     public static function getSessionKey($type$elementId$postfix '')
  1237.     {
  1238.         $sessionId Session::getSessionId();
  1239.         $tmpStoreKey $type '_session_' $elementId '_' $sessionId $postfix;
  1240.         return $tmpStoreKey;
  1241.     }
  1242.     /**
  1243.      *
  1244.      * @param string $type
  1245.      * @param int $elementId
  1246.      * @param null|string $postfix
  1247.      *
  1248.      * @return AbstractObject|Document|Asset|null
  1249.      */
  1250.     public static function getElementFromSession($type$elementId$postfix '')
  1251.     {
  1252.         $element null;
  1253.         $tmpStoreKey self::getSessionKey($type$elementId$postfix);
  1254.         $tmpStore TmpStore::get($tmpStoreKey);
  1255.         if ($tmpStore) {
  1256.             $data $tmpStore->getData();
  1257.             if ($data) {
  1258.                 $element Serialize::unserialize($data);
  1259.                 $context = [
  1260.                     'source' => __METHOD__,
  1261.                     'conversion' => 'unmarshal',
  1262.                 ];
  1263.                 $copier Self::getDeepCopyInstance($element$context);
  1264.                 if ($element instanceof Concrete) {
  1265.                     $copier->addFilter(
  1266.                         new PimcoreClassDefinitionReplaceFilter(
  1267.                             function (Concrete $objectData $fieldDefinition$property$currentValue) {
  1268.                                 if ($fieldDefinition instanceof Data\CustomVersionMarshalInterface) {
  1269.                                     return $fieldDefinition->unmarshalVersion($object$currentValue);
  1270.                                 }
  1271.                                 return $currentValue;
  1272.                             }
  1273.                         ),
  1274.                         new PimcoreClassDefinitionMatcher(Data\CustomVersionMarshalInterface::class)
  1275.                     );
  1276.                 }
  1277.                 return $copier->copy($element);
  1278.             }
  1279.         }
  1280.         return $element;
  1281.     }
  1282.     /**
  1283.      * @internal
  1284.      *
  1285.      * @param ElementInterface $element
  1286.      * @param string $postfix
  1287.      * @param bool $clone save a copy
  1288.      */
  1289.     public static function saveElementToSession($element$postfix ''$clone true)
  1290.     {
  1291.         if ($clone) {
  1292.             $context = [
  1293.                 'source' => __METHOD__,
  1294.                 'conversion' => 'marshal',
  1295.             ];
  1296.             $copier self::getDeepCopyInstance($element$context);
  1297.             if ($element instanceof Concrete) {
  1298.                 $copier->addFilter(
  1299.                     new PimcoreClassDefinitionReplaceFilter(
  1300.                         function (Concrete $objectData $fieldDefinition$property$currentValue) {
  1301.                             if ($fieldDefinition instanceof Data\CustomVersionMarshalInterface) {
  1302.                                 return $fieldDefinition->marshalVersion($object$currentValue);
  1303.                             }
  1304.                             return $currentValue;
  1305.                         }
  1306.                     ),
  1307.                     new PimcoreClassDefinitionMatcher(Data\CustomVersionMarshalInterface::class)
  1308.                 );
  1309.             }
  1310.             $element $copier->copy($element);
  1311.         }
  1312.         $elementType Service::getElementType($element);
  1313.         $tmpStoreKey self::getSessionKey($elementType$element->getId(), $postfix);
  1314.         $tag $elementType '-session' $postfix;
  1315.         if ($element instanceof ElementDumpStateInterface) {
  1316.             self::loadAllFields($element);
  1317.             $element->setInDumpState(true);
  1318.         }
  1319.         $serializedData Serialize::serialize($element);
  1320.         TmpStore::set($tmpStoreKey$serializedData$tag);
  1321.     }
  1322.     /**
  1323.      * @internal
  1324.      *
  1325.      * @param string $type
  1326.      * @param int $elementId
  1327.      * @param string $postfix
  1328.      */
  1329.     public static function removeElementFromSession($type$elementId$postfix '')
  1330.     {
  1331.         $tmpStoreKey self::getSessionKey($type$elementId$postfix);
  1332.         TmpStore::delete($tmpStoreKey);
  1333.     }
  1334.     /**
  1335.      * @internal
  1336.      *
  1337.      * @param mixed|null $element
  1338.      * @param array|null $context
  1339.      *
  1340.      * @return DeepCopy
  1341.      */
  1342.     public static function getDeepCopyInstance($element, ?array $context = []): DeepCopy
  1343.     {
  1344.         $copier = new DeepCopy();
  1345.         $copier->skipUncloneable(true);
  1346.         if ($element instanceof ElementInterface) {
  1347.             if (($context['conversion'] ?? false) === 'marshal') {
  1348.                 $sourceType Service::getElementType($element);
  1349.                 $sourceId $element->getId();
  1350.                 $copier->addTypeFilter(
  1351.                     new \DeepCopy\TypeFilter\ReplaceFilter(
  1352.                         function ($currentValue) {
  1353.                             if ($currentValue instanceof ElementInterface) {
  1354.                                 $elementType Service::getElementType($currentValue);
  1355.                                 $descriptor = new ElementDescriptor($elementType$currentValue->getId());
  1356.                                 return $descriptor;
  1357.                             }
  1358.                             return $currentValue;
  1359.                         }
  1360.                     ),
  1361.                     new MarshalMatcher($sourceType$sourceId)
  1362.                 );
  1363.             } elseif (($context['conversion'] ?? false) === 'unmarshal') {
  1364.                 $copier->addTypeFilter(
  1365.                     new \DeepCopy\TypeFilter\ReplaceFilter(
  1366.                         function ($currentValue) {
  1367.                             if ($currentValue instanceof ElementDescriptor) {
  1368.                                 $value Service::getElementById($currentValue->getType(), $currentValue->getId());
  1369.                                 return $value;
  1370.                             }
  1371.                             return $currentValue;
  1372.                         }
  1373.                     ),
  1374.                     new UnmarshalMatcher()
  1375.                 );
  1376.             }
  1377.         }
  1378.         if ($context['defaultFilters'] ?? false) {
  1379.             $copier->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
  1380.             $copier->addFilter(new SetNullFilter(), new PropertyTypeMatcher('Psr\Container\ContainerInterface'));
  1381.             $copier->addFilter(new SetNullFilter(), new PropertyTypeMatcher('Pimcore\Model\DataObject\ClassDefinition'));
  1382.         }
  1383.         $event = new GenericEvent(null, [
  1384.             'copier' => $copier,
  1385.             'element' => $element,
  1386.             'context' => $context,
  1387.         ]);
  1388.         \Pimcore::getEventDispatcher()->dispatch($eventSystemEvents::SERVICE_PRE_GET_DEEP_COPY);
  1389.         return $event->getArgument('copier');
  1390.     }
  1391.     /**
  1392.      * @internal
  1393.      *
  1394.      * @param array $rowData
  1395.      *
  1396.      * @return array
  1397.      */
  1398.     public static function escapeCsvRecord(array $rowData): array
  1399.     {
  1400.         if (self::$formatter === null) {
  1401.             self::$formatter = new EscapeFormula("'", ['=''-''+''@']);
  1402.         }
  1403.         $rowData self::$formatter->escapeRecord($rowData);
  1404.         return $rowData;
  1405.     }
  1406.     /**
  1407.      * @internal
  1408.      *
  1409.      * @param string $type
  1410.      * @param int|string $id
  1411.      *
  1412.      * @return string
  1413.      */
  1414.     public static function getElementCacheTag(string $type$id): string
  1415.     {
  1416.         return $type '_' $id;
  1417.     }
  1418. }