vendor/pimcore/pimcore/bundles/AdminBundle/Controller/Admin/Document/PageController.php line 45

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\Bundle\AdminBundle\Controller\Admin\Document;
  15. use Endroid\QrCode\Builder\Builder;
  16. use Endroid\QrCode\Writer\PngWriter;
  17. use Pimcore\Document\Editable\Block\BlockStateStack;
  18. use Pimcore\Document\Editable\EditmodeEditableDefinitionCollector;
  19. use Pimcore\Document\StaticPageGenerator;
  20. use Pimcore\Http\Request\Resolver\DocumentResolver;
  21. use Pimcore\Http\Request\Resolver\EditmodeResolver;
  22. use Pimcore\Messenger\GeneratePagePreviewMessage;
  23. use Pimcore\Model\Document;
  24. use Pimcore\Model\Document\Targeting\TargetingDocumentInterface;
  25. use Pimcore\Model\Element;
  26. use Pimcore\Model\Redirect;
  27. use Pimcore\Model\Schedule\Task;
  28. use Pimcore\Templating\Renderer\EditableRenderer;
  29. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  30. use Symfony\Component\HttpFoundation\JsonResponse;
  31. use Symfony\Component\HttpFoundation\Request;
  32. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  33. use Symfony\Component\Messenger\MessageBusInterface;
  34. use Symfony\Component\Routing\Annotation\Route;
  35. use Twig\Environment;
  36. /**
  37.  * @Route("/page", name="pimcore_admin_document_page_")
  38.  *
  39.  * @internal
  40.  */
  41. class PageController extends DocumentControllerBase
  42. {
  43.     /**
  44.      * @Route("/get-data-by-id", name="getdatabyid", methods={"GET"})
  45.      *
  46.      * @param Request $request
  47.      * @param StaticPageGenerator $staticPageGenerator
  48.      *
  49.      * @return JsonResponse
  50.      *
  51.      * @throws \Exception
  52.      */
  53.     public function getDataByIdAction(Request $requestStaticPageGenerator $staticPageGenerator): JsonResponse
  54.     {
  55.         $page Document\Page::getById((int)$request->get('id'));
  56.         if (!$page) {
  57.             throw $this->createNotFoundException('Page not found');
  58.         }
  59.         if (($lock $this->checkForLock($page)) instanceof JsonResponse) {
  60.             return $lock;
  61.         }
  62.         $page = clone $page;
  63.         $draftVersion null;
  64.         $page $this->getLatestVersion($page$draftVersion);
  65.         $pageVersions Element\Service::getSafeVersionInfo($page->getVersions());
  66.         $page->setVersions(array_splice($pageVersions, -11));
  67.         $page->setParent(null);
  68.         // unset useless data
  69.         $page->setEditables(null);
  70.         $page->setChildren(null);
  71.         $data $page->getObjectVars();
  72.         $data['locked'] = $page->isLocked();
  73.         $this->addTranslationsData($page$data);
  74.         $this->minimizeProperties($page$data);
  75.         if ($page->getContentMasterDocument()) {
  76.             $data['contentMasterDocumentPath'] = $page->getContentMasterDocument()->getRealFullPath();
  77.         }
  78.         if ($page->getStaticGeneratorEnabled()) {
  79.             $data['staticLastGenerated'] = $staticPageGenerator->getLastModified($page);
  80.         }
  81.         $data['url'] = $page->getUrl();
  82.         $data['scheduledTasks'] = array_map(
  83.             static function (Task $task) {
  84.                 return $task->getObjectVars();
  85.             },
  86.             $page->getScheduledTasks()
  87.         );
  88.         return $this->preSendDataActions($data$page$draftVersion);
  89.     }
  90.     /**
  91.      * @Route("/save", name="save", methods={"PUT", "POST"})
  92.      *
  93.      * @param Request $request
  94.      * @param StaticPageGenerator $staticPageGenerator
  95.      *
  96.      * @return JsonResponse
  97.      *
  98.      * @throws \Exception
  99.      */
  100.     public function saveAction(Request $requestStaticPageGenerator $staticPageGenerator): JsonResponse
  101.     {
  102.         $oldPage Document\Page::getById((int) $request->get('id'));
  103.         if (!$oldPage) {
  104.             throw $this->createNotFoundException('Page not found');
  105.         }
  106.         /** @var Document\Page|null $pageSession */
  107.         $pageSession $this->getFromSession($oldPage);
  108.         if ($pageSession) {
  109.             $page $pageSession;
  110.         } else {
  111.             $page $this->getLatestVersion($oldPage);
  112.         }
  113.         if ($request->get('missingRequiredEditable') !== null) {
  114.             $page->setMissingRequiredEditable(($request->get('missingRequiredEditable') == 'true') ? true false);
  115.         }
  116.         $settings = [];
  117.         if ($request->get('settings')) {
  118.             $settings $this->decodeJson($request->get('settings'));
  119.             if ($settings['published'] ?? false) {
  120.                 $page->setMissingRequiredEditable(null);
  121.             }
  122.         }
  123.         // check if settings exist, before saving meta data
  124.         if ($request->get('settings') && is_array($settings)) {
  125.             $metaData = [];
  126.             for ($i 1$i 30$i++) {
  127.                 if (array_key_exists('metadata_' $i$settings)) {
  128.                     $metaData[] = $settings['metadata_' $i];
  129.                 }
  130.             }
  131.             $page->setMetaData($metaData);
  132.         }
  133.         list($task$page$version) = $this->saveDocument($page$request);
  134.         if ($task === self::TASK_PUBLISH || $task === self::TASK_UNPUBLISH) {
  135.             $treeData $this->getTreeNodeConfig($page);
  136.             $data = [
  137.                 'versionDate' => $page->getModificationDate(),
  138.                 'versionCount' => $page->getVersionCount(),
  139.             ];
  140.             if ($staticGeneratorEnabled $page->getStaticGeneratorEnabled()) {
  141.                 $data['staticGeneratorEnabled'] = $staticGeneratorEnabled;
  142.                 $data['staticLastGenerated'] = $staticPageGenerator->getLastModified($page);
  143.             }
  144.             if ($page->getPrettyUrl() !== $oldPage->getPrettyUrl()
  145.                 && empty($oldPage->getPrettyUrl()) === false
  146.                 && empty($page->getPrettyUrl()) === false
  147.             ) {
  148.                 $redirect = new Redirect();
  149.                 $redirect->setSource($oldPage->getPrettyUrl());
  150.                 $redirect->setTarget($page->getPrettyUrl());
  151.                 $redirect->setStatusCode(301);
  152.                 $redirect->setType(Redirect::TYPE_AUTO_CREATE);
  153.                 $redirect->save();
  154.             }
  155.             return $this->adminJson([
  156.                 'success' => true,
  157.                 'treeData' => $treeData,
  158.                 'data' => $data,
  159.             ]);
  160.         } else {
  161.             $this->saveToSession($page);
  162.             $draftData = [];
  163.             if ($version) {
  164.                 $draftData = [
  165.                     'id' => $version->getId(),
  166.                     'modificationDate' => $version->getDate(),
  167.                     'isAutoSave' => $version->isAutoSave(),
  168.                 ];
  169.             }
  170.             $treeData $this->getTreeNodeConfig($page);
  171.             return $this->adminJson(['success' => true'treeData' => $treeData'draft' => $draftData]);
  172.         }
  173.     }
  174.     /**
  175.      * @Route("/generate-previews", name="generatepreviews", methods={"GET"})
  176.      *
  177.      * @param Request $request
  178.      * @param MessageBusInterface $messengerBusPimcoreCore
  179.      *
  180.      * @return JsonResponse
  181.      */
  182.     public function generatePreviewsAction(Request $requestMessageBusInterface $messengerBusPimcoreCore): JsonResponse
  183.     {
  184.         $list = new Document\Listing();
  185.         $list->setCondition('type = ?', ['page']);
  186.         foreach ($list->loadIdList() as $docId) {
  187.             $messengerBusPimcoreCore->dispatch(
  188.                 new GeneratePagePreviewMessage($docId\Pimcore\Tool::getHostUrl())
  189.             );
  190.             break;
  191.         }
  192.         return $this->adminJson(['success' => true]);
  193.     }
  194.     /**
  195.      * @Route("/display-preview-image", name="display_preview_image", methods={"GET"})
  196.      *
  197.      * @param Request $request
  198.      *
  199.      * @return BinaryFileResponse
  200.      */
  201.     public function displayPreviewImageAction(Request $request): BinaryFileResponse
  202.     {
  203.         $document Document\Page::getById((int) $request->get('id'));
  204.         if ($document instanceof Document\Page) {
  205.             return new BinaryFileResponse($document->getPreviewImageFilesystemPath(), 200, [
  206.                 'Content-Type' => 'image/jpg',
  207.             ]);
  208.         }
  209.         throw $this->createNotFoundException('Page not found');
  210.     }
  211.     /**
  212.      * @Route("/check-pretty-url", name="checkprettyurl", methods={"POST"})
  213.      *
  214.      * @param Request $request
  215.      *
  216.      * @return JsonResponse
  217.      */
  218.     public function checkPrettyUrlAction(Request $request): JsonResponse
  219.     {
  220.         $docId $request->get('id');
  221.         $path = (string) trim($request->get('path'));
  222.         $success true;
  223.         if ($path === '') {
  224.             return $this->adminJson([
  225.                 'success' => $success,
  226.             ]);
  227.         }
  228.         $message = [];
  229.         $path rtrim($path'/');
  230.         // must start with /
  231.         if ($path !== '' && strpos($path'/') !== 0) {
  232.             $success false;
  233.             $message[] = 'URL must start with /.';
  234.         }
  235.         if (strlen($path) < 2) {
  236.             $success false;
  237.             $message[] = 'URL must be at least 2 characters long.';
  238.         }
  239.         if (!Element\Service::isValidPath($path'document')) {
  240.             $success false;
  241.             $message[] = 'URL is invalid.';
  242.         }
  243.         $list = new Document\Listing();
  244.         $list->setCondition('(CONCAT(path, `key`) = ? OR id IN (SELECT id from documents_page WHERE prettyUrl = ?))
  245.             AND id != ?', [
  246.             $path$path$docId,
  247.         ]);
  248.         if ($list->getTotalCount() > 0) {
  249.             $success false;
  250.             $message[] = 'URL path already exists.';
  251.         }
  252.         return $this->adminJson([
  253.             'success' => $success,
  254.             'message' => implode('<br>'$message),
  255.         ]);
  256.     }
  257.     /**
  258.      * @Route("/clear-editable-data", name="cleareditabledata", methods={"PUT"})
  259.      *
  260.      * @param Request $request
  261.      *
  262.      * @return JsonResponse
  263.      */
  264.     public function clearEditableDataAction(Request $request): JsonResponse
  265.     {
  266.         $targetGroupId $request->get('targetGroup');
  267.         $docId $request->get('id');
  268.         $doc Document\PageSnippet::getById($docId);
  269.         if (!$doc) {
  270.             throw $this->createNotFoundException('Document not found');
  271.         }
  272.         foreach ($doc->getEditables() as $editable) {
  273.             if ($targetGroupId && $doc instanceof TargetingDocumentInterface) {
  274.                 // remove target group specific elements
  275.                 if (preg_match('/^' preg_quote($doc->getTargetGroupEditablePrefix($targetGroupId), '/') . '/'$editable->getName())) {
  276.                     $doc->removeEditable($editable->getName());
  277.                 }
  278.             } else {
  279.                 // remove all but target group data
  280.                 if (!preg_match('/^' preg_quote(TargetingDocumentInterface::TARGET_GROUP_EDITABLE_PREFIX'/') . '/'$editable->getName())) {
  281.                     $doc->removeEditable($editable->getName());
  282.                 }
  283.             }
  284.         }
  285.         $this->saveToSession($doctrue);
  286.         return $this->adminJson([
  287.             'success' => true,
  288.         ]);
  289.     }
  290.     /**
  291.      * @Route("/qr-code", name="qrcode", methods={"GET"})
  292.      *
  293.      * @param Request $request
  294.      *
  295.      * @return BinaryFileResponse
  296.      *
  297.      * @throws \Exception
  298.      */
  299.     public function qrCodeAction(Request $request): BinaryFileResponse
  300.     {
  301.         $page Document\Page::getById((int) $request->query->get('id'));
  302.         if (!$page) {
  303.             throw $this->createNotFoundException('Page not found');
  304.         }
  305.         $url $page->getUrl();
  306.         $result Builder::create()
  307.             ->writer(new PngWriter())
  308.             ->data($url)
  309.             ->size($request->query->get('download') ? 4000 500)
  310.             ->build();
  311.         $tmpFile PIMCORE_SYSTEM_TEMP_DIRECTORY '/qr-code-' uniqid() . '.png';
  312.         $result->saveToFile($tmpFile);
  313.         $response = new BinaryFileResponse($tmpFile);
  314.         $response->headers->set('Content-Type''image/png');
  315.         if ($request->query->get('download')) {
  316.             $response->setContentDisposition('attachment''qrcode-preview.png');
  317.         }
  318.         $response->deleteFileAfterSend(true);
  319.         return $response;
  320.     }
  321.     /**
  322.      * @Route("/areabrick-render-index-editmode", name="areabrick-render-index-editmode", methods={"POST"})
  323.      *
  324.      * @param Request $request
  325.      * @param BlockStateStack $blockStateStack
  326.      * @param EditmodeEditableDefinitionCollector $definitionCollector
  327.      * @param Environment $twig
  328.      * @param EditableRenderer $editableRenderer
  329.      * @param DocumentResolver $documentResolver
  330.      *
  331.      * @return JsonResponse
  332.      *
  333.      * @throws NotFoundHttpException|\Exception
  334.      *
  335.      */
  336.     public function areabrickRenderIndexEditmode(
  337.         Request $request,
  338.         BlockStateStack $blockStateStack,
  339.         EditmodeEditableDefinitionCollector $definitionCollector,
  340.         Environment $twig,
  341.         EditableRenderer $editableRenderer,
  342.         DocumentResolver $documentResolver
  343.     ): JsonResponse {
  344.         $blockStateStackData json_decode($request->get('blockStateStack'), true);
  345.         $blockStateStack->loadArray($blockStateStackData);
  346.         $document Document\PageSnippet::getById((int) $request->get('documentId'));
  347.         if (!$document) {
  348.             throw $this->createNotFoundException();
  349.         }
  350.         $document = clone $document;
  351.         $document->setEditables([]);
  352.         $documentResolver->setDocument($request$document);
  353.         $twig->addGlobal('document'$document);
  354.         $twig->addGlobal('editmode'true);
  355.         // we can't use EditmodeResolver::setForceEditmode() here, because it would also render included documents in editmode
  356.         // so we use the attribute as a workaround
  357.         $request->attributes->set(EditmodeResolver::ATTRIBUTE_EDITMODEtrue);
  358.         $areaBlockConfig json_decode($request->get('areablockConfig'), true);
  359.         /** @var Document\Editable\Areablock $areablock */
  360.         $areablock $editableRenderer->getEditable($document'areablock'$request->get('realName'), $areaBlockConfigtrue);
  361.         $areablock->setRealName($request->get('realName'));
  362.         $areablock->setEditmode(true);
  363.         $areaBrickData json_decode($request->get('areablockData'), true);
  364.         $areablock->setDataFromEditmode($areaBrickData);
  365.         $htmlCode trim($areablock->renderIndex($request->get('index'), true));
  366.         return new JsonResponse([
  367.             'editableDefinitions' => $definitionCollector->getDefinitions(),
  368.             'htmlCode' => $htmlCode,
  369.         ]);
  370.     }
  371.     /**
  372.      * @param Request $request
  373.      * @param Document $page
  374.      */
  375.     protected function setValuesToDocument(Request $requestDocument $page)
  376.     {
  377.         $this->addSettingsToDocument($request$page);
  378.         $this->addDataToDocument($request$page);
  379.         $this->addPropertiesToDocument($request$page);
  380.         $this->applySchedulerDataToElement($request$page);
  381.     }
  382. }