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

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.         $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($eventAdminEvents::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($eventAdminEvents::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. }