vendor/pimcore/pimcore/models/DataObject/ClassDefinition/Data/Localizedfields.php line 518

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\ClassDefinition\Data;
  15. use Pimcore\Logger;
  16. use Pimcore\Model;
  17. use Pimcore\Model\DataObject;
  18. use Pimcore\Model\DataObject\ClassDefinition\Data;
  19. use Pimcore\Model\DataObject\ClassDefinition\Layout;
  20. use Pimcore\Model\Element;
  21. use Pimcore\Normalizer\NormalizerInterface;
  22. use Pimcore\Tool;
  23. class Localizedfields extends Data implements CustomResourcePersistingInterface, TypeDeclarationSupportInterface, NormalizerInterface, DataContainerAwareInterface, IdRewriterInterface, PreGetDataInterface
  24. {
  25. use Element\ChildsCompatibilityTrait;
  26. use Layout\Traits\LabelTrait;
  27. use DataObject\Traits\ClassSavedTrait;
  28. /**
  29. * Static type of this element
  30. *
  31. * @internal
  32. *
  33. * @var string
  34. */
  35. public $fieldtype = 'localizedfields';
  36. /**
  37. * @internal
  38. *
  39. * @var array
  40. */
  41. public $childs = [];
  42. /**
  43. * @internal
  44. *
  45. * @var string
  46. */
  47. public $name;
  48. /**
  49. * @internal
  50. *
  51. * @var string
  52. */
  53. public $region;
  54. /**
  55. * @internal
  56. *
  57. * @var string
  58. */
  59. public $layout;
  60. /**
  61. * @internal
  62. *
  63. * @var string
  64. */
  65. public $title;
  66. /**
  67. * @internal
  68. *
  69. * @var string|int
  70. */
  71. public $width = 0;
  72. /**
  73. * @internal
  74. *
  75. * @var string|int
  76. */
  77. public $height = 0;
  78. /**
  79. * @internal
  80. *
  81. * @var int
  82. */
  83. public $maxTabs;
  84. /**
  85. * @internal
  86. *
  87. * @var bool
  88. */
  89. public $border = false;
  90. /**
  91. * @internal
  92. *
  93. * @var bool
  94. */
  95. public $provideSplitView;
  96. /**
  97. * @internal
  98. *
  99. * @var string
  100. */
  101. public $tabPosition = 'top';
  102. /**
  103. * @internal
  104. *
  105. * @var int
  106. */
  107. public $hideLabelsWhenTabsReached;
  108. /**
  109. * contains further localized field definitions if there are more than one localized fields in on class
  110. *
  111. * @internal
  112. *
  113. * @var array
  114. */
  115. protected $referencedFields = [];
  116. /**
  117. * @internal
  118. *
  119. * @var array|null
  120. */
  121. public $fieldDefinitionsCache;
  122. /**
  123. * @internal
  124. *
  125. * @var array|null
  126. */
  127. public $permissionView;
  128. /**
  129. * @internal
  130. *
  131. * @var array|null
  132. */
  133. public $permissionEdit;
  134. /**
  135. * @see Data::getDataForEditmode
  136. *
  137. * @param DataObject\Localizedfield $localizedField
  138. * @param null|DataObject\Concrete $object
  139. * @param mixed $params
  140. *
  141. * @return array
  142. */
  143. public function getDataForEditmode($localizedField, $object = null, $params = [])
  144. {
  145. $fieldData = [];
  146. $metaData = [];
  147. if (!$localizedField instanceof DataObject\Localizedfield) {
  148. return [];
  149. }
  150. $result = $this->doGetDataForEditMode($localizedField, $object, $fieldData, $metaData, 1, $params);
  151. // replace the real data with the data for the editmode
  152. foreach ($result['data'] as $language => &$data) {
  153. foreach ($data as $key => &$value) {
  154. $fieldDefinition = $this->getFieldDefinition($key);
  155. if ($fieldDefinition instanceof CalculatedValue) {
  156. $childData = new DataObject\Data\CalculatedValue($fieldDefinition->getName());
  157. $ownerType = $params['context']['containerType'] ?? 'localizedfield';
  158. $ownerName = $params['fieldname'] ?? $this->getName();
  159. $index = $params['context']['containerKey'] ?? null;
  160. $childData->setContextualData($ownerType, $ownerName, $index, $language, null, null, $fieldDefinition);
  161. $value = $fieldDefinition->getDataForEditmode($childData, $object, $params);
  162. } else {
  163. $value = $fieldDefinition->getDataForEditmode($value, $object, array_merge($params, $localizedField->getDao()->getFieldDefinitionParams($fieldDefinition->getName(), $language)));
  164. }
  165. }
  166. }
  167. return $result;
  168. }
  169. /**
  170. * @param DataObject\Localizedfield $data
  171. * @param DataObject\Concrete $object
  172. * @param array $fieldData
  173. * @param array $metaData
  174. * @param int $level
  175. * @param array $params
  176. *
  177. * @return array
  178. */
  179. private function doGetDataForEditMode($data, $object, &$fieldData, &$metaData, $level = 1, $params = [])
  180. {
  181. $class = $object->getClass();
  182. $inheritanceAllowed = $class->getAllowInherit();
  183. $inherited = false;
  184. $loadLazy = !($params['objectFromVersion'] ?? false);
  185. $dataItems = $data->getInternalData($loadLazy);
  186. foreach ($dataItems as $language => $values) {
  187. foreach ($this->getFieldDefinitions() as $fd) {
  188. if ($fd instanceof LazyLoadingSupportInterface && $fd->getLazyLoading() && $loadLazy) {
  189. $lazyKey = $data->buildLazyKey($fd->getName(), $language);
  190. if (!$data->isLazyKeyLoaded($lazyKey) && $fd instanceof CustomResourcePersistingInterface) {
  191. $params['language'] = $language;
  192. $params['object'] = $object;
  193. if (!isset($params['context'])) {
  194. $params['context'] = [];
  195. }
  196. $params['context']['object'] = $object;
  197. $value = $fd->load($data, $params);
  198. if ($value === 0 || !empty($value)) {
  199. $data->setLocalizedValue($fd->getName(), $value, $language, false);
  200. $values[$fd->getName()] = $value;
  201. }
  202. $data->markLazyKeyAsLoaded($lazyKey);
  203. }
  204. }
  205. $key = $fd->getName();
  206. $fdata = isset($values[$fd->getName()]) ? $values[$fd->getName()] : null;
  207. if (!isset($fieldData[$language][$key]) || $fd->isEmpty($fieldData[$language][$key])) {
  208. // never override existing data
  209. $fieldData[$language][$key] = $fdata;
  210. if (!$fd->isEmpty($fdata)) {
  211. $inherited = $level > 1;
  212. if (isset($params['context']['containerType']) && $params['context']['containerType'] === 'block') {
  213. $inherited = false;
  214. }
  215. $metaData[$language][$key] = ['inherited' => $inherited, 'objectid' => $object->getId()];
  216. }
  217. }
  218. }
  219. }
  220. if (isset($params['context']['containerType']) && $params['context']['containerType'] === 'block') {
  221. $inheritanceAllowed = false;
  222. }
  223. if ($inheritanceAllowed) {
  224. // check if there is a parent with the same type
  225. $parent = DataObject\Service::hasInheritableParentObject($object);
  226. if ($parent) {
  227. // same type, iterate over all language and all fields and check if there is something missing
  228. $validLanguages = Tool::getValidLanguages();
  229. $foundEmptyValue = false;
  230. foreach ($validLanguages as $language) {
  231. $fieldDefinitions = $this->getFieldDefinitions();
  232. foreach ($fieldDefinitions as $fd) {
  233. $key = $fd->getName();
  234. if ($fd->isEmpty($fieldData[$language][$key] ?? null)) {
  235. $foundEmptyValue = true;
  236. $inherited = true;
  237. $metaData[$language][$key] = ['inherited' => true, 'objectid' => $parent->getId()];
  238. }
  239. }
  240. }
  241. if ($foundEmptyValue) {
  242. $parentData = null;
  243. // still some values are passing, ask the parent
  244. if (isset($params['context']['containerType']) && $params['context']['containerType'] === 'objectbrick') {
  245. $brickContainerGetter = 'get' . ucfirst($params['fieldname']);
  246. $brickContainer = $parent->$brickContainerGetter();
  247. $brickGetter = 'get' . ucfirst($params['context']['containerKey']);
  248. $brickData = $brickContainer->$brickGetter();
  249. if ($brickData) {
  250. $parentData = $brickData->getLocalizedFields();
  251. }
  252. } else {
  253. if (method_exists($parent, 'getLocalizedFields')) {
  254. $parentData = $parent->getLocalizedFields();
  255. }
  256. }
  257. if ($parentData) {
  258. $parentResult = $this->doGetDataForEditMode(
  259. $parentData,
  260. $parent,
  261. $fieldData,
  262. $metaData,
  263. $level + 1,
  264. $params
  265. );
  266. }
  267. }
  268. }
  269. }
  270. $result = [
  271. 'data' => $fieldData,
  272. 'metaData' => $metaData,
  273. 'inherited' => $inherited,
  274. ];
  275. return $result;
  276. }
  277. /**
  278. * @see Data::getDataFromEditmode
  279. *
  280. * @param array $data
  281. * @param null|DataObject\Concrete $object
  282. * @param mixed $params
  283. *
  284. * @return DataObject\Localizedfield
  285. */
  286. public function getDataFromEditmode($data, $object = null, $params = [])
  287. {
  288. $localizedFields = $this->getDataFromObjectParam($object, $params);
  289. if (!$localizedFields instanceof DataObject\Localizedfield) {
  290. $localizedFields = new DataObject\Localizedfield();
  291. $context = isset($params['context']) ? $params['context'] : null;
  292. $localizedFields->setContext($context);
  293. }
  294. if ($object) {
  295. $localizedFields->setObject($object);
  296. }
  297. if (is_array($data)) {
  298. foreach ($data as $language => $fields) {
  299. foreach ($fields as $name => $fdata) {
  300. $fd = $this->getFieldDefinition($name);
  301. $params['language'] = $language;
  302. $localizedFields->setLocalizedValue(
  303. $name,
  304. $fd->getDataFromEditmode($fdata, $object, $params),
  305. $language
  306. );
  307. }
  308. }
  309. }
  310. return $localizedFields;
  311. }
  312. /**
  313. * @param DataObject\Localizedfield|null $data
  314. * @param DataObject\Concrete|null $object
  315. * @param mixed $params
  316. *
  317. * @return \stdClass
  318. */
  319. public function getDataForGrid($data, $object = null, $params = [])
  320. {
  321. $result = new \stdClass();
  322. foreach ($this->getFieldDefinitions() as $fd) {
  323. $key = $fd->getName();
  324. $context = $params['context'] ?? null;
  325. if (isset($context['containerType']) && $context['containerType'] === 'objectbrick') {
  326. $result->$key = 'NOT SUPPORTED';
  327. } else {
  328. $result->$key = $object->{'get' . ucfirst($fd->getName())}();
  329. }
  330. if (method_exists($fd, 'getDataForGrid')) {
  331. $result->$key = $fd->getDataForGrid($result->$key, $object, $params);
  332. }
  333. }
  334. return $result;
  335. }
  336. /**
  337. * @see Data::getVersionPreview
  338. *
  339. * @param DataObject\Localizedfield|null $data
  340. * @param null|DataObject\Concrete $object
  341. * @param mixed $params
  342. *
  343. * @return string
  344. */
  345. public function getVersionPreview($data, $object = null, $params = [])
  346. {
  347. // this is handled directly in the template
  348. // /bundles/AdminBundle/Resources/views/Admin/DataObject/DataObject/previewVersion.html.twig
  349. return 'LOCALIZED FIELDS';
  350. }
  351. /**
  352. * {@inheritdoc}
  353. */
  354. public function getForCsvExport($object, $params = [])
  355. {
  356. return 'NOT SUPPORTED';
  357. }
  358. /**
  359. * {@inheritdoc}
  360. */
  361. public function getDataForSearchIndex($object, $params = [])
  362. {
  363. $dataString = '';
  364. $lfData = $this->getDataFromObjectParam($object);
  365. if ($lfData instanceof DataObject\Localizedfield) {
  366. foreach ($lfData->getInternalData(true) as $language => $values) {
  367. foreach ($values as $fieldname => $lData) {
  368. $fd = $this->getFieldDefinition($fieldname);
  369. if ($fd) {
  370. $forSearchIndex = $fd->getDataForSearchIndex($lfData, [
  371. 'injectedData' => $lData,
  372. ]);
  373. if ($forSearchIndex) {
  374. $dataString .= $forSearchIndex . ' ';
  375. }
  376. }
  377. }
  378. }
  379. }
  380. return $dataString;
  381. }
  382. /**
  383. * @return array
  384. */
  385. public function getChildren()
  386. {
  387. return $this->childs;
  388. }
  389. /**
  390. * @param array $children
  391. *
  392. * @return $this
  393. */
  394. public function setChildren($children)
  395. {
  396. $this->childs = $children;
  397. $this->fieldDefinitionsCache = null;
  398. return $this;
  399. }
  400. /**
  401. * @return bool
  402. */
  403. public function hasChildren()
  404. {
  405. return is_array($this->childs) && count($this->childs) > 0;
  406. }
  407. /**
  408. * @param Data|Layout $child
  409. */
  410. public function addChild($child)
  411. {
  412. $this->childs[] = $child;
  413. $this->fieldDefinitionsCache = null;
  414. }
  415. /**
  416. * @param Data[] $referencedFields
  417. */
  418. public function setReferencedFields($referencedFields)
  419. {
  420. $this->referencedFields = $referencedFields;
  421. $this->fieldDefinitionsCache = null;
  422. }
  423. /**
  424. * @return Data[]
  425. */
  426. public function getReferencedFields()
  427. {
  428. return $this->referencedFields;
  429. }
  430. /**
  431. * @param Data $field
  432. */
  433. public function addReferencedField($field)
  434. {
  435. $this->referencedFields[] = $field;
  436. $this->fieldDefinitionsCache = null;
  437. }
  438. /**
  439. * {@inheritdoc}
  440. */
  441. public function save($object, $params = [])
  442. {
  443. $localizedFields = $this->getDataFromObjectParam($object, $params);
  444. if ($localizedFields instanceof DataObject\Localizedfield) {
  445. if ((!isset($params['newParent']) || !$params['newParent']) && isset($params['isUpdate']) && $params['isUpdate'] && !$localizedFields->hasDirtyLanguages()) {
  446. return;
  447. }
  448. if ($object instanceof DataObject\Fieldcollection\Data\AbstractData || $object instanceof DataObject\Objectbrick\Data\AbstractData) {
  449. $object = $object->getObject();
  450. }
  451. $localizedFields->setObject($object, false);
  452. $context = isset($params['context']) ? $params['context'] : null;
  453. $localizedFields->setContext($context);
  454. $localizedFields->loadLazyData();
  455. $localizedFields->save($params);
  456. }
  457. }
  458. /**
  459. * {@inheritdoc}
  460. */
  461. public function load($object, $params = [])
  462. {
  463. if ($object instanceof DataObject\Fieldcollection\Data\AbstractData || $object instanceof DataObject\Objectbrick\Data\AbstractData) {
  464. $object = $object->getObject();
  465. }
  466. $localizedFields = new DataObject\Localizedfield();
  467. $localizedFields->setObject($object);
  468. $context = isset($params['context']) ? $params['context'] : null;
  469. $localizedFields->setContext($context);
  470. $localizedFields->load($object, $params);
  471. $localizedFields->resetDirtyMap();
  472. $localizedFields->resetLanguageDirtyMap();
  473. return $localizedFields;
  474. }
  475. /**
  476. * {@inheritdoc}
  477. */
  478. public function delete($object, $params = [])
  479. {
  480. $localizedFields = $this->getDataFromObjectParam($object, $params);
  481. if ($localizedFields instanceof DataObject\Localizedfield) {
  482. $localizedFields->setObject($object);
  483. $context = $params['context'] ?? [];
  484. $localizedFields->setContext($context);
  485. $localizedFields->delete(true, false);
  486. }
  487. }
  488. /**
  489. * This method is called in DataObject\ClassDefinition::save() and is used to create the database table for the localized data
  490. *
  491. * @param DataObject\ClassDefinition $class
  492. * @param array $params
  493. */
  494. public function classSaved($class, $params = [])
  495. {
  496. // create a dummy instance just for updating the tables
  497. $localizedFields = new DataObject\Localizedfield();
  498. $localizedFields->setClass($class);
  499. $context = $params['context'] ?? [];
  500. $localizedFields->setContext($context);
  501. $localizedFields->createUpdateTable($params);
  502. foreach ($this->getFieldDefinitions() as $fd) {
  503. //TODO Pimcore 11 remove method_exists call
  504. if (!$fd instanceof DataContainerAwareInterface && method_exists($fd, 'classSaved')) {
  505. $fd->classSaved($class, $params);
  506. }
  507. }
  508. }
  509. /**
  510. * { @inheritdoc }
  511. */
  512. public function preGetData(/** mixed */ $container, /** array */ $params = []) // : mixed
  513. {
  514. if (
  515. !$container instanceof DataObject\Concrete &&
  516. !$container instanceof DataObject\Fieldcollection\Data\AbstractData &&
  517. !$container instanceof DataObject\Objectbrick\Data\AbstractData
  518. ) {
  519. throw new \Exception('Localized Fields are only valid in Objects, Fieldcollections and Objectbricks');
  520. }
  521. $lf = $container->getObjectVar('localizedfields');
  522. if (!$lf instanceof DataObject\Localizedfield) {
  523. $lf = new DataObject\Localizedfield();
  524. $object = $container;
  525. if ($container instanceof DataObject\Objectbrick\Data\AbstractData) {
  526. $object = $container->getObject();
  527. $context = [
  528. 'containerType' => 'objectbrick',
  529. 'containerKey' => $container->getType(),
  530. 'fieldname' => $container->getFieldname(),
  531. ];
  532. $lf->setContext($context);
  533. } elseif ($container instanceof DataObject\Fieldcollection\Data\AbstractData) {
  534. $object = $container->getObject();
  535. $context = [
  536. 'containerType' => 'fieldcollection',
  537. 'containerKey' => $container->getType(),
  538. 'fieldname' => $container->getFieldname(),
  539. ];
  540. $lf->setContext($context);
  541. } elseif ($container instanceof DataObject\Concrete) {
  542. $context = ['object' => $container];
  543. $lf->setContext($context);
  544. }
  545. $lf->setObject($object);
  546. $container->setObjectVar('localizedfields', $lf);
  547. }
  548. return $container->getObjectVar('localizedfields');
  549. }
  550. /**
  551. * {@inheritdoc}
  552. */
  553. public function getGetterCode($class)
  554. {
  555. $code = '';
  556. if (!$class instanceof DataObject\Fieldcollection\Definition) {
  557. $code .= parent::getGetterCode($class);
  558. }
  559. $fieldDefinitions = $this->getFieldDefinitions();
  560. foreach ($fieldDefinitions as $fd) {
  561. $code .= $fd->getGetterCodeLocalizedfields($class);
  562. }
  563. return $code;
  564. }
  565. /**
  566. * {@inheritdoc}
  567. */
  568. public function getSetterCode($class)
  569. {
  570. $code = '';
  571. if (!$class instanceof DataObject\Fieldcollection\Definition) {
  572. $code .= parent::getSetterCode($class);
  573. }
  574. foreach ($this->getFieldDefinitions() as $fd) {
  575. $code .= $fd->getSetterCodeLocalizedfields($class);
  576. }
  577. return $code;
  578. }
  579. /**
  580. * @param string $name
  581. * @param array $context additional contextual data
  582. *
  583. * @return DataObject\ClassDefinition\Data|null
  584. */
  585. public function getFieldDefinition($name, $context = [])
  586. {
  587. $fds = $this->getFieldDefinitions($context);
  588. if (isset($fds[$name])) {
  589. $fieldDefinition = $fds[$name];
  590. if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  591. return $fieldDefinition;
  592. }
  593. $fieldDefinition = $this->doEnrichFieldDefinition($fieldDefinition, $context);
  594. return $fieldDefinition;
  595. }
  596. return null;
  597. }
  598. /**
  599. * @param array $context additional contextual data
  600. *
  601. * @return Data[]
  602. */
  603. public function getFieldDefinitions($context = [])
  604. {
  605. if (empty($this->fieldDefinitionsCache)) {
  606. $definitions = $this->doGetFieldDefinitions();
  607. foreach ($this->getReferencedFields() as $rf) {
  608. if ($rf instanceof DataObject\ClassDefinition\Data\Localizedfields) {
  609. $definitions = array_merge($definitions, $this->doGetFieldDefinitions($rf->getChildren()));
  610. }
  611. }
  612. $this->fieldDefinitionsCache = $definitions;
  613. }
  614. if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  615. return $this->fieldDefinitionsCache;
  616. }
  617. $enrichedFieldDefinitions = [];
  618. if (is_array($this->fieldDefinitionsCache)) {
  619. foreach ($this->fieldDefinitionsCache as $key => $fieldDefinition) {
  620. $fieldDefinition = $this->doEnrichFieldDefinition($fieldDefinition, $context);
  621. $enrichedFieldDefinitions[$key] = $fieldDefinition;
  622. }
  623. }
  624. return $enrichedFieldDefinitions;
  625. }
  626. private function doEnrichFieldDefinition($fieldDefinition, $context = [])
  627. {
  628. //TODO Pimcore 11: remove method_exists BC layer
  629. if ($fieldDefinition instanceof FieldDefinitionEnrichmentInterface || method_exists($fieldDefinition, 'enrichFieldDefinition')) {
  630. if (!$fieldDefinition instanceof FieldDefinitionEnrichmentInterface) {
  631. trigger_deprecation('pimcore/pimcore', '10.1',
  632. sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  633. 'Implement the %s interface instead.', FieldDefinitionEnrichmentInterface::class));
  634. }
  635. $context['class'] = $this;
  636. $fieldDefinition = $fieldDefinition->enrichFieldDefinition($context);
  637. }
  638. return $fieldDefinition;
  639. }
  640. /**
  641. * @param mixed $def
  642. * @param array $fields
  643. *
  644. * @return array
  645. */
  646. private function doGetFieldDefinitions($def = null, $fields = [])
  647. {
  648. if ($def === null) {
  649. $def = $this->getChildren();
  650. }
  651. if (is_array($def)) {
  652. foreach ($def as $child) {
  653. $fields = array_merge($fields, $this->doGetFieldDefinitions($child, $fields));
  654. }
  655. }
  656. if ($def instanceof DataObject\ClassDefinition\Layout) {
  657. if ($def->hasChildren()) {
  658. foreach ($def->getChildren() as $child) {
  659. $fields = array_merge($fields, $this->doGetFieldDefinitions($child, $fields));
  660. }
  661. }
  662. }
  663. if ($def instanceof DataObject\ClassDefinition\Data) {
  664. $fields[$def->getName()] = $def;
  665. }
  666. return $fields;
  667. }
  668. /**
  669. * {@inheritdoc}
  670. */
  671. public function getCacheTags($data, array $tags = [])
  672. {
  673. if (!$data instanceof DataObject\Localizedfield) {
  674. return $tags;
  675. }
  676. foreach ($data->getInternalData(true) as $language => $values) {
  677. foreach ($this->getFieldDefinitions() as $fd) {
  678. if (isset($values[$fd->getName()])) {
  679. $tags = $fd->getCacheTags($values[$fd->getName()], $tags);
  680. }
  681. }
  682. }
  683. return $tags;
  684. }
  685. /**
  686. * @param DataObject\Localizedfield|null $data
  687. *
  688. * @return array
  689. */
  690. public function resolveDependencies($data)
  691. {
  692. $dependencies = [];
  693. if (!$data instanceof DataObject\Localizedfield) {
  694. return [];
  695. }
  696. foreach ($data->getInternalData(true) as $language => $values) {
  697. foreach ($this->getFieldDefinitions() as $fd) {
  698. if (isset($values[$fd->getName()])) {
  699. $dependencies = array_merge($dependencies, $fd->resolveDependencies($values[$fd->getName()]));
  700. }
  701. }
  702. }
  703. return $dependencies;
  704. }
  705. /**
  706. * @param string|int $height
  707. *
  708. * @return $this
  709. */
  710. public function setHeight($height)
  711. {
  712. if (is_numeric($height)) {
  713. $height = (int)$height;
  714. }
  715. $this->height = $height;
  716. return $this;
  717. }
  718. /**
  719. * @return string|int
  720. */
  721. public function getHeight()
  722. {
  723. return $this->height;
  724. }
  725. /**
  726. * @param mixed $layout
  727. *
  728. * @return $this
  729. */
  730. public function setLayout($layout)
  731. {
  732. $this->layout = $layout;
  733. return $this;
  734. }
  735. /**
  736. * @return string
  737. */
  738. public function getLayout()
  739. {
  740. return $this->layout;
  741. }
  742. /**
  743. * @return bool
  744. */
  745. public function getBorder(): bool
  746. {
  747. return $this->border;
  748. }
  749. /**
  750. * @param bool $border
  751. */
  752. public function setBorder(bool $border): void
  753. {
  754. $this->border = $border;
  755. }
  756. /**
  757. * @param string $name
  758. *
  759. * @return $this|Data
  760. *
  761. * @throws \Exception
  762. */
  763. public function setName($name)
  764. {
  765. if ($name !== 'localizedfields') {
  766. throw new \Exception('Localizedfields can only be named `localizedfields`, no other names are allowed');
  767. }
  768. $this->name = $name;
  769. return $this;
  770. }
  771. /**
  772. * @param string|null $region
  773. *
  774. * @return $this
  775. */
  776. public function setRegion($region)
  777. {
  778. $this->region = $region;
  779. return $this;
  780. }
  781. /**
  782. * @return string
  783. */
  784. public function getRegion()
  785. {
  786. return $this->region;
  787. }
  788. /**
  789. * @param int|string $width
  790. *
  791. * @return $this
  792. */
  793. public function setWidth($width)
  794. {
  795. if (is_numeric($width)) {
  796. $width = (int)$width;
  797. }
  798. $this->width = $width;
  799. return $this;
  800. }
  801. /**
  802. * @return int|string
  803. */
  804. public function getWidth()
  805. {
  806. return $this->width;
  807. }
  808. /**
  809. * {@inheritdoc}
  810. */
  811. public function checkValidity($data, $omitMandatoryCheck = false, $params = [])
  812. {
  813. $config = \Pimcore\Config::getSystemConfiguration('general');
  814. $languages = [];
  815. if (isset($config['valid_languages'])) {
  816. $languages = explode(',', $config['valid_languages']);
  817. }
  818. $dataForValidityCheck = $this->getDataForValidity($data, $languages);
  819. $validationExceptions = [];
  820. if (!$omitMandatoryCheck) {
  821. foreach ($languages as $language) {
  822. foreach ($this->getFieldDefinitions() as $fd) {
  823. try {
  824. try {
  825. if (isset($dataForValidityCheck[$language]) && isset($dataForValidityCheck[$language][$fd->getName()])) {
  826. $fd->checkValidity($dataForValidityCheck[$language][$fd->getName()], false, $params);
  827. } else {
  828. $fd->checkValidity(null, false, $params);
  829. }
  830. } catch (\Exception $e) {
  831. if ($data->getObject()->getClass()->getAllowInherit()) {
  832. //try again with parent data when inheritance is activated
  833. try {
  834. $getInheritedValues = DataObject::doGetInheritedValues();
  835. DataObject::setGetInheritedValues(true);
  836. $value = null;
  837. $context = $data->getContext();
  838. $containerType = $context['containerType'] ?? null;
  839. if ($containerType === 'objectbrick') {
  840. $brickContainer = $data->getObject()->{'get' . ucfirst($context['fieldname'])}();
  841. $brick = $brickContainer->{'get' . ucfirst($context['containerKey'])}();
  842. if ($brick) {
  843. $value = $brick->{'get' . ucfirst($fd->getName())}($language);
  844. }
  845. } elseif ($containerType === null || $containerType === 'object') {
  846. $getter = 'get' . ucfirst($fd->getName());
  847. $value = $data->getObject()->$getter($language);
  848. }
  849. $fd->checkValidity($value, $omitMandatoryCheck, $params);
  850. DataObject::setGetInheritedValues($getInheritedValues);
  851. } catch (\Exception $e) {
  852. if (!$e instanceof Model\Element\ValidationException) {
  853. throw $e;
  854. }
  855. $exceptionClass = get_class($e);
  856. throw new $exceptionClass($e->getMessage() . ' fieldname=' . $fd->getName(), $e->getCode(), $e->getPrevious());
  857. }
  858. } else {
  859. if ($e instanceof Model\Element\ValidationException) {
  860. throw $e;
  861. }
  862. $exceptionClass = get_class($e);
  863. throw new $exceptionClass($e->getMessage() . ' fieldname=' . $fd->getName(), $e->getCode(), $e);
  864. }
  865. }
  866. } catch (Model\Element\ValidationException $ve) {
  867. $ve->addContext($this->getName() . '-' . $language);
  868. $validationExceptions[] = $ve;
  869. }
  870. }
  871. }
  872. }
  873. if (count($validationExceptions) > 0) {
  874. $aggregatedExceptions = new Model\Element\ValidationException();
  875. $aggregatedExceptions->setSubItems($validationExceptions);
  876. throw $aggregatedExceptions;
  877. }
  878. }
  879. /**
  880. * @param DataObject\Localizedfield|mixed $localizedObject
  881. * @param array $languages
  882. *
  883. * @return array
  884. */
  885. private function getDataForValidity($localizedObject, array $languages)
  886. {
  887. if (!$localizedObject->getObject()
  888. || $localizedObject->getObject()->getType() != DataObject::OBJECT_TYPE_VARIANT
  889. || !$localizedObject instanceof DataObject\Localizedfield) {
  890. return $localizedObject->getInternalData(true);
  891. }
  892. //prepare data for variants
  893. $data = [];
  894. foreach ($languages as $language) {
  895. $data[$language] = [];
  896. foreach ($this->getFieldDefinitions() as $fd) {
  897. $data[$language][$fd->getName()] = $localizedObject->getLocalizedValue($fd->getName(), $language);
  898. }
  899. }
  900. return $data;
  901. }
  902. /**
  903. * @param mixed $data
  904. * @param DataObject\Concrete|null $object
  905. * @param mixed $params
  906. *
  907. * @return array|null
  908. */
  909. public function getDiffDataForEditmode($data, $object = null, $params = [])
  910. {
  911. $return = [];
  912. $myname = $this->getName();
  913. if (!$data instanceof DataObject\Localizedfield) {
  914. return [];
  915. }
  916. foreach ($data->getInternalData(true) as $language => $values) {
  917. foreach ($this->getFieldDefinitions() as $fd) {
  918. $fieldname = $fd->getName();
  919. $subdata = $fd->getDiffDataForEditmode($values[$fieldname], $object, $params);
  920. foreach ($subdata as $item) {
  921. $diffdata['field'] = $this->getName();
  922. $diffdata['key'] = $this->getName().'~'.$fieldname.'~'.$item['key'].'~'.$language;
  923. $diffdata['type'] = $item['type'];
  924. $diffdata['value'] = $item['value'];
  925. // this is not needed anymoe
  926. unset($item['type']);
  927. unset($item['value']);
  928. $diffdata['title'] = $this->getName().' / '.$item['title'];
  929. $diffdata['lang'] = $language;
  930. $diffdata['data'] = $item;
  931. $diffdata['extData'] = [
  932. 'fieldname' => $fieldname,
  933. ];
  934. $diffdata['disabled'] = $item['disabled'];
  935. $return[] = $diffdata;
  936. }
  937. }
  938. }
  939. return $return;
  940. }
  941. /**
  942. * @param array $data
  943. * @param DataObject\Concrete|null $object
  944. * @param mixed $params
  945. *
  946. * @return mixed
  947. */
  948. public function getDiffDataFromEditmode($data, $object = null, $params = [])
  949. {
  950. $localFields = $this->getDataFromObjectParam($object, $params);
  951. $localData = [];
  952. // get existing data
  953. if ($localFields instanceof DataObject\Localizedfield) {
  954. $localData = $localFields->getInternalData(true);
  955. }
  956. $mapping = [];
  957. foreach ($data as $item) {
  958. $extData = $item['extData'];
  959. $fieldname = $extData['fieldname'];
  960. $language = $item['lang'];
  961. $values = $mapping[$fieldname] ?? [];
  962. $itemdata = $item['data'];
  963. if ($itemdata) {
  964. $values[] = $itemdata;
  965. }
  966. $mapping[$language][$fieldname] = $values;
  967. }
  968. foreach ($mapping as $language => $fields) {
  969. foreach ($fields as $key => $value) {
  970. $fd = $this->getFieldDefinition($key);
  971. if ($fd && $fd->isDiffChangeAllowed($object)) {
  972. if ($value == null) {
  973. unset($localData[$language][$key]);
  974. } else {
  975. $localData[$language][$key] = $fd->getDiffDataFromEditmode($value);
  976. }
  977. }
  978. }
  979. }
  980. $localizedFields = new DataObject\Localizedfield($localData);
  981. $localizedFields->setObject($object);
  982. return $localizedFields;
  983. }
  984. /**
  985. * {@inheritdoc}
  986. */
  987. public function isDiffChangeAllowed($object, $params = [])
  988. {
  989. return true;
  990. }
  991. /**
  992. * @return array
  993. */
  994. public function __sleep()
  995. {
  996. $vars = get_object_vars($this);
  997. $blockedVars = [
  998. 'fieldDefinitionsCache',
  999. 'referencedFields',
  1000. 'blockedVarsForExport',
  1001. 'permissionView',
  1002. 'permissionEdit',
  1003. ];
  1004. foreach ($blockedVars as $blockedVar) {
  1005. unset($vars[$blockedVar]);
  1006. }
  1007. return array_keys($vars);
  1008. }
  1009. /**
  1010. * { @inheritdoc }
  1011. */
  1012. public function rewriteIds(/** mixed */ $container, /** array */ $idMapping, /** array */ $params = []) /** :mixed */
  1013. {
  1014. $data = $this->getDataFromObjectParam($container, $params);
  1015. $validLanguages = Tool::getValidLanguages();
  1016. foreach ($validLanguages as $language) {
  1017. foreach ($this->getFieldDefinitions() as $fd) {
  1018. //TODO Pimcore 11: remove method_exists BC layer
  1019. if ($fd instanceof IdRewriterInterface || method_exists($fd, 'rewriteIds')) {
  1020. if (!$fd instanceof IdRewriterInterface) {
  1021. trigger_deprecation('pimcore/pimcore', '10.1',
  1022. sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  1023. 'Implement the %s interface instead.', IdRewriterInterface::class));
  1024. }
  1025. $d = $fd->rewriteIds($data, $idMapping, ['language' => $language]);
  1026. $data->setLocalizedValue($fd->getName(), $d, $language);
  1027. }
  1028. }
  1029. }
  1030. return $data;
  1031. }
  1032. /**
  1033. * @return int
  1034. */
  1035. public function getHideLabelsWhenTabsReached()
  1036. {
  1037. return $this->hideLabelsWhenTabsReached;
  1038. }
  1039. /**
  1040. * @param int $hideLabelsWhenTabsReached
  1041. *
  1042. * @return $this
  1043. */
  1044. public function setHideLabelsWhenTabsReached($hideLabelsWhenTabsReached)
  1045. {
  1046. $this->hideLabelsWhenTabsReached = $hideLabelsWhenTabsReached;
  1047. return $this;
  1048. }
  1049. /**
  1050. * @param int $maxTabs
  1051. */
  1052. public function setMaxTabs($maxTabs)
  1053. {
  1054. $this->maxTabs = $maxTabs;
  1055. }
  1056. /**
  1057. * @return int
  1058. */
  1059. public function getMaxTabs()
  1060. {
  1061. return $this->maxTabs;
  1062. }
  1063. /**
  1064. * @param int $labelWidth
  1065. */
  1066. public function setLabelWidth($labelWidth)
  1067. {
  1068. $this->labelWidth = (int)$labelWidth;
  1069. }
  1070. /**
  1071. * @return int
  1072. */
  1073. public function getLabelWidth()
  1074. {
  1075. return $this->labelWidth;
  1076. }
  1077. /**
  1078. * @return array|null
  1079. */
  1080. public function getPermissionView(): ?array
  1081. {
  1082. return $this->permissionView;
  1083. }
  1084. /**
  1085. * @param array|null $permissionView
  1086. */
  1087. public function setPermissionView($permissionView): void
  1088. {
  1089. $this->permissionView = $permissionView;
  1090. }
  1091. /**
  1092. * @return array|null
  1093. */
  1094. public function getPermissionEdit(): ?array
  1095. {
  1096. return $this->permissionEdit;
  1097. }
  1098. /**
  1099. * @param array|null $permissionEdit
  1100. */
  1101. public function setPermissionEdit($permissionEdit): void
  1102. {
  1103. $this->permissionEdit = $permissionEdit;
  1104. }
  1105. /**
  1106. * @return bool
  1107. */
  1108. public function getProvideSplitView()
  1109. {
  1110. return $this->provideSplitView;
  1111. }
  1112. /**
  1113. * @param bool $provideSplitView
  1114. */
  1115. public function setProvideSplitView($provideSplitView): void
  1116. {
  1117. $this->provideSplitView = $provideSplitView;
  1118. }
  1119. /**
  1120. * {@inheritdoc}
  1121. */
  1122. public function supportsDirtyDetection()
  1123. {
  1124. return true;
  1125. }
  1126. /**
  1127. * {@inheritdoc}
  1128. */
  1129. public function isFilterable(): bool
  1130. {
  1131. return true;
  1132. }
  1133. /**
  1134. * @return string
  1135. */
  1136. public function getTabPosition(): string
  1137. {
  1138. return $this->tabPosition;
  1139. }
  1140. /**
  1141. * @param string $tabPosition
  1142. */
  1143. public function setTabPosition($tabPosition): void
  1144. {
  1145. $this->tabPosition = $tabPosition;
  1146. }
  1147. /**
  1148. * {@inheritdoc}
  1149. */
  1150. public function getParameterTypeDeclaration(): ?string
  1151. {
  1152. return '?\\' . DataObject\Localizedfield::class;
  1153. }
  1154. /**
  1155. * {@inheritdoc}
  1156. */
  1157. public function getReturnTypeDeclaration(): ?string
  1158. {
  1159. return '?\\' . DataObject\Localizedfield::class;
  1160. }
  1161. /**
  1162. * {@inheritdoc}
  1163. */
  1164. public function getPhpdocInputType(): ?string
  1165. {
  1166. return '\\'. DataObject\Localizedfield::class . '|null';
  1167. }
  1168. /**
  1169. * {@inheritdoc}
  1170. */
  1171. public function getPhpdocReturnType(): ?string
  1172. {
  1173. return '\\' . DataObject\Localizedfield::class . '|null';
  1174. }
  1175. /**
  1176. * {@inheritdoc}
  1177. */
  1178. public function normalize($value, $params = [])
  1179. {
  1180. if ($value instanceof DataObject\Localizedfield) {
  1181. $items = $value->getInternalData();
  1182. if (is_array($items)) {
  1183. $result = [];
  1184. foreach ($items as $language => $languageData) {
  1185. $languageResult = [];
  1186. foreach ($languageData as $elementName => $elementData) {
  1187. $fd = $this->getFieldDefinition($elementName);
  1188. if (!$fd) {
  1189. // class definition seems to have changed
  1190. Logger::warn('class definition seems to have changed, element name: '.$elementName);
  1191. continue;
  1192. }
  1193. if ($fd instanceof NormalizerInterface) {
  1194. $dataForResource = $fd->normalize($elementData, $params);
  1195. $languageResult[$elementName] = $dataForResource;
  1196. }
  1197. }
  1198. $result[$language] = $languageResult;
  1199. }
  1200. return $result;
  1201. }
  1202. }
  1203. return null;
  1204. }
  1205. /**
  1206. * {@inheritdoc}
  1207. */
  1208. public function denormalize($value, $params = [])
  1209. {
  1210. if (is_array($value)) {
  1211. $lf = new DataObject\Localizedfield();
  1212. $lf->setObject($params['object']);
  1213. $items = [];
  1214. foreach ($value as $language => $languageData) {
  1215. $languageResult = [];
  1216. foreach ($languageData as $elementName => $elementData) {
  1217. $fd = $this->getFieldDefinition($elementName);
  1218. if (!$fd) {
  1219. // class definition seems to have changed
  1220. Logger::warn('class definition seems to have changed, element name: '.$elementName);
  1221. continue;
  1222. }
  1223. if ($fd instanceof NormalizerInterface) {
  1224. $dataFromResource = $fd->denormalize($elementData, $params);
  1225. $languageResult[$elementName] = $dataFromResource;
  1226. }
  1227. }
  1228. $items[$language] = $languageResult;
  1229. }
  1230. $lf->setItems($items);
  1231. return $lf;
  1232. }
  1233. return null;
  1234. }
  1235. }