vendor/pimcore/pimcore/lib/Document/Editable/EditableHandler.php line 327

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.  * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  * @license    http://www.pimcore.org/license     GPLv3 and PEL
  13.  */
  14. namespace Pimcore\Document\Editable;
  15. use Pimcore\Extension\Document\Areabrick\AreabrickInterface;
  16. use Pimcore\Extension\Document\Areabrick\AreabrickManagerInterface;
  17. use Pimcore\Extension\Document\Areabrick\EditableDialogBoxInterface;
  18. use Pimcore\Extension\Document\Areabrick\Exception\ConfigurationException;
  19. use Pimcore\Extension\Document\Areabrick\TemplateAreabrickInterface;
  20. use Pimcore\Http\RequestHelper;
  21. use Pimcore\Http\ResponseStack;
  22. use Pimcore\HttpKernel\BundleLocator\BundleLocatorInterface;
  23. use Pimcore\HttpKernel\WebPathResolver;
  24. use Pimcore\Model\Document\Editable;
  25. use Pimcore\Model\Document\Editable\Area\Info;
  26. use Pimcore\Model\Document\PageSnippet;
  27. use Pimcore\Templating\Model\ViewModel;
  28. use Pimcore\Templating\Model\ViewModelInterface;
  29. use Pimcore\Templating\Renderer\ActionRenderer;
  30. use Psr\Log\LoggerAwareInterface;
  31. use Psr\Log\LoggerAwareTrait;
  32. use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
  33. use Symfony\Component\HttpFoundation\Response;
  34. use Symfony\Contracts\Translation\TranslatorInterface;
  35. class EditableHandler implements EditableHandlerInterfaceLoggerAwareInterface
  36. {
  37.     use LoggerAwareTrait;
  38.     /**
  39.      * @var AreabrickManagerInterface
  40.      */
  41.     protected $brickManager;
  42.     /**
  43.      * @var EngineInterface
  44.      */
  45.     protected $templating;
  46.     /**
  47.      * @var BundleLocatorInterface
  48.      */
  49.     protected $bundleLocator;
  50.     /**
  51.      * @var WebPathResolver
  52.      */
  53.     protected $webPathResolver;
  54.     /**
  55.      * @var ActionRenderer
  56.      */
  57.     protected $actionRenderer;
  58.     /**
  59.      * @var RequestHelper
  60.      */
  61.     protected $requestHelper;
  62.     /**
  63.      * @var TranslatorInterface
  64.      */
  65.     protected $translator;
  66.     /**
  67.      * @var ResponseStack
  68.      */
  69.     protected $responseStack;
  70.     /**
  71.      * @var array
  72.      */
  73.     protected $brickTemplateCache = [];
  74.     public const ATTRIBUTE_AREABRICK_INFO '_pimcore_areabrick_info';
  75.     /**
  76.      * @param AreabrickManagerInterface $brickManager
  77.      * @param EngineInterface $templating
  78.      * @param BundleLocatorInterface $bundleLocator
  79.      * @param WebPathResolver $webPathResolver
  80.      * @param ActionRenderer $actionRenderer
  81.      * @param RequestHelper $requestHelper
  82.      * @param TranslatorInterface $translator
  83.      * @param ResponseStack $responseStack
  84.      */
  85.     public function __construct(
  86.         AreabrickManagerInterface $brickManager,
  87.         EngineInterface $templating,
  88.         BundleLocatorInterface $bundleLocator,
  89.         WebPathResolver $webPathResolver,
  90.         ActionRenderer $actionRenderer,
  91.         RequestHelper $requestHelper,
  92.         TranslatorInterface $translator,
  93.         ResponseStack $responseStack
  94.     ) {
  95.         $this->brickManager $brickManager;
  96.         $this->templating $templating;
  97.         $this->bundleLocator $bundleLocator;
  98.         $this->webPathResolver $webPathResolver;
  99.         $this->actionRenderer $actionRenderer;
  100.         $this->requestHelper $requestHelper;
  101.         $this->translator $translator;
  102.         $this->responseStack $responseStack;
  103.     }
  104.     /**
  105.      * {@inheritdoc}
  106.      */
  107.     public function supports($view)
  108.     {
  109.         return $view instanceof ViewModelInterface;
  110.     }
  111.     /**
  112.      * @inheritDoc
  113.      */
  114.     public function isBrickEnabled(Editable $editable$brick)
  115.     {
  116.         if ($brick instanceof AreabrickInterface) {
  117.             $brick $brick->getId();
  118.         }
  119.         return $this->brickManager->isEnabled($brick);
  120.     }
  121.     /**
  122.      * {@inheritdoc}
  123.      */
  124.     public function getAvailableAreablockAreas(Editable\Areablock $editable, array $options)
  125.     {
  126.         /** @var ViewModel $view */
  127.         $view $editable->getView();
  128.         $areas = [];
  129.         foreach ($this->brickManager->getBricks() as $brick) {
  130.             // don't show disabled bricks
  131.             if (!isset($options['dontCheckEnabled']) || !$options['dontCheckEnabled']) {
  132.                 if (!$this->isBrickEnabled($editable$brick)) {
  133.                     continue;
  134.                 }
  135.             }
  136.             if (!(empty($options['allowed']) || in_array($brick->getId(), $options['allowed']))) {
  137.                 continue;
  138.             }
  139.             $name $brick->getName();
  140.             $desc $brick->getDescription();
  141.             $icon $brick->getIcon();
  142.             $limit $options['limits'][$brick->getId()] ?? null;
  143.             $hasDialogBoxConfiguration $brick instanceof EditableDialogBoxInterface;
  144.             // autoresolve icon as <bundleName>/Resources/public/areas/<id>/icon.png
  145.             if (null === $icon) {
  146.                 $bundle null;
  147.                 try {
  148.                     $bundle $this->bundleLocator->getBundle($brick);
  149.                     // check if file exists
  150.                     $iconPath sprintf('%s/Resources/public/areas/%s/icon.png'$bundle->getPath(), $brick->getId());
  151.                     if (file_exists($iconPath)) {
  152.                         // build URL to icon
  153.                         $icon $this->webPathResolver->getPath($bundle'areas/' $brick->getId(), 'icon.png');
  154.                     }
  155.                 } catch (\Exception $e) {
  156.                     $icon '';
  157.                 }
  158.             }
  159.             if ($view->getEditmode) {
  160.                 $name $this->translator->trans($name);
  161.                 $desc $this->translator->trans($desc);
  162.             }
  163.             $areas[$brick->getId()] = [
  164.                 'name' => $name,
  165.                 'description' => $desc,
  166.                 'type' => $brick->getId(),
  167.                 'icon' => $icon,
  168.                 'limit' => $limit,
  169.                 'hasDialogBoxConfiguration' => $hasDialogBoxConfiguration,
  170.             ];
  171.         }
  172.         return $areas;
  173.     }
  174.     /**
  175.      * {@inheritdoc}
  176.      */
  177.     public function renderAreaFrontend(Info $info)
  178.     {
  179.         $editable $info->getEditable();
  180.         /** @var ViewModelInterface $view */
  181.         $view $editable->getView();
  182.         $brick $this->brickManager->getBrick($info->getId());
  183.         $info->setView($view);
  184.         $request $this->requestHelper->getCurrentRequest();
  185.         $brickInfoRestoreValue $request->attributes->get(self::ATTRIBUTE_AREABRICK_INFO);
  186.         $request->attributes->set(self::ATTRIBUTE_AREABRICK_INFO$info);
  187.         $info->setRequest($request);
  188.         // call action
  189.         $this->handleBrickActionResult($brick->action($info));
  190.         // assign parameters to view
  191.         $params $info->getParams();
  192.         $view->getParameters()->add($params);
  193.         $view->getParameters()->add([
  194.             'brick' => $info// alias of `info` for compatibility reasons
  195.             'info' => $info,
  196.             'instance' => $brick,
  197.         ]);
  198.         if (!$brick->hasViewTemplate()) {
  199.             return;
  200.         }
  201.         // check if view template exists and throw error before open tag is rendered
  202.         $viewTemplate $this->resolveBrickTemplate($brick'view');
  203.         if (!$this->templating->exists($viewTemplate)) {
  204.             $e = new ConfigurationException(sprintf(
  205.                 'The view template "%s" for areabrick %s does not exist',
  206.                 $viewTemplate,
  207.                 $brick->getId()
  208.             ));
  209.             $this->logger->error($e->getMessage());
  210.             throw $e;
  211.         }
  212.         // general parameters
  213.         $editmode $view->get('editmode');
  214.         $forceEditInView array_key_exists('forceEditInView'$params) && $params['forceEditInView'];
  215.         // view parameters
  216.         $viewParameters array_merge($view->getParameters()->all(), [
  217.             // enable editmode if editmode is active and the brick has no edit template or edit in view is forced
  218.             'editmode' => $editmode ? (!$brick->hasEditTemplate() || $forceEditInView) : false,
  219.         ]);
  220.         // edit parameters
  221.         $editTemplate null;
  222.         $editParameters = [];
  223.         if ($brick->hasEditTemplate() && $editmode && !($brick instanceof EditableDialogBoxInterface)) {
  224.             $editTemplate $this->resolveBrickTemplate($brick'edit');
  225.             $editParameters array_merge($view->getParameters()->all(), [
  226.                 'editmode' => true,
  227.             ]);
  228.             @trigger_error('Using edit.html.(php|twig) in document areablocks/bricks is marked as deprecated and will be removed in Pimcore v7'E_USER_DEPRECATED);
  229.         }
  230.         // render complete areabrick
  231.         // passing the engine interface is necessary otherwise rendering a
  232.         // php template inside the twig template returns the content of the php file
  233.         // instead of actually parsing the php template
  234.         echo $this->templating->render('PimcoreCoreBundle:Areabrick:wrapper.html.twig', [
  235.             'brick' => $brick,
  236.             'info' => $info,
  237.             'templating' => $this->templating,
  238.             'editmode' => $editmode,
  239.             'viewTemplate' => $viewTemplate,
  240.             'viewParameters' => $viewParameters,
  241.             'editTemplate' => $editTemplate,
  242.             'editParameters' => $editParameters,
  243.         ]);
  244.         if ($brickInfoRestoreValue === null) {
  245.             $request->attributes->remove(self::ATTRIBUTE_AREABRICK_INFO);
  246.         } else {
  247.             $request->attributes->set(self::ATTRIBUTE_AREABRICK_INFO$brickInfoRestoreValue);
  248.         }
  249.         // call post render
  250.         $this->handleBrickActionResult($brick->postRenderAction($info));
  251.     }
  252.     protected function handleBrickActionResult($result)
  253.     {
  254.         // if the action result is a response object, push it onto the
  255.         // response stack. this response will be used by the ResponseStackListener
  256.         // and sent back to the client
  257.         if ($result instanceof Response) {
  258.             $this->responseStack->push($result);
  259.         }
  260.     }
  261.     /**
  262.      * Try to get the brick template from get*Template method. If method returns null and brick implements
  263.      * TemplateAreabrickInterface fall back to auto-resolving the template reference. See interface for examples.
  264.      *
  265.      * @param AreabrickInterface $brick
  266.      * @param string $type
  267.      *
  268.      * @return mixed|null|string
  269.      */
  270.     protected function resolveBrickTemplate(AreabrickInterface $brick$type)
  271.     {
  272.         $cacheKey sprintf('%s.%s'$brick->getId(), $type);
  273.         if (isset($this->brickTemplateCache[$cacheKey])) {
  274.             return $this->brickTemplateCache[$cacheKey];
  275.         }
  276.         $template null;
  277.         if ($type === 'view') {
  278.             $template $brick->getViewTemplate();
  279.         } elseif ($type === 'edit') {
  280.             $template $brick->getEditTemplate();
  281.         }
  282.         if (null === $template) {
  283.             if ($brick instanceof TemplateAreabrickInterface) {
  284.                 $template $this->buildBrickTemplateReference($brick$type);
  285.             } else {
  286.                 $e = new ConfigurationException(sprintf(
  287.                     'Brick %s is configured to have a %s template but does not return a template path and does not implement %s',
  288.                     $brick->getId(),
  289.                     $type,
  290.                     TemplateAreabrickInterface::class
  291.                 ));
  292.                 $this->logger->error($e->getMessage());
  293.                 throw $e;
  294.             }
  295.         }
  296.         $this->brickTemplateCache[$cacheKey] = $template;
  297.         return $template;
  298.     }
  299.     /**
  300.      * Return either bundle or global (= app/Resources) template reference
  301.      *
  302.      * @param TemplateAreabrickInterface $brick
  303.      * @param string $type
  304.      *
  305.      * @return string
  306.      */
  307.     protected function buildBrickTemplateReference(TemplateAreabrickInterface $brick$type)
  308.     {
  309.         if ($brick->getTemplateLocation() === TemplateAreabrickInterface::TEMPLATE_LOCATION_BUNDLE) {
  310.             $bundle $this->bundleLocator->getBundle($brick);
  311.             return sprintf(
  312.                 '%s:Areas/%s:%s.%s',
  313.                 $bundle->getName(),
  314.                 $brick->getId(),
  315.                 $type,
  316.                 $brick->getTemplateSuffix()
  317.             );
  318.         } else {
  319.             return sprintf(
  320.                 'Areas/%s/%s.%s',
  321.                 $brick->getId(),
  322.                 $type,
  323.                 $brick->getTemplateSuffix()
  324.             );
  325.         }
  326.     }
  327.     /**
  328.      * {@inheritdoc}
  329.      */
  330.     public function renderAction($view$controller$action$parent null, array $attributes = [], array $query = [], array $options = [])
  331.     {
  332.         $document $attributes['document'] ?? null;
  333.         if ($document && $document instanceof PageSnippet) {
  334.             unset($attributes['document']);
  335.             $attributes $this->actionRenderer->addDocumentAttributes($document$attributes);
  336.         }
  337.         $uri $this->actionRenderer->createControllerReference(
  338.             $parent,
  339.             $controller,
  340.             $action,
  341.             $attributes,
  342.             $query
  343.         );
  344.         return $this->actionRenderer->render($uri$options);
  345.     }
  346. }
  347. class_alias(EditableHandler::class, 'Pimcore\Document\Tag\TagHandler');