vendor/pimcore/pimcore/models/Document.php line 231

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;
  15. use Doctrine\DBAL\Exception\DeadlockException;
  16. use Pimcore\Event\DocumentEvents;
  17. use Pimcore\Event\FrontendEvents;
  18. use Pimcore\Event\Model\DocumentEvent;
  19. use Pimcore\Logger;
  20. use Pimcore\Model\Document\Hardlink;
  21. use Pimcore\Model\Document\Hardlink\Wrapper\WrapperInterface;
  22. use Pimcore\Model\Document\Listing;
  23. use Pimcore\Model\Element\ElementInterface;
  24. use Pimcore\Model\Exception\NotFoundException;
  25. use Pimcore\Tool;
  26. use Pimcore\Tool\Frontend as FrontendTool;
  27. use Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter;
  28. use Symfony\Component\EventDispatcher\GenericEvent;
  29. /**
  30.  * @method \Pimcore\Model\Document\Dao getDao()
  31.  * @method bool __isBasedOnLatestData()
  32.  * @method int getChildAmount($user = null)
  33.  * @method string getCurrentFullPath()
  34.  */
  35. class Document extends Element\AbstractElement
  36. {
  37.     /**
  38.      * all possible types of documents
  39.      *
  40.      * @internal
  41.      *
  42.      * @deprecated will be removed in Pimcore 11. Use getTypes() method.
  43.      *
  44.      * @var array
  45.      */
  46.     public static $types = ['folder''page''snippet''link''hardlink''email''newsletter''printpage''printcontainer'];
  47.     /**
  48.      * @var bool
  49.      */
  50.     private static $hideUnpublished false;
  51.     /**
  52.      * @internal
  53.      *
  54.      * @var string|null
  55.      */
  56.     protected $fullPathCache;
  57.     /**
  58.      * @internal
  59.      *
  60.      * @var int
  61.      */
  62.     protected $id;
  63.     /**
  64.      * @internal
  65.      *
  66.      * @var int
  67.      */
  68.     protected $parentId;
  69.     /**
  70.      * @internal
  71.      *
  72.      * @var self|null
  73.      */
  74.     protected $parent;
  75.     /**
  76.      * @internal
  77.      *
  78.      * @var string
  79.      */
  80.     protected string $type;
  81.     /**
  82.      * @internal
  83.      *
  84.      * @var string
  85.      */
  86.     protected $key;
  87.     /**
  88.      * @internal
  89.      *
  90.      * @var string
  91.      */
  92.     protected $path;
  93.     /**
  94.      * @internal
  95.      *
  96.      * @var int|null
  97.      */
  98.     protected ?int $index null;
  99.     /**
  100.      * @internal
  101.      *
  102.      * @var bool
  103.      */
  104.     protected bool $published true;
  105.     /**
  106.      * @internal
  107.      *
  108.      * @var int
  109.      */
  110.     protected $creationDate;
  111.     /**
  112.      * @internal
  113.      *
  114.      * @var int
  115.      */
  116.     protected $modificationDate;
  117.     /**
  118.      * @internal
  119.      *
  120.      * @var int|null
  121.      */
  122.     protected ?int $userOwner null;
  123.     /**
  124.      * @internal
  125.      *
  126.      * @var int|null
  127.      */
  128.     protected ?int $userModification null;
  129.     /**
  130.      * @internal
  131.      *
  132.      * @var array|null
  133.      */
  134.     protected $properties null;
  135.     /**
  136.      * @internal
  137.      *
  138.      * @var array
  139.      */
  140.     protected $children = [];
  141.     /**
  142.      * @internal
  143.      *
  144.      * @var bool[]
  145.      */
  146.     protected $hasChildren = [];
  147.     /**
  148.      * @internal
  149.      *
  150.      * @var array
  151.      */
  152.     protected $siblings = [];
  153.     /**
  154.      * @internal
  155.      *
  156.      * @var bool[]
  157.      */
  158.     protected $hasSiblings = [];
  159.     /**
  160.      * enum('self','propagate') nullable
  161.      *
  162.      * @internal
  163.      *
  164.      * @var string|null
  165.      */
  166.     protected $locked null;
  167.     /**
  168.      * @internal
  169.      *
  170.      * @var int
  171.      */
  172.     protected $versionCount;
  173.     /**
  174.      * get possible types
  175.      *
  176.      * @return array
  177.      */
  178.     public static function getTypes()
  179.     {
  180.         $documentsConfig \Pimcore\Config::getSystemConfiguration('documents');
  181.         return $documentsConfig['types'];
  182.     }
  183.     /**
  184.      * @param string $path
  185.      * @param bool $force
  186.      *
  187.      * @return static|null
  188.      */
  189.     public static function getByPath($path$force false)
  190.     {
  191.         $path Element\Service::correctPath($path);
  192.         $cacheKey 'document_path_' md5($path);
  193.         if (\Pimcore\Cache\Runtime::isRegistered($cacheKey)) {
  194.             return \Pimcore\Cache\Runtime::get($cacheKey);
  195.         }
  196.         $doc null;
  197.         try {
  198.             $helperDoc = new Document();
  199.             $helperDoc->getDao()->getByPath($path);
  200.             $doc = static::getById($helperDoc->getId(), $force);
  201.             \Pimcore\Cache\Runtime::set($cacheKey$doc);
  202.         } catch (NotFoundException $e) {
  203.             $doc null;
  204.         }
  205.         return $doc;
  206.     }
  207.     /**
  208.      * @internal
  209.      *
  210.      * @param Document $document
  211.      *
  212.      * @return bool
  213.      */
  214.     protected static function typeMatch(Document $document)
  215.     {
  216.         $staticType get_called_class();
  217.         if ($staticType != Document::class) {
  218.             if (!$document instanceof $staticType) {
  219.                 return false;
  220.             }
  221.         }
  222.         return true;
  223.     }
  224.     /**
  225.      * @param int $id
  226.      * @param bool $force
  227.      *
  228.      * @return static|null
  229.      */
  230.     public static function getById($id$force false)
  231.     {
  232.         if (!is_numeric($id) || $id 1) {
  233.             return null;
  234.         }
  235.         $id = (int)$id;
  236.         $cacheKey self::getCacheKey($id);
  237.         if (!$force && \Pimcore\Cache\Runtime::isRegistered($cacheKey)) {
  238.             $document \Pimcore\Cache\Runtime::get($cacheKey);
  239.             if ($document && static::typeMatch($document)) {
  240.                 return $document;
  241.             }
  242.         }
  243.         if ($force || !($document \Pimcore\Cache::load($cacheKey))) {
  244.             $document = new Document();
  245.             try {
  246.                 $document->getDao()->getById($id);
  247.             } catch (NotFoundException $e) {
  248.                 return null;
  249.             }
  250.             $className 'Pimcore\\Model\\Document\\' ucfirst($document->getType());
  251.             // this is the fallback for custom document types using prefixes
  252.             // so we need to check if the class exists first
  253.             if (!Tool::classExists($className)) {
  254.                 $oldStyleClass 'Document_' ucfirst($document->getType());
  255.                 if (Tool::classExists($oldStyleClass)) {
  256.                     $className $oldStyleClass;
  257.                 }
  258.             }
  259.             /** @var Document $document */
  260.             $document self::getModelFactory()->build($className);
  261.             \Pimcore\Cache\Runtime::set($cacheKey$document);
  262.             $document->getDao()->getById($id);
  263.             $document->__setDataVersionTimestamp($document->getModificationDate());
  264.             $document->resetDirtyMap();
  265.             \Pimcore\Cache::save($document$cacheKey);
  266.         } else {
  267.             \Pimcore\Cache\Runtime::set($cacheKey$document);
  268.         }
  269.         if (!$document || !static::typeMatch($document)) {
  270.             return null;
  271.         }
  272.         return $document;
  273.     }
  274.     /**
  275.      * @param int $parentId
  276.      * @param array $data
  277.      * @param bool $save
  278.      *
  279.      * @return static
  280.      */
  281.     public static function create($parentId$data = [], $save true)
  282.     {
  283.         $document = new static();
  284.         $document->setParentId($parentId);
  285.         self::checkCreateData($data);
  286.         $document->setValues($data);
  287.         if ($save) {
  288.             $document->save();
  289.         }
  290.         return $document;
  291.     }
  292.     /**
  293.      * @param array $config
  294.      *
  295.      * @return Listing
  296.      *
  297.      * @throws \Exception
  298.      */
  299.     public static function getList(array $config = []): Listing
  300.     {
  301.         /** @var Listing $list */
  302.         $list self::getModelFactory()->build(Listing::class);
  303.         $list->setValues($config);
  304.         return $list;
  305.     }
  306.     /**
  307.      * @deprecated will be removed in Pimcore 11
  308.      *
  309.      * @param array $config
  310.      *
  311.      * @return int count
  312.      */
  313.     public static function getTotalCount(array $config = []): int
  314.     {
  315.         $list = static::getList($config);
  316.         return $list->getTotalCount();
  317.     }
  318.     /**
  319.      * {@inheritdoc}
  320.      */
  321.     public function save()
  322.     {
  323.         $isUpdate false;
  324.         try {
  325.             // additional parameters (e.g. "versionNote" for the version note)
  326.             $params = [];
  327.             if (func_num_args() && is_array(func_get_arg(0))) {
  328.                 $params func_get_arg(0);
  329.             }
  330.             $preEvent = new DocumentEvent($this$params);
  331.             if ($this->getId()) {
  332.                 $isUpdate true;
  333.                 \Pimcore::getEventDispatcher()->dispatch($preEventDocumentEvents::PRE_UPDATE);
  334.             } else {
  335.                 \Pimcore::getEventDispatcher()->dispatch($preEventDocumentEvents::PRE_ADD);
  336.             }
  337.             $params $preEvent->getArguments();
  338.             $this->correctPath();
  339.             $differentOldPath null;
  340.             // we wrap the save actions in a loop here, so that we can restart the database transactions in the case it fails
  341.             // if a transaction fails it gets restarted $maxRetries times, then the exception is thrown out
  342.             // this is especially useful to avoid problems with deadlocks in multi-threaded environments (forked workers, ...)
  343.             $maxRetries 5;
  344.             for ($retries 0$retries $maxRetries$retries++) {
  345.                 $this->beginTransaction();
  346.                 try {
  347.                     $this->updateModificationInfos();
  348.                     if (!$isUpdate) {
  349.                         $this->getDao()->create();
  350.                     }
  351.                     // get the old path from the database before the update is done
  352.                     $oldPath null;
  353.                     if ($isUpdate) {
  354.                         $oldPath $this->getDao()->getCurrentFullPath();
  355.                     }
  356.                     $this->update($params);
  357.                     // if the old path is different from the new path, update all children
  358.                     $updatedChildren = [];
  359.                     if ($oldPath && $oldPath != $this->getRealFullPath()) {
  360.                         $differentOldPath $oldPath;
  361.                         $this->getDao()->updateWorkspaces();
  362.                         $updatedChildren $this->getDao()->updateChildPaths($oldPath);
  363.                     }
  364.                     $this->commit();
  365.                     break; // transaction was successfully completed, so we cancel the loop here -> no restart required
  366.                 } catch (\Exception $e) {
  367.                     try {
  368.                         $this->rollBack();
  369.                     } catch (\Exception $er) {
  370.                         // PDO adapter throws exceptions if rollback fails
  371.                         Logger::error($er);
  372.                     }
  373.                     // we try to start the transaction $maxRetries times again (deadlocks, ...)
  374.                     if ($e instanceof DeadlockException && $retries < ($maxRetries 1)) {
  375.                         $run $retries 1;
  376.                         $waitTime rand(15) * 100000// microseconds
  377.                         Logger::warn('Unable to finish transaction (' $run ". run) because of the following reason '" $e->getMessage() . "'. --> Retrying in " $waitTime ' microseconds ... (' . ($run 1) . ' of ' $maxRetries ')');
  378.                         usleep($waitTime); // wait specified time until we restart the transaction
  379.                     } else {
  380.                         // if the transaction still fail after $maxRetries retries, we throw out the exception
  381.                         throw $e;
  382.                     }
  383.                 }
  384.             }
  385.             $additionalTags = [];
  386.             if (isset($updatedChildren) && is_array($updatedChildren)) {
  387.                 foreach ($updatedChildren as $documentId) {
  388.                     $tag 'document_' $documentId;
  389.                     $additionalTags[] = $tag;
  390.                     // remove the child also from registry (internal cache) to avoid path inconsistencies during long running scripts, such as CLI
  391.                     \Pimcore\Cache\Runtime::set($tagnull);
  392.                 }
  393.             }
  394.             $this->clearDependentCache($additionalTags);
  395.             $postEvent = new DocumentEvent($this$params);
  396.             if ($isUpdate) {
  397.                 if ($differentOldPath) {
  398.                     $postEvent->setArgument('oldPath'$differentOldPath);
  399.                 }
  400.                 \Pimcore::getEventDispatcher()->dispatch($postEventDocumentEvents::POST_UPDATE);
  401.             } else {
  402.                 \Pimcore::getEventDispatcher()->dispatch($postEventDocumentEvents::POST_ADD);
  403.             }
  404.             return $this;
  405.         } catch (\Exception $e) {
  406.             $failureEvent = new DocumentEvent($this$params);
  407.             $failureEvent->setArgument('exception'$e);
  408.             if ($isUpdate) {
  409.                 \Pimcore::getEventDispatcher()->dispatch($failureEventDocumentEvents::POST_UPDATE_FAILURE);
  410.             } else {
  411.                 \Pimcore::getEventDispatcher()->dispatch($failureEventDocumentEvents::POST_ADD_FAILURE);
  412.             }
  413.             throw $e;
  414.         }
  415.     }
  416.     /**
  417.      * @throws \Exception
  418.      */
  419.     private function correctPath()
  420.     {
  421.         // set path
  422.         if ($this->getId() != 1) { // not for the root node
  423.             // check for a valid key, home has no key, so omit the check
  424.             if (!Element\Service::isValidKey($this->getKey(), 'document')) {
  425.                 throw new \Exception('invalid key for document with id [ ' $this->getId() . ' ] key is: [' $this->getKey() . ']');
  426.             }
  427.             if ($this->getParentId() == $this->getId()) {
  428.                 throw new \Exception("ParentID and ID is identical, an element can't be the parent of itself.");
  429.             }
  430.             $parent Document::getById($this->getParentId());
  431.             if ($parent) {
  432.                 // use the parent's path from the database here (getCurrentFullPath), to ensure the path really exists and does not rely on the path
  433.                 // that is currently in the parent object (in memory), because this might have changed but wasn't not saved
  434.                 $this->setPath(str_replace('//''/'$parent->getCurrentFullPath() . '/'));
  435.             } else {
  436.                 // parent document doesn't exist anymore, set the parent to to root
  437.                 $this->setParentId(1);
  438.                 $this->setPath('/');
  439.             }
  440.             if (strlen($this->getKey()) < 1) {
  441.                 throw new \Exception('Document requires key, generated key automatically');
  442.             }
  443.         } elseif ($this->getId() == 1) {
  444.             // some data in root node should always be the same
  445.             $this->setParentId(0);
  446.             $this->setPath('/');
  447.             $this->setKey('');
  448.             $this->setType('page');
  449.         }
  450.         if (Document\Service::pathExists($this->getRealFullPath())) {
  451.             $duplicate Document::getByPath($this->getRealFullPath());
  452.             if ($duplicate instanceof Document && $duplicate->getId() != $this->getId()) {
  453.                 throw new \Exception('Duplicate full path [ ' $this->getRealFullPath() . ' ] - cannot save document');
  454.             }
  455.         }
  456.         $this->validatePathLength();
  457.     }
  458.     /**
  459.      * @internal
  460.      *
  461.      * @param array $params additional parameters (e.g. "versionNote" for the version note)
  462.      *
  463.      * @throws \Exception
  464.      */
  465.     protected function update($params = [])
  466.     {
  467.         $disallowedKeysInFirstLevel = ['install''admin''plugin'];
  468.         if ($this->getParentId() == && in_array($this->getKey(), $disallowedKeysInFirstLevel)) {
  469.             throw new \Exception('Key: ' $this->getKey() . ' is not allowed in first level (root-level)');
  470.         }
  471.         // set index if null
  472.         if ($this->getIndex() === null) {
  473.             $this->setIndex($this->getDao()->getNextIndex());
  474.         }
  475.         // save properties
  476.         $this->getProperties();
  477.         $this->getDao()->deleteAllProperties();
  478.         if (is_array($this->getProperties()) && count($this->getProperties()) > 0) {
  479.             foreach ($this->getProperties() as $property) {
  480.                 if (!$property->getInherited()) {
  481.                     $property->setDao(null);
  482.                     $property->setCid($this->getId());
  483.                     $property->setCtype('document');
  484.                     $property->setCpath($this->getRealFullPath());
  485.                     $property->save();
  486.                 }
  487.             }
  488.         }
  489.         // save dependencies
  490.         $d = new Dependency();
  491.         $d->setSourceType('document');
  492.         $d->setSourceId($this->getId());
  493.         foreach ($this->resolveDependencies() as $requirement) {
  494.             if ($requirement['id'] == $this->getId() && $requirement['type'] == 'document') {
  495.                 // dont't add a reference to yourself
  496.                 continue;
  497.             } else {
  498.                 $d->addRequirement($requirement['id'], $requirement['type']);
  499.             }
  500.         }
  501.         $d->save();
  502.         $this->getDao()->update();
  503.         //set document to registry
  504.         \Pimcore\Cache\Runtime::set(self::getCacheKey($this->getId()), $this);
  505.     }
  506.     /**
  507.      * @internal
  508.      *
  509.      * @param int $index
  510.      */
  511.     public function saveIndex($index)
  512.     {
  513.         $this->getDao()->saveIndex($index);
  514.         $this->clearDependentCache();
  515.     }
  516.     /**
  517.      * {@inheritdoc}
  518.      */
  519.     public function clearDependentCache($additionalTags = [])
  520.     {
  521.         try {
  522.             $tags = [$this->getCacheTag(), 'document_properties''output'];
  523.             $tags array_merge($tags$additionalTags);
  524.             \Pimcore\Cache::clearTags($tags);
  525.         } catch (\Exception $e) {
  526.             Logger::crit($e);
  527.         }
  528.     }
  529.     /**
  530.      * set the children of the document
  531.      *
  532.      * @param self[] $children
  533.      * @param bool $includingUnpublished
  534.      *
  535.      * @return $this
  536.      */
  537.     public function setChildren($children$includingUnpublished false)
  538.     {
  539.         if (empty($children)) {
  540.             // unset all cached children
  541.             $this->hasChildren = [];
  542.             $this->children = [];
  543.         } elseif (is_array($children)) {
  544.             $cacheKey $this->getListingCacheKey([$includingUnpublished]);
  545.             $this->children[$cacheKey] = $children;
  546.             $this->hasChildren[$cacheKey] = (bool) count($children);
  547.         }
  548.         return $this;
  549.     }
  550.     /**
  551.      * Get a list of the children (not recursivly)
  552.      *
  553.      * @param bool $includingUnpublished
  554.      *
  555.      * @return self[]
  556.      */
  557.     public function getChildren($includingUnpublished false)
  558.     {
  559.         $cacheKey $this->getListingCacheKey(func_get_args());
  560.         if (!isset($this->children[$cacheKey])) {
  561.             $list = new Document\Listing();
  562.             $list->setUnpublished($includingUnpublished);
  563.             $list->setCondition('parentId = ?'$this->getId());
  564.             $list->setOrderKey('index');
  565.             $list->setOrder('asc');
  566.             $this->children[$cacheKey] = $list->load();
  567.         }
  568.         return $this->children[$cacheKey];
  569.     }
  570.     /**
  571.      * Returns true if the document has at least one child
  572.      *
  573.      * @param bool $includingUnpublished
  574.      *
  575.      * @return bool
  576.      */
  577.     public function hasChildren($includingUnpublished null)
  578.     {
  579.         $cacheKey $this->getListingCacheKey(func_get_args());
  580.         if (isset($this->hasChildren[$cacheKey])) {
  581.             return $this->hasChildren[$cacheKey];
  582.         }
  583.         return $this->hasChildren[$cacheKey] = $this->getDao()->hasChildren($includingUnpublished);
  584.     }
  585.     /**
  586.      * Get a list of the sibling documents
  587.      *
  588.      * @param bool $includingUnpublished
  589.      *
  590.      * @return array
  591.      */
  592.     public function getSiblings($includingUnpublished false)
  593.     {
  594.         $cacheKey $this->getListingCacheKey(func_get_args());
  595.         if (!isset($this->siblings[$cacheKey])) {
  596.             $list = new Document\Listing();
  597.             $list->setUnpublished($includingUnpublished);
  598.             // string conversion because parentId could be 0
  599.             $list->addConditionParam('parentId = ?', (string)$this->getParentId());
  600.             $list->addConditionParam('id != ?'$this->getId());
  601.             $list->setOrderKey('index');
  602.             $list->setOrder('asc');
  603.             $this->siblings[$cacheKey] = $list->load();
  604.             $this->hasSiblings[$cacheKey] = (bool) count($this->siblings[$cacheKey]);
  605.         }
  606.         return $this->siblings[$cacheKey];
  607.     }
  608.     /**
  609.      * Returns true if the document has at least one sibling
  610.      *
  611.      * @param bool|null $includingUnpublished
  612.      *
  613.      * @return bool
  614.      */
  615.     public function hasSiblings($includingUnpublished null)
  616.     {
  617.         $cacheKey $this->getListingCacheKey(func_get_args());
  618.         if (isset($this->hasSiblings[$cacheKey])) {
  619.             return $this->hasSiblings[$cacheKey];
  620.         }
  621.         return $this->hasSiblings[$cacheKey] = $this->getDao()->hasSiblings($includingUnpublished);
  622.     }
  623.     /**
  624.      * {@inheritdoc}
  625.      */
  626.     public function getLocked()
  627.     {
  628.         if (empty($this->locked)) {
  629.             return null;
  630.         }
  631.         return $this->locked;
  632.     }
  633.     /**
  634.      * {@inheritdoc}
  635.      */
  636.     public function setLocked($locked)
  637.     {
  638.         $this->locked $locked;
  639.         return $this;
  640.     }
  641.     /**
  642.      * @internal
  643.      *
  644.      * @throws \Exception
  645.      */
  646.     protected function doDelete()
  647.     {
  648.         // remove children
  649.         if ($this->hasChildren()) {
  650.             // delete also unpublished children
  651.             $unpublishedStatus self::doHideUnpublished();
  652.             self::setHideUnpublished(false);
  653.             foreach ($this->getChildren(true) as $child) {
  654.                 if (!$child instanceof WrapperInterface) {
  655.                     $child->delete();
  656.                 }
  657.             }
  658.             self::setHideUnpublished($unpublishedStatus);
  659.         }
  660.         // remove all properties
  661.         $this->getDao()->deleteAllProperties();
  662.         // remove permissions
  663.         $this->getDao()->deleteAllPermissions();
  664.         // remove dependencies
  665.         $d $this->getDependencies();
  666.         $d->cleanAllForElement($this);
  667.         // remove translations
  668.         $service = new Document\Service;
  669.         $service->removeTranslation($this);
  670.     }
  671.     /**
  672.      * {@inheritdoc}
  673.      */
  674.     public function delete()
  675.     {
  676.         \Pimcore::getEventDispatcher()->dispatch(new DocumentEvent($this), DocumentEvents::PRE_DELETE);
  677.         $this->beginTransaction();
  678.         try {
  679.             if ($this->getId() == 1) {
  680.                 throw new \Exception('root-node cannot be deleted');
  681.             }
  682.             $this->doDelete();
  683.             $this->getDao()->delete();
  684.             $this->commit();
  685.             //clear parent data from registry
  686.             $parentCacheKey self::getCacheKey($this->getParentId());
  687.             if (\Pimcore\Cache\Runtime::isRegistered($parentCacheKey)) {
  688.                 /** @var Document $parent */
  689.                 $parent \Pimcore\Cache\Runtime::get($parentCacheKey);
  690.                 if ($parent instanceof self) {
  691.                     $parent->setChildren(null);
  692.                 }
  693.             }
  694.         } catch (\Exception $e) {
  695.             $this->rollBack();
  696.             $failureEvent = new DocumentEvent($this);
  697.             $failureEvent->setArgument('exception'$e);
  698.             \Pimcore::getEventDispatcher()->dispatch($failureEventDocumentEvents::POST_DELETE_FAILURE);
  699.             Logger::error($e);
  700.             throw $e;
  701.         }
  702.         // clear cache
  703.         $this->clearDependentCache();
  704.         //clear document from registry
  705.         \Pimcore\Cache\Runtime::set(self::getCacheKey($this->getId()), null);
  706.         \Pimcore::getEventDispatcher()->dispatch(new DocumentEvent($this), DocumentEvents::POST_DELETE);
  707.     }
  708.     /**
  709.      * {@inheritdoc}
  710.      */
  711.     public function getFullPath(bool $force false)
  712.     {
  713.         $link $force null $this->fullPathCache;
  714.         // check if this document is also the site root, if so return /
  715.         try {
  716.             if (!$link && \Pimcore\Tool::isFrontend() && Site::isSiteRequest()) {
  717.                 $site Site::getCurrentSite();
  718.                 if ($site instanceof Site) {
  719.                     if ($site->getRootDocument()->getId() == $this->getId()) {
  720.                         $link '/';
  721.                     }
  722.                 }
  723.             }
  724.         } catch (\Exception $e) {
  725.             Logger::error($e);
  726.         }
  727.         $requestStack \Pimcore::getContainer()->get('request_stack');
  728.         $masterRequest $requestStack->getMasterRequest();
  729.         // @TODO please forgive me, this is the dirtiest hack I've ever made :(
  730.         // if you got confused by this functionality drop me a line and I'll buy you some beers :)
  731.         // this is for the case that a link points to a document outside of the current site
  732.         // in this case we look for a hardlink in the current site which points to the current document
  733.         // why this could happen: we have 2 sites, in one site there's a hardlink to the other site and on a page inside
  734.         // the hardlink there are snippets embedded and this snippets have links pointing to a document which is also
  735.         // inside the hardlink scope, but this is an ID link, so we cannot rewrite the link the usual way because in the
  736.         // snippet / link we don't know anymore that whe a inside a hardlink wrapped document
  737.         if (!$link && \Pimcore\Tool::isFrontend() && Site::isSiteRequest() && !FrontendTool::isDocumentInCurrentSite($this)) {
  738.             if ($masterRequest && ($masterDocument $masterRequest->get(DynamicRouter::CONTENT_KEY))) {
  739.                 if ($masterDocument instanceof WrapperInterface) {
  740.                     $hardlinkPath '';
  741.                     $hardlink $masterDocument->getHardLinkSource();
  742.                     $hardlinkTarget $hardlink->getSourceDocument();
  743.                     if ($hardlinkTarget) {
  744.                         $hardlinkPath preg_replace('@^' preg_quote(Site::getCurrentSite()->getRootPath(), '@') . '@'''$hardlink->getRealFullPath());
  745.                         $link preg_replace('@^' preg_quote($hardlinkTarget->getRealFullPath(), '@') . '@',
  746.                             $hardlinkPath$this->getRealFullPath());
  747.                     }
  748.                     if (strpos($this->getRealFullPath(), Site::getCurrentSite()->getRootDocument()->getRealFullPath()) === false && strpos($link$hardlinkPath) === false) {
  749.                         $link null;
  750.                     }
  751.                 }
  752.             }
  753.             if (!$link) {
  754.                 $config \Pimcore\Config::getSystemConfiguration('general');
  755.                 $request $requestStack->getCurrentRequest();
  756.                 $scheme 'http://';
  757.                 if ($request) {
  758.                     $scheme $request->getScheme() . '://';
  759.                 }
  760.                 /** @var Site $site */
  761.                 if ($site FrontendTool::getSiteForDocument($this)) {
  762.                     if ($site->getMainDomain()) {
  763.                         // check if current document is the root of the different site, if so, preg_replace below doesn't work, so just return /
  764.                         if ($site->getRootDocument()->getId() == $this->getId()) {
  765.                             $link $scheme $site->getMainDomain() . '/';
  766.                         } else {
  767.                             $link $scheme $site->getMainDomain() .
  768.                                 preg_replace('@^' $site->getRootPath() . '/@''/'$this->getRealFullPath());
  769.                         }
  770.                     }
  771.                 }
  772.                 if (!$link && !empty($config['domain']) && !($this instanceof WrapperInterface)) {
  773.                     $link $scheme $config['domain'] . $this->getRealFullPath();
  774.                 }
  775.             }
  776.         }
  777.         if (!$link) {
  778.             $link $this->getPath() . $this->getKey();
  779.         }
  780.         if ($masterRequest) {
  781.             // caching should only be done when master request is available as it is done for performance reasons
  782.             // of the web frontend, without a request object there's no need to cache anything
  783.             // for details also see https://github.com/pimcore/pimcore/issues/5707
  784.             $this->fullPathCache $link;
  785.         }
  786.         $link $this->prepareFrontendPath($link);
  787.         return $link;
  788.     }
  789.     /**
  790.      * @param string $path
  791.      *
  792.      * @return string
  793.      */
  794.     private function prepareFrontendPath($path)
  795.     {
  796.         if (\Pimcore\Tool::isFrontend()) {
  797.             $path urlencode_ignore_slash($path);
  798.             $event = new GenericEvent($this, [
  799.                 'frontendPath' => $path,
  800.             ]);
  801.             \Pimcore::getEventDispatcher()->dispatch($eventFrontendEvents::DOCUMENT_PATH);
  802.             $path $event->getArgument('frontendPath');
  803.         }
  804.         return $path;
  805.     }
  806.     /**
  807.      * {@inheritdoc}
  808.      */
  809.     public function getCreationDate()
  810.     {
  811.         return $this->creationDate;
  812.     }
  813.     /**
  814.      * {@inheritdoc}
  815.      */
  816.     public function getId(): int
  817.     {
  818.         return (int) $this->id;
  819.     }
  820.     /**
  821.      * {@inheritdoc}
  822.      */
  823.     public function getKey()
  824.     {
  825.         return $this->key;
  826.     }
  827.     /**
  828.      * {@inheritdoc}
  829.      */
  830.     public function getModificationDate()
  831.     {
  832.         return $this->modificationDate;
  833.     }
  834.     /**
  835.      * {@inheritdoc}
  836.      */
  837.     public function getParentId()
  838.     {
  839.         return $this->parentId;
  840.     }
  841.     /**
  842.      * {@inheritdoc}
  843.      */
  844.     public function getPath()
  845.     {
  846.         // check for site, if so rewrite the path for output
  847.         try {
  848.             if (\Pimcore\Tool::isFrontend() && Site::isSiteRequest()) {
  849.                 $site Site::getCurrentSite();
  850.                 if ($site instanceof Site) {
  851.                     if ($site->getRootDocument() instanceof Document\Page && $site->getRootDocument() !== $this) {
  852.                         $rootPath $site->getRootPath();
  853.                         $rootPath preg_quote($rootPath'@');
  854.                         $link preg_replace('@^' $rootPath '@'''$this->path);
  855.                         return $link;
  856.                     }
  857.                 }
  858.             }
  859.         } catch (\Exception $e) {
  860.             Logger::error($e);
  861.         }
  862.         return $this->path;
  863.     }
  864.     /**
  865.      * {@inheritdoc}
  866.      */
  867.     public function getRealPath()
  868.     {
  869.         return $this->path;
  870.     }
  871.     /**
  872.      * {@inheritdoc}
  873.      */
  874.     public function getRealFullPath()
  875.     {
  876.         $path $this->getRealPath() . $this->getKey();
  877.         return $path;
  878.     }
  879.     /**
  880.      * {@inheritdoc}
  881.      */
  882.     public function setCreationDate($creationDate)
  883.     {
  884.         $this->creationDate = (int) $creationDate;
  885.         return $this;
  886.     }
  887.     /**
  888.      * {@inheritdoc}
  889.      */
  890.     public function setId($id)
  891.     {
  892.         $this->id = (int) $id;
  893.         return $this;
  894.     }
  895.     /**
  896.      * {@inheritdoc}
  897.      */
  898.     public function setKey($key)
  899.     {
  900.         $this->key $key;
  901.         return $this;
  902.     }
  903.     /**
  904.      * {@inheritdoc}
  905.      */
  906.     public function setModificationDate($modificationDate)
  907.     {
  908.         $this->markFieldDirty('modificationDate');
  909.         $this->modificationDate = (int) $modificationDate;
  910.         return $this;
  911.     }
  912.     /**
  913.      * Set the parent id of the document.
  914.      *
  915.      * @param int $parentId
  916.      *
  917.      * @return Document
  918.      */
  919.     public function setParentId($parentId)
  920.     {
  921.         $this->parentId = (int) $parentId;
  922.         $this->parent null;
  923.         $this->siblings = [];
  924.         $this->hasSiblings = [];
  925.         return $this;
  926.     }
  927.     /**
  928.      * {@inheritdoc}
  929.      */
  930.     public function setPath($path)
  931.     {
  932.         $this->path $path;
  933.         return $this;
  934.     }
  935.     /**
  936.      * Returns the document index.
  937.      *
  938.      * @return int|null
  939.      */
  940.     public function getIndex(): ?int
  941.     {
  942.         return $this->index;
  943.     }
  944.     /**
  945.      * Set the document index.
  946.      *
  947.      * @param int $index
  948.      *
  949.      * @return Document
  950.      */
  951.     public function setIndex($index)
  952.     {
  953.         $this->index = (int) $index;
  954.         return $this;
  955.     }
  956.     /**
  957.      * {@inheritdoc}
  958.      */
  959.     public function getType()
  960.     {
  961.         return $this->type;
  962.     }
  963.     /**
  964.      * Set the document type.
  965.      *
  966.      * @param string $type
  967.      *
  968.      * @return Document
  969.      */
  970.     public function setType($type)
  971.     {
  972.         $this->type $type;
  973.         return $this;
  974.     }
  975.     /**
  976.      * {@inheritdoc}
  977.      */
  978.     public function getUserModification()
  979.     {
  980.         return $this->userModification;
  981.     }
  982.     /**
  983.      * {@inheritdoc}
  984.      */
  985.     public function getUserOwner()
  986.     {
  987.         return $this->userOwner;
  988.     }
  989.     /**
  990.      * {@inheritdoc}
  991.      */
  992.     public function setUserModification($userModification)
  993.     {
  994.         $this->markFieldDirty('userModification');
  995.         $this->userModification = (int) $userModification;
  996.         return $this;
  997.     }
  998.     /**
  999.      * {@inheritdoc}
  1000.      */
  1001.     public function setUserOwner($userOwner)
  1002.     {
  1003.         $this->userOwner = (int) $userOwner;
  1004.         return $this;
  1005.     }
  1006.     /**
  1007.      * @return bool
  1008.      */
  1009.     public function isPublished()
  1010.     {
  1011.         return $this->getPublished();
  1012.     }
  1013.     /**
  1014.      * @return bool
  1015.      */
  1016.     public function getPublished()
  1017.     {
  1018.         return (bool) $this->published;
  1019.     }
  1020.     /**
  1021.      * @param bool $published
  1022.      *
  1023.      * @return Document
  1024.      */
  1025.     public function setPublished($published)
  1026.     {
  1027.         $this->published = (bool) $published;
  1028.         return $this;
  1029.     }
  1030.     /**
  1031.      * {@inheritdoc}
  1032.      */
  1033.     public function getProperties()
  1034.     {
  1035.         if ($this->properties === null) {
  1036.             // try to get from cache
  1037.             $cacheKey 'document_properties_' $this->getId();
  1038.             $properties \Pimcore\Cache::load($cacheKey);
  1039.             if (!is_array($properties)) {
  1040.                 $properties $this->getDao()->getProperties();
  1041.                 $elementCacheTag $this->getCacheTag();
  1042.                 $cacheTags = ['document_properties' => 'document_properties'$elementCacheTag => $elementCacheTag];
  1043.                 \Pimcore\Cache::save($properties$cacheKey$cacheTags);
  1044.             }
  1045.             $this->setProperties($properties);
  1046.         }
  1047.         return $this->properties;
  1048.     }
  1049.     /**
  1050.      * {@inheritdoc}
  1051.      */
  1052.     public function setProperties(?array $properties)
  1053.     {
  1054.         $this->properties $properties;
  1055.         return $this;
  1056.     }
  1057.     /**
  1058.      * {@inheritdoc}
  1059.      */
  1060.     public function setProperty($name$type$data$inherited false$inheritable false)
  1061.     {
  1062.         $this->getProperties();
  1063.         $property = new Property();
  1064.         $property->setType($type);
  1065.         $property->setCid($this->getId());
  1066.         $property->setName($name);
  1067.         $property->setCtype('document');
  1068.         $property->setData($data);
  1069.         $property->setInherited($inherited);
  1070.         $property->setInheritable($inheritable);
  1071.         $this->properties[$name] = $property;
  1072.         return $this;
  1073.     }
  1074.     /**
  1075.      * {@inheritdoc}
  1076.      */
  1077.     public function getParent()
  1078.     {
  1079.         if ($this->parent === null) {
  1080.             $this->setParent(Document::getById($this->getParentId()));
  1081.         }
  1082.         return $this->parent;
  1083.     }
  1084.     /**
  1085.      * Set the parent document instance.
  1086.      *
  1087.      * @param Document $parent
  1088.      *
  1089.      * @return Document
  1090.      */
  1091.     public function setParent($parent)
  1092.     {
  1093.         $this->parent $parent;
  1094.         if ($parent instanceof Document) {
  1095.             $this->parentId $parent->getId();
  1096.         }
  1097.         return $this;
  1098.     }
  1099.     public function __sleep()
  1100.     {
  1101.         $parentVars parent::__sleep();
  1102.         $blockedVars = ['hasChildren''versions''scheduledTasks''parent''fullPathCache'];
  1103.         if ($this->isInDumpState()) {
  1104.             // this is if we want to make a full dump of the object (eg. for a new version), including children for recyclebin
  1105.             $this->removeInheritedProperties();
  1106.         } else {
  1107.             // this is if we want to cache the object
  1108.             $blockedVars array_merge($blockedVars, ['children''properties']);
  1109.         }
  1110.         return array_diff($parentVars$blockedVars);
  1111.     }
  1112.     public function __wakeup()
  1113.     {
  1114.         if ($this->isInDumpState()) {
  1115.             // 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)
  1116.             $originalElement Document::getById($this->getId());
  1117.             if ($originalElement) {
  1118.                 $this->setKey($originalElement->getKey());
  1119.                 $this->setPath($originalElement->getRealPath());
  1120.             }
  1121.         }
  1122.         if ($this->isInDumpState() && $this->properties !== null) {
  1123.             $this->renewInheritedProperties();
  1124.         }
  1125.         $this->setInDumpState(false);
  1126.     }
  1127.     /**
  1128.      * Set true if want to hide documents.
  1129.      *
  1130.      * @param bool $hideUnpublished
  1131.      */
  1132.     public static function setHideUnpublished($hideUnpublished)
  1133.     {
  1134.         self::$hideUnpublished $hideUnpublished;
  1135.     }
  1136.     /**
  1137.      * Checks if unpublished documents should be hidden.
  1138.      *
  1139.      * @return bool
  1140.      */
  1141.     public static function doHideUnpublished()
  1142.     {
  1143.         return self::$hideUnpublished;
  1144.     }
  1145.     /**
  1146.      * {@inheritdoc}
  1147.      */
  1148.     public function getVersionCount(): int
  1149.     {
  1150.         return $this->versionCount $this->versionCount 0;
  1151.     }
  1152.     /**
  1153.      * {@inheritdoc}
  1154.      */
  1155.     public function setVersionCount(?int $versionCount): ElementInterface
  1156.     {
  1157.         $this->versionCount = (int) $versionCount;
  1158.         return $this;
  1159.     }
  1160.     /**
  1161.      * @internal
  1162.      *
  1163.      * @param array $args
  1164.      *
  1165.      * @return string
  1166.      */
  1167.     protected function getListingCacheKey(array $args = [])
  1168.     {
  1169.         $unpublished = (bool)($args[0] ?? false);
  1170.         $cacheKey = (string)$unpublished;
  1171.         return $cacheKey;
  1172.     }
  1173.     public function __clone()
  1174.     {
  1175.         parent::__clone();
  1176.         $this->parent null;
  1177.         $this->hasSiblings = [];
  1178.         $this->siblings = [];
  1179.         $this->fullPathCache null;
  1180.     }
  1181. }