vendor/pimcore/pimcore/models/Element/AbstractElement.php line 428

Open in your IDE?
  1. <?php
  2. /**
  3. * Pimcore
  4. *
  5. * This source file is available under two different licenses:
  6. * - GNU General Public License version 3 (GPLv3)
  7. * - Pimcore Commercial License (PCL)
  8. * Full copyright and license information is available in
  9. * LICENSE.md which is distributed with this source code.
  10. *
  11. * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12. * @license http://www.pimcore.org/license GPLv3 and PCL
  13. */
  14. namespace Pimcore\Model\Element;
  15. use Pimcore\Cache;
  16. use Pimcore\Cache\RuntimeCache;
  17. use Pimcore\Event\AdminEvents;
  18. use Pimcore\Event\Model\ElementEvent;
  19. use Pimcore\Event\Traits\RecursionBlockingEventDispatchHelperTrait;
  20. use Pimcore\Model;
  21. use Pimcore\Model\Element\Traits\DirtyIndicatorTrait;
  22. use Pimcore\Model\User;
  23. /**
  24. * @method Model\Document\Dao|Model\Asset\Dao|Model\DataObject\AbstractObject\Dao getDao()
  25. */
  26. abstract class AbstractElement extends Model\AbstractModel implements ElementInterface, ElementDumpStateInterface, DirtyIndicatorInterface
  27. {
  28. use ElementDumpStateTrait;
  29. use DirtyIndicatorTrait;
  30. use RecursionBlockingEventDispatchHelperTrait;
  31. /**
  32. * @internal
  33. *
  34. * @var Model\Dependency|null
  35. */
  36. protected $dependencies;
  37. /**
  38. * @internal
  39. *
  40. * @var int
  41. */
  42. protected $__dataVersionTimestamp = null;
  43. /**
  44. * @internal
  45. *
  46. * @var string|null
  47. */
  48. protected $path;
  49. /**
  50. * @internal
  51. *
  52. * @var array|null
  53. */
  54. protected ?array $properties = null;
  55. /**
  56. * @internal
  57. *
  58. * @var bool
  59. */
  60. public static $doNotRestoreKeyAndPath = false;
  61. /**
  62. * @internal
  63. *
  64. * @var int|null
  65. */
  66. protected ?int $id = null;
  67. /**
  68. * @return string|null
  69. */
  70. public function getPath()
  71. {
  72. return $this->path;
  73. }
  74. /**
  75. * @param string $path
  76. *
  77. * @return $this
  78. */
  79. public function setPath($path)
  80. {
  81. $this->path = (string) $path;
  82. return $this;
  83. }
  84. /**
  85. * @internal
  86. *
  87. * @var int|null
  88. */
  89. protected ?int $creationDate = null;
  90. /**
  91. * @internal
  92. *
  93. * @var int|null
  94. */
  95. protected $modificationDate;
  96. /**
  97. * @internal
  98. *
  99. * @var int
  100. */
  101. protected $versionCount = 0;
  102. /**
  103. * @internal
  104. *
  105. * @var int|null
  106. */
  107. protected ?int $userOwner = null;
  108. /**
  109. * @internal
  110. *
  111. * @var string|null
  112. */
  113. protected ?string $locked = null;
  114. /**
  115. * @internal
  116. *
  117. * @var int|null
  118. */
  119. protected ?int $userModification = null;
  120. /**
  121. * @internal
  122. *
  123. * @var int|null
  124. */
  125. protected ?int $parentId = null;
  126. /**
  127. * @return int|null
  128. */
  129. public function getParentId()
  130. {
  131. return $this->parentId;
  132. }
  133. /**
  134. * @param int $parentId
  135. *
  136. * @return $this
  137. */
  138. public function setParentId($parentId)
  139. {
  140. $parentId = (int) $parentId;
  141. $this->parentId = $parentId;
  142. $this->parent = null;
  143. return $this;
  144. }
  145. /**
  146. * @return int|null
  147. */
  148. public function getUserModification()
  149. {
  150. return $this->userModification;
  151. }
  152. /**
  153. * @param int $userModification
  154. *
  155. * @return $this
  156. */
  157. public function setUserModification($userModification)
  158. {
  159. $this->markFieldDirty('userModification');
  160. $this->userModification = (int) $userModification;
  161. return $this;
  162. }
  163. /**
  164. * @return int|null
  165. */
  166. public function getCreationDate()
  167. {
  168. return $this->creationDate;
  169. }
  170. /**
  171. * @param int $creationDate
  172. *
  173. * @return $this
  174. */
  175. public function setCreationDate($creationDate)
  176. {
  177. $this->creationDate = (int) $creationDate;
  178. return $this;
  179. }
  180. /**
  181. * @return int|null
  182. */
  183. public function getModificationDate()
  184. {
  185. return $this->modificationDate;
  186. }
  187. /**
  188. * @param int $modificationDate
  189. *
  190. * @return $this
  191. */
  192. public function setModificationDate($modificationDate)
  193. {
  194. $this->markFieldDirty('modificationDate');
  195. $this->modificationDate = (int) $modificationDate;
  196. return $this;
  197. }
  198. /**
  199. * @return int|null
  200. */
  201. public function getUserOwner()
  202. {
  203. return $this->userOwner;
  204. }
  205. /**
  206. * @param int $userOwner
  207. *
  208. * @return $this
  209. */
  210. public function setUserOwner($userOwner)
  211. {
  212. $this->userOwner = (int) $userOwner;
  213. return $this;
  214. }
  215. /**
  216. * enum('self','propagate') nullable
  217. *
  218. * @return string|null
  219. */
  220. public function getLocked()
  221. {
  222. if (empty($this->locked)) {
  223. return null;
  224. }
  225. return $this->locked;
  226. }
  227. /**
  228. * enum('self','propagate') nullable
  229. *
  230. * @param string|null $locked
  231. *
  232. * @return $this
  233. */
  234. public function setLocked($locked)
  235. {
  236. $this->locked = $locked;
  237. return $this;
  238. }
  239. /**
  240. * @return int|null
  241. */
  242. public function getId()
  243. {
  244. return $this->id;
  245. }
  246. /**
  247. * @param int|null $id
  248. *
  249. * @return $this
  250. */
  251. public function setId($id)
  252. {
  253. $this->id = $id ? (int)$id : null;
  254. return $this;
  255. }
  256. /**
  257. * @var self|null
  258. */
  259. protected $parent = null;
  260. /**
  261. * @return self|null
  262. */
  263. public function getParent()
  264. {
  265. if ($this->parent === null) {
  266. $parent = Service::getElementById(Service::getElementType($this), $this->getParentId());
  267. $this->setParent($parent);
  268. }
  269. return $this->parent;
  270. }
  271. /**
  272. * @return Model\Property[]
  273. */
  274. public function getProperties()
  275. {
  276. $type = Service::getElementType($this);
  277. if ($this->properties === null) {
  278. // try to get from cache
  279. $cacheKey = $type . '_properties_' . $this->getId();
  280. $properties = Cache::load($cacheKey);
  281. if (!is_array($properties)) {
  282. $properties = $this->getDao()->getProperties();
  283. $elementCacheTag = $this->getCacheTag();
  284. $cacheTags = [$type . '_properties' => $type . '_properties', $elementCacheTag => $elementCacheTag];
  285. Cache::save($properties, $cacheKey, $cacheTags);
  286. }
  287. $this->setProperties($properties);
  288. }
  289. return $this->properties;
  290. }
  291. /**
  292. * {@inheritdoc}
  293. */
  294. public function setProperties(?array $properties)
  295. {
  296. $this->properties = $properties;
  297. return $this;
  298. }
  299. /**
  300. * @param string $name
  301. * @param string $type
  302. * @param mixed $data
  303. * @param bool $inherited
  304. * @param bool $inheritable
  305. *
  306. * @return $this
  307. */
  308. public function setProperty($name, $type, $data, $inherited = false, $inheritable = false)
  309. {
  310. $this->getProperties();
  311. $property = new Model\Property();
  312. $property->setType($type);
  313. $property->setCid($this->getId());
  314. $property->setName($name);
  315. $property->setCtype(Service::getElementType($this));
  316. $property->setData($data);
  317. $property->setInherited($inherited);
  318. $property->setInheritable($inheritable);
  319. $this->properties[$name] = $property;
  320. return $this;
  321. }
  322. /**
  323. * @internal
  324. */
  325. protected function updateModificationInfos()
  326. {
  327. if (Model\Version::isEnabled() === true) {
  328. $this->setVersionCount($this->getDao()->getVersionCountForUpdate() + 1);
  329. }
  330. if ($this->getVersionCount() > 4200000000) {
  331. $this->setVersionCount(1);
  332. }
  333. $modificationDateKey = 'modificationDate';
  334. if (!$this->isFieldDirty($modificationDateKey)) {
  335. $updateTime = time();
  336. $this->setModificationDate($updateTime);
  337. }
  338. if (!$this->getCreationDate()) {
  339. $this->setCreationDate($this->getModificationDate());
  340. }
  341. // auto assign user if possible, if not changed explicitly, if no user present, use ID=0 which represents the "system" user
  342. $userModificationKey = 'userModification';
  343. if (!$this->isFieldDirty($userModificationKey)) {
  344. $userId = 0;
  345. $user = \Pimcore\Tool\Admin::getCurrentUser();
  346. if ($user instanceof User) {
  347. $userId = $user->getId();
  348. }
  349. $this->setUserModification($userId);
  350. }
  351. if ($this->getUserOwner() === null) {
  352. $this->setUserOwner($this->getUserModification());
  353. }
  354. }
  355. /**
  356. * {@inheritdoc}
  357. */
  358. public function getProperty($name, $asContainer = false)
  359. {
  360. $properties = $this->getProperties();
  361. if ($this->hasProperty($name)) {
  362. if ($asContainer) {
  363. return $properties[$name];
  364. } else {
  365. return $properties[$name]->getData();
  366. }
  367. }
  368. return null;
  369. }
  370. /**
  371. * {@inheritdoc}
  372. */
  373. public function hasProperty($name)
  374. {
  375. $properties = $this->getProperties();
  376. return array_key_exists($name, $properties);
  377. }
  378. /**
  379. * @param string $name
  380. */
  381. public function removeProperty($name)
  382. {
  383. $properties = $this->getProperties();
  384. unset($properties[$name]);
  385. $this->setProperties($properties);
  386. }
  387. /**
  388. * @return int
  389. */
  390. public function getVersionCount(): int
  391. {
  392. return $this->versionCount ? $this->versionCount : 0;
  393. }
  394. /**
  395. * @param int|null $versionCount
  396. *
  397. * @return $this
  398. */
  399. public function setVersionCount(?int $versionCount): ElementInterface
  400. {
  401. $this->versionCount = (int) $versionCount;
  402. return $this;
  403. }
  404. /**
  405. * {@inheritdoc}
  406. */
  407. public function getCacheTag()
  408. {
  409. $elementType = Service::getElementType($this);
  410. return Service::getElementCacheTag($elementType, $this->getId());
  411. }
  412. /**
  413. * @internal
  414. *
  415. * @param string|int $id
  416. *
  417. * @return string
  418. */
  419. protected static function getCacheKey($id): string
  420. {
  421. $elementType = Service::getElementTypeByClassName(static::class);
  422. return Service::getElementCacheTag($elementType, $id);
  423. }
  424. /**
  425. * {@inheritdoc}
  426. */
  427. public function getCacheTags(array $tags = []): array
  428. {
  429. $tags[$this->getCacheTag()] = $this->getCacheTag();
  430. return $tags;
  431. }
  432. /**
  433. * Resolves the dependencies of the element and returns an array of them - Used by update()
  434. *
  435. * @internal
  436. *
  437. * @return array
  438. */
  439. protected function resolveDependencies(): array
  440. {
  441. $dependencies = [[]];
  442. // check for properties
  443. if (method_exists($this, 'getProperties')) {
  444. foreach ($this->getProperties() as $property) {
  445. $dependencies[] = $property->resolveDependencies();
  446. }
  447. }
  448. return array_merge(...$dependencies);
  449. }
  450. /**
  451. * {@inheritdoc}
  452. */
  453. public function isLocked()
  454. {
  455. if ($this->getLocked()) {
  456. return true;
  457. }
  458. // check for inherited
  459. return $this->getDao()->isLocked();
  460. }
  461. /**
  462. * @param User|null $user
  463. *
  464. * @return array
  465. *
  466. * @throws \Exception
  467. *
  468. * @internal
  469. */
  470. public function getUserPermissions(?User $user = null)
  471. {
  472. $baseClass = Service::getBaseClassNameForElement($this);
  473. $workspaceClass = '\\Pimcore\\Model\\User\\Workspace\\' . $baseClass;
  474. /** @var Model\AbstractModel $dummy */
  475. $dummy = new $workspaceClass();
  476. $vars = $dummy->getObjectVars();
  477. $ignored = ['userId', 'cid', 'cpath', 'dao'];
  478. $permissions = [];
  479. $columns = array_diff(array_keys($vars), $ignored);
  480. $defaultValue = 0;
  481. if (null === $user) {
  482. $user = \Pimcore\Tool\Admin::getCurrentUser();
  483. }
  484. if ((!$user && php_sapi_name() === 'cli') || $user?->isAdmin()) {
  485. $defaultValue = 1;
  486. }
  487. foreach ($columns as $name) {
  488. $permissions[$name] = $defaultValue;
  489. }
  490. if (!$user || $user->isAdmin() || !$user->isAllowed(Service::getElementType($this) . 's')) {
  491. return $permissions;
  492. }
  493. $permissions = $this->getDao()->areAllowed($columns, $user);
  494. foreach ($permissions as $type => $isAllowed) {
  495. $event = new ElementEvent($this, ['isAllowed' => $isAllowed, 'permissionType' => $type, 'user' => $user]);
  496. \Pimcore::getEventDispatcher()->dispatch($event, AdminEvents::ELEMENT_PERMISSION_IS_ALLOWED);
  497. $permissions[$type] = $event->getArgument('isAllowed');
  498. }
  499. return $permissions;
  500. }
  501. /**
  502. * {@inheritdoc}
  503. */
  504. public function isAllowed($type, ?User $user = null)
  505. {
  506. if (null === $user) {
  507. $user = \Pimcore\Tool\Admin::getCurrentUser();
  508. }
  509. if (!$user) {
  510. if (php_sapi_name() === 'cli') {
  511. return true;
  512. }
  513. return false;
  514. }
  515. //everything is allowed for admin
  516. if ($user->isAdmin()) {
  517. return true;
  518. }
  519. if (!$user->isAllowed(Service::getElementType($this) . 's')) {
  520. return false;
  521. }
  522. $isAllowed = $this->getDao()->isAllowed($type, $user);
  523. $event = new ElementEvent($this, ['isAllowed' => $isAllowed, 'permissionType' => $type, 'user' => $user]);
  524. \Pimcore::getEventDispatcher()->dispatch($event, AdminEvents::ELEMENT_PERMISSION_IS_ALLOWED);
  525. return (bool) $event->getArgument('isAllowed');
  526. }
  527. /**
  528. * @internal
  529. */
  530. public function unlockPropagate()
  531. {
  532. $type = Service::getElementType($this);
  533. $ids = $this->getDao()->unlockPropagate();
  534. // invalidate cache items
  535. foreach ($ids as $id) {
  536. $element = Service::getElementById($type, $id);
  537. if ($element) {
  538. $element->clearDependentCache();
  539. }
  540. }
  541. }
  542. /**
  543. * @internal
  544. *
  545. * @throws \Exception
  546. */
  547. protected function validatePathLength()
  548. {
  549. if (mb_strlen($this->getRealFullPath()) > 765) {
  550. throw new \Exception("Full path is limited to 765 characters, reduce the length of your parent's path");
  551. }
  552. }
  553. /**
  554. * {@inheritdoc}
  555. */
  556. public function __toString()
  557. {
  558. return $this->getFullPath();
  559. }
  560. /**
  561. * @return int
  562. */
  563. public function __getDataVersionTimestamp()
  564. {
  565. return $this->__dataVersionTimestamp;
  566. }
  567. /**
  568. * @param int $_dataVersionTimestamp
  569. */
  570. public function __setDataVersionTimestamp($_dataVersionTimestamp)
  571. {
  572. $this->__dataVersionTimestamp = $_dataVersionTimestamp;
  573. }
  574. /**
  575. * {@inheritdoc}
  576. */
  577. public function __isBasedOnLatestData()
  578. {
  579. return $this->getDao()->__isBasedOnLatestData();
  580. }
  581. /**
  582. * @internal
  583. *
  584. * @param string|null $versionNote
  585. * @param bool $saveOnlyVersion
  586. * @param bool $saveStackTrace
  587. * @param bool $isAutoSave
  588. *
  589. * @return Model\Version
  590. *
  591. * @throws \Exception
  592. */
  593. protected function doSaveVersion($versionNote = null, $saveOnlyVersion = true, $saveStackTrace = true, $isAutoSave = false)
  594. {
  595. $version = null;
  596. if ($isAutoSave) {
  597. $list = new Model\Version\Listing();
  598. $list->setLoadAutoSave(true);
  599. $list->setCondition('autoSave = 1 AND cid = ? AND cType = ? AND userId = ? ', [$this->getId(), Service::getElementType($this), $this->getUserModification()]);
  600. $version = $list->current();
  601. }
  602. if (!$version) {
  603. /** @var Model\Version $version */
  604. $version = self::getModelFactory()->build(Model\Version::class);
  605. }
  606. $version->setCid($this->getId());
  607. $version->setCtype(Service::getElementType($this));
  608. $version->setDate($this->getModificationDate());
  609. $version->setUserId($this->getUserModification());
  610. $version->setData($this);
  611. $version->setNote($versionNote);
  612. $version->setGenerateStackTrace($saveStackTrace);
  613. $version->setAutoSave($isAutoSave);
  614. if ($saveOnlyVersion) {
  615. $versionCount = $this->getDao()->getVersionCountForUpdate();
  616. $versionCount++;
  617. } else {
  618. $versionCount = $this->getVersionCount();
  619. }
  620. $version->setVersionCount($versionCount);
  621. $version->save();
  622. return $version;
  623. }
  624. /**
  625. * {@inheritdoc}
  626. */
  627. public function getDependencies()
  628. {
  629. if (!$this->dependencies) {
  630. $this->dependencies = Model\Dependency::getBySourceId($this->getId(), Service::getElementType($this));
  631. }
  632. return $this->dependencies;
  633. }
  634. /**
  635. * {@inheritdoc}
  636. */
  637. public function getScheduledTasks()
  638. {
  639. return [];
  640. }
  641. /**
  642. * {@inheritdoc}
  643. */
  644. public function getVersions()
  645. {
  646. return [];
  647. }
  648. /**
  649. * @internal
  650. *
  651. * @return string[]
  652. */
  653. protected function getBlockedVars(): array
  654. {
  655. return ['dependencies', 'parent'];
  656. }
  657. /**
  658. * {@inheritdoc}
  659. */
  660. public function __sleep()
  661. {
  662. if ($this->isInDumpState()) {
  663. // this is if we want to make a full dump of the object (eg. for a new version), including children for recyclebin
  664. $this->removeInheritedProperties();
  665. }
  666. return array_diff(parent::__sleep(), $this->getBlockedVars());
  667. }
  668. public function __wakeup()
  669. {
  670. if ($this->isInDumpState()) {
  671. // set current key and path this is necessary because the serialized data can have a different path than the original element ( element was renamed or moved )
  672. $originalElement = static::getById($this->getId());
  673. if ($originalElement && !self::$doNotRestoreKeyAndPath) {
  674. // set key and path for DataObject and Document (assets have different wakeup call)
  675. $this->setKey($originalElement->getKey());
  676. $this->setPath($originalElement->getRealPath());
  677. }
  678. }
  679. if ($this->isInDumpState() && $this->properties !== null) {
  680. $this->renewInheritedProperties();
  681. }
  682. $this->setInDumpState(false);
  683. }
  684. public function __clone()
  685. {
  686. parent::__clone();
  687. $this->dependencies = null;
  688. }
  689. /**
  690. * @internal
  691. *
  692. * @param int $userId
  693. */
  694. public function deleteAutoSaveVersions($userId = null)
  695. {
  696. $list = new Model\Version\Listing();
  697. $list->setLoadAutoSave(true);
  698. if ($userId) {
  699. $list->setCondition('`ctype` = ? AND cid = ? AND `autoSave` = 1 AND userId = ?', [Service::getElementType($this), $this->getId(), $userId]);
  700. } else {
  701. $list->setCondition('`ctype` = ? AND cid = ? AND `autoSave` = 1', [Service::getElementType($this), $this->getId()]);
  702. }
  703. foreach ($list->load() as $version) {
  704. $version->delete();
  705. }
  706. }
  707. /**
  708. * @internal
  709. */
  710. protected function removeInheritedProperties()
  711. {
  712. $myProperties = $this->getProperties();
  713. if ($myProperties) {
  714. foreach ($this->getProperties() as $name => $property) {
  715. if ($property->getInherited()) {
  716. unset($myProperties[$name]);
  717. }
  718. }
  719. }
  720. $this->setProperties($myProperties);
  721. }
  722. /**
  723. * @internal
  724. */
  725. protected function renewInheritedProperties()
  726. {
  727. $this->removeInheritedProperties();
  728. // add to registry to avoid infinite regresses in the following $this->getDao()->getProperties()
  729. $cacheKey = self::getCacheKey($this->getId());
  730. if (!RuntimeCache::isRegistered($cacheKey)) {
  731. RuntimeCache::set($cacheKey, $this);
  732. }
  733. $myProperties = $this->getProperties();
  734. $inheritedProperties = $this->getDao()->getProperties(true);
  735. $this->setProperties(array_merge($inheritedProperties, $myProperties));
  736. }
  737. }