pimcore/lib/Pimcore/Bundle/CoreBundle/EventListener/Frontend/EditmodeListener.php line 98

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