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

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 ElementInterfaceElementDumpStateInterfaceDirtyIndicatorInterface
  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.         if ($this->modificationDate != (int)$modificationDate) {
  195.             $this->markFieldDirty('modificationDate');
  196.             $this->modificationDate = (int)$modificationDate;
  197.         }
  198.         return $this;
  199.     }
  200.     /**
  201.      * @return int|null
  202.      */
  203.     public function getUserOwner()
  204.     {
  205.         return $this->userOwner;
  206.     }
  207.     /**
  208.      * @param int $userOwner
  209.      *
  210.      * @return $this
  211.      */
  212.     public function setUserOwner($userOwner)
  213.     {
  214.         $this->userOwner = (int) $userOwner;
  215.         return $this;
  216.     }
  217.     /**
  218.      * enum('self','propagate') nullable
  219.      *
  220.      * @return string|null
  221.      */
  222.     public function getLocked()
  223.     {
  224.         if (empty($this->locked)) {
  225.             return null;
  226.         }
  227.         return $this->locked;
  228.     }
  229.     /**
  230.      * enum('self','propagate') nullable
  231.      *
  232.      * @param string|null $locked
  233.      *
  234.      * @return $this
  235.      */
  236.     public function setLocked($locked)
  237.     {
  238.         $this->locked $locked;
  239.         return $this;
  240.     }
  241.     /**
  242.      * @return int|null
  243.      */
  244.     public function getId()
  245.     {
  246.         return $this->id;
  247.     }
  248.     /**
  249.      * @param int|null $id
  250.      *
  251.      * @return $this
  252.      */
  253.     public function setId($id)
  254.     {
  255.         $this->id $id ? (int)$id null;
  256.         return $this;
  257.     }
  258.     /**
  259.      * @var self|null
  260.      */
  261.     protected $parent null;
  262.     /**
  263.      * @return self|null
  264.      */
  265.     public function getParent()
  266.     {
  267.         if ($this->parent === null) {
  268.             $parent Service::getElementById(Service::getElementType($this), $this->getParentId());
  269.             $this->setParent($parent);
  270.         }
  271.         return $this->parent;
  272.     }
  273.     /**
  274.      * @return Model\Property[]
  275.      */
  276.     public function getProperties()
  277.     {
  278.         $type Service::getElementType($this);
  279.         if ($this->properties === null) {
  280.             // try to get from cache
  281.             $cacheKey $type '_properties_' $this->getId();
  282.             $properties Cache::load($cacheKey);
  283.             if (!is_array($properties)) {
  284.                 $properties $this->getDao()->getProperties();
  285.                 $elementCacheTag $this->getCacheTag();
  286.                 $cacheTags = [$type '_properties' => $type '_properties'$elementCacheTag => $elementCacheTag];
  287.                 Cache::save($properties$cacheKey$cacheTags);
  288.             }
  289.             $this->setProperties($properties);
  290.         }
  291.         return $this->properties;
  292.     }
  293.     /**
  294.      * {@inheritdoc}
  295.      */
  296.     public function setProperties(?array $properties)
  297.     {
  298.         $this->properties $properties;
  299.         return $this;
  300.     }
  301.     /**
  302.      * @param string $name
  303.      * @param string $type
  304.      * @param mixed $data
  305.      * @param bool $inherited
  306.      * @param bool $inheritable
  307.      *
  308.      * @return $this
  309.      */
  310.     public function setProperty($name$type$data$inherited false$inheritable false)
  311.     {
  312.         $this->getProperties();
  313.         $property = new Model\Property();
  314.         $property->setType($type);
  315.         $property->setCid($this->getId());
  316.         $property->setName($name);
  317.         $property->setCtype(Service::getElementType($this));
  318.         $property->setData($data);
  319.         $property->setInherited($inherited);
  320.         $property->setInheritable($inheritable);
  321.         $this->properties[$name] = $property;
  322.         return $this;
  323.     }
  324.     /**
  325.      * @internal
  326.      */
  327.     protected function updateModificationInfos()
  328.     {
  329.         if (Model\Version::isEnabled() === true) {
  330.             $this->setVersionCount($this->getDao()->getVersionCountForUpdate() + 1);
  331.         }
  332.         if ($this->getVersionCount() > 4200000000) {
  333.             $this->setVersionCount(1);
  334.         }
  335.         $modificationDateKey 'modificationDate';
  336.         if (!$this->isFieldDirty($modificationDateKey)) {
  337.             $updateTime time();
  338.             $this->setModificationDate($updateTime);
  339.         }
  340.         if (!$this->getCreationDate()) {
  341.             $this->setCreationDate($this->getModificationDate());
  342.         }
  343.         // auto assign user if possible, if not changed explicitly, if no user present, use ID=0 which represents the "system" user
  344.         $userModificationKey 'userModification';
  345.         if (!$this->isFieldDirty($userModificationKey)) {
  346.             $userId 0;
  347.             $user \Pimcore\Tool\Admin::getCurrentUser();
  348.             if ($user instanceof User) {
  349.                 $userId $user->getId();
  350.             }
  351.             $this->setUserModification($userId);
  352.         }
  353.         if ($this->getUserOwner() === null) {
  354.             $this->setUserOwner($this->getUserModification());
  355.         }
  356.     }
  357.     /**
  358.      * {@inheritdoc}
  359.      */
  360.     public function getProperty($name$asContainer false)
  361.     {
  362.         $properties $this->getProperties();
  363.         if ($this->hasProperty($name)) {
  364.             if ($asContainer) {
  365.                 return $properties[$name];
  366.             } else {
  367.                 return $properties[$name]->getData();
  368.             }
  369.         }
  370.         return null;
  371.     }
  372.     /**
  373.      * {@inheritdoc}
  374.      */
  375.     public function hasProperty($name)
  376.     {
  377.         $properties $this->getProperties();
  378.         return array_key_exists($name$properties);
  379.     }
  380.     /**
  381.      * @param string $name
  382.      */
  383.     public function removeProperty($name)
  384.     {
  385.         $properties $this->getProperties();
  386.         unset($properties[$name]);
  387.         $this->setProperties($properties);
  388.     }
  389.     /**
  390.      * @return int
  391.      */
  392.     public function getVersionCount(): int
  393.     {
  394.         return $this->versionCount $this->versionCount 0;
  395.     }
  396.     /**
  397.      * @param int|null $versionCount
  398.      *
  399.      * @return $this
  400.      */
  401.     public function setVersionCount(?int $versionCount): ElementInterface
  402.     {
  403.         $this->versionCount = (int) $versionCount;
  404.         return $this;
  405.     }
  406.     /**
  407.      * {@inheritdoc}
  408.      */
  409.     public function getCacheTag()
  410.     {
  411.         $elementType Service::getElementType($this);
  412.         return Service::getElementCacheTag($elementType$this->getId());
  413.     }
  414.     /**
  415.      * @internal
  416.      *
  417.      * @param string|int $id
  418.      *
  419.      * @return string
  420.      */
  421.     protected static function getCacheKey($id): string
  422.     {
  423.         $elementType Service::getElementTypeByClassName(static::class);
  424.         return Service::getElementCacheTag($elementType$id);
  425.     }
  426.     /**
  427.      * {@inheritdoc}
  428.      */
  429.     public function getCacheTags(array $tags = []): array
  430.     {
  431.         $tags[$this->getCacheTag()] = $this->getCacheTag();
  432.         return $tags;
  433.     }
  434.     /**
  435.      * Resolves the dependencies of the element and returns an array of them - Used by update()
  436.      *
  437.      * @internal
  438.      *
  439.      * @return array
  440.      */
  441.     protected function resolveDependencies(): array
  442.     {
  443.         $dependencies = [[]];
  444.         // check for properties
  445.         if (method_exists($this'getProperties')) {
  446.             foreach ($this->getProperties() as $property) {
  447.                 $dependencies[] = $property->resolveDependencies();
  448.             }
  449.         }
  450.         return array_merge(...$dependencies);
  451.     }
  452.     /**
  453.      * {@inheritdoc}
  454.      */
  455.     public function isLocked()
  456.     {
  457.         if ($this->getLocked()) {
  458.             return true;
  459.         }
  460.         // check for inherited
  461.         return $this->getDao()->isLocked();
  462.     }
  463.     /**
  464.      * @param User|null $user
  465.      *
  466.      * @return array
  467.      *
  468.      * @throws \Exception
  469.      *
  470.      * @internal
  471.      */
  472.     public function getUserPermissions(?User $user null)
  473.     {
  474.         $baseClass Service::getBaseClassNameForElement($this);
  475.         $workspaceClass '\\Pimcore\\Model\\User\\Workspace\\' $baseClass;
  476.         /** @var Model\AbstractModel $dummy */
  477.         $dummy = new $workspaceClass();
  478.         $vars $dummy->getObjectVars();
  479.         $ignored = ['userId''cid''cpath''dao'];
  480.         $permissions = [];
  481.         $columns array_diff(array_keys($vars), $ignored);
  482.         $defaultValue 0;
  483.         if (null === $user) {
  484.             $user \Pimcore\Tool\Admin::getCurrentUser();
  485.         }
  486.         if ((!$user && php_sapi_name() === 'cli') || $user?->isAdmin()) {
  487.             $defaultValue 1;
  488.         }
  489.         foreach ($columns as $name) {
  490.             $permissions[$name] = $defaultValue;
  491.         }
  492.         if (!$user || $user->isAdmin() || !$user->isAllowed(Service::getElementType($this) . 's')) {
  493.             return $permissions;
  494.         }
  495.         $permissions $this->getDao()->areAllowed($columns$user);
  496.         foreach ($permissions as $type => $isAllowed) {
  497.             $event = new ElementEvent($this, ['isAllowed' => $isAllowed'permissionType' => $type'user' => $user]);
  498.             \Pimcore::getEventDispatcher()->dispatch($eventAdminEvents::ELEMENT_PERMISSION_IS_ALLOWED);
  499.             $permissions[$type] = $event->getArgument('isAllowed');
  500.         }
  501.         return $permissions;
  502.     }
  503.     /**
  504.      * {@inheritdoc}
  505.      */
  506.     public function isAllowed($type, ?User $user null)
  507.     {
  508.         if (null === $user) {
  509.             $user \Pimcore\Tool\Admin::getCurrentUser();
  510.         }
  511.         if (!$user) {
  512.             if (php_sapi_name() === 'cli') {
  513.                 return true;
  514.             }
  515.             return false;
  516.         }
  517.         //everything is allowed for admin
  518.         if ($user->isAdmin()) {
  519.             return true;
  520.         }
  521.         if (!$user->isAllowed(Service::getElementType($this) . 's')) {
  522.             return false;
  523.         }
  524.         $isAllowed $this->getDao()->isAllowed($type$user);
  525.         $event = new ElementEvent($this, ['isAllowed' => $isAllowed'permissionType' => $type'user' => $user]);
  526.         \Pimcore::getEventDispatcher()->dispatch($eventAdminEvents::ELEMENT_PERMISSION_IS_ALLOWED);
  527.         return (bool) $event->getArgument('isAllowed');
  528.     }
  529.     /**
  530.      * @internal
  531.      */
  532.     public function unlockPropagate()
  533.     {
  534.         $type Service::getElementType($this);
  535.         $ids $this->getDao()->unlockPropagate();
  536.         // invalidate cache items
  537.         foreach ($ids as $id) {
  538.             $element Service::getElementById($type$id);
  539.             if ($element) {
  540.                 $element->clearDependentCache();
  541.             }
  542.         }
  543.     }
  544.     /**
  545.      * @internal
  546.      *
  547.      * @throws \Exception
  548.      */
  549.     protected function validatePathLength()
  550.     {
  551.         if (mb_strlen($this->getRealFullPath()) > 765) {
  552.             throw new \Exception("Full path is limited to 765 characters, reduce the length of your parent's path");
  553.         }
  554.     }
  555.     /**
  556.      * {@inheritdoc}
  557.      */
  558.     public function __toString()
  559.     {
  560.         return $this->getFullPath();
  561.     }
  562.     /**
  563.      * @return int
  564.      */
  565.     public function __getDataVersionTimestamp()
  566.     {
  567.         return $this->__dataVersionTimestamp;
  568.     }
  569.     /**
  570.      * @param int $_dataVersionTimestamp
  571.      */
  572.     public function __setDataVersionTimestamp($_dataVersionTimestamp)
  573.     {
  574.         $this->__dataVersionTimestamp $_dataVersionTimestamp;
  575.     }
  576.     /**
  577.      * {@inheritdoc}
  578.      */
  579.     public function __isBasedOnLatestData()
  580.     {
  581.         return $this->getDao()->__isBasedOnLatestData();
  582.     }
  583.     /**
  584.      * @internal
  585.      *
  586.      * @param string|null $versionNote
  587.      * @param bool $saveOnlyVersion
  588.      * @param bool $saveStackTrace
  589.      * @param bool $isAutoSave
  590.      *
  591.      * @return Model\Version
  592.      *
  593.      * @throws \Exception
  594.      */
  595.     protected function doSaveVersion($versionNote null$saveOnlyVersion true$saveStackTrace true$isAutoSave false)
  596.     {
  597.         $version null;
  598.         if ($isAutoSave) {
  599.             $list = new Model\Version\Listing();
  600.             $list->setLoadAutoSave(true);
  601.             $list->setCondition('autoSave = 1 AND cid = ? AND cType = ? AND userId = ? ', [$this->getId(), Service::getElementType($this), $this->getUserModification()]);
  602.             $version $list->current();
  603.         }
  604.         if (!$version) {
  605.             /** @var Model\Version $version */
  606.             $version self::getModelFactory()->build(Model\Version::class);
  607.         }
  608.         $version->setCid($this->getId());
  609.         $version->setCtype(Service::getElementType($this));
  610.         $version->setDate($this->getModificationDate());
  611.         $version->setUserId($this->getUserModification());
  612.         $version->setData($this);
  613.         $version->setNote($versionNote);
  614.         $version->setGenerateStackTrace($saveStackTrace);
  615.         $version->setAutoSave($isAutoSave);
  616.         if ($saveOnlyVersion) {
  617.             $versionCount $this->getDao()->getVersionCountForUpdate();
  618.             $versionCount++;
  619.         } else {
  620.             $versionCount $this->getVersionCount();
  621.         }
  622.         $version->setVersionCount($versionCount);
  623.         $version->save();
  624.         return $version;
  625.     }
  626.     /**
  627.      * {@inheritdoc}
  628.      */
  629.     public function getDependencies()
  630.     {
  631.         if (!$this->dependencies) {
  632.             $this->dependencies Model\Dependency::getBySourceId($this->getId(), Service::getElementType($this));
  633.         }
  634.         return $this->dependencies;
  635.     }
  636.     /**
  637.      * {@inheritdoc}
  638.      */
  639.     public function getScheduledTasks()
  640.     {
  641.         return [];
  642.     }
  643.     /**
  644.      * {@inheritdoc}
  645.      */
  646.     public function getVersions()
  647.     {
  648.         return [];
  649.     }
  650.     /**
  651.      * @internal
  652.      *
  653.      * @return string[]
  654.      */
  655.     protected function getBlockedVars(): array
  656.     {
  657.         return ['dependencies''parent'];
  658.     }
  659.     /**
  660.      * {@inheritdoc}
  661.      */
  662.     public function __sleep()
  663.     {
  664.         if ($this->isInDumpState()) {
  665.             // this is if we want to make a full dump of the object (eg. for a new version), including children for recyclebin
  666.             $this->removeInheritedProperties();
  667.         }
  668.         return array_diff(parent::__sleep(), $this->getBlockedVars());
  669.     }
  670.     public function __wakeup()
  671.     {
  672.         if ($this->isInDumpState()) {
  673.             // 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 )
  674.             $originalElement = static::getById($this->getId());
  675.             if ($originalElement && !self::$doNotRestoreKeyAndPath) {
  676.                 // set key and path for DataObject and Document (assets have different wakeup call)
  677.                 $this->setKey($originalElement->getKey());
  678.                 $this->setPath($originalElement->getRealPath());
  679.             }
  680.         }
  681.         if ($this->isInDumpState() && $this->properties !== null) {
  682.             $this->renewInheritedProperties();
  683.         }
  684.         $this->setInDumpState(false);
  685.     }
  686.     public function __clone()
  687.     {
  688.         parent::__clone();
  689.         $this->dependencies null;
  690.     }
  691.     /**
  692.      * @internal
  693.      *
  694.      * @param int $userId
  695.      */
  696.     public function deleteAutoSaveVersions($userId null)
  697.     {
  698.         $list = new Model\Version\Listing();
  699.         $list->setLoadAutoSave(true);
  700.         if ($userId) {
  701.             $list->setCondition('`ctype` = ? AND cid = ? AND `autoSave` = 1 AND userId = ?', [Service::getElementType($this), $this->getId(), $userId]);
  702.         } else {
  703.             $list->setCondition('`ctype` = ? AND cid = ? AND `autoSave` = 1', [Service::getElementType($this), $this->getId()]);
  704.         }
  705.         foreach ($list->load() as $version) {
  706.             $version->delete();
  707.         }
  708.     }
  709.     /**
  710.      * @internal
  711.      */
  712.     protected function removeInheritedProperties()
  713.     {
  714.         $myProperties $this->getProperties();
  715.         if ($myProperties) {
  716.             foreach ($this->getProperties() as $name => $property) {
  717.                 if ($property->getInherited()) {
  718.                     unset($myProperties[$name]);
  719.                 }
  720.             }
  721.         }
  722.         $this->setProperties($myProperties);
  723.     }
  724.     /**
  725.      * @internal
  726.      */
  727.     protected function renewInheritedProperties()
  728.     {
  729.         $this->removeInheritedProperties();
  730.         // add to registry to avoid infinite regresses in the following $this->getDao()->getProperties()
  731.         $cacheKey self::getCacheKey($this->getId());
  732.         if (!RuntimeCache::isRegistered($cacheKey)) {
  733.             RuntimeCache::set($cacheKey$this);
  734.         }
  735.         $myProperties $this->getProperties();
  736.         $inheritedProperties $this->getDao()->getProperties(true);
  737.         $this->setProperties(array_merge($inheritedProperties$myProperties));
  738.     }
  739. }