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

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