vendor/pimcore/pimcore/models/DataObject/Service.php line 850

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\DataObject;
  15. use DeepCopy\Filter\SetNullFilter;
  16. use DeepCopy\Matcher\PropertyNameMatcher;
  17. use Pimcore\Cache\Runtime;
  18. use Pimcore\DataObject\GridColumnConfig\ConfigElementInterface;
  19. use Pimcore\DataObject\GridColumnConfig\Operator\AbstractOperator;
  20. use Pimcore\DataObject\GridColumnConfig\Service as GridColumnConfigService;
  21. use Pimcore\Db;
  22. use Pimcore\Event\DataObjectEvents;
  23. use Pimcore\Event\Model\DataObjectEvent;
  24. use Pimcore\Localization\LocaleServiceInterface;
  25. use Pimcore\Logger;
  26. use Pimcore\Model;
  27. use Pimcore\Model\DataObject;
  28. use Pimcore\Model\DataObject\ClassDefinition\Data\IdRewriterInterface;
  29. use Pimcore\Model\DataObject\ClassDefinition\Data\LayoutDefinitionEnrichmentInterface;
  30. use Pimcore\Model\Element;
  31. use Pimcore\Model\Element\DirtyIndicatorInterface;
  32. use Pimcore\Tool;
  33. use Pimcore\Tool\Admin as AdminTool;
  34. use Pimcore\Tool\Session;
  35. use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
  36. use Symfony\Component\ExpressionLanguage\SyntaxError;
  37. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
  38. /**
  39.  * @method \Pimcore\Model\Element\Dao getDao()
  40.  */
  41. class Service extends Model\Element\Service
  42. {
  43.     /**
  44.      * @var array
  45.      */
  46.     protected $_copyRecursiveIds;
  47.     /**
  48.      * @var Model\User|null
  49.      */
  50.     protected $_user;
  51.     /**
  52.      * System fields used by filter conditions
  53.      *
  54.      * @var array
  55.      */
  56.     protected static $systemFields = ['o_path''o_key''o_id''o_published''o_creationDate''o_modificationDate''o_fullpath'];
  57.     /**
  58.      * @param Model\User $user
  59.      */
  60.     public function __construct($user null)
  61.     {
  62.         $this->_user $user;
  63.     }
  64.     /**
  65.      * finds all objects which hold a reference to a specific user
  66.      *
  67.      * @static
  68.      *
  69.      * @param  int $userId
  70.      *
  71.      * @return Concrete[]
  72.      */
  73.     public static function getObjectsReferencingUser($userId)
  74.     {
  75.         $userObjects = [[]];
  76.         $classesList = new ClassDefinition\Listing();
  77.         $classesList->setOrderKey('name');
  78.         $classesList->setOrder('asc');
  79.         $classesToCheck = [];
  80.         foreach ($classesList as $class) {
  81.             $fieldDefinitions $class->getFieldDefinitions();
  82.             $dataKeys = [];
  83.             if (is_array($fieldDefinitions)) {
  84.                 foreach ($fieldDefinitions as $tag) {
  85.                     if ($tag instanceof ClassDefinition\Data\User) {
  86.                         $dataKeys[] = $tag->getName();
  87.                     }
  88.                 }
  89.             }
  90.             if (is_array($dataKeys) && count($dataKeys) > 0) {
  91.                 $classesToCheck[$class->getName()] = $dataKeys;
  92.             }
  93.         }
  94.         foreach ($classesToCheck as $classname => $fields) {
  95.             $listName '\\Pimcore\\Model\\DataObject\\' ucfirst($classname) . '\\Listing';
  96.             $list = new $listName();
  97.             $conditionParts = [];
  98.             foreach ($fields as $field) {
  99.                 $conditionParts[] = $field ' = ?';
  100.             }
  101.             $list->setCondition(implode(' AND '$conditionParts), array_fill(0count($conditionParts), $userId));
  102.             $objects $list->load();
  103.             $userObjects[] = $objects;
  104.         }
  105.         return \array_merge(...$userObjects);
  106.     }
  107.     /**
  108.      * @param AbstractObject $target
  109.      * @param AbstractObject $source
  110.      *
  111.      * @return AbstractObject|void
  112.      */
  113.     public function copyRecursive($target$source)
  114.     {
  115.         // avoid recursion
  116.         if (!$this->_copyRecursiveIds) {
  117.             $this->_copyRecursiveIds = [];
  118.         }
  119.         if (in_array($source->getId(), $this->_copyRecursiveIds)) {
  120.             return;
  121.         }
  122.         $source->getProperties();
  123.         //load all in case of lazy loading fields
  124.         self::loadAllObjectFields($source);
  125.         // triggers actions before object cloning
  126.         $event = new DataObjectEvent($source, [
  127.             'target_element' => $target,
  128.         ]);
  129.         \Pimcore::getEventDispatcher()->dispatch($eventDataObjectEvents::PRE_COPY);
  130.         $target $event->getArgument('target_element');
  131.         $new $this->copy($source$target);
  132.         // add to store
  133.         $this->_copyRecursiveIds[] = $new->getId();
  134.         $children $source->getChildren([
  135.             DataObject::OBJECT_TYPE_OBJECT,
  136.             DataObject::OBJECT_TYPE_VARIANT,
  137.             DataObject::OBJECT_TYPE_FOLDER,
  138.         ], true);
  139.         foreach ($children as $child) {
  140.             $this->copyRecursive($new$child);
  141.         }
  142.         $this->updateChildren($target$new);
  143.         // triggers actions after the complete document cloning
  144.         $event = new DataObjectEvent($new, [
  145.             'base_element' => $source// the element used to make a copy
  146.         ]);
  147.         \Pimcore::getEventDispatcher()->dispatch($eventDataObjectEvents::POST_COPY);
  148.         return $new;
  149.     }
  150.     /**
  151.      * @param  AbstractObject $target
  152.      * @param  AbstractObject $source
  153.      *
  154.      * @return AbstractObject copied object
  155.      */
  156.     public function copyAsChild($target$source)
  157.     {
  158.         $isDirtyDetectionDisabled DataObject::isDirtyDetectionDisabled();
  159.         DataObject::setDisableDirtyDetection(true);
  160.         //load properties
  161.         $source->getProperties();
  162.         //load all in case of lazy loading fields
  163.         self::loadAllObjectFields($source);
  164.         // triggers actions before object cloning
  165.         $event = new DataObjectEvent($source, [
  166.             'target_element' => $target,
  167.         ]);
  168.         \Pimcore::getEventDispatcher()->dispatch($eventDataObjectEvents::PRE_COPY);
  169.         $target $event->getArgument('target_element');
  170.         $new $this->copy($source$target);
  171.         DataObject::setDisableDirtyDetection($isDirtyDetectionDisabled);
  172.         $this->updateChildren($target$new);
  173.         // triggers actions after the complete object cloning
  174.         $event = new DataObjectEvent($new, [
  175.             'base_element' => $source// the element used to make a copy
  176.         ]);
  177.         \Pimcore::getEventDispatcher()->dispatch($eventDataObjectEvents::POST_COPY);
  178.         return $new;
  179.     }
  180.     private function copy(AbstractObject $sourceAbstractObject $target): AbstractObject
  181.     {
  182.         /** @var AbstractObject $new */
  183.         $new Element\Service::cloneMe($source);
  184.         $new->setId(null);
  185.         $new->setChildren(null);
  186.         $new->setKey(Element\Service::getSafeCopyName($new->getKey(), $target));
  187.         $new->setParentId($target->getId());
  188.         $new->setUserOwner($this->_user $this->_user->getId() : 0);
  189.         $new->setUserModification($this->_user $this->_user->getId() : 0);
  190.         $new->setDao(null);
  191.         $new->setLocked(null);
  192.         $new->setCreationDate(time());
  193.         if ($new instanceof Concrete) {
  194.             foreach ($new->getClass()->getFieldDefinitions() as $fieldDefinition) {
  195.                 if ($fieldDefinition instanceof DataObject\ClassDefinition\Data\Localizedfields) {
  196.                     foreach ($fieldDefinition->getFieldDefinitions() as $localizedFieldDefinition) {
  197.                         if ($localizedFieldDefinition->getUnique()) {
  198.                             foreach (Tool::getValidLanguages() as $language) {
  199.                                 $new->set($localizedFieldDefinition->getName(), null$language);
  200.                             }
  201.                             $new->setPublished(false);
  202.                         }
  203.                     }
  204.                 } elseif ($fieldDefinition->getUnique()) {
  205.                     $new->set($fieldDefinition->getName(), null);
  206.                     $new->setPublished(false);
  207.                 }
  208.             }
  209.         }
  210.         $new->save();
  211.         return $new;
  212.     }
  213.     /**
  214.      * @param Concrete $target
  215.      * @param Concrete $source
  216.      *
  217.      * @return Concrete
  218.      *
  219.      * @throws \Exception
  220.      */
  221.     public function copyContents($target$source)
  222.     {
  223.         // check if the type is the same
  224.         if (get_class($source) !== get_class($target)) {
  225.             throw new \Exception('Source and target have to be the same type');
  226.         }
  227.         //load all in case of lazy loading fields
  228.         self::loadAllObjectFields($source);
  229.         /**
  230.          * @var Concrete $new
  231.          */
  232.         $new Element\Service::cloneMe($source);
  233.         $new->setChildren($target->getChildren());
  234.         $new->setId($target->getId());
  235.         $new->setPath($target->getRealPath());
  236.         $new->setKey($target->getKey());
  237.         $new->setParentId($target->getParentId());
  238.         $new->setScheduledTasks($source->getScheduledTasks());
  239.         $new->setProperties(self::cloneProperties($source->getProperties()));
  240.         $new->setUserModification($this->_user $this->_user->getId() : 0);
  241.         $new->save();
  242.         $target Concrete::getById($new->getId());
  243.         return $target;
  244.     }
  245.     /**
  246.      * @param string $field
  247.      *
  248.      * @return bool
  249.      *
  250.      * @internal
  251.      */
  252.     public static function isHelperGridColumnConfig($field)
  253.     {
  254.         return strpos($field'#') === 0;
  255.     }
  256.     /**
  257.      * Language only user for classification store !!!
  258.      *
  259.      * @param AbstractObject $object
  260.      * @param array|null $fields
  261.      * @param string|null $requestedLanguage
  262.      * @param array $params
  263.      *
  264.      * @return array
  265.      *
  266.      * @internal
  267.      */
  268.     public static function gridObjectData($object$fields null$requestedLanguage null$params = [])
  269.     {
  270.         $data Element\Service::gridElementData($object);
  271.         $csvMode $params['csvMode'] ?? false;
  272.         if ($object instanceof Concrete) {
  273.             $user AdminTool::getCurrentUser();
  274.             $context = ['object' => $object,
  275.                 'purpose' => 'gridview',
  276.                 'language' => $requestedLanguage, ];
  277.             $data['classname'] = $object->getClassName();
  278.             $data['idPath'] = Element\Service::getIdPath($object);
  279.             $data['inheritedFields'] = [];
  280.             $data['permissions'] = $object->getUserPermissions($user);
  281.             $data['locked'] = $object->isLocked();
  282.             if (is_null($fields)) {
  283.                 $fields array_keys($object->getclass()->getFieldDefinitions());
  284.             }
  285.             $haveHelperDefinition false;
  286.             foreach ($fields as $key) {
  287.                 $brickDescriptor null;
  288.                 $brickKey null;
  289.                 $brickType null;
  290.                 $brickGetter null;
  291.                 $dataKey $key;
  292.                 $keyParts explode('~'$key);
  293.                 $def $object->getClass()->getFieldDefinition($key$context);
  294.                 if (strpos($key'#') === 0) {
  295.                     if (!$haveHelperDefinition) {
  296.                         $helperDefinitions self::getHelperDefinitions();
  297.                         $haveHelperDefinition true;
  298.                     }
  299.                     if (!empty($helperDefinitions[$key])) {
  300.                         $context['fieldname'] = $key;
  301.                         $data[$key] = self::calculateCellValue($object$helperDefinitions$key$context);
  302.                     }
  303.                 } elseif (strpos($key'~') === 0) {
  304.                     $type $keyParts[1];
  305.                     if ($type === 'classificationstore') {
  306.                         $data[$key] = self::getStoreValueForObject($object$key$requestedLanguage);
  307.                     }
  308.                 } elseif (count($keyParts) > 1) {
  309.                     // brick
  310.                     $brickType $keyParts[0];
  311.                     if (strpos($brickType'?') !== false) {
  312.                         $brickDescriptor substr($brickType1);
  313.                         $brickDescriptor json_decode($brickDescriptortrue);
  314.                         $brickType $brickDescriptor['containerKey'];
  315.                     }
  316.                     $brickKey $keyParts[1];
  317.                     $key self::getFieldForBrickType($object->getclass(), $brickType);
  318.                     $brickClass Objectbrick\Definition::getByKey($brickType);
  319.                     $context['outerFieldname'] = $key;
  320.                     if ($brickDescriptor) {
  321.                         $innerContainer $brickDescriptor['innerContainer'] ?? 'localizedfields';
  322.                         /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $localizedFields */
  323.                         $localizedFields $brickClass->getFieldDefinition($innerContainer);
  324.                         $def $localizedFields->getFieldDefinition($brickDescriptor['brickfield']);
  325.                     } elseif ($brickClass instanceof Objectbrick\Definition) {
  326.                         $def $brickClass->getFieldDefinition($brickKey$context);
  327.                     }
  328.                 }
  329.                 if (!empty($key)) {
  330.                     // some of the not editable field require a special response
  331.                     $getter 'get' ucfirst($key);
  332.                     $needLocalizedPermissions false;
  333.                     // if the definition is not set try to get the definition from localized fields
  334.                     if (!$def) {
  335.                         /** @var Model\DataObject\ClassDefinition\Data\Localizedfields|null $locFields */
  336.                         $locFields $object->getClass()->getFieldDefinition('localizedfields');
  337.                         if ($locFields) {
  338.                             $def $locFields->getFieldDefinition($key$context);
  339.                             if ($def) {
  340.                                 $needLocalizedPermissions true;
  341.                             }
  342.                         }
  343.                     }
  344.                     //relation type fields with remote owner do not have a getter
  345.                     if (method_exists($object$getter)) {
  346.                         //system columns must not be inherited
  347.                         if (in_array($keyConcrete::SYSTEM_COLUMN_NAMES)) {
  348.                             $data[$dataKey] = $object->$getter();
  349.                         } else {
  350.                             $valueObject self::getValueForObject($object$key$brickType$brickKey$def$context$brickDescriptor);
  351.                             $data['inheritedFields'][$dataKey] = ['inherited' => $valueObject->objectid != $object->getId(), 'objectid' => $valueObject->objectid];
  352.                             if ($csvMode || method_exists($def'getDataForGrid')) {
  353.                                 if ($brickKey) {
  354.                                     $context['containerType'] = 'objectbrick';
  355.                                     $context['containerKey'] = $brickType;
  356.                                     $context['outerFieldname'] = $key;
  357.                                 }
  358.                                 $params array_merge($params, ['context' => $context]);
  359.                                 if (!isset($params['purpose'])) {
  360.                                     $params['purpose'] = 'gridview';
  361.                                 }
  362.                                 if ($csvMode) {
  363.                                     $getterParams = ['language' => $requestedLanguage];
  364.                                     $tempData $def->getForCsvExport($object$getterParams);
  365.                                 } elseif (method_exists($def'getDataForGrid')) {
  366.                                     $tempData $def->getDataForGrid($valueObject->value$object$params);
  367.                                 } else {
  368.                                     continue;
  369.                                 }
  370.                                 if ($def instanceof ClassDefinition\Data\Localizedfields) {
  371.                                     $needLocalizedPermissions true;
  372.                                     foreach ($tempData as $tempKey => $tempValue) {
  373.                                         $data[$tempKey] = $tempValue;
  374.                                     }
  375.                                 } else {
  376.                                     $data[$dataKey] = $tempData;
  377.                                     if ($def instanceof Model\DataObject\ClassDefinition\Data\Select && $def->getOptionsProviderClass()) {
  378.                                         $data[$dataKey '%options'] = $def->getOptions();
  379.                                     }
  380.                                 }
  381.                             } else {
  382.                                 $data[$dataKey] = $valueObject->value;
  383.                             }
  384.                         }
  385.                     }
  386.                     // because the key for the classification store has not a direct getter, you have to check separately if the data is inheritable
  387.                     if (strpos($key'~') === && empty($data[$key])) {
  388.                         $type $keyParts[1];
  389.                         if ($type === 'classificationstore') {
  390.                             $parent self::hasInheritableParentObject($object);
  391.                             if (!empty($parent)) {
  392.                                 $data[$dataKey] = self::getStoreValueForObject($parent$key$requestedLanguage);
  393.                                 $data['inheritedFields'][$dataKey] = ['inherited' => $parent->getId() != $object->getId(), 'objectid' => $parent->getId()];
  394.                             }
  395.                         }
  396.                     }
  397.                     if ($needLocalizedPermissions) {
  398.                         if (!$user->isAdmin()) {
  399.                             $locale \Pimcore::getContainer()->get(LocaleServiceInterface::class)->findLocale();
  400.                             $permissionTypes = ['View''Edit'];
  401.                             foreach ($permissionTypes as $permissionType) {
  402.                                 //TODO, this needs refactoring! Ideally, call it only once!
  403.                                 $languagesAllowed self::getLanguagePermissions($object$user'l' $permissionType);
  404.                                 if ($languagesAllowed) {
  405.                                     $languagesAllowed array_keys($languagesAllowed);
  406.                                     if (!in_array($locale$languagesAllowed)) {
  407.                                         $data['metadata']['permission'][$key]['no' $permissionType] = 1;
  408.                                         if ($permissionType === 'View') {
  409.                                             $data[$key] = null;
  410.                                         }
  411.                                     }
  412.                                 }
  413.                             }
  414.                         }
  415.                     }
  416.                 }
  417.             }
  418.         }
  419.         return $data;
  420.     }
  421.     /**
  422.      * @param array $helperDefinitions
  423.      * @param string $key
  424.      *
  425.      * @return string[]|null
  426.      *
  427.      * @internal
  428.      */
  429.     public static function expandGridColumnForExport($helperDefinitions$key)
  430.     {
  431.         $config self::getConfigForHelperDefinition($helperDefinitions$key);
  432.         if ($config instanceof AbstractOperator && $config->expandLocales()) {
  433.             return $config->getValidLanguages();
  434.         }
  435.         return null;
  436.     }
  437.     /**
  438.      * @param array $helperDefinitions
  439.      * @param string $key
  440.      * @param array $context
  441.      *
  442.      * @return mixed|null|ConfigElementInterface|ConfigElementInterface[]
  443.      *
  444.      * @internal
  445.      */
  446.     public static function getConfigForHelperDefinition($helperDefinitions$key$context = [])
  447.     {
  448.         $cacheKey 'gridcolumn_config_' $key;
  449.         if (isset($context['language'])) {
  450.             $cacheKey .= '_' $context['language'];
  451.         }
  452.         if (Runtime::isRegistered($cacheKey)) {
  453.             $config Runtime::get($cacheKey);
  454.         } else {
  455.             $definition $helperDefinitions[$key];
  456.             $attributes json_decode(json_encode($definition->attributes));
  457.             // TODO refactor how the service is accessed into something non-static and inject the service there
  458.             $service \Pimcore::getContainer()->get(GridColumnConfigService::class);
  459.             $config $service->buildOutputDataConfig([$attributes], $context);
  460.             if (!$config) {
  461.                 return null;
  462.             }
  463.             $config $config[0];
  464.             Runtime::save($config$cacheKey);
  465.         }
  466.         return $config;
  467.     }
  468.     /**
  469.      * @param AbstractObject $object
  470.      * @param array $helperDefinitions
  471.      * @param string $key
  472.      * @param array $context
  473.      *
  474.      * @return \stdClass|array|null
  475.      */
  476.     public static function calculateCellValue($object$helperDefinitions$key$context = [])
  477.     {
  478.         $config = static::getConfigForHelperDefinition($helperDefinitions$key$context);
  479.         if (!$config) {
  480.             return null;
  481.         }
  482.         $inheritanceEnabled AbstractObject::getGetInheritedValues();
  483.         AbstractObject::setGetInheritedValues(true);
  484.         $result $config->getLabeledValue($object);
  485.         if (isset($result->value)) {
  486.             $result $result->value;
  487.             if (!empty($config->renderer)) {
  488.                 $classname 'Pimcore\\Model\\DataObject\\ClassDefinition\\Data\\' ucfirst($config->renderer);
  489.                 /** @var Model\DataObject\ClassDefinition\Data $rendererImpl */
  490.                 $rendererImpl = new $classname();
  491.                 if (method_exists($rendererImpl'getDataForGrid')) {
  492.                     $result $rendererImpl->getDataForGrid($result$object, []);
  493.                 }
  494.             }
  495.             return $result;
  496.         }
  497.         AbstractObject::setGetInheritedValues($inheritanceEnabled);
  498.         return null;
  499.     }
  500.     /**
  501.      * @return mixed
  502.      */
  503.     public static function getHelperDefinitions()
  504.     {
  505.         return Session::useSession(function (AttributeBagInterface $session) {
  506.             $existingColumns $session->get('helpercolumns', []);
  507.             return $existingColumns;
  508.         }, 'pimcore_gridconfig');
  509.     }
  510.     /**
  511.      * @param AbstractObject|Model\DataObject\Fieldcollection\Data\AbstractData|Model\DataObject\Objectbrick\Data\AbstractData $object
  512.      * @param Model\User $user
  513.      * @param string $type
  514.      *
  515.      * @return array|null
  516.      */
  517.     public static function getLanguagePermissions($object$user$type)
  518.     {
  519.         $languageAllowed null;
  520.         $object $object instanceof Model\DataObject\Fieldcollection\Data\AbstractData ||
  521.         $object instanceof  Model\DataObject\Objectbrick\Data\AbstractData ?
  522.             $object->getObject() : $object;
  523.         $permission $object->getPermissions($type$user);
  524.         if ($permission !== null) {
  525.             // backwards compatibility. If all entries are null, then the workspace rule was set up with
  526.             // an older pimcore
  527.             $permission $permission[$type];
  528.             if ($permission) {
  529.                 $permission explode(','$permission);
  530.                 if ($languageAllowed === null) {
  531.                     $languageAllowed = [];
  532.                 }
  533.                 foreach ($permission as $language) {
  534.                     $languageAllowed[$language] = 1;
  535.                 }
  536.             }
  537.         }
  538.         return $languageAllowed;
  539.     }
  540.     /**
  541.      * @param string $classId
  542.      * @param array $permissionSet
  543.      *
  544.      * @return array|null
  545.      */
  546.     public static function getLayoutPermissions($classId$permissionSet)
  547.     {
  548.         $layoutPermissions null;
  549.         if ($permissionSet !== null) {
  550.             // backwards compatibility. If all entries are null, then the workspace rule was set up with
  551.             // an older pimcore
  552.             $permission $permissionSet['layouts'];
  553.             if ($permission) {
  554.                 $permission explode(','$permission);
  555.                 if ($layoutPermissions === null) {
  556.                     $layoutPermissions = [];
  557.                 }
  558.                 foreach ($permission as $p) {
  559.                     if (preg_match(sprintf('#^(%s)_(.*)#'$classId), $p$setting)) {
  560.                         $l $setting[2];
  561.                         $layoutPermissions[$l] = $l;
  562.                     }
  563.                 }
  564.             }
  565.         }
  566.         return $layoutPermissions;
  567.     }
  568.     /**
  569.      * @param ClassDefinition $class
  570.      * @param string $bricktype
  571.      *
  572.      * @return int|null|string
  573.      */
  574.     public static function getFieldForBrickType(ClassDefinition $class$bricktype)
  575.     {
  576.         $fieldDefinitions $class->getFieldDefinitions();
  577.         foreach ($fieldDefinitions as $key => $fd) {
  578.             if ($fd instanceof ClassDefinition\Data\Objectbricks && in_array($bricktype$fd->getAllowedTypes())) {
  579.                 return $key;
  580.             }
  581.         }
  582.         return null;
  583.     }
  584.     /**
  585.      * gets value for given object and getter, including inherited values
  586.      *
  587.      * @static
  588.      *
  589.      * @param Concrete $object
  590.      * @param string $key
  591.      * @param string|null $brickType
  592.      * @param string|null $brickKey
  593.      * @param ClassDefinition\Data|null $fieldDefinition
  594.      * @param array $context
  595.      * @param array|null $brickDescriptor
  596.      *
  597.      * @return \stdClass, value and objectid where the value comes from
  598.      */
  599.     private static function getValueForObject($object$key$brickType null$brickKey null$fieldDefinition null$context = [], $brickDescriptor null)
  600.     {
  601.         $getter 'get' ucfirst($key);
  602.         $value $object->$getter();
  603.         if (!empty($value) && !empty($brickType)) {
  604.             $getBrickType 'get' ucfirst($brickType);
  605.             $value $value->$getBrickType();
  606.             if (!empty($value) && !empty($brickKey)) {
  607.                 if ($brickDescriptor) {
  608.                     $innerContainer $brickDescriptor['innerContainer'] ?? 'localizedfields';
  609.                     $localizedFields $value->{'get' ucfirst($innerContainer)}();
  610.                     $brickDefinition Model\DataObject\Objectbrick\Definition::getByKey($brickType);
  611.                     /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $fieldDefinitionLocalizedFields */
  612.                     $fieldDefinitionLocalizedFields $brickDefinition->getFieldDefinition('localizedfields');
  613.                     $fieldDefinition $fieldDefinitionLocalizedFields->getFieldDefinition($brickKey);
  614.                     $value $localizedFields->getLocalizedValue($brickDescriptor['brickfield']);
  615.                 } else {
  616.                     $brickFieldGetter 'get' ucfirst($brickKey);
  617.                     $value $value->$brickFieldGetter();
  618.                 }
  619.             }
  620.         }
  621.         if (!$fieldDefinition) {
  622.             $fieldDefinition $object->getClass()->getFieldDefinition($key$context);
  623.         }
  624.         if (!empty($brickType) && !empty($brickKey) && !$brickDescriptor) {
  625.             $brickClass Objectbrick\Definition::getByKey($brickType);
  626.             $context = ['object' => $object'outerFieldname' => $key];
  627.             $fieldDefinition $brickClass->getFieldDefinition($brickKey$context);
  628.         }
  629.         if ($fieldDefinition->isEmpty($value)) {
  630.             $parent self::hasInheritableParentObject($object);
  631.             if (!empty($parent)) {
  632.                 return self::getValueForObject($parent$key$brickType$brickKey$fieldDefinition$context$brickDescriptor);
  633.             }
  634.         }
  635.         $result = new \stdClass();
  636.         $result->value $value;
  637.         $result->objectid $object->getId();
  638.         return $result;
  639.     }
  640.     /**
  641.      * gets store value for given object and key
  642.      *
  643.      * @static
  644.      *
  645.      * @param Concrete $object
  646.      * @param string $key
  647.      * @param string|null $requestedLanguage
  648.      *
  649.      * @return string|null
  650.      */
  651.     private static function getStoreValueForObject($object$key$requestedLanguage)
  652.     {
  653.         $keyParts explode('~'$key);
  654.         if (strpos($key'~') === 0) {
  655.             $type $keyParts[1];
  656.             if ($type === 'classificationstore') {
  657.                 $field $keyParts[2];
  658.                 $groupKeyId explode('-'$keyParts[3]);
  659.                 $groupId = (int) $groupKeyId[0];
  660.                 $keyid = (int) $groupKeyId[1];
  661.                 $getter 'get' ucfirst($field);
  662.                 if (method_exists($object$getter)) {
  663.                     /** @var Classificationstore $classificationStoreData */
  664.                     $classificationStoreData $object->$getter();
  665.                     /** @var Model\DataObject\ClassDefinition\Data\Classificationstore $csFieldDefinition */
  666.                     $csFieldDefinition $object->getClass()->getFieldDefinition($field);
  667.                     $csLanguage $requestedLanguage;
  668.                     if (!$csFieldDefinition->isLocalized()) {
  669.                         $csLanguage 'default';
  670.                     }
  671.                     $fielddata $classificationStoreData->getLocalizedKeyValue($groupId$keyid$csLanguagetruetrue);
  672.                     $keyConfig Model\DataObject\Classificationstore\KeyConfig::getById($keyid);
  673.                     $type $keyConfig->getType();
  674.                     $definition json_decode($keyConfig->getDefinition());
  675.                     $definition \Pimcore\Model\DataObject\Classificationstore\Service::getFieldDefinitionFromJson($definition$type);
  676.                     if (method_exists($definition'getDataForGrid')) {
  677.                         $fielddata $definition->getDataForGrid($fielddata$object);
  678.                     }
  679.                     return $fielddata;
  680.                 }
  681.             }
  682.         }
  683.         return null;
  684.     }
  685.     /**
  686.      * @param Concrete $object
  687.      *
  688.      * @return Concrete|null
  689.      */
  690.     public static function hasInheritableParentObject(Concrete $object)
  691.     {
  692.         if ($object->getClass()->getAllowInherit()) {
  693.             return $object->getNextParentForInheritance();
  694.         }
  695.         return null;
  696.     }
  697.     /**
  698.      * call the getters of each object field, in case some of the are lazy loading and we need the data to be loaded
  699.      *
  700.      * @static
  701.      *
  702.      * @param AbstractObject $object
  703.      */
  704.     public static function loadAllObjectFields($object)
  705.     {
  706.         $object->getProperties();
  707.         if ($object instanceof Concrete) {
  708.             //load all in case of lazy loading fields
  709.             $fd $object->getClass()->getFieldDefinitions();
  710.             foreach ($fd as $def) {
  711.                 $getter 'get' ucfirst($def->getName());
  712.                 if (method_exists($object$getter)) {
  713.                     $value $object->$getter();
  714.                     if ($value instanceof Localizedfield) {
  715.                         $value->loadLazyData();
  716.                     } elseif ($value instanceof Objectbrick) {
  717.                         $value->loadLazyData();
  718.                     } elseif ($value instanceof Fieldcollection) {
  719.                         $value->loadLazyData();
  720.                     }
  721.                 }
  722.             }
  723.         }
  724.     }
  725.     /**
  726.      * @static
  727.      *
  728.      * @param Concrete|string $object
  729.      * @param string|ClassDefinition\Data\Select|ClassDefinition\Data\Multiselect $definition
  730.      *
  731.      * @return array
  732.      */
  733.     public static function getOptionsForSelectField($object$definition)
  734.     {
  735.         $class null;
  736.         $options = [];
  737.         if (is_object($object) && method_exists($object'getClass')) {
  738.             $class $object->getClass();
  739.         } elseif (is_string($object)) {
  740.             $object '\\' ltrim($object'\\');
  741.             $object = new $object();
  742.             $class $object->getClass();
  743.         }
  744.         if ($class) {
  745.             if (is_string($definition)) {
  746.                 $definition $class->getFieldDefinition($definition);
  747.             }
  748.             if ($definition instanceof ClassDefinition\Data\Select || $definition instanceof ClassDefinition\Data\Multiselect) {
  749.                 $optionsProvider DataObject\ClassDefinition\Helper\OptionsProviderResolver::resolveProvider(
  750.                     $definition->getOptionsProviderClass(),
  751.                     DataObject\ClassDefinition\Helper\OptionsProviderResolver::MODE_MULTISELECT
  752.                 );
  753.                 if ($optionsProvider instanceof DataObject\ClassDefinition\DynamicOptionsProvider\MultiSelectOptionsProviderInterface) {
  754.                     $_options $optionsProvider->getOptions(['fieldname' => $definition->getName()], $definition);
  755.                 } else {
  756.                     $_options $definition->getOptions();
  757.                 }
  758.                 foreach ($_options as $option) {
  759.                     $options[$option['value']] = $option['key'];
  760.                 }
  761.             }
  762.         }
  763.         return $options;
  764.     }
  765.     /**
  766.      * alias of getOptionsForMultiSelectField
  767.      *
  768.      * @param Concrete|string $object
  769.      * @param string|ClassDefinition\Data\Select|ClassDefinition\Data\Multiselect $fieldname
  770.      *
  771.      * @return array
  772.      */
  773.     public static function getOptionsForMultiSelectField($object$fieldname)
  774.     {
  775.         return self::getOptionsForSelectField($object$fieldname);
  776.     }
  777.     /**
  778.      * @static
  779.      *
  780.      * @param string $path
  781.      * @param string|null $type
  782.      *
  783.      * @return bool
  784.      */
  785.     public static function pathExists($path$type null)
  786.     {
  787.         if (!$path) {
  788.             return false;
  789.         }
  790.         $path Element\Service::correctPath($path);
  791.         try {
  792.             $object = new DataObject();
  793.             $pathElements explode('/'$path);
  794.             $keyIdx count($pathElements) - 1;
  795.             $key $pathElements[$keyIdx];
  796.             $validKey Element\Service::getValidKey($key'object');
  797.             unset($pathElements[$keyIdx]);
  798.             $pathOnly implode('/'$pathElements);
  799.             if ($validKey == $key && self::isValidPath($pathOnly'object')) {
  800.                 $object->getDao()->getByPath($path);
  801.                 return true;
  802.             }
  803.         } catch (\Exception $e) {
  804.         }
  805.         return false;
  806.     }
  807.     /**
  808.      * Rewrites id from source to target, $rewriteConfig contains
  809.      * array(
  810.      *  "document" => array(
  811.      *      SOURCE_ID => TARGET_ID,
  812.      *      SOURCE_ID => TARGET_ID
  813.      *  ),
  814.      *  "object" => array(...),
  815.      *  "asset" => array(...)
  816.      * )
  817.      *
  818.      * @param AbstractObject $object
  819.      * @param array $rewriteConfig
  820.      * @param array $params
  821.      *
  822.      * @return AbstractObject
  823.      */
  824.     public static function rewriteIds($object$rewriteConfig$params = [])
  825.     {
  826.         // rewriting elements only for snippets and pages
  827.         if ($object instanceof Concrete) {
  828.             $fields $object->getClass()->getFieldDefinitions();
  829.             foreach ($fields as $field) {
  830.                 //TODO Pimcore 11: remove method_exists BC layer
  831.                 if ($field instanceof IdRewriterInterface || method_exists($field'rewriteIds')) {
  832.                     if (!$field instanceof IdRewriterInterface) {
  833.                         trigger_deprecation('pimcore/pimcore''10.1',
  834.                             sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  835.                             'Implement the %s interface instead.'IdRewriterInterface::class));
  836.                     }
  837.                     $setter 'set' ucfirst($field->getName());
  838.                     if (method_exists($object$setter)) { // check for non-owner-objects
  839.                         $object->$setter($field->rewriteIds($object$rewriteConfig));
  840.                     }
  841.                 }
  842.             }
  843.         }
  844.         // rewriting properties
  845.         $properties $object->getProperties();
  846.         foreach ($properties as &$property) {
  847.             $property->rewriteIds($rewriteConfig);
  848.         }
  849.         $object->setProperties($properties);
  850.         return $object;
  851.     }
  852.     /**
  853.      * @param Concrete $object
  854.      *
  855.      * @return DataObject\ClassDefinition\CustomLayout[]
  856.      */
  857.     public static function getValidLayouts(Concrete $object)
  858.     {
  859.         $user AdminTool::getCurrentUser();
  860.         $resultList = [];
  861.         $isMasterAllowed $user->getAdmin();
  862.         $permissionSet $object->getPermissions('layouts'$user);
  863.         $layoutPermissions self::getLayoutPermissions($object->getClassId(), $permissionSet);
  864.         if (!$layoutPermissions || isset($layoutPermissions[0])) {
  865.             $isMasterAllowed true;
  866.         }
  867.         if ($user->getAdmin()) {
  868.             $superLayout = new ClassDefinition\CustomLayout();
  869.             $superLayout->setId('-1');
  870.             $superLayout->setName('Master (Admin Mode)');
  871.             $resultList[-1] = $superLayout;
  872.         }
  873.         if ($isMasterAllowed) {
  874.             $master = new ClassDefinition\CustomLayout();
  875.             $master->setId('0');
  876.             $master->setName('Master');
  877.             $resultList[0] = $master;
  878.         }
  879.         $classId $object->getClassId();
  880.         $list = new ClassDefinition\CustomLayout\Listing();
  881.         $list->setOrderKey('name');
  882.         $condition 'classId = ' $list->quote($classId);
  883.         if (is_array($layoutPermissions) && count($layoutPermissions)) {
  884.             $layoutIds array_values($layoutPermissions);
  885.             $condition .= ' AND id IN (' implode(','array_map([$list'quote'], $layoutIds)) . ')';
  886.         }
  887.         $list->setCondition($condition);
  888.         $list $list->load();
  889.         if ((!count($resultList) && !count($list)) || (count($resultList) == && !count($list))) {
  890.             return [];
  891.         }
  892.         foreach ($list as $customLayout) {
  893.             if ($customLayout instanceof ClassDefinition\CustomLayout) {
  894.                 $resultList[$customLayout->getId()] = $customLayout;
  895.             }
  896.         }
  897.         return $resultList;
  898.     }
  899.     /**
  900.      * Returns the fields of a datatype container (e.g. block or localized fields)
  901.      *
  902.      * @param ClassDefinition\Data|Model\DataObject\ClassDefinition\Layout $layout
  903.      * @param string $targetClass
  904.      * @param ClassDefinition\Data[] $targetList
  905.      * @param bool $insideDataType
  906.      *
  907.      * @return ClassDefinition\Data[]
  908.      */
  909.     public static function extractFieldDefinitions($layout$targetClass$targetList$insideDataType)
  910.     {
  911.         if ($insideDataType && $layout instanceof ClassDefinition\Data && !is_a($layout$targetClass)) {
  912.             $targetList[$layout->getName()] = $layout;
  913.         }
  914.         if (method_exists($layout'getChildren')) {
  915.             $children $layout->getChildren();
  916.             $insideDataType |= is_a($layout$targetClass);
  917.             if (is_array($children)) {
  918.                 foreach ($children as $child) {
  919.                     $targetList self::extractFieldDefinitions($child$targetClass$targetList$insideDataType);
  920.                 }
  921.             }
  922.         }
  923.         return $targetList;
  924.     }
  925.     /** Calculates the super layout definition for the given object.
  926.      * @param Concrete $object
  927.      *
  928.      * @return mixed
  929.      */
  930.     public static function getSuperLayoutDefinition(Concrete $object)
  931.     {
  932.         $masterLayout $object->getClass()->getLayoutDefinitions();
  933.         $superLayout unserialize(serialize($masterLayout));
  934.         self::createSuperLayout($superLayout);
  935.         return $superLayout;
  936.     }
  937.     /**
  938.      * @param ClassDefinition\Data|Model\DataObject\ClassDefinition\Layout $layout
  939.      */
  940.     public static function createSuperLayout($layout)
  941.     {
  942.         if ($layout instanceof ClassDefinition\Data) {
  943.             $layout->setInvisible(false);
  944.             $layout->setNoteditable(false);
  945.         }
  946.         if ($layout instanceof Model\DataObject\ClassDefinition\Data\Fieldcollections) {
  947.             $layout->setDisallowAddRemove(false);
  948.             $layout->setDisallowReorder(false);
  949.         }
  950.         if (method_exists($layout'getChildren')) {
  951.             $children $layout->getChildren();
  952.             if (is_array($children)) {
  953.                 foreach ($children as $child) {
  954.                     self::createSuperLayout($child);
  955.                 }
  956.             }
  957.         }
  958.     }
  959.     /**
  960.      * @param ClassDefinition\Data[] $masterDefinition
  961.      * @param ClassDefinition\Data|ClassDefinition\Layout|null $layout
  962.      *
  963.      * @return bool
  964.      */
  965.     private static function synchronizeCustomLayoutFieldWithMaster($masterDefinition, &$layout)
  966.     {
  967.         if (is_null($layout)) {
  968.             return true;
  969.         }
  970.         if ($layout instanceof ClassDefinition\Data) {
  971.             $fieldname $layout->name;
  972.             if (empty($masterDefinition[$fieldname])) {
  973.                 return false;
  974.             }
  975.             if ($layout->getFieldtype() !== $masterDefinition[$fieldname]->getFieldType()) {
  976.                 $layout->adoptMasterDefinition($masterDefinition[$fieldname]);
  977.             } else {
  978.                 $layout->synchronizeWithMasterDefinition($masterDefinition[$fieldname]);
  979.             }
  980.         }
  981.         if (method_exists($layout'getChildren')) {
  982.             $children $layout->getChildren();
  983.             if (is_array($children)) {
  984.                 $count count($children);
  985.                 for ($i $count 1$i >= 0$i--) {
  986.                     $child $children[$i];
  987.                     if (!self::synchronizeCustomLayoutFieldWithMaster($masterDefinition$child)) {
  988.                         unset($children[$i]);
  989.                     }
  990.                     $layout->setChildren($children);
  991.                 }
  992.             }
  993.         }
  994.         return true;
  995.     }
  996.     /** Synchronizes a custom layout with its master layout
  997.      * @param ClassDefinition\CustomLayout $customLayout
  998.      */
  999.     public static function synchronizeCustomLayout(ClassDefinition\CustomLayout $customLayout)
  1000.     {
  1001.         $classId $customLayout->getClassId();
  1002.         $class ClassDefinition::getById($classId);
  1003.         if ($class && ($class->getModificationDate() > $customLayout->getModificationDate())) {
  1004.             $masterDefinition $class->getFieldDefinitions();
  1005.             $customLayoutDefinition $customLayout->getLayoutDefinitions();
  1006.             foreach (['Localizedfields''Block'] as $dataType) {
  1007.                 $targetList self::extractFieldDefinitions($class->getLayoutDefinitions(), '\Pimcore\Model\DataObject\ClassDefinition\Data\\' $dataType, [], false);
  1008.                 $masterDefinition array_merge($masterDefinition$targetList);
  1009.             }
  1010.             self::synchronizeCustomLayoutFieldWithMaster($masterDefinition$customLayoutDefinition);
  1011.             $customLayout->save();
  1012.         }
  1013.     }
  1014.     /**
  1015.      * @param string $classId
  1016.      * @param int $objectId
  1017.      *
  1018.      * @return array|null
  1019.      *
  1020.      * @internal
  1021.      */
  1022.     public static function getCustomGridFieldDefinitions($classId$objectId)
  1023.     {
  1024.         $object DataObject::getById($objectId);
  1025.         $class ClassDefinition::getById($classId);
  1026.         $masterFieldDefinition $class->getFieldDefinitions();
  1027.         if (!$object) {
  1028.             return null;
  1029.         }
  1030.         $user AdminTool::getCurrentUser();
  1031.         if ($user->isAdmin()) {
  1032.             return null;
  1033.         }
  1034.         $permissionList = [];
  1035.         $parentPermissionSet $object->getPermissions(null$usertrue);
  1036.         if ($parentPermissionSet) {
  1037.             $permissionList[] = $parentPermissionSet;
  1038.         }
  1039.         $childPermissions $object->getChildPermissions(null$user);
  1040.         $permissionList array_merge($permissionList$childPermissions);
  1041.         $layoutDefinitions = [];
  1042.         foreach ($permissionList as $permissionSet) {
  1043.             $allowedLayoutIds self::getLayoutPermissions($classId$permissionSet);
  1044.             if (is_array($allowedLayoutIds)) {
  1045.                 foreach ($allowedLayoutIds as $allowedLayoutId) {
  1046.                     if ($allowedLayoutId) {
  1047.                         if (!isset($layoutDefinitions[$allowedLayoutId])) {
  1048.                             $customLayout ClassDefinition\CustomLayout::getById($allowedLayoutId);
  1049.                             if (!$customLayout) {
  1050.                                 continue;
  1051.                             }
  1052.                             $layoutDefinitions[$allowedLayoutId] = $customLayout;
  1053.                         }
  1054.                     }
  1055.                 }
  1056.             }
  1057.         }
  1058.         $mergedFieldDefinition self::cloneDefinition($masterFieldDefinition);
  1059.         if (count($layoutDefinitions)) {
  1060.             foreach ($mergedFieldDefinition as $key => $def) {
  1061.                 if ($def instanceof ClassDefinition\Data\Localizedfields) {
  1062.                     $mergedLocalizedFieldDefinitions $mergedFieldDefinition[$key]->getFieldDefinitions();
  1063.                     foreach ($mergedLocalizedFieldDefinitions as $locKey => $locValue) {
  1064.                         $mergedLocalizedFieldDefinitions[$locKey]->setInvisible(false);
  1065.                         $mergedLocalizedFieldDefinitions[$locKey]->setNotEditable(false);
  1066.                     }
  1067.                     $mergedFieldDefinition[$key]->setChildren($mergedLocalizedFieldDefinitions);
  1068.                 } else {
  1069.                     $mergedFieldDefinition[$key]->setInvisible(false);
  1070.                     $mergedFieldDefinition[$key]->setNotEditable(false);
  1071.                 }
  1072.             }
  1073.         }
  1074.         foreach ($layoutDefinitions as $customLayoutDefinition) {
  1075.             $layoutDefinitions $customLayoutDefinition->getLayoutDefinitions();
  1076.             $dummyClass = new ClassDefinition();
  1077.             $dummyClass->setLayoutDefinitions($layoutDefinitions);
  1078.             $customFieldDefinitions $dummyClass->getFieldDefinitions();
  1079.             foreach ($mergedFieldDefinition as $key => $value) {
  1080.                 if (empty($customFieldDefinitions[$key])) {
  1081.                     unset($mergedFieldDefinition[$key]);
  1082.                 }
  1083.             }
  1084.             foreach ($customFieldDefinitions as $key => $def) {
  1085.                 if ($def instanceof ClassDefinition\Data\Localizedfields) {
  1086.                     if (!$mergedFieldDefinition[$key]) {
  1087.                         continue;
  1088.                     }
  1089.                     $customLocalizedFieldDefinitions $def->getFieldDefinitions();
  1090.                     $mergedLocalizedFieldDefinitions $mergedFieldDefinition[$key]->getFieldDefinitions();
  1091.                     foreach ($mergedLocalizedFieldDefinitions as $locKey => $locValue) {
  1092.                         self::mergeFieldDefinition($mergedLocalizedFieldDefinitions$customLocalizedFieldDefinitions$locKey);
  1093.                     }
  1094.                     $mergedFieldDefinition[$key]->setChildren($mergedLocalizedFieldDefinitions);
  1095.                 } else {
  1096.                     self::mergeFieldDefinition($mergedFieldDefinition$customFieldDefinitions$key);
  1097.                 }
  1098.             }
  1099.         }
  1100.         return $mergedFieldDefinition;
  1101.     }
  1102.     /**
  1103.      * @param mixed $definition
  1104.      *
  1105.      * @return array
  1106.      */
  1107.     public static function cloneDefinition($definition)
  1108.     {
  1109.         $deepCopy = new \DeepCopy\DeepCopy();
  1110.         $deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('fieldDefinitionsCache'));
  1111.         $theCopy $deepCopy->copy($definition);
  1112.         return $theCopy;
  1113.     }
  1114.     /**
  1115.      * @param array $mergedFieldDefinition
  1116.      * @param array $customFieldDefinitions
  1117.      * @param string $key
  1118.      */
  1119.     private static function mergeFieldDefinition(&$mergedFieldDefinition, &$customFieldDefinitions$key)
  1120.     {
  1121.         if (empty($customFieldDefinitions[$key])) {
  1122.             unset($mergedFieldDefinition[$key]);
  1123.         } elseif (isset($mergedFieldDefinition[$key])) {
  1124.             $def $customFieldDefinitions[$key];
  1125.             if ($def->getNotEditable()) {
  1126.                 $mergedFieldDefinition[$key]->setNotEditable(true);
  1127.             }
  1128.             if ($def->getInvisible()) {
  1129.                 if ($mergedFieldDefinition[$key] instanceof ClassDefinition\Data\Objectbricks) {
  1130.                     unset($mergedFieldDefinition[$key]);
  1131.                     return;
  1132.                 }
  1133.                 $mergedFieldDefinition[$key]->setInvisible(true);
  1134.             }
  1135.             if ($def->title) {
  1136.                 $mergedFieldDefinition[$key]->setTitle($def->title);
  1137.             }
  1138.         }
  1139.     }
  1140.     /**
  1141.      * @param ClassDefinition\Data|ClassDefinition\Layout $layout
  1142.      * @param ClassDefinition\Data[] $fieldDefinitions
  1143.      *
  1144.      * @return bool
  1145.      */
  1146.     private static function doFilterCustomGridFieldDefinitions(&$layout$fieldDefinitions)
  1147.     {
  1148.         if ($layout instanceof ClassDefinition\Data) {
  1149.             $name $layout->getName();
  1150.             if (empty($fieldDefinitions[$name]) || $fieldDefinitions[$name]->getInvisible()) {
  1151.                 return false;
  1152.             }
  1153.             $layout->setNoteditable($layout->getNoteditable() | $fieldDefinitions[$name]->getNoteditable());
  1154.         }
  1155.         if (method_exists($layout'getChildren')) {
  1156.             $children $layout->getChildren();
  1157.             if (is_array($children)) {
  1158.                 $count count($children);
  1159.                 for ($i $count 1$i >= 0$i--) {
  1160.                     $child $children[$i];
  1161.                     if (!self::doFilterCustomGridFieldDefinitions($child$fieldDefinitions)) {
  1162.                         unset($children[$i]);
  1163.                     }
  1164.                 }
  1165.                 $layout->setChildren(array_values($children));
  1166.             }
  1167.         }
  1168.         return true;
  1169.     }
  1170.     /**  Determines the custom layout definition (if necessary) for the given class
  1171.      * @param ClassDefinition $class
  1172.      * @param int $objectId
  1173.      *
  1174.      * @return array layout
  1175.      *
  1176.      * @internal
  1177.      */
  1178.     public static function getCustomLayoutDefinitionForGridColumnConfig(ClassDefinition $class$objectId)
  1179.     {
  1180.         $layoutDefinitions $class->getLayoutDefinitions();
  1181.         $result = [
  1182.             'layoutDefinition' => $layoutDefinitions,
  1183.         ];
  1184.         if (!$objectId) {
  1185.             return $result;
  1186.         }
  1187.         $user AdminTool::getCurrentUser();
  1188.         if ($user->isAdmin()) {
  1189.             return $result;
  1190.         }
  1191.         $mergedFieldDefinition self::getCustomGridFieldDefinitions($class->getId(), $objectId);
  1192.         if (is_array($mergedFieldDefinition)) {
  1193.             if (isset($mergedFieldDefinition['localizedfields'])) {
  1194.                 $childs $mergedFieldDefinition['localizedfields']->getFieldDefinitions();
  1195.                 if (is_array($childs)) {
  1196.                     foreach ($childs as $locKey => $locValue) {
  1197.                         $mergedFieldDefinition[$locKey] = $locValue;
  1198.                     }
  1199.                 }
  1200.             }
  1201.             self::doFilterCustomGridFieldDefinitions($layoutDefinitions$mergedFieldDefinition);
  1202.             $result['layoutDefinition'] = $layoutDefinitions;
  1203.             $result['fieldDefinition'] = $mergedFieldDefinition;
  1204.         }
  1205.         return $result;
  1206.     }
  1207.     /**
  1208.      * @param AbstractObject $item
  1209.      * @param int $nr
  1210.      *
  1211.      * @return string
  1212.      *
  1213.      * @throws \Exception
  1214.      */
  1215.     public static function getUniqueKey($item$nr 0)
  1216.     {
  1217.         $list = new Listing();
  1218.         $list->setUnpublished(true);
  1219.         $list->setObjectTypes(DataObject::$types);
  1220.         $key Element\Service::getValidKey($item->getKey(), 'object');
  1221.         if (!$key) {
  1222.             throw new \Exception('No item key set.');
  1223.         }
  1224.         if ($nr) {
  1225.             $key .= '_'.$nr;
  1226.         }
  1227.         $parent $item->getParent();
  1228.         if (!$parent) {
  1229.             throw new \Exception('You have to set a parent Object to determine a unique Key');
  1230.         }
  1231.         if (!$item->getId()) {
  1232.             $list->setCondition('o_parentId = ? AND `o_key` = ? ', [$parent->getId(), $key]);
  1233.         } else {
  1234.             $list->setCondition('o_parentId = ? AND `o_key` = ? AND o_id != ? ', [$parent->getId(), $key$item->getId()]);
  1235.         }
  1236.         $check $list->loadIdList();
  1237.         if (!empty($check)) {
  1238.             $nr++;
  1239.             $key self::getUniqueKey($item$nr);
  1240.         }
  1241.         return $key;
  1242.     }
  1243.     /**
  1244.      * Enriches the layout definition before it is returned to the admin interface.
  1245.      *
  1246.      * @param Model\DataObject\ClassDefinition\Data|Model\DataObject\ClassDefinition\Layout|null $layout
  1247.      * @param Concrete|null $object
  1248.      * @param array $context additional contextual data
  1249.      *
  1250.      * @internal
  1251.      */
  1252.     public static function enrichLayoutDefinition(&$layout$object null$context = [])
  1253.     {
  1254.         if (is_null($layout)) {
  1255.             return;
  1256.         }
  1257.         $context['object'] = $object;
  1258.         //TODO Pimcore 11: remove method_exists BC layer
  1259.         if ($layout instanceof LayoutDefinitionEnrichmentInterface || method_exists($layout'enrichLayoutDefinition')) {
  1260.             if (!$layout instanceof LayoutDefinitionEnrichmentInterface) {
  1261.                 trigger_deprecation('pimcore/pimcore''10.1',
  1262.                     sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  1263.                     'Implement the %s interface instead.'LayoutDefinitionEnrichmentInterface::class));
  1264.             }
  1265.             $layout->enrichLayoutDefinition($object$context);
  1266.         }
  1267.         if ($layout instanceof Model\DataObject\ClassDefinition\Data\Localizedfields || $layout instanceof Model\DataObject\ClassDefinition\Data\Classificationstore && $layout->localized === true) {
  1268.             $user AdminTool::getCurrentUser();
  1269.             if (!$user->isAdmin() && ($context['purpose'] ?? null) !== 'gridconfig' && $object) {
  1270.                 $allowedView self::getLanguagePermissions($object$user'lView');
  1271.                 $allowedEdit self::getLanguagePermissions($object$user'lEdit');
  1272.                 self::enrichLayoutPermissions($layout$allowedView$allowedEdit);
  1273.             }
  1274.             if (isset($context['containerType']) && $context['containerType'] === 'fieldcollection') {
  1275.                 $context['subContainerType'] = 'localizedfield';
  1276.             } elseif (isset($context['containerType']) && $context['containerType'] === 'objectbrick') {
  1277.                 $context['subContainerType'] = 'localizedfield';
  1278.             } else {
  1279.                 $context['ownerType'] = 'localizedfield';
  1280.             }
  1281.             $context['ownerName'] = 'localizedfields';
  1282.         }
  1283.         if (method_exists($layout'getChildren')) {
  1284.             $children $layout->getChildren();
  1285.             if (is_array($children)) {
  1286.                 foreach ($children as $child) {
  1287.                     self::enrichLayoutDefinition($child$object$context);
  1288.                 }
  1289.             }
  1290.         }
  1291.     }
  1292.     /**
  1293.      * @param Model\DataObject\ClassDefinition\Data $layout
  1294.      * @param array $allowedView
  1295.      * @param array $allowedEdit
  1296.      *
  1297.      * @internal
  1298.      */
  1299.     public static function enrichLayoutPermissions(&$layout$allowedView$allowedEdit)
  1300.     {
  1301.         if ($layout instanceof Model\DataObject\ClassDefinition\Data\Localizedfields || $layout instanceof Model\DataObject\ClassDefinition\Data\Classificationstore && $layout->localized === true) {
  1302.             if (is_array($allowedView) && count($allowedView) > 0) {
  1303.                 $haveAllowedViewDefault null;
  1304.                 if ($layout->getFieldtype() === 'localizedfields') {
  1305.                     $haveAllowedViewDefault = isset($allowedView['default']);
  1306.                     if ($haveAllowedViewDefault) {
  1307.                         unset($allowedView['default']);
  1308.                     }
  1309.                 }
  1310.                 if (!($haveAllowedViewDefault && count($allowedView) == 0)) {
  1311.                     $layout->setPermissionView(
  1312.                         AdminTool::reorderWebsiteLanguages(
  1313.                             AdminTool::getCurrentUser(),
  1314.                             array_keys($allowedView),
  1315.                             true
  1316.                         )
  1317.                     );
  1318.                 }
  1319.             }
  1320.             if (is_array($allowedEdit) && count($allowedEdit) > 0) {
  1321.                 $haveAllowedEditDefault null;
  1322.                 if ($layout->getFieldtype() === 'localizedfields') {
  1323.                     $haveAllowedEditDefault = isset($allowedEdit['default']);
  1324.                     if ($haveAllowedEditDefault) {
  1325.                         unset($allowedEdit['default']);
  1326.                     }
  1327.                 }
  1328.                 if (!($haveAllowedEditDefault && count($allowedEdit) == 0)) {
  1329.                     $layout->setPermissionEdit(
  1330.                         AdminTool::reorderWebsiteLanguages(
  1331.                             AdminTool::getCurrentUser(),
  1332.                             array_keys($allowedEdit),
  1333.                             true
  1334.                         )
  1335.                     );
  1336.                 }
  1337.             }
  1338.         } else {
  1339.             if (method_exists($layout'getChildren')) {
  1340.                 $children $layout->getChildren();
  1341.                 if (is_array($children)) {
  1342.                     foreach ($children as $child) {
  1343.                         self::enrichLayoutPermissions($child$allowedView$allowedEdit);
  1344.                     }
  1345.                 }
  1346.             }
  1347.         }
  1348.     }
  1349.     private static function evaluateExpression(Model\DataObject\ClassDefinition\Data\CalculatedValue $fdConcrete $object, ?DataObject\Data\CalculatedValue $data)
  1350.     {
  1351.         $expressionLanguage = new ExpressionLanguage();
  1352.         //overwrite constant function to aviod exposing internal information
  1353.         $expressionLanguage->register('constant', function ($str) {
  1354.             throw new SyntaxError('`constant` function not available');
  1355.         }, function ($arguments$str) {
  1356.             throw new SyntaxError('`constant` function not available');
  1357.         });
  1358.         return $expressionLanguage->evaluate($fd->getCalculatorExpression(), ['object' => $object'data' => $data]);
  1359.     }
  1360.     /**
  1361.      * @param Concrete $object
  1362.      * @param array $params
  1363.      * @param Model\DataObject\Data\CalculatedValue|null $data
  1364.      *
  1365.      * @return string|null
  1366.      *
  1367.      * @internal
  1368.      */
  1369.     public static function getCalculatedFieldValueForEditMode($object$params$data)
  1370.     {
  1371.         if (!$data) {
  1372.             return null;
  1373.         }
  1374.         $fieldname $data->getFieldname();
  1375.         $ownerType $data->getOwnerType();
  1376.         $fd $data->getKeyDefinition();
  1377.         if ($fd === null) {
  1378.             if ($ownerType === 'object') {
  1379.                 $fd $object->getClass()->getFieldDefinition($fieldname);
  1380.             } elseif ($ownerType === 'localizedfield') {
  1381.                 /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $lfDef */
  1382.                 $lfDef $object->getClass()->getFieldDefinition('localizedfields');
  1383.                 $fd $lfDef->getFieldDefinition($fieldname);
  1384.             }
  1385.         }
  1386.         if (!$fd instanceof Model\DataObject\ClassDefinition\Data\CalculatedValue) {
  1387.             return null;
  1388.         }
  1389.         $inheritanceEnabled Model\DataObject\Concrete::getGetInheritedValues();
  1390.         Model\DataObject\Concrete::setGetInheritedValues(true);
  1391.         switch ($fd->getCalculatorType()) {
  1392.             case DataObject\ClassDefinition\Data\CalculatedValue::CALCULATOR_TYPE_CLASS:
  1393.                 $className $fd->getCalculatorClass();
  1394.                 $calculator Model\DataObject\ClassDefinition\Helper\CalculatorClassResolver::resolveCalculatorClass($className);
  1395.                 if (!$calculator instanceof DataObject\ClassDefinition\CalculatorClassInterface) {
  1396.                     Logger::error('Class does not exist or is not valid: ' $className);
  1397.                     return null;
  1398.                 }
  1399.                 $result $calculator->getCalculatedValueForEditMode($object$data);
  1400.                 break;
  1401.             case DataObject\ClassDefinition\Data\CalculatedValue::CALCULATOR_TYPE_EXPRESSION:
  1402.                 try {
  1403.                     $result self::evaluateExpression($fd$object$data);
  1404.                 } catch (SyntaxError $exception) {
  1405.                     return $exception->getMessage();
  1406.                 }
  1407.                 break;
  1408.             default:
  1409.                 return null;
  1410.         }
  1411.         Model\DataObject\Concrete::setGetInheritedValues($inheritanceEnabled);
  1412.         return $result;
  1413.     }
  1414.     /**
  1415.      * @param Concrete|Model\DataObject\Fieldcollection\Data\AbstractData|Model\DataObject\Objectbrick\Data\AbstractData $object
  1416.      * @param Model\DataObject\Data\CalculatedValue|null $data
  1417.      *
  1418.      * @return mixed|null
  1419.      */
  1420.     public static function getCalculatedFieldValue($object$data)
  1421.     {
  1422.         if (!$data) {
  1423.             return null;
  1424.         }
  1425.         $fieldname $data->getFieldname();
  1426.         $ownerType $data->getOwnerType();
  1427.         $fd $data->getKeyDefinition();
  1428.         if ($fd === null) {
  1429.             if ($ownerType === 'object') {
  1430.                 $fd $object->getClass()->getFieldDefinition($fieldname);
  1431.             } elseif ($ownerType === 'localizedfield') {
  1432.                 /** @var Model\DataObject\ClassDefinition\Data\Localizedfields $lfDef */
  1433.                 $lfDef $object->getClass()->getFieldDefinition('localizedfields');
  1434.                 $fd $lfDef->getFieldDefinition($fieldname);
  1435.             }
  1436.         }
  1437.         if (!$fd instanceof Model\DataObject\ClassDefinition\Data\CalculatedValue) {
  1438.             return null;
  1439.         }
  1440.         $inheritanceEnabled Model\DataObject\Concrete::getGetInheritedValues();
  1441.         Model\DataObject\Concrete::setGetInheritedValues(true);
  1442.         if (
  1443.             $object instanceof Model\DataObject\Fieldcollection\Data\AbstractData ||
  1444.             $object instanceof Model\DataObject\Objectbrick\Data\AbstractData
  1445.         ) {
  1446.             $object $object->getObject();
  1447.         }
  1448.         switch ($fd->getCalculatorType()) {
  1449.             case DataObject\ClassDefinition\Data\CalculatedValue::CALCULATOR_TYPE_CLASS:
  1450.                 $className $fd->getCalculatorClass();
  1451.                 $calculator Model\DataObject\ClassDefinition\Helper\CalculatorClassResolver::resolveCalculatorClass($className);
  1452.                 if (!$calculator instanceof DataObject\ClassDefinition\CalculatorClassInterface) {
  1453.                     Logger::error('Class does not exist or is not valid: ' $className);
  1454.                     return null;
  1455.                 }
  1456.                 $result $calculator->compute($object$data);
  1457.                 break;
  1458.             case DataObject\ClassDefinition\Data\CalculatedValue::CALCULATOR_TYPE_EXPRESSION:
  1459.                 try {
  1460.                     $result self::evaluateExpression($fd$object$data);
  1461.                 } catch (SyntaxError $exception) {
  1462.                     return $exception->getMessage();
  1463.                 }
  1464.                 break;
  1465.             default:
  1466.                 return null;
  1467.         }
  1468.         Model\DataObject\Concrete::setGetInheritedValues($inheritanceEnabled);
  1469.         return $result;
  1470.     }
  1471.     /**
  1472.      * @return array
  1473.      */
  1474.     public static function getSystemFields()
  1475.     {
  1476.         return self::$systemFields;
  1477.     }
  1478.     /**
  1479.      * @param Concrete $container
  1480.      * @param ClassDefinition|ClassDefinition\Data $fd
  1481.      */
  1482.     public static function doResetDirtyMap($container$fd)
  1483.     {
  1484.         if (!method_exists($fd'getFieldDefinitions')) {
  1485.             return;
  1486.         }
  1487.         $fieldDefinitions $fd->getFieldDefinitions();
  1488.         if (is_array($fieldDefinitions)) {
  1489.             foreach ($fieldDefinitions as $fieldDefinition) {
  1490.                 $value $container->getObjectVar($fieldDefinition->getName());
  1491.                 if ($value instanceof Localizedfield) {
  1492.                     $value->resetLanguageDirtyMap();
  1493.                 }
  1494.                 if ($value instanceof DirtyIndicatorInterface) {
  1495.                     $value->resetDirtyMap();
  1496.                     self::doResetDirtyMap($value$fieldDefinitions[$fieldDefinition->getName()]);
  1497.                 }
  1498.             }
  1499.         }
  1500.     }
  1501.     /**
  1502.      * @param AbstractObject $object
  1503.      */
  1504.     public static function recursiveResetDirtyMap(AbstractObject $object)
  1505.     {
  1506.         if ($object instanceof DirtyIndicatorInterface) {
  1507.             $object->resetDirtyMap();
  1508.         }
  1509.         if ($object instanceof Concrete) {
  1510.             self::doResetDirtyMap($object$object->getClass());
  1511.         }
  1512.     }
  1513.     /**
  1514.      * @internal
  1515.      *
  1516.      * @param array $descriptor
  1517.      *
  1518.      * @return array
  1519.      */
  1520.     public static function buildConditionPartsFromDescriptor($descriptor)
  1521.     {
  1522.         $db Db::get();
  1523.         $conditionParts = [];
  1524.         foreach ($descriptor as $key => $value) {
  1525.             $lastChar is_string($value) ? $value[strlen($value) - 1] : null;
  1526.             if ($lastChar === '%') {
  1527.                 $conditionParts[] = $key ' LIKE ' $db->quote($value);
  1528.             } else {
  1529.                 $conditionParts[] = $key ' = ' $db->quote($value);
  1530.             }
  1531.         }
  1532.         return $conditionParts;
  1533.     }
  1534.     /**
  1535.      * @param AbstractObject $object
  1536.      * @param string $requestedLanguage
  1537.      * @param array $fields
  1538.      * @param array $helperDefinitions
  1539.      * @param LocaleServiceInterface $localeService
  1540.      * @param bool $returnMappedFieldNames
  1541.      * @param array $context
  1542.      *
  1543.      * @return array
  1544.      *
  1545.      * @internal
  1546.      */
  1547.     public static function getCsvDataForObject(AbstractObject $object$requestedLanguage$fields$helperDefinitionsLocaleServiceInterface $localeService$returnMappedFieldNames false$context = [])
  1548.     {
  1549.         $objectData = [];
  1550.         $mappedFieldnames = [];
  1551.         foreach ($fields as $field) {
  1552.             if (static::isHelperGridColumnConfig($field) && $validLanguages = static::expandGridColumnForExport($helperDefinitions$field)) {
  1553.                 $currentLocale $localeService->getLocale();
  1554.                 $mappedFieldnameBase self::mapFieldname($field$helperDefinitions);
  1555.                 foreach ($validLanguages as $validLanguage) {
  1556.                     $localeService->setLocale($validLanguage);
  1557.                     $fieldData self::getCsvFieldData($currentLocale$field$object$validLanguage$helperDefinitions);
  1558.                     $localizedFieldKey $field '-' $validLanguage;
  1559.                     if (!isset($mappedFieldnames[$localizedFieldKey])) {
  1560.                         $mappedFieldnames[$localizedFieldKey] = $mappedFieldnameBase '-' $validLanguage;
  1561.                     }
  1562.                     $objectData[$localizedFieldKey] = $fieldData;
  1563.                 }
  1564.                 $localeService->setLocale($currentLocale);
  1565.             } else {
  1566.                 $fieldData self::getCsvFieldData($requestedLanguage$field$object$requestedLanguage$helperDefinitions);
  1567.                 if (!isset($mappedFieldnames[$field])) {
  1568.                     $mappedFieldnames[$field] = self::mapFieldname($field$helperDefinitions);
  1569.                 }
  1570.                 $objectData[$field] = $fieldData;
  1571.             }
  1572.         }
  1573.         if ($returnMappedFieldNames) {
  1574.             $tmp = [];
  1575.             foreach ($mappedFieldnames as $key => $value) {
  1576.                 $tmp[$value] = $objectData[$key];
  1577.             }
  1578.             $objectData $tmp;
  1579.         }
  1580.         $event = new DataObjectEvent($object, ['objectData' => $objectData,
  1581.             'context' => $context,
  1582.             'requestedLanguage' => $requestedLanguage,
  1583.             'fields' => $fields,
  1584.             'helperDefinitions' => $helperDefinitions,
  1585.             'localeService' => $localeService,
  1586.             'returnMappedFieldNames' => $returnMappedFieldNames,
  1587.         ]);
  1588.         \Pimcore::getEventDispatcher()->dispatch($eventDataObjectEvents::POST_CSV_ITEM_EXPORT);
  1589.         $objectData $event->getArgument('objectData');
  1590.         return $objectData;
  1591.     }
  1592.     /**
  1593.      * @param string $requestedLanguage
  1594.      * @param LocaleServiceInterface $localeService
  1595.      * @param DataObject\Listing $list
  1596.      * @param string[] $fields
  1597.      * @param bool $addTitles
  1598.      * @param array $context
  1599.      *
  1600.      * @return array
  1601.      *
  1602.      * @internal
  1603.      */
  1604.     public static function getCsvData($requestedLanguageLocaleServiceInterface $localeService$list$fields$addTitles true$context = [])
  1605.     {
  1606.         $mappedFieldnames = [];
  1607.         $data = [];
  1608.         Logger::debug('objects in list:' count($list->getObjects()));
  1609.         $helperDefinitions = static::getHelperDefinitions();
  1610.         foreach ($list->getObjects() as $object) {
  1611.             if ($fields) {
  1612.                 if ($addTitles && empty($data)) {
  1613.                     $tmp = [];
  1614.                     $mapped self::getCsvDataForObject($object$requestedLanguage$fields$helperDefinitions$localeServicetrue$context);
  1615.                     foreach ($mapped as $key => $value) {
  1616.                         $tmp[] = '"' $key '"';
  1617.                     }
  1618.                     $data[] = $tmp;
  1619.                 }
  1620.                 $rowData self::getCsvDataForObject($object$requestedLanguage$fields$helperDefinitions$localeServicefalse$context);
  1621.                 $rowData self::escapeCsvRecord($rowData);
  1622.                 $data[] = $rowData;
  1623.             }
  1624.         }
  1625.         return $data;
  1626.     }
  1627.     /**
  1628.      * @param string $field
  1629.      * @param array $helperDefinitions
  1630.      *
  1631.      * @return string
  1632.      */
  1633.     protected static function mapFieldname($field$helperDefinitions)
  1634.     {
  1635.         if (strpos($field'#') === 0) {
  1636.             if (isset($helperDefinitions[$field])) {
  1637.                 if ($helperDefinitions[$field]->attributes) {
  1638.                     return $helperDefinitions[$field]->attributes->label $helperDefinitions[$field]->attributes->label $field;
  1639.                 }
  1640.                 return $field;
  1641.             }
  1642.         } elseif (substr($field01) == '~') {
  1643.             $fieldParts explode('~'$field);
  1644.             $type $fieldParts[1];
  1645.             if ($type == 'classificationstore') {
  1646.                 $fieldname $fieldParts[2];
  1647.                 $groupKeyId explode('-'$fieldParts[3]);
  1648.                 $groupId = (int) $groupKeyId[0];
  1649.                 $keyId = (int) $groupKeyId[1];
  1650.                 $groupConfig DataObject\Classificationstore\GroupConfig::getById($groupId);
  1651.                 $keyConfig DataObject\Classificationstore\KeyConfig::getById($keyId);
  1652.                 $field $fieldname '~' $groupConfig->getName() . '~' $keyConfig->getName();
  1653.             }
  1654.         }
  1655.         return $field;
  1656.     }
  1657.     /**
  1658.      * @param string $fallbackLanguage
  1659.      * @param string $field
  1660.      * @param DataObject\Concrete $object
  1661.      * @param string $requestedLanguage
  1662.      * @param array $helperDefinitions
  1663.      *
  1664.      * @return mixed
  1665.      *
  1666.      * @internal
  1667.      */
  1668.     protected static function getCsvFieldData($fallbackLanguage$field$object$requestedLanguage$helperDefinitions)
  1669.     {
  1670.         //check if field is systemfield
  1671.         $systemFieldMap = [
  1672.             'id' => 'getId',
  1673.             'fullpath' => 'getRealFullPath',
  1674.             'published' => 'getPublished',
  1675.             'creationDate' => 'getCreationDate',
  1676.             'modificationDate' => 'getModificationDate',
  1677.             'filename' => 'getKey',
  1678.             'key' => 'getKey',
  1679.             'classname' => 'getClassname',
  1680.         ];
  1681.         if (in_array($fieldarray_keys($systemFieldMap))) {
  1682.             $getter $systemFieldMap[$field];
  1683.             return $object->$getter();
  1684.         } else {
  1685.             //check if field is standard object field
  1686.             $fieldDefinition $object->getClass()->getFieldDefinition($field);
  1687.             if ($fieldDefinition) {
  1688.                 return $fieldDefinition->getForCsvExport($object);
  1689.             } else {
  1690.                 $fieldParts explode('~'$field);
  1691.                 // check for objects bricks and localized fields
  1692.                 if (static::isHelperGridColumnConfig($field)) {
  1693.                     if ($helperDefinitions[$field]) {
  1694.                         $cellValue = static::calculateCellValue($object$helperDefinitions$field, ['language' => $requestedLanguage]);
  1695.                         // Mimic grid concatenation behavior
  1696.                         if (is_array($cellValue)) {
  1697.                             $cellValue implode(','$cellValue);
  1698.                         }
  1699.                         return $cellValue;
  1700.                     }
  1701.                 } elseif (substr($field01) == '~') {
  1702.                     $type $fieldParts[1];
  1703.                     if ($type == 'classificationstore') {
  1704.                         $fieldname $fieldParts[2];
  1705.                         $groupKeyId explode('-'$fieldParts[3]);
  1706.                         $groupId = (int) $groupKeyId[0];
  1707.                         $keyId = (int) $groupKeyId[1];
  1708.                         $getter 'get' ucfirst($fieldname);
  1709.                         if (method_exists($object$getter)) {
  1710.                             $keyConfig DataObject\Classificationstore\KeyConfig::getById($keyId);
  1711.                             $type $keyConfig->getType();
  1712.                             $definition json_decode($keyConfig->getDefinition());
  1713.                             $fieldDefinition \Pimcore\Model\DataObject\Classificationstore\Service::getFieldDefinitionFromJson($definition$type);
  1714.                             /** @var DataObject\ClassDefinition\Data\Classificationstore $csFieldDefinition */
  1715.                             $csFieldDefinition $object->getClass()->getFieldDefinition($fieldname);
  1716.                             $csLanguage $requestedLanguage;
  1717.                             if (!$csFieldDefinition->isLocalized()) {
  1718.                                 $csLanguage 'default';
  1719.                             }
  1720.                             return $fieldDefinition->getForCsvExport(
  1721.                                 $object,
  1722.                                 ['context' => [
  1723.                                     'containerType' => 'classificationstore',
  1724.                                     'fieldname' => $fieldname,
  1725.                                     'groupId' => $groupId,
  1726.                                     'keyId' => $keyId,
  1727.                                     'language' => $csLanguage,
  1728.                                 ]]
  1729.                             );
  1730.                         }
  1731.                     }
  1732.                     //key value store - ignore for now
  1733.                 } elseif (count($fieldParts) > 1) {
  1734.                     // brick
  1735.                     $brickType $fieldParts[0];
  1736.                     $brickDescriptor null;
  1737.                     $innerContainer null;
  1738.                     if (strpos($brickType'?') !== false) {
  1739.                         $brickDescriptor substr($brickType1);
  1740.                         $brickDescriptor json_decode($brickDescriptortrue);
  1741.                         $innerContainer $brickDescriptor['innerContainer'] ?? 'localizedfields';
  1742.                         $brickType $brickDescriptor['containerKey'];
  1743.                     }
  1744.                     $brickKey $fieldParts[1];
  1745.                     $key = static::getFieldForBrickType($object->getClass(), $brickType);
  1746.                     $brickClass DataObject\Objectbrick\Definition::getByKey($brickType);
  1747.                     if ($brickDescriptor) {
  1748.                         /** @var DataObject\ClassDefinition\Data\Localizedfields $localizedFields */
  1749.                         $localizedFields $brickClass->getFieldDefinition($innerContainer);
  1750.                         $fieldDefinition $localizedFields->getFieldDefinition($brickDescriptor['brickfield']);
  1751.                     } else {
  1752.                         $fieldDefinition $brickClass->getFieldDefinition($brickKey);
  1753.                     }
  1754.                     if ($fieldDefinition) {
  1755.                         $brickContainer $object->{'get' ucfirst($key)}();
  1756.                         if ($brickContainer && !empty($brickKey)) {
  1757.                             $brick $brickContainer->{'get' ucfirst($brickType)}();
  1758.                             if ($brick) {
  1759.                                 $params = [
  1760.                                     'context' => [
  1761.                                         'containerType' => 'objectbrick',
  1762.                                         'containerKey' => $brickType,
  1763.                                         'fieldname' => $brickKey,
  1764.                                     ],
  1765.                                 ];
  1766.                                 $value $brick;
  1767.                                 if ($brickDescriptor) {
  1768.                                     $innerContainer $brickDescriptor['innerContainer'] ?? 'localizedfields';
  1769.                                     $value $brick->{'get' ucfirst($innerContainer)}();
  1770.                                 }
  1771.                                 return $fieldDefinition->getForCsvExport($value$params);
  1772.                             }
  1773.                         }
  1774.                     }
  1775.                 } else {
  1776.                     // if the definition is not set try to get the definition from localized fields
  1777.                     /** @var DataObject\ClassDefinition\Data\Localizedfields|null $locFields */
  1778.                     $locFields $object->getClass()->getFieldDefinition('localizedfields');
  1779.                     if ($locFields) {
  1780.                         $fieldDefinition $locFields->getFieldDefinition($field);
  1781.                         if ($fieldDefinition) {
  1782.                             return $fieldDefinition->getForCsvExport($object->get('localizedFields'), ['language' => $fallbackLanguage]);
  1783.                         }
  1784.                     }
  1785.                 }
  1786.             }
  1787.         }
  1788.         return null;
  1789.     }
  1790. }