vendor/pimcore/pimcore/lib/Templating/Renderer/IncludeRenderer.php line 82

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\Templating\Renderer;
  15. use Pimcore\Cache;
  16. use Pimcore\Model;
  17. use Pimcore\Model\Document\PageSnippet;
  18. use Pimcore\Model\Document\Targeting\TargetingDocumentInterface;
  19. use Pimcore\Model\Element;
  20. use Pimcore\Targeting\Document\DocumentTargetingConfigurator;
  21. use Pimcore\Tool\DeviceDetector;
  22. use Pimcore\Tool\DomCrawler;
  23. use Pimcore\Tool\Frontend;
  24. /**
  25. * @internal
  26. */
  27. class IncludeRenderer
  28. {
  29. /**
  30. * @var ActionRenderer
  31. */
  32. protected $actionRenderer;
  33. /**
  34. * @var DocumentTargetingConfigurator
  35. */
  36. private $targetingConfigurator;
  37. public function __construct(
  38. ActionRenderer $actionRenderer,
  39. DocumentTargetingConfigurator $targetingConfigurator
  40. ) {
  41. $this->actionRenderer = $actionRenderer;
  42. $this->targetingConfigurator = $targetingConfigurator;
  43. }
  44. /**
  45. * Renders a document include
  46. *
  47. * @param mixed $include
  48. * @param array $params
  49. * @param bool $editmode
  50. * @param bool $cacheEnabled
  51. *
  52. * @return string
  53. */
  54. public function render($include, array $params = [], $editmode = false, $cacheEnabled = true)
  55. {
  56. if (!is_array($params)) {
  57. $params = [];
  58. }
  59. $originalInclude = $include;
  60. // this is if $this->inc is called eg. with $this->relation() as argument
  61. if (!$include instanceof PageSnippet && is_object($include) && method_exists($include, '__toString')) {
  62. $include = (string)$include;
  63. }
  64. if (is_numeric($include)) {
  65. try {
  66. $include = Model\Document::getById($include);
  67. } catch (\Exception $e) {
  68. $include = $originalInclude;
  69. }
  70. } elseif (is_string($include)) {
  71. try {
  72. $include = Model\Document::getByPath($include);
  73. } catch (\Exception $e) {
  74. $include = $originalInclude;
  75. }
  76. }
  77. if ($include instanceof PageSnippet && $include->isPublished()) {
  78. // apply best matching target group (if any)
  79. $this->targetingConfigurator->configureTargetGroup($include);
  80. }
  81. // check if output-cache is enabled, if so, we're also using the cache here
  82. $cacheKey = null;
  83. $cacheConfig = false;
  84. if ($cacheEnabled && !$editmode && $cacheConfig = Frontend::isOutputCacheEnabled()) {
  85. // cleanup params to avoid serializing Element\ElementInterface objects
  86. $cacheParams = $params;
  87. $cacheParams['~~include-document'] = $originalInclude;
  88. array_walk($cacheParams, function (&$value, $key) {
  89. if ($value instanceof Element\ElementInterface) {
  90. $value = $value->getId();
  91. } elseif (is_object($value) && method_exists($value, '__toString')) {
  92. $value = (string)$value;
  93. }
  94. });
  95. // TODO is this enough for cache or should we disable caching completely?
  96. if ($include instanceof TargetingDocumentInterface && $include->getUseTargetGroup()) {
  97. $cacheParams['target_group'] = $include->getUseTargetGroup();
  98. }
  99. $cacheKey = 'tag_inc__' . md5(serialize($cacheParams));
  100. if ($content = Cache::load($cacheKey)) {
  101. return $content;
  102. }
  103. }
  104. $params = array_merge($params, ['document' => $include]);
  105. $content = '';
  106. if ($include instanceof PageSnippet && $include->isPublished()) {
  107. $content = $this->renderAction($include, $params);
  108. if ($editmode) {
  109. $content = $this->modifyEditmodeContent($include, $content);
  110. }
  111. }
  112. // write contents to the cache, if output-cache is enabled & not in editmode
  113. if ($cacheConfig && !$editmode && !DeviceDetector::getInstance()->wasUsed()) {
  114. $cacheTags = ['output_inline'];
  115. $cacheTags[] = $cacheConfig['lifetime'] ? 'output_lifetime' : 'output';
  116. Cache::save($content, $cacheKey, $cacheTags, $cacheConfig['lifetime']);
  117. }
  118. return $content;
  119. }
  120. /**
  121. * @param PageSnippet $include
  122. * @param array $params
  123. *
  124. * @return string
  125. */
  126. protected function renderAction(PageSnippet $include, $params)
  127. {
  128. return $this->actionRenderer->render($include, $params);
  129. }
  130. /**
  131. * in editmode, we need to parse the returned html from the document include
  132. * add a class and the pimcore id / type so that it can be opened in editmode using the context menu
  133. * if there's no first level HTML container => add one (wrapper)
  134. *
  135. * @param PageSnippet $include
  136. * @param string $content
  137. *
  138. * @return string
  139. */
  140. protected function modifyEditmodeContent(PageSnippet $include, $content)
  141. {
  142. $editmodeClass = ' pimcore_editable pimcore_editable_inc ';
  143. // this is if the content that is included does already contain markup/html
  144. // this is needed by the editmode to highlight included documents
  145. try {
  146. $html = new DomCrawler($content);
  147. $childs = $html->filterXPath('//' . DomCrawler::FRAGMENT_WRAPPER_TAG . '/*'); // FRAGMENT_WRAPPER_TAG is added by DomCrawler for fragments
  148. /** @var \DOMElement $child */
  149. foreach ($childs as $child) {
  150. $child->setAttribute('class', $child->getAttribute('class') . $editmodeClass);
  151. $child->setAttribute('pimcore_type', $include->getType());
  152. $child->setAttribute('pimcore_id', (string) $include->getId());
  153. }
  154. $content = $html->html();
  155. $html->clear();
  156. unset($html);
  157. } catch (\Exception $e) {
  158. // add a div container if the include doesn't contain markup/html
  159. $content = '<div class="' . $editmodeClass . '" pimcore_id="' . $include->getId() . '" pimcore_type="' . $include->getType() . '">' . $content . '</div>';
  160. }
  161. return $content;
  162. }
  163. }