vendor/pimcore/pimcore/models/Document/Service.php line 530

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 Enterprise License (PEL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  * @category   Pimcore
  12.  * @package    Document
  13.  *
  14.  * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  15.  * @license    http://www.pimcore.org/license     GPLv3 and PEL
  16.  */
  17. namespace Pimcore\Model\Document;
  18. use Pimcore\Document\Renderer\DocumentRenderer;
  19. use Pimcore\Document\Renderer\DocumentRendererInterface;
  20. use Pimcore\Event\DocumentEvents;
  21. use Pimcore\Event\Model\DocumentEvent;
  22. use Pimcore\File;
  23. use Pimcore\Model;
  24. use Pimcore\Model\Document;
  25. use Pimcore\Model\Element;
  26. use Pimcore\Tool;
  27. use Pimcore\Tool\Serialize;
  28. use Symfony\Component\HttpFoundation\Request;
  29. /**
  30.  * @method \Pimcore\Model\Document\Service\Dao getDao()
  31.  * @method array getTranslations(Document $document)
  32.  * @method addTranslation(Document $document, Document $translation, $language = null)
  33.  * @method removeTranslation(Document $document)
  34.  * @method int getTranslationSourceId(Document $document)
  35.  */
  36. class Service extends Model\Element\Service
  37. {
  38.     /**
  39.      * @var Model\User|null
  40.      */
  41.     protected $_user;
  42.     /**
  43.      * @var array
  44.      */
  45.     protected $_copyRecursiveIds;
  46.     /**
  47.      * @var Document[]
  48.      */
  49.     protected $nearestPathCache;
  50.     /**
  51.      * @param Model\User $user
  52.      */
  53.     public function __construct($user null)
  54.     {
  55.         $this->_user $user;
  56.     }
  57.     /**
  58.      * Renders a document outside of a view
  59.      *
  60.      * Parameter order was kept for BC (useLayout before query and options).
  61.      *
  62.      * @static
  63.      *
  64.      * @param Document\PageSnippet $document
  65.      * @param array $attributes
  66.      * @param bool $useLayout
  67.      * @param array $query
  68.      * @param array $options
  69.      *
  70.      * @return string
  71.      */
  72.     public static function render(Document\PageSnippet $document, array $attributes = [], $useLayout false, array $query = [], array $options = []): string
  73.     {
  74.         $container = \Pimcore::getContainer();
  75.         /** @var DocumentRendererInterface $renderer */
  76.         $renderer $container->get(DocumentRenderer::class);
  77.         // keep useLayout compatibility
  78.         $attributes['_useLayout'] = $useLayout;
  79.         // set locale based on document
  80.         $localeService $container->get('pimcore.locale');
  81.         $documentLocale $document->getProperty('language');
  82.         $tempLocale $localeService->getLocale();
  83.         if ($documentLocale) {
  84.             $localeService->setLocale($documentLocale);
  85.         }
  86.         $content $renderer->render($document$attributes$query$options);
  87.         // restore original locale
  88.         $localeService->setLocale($tempLocale);
  89.         return $content;
  90.     }
  91.     /**
  92.      * Save document and all child documents
  93.      *
  94.      * @param     $document
  95.      * @param int $collectGarbageAfterIteration
  96.      * @param int $saved
  97.      */
  98.     public static function saveRecursive($document$collectGarbageAfterIteration 25, &$saved 0)
  99.     {
  100.         if ($document instanceof Document) {
  101.             $document->save();
  102.             $saved++;
  103.             if ($saved $collectGarbageAfterIteration === 0) {
  104.                 \Pimcore::collectGarbage();
  105.             }
  106.         }
  107.         foreach ($document->getChildren() as $child) {
  108.             if (!$child->hasChildren()) {
  109.                 $child->save();
  110.                 $saved++;
  111.                 if ($saved $collectGarbageAfterIteration === 0) {
  112.                     \Pimcore::collectGarbage();
  113.                 }
  114.             }
  115.             if ($child->hasChildren()) {
  116.                 self::saveRecursive($child$collectGarbageAfterIteration$saved);
  117.             }
  118.         }
  119.     }
  120.     /**
  121.      * @param  Document $target
  122.      * @param  Document $source
  123.      *
  124.      * @return Document copied document
  125.      */
  126.     public function copyRecursive($target$source)
  127.     {
  128.         // avoid recursion
  129.         if (!$this->_copyRecursiveIds) {
  130.             $this->_copyRecursiveIds = [];
  131.         }
  132.         if (in_array($source->getId(), $this->_copyRecursiveIds)) {
  133.             return;
  134.         }
  135.         if (method_exists($source'getElements')) {
  136.             $source->getElements();
  137.         }
  138.         $source->getProperties();
  139.         $new Element\Service::cloneMe($source);
  140.         $new->setId(null);
  141.         $new->setChildren(null);
  142.         $new->setKey(Element\Service::getSaveCopyName('document'$new->getKey(), $target));
  143.         $new->setParentId($target->getId());
  144.         $new->setUserOwner($this->_user $this->_user->getId() : 0);
  145.         $new->setUserModification($this->_user $this->_user->getId() : 0);
  146.         $new->setDao(null);
  147.         $new->setLocked(false);
  148.         $new->setCreationDate(time());
  149.         if (method_exists($new'setPrettyUrl')) {
  150.             $new->setPrettyUrl(null);
  151.         }
  152.         $new->save();
  153.         // add to store
  154.         $this->_copyRecursiveIds[] = $new->getId();
  155.         foreach ($source->getChildren(true) as $child) {
  156.             $this->copyRecursive($new$child);
  157.         }
  158.         $this->updateChildren($target$new);
  159.         // triggers actions after the complete document cloning
  160.         \Pimcore::getEventDispatcher()->dispatch(DocumentEvents::POST_COPY, new DocumentEvent($new, [
  161.             'base_element' => $source // the element used to make a copy
  162.         ]));
  163.         return $new;
  164.     }
  165.     /**
  166.      * @param Document $target
  167.      * @param Document $source
  168.      * @param bool $enableInheritance
  169.      * @param bool $resetIndex
  170.      *
  171.      * @return Document
  172.      *
  173.      * @throws \Exception
  174.      */
  175.     public function copyAsChild($target$source$enableInheritance false$resetIndex false$language false)
  176.     {
  177.         if (method_exists($source'getElements')) {
  178.             $source->getElements();
  179.         }
  180.         $source->getProperties();
  181.         /**
  182.          * @var Document $new
  183.          */
  184.         $new Element\Service::cloneMe($source);
  185.         $new->setId(null);
  186.         $new->setChildren(null);
  187.         $new->setKey(Element\Service::getSaveCopyName('document'$new->getKey(), $target));
  188.         $new->setParentId($target->getId());
  189.         $new->setUserOwner($this->_user $this->_user->getId() : 0);
  190.         $new->setUserModification($this->_user $this->_user->getId() : 0);
  191.         $new->setDao(null);
  192.         $new->setLocked(false);
  193.         $new->setCreationDate(time());
  194.         if ($resetIndex) {
  195.             // this needs to be after $new->setParentId($target->getId()); -> dependency!
  196.             $new->setIndex($new->getDao()->getNextIndex());
  197.         }
  198.         if (method_exists($new'setPrettyUrl')) {
  199.             $new->setPrettyUrl(null);
  200.         }
  201.         if ($enableInheritance && ($new instanceof Document\PageSnippet)) {
  202.             $new->setElements([]);
  203.             $new->setContentMasterDocumentId($source->getId());
  204.         }
  205.         if ($language) {
  206.             $new->setProperty('language''text'$languagefalse);
  207.         }
  208.         $new->save();
  209.         $this->updateChildren($target$new);
  210.         //link translated document
  211.         if ($language) {
  212.             $this->addTranslation($source$new$language);
  213.         }
  214.         // triggers actions after the complete document cloning
  215.         \Pimcore::getEventDispatcher()->dispatch(DocumentEvents::POST_COPY, new DocumentEvent($new, [
  216.             'base_element' => $source // the element used to make a copy
  217.         ]));
  218.         return $new;
  219.     }
  220.     /**
  221.      * @param $target
  222.      * @param $source
  223.      *
  224.      * @return mixed
  225.      *
  226.      * @throws \Exception
  227.      */
  228.     public function copyContents($target$source)
  229.     {
  230.         // check if the type is the same
  231.         if (get_class($source) != get_class($target)) {
  232.             throw new \Exception('Source and target have to be the same type');
  233.         }
  234.         if ($source instanceof Document\PageSnippet) {
  235.             $target->setElements($source->getElements());
  236.             $target->setTemplate($source->getTemplate());
  237.             $target->setAction($source->getAction());
  238.             $target->setController($source->getController());
  239.             if ($source instanceof Document\Page) {
  240.                 $target->setTitle($source->getTitle());
  241.                 $target->setDescription($source->getDescription());
  242.             }
  243.         } elseif ($source instanceof Document\Link) {
  244.             $target->setInternalType($source->getInternalType());
  245.             $target->setInternal($source->getInternal());
  246.             $target->setDirect($source->getDirect());
  247.             $target->setLinktype($source->getLinktype());
  248.         }
  249.         $target->setUserModification($this->_user $this->_user->getId() : 0);
  250.         $target->setProperties($source->getProperties());
  251.         $target->save();
  252.         return $target;
  253.     }
  254.     /**
  255.      * @param Document $document
  256.      *
  257.      * @return array
  258.      */
  259.     public static function gridDocumentData($document)
  260.     {
  261.         $data Element\Service::gridElementData($document);
  262.         if ($document instanceof Document\Page) {
  263.             $data['title'] = $document->getTitle();
  264.             $data['description'] = $document->getDescription();
  265.         } else {
  266.             $data['title'] = '';
  267.             $data['description'] = '';
  268.             $data['name'] = '';
  269.         }
  270.         return $data;
  271.     }
  272.     /**
  273.      * @static
  274.      *
  275.      * @param $doc
  276.      *
  277.      * @return mixed
  278.      */
  279.     public static function loadAllDocumentFields($doc)
  280.     {
  281.         $doc->getProperties();
  282.         if ($doc instanceof Document\PageSnippet) {
  283.             foreach ($doc->getElements() as $name => $data) {
  284.                 if (method_exists($data'load')) {
  285.                     $data->load();
  286.                 }
  287.             }
  288.         }
  289.         return $doc;
  290.     }
  291.     /**
  292.      * @static
  293.      *
  294.      * @param $path
  295.      * @param $type
  296.      *
  297.      * @return bool
  298.      */
  299.     public static function pathExists($path$type null)
  300.     {
  301.         $path Element\Service::correctPath($path);
  302.         try {
  303.             $document = new Document();
  304.             // validate path
  305.             if (self::isValidPath($path'document')) {
  306.                 $document->getDao()->getByPath($path);
  307.                 return true;
  308.             }
  309.         } catch (\Exception $e) {
  310.         }
  311.         return false;
  312.     }
  313.     /**
  314.      * @param $type
  315.      *
  316.      * @return bool
  317.      */
  318.     public static function isValidType($type)
  319.     {
  320.         return in_array($typeDocument::getTypes());
  321.     }
  322.     /**
  323.      * Rewrites id from source to target, $rewriteConfig contains
  324.      * array(
  325.      *  "document" => array(
  326.      *      SOURCE_ID => TARGET_ID,
  327.      *      SOURCE_ID => TARGET_ID
  328.      *  ),
  329.      *  "object" => array(...),
  330.      *  "asset" => array(...)
  331.      * )
  332.      *
  333.      * @param $document
  334.      * @param $rewriteConfig
  335.      * @param array $params
  336.      *
  337.      * @return Document
  338.      */
  339.     public static function rewriteIds($document$rewriteConfig$params = [])
  340.     {
  341.         // rewriting elements only for snippets and pages
  342.         if ($document instanceof Document\PageSnippet) {
  343.             if (array_key_exists('enableInheritance'$params) && $params['enableInheritance']) {
  344.                 $elements $document->getElements();
  345.                 $changedElements = [];
  346.                 $contentMaster $document->getContentMasterDocument();
  347.                 if ($contentMaster instanceof Document\PageSnippet) {
  348.                     $contentMasterElements $contentMaster->getElements();
  349.                     foreach ($contentMasterElements as $contentMasterElement) {
  350.                         if (method_exists($contentMasterElement'rewriteIds')) {
  351.                             $element = clone $contentMasterElement;
  352.                             $element->rewriteIds($rewriteConfig);
  353.                             if (Serialize::serialize($element) != Serialize::serialize($contentMasterElement)) {
  354.                                 $changedElements[] = $element;
  355.                             }
  356.                         }
  357.                     }
  358.                 }
  359.                 if (count($changedElements) > 0) {
  360.                     $elements $changedElements;
  361.                 }
  362.             } else {
  363.                 $elements $document->getElements();
  364.                 foreach ($elements as &$element) {
  365.                     if (method_exists($element'rewriteIds')) {
  366.                         $element->rewriteIds($rewriteConfig);
  367.                     }
  368.                 }
  369.             }
  370.             $document->setElements($elements);
  371.         } elseif ($document instanceof Document\Hardlink) {
  372.             if (array_key_exists('document'$rewriteConfig) && $document->getSourceId() && array_key_exists((int) $document->getSourceId(), $rewriteConfig['document'])) {
  373.                 $document->setSourceId($rewriteConfig['document'][(int) $document->getSourceId()]);
  374.             }
  375.         } elseif ($document instanceof Document\Link) {
  376.             if (array_key_exists('document'$rewriteConfig) && $document->getLinktype() == 'internal' && $document->getInternalType() == 'document' && array_key_exists((int) $document->getInternal(), $rewriteConfig['document'])) {
  377.                 $document->setInternal($rewriteConfig['document'][(int) $document->getInternal()]);
  378.             }
  379.         }
  380.         // rewriting properties
  381.         $properties $document->getProperties();
  382.         foreach ($properties as &$property) {
  383.             $property->rewriteIds($rewriteConfig);
  384.         }
  385.         $document->setProperties($properties);
  386.         return $document;
  387.     }
  388.     /**
  389.      * @param string $url
  390.      *
  391.      * @return Document|null
  392.      */
  393.     public static function getByUrl($url)
  394.     {
  395.         $urlParts parse_url($url);
  396.         $document null;
  397.         if ($urlParts['path']) {
  398.             $document Document::getByPath($urlParts['path']);
  399.             // search for a page in a site
  400.             if (!$document) {
  401.                 $sitesList = new Model\Site\Listing();
  402.                 $sitesObjects $sitesList->load();
  403.                 foreach ($sitesObjects as $site) {
  404.                     if ($site->getRootDocument() && (in_array($urlParts['host'], $site->getDomains()) || $site->getMainDomain() == $urlParts['host'])) {
  405.                         if ($document Document::getByPath($site->getRootDocument() . $urlParts['path'])) {
  406.                             break;
  407.                         }
  408.                     }
  409.                 }
  410.             }
  411.         }
  412.         return $document;
  413.     }
  414.     /**
  415.      * @param $item
  416.      * @param int $nr
  417.      *
  418.      * @return mixed|string
  419.      *
  420.      * @throws \Exception
  421.      */
  422.     public static function getUniqueKey($item$nr 0)
  423.     {
  424.         $list = new Listing();
  425.         $list->setUnpublished(true);
  426.         $key Element\Service::getValidKey($item->getKey(), 'document');
  427.         if (!$key) {
  428.             throw new \Exception('No item key set.');
  429.         }
  430.         if ($nr) {
  431.             $key $key '_' $nr;
  432.         }
  433.         $parent $item->getParent();
  434.         if (!$parent) {
  435.             throw new \Exception('You have to set a parent document to determine a unique Key');
  436.         }
  437.         if (!$item->getId()) {
  438.             $list->setCondition('parentId = ? AND `key` = ? ', [$parent->getId(), $key]);
  439.         } else {
  440.             $list->setCondition('parentId = ? AND `key` = ? AND id != ? ', [$parent->getId(), $key$item->getId()]);
  441.         }
  442.         $check $list->loadIdList();
  443.         if (!empty($check)) {
  444.             $nr++;
  445.             $key self::getUniqueKey($item$nr);
  446.         }
  447.         return $key;
  448.     }
  449.     /**
  450.      * Get the nearest document by path. Used to match nearest document for a static route.
  451.      *
  452.      * @param string|Request $path
  453.      * @param bool $ignoreHardlinks
  454.      * @param array $types
  455.      *
  456.      * @return Document|Document\PageSnippet|null
  457.      */
  458.     public function getNearestDocumentByPath($path$ignoreHardlinks false$types = [])
  459.     {
  460.         if ($path instanceof Request) {
  461.             $path urldecode($path->getPathInfo());
  462.         }
  463.         $cacheKey $ignoreHardlinks implode('-'$types);
  464.         $document null;
  465.         if (isset($this->nearestPathCache[$cacheKey])) {
  466.             $document $this->nearestPathCache[$cacheKey];
  467.         } else {
  468.             $paths = ['/'];
  469.             $tmpPaths = [];
  470.             $pathParts explode('/'$path);
  471.             foreach ($pathParts as $pathPart) {
  472.                 $tmpPaths[] = $pathPart;
  473.                 $t implode('/'$tmpPaths);
  474.                 if (!empty($t)) {
  475.                     $paths[] = $t;
  476.                 }
  477.             }
  478.             $paths array_reverse($paths);
  479.             foreach ($paths as $p) {
  480.                 if ($document Document::getByPath($p)) {
  481.                     if (empty($types) || in_array($document->getType(), $types)) {
  482.                         $document $this->nearestPathCache[$cacheKey] = $document;
  483.                         break;
  484.                     }
  485.                 } elseif (Model\Site::isSiteRequest()) {
  486.                     // also check for a pretty url in a site
  487.                     $site Model\Site::getCurrentSite();
  488.                     // undo the changed made by the site detection in self::match()
  489.                     $originalPath preg_replace('@^' $site->getRootPath() . '@'''$p);
  490.                     $sitePrettyDocId $this->getDao()->getDocumentIdByPrettyUrlInSite($site$originalPath);
  491.                     if ($sitePrettyDocId) {
  492.                         if ($sitePrettyDoc Document::getById($sitePrettyDocId)) {
  493.                             $document $this->nearestPathCache[$cacheKey] = $sitePrettyDoc;
  494.                             break;
  495.                         }
  496.                     }
  497.                 }
  498.             }
  499.         }
  500.         if ($document) {
  501.             if (!$ignoreHardlinks) {
  502.                 if ($document instanceof Document\Hardlink) {
  503.                     if ($hardLinkedDocument Document\Hardlink\Service::getNearestChildByPath($document$path)) {
  504.                         $document $hardLinkedDocument;
  505.                     } else {
  506.                         $document Document\Hardlink\Service::wrap($document);
  507.                     }
  508.                 }
  509.             }
  510.             return $document;
  511.         }
  512.         return null;
  513.     }
  514.     /**
  515.      * @param $id
  516.      * @param Request $request
  517.      * @param string $hostUrl
  518.      *
  519.      * @return bool
  520.      *
  521.      * @throws \Exception
  522.      */
  523.     public static function generatePagePreview($id$request null$hostUrl null)
  524.     {
  525.         $success false;
  526.         $doc Document::getById($id);
  527.         if (!$hostUrl) {
  528.             $hostUrl Tool::getHostUrl(false$request);
  529.         }
  530.         $url $hostUrl $doc->getRealFullPath();
  531.         $config = \Pimcore\Config::getSystemConfig();
  532.         if ($config->general->http_auth) {
  533.             $username $config->general->http_auth->username;
  534.             $password $config->general->http_auth->password;
  535.             if ($username && $password) {
  536.                 $url str_replace('://''://' $username .':'$password '@'$url);
  537.             }
  538.         }
  539.         $tmpFile PIMCORE_SYSTEM_TEMP_DIRECTORY '/screenshot_tmp_' $doc->getId() . '.png';
  540.         $file $doc->getPreviewImageFilesystemPath();
  541.         $dir dirname($file);
  542.         if (!is_dir($dir)) {
  543.             File::mkdir($dir);
  544.         }
  545.         if (\Pimcore\Image\HtmlToImage::convert($url$tmpFile)) {
  546.             $im = \Pimcore\Image::getInstance();
  547.             $im->load($tmpFile);
  548.             $im->scaleByWidth(400);
  549.             $im->save($file'jpeg'85);
  550.             // HDPi version
  551.             $im = \Pimcore\Image::getInstance();
  552.             $im->load($tmpFile);
  553.             $im->scaleByWidth(800);
  554.             $im->save($doc->getPreviewImageFilesystemPath(true), 'jpeg'85);
  555.             unlink($tmpFile);
  556.             $success true;
  557.         }
  558.         return $success;
  559.     }
  560. }