vendor/pimcore/pimcore/bundles/AdminBundle/Controller/Admin/Document/DocumentController.php line 128

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\AdminBundle\Controller\Admin\Document;
  15. use Pimcore\Bundle\AdminBundle\Controller\Admin\ElementControllerBase;
  16. use Pimcore\Bundle\AdminBundle\Controller\Traits\AdminStyleTrait;
  17. use Pimcore\Config;
  18. use Pimcore\Controller\EventedControllerInterface;
  19. use Pimcore\Db;
  20. use Pimcore\Event\Admin\ElementAdminStyleEvent;
  21. use Pimcore\Event\AdminEvents;
  22. use Pimcore\Image\HtmlToImage;
  23. use Pimcore\Logger;
  24. use Pimcore\Model\Document;
  25. use Pimcore\Model\Redirect;
  26. use Pimcore\Model\Site;
  27. use Pimcore\Model\Version;
  28. use Pimcore\Routing\Dynamic\DocumentRouteHandler;
  29. use Pimcore\Tool;
  30. use Pimcore\Tool\Frontend;
  31. use Pimcore\Tool\Session;
  32. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  33. use Symfony\Component\EventDispatcher\GenericEvent;
  34. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  35. use Symfony\Component\HttpFoundation\JsonResponse;
  36. use Symfony\Component\HttpFoundation\Request;
  37. use Symfony\Component\HttpFoundation\Response;
  38. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
  39. use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
  40. use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
  41. use Symfony\Component\Routing\Annotation\Route;
  42. /**
  43.  * @Route("/document")
  44.  */
  45. class DocumentController extends ElementControllerBase implements EventedControllerInterface
  46. {
  47.     use AdminStyleTrait;
  48.     /**
  49.      * @var Document\Service
  50.      */
  51.     protected $_documentService;
  52.     /**
  53.      * @Route("/tree-get-root", name="pimcore_admin_document_document_treegetroot", methods={"GET"})
  54.      *
  55.      * @param Request $request
  56.      *
  57.      * @return JsonResponse
  58.      */
  59.     public function treeGetRootAction(Request $request)
  60.     {
  61.         return parent::treeGetRootAction($request);
  62.     }
  63.     /**
  64.      * @Route("/delete-info", name="pimcore_admin_document_document_deleteinfo", methods={"GET"})
  65.      *
  66.      * @param Request $request
  67.      *
  68.      * @return JsonResponse
  69.      */
  70.     public function deleteInfoAction(Request $request)
  71.     {
  72.         return parent::deleteInfoAction($request);
  73.     }
  74.     /**
  75.      * @Route("/get-data-by-id", name="pimcore_admin_document_document_getdatabyid", methods={"GET"})
  76.      *
  77.      * @param Request $request
  78.      *
  79.      * @return JsonResponse
  80.      */
  81.     public function getDataByIdAction(Request $requestEventDispatcherInterface $eventDispatcher)
  82.     {
  83.         $document Document::getById($request->get('id'));
  84.         if (!$document) {
  85.             throw $this->createNotFoundException('Document not found');
  86.         }
  87.         $document = clone $document;
  88.         $data $document->getObjectVars();
  89.         $data['versionDate'] = $document->getModificationDate();
  90.         $data['php'] = [
  91.             'classes' => array_merge([get_class($document)], array_values(class_parents($document))),
  92.             'interfaces' => array_values(class_implements($document))
  93.         ];
  94.         $this->addAdminStyle($documentElementAdminStyleEvent::CONTEXT_EDITOR$data);
  95.         $event = new GenericEvent($this, [
  96.             'data' => $data,
  97.             'document' => $document
  98.         ]);
  99.         $eventDispatcher->dispatch(AdminEvents::DOCUMENT_GET_PRE_SEND_DATA$event);
  100.         $data $event->getArgument('data');
  101.         if ($document->isAllowed('view')) {
  102.             return $this->adminJson($data);
  103.         }
  104.         throw $this->createAccessDeniedHttpException();
  105.     }
  106.     /**
  107.      * @Route("/tree-get-childs-by-id", name="pimcore_admin_document_document_treegetchildsbyid", methods={"GET"})
  108.      *
  109.      * @param Request $request
  110.      *
  111.      * @return JsonResponse
  112.      */
  113.     public function treeGetChildsByIdAction(Request $requestEventDispatcherInterface $eventDispatcher)
  114.     {
  115.         $allParams array_merge($request->request->all(), $request->query->all());
  116.         $filter $request->get('filter');
  117.         $limit intval($allParams['limit'] ?? 100000000);
  118.         $offset intval($allParams['start'] ?? 0);
  119.         if (!is_null($filter)) {
  120.             if (substr($filter, -1) != '*') {
  121.                 $filter .= '*';
  122.             }
  123.             $filter str_replace('*''%'$filter);
  124.             $limit 100;
  125.             $offset 0;
  126.         }
  127.         $document Document::getById($allParams['node']);
  128.         if (!$document) {
  129.             throw $this->createNotFoundException('Document was not found');
  130.         }
  131.         $documents = [];
  132.         $cv false;
  133.         if ($document->hasChildren()) {
  134.             if ($allParams['view']) {
  135.                 $cv = \Pimcore\Model\Element\Service::getCustomViewById($allParams['view']);
  136.             }
  137.             $db Db::get();
  138.             $list = new Document\Listing();
  139.             if ($this->getAdminUser()->isAdmin()) {
  140.                 $condition 'parentId =  ' $db->quote($document->getId());
  141.             } else {
  142.                 $userIds $this->getAdminUser()->getRoles();
  143.                 $userIds[] = $this->getAdminUser()->getId();
  144.                 $condition 'parentId = ' $db->quote($document->getId()) . ' AND
  145.                 (
  146.                     (SELECT list FROM users_workspaces_document WHERE userId IN (' implode(','$userIds) . ') AND LOCATE(CONCAT(path,`key`),cpath)=1  ORDER BY LENGTH(cpath) DESC, FIELD(userId, '$this->getAdminUser()->getId() .') DESC, list DESC LIMIT 1)=1
  147.                     or
  148.                     (SELECT list FROM users_workspaces_document WHERE userId IN (' implode(','$userIds) . ') AND LOCATE(cpath,CONCAT(path,`key`))=1  ORDER BY LENGTH(cpath) DESC, FIELD(userId, '$this->getAdminUser()->getId() .') DESC, list DESC LIMIT 1)=1
  149.                 )';
  150.             }
  151.             if ($filter) {
  152.                 $db Db::get();
  153.                 $condition '(' $condition ')' ' AND CAST(documents.key AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci LIKE ' $db->quote($filter);
  154.             }
  155.             $list->setCondition($condition);
  156.             $list->setOrderKey(['index''id']);
  157.             $list->setOrder(['asc''asc']);
  158.             $list->setLimit($limit);
  159.             $list->setOffset($offset);
  160.             \Pimcore\Model\Element\Service::addTreeFilterJoins($cv$list);
  161.             $beforeListLoadEvent = new GenericEvent($this, [
  162.                 'list' => $list,
  163.                 'context' => $allParams
  164.             ]);
  165.             $eventDispatcher->dispatch(AdminEvents::DOCUMENT_LIST_BEFORE_LIST_LOAD$beforeListLoadEvent);
  166.             /** @var Document\Listing $list */
  167.             $list $beforeListLoadEvent->getArgument('list');
  168.             $childsList $list->load();
  169.             foreach ($childsList as $childDocument) {
  170.                 // only display document if listing is allowed for the current user
  171.                 if ($childDocument->isAllowed('list')) {
  172.                     $documents[] = $this->getTreeNodeConfig($childDocument);
  173.                 }
  174.             }
  175.         }
  176.         //Hook for modifying return value - e.g. for changing permissions based on document data
  177.         $event = new GenericEvent($this, [
  178.             'documents' => $documents,
  179.         ]);
  180.         $eventDispatcher->dispatch(AdminEvents::DOCUMENT_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA$event);
  181.         $documents $event->getArgument('documents');
  182.         if ($allParams['limit']) {
  183.             return $this->adminJson([
  184.                 'offset' => $offset,
  185.                 'limit' => $limit,
  186.                 'total' => $document->getChildAmount($this->getAdminUser()),
  187.                 'nodes' => $documents,
  188.                 'filter' => $request->get('filter') ? $request->get('filter') : '',
  189.                 'inSearch' => intval($request->get('inSearch'))
  190.             ]);
  191.         } else {
  192.             return $this->adminJson($documents);
  193.         }
  194.     }
  195.     /**
  196.      * @Route("/add", name="pimcore_admin_document_document_add", methods={"POST"})
  197.      *
  198.      * @param Request $request
  199.      *
  200.      * @return JsonResponse
  201.      */
  202.     public function addAction(Request $request)
  203.     {
  204.         $success false;
  205.         $errorMessage '';
  206.         // check for permission
  207.         $parentDocument Document::getById(intval($request->get('parentId')));
  208.         $document null;
  209.         if ($parentDocument->isAllowed('create')) {
  210.             $intendedPath $parentDocument->getRealFullPath() . '/' $request->get('key');
  211.             if (!Document\Service::pathExists($intendedPath)) {
  212.                 $createValues = [
  213.                     'userOwner' => $this->getAdminUser()->getId(),
  214.                     'userModification' => $this->getAdminUser()->getId(),
  215.                     'published' => false
  216.                 ];
  217.                 $createValues['key'] = \Pimcore\Model\Element\Service::getValidKey($request->get('key'), 'document');
  218.                 // check for a docType
  219.                 $docType Document\DocType::getById(intval($request->get('docTypeId')));
  220.                 if ($docType) {
  221.                     $createValues['template'] = $docType->getTemplate();
  222.                     $createValues['controller'] = $docType->getController();
  223.                     $createValues['action'] = $docType->getAction();
  224.                     $createValues['module'] = $docType->getModule();
  225.                 } elseif ($request->get('translationsBaseDocument')) {
  226.                     $translationsBaseDocument Document\PageSnippet::getById($request->get('translationsBaseDocument'));
  227.                     $createValues['template'] = $translationsBaseDocument->getTemplate();
  228.                     $createValues['controller'] = $translationsBaseDocument->getController();
  229.                     $createValues['action'] = $translationsBaseDocument->getAction();
  230.                     $createValues['module'] = $translationsBaseDocument->getModule();
  231.                 } elseif ($request->get('type') == 'page' || $request->get('type') == 'snippet' || $request->get('type') == 'email') {
  232.                     $createValues += Tool::getRoutingDefaults();
  233.                 }
  234.                 if ($request->get('inheritanceSource')) {
  235.                     $createValues['contentMasterDocumentId'] = $request->get('inheritanceSource');
  236.                 }
  237.                 switch ($request->get('type')) {
  238.                     case 'page':
  239.                         $document Document\Page::create($request->get('parentId'), $createValuesfalse);
  240.                         $document->setTitle($request->get('title'null));
  241.                         $document->setProperty('navigation_name''text'$request->get('name'null), falsefalse);
  242.                         $document->save();
  243.                         $success true;
  244.                         break;
  245.                     case 'snippet':
  246.                         $document Document\Snippet::create($request->get('parentId'), $createValues);
  247.                         $success true;
  248.                         break;
  249.                     case 'email'//ckogler
  250.                         $document Document\Email::create($request->get('parentId'), $createValues);
  251.                         $success true;
  252.                         break;
  253.                     case 'link':
  254.                         $document Document\Link::create($request->get('parentId'), $createValues);
  255.                         $success true;
  256.                         break;
  257.                     case 'hardlink':
  258.                         $document Document\Hardlink::create($request->get('parentId'), $createValues);
  259.                         $success true;
  260.                         break;
  261.                     case 'folder':
  262.                         $document Document\Folder::create($request->get('parentId'), $createValues);
  263.                         $document->setPublished(true);
  264.                         try {
  265.                             $document->save();
  266.                             $success true;
  267.                         } catch (\Exception $e) {
  268.                             return $this->adminJson(['success' => false'message' => $e->getMessage()]);
  269.                         }
  270.                         break;
  271.                     default:
  272.                         $classname '\\Pimcore\\Model\\Document\\' ucfirst($request->get('type'));
  273.                         // this is the fallback for custom document types using prefixes
  274.                         // so we need to check if the class exists first
  275.                         if (!\Pimcore\Tool::classExists($classname)) {
  276.                             $oldStyleClass '\\Document_' ucfirst($request->get('type'));
  277.                             if (\Pimcore\Tool::classExists($oldStyleClass)) {
  278.                                 $classname $oldStyleClass;
  279.                             }
  280.                         }
  281.                         if (Tool::classExists($classname)) {
  282.                             $document $classname::create($request->get('parentId'), $createValues);
  283.                             try {
  284.                                 $document->save();
  285.                                 $success true;
  286.                             } catch (\Exception $e) {
  287.                                 return $this->adminJson(['success' => false'message' => $e->getMessage()]);
  288.                             }
  289.                             break;
  290.                         } else {
  291.                             Logger::debug("Unknown document type, can't add [ " $request->get('type') . ' ] ');
  292.                         }
  293.                         break;
  294.                 }
  295.             } else {
  296.                 $errorMessage "prevented adding a document because document with same path+key [ $intendedPath ] already exists";
  297.                 Logger::debug($errorMessage);
  298.             }
  299.         } else {
  300.             $errorMessage 'prevented adding a document because of missing permissions';
  301.             Logger::debug($errorMessage);
  302.         }
  303.         if ($success && $document instanceof Document) {
  304.             if ($request->get('translationsBaseDocument')) {
  305.                 $translationsBaseDocument Document::getById($request->get('translationsBaseDocument'));
  306.                 $properties $translationsBaseDocument->getProperties();
  307.                 $properties array_merge($properties$document->getProperties());
  308.                 $document->setProperties($properties);
  309.                 $document->setProperty('language''text'$request->get('language'));
  310.                 $document->save();
  311.                 $service = new Document\Service();
  312.                 $service->addTranslation($translationsBaseDocument$document);
  313.             }
  314.             return $this->adminJson([
  315.                 'success' => $success,
  316.                 'id' => $document->getId(),
  317.                 'type' => $document->getType()
  318.             ]);
  319.         }
  320.         return $this->adminJson([
  321.             'success' => $success,
  322.             'message' => $errorMessage
  323.         ]);
  324.     }
  325.     /**
  326.      * @Route("/delete", name="pimcore_admin_document_document_delete", methods={"DELETE"})
  327.      *
  328.      * @param Request $request
  329.      *
  330.      * @return JsonResponse
  331.      */
  332.     public function deleteAction(Request $request)
  333.     {
  334.         if ($request->get('type') == 'childs') {
  335.             $parentDocument Document::getById($request->get('id'));
  336.             $list = new Document\Listing();
  337.             $list->setCondition('path LIKE ?', [$list->escapeLike($parentDocument->getRealFullPath()) . '/%']);
  338.             $list->setLimit(intval($request->get('amount')));
  339.             $list->setOrderKey('LENGTH(path)'false);
  340.             $list->setOrder('DESC');
  341.             $documents $list->load();
  342.             $deletedItems = [];
  343.             foreach ($documents as $document) {
  344.                 $deletedItems[$document->getId()] = $document->getRealFullPath();
  345.                 if ($document->isAllowed('delete') && !$document->isLocked()) {
  346.                     $document->delete();
  347.                 }
  348.             }
  349.             return $this->adminJson(['success' => true'deleted' => $deletedItems]);
  350.         } elseif ($request->get('id')) {
  351.             $document Document::getById($request->get('id'));
  352.             if ($document && $document->isAllowed('delete')) {
  353.                 try {
  354.                     if ($document->isLocked()) {
  355.                         throw new \Exception('prevented deleting document, because it is locked: ID: ' $document->getId());
  356.                     }
  357.                     $document->delete();
  358.                     return $this->adminJson(['success' => true]);
  359.                 } catch (\Exception $e) {
  360.                     Logger::err($e);
  361.                     return $this->adminJson(['success' => false'message' => $e->getMessage()]);
  362.                 }
  363.             }
  364.         }
  365.         throw $this->createAccessDeniedHttpException();
  366.     }
  367.     /**
  368.      * @Route("/update", name="pimcore_admin_document_document_update", methods={"PUT"})
  369.      *
  370.      * @param Request $request
  371.      *
  372.      * @return JsonResponse
  373.      *
  374.      * @throws \Exception
  375.      */
  376.     public function updateAction(Request $request)
  377.     {
  378.         $success false;
  379.         $allowUpdate true;
  380.         $document Document::getById($request->get('id'));
  381.         $oldPath $document->getDao()->getCurrentFullPath();
  382.         $oldDocument Document::getById($document->getId(), true);
  383.         // this prevents the user from renaming, relocating (actions in the tree) if the newest version isn't the published one
  384.         // the reason is that otherwise the content of the newer not published version will be overwritten
  385.         if ($document instanceof Document\PageSnippet) {
  386.             $latestVersion $document->getLatestVersion();
  387.             if ($latestVersion && $latestVersion->getData()->getModificationDate() != $document->getModificationDate()) {
  388.                 return $this->adminJson(['success' => false'message' => "You can't rename or relocate if there's a newer not published version"]);
  389.             }
  390.         }
  391.         if ($document->isAllowed('settings')) {
  392.             // if the position is changed the path must be changed || also from the children
  393.             if ($request->get('parentId')) {
  394.                 $parentDocument Document::getById($request->get('parentId'));
  395.                 //check if parent is changed
  396.                 if ($document->getParentId() != $parentDocument->getId()) {
  397.                     if (!$parentDocument->isAllowed('create')) {
  398.                         throw new \Exception('Prevented moving document - no create permission on new parent ');
  399.                     }
  400.                     $intendedPath $parentDocument->getRealPath();
  401.                     $pKey $parentDocument->getKey();
  402.                     if (!empty($pKey)) {
  403.                         $intendedPath .= $parentDocument->getKey() . '/';
  404.                     }
  405.                     $documentWithSamePath Document::getByPath($intendedPath $document->getKey());
  406.                     if ($documentWithSamePath != null) {
  407.                         $allowUpdate false;
  408.                     }
  409.                     if ($document->isLocked()) {
  410.                         $allowUpdate false;
  411.                     }
  412.                 }
  413.             }
  414.             if ($allowUpdate) {
  415.                 $blockedVars = ['controller''action''module'];
  416.                 if (!$document->isAllowed('rename') && $request->get('key')) {
  417.                     $blockedVars[] = 'key';
  418.                     Logger::debug('prevented renaming document because of missing permissions ');
  419.                 }
  420.                 $updateData array_merge($request->request->all(), $request->query->all());
  421.                 foreach ($updateData as $key => $value) {
  422.                     if (!in_array($key$blockedVars)) {
  423.                         $document->setValue($key$value);
  424.                     }
  425.                 }
  426.                 $document->setUserModification($this->getAdminUser()->getId());
  427.                 try {
  428.                     $document->save();
  429.                     if ($request->get('index') !== null) {
  430.                         $this->updateIndexesOfDocumentSiblings($document$request->get('index'));
  431.                     }
  432.                     $success true;
  433.                     $this->createRedirectForFormerPath($request$document$oldPath$oldDocument);
  434.                 } catch (\Exception $e) {
  435.                     return $this->adminJson(['success' => false'message' => $e->getMessage()]);
  436.                 }
  437.             } else {
  438.                 $msg 'Prevented moving document, because document with same path+key already exists or the document is locked. ID: ' $document->getId();
  439.                 Logger::debug($msg);
  440.                 return $this->adminJson(['success' => false'message' => $msg]);
  441.             }
  442.         } elseif ($document->isAllowed('rename') && $request->get('key')) {
  443.             //just rename
  444.             try {
  445.                 $document->setKey($request->get('key'));
  446.                 $document->setUserModification($this->getAdminUser()->getId());
  447.                 $document->save();
  448.                 $success true;
  449.                 $this->createRedirectForFormerPath($request$document$oldPath$oldDocument);
  450.             } catch (\Exception $e) {
  451.                 return $this->adminJson(['success' => false'message' => $e->getMessage()]);
  452.             }
  453.         } else {
  454.             Logger::debug('Prevented update document, because of missing permissions.');
  455.         }
  456.         return $this->adminJson(['success' => $success]);
  457.     }
  458.     private function createRedirectForFormerPath(Request $requestDocument $documentstring $oldPathDocument $oldDocument)
  459.     {
  460.         if ($document instanceof Document\Page || $document instanceof Document\Hardlink) {
  461.             if ($request->get('create_redirects') === 'true' && $this->getAdminUser()->isAllowed('redirects')) {
  462.                 if ($oldPath && $oldPath != $document->getRealFullPath()) {
  463.                     $sourceSite null;
  464.                     if ($oldDocument) {
  465.                         $sourceSite Frontend::getSiteForDocument($oldDocument);
  466.                         if ($sourceSite) {
  467.                             $oldPath preg_replace('@^' preg_quote($sourceSite->getRootPath(), '@') . '@'''$oldPath);
  468.                         }
  469.                     }
  470.                     $targetSite Frontend::getSiteForDocument($document);
  471.                     $this->doCreateRedirectForFormerPath($oldPath$document->getId(), $sourceSite$targetSite);
  472.                     if ($document->hasChildren()) {
  473.                         $list = new Document\Listing();
  474.                         $list->setCondition('path LIKE :path', [
  475.                             'path' => $list->escapeLike($document->getRealFullPath()) . '/%'
  476.                         ]);
  477.                         $childrenList $list->loadIdPathList();
  478.                         $count 0;
  479.                         foreach ($childrenList as $child) {
  480.                             $source preg_replace('@^' preg_quote($document->getRealFullPath(), '@') . '@'$oldDocument$child['path']);
  481.                             if ($sourceSite) {
  482.                                 $source preg_replace('@^' preg_quote($sourceSite->getRootPath(), '@') . '@'''$source);
  483.                             }
  484.                             $target $child['id'];
  485.                             $this->doCreateRedirectForFormerPath($source$target$sourceSite$targetSite);
  486.                             $count++;
  487.                             if ($count 10 === 0) {
  488.                                 \Pimcore::collectGarbage();
  489.                             }
  490.                         }
  491.                     }
  492.                 }
  493.             }
  494.         }
  495.     }
  496.     private function doCreateRedirectForFormerPath(string $sourceint $targetId, ?Site $sourceSite, ?Site $targetSite)
  497.     {
  498.         $redirect = new Redirect();
  499.         $redirect->setType(Redirect::TYPE_AUTO_CREATE);
  500.         $redirect->setRegex(false);
  501.         $redirect->setTarget($targetId);
  502.         $redirect->setSource($source);
  503.         $redirect->setStatusCode(301);
  504.         $redirect->setExpiry(time() + 86400 365); // this entry is removed automatically after 1 year
  505.         if ($sourceSite) {
  506.             $redirect->setSourceSite($sourceSite->getId());
  507.         }
  508.         if ($targetSite) {
  509.             $redirect->setTargetSite($targetSite->getId());
  510.         }
  511.         $redirect->save();
  512.     }
  513.     /**
  514.      * @param Document $document
  515.      * @param int $newIndex
  516.      */
  517.     protected function updateIndexesOfDocumentSiblings(Document $document$newIndex)
  518.     {
  519.         $updateLatestVersionIndex = function ($document$newIndex) {
  520.             if ($document instanceof Document\PageSnippet && $latestVersion $document->getLatestVersion()) {
  521.                 $document $latestVersion->loadData();
  522.                 $document->setIndex($newIndex);
  523.                 $latestVersion->save();
  524.             }
  525.         };
  526.         // if changed the index change also all documents on the same level
  527.         $newIndex intval($newIndex);
  528.         $document->saveIndex($newIndex);
  529.         $list = new Document\Listing();
  530.         $list->setCondition('parentId = ? AND id != ?', [$document->getParentId(), $document->getId()]);
  531.         $list->setOrderKey('index');
  532.         $list->setOrder('asc');
  533.         $childsList $list->load();
  534.         $count 0;
  535.         foreach ($childsList as $child) {
  536.             if ($count == $newIndex) {
  537.                 $count++;
  538.             }
  539.             $child->saveIndex($count);
  540.             $updateLatestVersionIndex($child$count);
  541.             $count++;
  542.         }
  543.     }
  544.     /**
  545.      * @Route("/doc-types", name="pimcore_admin_document_document_doctypesget", methods={"GET"})
  546.      *
  547.      * @param Request $request
  548.      *
  549.      * @return JsonResponse
  550.      */
  551.     public function docTypesGetAction(Request $request)
  552.     {
  553.         // get list of types
  554.         $list = new Document\DocType\Listing();
  555.         $list->load();
  556.         $docTypes = [];
  557.         foreach ($list->getDocTypes() as $type) {
  558.             if ($this->getAdminUser()->isAllowed($type->getId(), 'docType')) {
  559.                 $docTypes[] = $type->getObjectVars();
  560.             }
  561.         }
  562.         return $this->adminJson(['data' => $docTypes'success' => true'total' => count($docTypes)]);
  563.     }
  564.     /**
  565.      * @Route("/doc-types", name="pimcore_admin_document_document_doctypes", methods={"PUT", "POST","DELETE"})
  566.      *
  567.      * @param Request $request
  568.      *
  569.      * @return JsonResponse
  570.      */
  571.     public function docTypesAction(Request $request)
  572.     {
  573.         if ($request->get('data')) {
  574.             $this->checkPermission('document_types');
  575.             if ($request->get('xaction') == 'destroy') {
  576.                 $data $this->decodeJson($request->get('data'));
  577.                 $id $data['id'];
  578.                 $type Document\DocType::getById($id);
  579.                 $type->delete();
  580.                 return $this->adminJson(['success' => true'data' => []]);
  581.             } elseif ($request->get('xaction') == 'update') {
  582.                 $data $this->decodeJson($request->get('data'));
  583.                 // save type
  584.                 $type Document\DocType::getById($data['id']);
  585.                 $type->setValues($data);
  586.                 $type->save();
  587.                 return $this->adminJson(['data' => $type->getObjectVars(), 'success' => true]);
  588.             } elseif ($request->get('xaction') == 'create') {
  589.                 $data $this->decodeJson($request->get('data'));
  590.                 unset($data['id']);
  591.                 // save type
  592.                 $type Document\DocType::create();
  593.                 $type->setValues($data);
  594.                 $type->save();
  595.                 return $this->adminJson(['data' => $type->getObjectVars(), 'success' => true]);
  596.             }
  597.         }
  598.         return $this->adminJson(false);
  599.     }
  600.     /**
  601.      * @Route("/get-doc-types", name="pimcore_admin_document_document_getdoctypes", methods={"GET"})
  602.      *
  603.      * @param Request $request
  604.      *
  605.      * @return JsonResponse
  606.      */
  607.     public function getDocTypesAction(Request $request)
  608.     {
  609.         $list = new Document\DocType\Listing();
  610.         if ($request->get('type')) {
  611.             $type $request->get('type');
  612.             if (Document\Service::isValidType($type)) {
  613.                 $list->setFilter(function ($row) use ($type) {
  614.                     if ($row['type'] == $type) {
  615.                         return true;
  616.                     }
  617.                     return false;
  618.                 });
  619.             }
  620.         }
  621.         $list->load();
  622.         $docTypes = [];
  623.         foreach ($list->getDocTypes() as $type) {
  624.             $docTypes[] = $type->getObjectVars();
  625.         }
  626.         return $this->adminJson(['docTypes' => $docTypes]);
  627.     }
  628.     /**
  629.      * @Route("/version-to-session", name="pimcore_admin_document_document_versiontosession", methods={"POST"})
  630.      *
  631.      * @param Request $request
  632.      *
  633.      * @return Response
  634.      */
  635.     public function versionToSessionAction(Request $request)
  636.     {
  637.         $version Version::getById($request->get('id'));
  638.         $document $version->loadData();
  639.         Document\Service::saveElementToSession($document);
  640.         return new Response();
  641.     }
  642.     /**
  643.      * @Route("/publish-version", name="pimcore_admin_document_document_publishversion", methods={"POST"})
  644.      *
  645.      * @param Request $request
  646.      *
  647.      * @return JsonResponse
  648.      */
  649.     public function publishVersionAction(Request $request)
  650.     {
  651.         $this->versionToSessionAction($request);
  652.         $version Version::getById($request->get('id'));
  653.         $document $version->loadData();
  654.         $currentDocument Document::getById($document->getId());
  655.         if ($currentDocument->isAllowed('publish')) {
  656.             $document->setPublished(true);
  657.             try {
  658.                 $document->setKey($currentDocument->getKey());
  659.                 $document->setPath($currentDocument->getRealPath());
  660.                 $document->setUserModification($this->getAdminUser()->getId());
  661.                 $document->save();
  662.             } catch (\Exception $e) {
  663.                 return $this->adminJson(['success' => false'message' => $e->getMessage()]);
  664.             }
  665.         }
  666.         $this->addAdminStyle($documentElementAdminStyleEvent::CONTEXT_EDITOR$treeData);
  667.         return $this->adminJson(['success' => true'treeData' => $treeData]);
  668.     }
  669.     /**
  670.      * @Route("/update-site", name="pimcore_admin_document_document_updatesite", methods={"PUT"})
  671.      *
  672.      * @param Request $request
  673.      *
  674.      * @return JsonResponse
  675.      */
  676.     public function updateSiteAction(Request $request)
  677.     {
  678.         $domains $request->get('domains');
  679.         $domains str_replace(' '''$domains);
  680.         $domains explode("\n"$domains);
  681.         if (!$site Site::getByRootId(intval($request->get('id')))) {
  682.             $site Site::create([
  683.                 'rootId' => intval($request->get('id'))
  684.             ]);
  685.         }
  686.         $site->setDomains($domains);
  687.         $site->setMainDomain($request->get('mainDomain'));
  688.         $site->setErrorDocument($request->get('errorDocument'));
  689.         $site->setRedirectToMainDomain(($request->get('redirectToMainDomain') == 'true') ? true false);
  690.         $site->save();
  691.         $site->setRootDocument(null); // do not send the document to the frontend
  692.         return $this->adminJson($site);
  693.     }
  694.     /**
  695.      * @Route("/remove-site", name="pimcore_admin_document_document_removesite", methods={"DELETE"})
  696.      *
  697.      * @param Request $request
  698.      *
  699.      * @return JsonResponse
  700.      */
  701.     public function removeSiteAction(Request $request)
  702.     {
  703.         $site Site::getByRootId(intval($request->get('id')));
  704.         $site->delete();
  705.         return $this->adminJson(['success' => true]);
  706.     }
  707.     /**
  708.      * @Route("/copy-info", name="pimcore_admin_document_document_copyinfo", methods={"GET"})
  709.      *
  710.      * @param Request $request
  711.      *
  712.      * @return JsonResponse
  713.      */
  714.     public function copyInfoAction(Request $request)
  715.     {
  716.         $transactionId time();
  717.         $pasteJobs = [];
  718.         Session::useSession(function (AttributeBagInterface $session) use ($transactionId) {
  719.             $session->set($transactionId, ['idMapping' => []]);
  720.         }, 'pimcore_copy');
  721.         if ($request->get('type') == 'recursive' || $request->get('type') == 'recursive-update-references') {
  722.             $document Document::getById($request->get('sourceId'));
  723.             // first of all the new parent
  724.             $pasteJobs[] = [[
  725.                 'url' => $this->generateUrl('pimcore_admin_document_document_copy'),
  726.                 'method' => 'POST',
  727.                 'params' => [
  728.                     'sourceId' => $request->get('sourceId'),
  729.                     'targetId' => $request->get('targetId'),
  730.                     'type' => 'child',
  731.                     'language' => $request->get('language'),
  732.                     'enableInheritance' => $request->get('enableInheritance'),
  733.                     'transactionId' => $transactionId,
  734.                     'saveParentId' => true,
  735.                     'resetIndex' => true
  736.                 ]
  737.             ]];
  738.             $childIds = [];
  739.             if ($document->hasChildren()) {
  740.                 // get amount of childs
  741.                 $list = new Document\Listing();
  742.                 $list->setCondition('path LIKE ?', [$list->escapeLike($document->getRealFullPath()) . '/%']);
  743.                 $list->setOrderKey('LENGTH(path)'false);
  744.                 $list->setOrder('ASC');
  745.                 $childIds $list->loadIdList();
  746.                 if (count($childIds) > 0) {
  747.                     foreach ($childIds as $id) {
  748.                         $pasteJobs[] = [[
  749.                             'url' => $this->generateUrl('pimcore_admin_document_document_copy'),
  750.                             'method' => 'POST',
  751.                             'params' => [
  752.                                 'sourceId' => $id,
  753.                                 'targetParentId' => $request->get('targetId'),
  754.                                 'sourceParentId' => $request->get('sourceId'),
  755.                                 'type' => 'child',
  756.                                 'language' => $request->get('language'),
  757.                                 'enableInheritance' => $request->get('enableInheritance'),
  758.                                 'transactionId' => $transactionId
  759.                             ]
  760.                         ]];
  761.                     }
  762.                 }
  763.             }
  764.             // add id-rewrite steps
  765.             if ($request->get('type') == 'recursive-update-references') {
  766.                 for ($i 0$i < (count($childIds) + 1); $i++) {
  767.                     $pasteJobs[] = [[
  768.                         'url' => $this->generateUrl('pimcore_admin_document_document_copyrewriteids'),
  769.                         'method' => 'PUT',
  770.                         'params' => [
  771.                             'transactionId' => $transactionId,
  772.                             'enableInheritance' => $request->get('enableInheritance'),
  773.                             '_dc' => uniqid()
  774.                         ]
  775.                     ]];
  776.                 }
  777.             }
  778.         } elseif ($request->get('type') == 'child' || $request->get('type') == 'replace') {
  779.             // the object itself is the last one
  780.             $pasteJobs[] = [[
  781.                 'url' => $this->generateUrl('pimcore_admin_document_document_copy'),
  782.                 'method' => 'POST',
  783.                 'params' => [
  784.                     'sourceId' => $request->get('sourceId'),
  785.                     'targetId' => $request->get('targetId'),
  786.                     'type' => $request->get('type'),
  787.                     'language' => $request->get('language'),
  788.                     'enableInheritance' => $request->get('enableInheritance'),
  789.                     'transactionId' => $transactionId,
  790.                     'resetIndex' => ($request->get('type') == 'child')
  791.                 ]
  792.             ]];
  793.         }
  794.         return $this->adminJson([
  795.             'pastejobs' => $pasteJobs
  796.         ]);
  797.     }
  798.     /**
  799.      * @Route("/copy-rewrite-ids", name="pimcore_admin_document_document_copyrewriteids", methods={"PUT"})
  800.      *
  801.      * @param Request $request
  802.      *
  803.      * @return JsonResponse
  804.      */
  805.     public function copyRewriteIdsAction(Request $request)
  806.     {
  807.         $transactionId $request->get('transactionId');
  808.         $idStore Session::useSession(function (AttributeBagInterface $session) use ($transactionId) {
  809.             return $session->get($transactionId);
  810.         }, 'pimcore_copy');
  811.         if (!array_key_exists('rewrite-stack'$idStore)) {
  812.             $idStore['rewrite-stack'] = array_values($idStore['idMapping']);
  813.         }
  814.         $id array_shift($idStore['rewrite-stack']);
  815.         $document Document::getById($id);
  816.         if ($document) {
  817.             // create rewriteIds() config parameter
  818.             $rewriteConfig = ['document' => $idStore['idMapping']];
  819.             $document Document\Service::rewriteIds($document$rewriteConfig, [
  820.                 'enableInheritance' => ($request->get('enableInheritance') == 'true') ? true false
  821.             ]);
  822.             $document->setUserModification($this->getAdminUser()->getId());
  823.             $document->save();
  824.         }
  825.         // write the store back to the session
  826.         Session::useSession(function (AttributeBagInterface $session) use ($transactionId$idStore) {
  827.             $session->set($transactionId$idStore);
  828.         }, 'pimcore_copy');
  829.         return $this->adminJson([
  830.             'success' => true,
  831.             'id' => $id
  832.         ]);
  833.     }
  834.     /**
  835.      * @Route("/copy", name="pimcore_admin_document_document_copy", methods={"POST"})
  836.      *
  837.      * @param Request $request
  838.      *
  839.      * @return JsonResponse
  840.      */
  841.     public function copyAction(Request $request)
  842.     {
  843.         $success false;
  844.         $sourceId intval($request->get('sourceId'));
  845.         $source Document::getById($sourceId);
  846.         $session Session::get('pimcore_copy');
  847.         $targetId intval($request->get('targetId'));
  848.         $sessionBag $session->get($request->get('transactionId'));
  849.         if ($request->get('targetParentId')) {
  850.             $sourceParent Document::getById($request->get('sourceParentId'));
  851.             // this is because the key can get the prefix "_copy" if the target does already exists
  852.             if ($sessionBag['parentId']) {
  853.                 $targetParent Document::getById($sessionBag['parentId']);
  854.             } else {
  855.                 $targetParent Document::getById($request->get('targetParentId'));
  856.             }
  857.             $targetPath preg_replace('@^' $sourceParent->getRealFullPath() . '@'$targetParent '/'$source->getRealPath());
  858.             $target Document::getByPath($targetPath);
  859.         } else {
  860.             $target Document::getById($targetId);
  861.         }
  862.         if ($target instanceof Document) {
  863.             if ($target->isAllowed('create')) {
  864.                 if ($source != null) {
  865.                     if ($source instanceof Document\PageSnippet && $latestVersion $source->getLatestVersion()) {
  866.                         $source $latestVersion->loadData();
  867.                         $source->setPublished(false); //as latest version is used which is not published
  868.                     }
  869.                     if ($request->get('type') == 'child') {
  870.                         $enableInheritance = ($request->get('enableInheritance') == 'true') ? true false;
  871.                         $language false;
  872.                         if (Tool::isValidLanguage($request->get('language'))) {
  873.                             $language $request->get('language');
  874.                         }
  875.                         $resetIndex = ($request->get('resetIndex') == 'true') ? true false;
  876.                         $newDocument $this->_documentService->copyAsChild($target$source$enableInheritance$resetIndex$language);
  877.                         $sessionBag['idMapping'][(int)$source->getId()] = (int)$newDocument->getId();
  878.                         // this is because the key can get the prefix "_copy" if the target does already exists
  879.                         if ($request->get('saveParentId')) {
  880.                             $sessionBag['parentId'] = $newDocument->getId();
  881.                         }
  882.                         $session->set($request->get('transactionId'), $sessionBag);
  883.                         Session::writeClose();
  884.                     } elseif ($request->get('type') == 'replace') {
  885.                         $this->_documentService->copyContents($target$source);
  886.                     }
  887.                     $success true;
  888.                 } else {
  889.                     Logger::error('prevended copy/paste because document with same path+key already exists in this location');
  890.                 }
  891.             } else {
  892.                 Logger::error('could not execute copy/paste because of missing permissions on target [ ' $targetId ' ]');
  893.                 throw $this->createAccessDeniedHttpException();
  894.             }
  895.         }
  896.         return $this->adminJson(['success' => $success]);
  897.     }
  898.     /**
  899.      * @Route("/diff-versions/from/{from}/to/{to}", name="pimcore_admin_document_document_diffversions", requirements={"from": "\d+", "to": "\d+"}, methods={"GET"})
  900.      *
  901.      * @param Request $request
  902.      * @param int $from
  903.      * @param int $to
  904.      *
  905.      * @return Response
  906.      */
  907.     public function diffVersionsAction(Request $request$from$to)
  908.     {
  909.         // return with error if prerequisites do not match
  910.         if (!HtmlToImage::isSupported() || !class_exists('Imagick')) {
  911.             return $this->render('PimcoreAdminBundle:Admin/Document/Document:diff-versions-unsupported.html.php');
  912.         }
  913.         $versionFrom Version::getById($from);
  914.         $docFrom $versionFrom->loadData();
  915.         $prefix $request->getSchemeAndHttpHost() . $docFrom->getRealFullPath() . '?pimcore_version=';
  916.         $fromUrl $prefix $from;
  917.         $toUrl $prefix $to;
  918.         $toFileId uniqid();
  919.         $fromFileId uniqid();
  920.         $diffFileId uniqid();
  921.         $fromFile PIMCORE_SYSTEM_TEMP_DIRECTORY '/version-diff-tmp-' $fromFileId '.png';
  922.         $toFile PIMCORE_SYSTEM_TEMP_DIRECTORY '/version-diff-tmp-' $toFileId '.png';
  923.         $diffFile PIMCORE_SYSTEM_TEMP_DIRECTORY '/version-diff-tmp-' $diffFileId '.png';
  924.         $viewParams = [];
  925.         HtmlToImage::convert($fromUrl$fromFile);
  926.         HtmlToImage::convert($toUrl$toFile);
  927.         $image1 = new \Imagick($fromFile);
  928.         $image2 = new \Imagick($toFile);
  929.         if ($image1->getImageWidth() == $image2->getImageWidth() && $image1->getImageHeight() == $image2->getImageHeight()) {
  930.             $result $image1->compareImages($image2, \Imagick::METRIC_MEANSQUAREERROR);
  931.             $result[0]->setImageFormat('png');
  932.             $result[0]->writeImage($diffFile);
  933.             $result[0]->clear();
  934.             $result[0]->destroy();
  935.             $viewParams['image'] = $diffFileId;
  936.         } else {
  937.             $viewParams['image1'] = $fromFileId;
  938.             $viewParams['image2'] = $toFileId;
  939.         }
  940.         // cleanup
  941.         $image1->clear();
  942.         $image1->destroy();
  943.         $image2->clear();
  944.         $image2->destroy();
  945.         return $this->render('PimcoreAdminBundle:Admin/Document/Document:diff-versions.html.php'$viewParams);
  946.     }
  947.     /**
  948.      * @Route("/diff-versions-image", name="pimcore_admin_document_document_diffversionsimage", methods={"GET"})
  949.      *
  950.      * @param Request $request
  951.      *
  952.      * @return BinaryFileResponse
  953.      */
  954.     public function diffVersionsImageAction(Request $request)
  955.     {
  956.         $file PIMCORE_SYSTEM_TEMP_DIRECTORY '/version-diff-tmp-' $request->get('id') . '.png';
  957.         if (file_exists($file)) {
  958.             $response = new BinaryFileResponse($file);
  959.             $response->headers->set('Content-Type''image/png');
  960.             return $response;
  961.         }
  962.         throw $this->createNotFoundException('Version diff file not found');
  963.     }
  964.     /**
  965.      * @param Document $element
  966.      *
  967.      * @return array
  968.      */
  969.     protected function getTreeNodeConfig($element)
  970.     {
  971.         $site null;
  972.         $childDocument $element;
  973.         $config $this->get(Config::class);
  974.         $tmpDocument = [
  975.             'id' => $childDocument->getId(),
  976.             'idx' => intval($childDocument->getIndex()),
  977.             'text' => $childDocument->getKey(),
  978.             'type' => $childDocument->getType(),
  979.             'path' => $childDocument->getRealFullPath(),
  980.             'basePath' => $childDocument->getRealPath(),
  981.             'locked' => $childDocument->isLocked(),
  982.             'lockOwner' => $childDocument->getLocked() ? true false,
  983.             'published' => $childDocument->isPublished(),
  984.             'elementType' => 'document',
  985.             'leaf' => true,
  986.             'permissions' => [
  987.                 'view' => $childDocument->isAllowed('view'),
  988.                 'remove' => $childDocument->isAllowed('delete'),
  989.                 'settings' => $childDocument->isAllowed('settings'),
  990.                 'rename' => $childDocument->isAllowed('rename'),
  991.                 'publish' => $childDocument->isAllowed('publish'),
  992.                 'unpublish' => $childDocument->isAllowed('unpublish'),
  993.                 'create' => $childDocument->isAllowed('create')
  994.             ]
  995.         ];
  996.         // add icon
  997.         $tmpDocument['expandable'] = $childDocument->hasChildren();
  998.         $tmpDocument['loaded'] = !$childDocument->hasChildren();
  999.         // set type specific settings
  1000.         if ($childDocument->getType() == 'page') {
  1001.             $tmpDocument['leaf'] = false;
  1002.             $tmpDocument['expanded'] = !$childDocument->hasChildren();
  1003.             // test for a site
  1004.             if ($site Site::getByRootId($childDocument->getId())) {
  1005.                 unset($site->rootDocument);
  1006.                 $tmpDocument['site'] = $site;
  1007.             }
  1008.         } elseif ($childDocument->getType() == 'folder' || $childDocument->getType() == 'link' || $childDocument->getType() == 'hardlink') {
  1009.             $tmpDocument['leaf'] = false;
  1010.             $tmpDocument['expanded'] = !$childDocument->hasChildren();
  1011.         } elseif (method_exists($childDocument'getTreeNodeConfig')) {
  1012.             $tmp $childDocument->getTreeNodeConfig();
  1013.             $tmpDocument array_merge($tmpDocument$tmp);
  1014.         }
  1015.         $this->addAdminStyle($childDocumentElementAdminStyleEvent::CONTEXT_TREE$tmpDocument);
  1016.         // PREVIEWS temporary disabled, need's to be optimized some time
  1017.         if ($childDocument instanceof Document\Page && isset($config['documents']['generate_preview'])) {
  1018.             $thumbnailFile $childDocument->getPreviewImageFilesystemPath();
  1019.             // only if the thumbnail exists and isn't out of time
  1020.             if (file_exists($thumbnailFile) && filemtime($thumbnailFile) > ($childDocument->getModificationDate() - 20)) {
  1021.                 $tmpDocument['thumbnail'] = $this->generateUrl('pimcore_admin_page_display_preview_image', ['id' => $childDocument->getId()]);
  1022.                 $thumbnailFileHdpi $childDocument->getPreviewImageFilesystemPath(true);
  1023.                 if (file_exists($thumbnailFileHdpi)) {
  1024.                     $tmpDocument['thumbnailHdpi'] = $this->generateUrl('pimcore_admin_page_display_preview_image',
  1025.                         ['id' => $childDocument->getId(), 'hdpi' => true]);
  1026.                 }
  1027.             }
  1028.         }
  1029.         $tmpDocument['cls'] = '';
  1030.         if ($childDocument instanceof Document\Page) {
  1031.             $tmpDocument['url'] = $childDocument->getFullPath();
  1032.             $site Tool\Frontend::getSiteForDocument($childDocument);
  1033.             if ($site instanceof Site) {
  1034.                 $tmpDocument['url'] = 'http://' $site->getMainDomain() . preg_replace('@^' $site->getRootPath() . '/?@''/'$childDocument->getRealFullPath());
  1035.             }
  1036.         }
  1037.         if ($childDocument->getProperty('navigation_exclude')) {
  1038.             $tmpDocument['cls'] .= 'pimcore_navigation_exclude ';
  1039.         }
  1040.         if (!$childDocument->isPublished()) {
  1041.             $tmpDocument['cls'] .= 'pimcore_unpublished ';
  1042.         }
  1043.         if ($childDocument->isLocked()) {
  1044.             $tmpDocument['cls'] .= 'pimcore_treenode_locked ';
  1045.         }
  1046.         if ($childDocument->getLocked()) {
  1047.             $tmpDocument['cls'] .= 'pimcore_treenode_lockOwner ';
  1048.         }
  1049.         return $tmpDocument;
  1050.     }
  1051.     /**
  1052.      * @Route("/get-id-for-path", name="pimcore_admin_document_document_getidforpath", methods={"GET"})
  1053.      *
  1054.      * @param Request $request
  1055.      *
  1056.      * @return JsonResponse
  1057.      */
  1058.     public function getIdForPathAction(Request $request)
  1059.     {
  1060.         if ($doc Document::getByPath($request->get('path'))) {
  1061.             return $this->adminJson([
  1062.                 'id' => $doc->getId(),
  1063.                 'type' => $doc->getType()
  1064.             ]);
  1065.         } else {
  1066.             return $this->adminJson(false);
  1067.         }
  1068.     }
  1069.     /**
  1070.      * SEO PANEL
  1071.      */
  1072.     /**
  1073.      * @Route("/seopanel-tree-root", name="pimcore_admin_document_document_seopaneltreeroot", methods={"GET"})
  1074.      *
  1075.      * @param DocumentRouteHandler $documentRouteHandler
  1076.      *
  1077.      * @return JsonResponse
  1078.      */
  1079.     public function seopanelTreeRootAction(DocumentRouteHandler $documentRouteHandler)
  1080.     {
  1081.         $this->checkPermission('seo_document_editor');
  1082.         /** @var Document\Page $root */
  1083.         $root Document\Page::getById(1);
  1084.         if ($root->isAllowed('list')) {
  1085.             // make sure document routes are also built for unpublished documents
  1086.             $documentRouteHandler->setForceHandleUnpublishedDocuments(true);
  1087.             $nodeConfig $this->getSeoNodeConfig($root);
  1088.             return $this->adminJson($nodeConfig);
  1089.         }
  1090.         throw $this->createAccessDeniedHttpException();
  1091.     }
  1092.     /**
  1093.      * @Route("/seopanel-tree", name="pimcore_admin_document_document_seopaneltree", methods={"GET"})
  1094.      *
  1095.      * @param Request $request
  1096.      * @param EventDispatcherInterface $eventDispatcher
  1097.      * @param DocumentRouteHandler $documentRouteHandler
  1098.      *
  1099.      * @return JsonResponse
  1100.      */
  1101.     public function seopanelTreeAction(
  1102.         Request $request,
  1103.         EventDispatcherInterface $eventDispatcher,
  1104.         DocumentRouteHandler $documentRouteHandler
  1105.     ) {
  1106.         $allParams array_merge($request->request->all(), $request->query->all());
  1107.         $filterPrepareEvent = new GenericEvent($this, [
  1108.             'requestParams' => $allParams
  1109.         ]);
  1110.         $eventDispatcher->dispatch(AdminEvents::DOCUMENT_LIST_BEFORE_FILTER_PREPARE$filterPrepareEvent);
  1111.         $allParams $filterPrepareEvent->getArgument('requestParams');
  1112.         $this->checkPermission('seo_document_editor');
  1113.         // make sure document routes are also built for unpublished documents
  1114.         $documentRouteHandler->setForceHandleUnpublishedDocuments(true);
  1115.         $document Document::getById($allParams['node']);
  1116.         $documents = [];
  1117.         if ($document->hasChildren()) {
  1118.             $list = new Document\Listing();
  1119.             $list->setCondition('parentId = ?'$document->getId());
  1120.             $list->setOrderKey('index');
  1121.             $list->setOrder('asc');
  1122.             $beforeListLoadEvent = new GenericEvent($this, [
  1123.                 'list' => $list,
  1124.                 'context' => $allParams
  1125.             ]);
  1126.             $eventDispatcher->dispatch(AdminEvents::DOCUMENT_LIST_BEFORE_LIST_LOAD$beforeListLoadEvent);
  1127.             /** @var Document\Listing $list */
  1128.             $list $beforeListLoadEvent->getArgument('list');
  1129.             $childsList $list->load();
  1130.             foreach ($childsList as $childDocument) {
  1131.                 // only display document if listing is allowed for the current user
  1132.                 if ($childDocument->isAllowed('list')) {
  1133.                     $list = new Document\Listing();
  1134.                     $list->setCondition('path LIKE ? and type = ?', [$list->escapeLike($childDocument->getRealFullPath()). '/%''page']);
  1135.                     if ($childDocument instanceof Document\Page || $list->getTotalCount() > 0) {
  1136.                         $documents[] = $this->getSeoNodeConfig($childDocument);
  1137.                     }
  1138.                 }
  1139.             }
  1140.         }
  1141.         $result = ['data' => $documents'success' => true'total' => count($documents)];
  1142.         $afterListLoadEvent = new GenericEvent($this, [
  1143.             'list' => $result,
  1144.             'context' => $allParams
  1145.         ]);
  1146.         $eventDispatcher->dispatch(AdminEvents::DOCUMENT_LIST_AFTER_LIST_LOAD$afterListLoadEvent);
  1147.         $result $afterListLoadEvent->getArgument('list');
  1148.         return $this->adminJson($result['data']);
  1149.     }
  1150.     /**
  1151.      * @Route("/language-tree", name="pimcore_admin_document_document_languagetree", methods={"GET"})
  1152.      *
  1153.      * @param Request $request
  1154.      *
  1155.      * @return JsonResponse
  1156.      */
  1157.     public function languageTreeAction(Request $request)
  1158.     {
  1159.         $document Document::getById($request->query->get('node'));
  1160.         $service = new Document\Service();
  1161.         $languages explode(','$request->get('languages'));
  1162.         $result = [];
  1163.         foreach ($document->getChildren() as $child) {
  1164.             $result[] = $this->getTranslationTreeNodeConfig($child$languages);
  1165.         }
  1166.         return $this->adminJson($result);
  1167.     }
  1168.     /**
  1169.      * @Route("/language-tree-root", name="pimcore_admin_document_document_languagetreeroot", methods={"GET"})
  1170.      *
  1171.      * @param Request $request
  1172.      *
  1173.      * @return JsonResponse
  1174.      *
  1175.      * @throws \Exception
  1176.      */
  1177.     public function languageTreeRootAction(Request $request)
  1178.     {
  1179.         $document Document::getById($request->query->get('id'));
  1180.         if (!$document) {
  1181.             return $this->adminJson([
  1182.                 'success' => false
  1183.             ]);
  1184.         }
  1185.         $service = new Document\Service();
  1186.         $locales Tool::getSupportedLocales();
  1187.         $lang $document->getProperty('language');
  1188.         $columns = [
  1189.             [
  1190.                 'xtype' => 'treecolumn',
  1191.                 'text' => $lang $locales[$lang] : '',
  1192.                 'dataIndex' => 'text',
  1193.                 'cls' => $lang 'x-column-header_' strtolower($lang) : null,
  1194.                 'width' => 300,
  1195.                 'sortable' => false,
  1196.             ],
  1197.         ];
  1198.         $translations $service->getTranslations($document);
  1199.         $combinedTranslations $translations;
  1200.         if ($parentDocument $document->getParent()) {
  1201.             $parentTranslations $service->getTranslations($parentDocument);
  1202.             foreach ($parentTranslations as $language => $languageDocumentId) {
  1203.                 $combinedTranslations[$language] = $translations[$language] ?? $languageDocumentId;
  1204.             }
  1205.         }
  1206.         foreach ($combinedTranslations as $language => $languageDocumentId) {
  1207.             $languageDocument Document::getById($languageDocumentId);
  1208.             if ($languageDocument && $languageDocument->isAllowed('list') && $language != $document->getProperty('language')) {
  1209.                 $columns[] = [
  1210.                     'text' => $locales[$language],
  1211.                     'dataIndex' => $language,
  1212.                     'cls' => 'x-column-header_' strtolower($language),
  1213.                     'width' => 300,
  1214.                     'sortable' => false,
  1215.                 ];
  1216.             }
  1217.         }
  1218.         return $this->adminJson([
  1219.             'root' => $this->getTranslationTreeNodeConfig($documentarray_keys($translations), $translations),
  1220.             'columns' => $columns,
  1221.             'languages' => array_keys($translations)
  1222.         ]);
  1223.     }
  1224.     private function getTranslationTreeNodeConfig($document, array $languages, array $translations null)
  1225.     {
  1226.         $service = new Document\Service();
  1227.         $config $this->getTreeNodeConfig($document);
  1228.         $translations is_null($translations) ? $service->getTranslations($document) : $translations;
  1229.         foreach ($languages as $language) {
  1230.             if ($languageDocument $translations[$language]) {
  1231.                 $languageDocument Document::getById($languageDocument);
  1232.                 $config[$language] = [
  1233.                     'text' => $languageDocument->getKey(),
  1234.                     'id' => $languageDocument->getId(),
  1235.                     'type' => $languageDocument->getType(),
  1236.                     'fullPath' => $languageDocument->getFullPath(),
  1237.                     'published' => $languageDocument->getPublished(),
  1238.                     'itemType' => 'document',
  1239.                     'permissions' => $languageDocument->getUserPermissions()
  1240.                 ];
  1241.             } elseif (!$document instanceof Document\Folder) {
  1242.                 $config[$language] = [
  1243.                     'text' => '--',
  1244.                     'itemType' => 'empty'
  1245.                 ];
  1246.             }
  1247.         }
  1248.         return $config;
  1249.     }
  1250.     /**
  1251.      * @Route("/convert", name="pimcore_admin_document_document_convert", methods={"PUT"})
  1252.      *
  1253.      * @param Request $request
  1254.      *
  1255.      * @return JsonResponse
  1256.      */
  1257.     public function convertAction(Request $request)
  1258.     {
  1259.         $document Document::getById($request->get('id'));
  1260.         $type $request->get('type');
  1261.         $class '\\Pimcore\\Model\\Document\\' ucfirst($type);
  1262.         if (Tool::classExists($class)) {
  1263.             $new = new $class;
  1264.             // overwrite internal store to avoid "duplicate full path" error
  1265.             \Pimcore\Cache\Runtime::set('document_' $document->getId(), $new);
  1266.             $props $document->getObjectVars();
  1267.             foreach ($props as $name => $value) {
  1268.                 $new->setValue($name$value);
  1269.             }
  1270.             if ($type == 'hardlink' || $type == 'folder') {
  1271.                 // remove navigation settings
  1272.                 foreach (['name''title''target''exclude''class''anchor''parameters''relation''accesskey''tabindex'] as $propertyName) {
  1273.                     $new->removeProperty('navigation_' $propertyName);
  1274.                 }
  1275.             }
  1276.             $new->setType($type);
  1277.             $new->save();
  1278.         }
  1279.         return $this->adminJson(['success' => true]);
  1280.     }
  1281.     /**
  1282.      * @Route("/translation-determine-parent", name="pimcore_admin_document_document_translationdetermineparent", methods={"GET"})
  1283.      *
  1284.      * @param Request $request
  1285.      *
  1286.      * @return JsonResponse
  1287.      */
  1288.     public function translationDetermineParentAction(Request $request)
  1289.     {
  1290.         $success false;
  1291.         $targetDocument null;
  1292.         $document Document::getById($request->get('id'));
  1293.         if ($document) {
  1294.             $service = new Document\Service;
  1295.             $document $document->getId() === $document $document->getParent();
  1296.             $translations $service->getTranslations($document);
  1297.             if (isset($translations[$request->get('language')])) {
  1298.                 $targetDocument Document::getById($translations[$request->get('language')]);
  1299.                 $success true;
  1300.             }
  1301.         }
  1302.         return $this->adminJson([
  1303.             'success' => $success,
  1304.             'targetPath' => $targetDocument $targetDocument->getRealFullPath() : null,
  1305.             'targetId' => $targetDocument $targetDocument->getid() : null
  1306.         ]);
  1307.     }
  1308.     /**
  1309.      * @Route("/translation-add", name="pimcore_admin_document_document_translationadd", methods={"POST"})
  1310.      *
  1311.      * @param Request $request
  1312.      *
  1313.      * @return JsonResponse
  1314.      */
  1315.     public function translationAddAction(Request $request)
  1316.     {
  1317.         $sourceDocument Document::getById($request->get('sourceId'));
  1318.         $targetDocument Document::getByPath($request->get('targetPath'));
  1319.         if ($sourceDocument && $targetDocument) {
  1320.             if (empty($sourceDocument->getProperty('language'))) {
  1321.                 throw new \Exception(sprintf('Source Document(ID:%s) Language(Properties) missing'$sourceDocument->getId()));
  1322.             }
  1323.             if (empty($targetDocument->getProperty('language'))) {
  1324.                 throw new \Exception(sprintf('Target Document(ID:%s) Language(Properties) missing'$sourceDocument->getId()));
  1325.             }
  1326.             $service = new Document\Service;
  1327.             if ($service->getTranslationSourceId($targetDocument) != $targetDocument->getId()) {
  1328.                 throw new \Exception('Target Document already linked to Source Document ID('.$service->getTranslationSourceId($targetDocument).'). Please unlink existing relation first.');
  1329.             }
  1330.             $service->addTranslation($sourceDocument$targetDocument);
  1331.         }
  1332.         return $this->adminJson([
  1333.             'success' => true
  1334.         ]);
  1335.     }
  1336.     /**
  1337.      * @Route("/translation-remove", name="pimcore_admin_document_document_translationremove", methods={"DELETE"})
  1338.      *
  1339.      * @param Request $request
  1340.      *
  1341.      * @return JsonResponse
  1342.      */
  1343.     public function translationRemoveAction(Request $request)
  1344.     {
  1345.         $sourceDocument Document::getById($request->get('sourceId'));
  1346.         $targetDocument Document::getById($request->get('targetId'));
  1347.         if ($sourceDocument && $targetDocument) {
  1348.             $service = new Document\Service;
  1349.             $service->removeTranslationLink($sourceDocument$targetDocument);
  1350.         }
  1351.         return $this->adminJson([
  1352.             'success' => true
  1353.         ]);
  1354.     }
  1355.     /**
  1356.      * @Route("/translation-check-language", name="pimcore_admin_document_document_translationchecklanguage", methods={"GET"})
  1357.      *
  1358.      * @param Request $request
  1359.      *
  1360.      * @return JsonResponse
  1361.      */
  1362.     public function translationCheckLanguageAction(Request $request)
  1363.     {
  1364.         $success false;
  1365.         $language null;
  1366.         $translationLinks null;
  1367.         $document Document::getByPath($request->get('path'));
  1368.         if ($document) {
  1369.             $language $document->getProperty('language');
  1370.             if ($language) {
  1371.                 $success true;
  1372.             }
  1373.             //check if document is already linked to other langauges
  1374.             $translationLinks array_keys($this->_documentService->getTranslations($document));
  1375.         }
  1376.         return $this->adminJson([
  1377.             'success' => $success,
  1378.             'language' => $language,
  1379.             'translationLinks' => $translationLinks
  1380.         ]);
  1381.     }
  1382.     /**
  1383.      * @param Document\Page $document
  1384.      *
  1385.      * @return array
  1386.      */
  1387.     private function getSeoNodeConfig($document)
  1388.     {
  1389.         $nodeConfig $this->getTreeNodeConfig($document);
  1390.         if (method_exists($document'getTitle') && method_exists($document'getDescription')) {
  1391.             // analyze content
  1392.             $nodeConfig['prettyUrl'] = $document->getPrettyUrl();
  1393.             $title $document->getTitle();
  1394.             $description $document->getDescription();
  1395.             $nodeConfig['title'] = $title;
  1396.             $nodeConfig['description'] = $description;
  1397.             $nodeConfig['title_length'] = mb_strlen($title);
  1398.             $nodeConfig['description_length'] = mb_strlen($description);
  1399.         }
  1400.         return $nodeConfig;
  1401.     }
  1402.     /**
  1403.      * @param FilterControllerEvent $event
  1404.      */
  1405.     public function onKernelController(FilterControllerEvent $event)
  1406.     {
  1407.         $isMasterRequest $event->isMasterRequest();
  1408.         if (!$isMasterRequest) {
  1409.             return;
  1410.         }
  1411.         // check permissions
  1412.         $this->checkActionPermission($event'documents', ['docTypesGetAction']);
  1413.         $this->_documentService = new Document\Service($this->getAdminUser());
  1414.     }
  1415.     /**
  1416.      * @param FilterResponseEvent $event
  1417.      */
  1418.     public function onKernelResponse(FilterResponseEvent $event)
  1419.     {
  1420.         // nothing to do
  1421.     }
  1422. }