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

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