vendor/pimcore/pimcore/bundles/CoreBundle/EventListener/Frontend/EditmodeListener.php line 96

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\Bundle\CoreBundle\EventListener\Frontend;
  15. use Pimcore\Bundle\AdminBundle\Security\User\UserLoader;
  16. use Pimcore\Bundle\CoreBundle\EventListener\Traits\PimcoreContextAwareTrait;
  17. use Pimcore\Extension\Bundle\PimcoreBundleManager;
  18. use Pimcore\Http\Request\Resolver\DocumentResolver;
  19. use Pimcore\Http\Request\Resolver\EditmodeResolver;
  20. use Pimcore\Http\Request\Resolver\PimcoreContextResolver;
  21. use Pimcore\Model\Document;
  22. use Pimcore\Version;
  23. use Psr\Log\LoggerAwareTrait;
  24. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  25. use Symfony\Component\HttpFoundation\Response;
  26. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  27. use Symfony\Component\HttpKernel\Event\GetResponseEvent;
  28. use Symfony\Component\HttpKernel\KernelEvents;
  29. /**
  30.  * Modifies responses for editmode
  31.  */
  32. class EditmodeListener implements EventSubscriberInterface
  33. {
  34.     use LoggerAwareTrait;
  35.     use PimcoreContextAwareTrait;
  36.     /**
  37.      * @var EditmodeResolver
  38.      */
  39.     protected $editmodeResolver;
  40.     /**
  41.      * @var DocumentResolver
  42.      */
  43.     protected $documentResolver;
  44.     /**
  45.      * @var UserLoader
  46.      */
  47.     protected $userLoader;
  48.     /**
  49.      * @var PimcoreBundleManager
  50.      */
  51.     protected $bundleManager;
  52.     /**
  53.      * @var array
  54.      */
  55.     protected $contentTypes = [
  56.         'text/html'
  57.     ];
  58.     /**
  59.      * @param EditmodeResolver $editmodeResolver
  60.      * @param DocumentResolver $documentResolver
  61.      * @param UserLoader $userLoader
  62.      * @param PimcoreBundleManager $bundleManager
  63.      */
  64.     public function __construct(
  65.         EditmodeResolver $editmodeResolver,
  66.         DocumentResolver $documentResolver,
  67.         UserLoader $userLoader,
  68.         PimcoreBundleManager $bundleManager
  69.     ) {
  70.         $this->editmodeResolver $editmodeResolver;
  71.         $this->documentResolver $documentResolver;
  72.         $this->userLoader $userLoader;
  73.         $this->bundleManager $bundleManager;
  74.     }
  75.     /**
  76.      * @inheritdoc
  77.      */
  78.     public static function getSubscribedEvents()
  79.     {
  80.         return [
  81.             KernelEvents::REQUEST => 'onKernelRequest',
  82.             KernelEvents::RESPONSE => 'onKernelResponse'
  83.         ];
  84.     }
  85.     public function onKernelRequest(GetResponseEvent $event)
  86.     {
  87.         $request $event->getRequest();
  88.         if (!$event->isMasterRequest()) {
  89.             return; // only resolve editmode in frontend
  90.         }
  91.         if (!$this->matchesPimcoreContext($requestPimcoreContextResolver::CONTEXT_DEFAULT)) {
  92.             return;
  93.         }
  94.         // trigger this once to make sure it is resolved properly
  95.         // TODO is this needed?
  96.         $this->editmodeResolver->isEditmode($request);
  97.     }
  98.     public function onKernelResponse(FilterResponseEvent $event)
  99.     {
  100.         $request $event->getRequest();
  101.         $response $event->getResponse();
  102.         if (!$event->isMasterRequest()) {
  103.             return; // only master requests inject editmode assets
  104.         }
  105.         if (!$this->matchesPimcoreContext($requestPimcoreContextResolver::CONTEXT_DEFAULT)) {
  106.             return;
  107.         }
  108.         if (!$this->editmodeResolver->isEditmode($request)) {
  109.             return;
  110.         }
  111.         if (!$this->contentTypeMatches($response)) {
  112.             return;
  113.         }
  114.         $document $this->documentResolver->getDocument($request);
  115.         if (!$document) {
  116.             return;
  117.         }
  118.         $this->logger->info('Injecting editmode assets into request {request}', [
  119.             'request' => $request->getPathInfo()
  120.         ]);
  121.         $this->addEditmodeAssets($document$response);
  122.         // set sameorigin header for editmode responses
  123.         $response->headers->set('X-Frame-Options''SAMEORIGIN'true);
  124.     }
  125.     /**
  126.      * @param Response $response
  127.      *
  128.      * @return bool
  129.      */
  130.     protected function contentTypeMatches(Response $response)
  131.     {
  132.         $contentType $response->headers->get('Content-Type');
  133.         if (!$contentType) {
  134.             return true;
  135.         }
  136.         // check for substring as the content type could define attributes (e.g. charset)
  137.         foreach ($this->contentTypes as $ct) {
  138.             if (false !== strpos($contentType$ct)) {
  139.                 return true;
  140.             }
  141.         }
  142.         return false;
  143.     }
  144.     /**
  145.      * Inject editmode assets into response HTML
  146.      *
  147.      * @param Document $document
  148.      * @param Response $response
  149.      */
  150.     protected function addEditmodeAssets(Document $documentResponse $response)
  151.     {
  152.         if (Document\Service::isValidType($document->getType())) {
  153.             $html $response->getContent();
  154.             if (!$html) {
  155.                 return;
  156.             }
  157.             $user $this->userLoader->getUser();
  158.             $htmlElement preg_match('/<html[^a-zA-Z]?( [^>]+)?>/'$html);
  159.             $headElement preg_match('/<head[^a-zA-Z]?( [^>]+)?>/'$html);
  160.             $bodyElement preg_match('/<body[^a-zA-Z]?( [^>]+)?>/'$html);
  161.             $skipCheck false;
  162.             // if there's no head and no body, create a wrapper including these elements
  163.             // add html headers for snippets in editmode, so there is no problem with javascript
  164.             if (!$headElement && !$bodyElement && !$htmlElement) {
  165.                 $html "<!DOCTYPE html>\n<html>\n<head></head><body>" $html '</body></html>';
  166.                 $skipCheck true;
  167.             }
  168.             if ($skipCheck || ($headElement && $bodyElement && $htmlElement)) {
  169.                 $startupJavascript '/bundles/pimcoreadmin/js/pimcore/document/edit/startup.js';
  170.                 $headHtml $this->buildHeadHtml($document$user->getLanguage());
  171.                 $bodyHtml "\n\n" '<script src="' $startupJavascript '?_dc=' Version::getRevision() . '"></script>' "\n\n";
  172.                 $html preg_replace('@</head>@i'$headHtml "\n\n</head>"$html1);
  173.                 $html preg_replace('@</body>@i'$bodyHtml "\n\n</body>"$html1);
  174.                 $response->setContent($html);
  175.             } else {
  176.                 $response->setContent('<div style="font-size:30px; font-family: Arial; font-weight:bold; color:red; text-align: center; margin: 40px 0">You have to define a &lt;html&gt;, &lt;head&gt;, &lt;body&gt;<br />HTML-tag in your view/layout markup!</div>');
  177.             }
  178.         }
  179.     }
  180.     /**
  181.      * @param Document $document
  182.      * @param string $language
  183.      *
  184.      * @return string
  185.      */
  186.     protected function buildHeadHtml(Document $document$language)
  187.     {
  188.         $libraries $this->getEditmodeLibraries();
  189.         $scripts $this->getEditmodeScripts();
  190.         $stylesheets $this->getEditmodeStylesheets();
  191.         $headHtml "\n\n\n<!-- pimcore editmode -->\n";
  192.         $headHtml .= '<meta name="google" value="notranslate">';
  193.         $headHtml .= "\n\n";
  194.         // include stylesheets
  195.         foreach ($stylesheets as $stylesheet) {
  196.             $headHtml .= '<link rel="stylesheet" type="text/css" href="' $stylesheet '?_dc=' Version::getRevision() . '" />';
  197.             $headHtml .= "\n";
  198.         }
  199.         $headHtml .= "\n\n";
  200.         // include script libraries
  201.         foreach ($libraries as $script) {
  202.             $headHtml .= '<script src="' $script '?_dc=' Version::getRevision() . '"></script>';
  203.             $headHtml .= "\n";
  204.         }
  205.         // combine the pimcore scripts in non-devmode
  206.         if (\Pimcore::disableMinifyJs()) {
  207.             foreach ($scripts as $script) {
  208.                 $headHtml .= '<script src="' $script '?_dc=' Version::getRevision() . '"></script>';
  209.                 $headHtml .= "\n";
  210.             }
  211.         } else {
  212.             $scriptContents '';
  213.             foreach ($scripts as $scriptUrl) {
  214.                 $scriptContents .= file_get_contents(PIMCORE_WEB_ROOT $scriptUrl) . "\n\n\n";
  215.             }
  216.             $headHtml .= '<script src="' . \Pimcore\Tool\Admin::getMinimizedScriptPath($scriptContents) . '"></script>' "\n";
  217.         }
  218.         $headHtml .= '<script src="/admin/misc/json-translations-system?language=' $language '&_dc=' Version::getRevision() . '"></script>' "\n";
  219.         $headHtml .= "\n\n";
  220.         // set var for editable configurations which is filled by Document\Tag::admin()
  221.         $headHtml .= '<script>
  222.             var editableConfigurations = [];
  223.             var pimcore_document_id = ' $document->getId() . ';
  224.         </script>';
  225.         $headHtml .= "\n\n<!-- /pimcore editmode -->\n\n\n";
  226.         return $headHtml;
  227.     }
  228.     /**
  229.      * @return array
  230.      */
  231.     protected function getEditmodeLibraries()
  232.     {
  233.         $disableMinifyJs = \Pimcore::disableMinifyJs();
  234.         return [
  235.             '/bundles/pimcoreadmin/js/pimcore/common.js',
  236.             '/bundles/pimcoreadmin/js/lib/class.js',
  237.             '/bundles/pimcoreadmin/js/lib/ext/ext-all' . ($disableMinifyJs '-debug' '') . '.js',
  238.             '/bundles/pimcoreadmin/js/lib/ckeditor/ckeditor.js'
  239.         ];
  240.     }
  241.     /**
  242.      * @return array
  243.      */
  244.     protected function getEditmodeScripts()
  245.     {
  246.         return array_merge(
  247.             [
  248.                 '/bundles/pimcoreadmin/js/pimcore/functions.js',
  249.                 '/bundles/pimcoreadmin/js/pimcore/overrides.js',
  250.                 '/bundles/pimcoreadmin/js/pimcore/tool/milestoneslider.js',
  251.                 '/bundles/pimcoreadmin/js/pimcore/element/tag/imagehotspotmarkereditor.js',
  252.                 '/bundles/pimcoreadmin/js/pimcore/element/tag/imagecropper.js',
  253.                 '/bundles/pimcoreadmin/js/pimcore/document/edit/helper.js',
  254.                 '/bundles/pimcoreadmin/js/pimcore/elementservice.js',
  255.                 '/bundles/pimcoreadmin/js/pimcore/document/edit/dnd.js',
  256.                 '/bundles/pimcoreadmin/js/pimcore/document/tag.js',
  257.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/block.js',
  258.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/scheduledblock.js',
  259.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/date.js',
  260.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/relation.js',
  261.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/relations.js',
  262.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/checkbox.js',
  263.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/image.js',
  264.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/input.js',
  265.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/link.js',
  266.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/select.js',
  267.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/snippet.js',
  268.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/textarea.js',
  269.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/numeric.js',
  270.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/wysiwyg.js',
  271.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/renderlet.js',
  272.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/table.js',
  273.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/video.js',
  274.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/multiselect.js',
  275.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/areablock.js',
  276.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/area.js',
  277.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/pdf.js',
  278.                 '/bundles/pimcoreadmin/js/pimcore/document/tags/embed.js',
  279.                 '/bundles/pimcoreadmin/js/pimcore/document/edit/helper.js'
  280.             ],
  281.             $this->bundleManager->getEditmodeJsPaths()
  282.         );
  283.     }
  284.     /**
  285.      * @return array
  286.      */
  287.     protected function getEditmodeStylesheets()
  288.     {
  289.         return array_merge(
  290.             [
  291.                 '/bundles/pimcoreadmin/css/icons.css',
  292.                 '/bundles/pimcoreadmin/css/editmode.css?_dc=' time()
  293.             ],
  294.             $this->bundleManager->getEditmodeCssPaths()
  295.         );
  296.     }
  297. }