vendor/pimcore/pimcore/bundles/AdminBundle/Controller/Admin/MiscController.php line 256

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;
  15. use Pimcore\Bundle\AdminBundle\Controller\AdminController;
  16. use Pimcore\Config;
  17. use Pimcore\Controller\Config\ControllerDataProvider;
  18. use Pimcore\Controller\Configuration\TemplatePhp;
  19. use Pimcore\Db;
  20. use Pimcore\File;
  21. use Pimcore\Localization\LocaleServiceInterface;
  22. use Pimcore\Tool;
  23. use Pimcore\Translation\Translator;
  24. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  25. use Symfony\Component\HttpFoundation\JsonResponse;
  26. use Symfony\Component\HttpFoundation\Request;
  27. use Symfony\Component\HttpFoundation\Response;
  28. use Symfony\Component\HttpKernel\Bundle\BundleInterface;
  29. use Symfony\Component\HttpKernel\Profiler\Profiler;
  30. use Symfony\Component\Routing\Annotation\Route;
  31. /**
  32.  * @Route("/misc")
  33.  */
  34. class MiscController extends AdminController
  35. {
  36.     /**
  37.      * @Route("/get-available-modules", name="pimcore_admin_misc_getavailablemodules", methods={"GET"})
  38.      *
  39.      * @param ControllerDataProvider $provider
  40.      *
  41.      * @return JsonResponse
  42.      */
  43.     public function getAvailableModulesAction(ControllerDataProvider $provider)
  44.     {
  45.         // convert to normal array
  46.         $bundles array_values($provider->getBundles());
  47.         $result array_map(function (BundleInterface $bundle) {
  48.             return [
  49.                 'name' => $bundle->getName()
  50.             ];
  51.         }, $bundles);
  52.         sort($result);
  53.         return $this->adminJson([
  54.             'data' => $result
  55.         ]);
  56.     }
  57.     /**
  58.      * @Route("/get-available-controllers", name="pimcore_admin_misc_getavailablecontrollers", methods={"GET"})
  59.      *
  60.      * @param Request $request
  61.      * @param ControllerDataProvider $provider
  62.      *
  63.      * @return JsonResponse
  64.      */
  65.     public function getAvailableControllersAction(Request $requestControllerDataProvider $provider)
  66.     {
  67.         $routingDefaults $this->getParameter('pimcore.routing.defaults');
  68.         $bundle $request->get('moduleName');
  69.         $controllers $provider->getControllers($bundle$routingDefaults['bundle']);
  70.         $result array_map(function ($controller) {
  71.             return [
  72.                 'name' => $controller
  73.             ];
  74.         }, $controllers);
  75.         sort($result);
  76.         return $this->adminJson([
  77.             'data' => $result
  78.         ]);
  79.     }
  80.     /**
  81.      * @Route("/get-available-actions", name="pimcore_admin_misc_getavailableactions", methods={"GET"})
  82.      *
  83.      * @param Request $request
  84.      * @param ControllerDataProvider $provider
  85.      *
  86.      * @return JsonResponse
  87.      */
  88.     public function getAvailableActionsAction(Request $requestControllerDataProvider $provider)
  89.     {
  90.         $routingDefaults $this->getParameter('pimcore.routing.defaults');
  91.         $bundle $request->get('moduleName');
  92.         if (empty($bundle)) {
  93.             $bundle $routingDefaults['bundle'];
  94.         }
  95.         $controller $request->get('controllerName');
  96.         if (empty($controller)) {
  97.             $controller $routingDefaults['controller'];
  98.         }
  99.         $actions $provider->getActions($controller$bundle);
  100.         $result array_map(function ($action) {
  101.             return [
  102.                 'name' => $action
  103.             ];
  104.         }, $actions);
  105.         sort($result);
  106.         return $this->adminJson([
  107.             'data' => $result
  108.         ]);
  109.     }
  110.     /**
  111.      * @Route("/get-available-templates", name="pimcore_admin_misc_getavailabletemplates", methods={"GET"})
  112.      *
  113.      * @param ControllerDataProvider $provider
  114.      *
  115.      * @return JsonResponse
  116.      */
  117.     public function getAvailableTemplatesAction(ControllerDataProvider $provider)
  118.     {
  119.         $templates $provider->getTemplates();
  120.         $result array_map(function ($template) {
  121.             return [
  122.                 'path' => $template
  123.             ];
  124.         }, $templates);
  125.         sort($result);
  126.         return $this->adminJson([
  127.             'data' => $result
  128.         ]);
  129.     }
  130.     /**
  131.      * @Route("/json-translations-system", name="pimcore_admin_misc_jsontranslationssystem", methods={"GET"})
  132.      *
  133.      * @param Request $request
  134.      *
  135.      * @return Response
  136.      */
  137.     public function jsonTranslationsSystemAction(Request $request)
  138.     {
  139.         $language $request->get('language');
  140.         $translator $this->get('translator');
  141.         $translator->lazyInitialize('admin'$language);
  142.         $translations $translator->getCatalogue($language)->all('admin');
  143.         if ($language != 'en') {
  144.             // add en as a fallback
  145.             $translator->lazyInitialize('admin''en');
  146.             foreach ($translator->getCatalogue('en')->all('admin') as $key => $value) {
  147.                 if (!isset($translations[$key]) || empty($translations[$key])) {
  148.                     $translations[$key] = $value;
  149.                 }
  150.             }
  151.         }
  152.         $caseInsensitive $translator instanceof Translator && $translator->getCaseInsensitive();
  153.         $response = new Response('pimcore.system_i18n = ' $this->encodeJson($translations) . ';pimcore.system_i18n_case_insensitive='json_encode($caseInsensitive));
  154.         $response->headers->set('Content-Type''text/javascript');
  155.         return $response;
  156.     }
  157.     /**
  158.      * @Route("/script-proxy", name="pimcore_admin_misc_scriptproxy", methods={"GET"})
  159.      *
  160.      * @param Request $request
  161.      *
  162.      * @return Response
  163.      */
  164.     public function scriptProxyAction(Request $request)
  165.     {
  166.         $allowedFileTypes = ['js''css'];
  167.         $scripts explode(','$request->get('scripts'));
  168.         if ($request->get('scriptPath')) {
  169.             $scriptPath PIMCORE_WEB_ROOT $request->get('scriptPath');
  170.         } else {
  171.             $scriptPath PIMCORE_SYSTEM_TEMP_DIRECTORY '/';
  172.         }
  173.         $scriptsContent '';
  174.         foreach ($scripts as $script) {
  175.             $filePath $scriptPath $script;
  176.             if (is_file($filePath) && is_readable($filePath) && in_array(\Pimcore\File::getFileExtension($script), $allowedFileTypes)) {
  177.                 $scriptsContent .= file_get_contents($filePath);
  178.             }
  179.         }
  180.         if (!empty($scriptsContent)) {
  181.             $fileExtension = \Pimcore\File::getFileExtension($scripts[0]);
  182.             $contentType 'text/javascript';
  183.             if ($fileExtension == 'css') {
  184.                 $contentType 'text/css';
  185.             }
  186.             $lifetime 86400;
  187.             $response = new Response($scriptsContent);
  188.             $response->headers->set('Cache-Control''max-age=' $lifetime);
  189.             $response->headers->set('Pragma''');
  190.             $response->headers->set('Content-Type'$contentType);
  191.             $response->headers->set('Expires'gmdate('D, d M Y H:i:s'time() + $lifetime) . ' GMT');
  192.             return $response;
  193.         } else {
  194.             throw $this->createNotFoundException('Scripts not found');
  195.         }
  196.     }
  197.     /**
  198.      * @Route("/admin-css", name="pimcore_admin_misc_admincss", methods={"GET"})
  199.      *
  200.      * @param Request $request
  201.      * @param Config $config
  202.      *
  203.      * @return Response
  204.      */
  205.     public function adminCssAction(Request $requestConfig $config)
  206.     {
  207.         // customviews config
  208.         $cvData Tool::getCustomViewConfig();
  209.         $response $this->render('PimcoreAdminBundle:Admin/Misc:admin-css.html.php', ['customviews' => $cvData'config' => $config]);
  210.         $response->headers->set('Content-Type''text/css; charset=UTF-8');
  211.         return $response;
  212.     }
  213.     /**
  214.      * @Route("/ping", name="pimcore_admin_misc_ping", methods={"GET"})
  215.      *
  216.      * @param Request $request
  217.      *
  218.      * @return JsonResponse
  219.      */
  220.     public function pingAction(Request $request)
  221.     {
  222.         $response = [
  223.             'success' => true
  224.         ];
  225.         return $this->adminJson($response);
  226.     }
  227.     /**
  228.      * @Route("/available-languages", name="pimcore_admin_misc_availablelanguages", methods={"GET"})
  229.      *
  230.      * @param Request $request
  231.      *
  232.      * @return Response
  233.      */
  234.     public function availableLanguagesAction(Request $request)
  235.     {
  236.         $locales Tool::getSupportedLocales();
  237.         $response = new Response('pimcore.available_languages = ' $this->encodeJson($locales) . ';');
  238.         $response->headers->set('Content-Type''text/javascript');
  239.         return $response;
  240.     }
  241.     /**
  242.      * @Route("/get-valid-filename", name="pimcore_admin_misc_getvalidfilename", methods={"GET"})
  243.      *
  244.      * @param Request $request
  245.      *
  246.      * @return JsonResponse
  247.      */
  248.     public function getValidFilenameAction(Request $request)
  249.     {
  250.         return $this->adminJson([
  251.             'filename' => \Pimcore\Model\Element\Service::getValidKey($request->get('value'), $request->get('type'))
  252.         ]);
  253.     }
  254.     /* FILEEXPLORER */
  255.     /**
  256.      * @Route("/fileexplorer-tree", name="pimcore_admin_misc_fileexplorertree", methods={"GET"})
  257.      *
  258.      * @param Request $request
  259.      *
  260.      * @return JsonResponse
  261.      */
  262.     public function fileexplorerTreeAction(Request $request)
  263.     {
  264.         $this->checkPermission('fileexplorer');
  265.         $referencePath $this->getFileexplorerPath($request'node');
  266.         $items scandir($referencePath);
  267.         $contents = [];
  268.         foreach ($items as $item) {
  269.             if ($item == '.' || $item == '..') {
  270.                 continue;
  271.             }
  272.             $file $referencePath '/' $item;
  273.             $file str_replace('//''/'$file);
  274.             if (is_dir($file) || is_file($file)) {
  275.                 $itemConfig = [
  276.                     'id' => '/fileexplorer' str_replace(PIMCORE_PROJECT_ROOT''$file),
  277.                     'text' => $item,
  278.                     'leaf' => true,
  279.                     'writeable' => is_writable($file)
  280.                 ];
  281.                 if (is_dir($file)) {
  282.                     $itemConfig['leaf'] = false;
  283.                     $itemConfig['type'] = 'folder';
  284.                     if (is_dir_empty($file)) {
  285.                         $itemConfig['loaded'] = true;
  286.                     }
  287.                     $itemConfig['expandable'] = true;
  288.                 } elseif (is_file($file)) {
  289.                     $itemConfig['type'] = 'file';
  290.                 }
  291.                 $contents[] = $itemConfig;
  292.             }
  293.         }
  294.         return $this->adminJson($contents);
  295.     }
  296.     /**
  297.      * @Route("/fileexplorer-content", name="pimcore_admin_misc_fileexplorercontent", methods={"GET"})
  298.      *
  299.      * @param Request $request
  300.      *
  301.      * @return JsonResponse
  302.      */
  303.     public function fileexplorerContentAction(Request $request)
  304.     {
  305.         $this->checkPermission('fileexplorer');
  306.         $success false;
  307.         $writeable false;
  308.         $file $this->getFileexplorerPath($request'path');
  309.         $content null;
  310.         if (is_file($file)) {
  311.             if (is_readable($file)) {
  312.                 $content file_get_contents($file);
  313.                 $success true;
  314.                 $writeable is_writeable($file);
  315.             }
  316.         }
  317.         return $this->adminJson([
  318.             'success' => $success,
  319.             'content' => $content,
  320.             'writeable' => $writeable,
  321.             'filename' => basename($file),
  322.             'path' => preg_replace('@^' preg_quote(PIMCORE_PROJECT_ROOT'@') . '@'''$file)
  323.         ]);
  324.     }
  325.     /**
  326.      * @Route("/fileexplorer-content-save", name="pimcore_admin_misc_fileexplorercontentsave", methods={"PUT"})
  327.      *
  328.      * @param Request $request
  329.      *
  330.      * @return JsonResponse
  331.      */
  332.     public function fileexplorerContentSaveAction(Request $request)
  333.     {
  334.         $this->checkPermission('fileexplorer');
  335.         $success false;
  336.         if ($request->get('content') && $request->get('path')) {
  337.             $file $this->getFileexplorerPath($request'path');
  338.             if (is_file($file) && is_writeable($file)) {
  339.                 File::put($file$request->get('content'));
  340.                 $success true;
  341.             }
  342.         }
  343.         return $this->adminJson([
  344.             'success' => $success
  345.         ]);
  346.     }
  347.     /**
  348.      * @Route("/fileexplorer-add", name="pimcore_admin_misc_fileexploreradd", methods={"POST"})
  349.      *
  350.      * @param Request $request
  351.      *
  352.      * @return JsonResponse
  353.      *
  354.      * @throws \Exception
  355.      */
  356.     public function fileexplorerAddAction(Request $request)
  357.     {
  358.         $this->checkPermission('fileexplorer');
  359.         $success false;
  360.         if ($request->get('filename') && $request->get('path')) {
  361.             $path $this->getFileexplorerPath($request'path');
  362.             $file $path '/' $request->get('filename');
  363.             $file resolvePath($file);
  364.             if (strpos($filePIMCORE_PROJECT_ROOT) !== 0) {
  365.                 throw new \Exception('not allowed');
  366.             }
  367.             if (is_writeable(dirname($file))) {
  368.                 File::put($file'');
  369.                 $success true;
  370.             }
  371.         }
  372.         return $this->adminJson([
  373.             'success' => $success
  374.         ]);
  375.     }
  376.     /**
  377.      * @Route("/fileexplorer-add-folder", name="pimcore_admin_misc_fileexploreraddfolder", methods={"POST"})
  378.      *
  379.      * @param Request $request
  380.      *
  381.      * @return JsonResponse
  382.      *
  383.      * @throws \Exception
  384.      */
  385.     public function fileexplorerAddFolderAction(Request $request)
  386.     {
  387.         $this->checkPermission('fileexplorer');
  388.         $success false;
  389.         if ($request->get('filename') && $request->get('path')) {
  390.             $path $this->getFileexplorerPath($request'path');
  391.             $file $path '/' $request->get('filename');
  392.             $file resolvePath($file);
  393.             if (strpos($filePIMCORE_PROJECT_ROOT) !== 0) {
  394.                 throw new \Exception('not allowed');
  395.             }
  396.             if (is_writeable(dirname($file))) {
  397.                 File::mkdir($file);
  398.                 $success true;
  399.             }
  400.         }
  401.         return $this->adminJson([
  402.             'success' => $success
  403.         ]);
  404.     }
  405.     /**
  406.      * @Route("/fileexplorer-delete", name="pimcore_admin_misc_fileexplorerdelete", methods={"DELETE"})
  407.      *
  408.      * @param Request $request
  409.      *
  410.      * @return JsonResponse
  411.      */
  412.     public function fileexplorerDeleteAction(Request $request)
  413.     {
  414.         $this->checkPermission('fileexplorer');
  415.         $success false;
  416.         if ($request->get('path')) {
  417.             $file $this->getFileexplorerPath($request'path');
  418.             if (is_writeable($file)) {
  419.                 unlink($file);
  420.                 $success true;
  421.             }
  422.         }
  423.         return $this->adminJson([
  424.             'success' => $success
  425.         ]);
  426.     }
  427.     /**
  428.      * @Route("/fileexplorer-rename", name="pimcore_admin_misc_fileexplorerrename", methods={"PUT"})
  429.      *
  430.      * @param Request $request
  431.      *
  432.      * @return JsonResponse
  433.      */
  434.     public function fileexplorerRenameAction(Request $request)
  435.     {
  436.         $this->checkPermission('fileexplorer');
  437.         $success false;
  438.         if ($request->get('path') && $request->get('newPath')) {
  439.             $file $this->getFileexplorerPath($request'path');
  440.             $newFile $this->getFileexplorerPath($request'newPath');
  441.             $success rename($file$newFile);
  442.         }
  443.         return $this->adminJson([
  444.             'success' => $success
  445.         ]);
  446.     }
  447.     /**
  448.      * @param Request $request
  449.      * @param string $paramName
  450.      *
  451.      * @return mixed|string
  452.      *
  453.      * @throws \Exception
  454.      */
  455.     private function getFileexplorerPath(Request $request$paramName 'node')
  456.     {
  457.         $path preg_replace("/^\/fileexplorer/"''$request->get($paramName));
  458.         $path resolvePath(PIMCORE_PROJECT_ROOT $path);
  459.         if (strpos($pathPIMCORE_PROJECT_ROOT) !== 0) {
  460.             throw new \Exception('operation permitted, permission denied');
  461.         }
  462.         return $path;
  463.     }
  464.     /**
  465.      * @Route("/maintenance", name="pimcore_admin_misc_maintenance", methods={"POST"})
  466.      *
  467.      * @param Request $request
  468.      *
  469.      * @return JsonResponse
  470.      */
  471.     public function maintenanceAction(Request $request)
  472.     {
  473.         $this->checkPermission('maintenance_mode');
  474.         if ($request->get('activate')) {
  475.             Tool\Admin::activateMaintenanceMode(Tool\Session::getSessionId());
  476.         }
  477.         if ($request->get('deactivate')) {
  478.             Tool\Admin::deactivateMaintenanceMode();
  479.         }
  480.         return $this->adminJson([
  481.             'success' => true
  482.         ]);
  483.     }
  484.     /**
  485.      * @Route("/http-error-log", name="pimcore_admin_misc_httperrorlog", methods={"POST"})
  486.      *
  487.      * @param Request $request
  488.      *
  489.      * @return JsonResponse
  490.      */
  491.     public function httpErrorLogAction(Request $request)
  492.     {
  493.         $this->checkPermission('http_errors');
  494.         $db Db::get();
  495.         $limit intval($request->get('limit'));
  496.         $offset intval($request->get('start'));
  497.         $sort $request->get('sort');
  498.         $dir $request->get('dir');
  499.         $filter $request->get('filter');
  500.         if (!$limit) {
  501.             $limit 20;
  502.         }
  503.         if (!$offset) {
  504.             $offset 0;
  505.         }
  506.         if (!$sort || !in_array($sort, ['code''uri''date''count'])) {
  507.             $sort 'count';
  508.         }
  509.         if (!$dir || !in_array($dir, ['DESC''ASC'])) {
  510.             $dir 'DESC';
  511.         }
  512.         $condition '';
  513.         if ($filter) {
  514.             $filter $db->quote('%' $filter '%');
  515.             $conditionParts = [];
  516.             foreach (['uri''code''parametersGet''parametersPost''serverVars''cookies'] as $field) {
  517.                 $conditionParts[] = $field ' LIKE ' $filter;
  518.             }
  519.             $condition ' WHERE ' implode(' OR '$conditionParts);
  520.         }
  521.         $logs $db->fetchAll('SELECT code,uri,`count`,date FROM http_error_log ' $condition ' ORDER BY ' $sort ' ' $dir ' LIMIT ' $offset ',' $limit);
  522.         $total $db->fetchOne('SELECT count(*) FROM http_error_log ' $condition);
  523.         return $this->adminJson([
  524.             'items' => $logs,
  525.             'total' => $total,
  526.             'success' => true
  527.         ]);
  528.     }
  529.     /**
  530.      * @Route("/http-error-log-flush", name="pimcore_admin_misc_httperrorlogflush", methods={"DELETE"})
  531.      *
  532.      * @param Request $request
  533.      *
  534.      * @return JsonResponse
  535.      */
  536.     public function httpErrorLogFlushAction(Request $request)
  537.     {
  538.         $this->checkPermission('http_errors');
  539.         $db Db::get();
  540.         $db->query('TRUNCATE TABLE http_error_log');
  541.         return $this->adminJson([
  542.             'success' => true
  543.         ]);
  544.     }
  545.     /**
  546.      * @Route("/http-error-log-detail", name="pimcore_admin_misc_httperrorlogdetail", methods={"GET"})
  547.      *
  548.      * @param Request $request
  549.      *
  550.      * @return Response
  551.      */
  552.     public function httpErrorLogDetailAction(Request $request)
  553.     {
  554.         $this->checkPermission('http_errors');
  555.         $db Db::get();
  556.         $data $db->fetchRow('SELECT * FROM http_error_log WHERE uri = ?', [$request->get('uri')]);
  557.         foreach ($data as $key => &$value) {
  558.             if (in_array($key, ['parametersGet''parametersPost''serverVars''cookies'])) {
  559.                 $value unserialize($value);
  560.             }
  561.         }
  562.         $response $this->render('PimcoreAdminBundle:Admin/Misc:http-error-log-detail.html.php', ['data' => $data]);
  563.         return $response;
  564.     }
  565.     /**
  566.      * @Route("/country-list", name="pimcore_admin_misc_countrylist", methods={"GET"})
  567.      *
  568.      * @param LocaleServiceInterface $localeService
  569.      *
  570.      * @return JsonResponse
  571.      */
  572.     public function countryListAction(LocaleServiceInterface $localeService)
  573.     {
  574.         $countries $localeService->getDisplayRegions();
  575.         asort($countries);
  576.         $options = [];
  577.         foreach ($countries as $short => $translation) {
  578.             if (strlen($short) == 2) {
  579.                 $options[] = [
  580.                     'name' => $translation,
  581.                     'code' => $short
  582.                 ];
  583.             }
  584.         }
  585.         return $this->adminJson(['data' => $options]);
  586.     }
  587.     /**
  588.      * @Route("/language-list", name="pimcore_admin_misc_languagelist", methods={"GET"})
  589.      *
  590.      * @param Request $request
  591.      *
  592.      * @return JsonResponse
  593.      */
  594.     public function languageListAction(Request $request)
  595.     {
  596.         $locales Tool::getSupportedLocales();
  597.         $options = [];
  598.         foreach ($locales as $short => $translation) {
  599.             $options[] = [
  600.                 'name' => $translation,
  601.                 'code' => $short
  602.             ];
  603.         }
  604.         return $this->adminJson(['data' => $options]);
  605.     }
  606.     /**
  607.      * @Route("/phpinfo", name="pimcore_admin_misc_phpinfo", methods={"GET"})
  608.      *
  609.      * @param Request $request
  610.      * @param Profiler $profiler
  611.      *
  612.      * @throws \Exception
  613.      *
  614.      * @return Response
  615.      */
  616.     public function phpinfoAction(Request $request, ?Profiler $profiler)
  617.     {
  618.         if ($profiler) {
  619.             $profiler->disable();
  620.         }
  621.         if (!$this->getAdminUser()->isAdmin()) {
  622.             throw new \Exception('Permission denied');
  623.         }
  624.         ob_start();
  625.         phpinfo();
  626.         $content ob_get_clean();
  627.         return new Response($content);
  628.     }
  629.     /**
  630.      * @Route("/get-language-flag", name="pimcore_admin_misc_getlanguageflag", methods={"GET"})
  631.      *
  632.      * @param Request $request
  633.      *
  634.      * @return BinaryFileResponse
  635.      */
  636.     public function getLanguageFlagAction(Request $request)
  637.     {
  638.         $iconPath Tool::getLanguageFlagFile($request->get('language'));
  639.         $response = new BinaryFileResponse($iconPath);
  640.         $response->headers->set('Content-Type''image/svg+xml');
  641.         return $response;
  642.     }
  643.     /**
  644.      * @Route("/icon-list", name="pimcore_admin_misc_iconlist", methods={"GET"})
  645.      * @TemplatePhp()
  646.      *
  647.      * @param Request $request
  648.      * @param Profiler $profiler
  649.      */
  650.     public function iconListAction(Request $request, ?Profiler $profiler)
  651.     {
  652.         if ($profiler) {
  653.             $profiler->disable();
  654.         }
  655.     }
  656.     /**
  657.      * @Route("/test", name="pimcore_admin_misc_test")
  658.      *
  659.      * @param Request $request
  660.      *
  661.      * @return Response
  662.      */
  663.     public function testAction(Request $request)
  664.     {
  665.         return new Response('done');
  666.     }
  667. }