vendor/pimcore/pimcore/bundles/AdminBundle/Controller/Admin/SettingsController.php line 77

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Bundle\AdminBundle\Controller\Admin;
  15. use Pimcore\Bundle\AdminBundle\Controller\AdminAbstractController;
  16. use Pimcore\Cache;
  17. use Pimcore\Cache\Core\CoreCacheHandler;
  18. use Pimcore\Cache\Symfony\CacheClearer;
  19. use Pimcore\Config;
  20. use Pimcore\Db;
  21. use Pimcore\Event\SystemEvents;
  22. use Pimcore\File;
  23. use Pimcore\Helper\StopMessengerWorkersTrait;
  24. use Pimcore\Localization\LocaleServiceInterface;
  25. use Pimcore\Model;
  26. use Pimcore\Model\Asset;
  27. use Pimcore\Model\Document;
  28. use Pimcore\Model\Element;
  29. use Pimcore\Model\Exception\ConfigWriteException;
  30. use Pimcore\Model\Glossary;
  31. use Pimcore\Model\Metadata;
  32. use Pimcore\Model\Property;
  33. use Pimcore\Model\Staticroute;
  34. use Pimcore\Model\Tool\SettingsStore;
  35. use Pimcore\Model\WebsiteSetting;
  36. use Pimcore\Tool;
  37. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  38. use Symfony\Component\EventDispatcher\GenericEvent;
  39. use Symfony\Component\Filesystem\Filesystem;
  40. use Symfony\Component\HttpFoundation\File\UploadedFile;
  41. use Symfony\Component\HttpFoundation\JsonResponse;
  42. use Symfony\Component\HttpFoundation\Request;
  43. use Symfony\Component\HttpFoundation\Response;
  44. use Symfony\Component\HttpFoundation\StreamedResponse;
  45. use Symfony\Component\HttpKernel\Event\TerminateEvent;
  46. use Symfony\Component\HttpKernel\KernelEvents;
  47. use Symfony\Component\HttpKernel\KernelInterface;
  48. use Symfony\Component\Routing\Annotation\Route;
  49. use Symfony\Component\Yaml\Yaml;
  50. use Symfony\Contracts\Translation\TranslatorInterface;
  51. /**
  52.  * @Route("/settings")
  53.  *
  54.  * @internal
  55.  */
  56. class SettingsController extends AdminAbstractController
  57. {
  58.     use StopMessengerWorkersTrait;
  59.     private const CUSTOM_LOGO_PATH 'custom-logo.image';
  60.     public function __construct(protected TranslatorInterface $translator)
  61.     {
  62.     }
  63.     /**
  64.      * @Route("/display-custom-logo", name="pimcore_settings_display_custom_logo", methods={"GET"})
  65.      *
  66.      * @param Request $request
  67.      *
  68.      * @return StreamedResponse
  69.      */
  70.     public function displayCustomLogoAction(Request $request)
  71.     {
  72.         $mime 'image/svg+xml';
  73.         if ($request->get('white')) {
  74.             $logo PIMCORE_WEB_ROOT '/bundles/pimcoreadmin/img/logo-claim-white.svg';
  75.         } else {
  76.             $logo PIMCORE_WEB_ROOT '/bundles/pimcoreadmin/img/logo-claim-gray.svg';
  77.         }
  78.         $stream fopen($logo'rb');
  79.         $storage Tool\Storage::get('admin');
  80.         if ($storage->fileExists(self::CUSTOM_LOGO_PATH)) {
  81.             try {
  82.                 $mime $storage->mimeType(self::CUSTOM_LOGO_PATH);
  83.                 $stream $storage->readStream(self::CUSTOM_LOGO_PATH);
  84.             } catch (\Exception $e) {
  85.                 // do nothing
  86.             }
  87.         }
  88.         return new StreamedResponse(function () use ($stream) {
  89.             fpassthru($stream);
  90.         }, 200, [
  91.             'Content-Type' => $mime,
  92.             'Content-Security-Policy' => "script-src 'none'",
  93.         ]);
  94.     }
  95.     /**
  96.      * @Route("/upload-custom-logo", name="pimcore_admin_settings_uploadcustomlogo", methods={"POST"})
  97.      *
  98.      * @param Request $request
  99.      *
  100.      * @return JsonResponse
  101.      *
  102.      * @throws \Exception
  103.      */
  104.     public function uploadCustomLogoAction(Request $request)
  105.     {
  106.         $logoFile $request->files->get('Filedata');
  107.         if (!$logoFile instanceof UploadedFile
  108.             || !in_array($logoFile->guessExtension(), ['svg''png''jpg'])
  109.         ) {
  110.             throw new \Exception('Unsupported file format.');
  111.         }
  112.         $storage Tool\Storage::get('admin');
  113.         $storage->writeStream(self::CUSTOM_LOGO_PATHfopen($logoFile->getPathname(), 'rb'));
  114.         // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in
  115.         // Ext.form.Action.Submit and mark the submission as failed
  116.         $response $this->adminJson(['success' => true]);
  117.         $response->headers->set('Content-Type''text/html');
  118.         return $response;
  119.     }
  120.     /**
  121.      * @Route("/delete-custom-logo", name="pimcore_admin_settings_deletecustomlogo", methods={"DELETE"})
  122.      *
  123.      * @param Request $request
  124.      *
  125.      * @return JsonResponse
  126.      */
  127.     public function deleteCustomLogoAction(Request $request)
  128.     {
  129.         if (Tool\Storage::get('admin')->fileExists(self::CUSTOM_LOGO_PATH)) {
  130.             Tool\Storage::get('admin')->delete(self::CUSTOM_LOGO_PATH);
  131.         }
  132.         return $this->adminJson(['success' => true]);
  133.     }
  134.     /**
  135.      * Used by the predefined metadata grid
  136.      *
  137.      * @Route("/predefined-metadata", name="pimcore_admin_settings_metadata", methods={"POST"})
  138.      *
  139.      * @param Request $request
  140.      *
  141.      * @return JsonResponse
  142.      */
  143.     public function metadataAction(Request $request)
  144.     {
  145.         $this->checkPermission('asset_metadata');
  146.         if ($request->get('data')) {
  147.             if ($request->get('xaction') == 'destroy') {
  148.                 $data $this->decodeJson($request->get('data'));
  149.                 $id $data['id'];
  150.                 $metadata Metadata\Predefined::getById($id);
  151.                 if (!$metadata->isWriteable()) {
  152.                     throw new ConfigWriteException();
  153.                 }
  154.                 $metadata->delete();
  155.                 return $this->adminJson(['success' => true'data' => []]);
  156.             } elseif ($request->get('xaction') == 'update') {
  157.                 $data $this->decodeJson($request->get('data'));
  158.                 // save type
  159.                 $metadata Metadata\Predefined::getById($data['id']);
  160.                 if (!$metadata->isWriteable()) {
  161.                     throw new ConfigWriteException();
  162.                 }
  163.                 $metadata->setValues($data);
  164.                 $existingItem Metadata\Predefined\Listing::getByKeyAndLanguage($metadata->getName(), $metadata->getLanguage(), $metadata->getTargetSubtype());
  165.                 if ($existingItem && $existingItem->getId() != $metadata->getId()) {
  166.                     return $this->adminJson(['message' => 'rule_violation''success' => false]);
  167.                 }
  168.                 $metadata->minimize();
  169.                 $metadata->save();
  170.                 $metadata->expand();
  171.                 $responseData $metadata->getObjectVars();
  172.                 $responseData['writeable'] = $metadata->isWriteable();
  173.                 return $this->adminJson(['data' => $responseData'success' => true]);
  174.             } elseif ($request->get('xaction') == 'create') {
  175.                 if (!(new Metadata\Predefined())->isWriteable()) {
  176.                     throw new ConfigWriteException();
  177.                 }
  178.                 $data $this->decodeJson($request->get('data'));
  179.                 unset($data['id']);
  180.                 // save type
  181.                 $metadata Metadata\Predefined::create();
  182.                 $metadata->setValues($data);
  183.                 $existingItem Metadata\Predefined\Listing::getByKeyAndLanguage($metadata->getName(), $metadata->getLanguage(), $metadata->getTargetSubtype());
  184.                 if ($existingItem) {
  185.                     return $this->adminJson(['message' => 'rule_violation''success' => false]);
  186.                 }
  187.                 $metadata->save();
  188.                 $responseData $metadata->getObjectVars();
  189.                 $responseData['writeable'] = $metadata->isWriteable();
  190.                 return $this->adminJson(['data' => $responseData'success' => true]);
  191.             }
  192.         } else {
  193.             // get list of types
  194.             $list = new Metadata\Predefined\Listing();
  195.             if ($filter $request->get('filter')) {
  196.                 $list->setFilter(function (Metadata\Predefined $predefined) use ($filter) {
  197.                     foreach ($predefined->getObjectVars() as $value) {
  198.                         if (stripos((string)$value$filter) !== false) {
  199.                             return true;
  200.                         }
  201.                     }
  202.                     return false;
  203.                 });
  204.             }
  205.             $properties = [];
  206.             foreach ($list->getDefinitions() as $metadata) {
  207.                 $metadata->expand();
  208.                 $data $metadata->getObjectVars();
  209.                 $data['writeable'] = $metadata->isWriteable();
  210.                 $properties[] = $data;
  211.             }
  212.             return $this->adminJson(['data' => $properties'success' => true'total' => $list->getTotalCount()]);
  213.         }
  214.         return $this->adminJson(['success' => false]);
  215.     }
  216.     /**
  217.      * @Route("/get-predefined-metadata", name="pimcore_admin_settings_getpredefinedmetadata", methods={"GET"})
  218.      *
  219.      * @param Request $request
  220.      *
  221.      * @return JsonResponse
  222.      */
  223.     public function getPredefinedMetadataAction(Request $request)
  224.     {
  225.         $type $request->get('type');
  226.         $subType $request->get('subType');
  227.         $group $request->get('group');
  228.         $list Metadata\Predefined\Listing::getByTargetType($type, [$subType]);
  229.         $result = [];
  230.         foreach ($list as $item) {
  231.             $itemGroup $item->getGroup() ?? '';
  232.             if ($group === 'default' || $group === $itemGroup) {
  233.                 $item->expand();
  234.                 $data $item->getObjectVars();
  235.                 $data['writeable'] = $item->isWriteable();
  236.                 $result[] = $data;
  237.             }
  238.         }
  239.         return $this->adminJson(['data' => $result'success' => true]);
  240.     }
  241.     /**
  242.      * @Route("/properties", name="pimcore_admin_settings_properties", methods={"POST"})
  243.      *
  244.      * @param Request $request
  245.      *
  246.      * @return JsonResponse
  247.      */
  248.     public function propertiesAction(Request $request)
  249.     {
  250.         if ($request->get('data')) {
  251.             $this->checkPermission('predefined_properties');
  252.             if ($request->get('xaction') == 'destroy') {
  253.                 $data $this->decodeJson($request->get('data'));
  254.                 $id $data['id'];
  255.                 $property Property\Predefined::getById($id);
  256.                 if (!$property->isWriteable()) {
  257.                     throw new ConfigWriteException();
  258.                 }
  259.                 $property->delete();
  260.                 return $this->adminJson(['success' => true'data' => []]);
  261.             } elseif ($request->get('xaction') == 'update') {
  262.                 $data $this->decodeJson($request->get('data'));
  263.                 // save type
  264.                 $property Property\Predefined::getById($data['id']);
  265.                 if (!$property->isWriteable()) {
  266.                     throw new ConfigWriteException();
  267.                 }
  268.                 if (is_array($data['ctype'])) {
  269.                     $data['ctype'] = implode(','$data['ctype']);
  270.                 }
  271.                 $property->setValues($data);
  272.                 $property->save();
  273.                 $responseData $property->getObjectVars();
  274.                 $responseData['writeable'] = $property->isWriteable();
  275.                 return $this->adminJson(['data' => $responseData'success' => true]);
  276.             } elseif ($request->get('xaction') == 'create') {
  277.                 if (!(new Property\Predefined())->isWriteable()) {
  278.                     throw new ConfigWriteException();
  279.                 }
  280.                 $data $this->decodeJson($request->get('data'));
  281.                 unset($data['id']);
  282.                 // save type
  283.                 $property Property\Predefined::create();
  284.                 $property->setValues($data);
  285.                 $property->save();
  286.                 $responseData $property->getObjectVars();
  287.                 $responseData['writeable'] = $property->isWriteable();
  288.                 return $this->adminJson(['data' => $responseData'success' => true]);
  289.             }
  290.         } else {
  291.             // get list of types
  292.             $list = new Property\Predefined\Listing();
  293.             if ($filter $request->get('filter')) {
  294.                 $list->setFilter(function (Property\Predefined $predefined) use ($filter) {
  295.                     foreach ($predefined->getObjectVars() as $value) {
  296.                         if ($value) {
  297.                             $cellValues is_array($value) ? $value : [$value];
  298.                             foreach ($cellValues as $cellValue) {
  299.                                 if (stripos((string)$cellValue$filter) !== false) {
  300.                                     return true;
  301.                                 }
  302.                             }
  303.                         }
  304.                     }
  305.                     return false;
  306.                 });
  307.             }
  308.             $properties = [];
  309.             foreach ($list->getProperties() as $property) {
  310.                 $data $property->getObjectVars();
  311.                 $data['writeable'] = $property->isWriteable();
  312.                 $properties[] = $data;
  313.             }
  314.             return $this->adminJson(['data' => $properties'success' => true'total' => $list->getTotalCount()]);
  315.         }
  316.         return $this->adminJson(['success' => false]);
  317.     }
  318.     /**
  319.      * @Route("/get-system", name="pimcore_admin_settings_getsystem", methods={"GET"})
  320.      *
  321.      * @param Request $request
  322.      * @param Config $config
  323.      *
  324.      * @return JsonResponse
  325.      */
  326.     public function getSystemAction(Request $requestConfig $config)
  327.     {
  328.         $this->checkPermission('system_settings');
  329.         $valueArray = [
  330.             'general' => $config['general'],
  331.             'documents' => $config['documents'],
  332.             'assets' => $config['assets'],
  333.             'objects' => $config['objects'],
  334.             'branding' => $config['branding'],
  335.             'email' => $config['email'],
  336.         ];
  337.         $locales Tool::getSupportedLocales();
  338.         $languageOptions = [];
  339.         $validLanguages = [];
  340.         foreach ($locales as $short => $translation) {
  341.             if (!empty($short)) {
  342.                 $languageOptions[] = [
  343.                     'language' => $short,
  344.                     'display' => $translation " ($short)",
  345.                 ];
  346.                 $validLanguages[] = $short;
  347.             }
  348.         }
  349.         $valueArray['general']['valid_language'] = explode(','$valueArray['general']['valid_languages']);
  350.         //for "wrong" legacy values
  351.         foreach ($valueArray['general']['valid_language'] as $existingValue) {
  352.             if (!in_array($existingValue$validLanguages)) {
  353.                 $languageOptions[] = [
  354.                     'language' => $existingValue,
  355.                     'display' => $existingValue,
  356.                 ];
  357.             }
  358.         }
  359.         $response = [
  360.             'values' => $valueArray,
  361.             'config' => [
  362.                 'languages' => $languageOptions,
  363.             ],
  364.         ];
  365.         return $this->adminJson($response);
  366.     }
  367.     /**
  368.      * @Route("/set-system", name="pimcore_admin_settings_setsystem", methods={"PUT"})
  369.      *
  370.      * @param Request $request
  371.      * @param LocaleServiceInterface $localeService
  372.      *
  373.      * @return JsonResponse
  374.      */
  375.     public function setSystemAction(
  376.         LocaleServiceInterface $localeService,
  377.         Request $request,
  378.         KernelInterface $kernel,
  379.         EventDispatcherInterface $eventDispatcher,
  380.         CoreCacheHandler $cache,
  381.         Filesystem $filesystem,
  382.         CacheClearer $symfonyCacheClearer
  383.     ) {
  384.         $this->checkPermission('system_settings');
  385.         $values $this->decodeJson($request->get('data'));
  386.         $existingValues = [];
  387.         try {
  388.             $file Config::locateConfigFile('system.yml');
  389.             $existingValues Config::getConfigInstance($filetrue);
  390.         } catch (\Exception $e) {
  391.             // nothing to do
  392.         }
  393.         // localized error pages
  394.         $localizedErrorPages = [];
  395.         // fallback languages
  396.         $fallbackLanguages = [];
  397.         $existingValues['pimcore']['general']['fallback_languages'] = [];
  398.         $languages explode(','$values['general.validLanguages']);
  399.         $filteredLanguages = [];
  400.         foreach ($languages as $language) {
  401.             if (isset($values['general.fallbackLanguages.' $language])) {
  402.                 $fallbackLanguages[$language] = str_replace(' '''$values['general.fallbackLanguages.' $language]);
  403.             }
  404.             // localized error pages
  405.             if (isset($values['documents.error_pages.localized.' $language])) {
  406.                 $localizedErrorPages[$language] = $values['documents.error_pages.localized.' $language];
  407.             }
  408.             if ($localeService->isLocale($language)) {
  409.                 $filteredLanguages[] = $language;
  410.             }
  411.         }
  412.         // check if there's a fallback language endless loop
  413.         foreach ($fallbackLanguages as $sourceLang => $targetLang) {
  414.             $this->checkFallbackLanguageLoop($sourceLang$fallbackLanguages);
  415.         }
  416.         $settings['pimcore'] = [
  417.             'general' => [
  418.                 'domain' => $values['general.domain'],
  419.                 'redirect_to_maindomain' => $values['general.redirect_to_maindomain'],
  420.                 'language' => $values['general.language'],
  421.                 'valid_languages' => implode(','$filteredLanguages),
  422.                 'fallback_languages' => $fallbackLanguages,
  423.                 'default_language' => $values['general.defaultLanguage'],
  424.                 'debug_admin_translations' => $values['general.debug_admin_translations'],
  425.             ],
  426.             'documents' => [
  427.                 'versions' => [
  428.                     'days' => $values['documents.versions.days'] ?? null,
  429.                     'steps' => $values['documents.versions.steps'] ?? null,
  430.                 ],
  431.                 'error_pages' => [
  432.                     'default' => $values['documents.error_pages.default'],
  433.                     'localized' => $localizedErrorPages,
  434.                 ],
  435.             ],
  436.             'objects' => [
  437.                 'versions' => [
  438.                     'days' => $values['objects.versions.days'] ?? null,
  439.                     'steps' => $values['objects.versions.steps'] ?? null,
  440.                 ],
  441.             ],
  442.             'assets' => [
  443.                 'versions' => [
  444.                     'days' => $values['assets.versions.days'] ?? null,
  445.                     'steps' => $values['assets.versions.steps'] ?? null,
  446.                 ],
  447.                 'hide_edit_image' => $values['assets.hide_edit_image'],
  448.                 'disable_tree_preview' => $values['assets.disable_tree_preview'],
  449.             ],
  450.         ];
  451.         //branding
  452.         $settings['pimcore_admin'] = [
  453.             'branding' =>
  454.                 [
  455.                     'login_screen_invert_colors' => $values['branding.login_screen_invert_colors'],
  456.                     'color_login_screen' => $values['branding.color_login_screen'],
  457.                     'color_admin_interface' => $values['branding.color_admin_interface'],
  458.                     'color_admin_interface_background' => $values['branding.color_admin_interface_background'],
  459.                     'login_screen_custom_image' => str_replace('%''%%'$values['branding.login_screen_custom_image']),
  460.                 ],
  461.         ];
  462.         if (array_key_exists('email.debug.emailAddresses'$values) && $values['email.debug.emailAddresses']) {
  463.             $settings['pimcore']['email']['debug']['email_addresses'] = $values['email.debug.emailAddresses'];
  464.         }
  465.         $settingsYml Yaml::dump($settings5);
  466.         $configFile Config::locateConfigFile('system.yml');
  467.         File::put($configFile$settingsYml);
  468.         // clear all caches
  469.         $this->clearSymfonyCache($request$kernel$eventDispatcher$symfonyCacheClearer);
  470.         $this->stopMessengerWorkers();
  471.         $eventDispatcher->addListener(KernelEvents::TERMINATE, function (TerminateEvent $event) use (
  472.             $cache$eventDispatcher$filesystem
  473.         ) {
  474.             // we need to clear the cache with a delay, because the cache is used by messenger:stop-workers
  475.             // to send the stop signal to all worker processes
  476.             sleep(2);
  477.             $this->clearPimcoreCache($cache$eventDispatcher$filesystem);
  478.         });
  479.         return $this->adminJson(['success' => true]);
  480.     }
  481.     /**
  482.      * @param string $source
  483.      * @param array $definitions
  484.      * @param array $fallbacks
  485.      *
  486.      * @throws \Exception
  487.      */
  488.     protected function checkFallbackLanguageLoop($source$definitions$fallbacks = [])
  489.     {
  490.         if (isset($definitions[$source])) {
  491.             $targets explode(','$definitions[$source]);
  492.             foreach ($targets as $l) {
  493.                 $target trim($l);
  494.                 if ($target) {
  495.                     if (in_array($target$fallbacks)) {
  496.                         throw new \Exception("Language `$source` | `$target` causes an infinte loop.");
  497.                     }
  498.                     $fallbacks[] = $target;
  499.                     $this->checkFallbackLanguageLoop($target$definitions$fallbacks);
  500.                 }
  501.             }
  502.         } else {
  503.             throw new \Exception("Language `$source` doesn't exist");
  504.         }
  505.     }
  506.     /**
  507.      * @Route("/get-web2print", name="pimcore_admin_settings_getweb2print", methods={"GET"})
  508.      *
  509.      * @param Request $request
  510.      *
  511.      * @return JsonResponse
  512.      */
  513.     public function getWeb2printAction(Request $request)
  514.     {
  515.         $this->checkPermission('web2print_settings');
  516.         $values Config::getWeb2PrintConfig();
  517.         $valueArray $values->toArray();
  518.         $optionsString = [];
  519.         if ($valueArray['wkhtml2pdfOptions'] ?? false) {
  520.             foreach ($valueArray['wkhtml2pdfOptions'] as $key => $value) {
  521.                 $tmpStr '--'.$key;
  522.                 if ($value !== null && $value !== '') {
  523.                     $tmpStr .= ' '.$value;
  524.                 }
  525.                 $optionsString[] = $tmpStr;
  526.             }
  527.         }
  528.         $valueArray['wkhtml2pdfOptions'] = implode("\n"$optionsString);
  529.         $response = [
  530.             'values' => $valueArray,
  531.         ];
  532.         return $this->adminJson($response);
  533.     }
  534.     /**
  535.      * @Route("/set-web2print", name="pimcore_admin_settings_setweb2print", methods={"PUT"})
  536.      *
  537.      * @param Request $request
  538.      *
  539.      * @return JsonResponse
  540.      */
  541.     public function setWeb2printAction(Request $request)
  542.     {
  543.         $this->checkPermission('web2print_settings');
  544.         $values $this->decodeJson($request->get('data'));
  545.         unset($values['documentation']);
  546.         unset($values['additions']);
  547.         unset($values['json_converter']);
  548.         if ($values['wkhtml2pdfOptions']) {
  549.             $optionArray = [];
  550.             $lines explode("\n"$values['wkhtml2pdfOptions']);
  551.             foreach ($lines as $line) {
  552.                 $parts explode(' 'substr($line2));
  553.                 $key trim($parts[0]);
  554.                 if ($key) {
  555.                     $value trim($parts[1] ?? '');
  556.                     $optionArray[$key] = $value;
  557.                 }
  558.             }
  559.             $values['wkhtml2pdfOptions'] = $optionArray;
  560.         }
  561.         \Pimcore\Web2Print\Config::save($values);
  562.         return $this->adminJson(['success' => true]);
  563.     }
  564.     /**
  565.      * @Route("/clear-cache", name="pimcore_admin_settings_clearcache", methods={"DELETE"})
  566.      *
  567.      * @param Request $request
  568.      * @param KernelInterface $kernel
  569.      * @param EventDispatcherInterface $eventDispatcher
  570.      * @param CoreCacheHandler $cache
  571.      * @param Filesystem $filesystem
  572.      * @param CacheClearer $symfonyCacheClearer
  573.      *
  574.      * @return JsonResponse
  575.      */
  576.     public function clearCacheAction(
  577.         Request $request,
  578.         KernelInterface $kernel,
  579.         EventDispatcherInterface $eventDispatcher,
  580.         CoreCacheHandler $cache,
  581.         Filesystem $filesystem,
  582.         CacheClearer $symfonyCacheClearer
  583.     ) {
  584.         $this->checkPermissionsHasOneOf(['clear_cache''system_settings']);
  585.         $result = [
  586.             'success' => true,
  587.         ];
  588.         $clearPimcoreCache = !(bool)$request->get('only_symfony_cache');
  589.         $clearSymfonyCache = !(bool)$request->get('only_pimcore_cache');
  590.         if ($clearPimcoreCache) {
  591.             $this->clearPimcoreCache($cache$eventDispatcher$filesystem);
  592.         }
  593.         if ($clearSymfonyCache) {
  594.             $this->clearSymfonyCache($request$kernel$eventDispatcher$symfonyCacheClearer);
  595.         }
  596.         $response = new JsonResponse($result);
  597.         if ($clearSymfonyCache) {
  598.             // we send the response directly here and exit to make sure no code depending on the stale container
  599.             // is running after this
  600.             $response->sendHeaders();
  601.             $response->sendContent();
  602.             exit;
  603.         }
  604.         return $response;
  605.     }
  606.     private function clearPimcoreCache(
  607.         CoreCacheHandler $cache,
  608.         EventDispatcherInterface $eventDispatcher,
  609.         Filesystem $filesystem,
  610.     ): void {
  611.         // empty document cache
  612.         $cache->clearAll();
  613.         if ($filesystem->exists(PIMCORE_CACHE_DIRECTORY)) {
  614.             $filesystem->remove(PIMCORE_CACHE_DIRECTORY);
  615.         }
  616.         // PIMCORE-1854 - recreate .dummy file => should remain
  617.         File::put(PIMCORE_CACHE_DIRECTORY '/.gitkeep''');
  618.         $eventDispatcher->dispatch(new GenericEvent(), SystemEvents::CACHE_CLEAR);
  619.     }
  620.     private function clearSymfonyCache(
  621.         Request $request,
  622.         KernelInterface $kernel,
  623.         EventDispatcherInterface $eventDispatcher,
  624.         CacheClearer $symfonyCacheClearer,
  625.     ): void {
  626.         // pass one or move env parameters to clear multiple envs
  627.         // if no env is passed it will use the current one
  628.         $environments $request->get('env'$kernel->getEnvironment());
  629.         if (!is_array($environments)) {
  630.             $environments trim((string)$environments);
  631.             if (empty($environments)) {
  632.                 $environments = [];
  633.             } else {
  634.                 $environments = [$environments];
  635.             }
  636.         }
  637.         if (empty($environments)) {
  638.             $environments = [$kernel->getEnvironment()];
  639.         }
  640.         $result['environments'] = $environments;
  641.         if (in_array($kernel->getEnvironment(), $environments)) {
  642.             // remove terminate and exception event listeners for the current env as they break with a
  643.             // cleared container - see #2434
  644.             foreach ($eventDispatcher->getListeners(KernelEvents::TERMINATE) as $listener) {
  645.                 $eventDispatcher->removeListener(KernelEvents::TERMINATE$listener);
  646.             }
  647.             foreach ($eventDispatcher->getListeners(KernelEvents::EXCEPTION) as $listener) {
  648.                 $eventDispatcher->removeListener(KernelEvents::EXCEPTION$listener);
  649.             }
  650.         }
  651.         foreach ($environments as $environment) {
  652.             try {
  653.                 $symfonyCacheClearer->clear($environment);
  654.             } catch (\Throwable $e) {
  655.                 $errors $result['errors'] ?? [];
  656.                 $errors[] = $e->getMessage();
  657.                 $result array_merge($result, [
  658.                     'success' => false,
  659.                     'errors' => $errors,
  660.                 ]);
  661.             }
  662.         }
  663.     }
  664.     /**
  665.      * @Route("/clear-output-cache", name="pimcore_admin_settings_clearoutputcache", methods={"DELETE"})
  666.      *
  667.      * @param EventDispatcherInterface $eventDispatcher
  668.      *
  669.      * @return JsonResponse
  670.      */
  671.     public function clearOutputCacheAction(EventDispatcherInterface $eventDispatcher)
  672.     {
  673.         $this->checkPermission('clear_fullpage_cache');
  674.         // remove "output" out of the ignored tags, if a cache lifetime is specified
  675.         Cache::removeIgnoredTagOnClear('output');
  676.         // empty document cache
  677.         Cache::clearTags(['output''output_lifetime']);
  678.         $eventDispatcher->dispatch(new GenericEvent(), SystemEvents::CACHE_CLEAR_FULLPAGE_CACHE);
  679.         return $this->adminJson(['success' => true]);
  680.     }
  681.     /**
  682.      * @Route("/clear-temporary-files", name="pimcore_admin_settings_cleartemporaryfiles", methods={"DELETE"})
  683.      *
  684.      * @param EventDispatcherInterface $eventDispatcher
  685.      *
  686.      * @return JsonResponse
  687.      */
  688.     public function clearTemporaryFilesAction(EventDispatcherInterface $eventDispatcher)
  689.     {
  690.         $this->checkPermission('clear_temp_files');
  691.         // public files
  692.         Tool\Storage::get('thumbnail')->deleteDirectory('/');
  693.         Db::get()->executeQuery('TRUNCATE TABLE assets_image_thumbnail_cache');
  694.         Tool\Storage::get('asset_cache')->deleteDirectory('/');
  695.         // system files
  696.         recursiveDelete(PIMCORE_SYSTEM_TEMP_DIRECTORYfalse);
  697.         $eventDispatcher->dispatch(new GenericEvent(), SystemEvents::CACHE_CLEAR_TEMPORARY_FILES);
  698.         return $this->adminJson(['success' => true]);
  699.     }
  700.     /**
  701.      * @Route("/staticroutes", name="pimcore_admin_settings_staticroutes", methods={"POST"})
  702.      *
  703.      * @param Request $request
  704.      *
  705.      * @return JsonResponse
  706.      */
  707.     public function staticroutesAction(Request $request)
  708.     {
  709.         if ($request->get('data')) {
  710.             $this->checkPermission('routes');
  711.             $data $this->decodeJson($request->get('data'));
  712.             if (is_array($data)) {
  713.                 foreach ($data as &$value) {
  714.                     if (is_string($value)) {
  715.                         $value trim($value);
  716.                     }
  717.                 }
  718.             }
  719.             if ($request->get('xaction') == 'destroy') {
  720.                 $data $this->decodeJson($request->get('data'));
  721.                 $id $data['id'];
  722.                 $route Staticroute::getById($id);
  723.                 if (!$route->isWriteable()) {
  724.                     throw new ConfigWriteException();
  725.                 }
  726.                 $route->delete();
  727.                 return $this->adminJson(['success' => true'data' => []]);
  728.             } elseif ($request->get('xaction') == 'update') {
  729.                 // save routes
  730.                 $route Staticroute::getById($data['id']);
  731.                 if (!$route->isWriteable()) {
  732.                     throw new ConfigWriteException();
  733.                 }
  734.                 $route->setValues($data);
  735.                 $route->save();
  736.                 return $this->adminJson(['data' => $route->getObjectVars(), 'success' => true]);
  737.             } elseif ($request->get('xaction') == 'create') {
  738.                 if (!(new Staticroute())->isWriteable()) {
  739.                     throw new ConfigWriteException();
  740.                 }
  741.                 unset($data['id']);
  742.                 // save route
  743.                 $route = new Staticroute();
  744.                 $route->setValues($data);
  745.                 $route->save();
  746.                 $responseData $route->getObjectVars();
  747.                 $responseData['writeable'] = $route->isWriteable();
  748.                 return $this->adminJson(['data' => $responseData'success' => true]);
  749.             }
  750.         } else {
  751.             // get list of routes
  752.             $list = new Staticroute\Listing();
  753.             if ($filter $request->get('filter')) {
  754.                 $list->setFilter(function (Staticroute $staticRoute) use ($filter) {
  755.                     foreach ($staticRoute->getObjectVars() as $value) {
  756.                         if (!is_scalar($value)) {
  757.                             continue;
  758.                         }
  759.                         if (stripos((string)$value$filter) !== false) {
  760.                             return true;
  761.                         }
  762.                     }
  763.                     return false;
  764.                 });
  765.             }
  766.             $routes = [];
  767.             foreach ($list->getRoutes() as $routeFromList) {
  768.                 $route $routeFromList->getObjectVars();
  769.                 $route['writeable'] = $routeFromList->isWriteable();
  770.                 if (is_array($routeFromList->getSiteId())) {
  771.                     $route['siteId'] = implode(','$routeFromList->getSiteId());
  772.                 }
  773.                 $routes[] = $route;
  774.             }
  775.             return $this->adminJson(['data' => $routes'success' => true'total' => $list->getTotalCount()]);
  776.         }
  777.         return $this->adminJson(['success' => false]);
  778.     }
  779.     /**
  780.      * @Route("/get-available-admin-languages", name="pimcore_admin_settings_getavailableadminlanguages", methods={"GET"})
  781.      *
  782.      * @param Request $request
  783.      *
  784.      * @return JsonResponse
  785.      */
  786.     public function getAvailableAdminLanguagesAction(Request $request)
  787.     {
  788.         $langs = [];
  789.         $availableLanguages Tool\Admin::getLanguages();
  790.         $locales Tool::getSupportedLocales();
  791.         foreach ($availableLanguages as $lang) {
  792.             if (array_key_exists($lang$locales)) {
  793.                 $langs[] = [
  794.                     'language' => $lang,
  795.                     'display' => $locales[$lang],
  796.                 ];
  797.             }
  798.         }
  799.         usort($langs, function ($a$b) {
  800.             return strcmp($a['display'], $b['display']);
  801.         });
  802.         return $this->adminJson($langs);
  803.     }
  804.     /**
  805.      * @Route("/glossary", name="pimcore_admin_settings_glossary", methods={"POST"})
  806.      *
  807.      * @param Request $request
  808.      *
  809.      * @return JsonResponse
  810.      */
  811.     public function glossaryAction(Request $request)
  812.     {
  813.         if ($request->get('data')) {
  814.             $this->checkPermission('glossary');
  815.             Cache::clearTag('glossary');
  816.             if ($request->get('xaction') == 'destroy') {
  817.                 $data $this->decodeJson($request->get('data'));
  818.                 $id $data['id'];
  819.                 $glossary Glossary::getById($id);
  820.                 $glossary->delete();
  821.                 return $this->adminJson(['success' => true'data' => []]);
  822.             } elseif ($request->get('xaction') == 'update') {
  823.                 $data $this->decodeJson($request->get('data'));
  824.                 // save glossary
  825.                 $glossary Glossary::getById($data['id']);
  826.                 if (!empty($data['link'])) {
  827.                     if ($doc Document::getByPath($data['link'])) {
  828.                         $data['link'] = $doc->getId();
  829.                     }
  830.                 }
  831.                 $glossary->setValues($data);
  832.                 $glossary->save();
  833.                 if ($link $glossary->getLink()) {
  834.                     if ((int)$link 0) {
  835.                         if ($doc Document::getById((int)$link)) {
  836.                             $glossary->setLink($doc->getRealFullPath());
  837.                         }
  838.                     }
  839.                 }
  840.                 return $this->adminJson(['data' => $glossary'success' => true]);
  841.             } elseif ($request->get('xaction') == 'create') {
  842.                 $data $this->decodeJson($request->get('data'));
  843.                 unset($data['id']);
  844.                 // save glossary
  845.                 $glossary = new Glossary();
  846.                 if (!empty($data['link'])) {
  847.                     if ($doc Document::getByPath($data['link'])) {
  848.                         $data['link'] = $doc->getId();
  849.                     }
  850.                 }
  851.                 $glossary->setValues($data);
  852.                 $glossary->save();
  853.                 if ($link $glossary->getLink()) {
  854.                     if ((int)$link 0) {
  855.                         if ($doc Document::getById((int)$link)) {
  856.                             $glossary->setLink($doc->getRealFullPath());
  857.                         }
  858.                     }
  859.                 }
  860.                 return $this->adminJson(['data' => $glossary->getObjectVars(), 'success' => true]);
  861.             }
  862.         } else {
  863.             // get list of glossaries
  864.             $list = new Glossary\Listing();
  865.             $list->setLimit($request->get('limit'));
  866.             $list->setOffset($request->get('start'));
  867.             $sortingSettings \Pimcore\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings(array_merge($request->request->all(), $request->query->all()));
  868.             if ($sortingSettings['orderKey']) {
  869.                 $list->setOrderKey($sortingSettings['orderKey']);
  870.                 $list->setOrder($sortingSettings['order']);
  871.             }
  872.             if ($request->get('filter')) {
  873.                 $list->setCondition('`text` LIKE ' $list->quote('%'.$request->get('filter').'%'));
  874.             }
  875.             $list->load();
  876.             $glossaries = [];
  877.             foreach ($list->getGlossary() as $glossary) {
  878.                 if ($link $glossary->getLink()) {
  879.                     if ((int)$link 0) {
  880.                         if ($doc Document::getById((int)$link)) {
  881.                             $glossary->setLink($doc->getRealFullPath());
  882.                         }
  883.                     }
  884.                 }
  885.                 $glossaries[] = $glossary->getObjectVars();
  886.             }
  887.             return $this->adminJson(['data' => $glossaries'success' => true'total' => $list->getTotalCount()]);
  888.         }
  889.         return $this->adminJson(['success' => false]);
  890.     }
  891.     /**
  892.      * @Route("/get-available-sites", name="pimcore_admin_settings_getavailablesites", methods={"GET"})
  893.      *
  894.      * @param Request $request
  895.      *
  896.      * @return JsonResponse
  897.      */
  898.     public function getAvailableSitesAction(Request $request)
  899.     {
  900.         $excludeMainSite $request->get('excludeMainSite');
  901.         $sitesList = new Model\Site\Listing();
  902.         $sitesObjects $sitesList->load();
  903.         $sites = [];
  904.         if (!$excludeMainSite) {
  905.             $sites[] = [
  906.                 'id' => 'default',
  907.                 'rootId' => 1,
  908.                 'domains' => '',
  909.                 'rootPath' => '/',
  910.                 'domain' => $this->translator->trans('main_site'),
  911.             ];
  912.         }
  913.         foreach ($sitesObjects as $site) {
  914.             if ($site->getRootDocument()) {
  915.                 if ($site->getMainDomain()) {
  916.                     $sites[] = [
  917.                         'id' => $site->getId(),
  918.                         'rootId' => $site->getRootId(),
  919.                         'domains' => implode(','$site->getDomains()),
  920.                         'rootPath' => $site->getRootPath(),
  921.                         'domain' => $site->getMainDomain(),
  922.                     ];
  923.                 }
  924.             } else {
  925.                 // site is useless, parent doesn't exist anymore
  926.                 $site->delete();
  927.             }
  928.         }
  929.         return $this->adminJson($sites);
  930.     }
  931.     /**
  932.      * @Route("/get-available-countries", name="pimcore_admin_settings_getavailablecountries", methods={"GET"})
  933.      *
  934.      * @param LocaleServiceInterface $localeService
  935.      *
  936.      * @return JsonResponse
  937.      */
  938.     public function getAvailableCountriesAction(LocaleServiceInterface $localeService)
  939.     {
  940.         $countries $localeService->getDisplayRegions();
  941.         asort($countries);
  942.         $options = [];
  943.         foreach ($countries as $short => $translation) {
  944.             if (strlen($short) == 2) {
  945.                 $options[] = [
  946.                     'key' => $translation ' (' $short ')',
  947.                     'value' => $short,
  948.                 ];
  949.             }
  950.         }
  951.         $result = ['data' => $options'success' => true'total' => count($options)];
  952.         return $this->adminJson($result);
  953.     }
  954.     /**
  955.      * @Route("/thumbnail-adapter-check", name="pimcore_admin_settings_thumbnailadaptercheck", methods={"GET"})
  956.      *
  957.      * @param Request $request
  958.      * @param TranslatorInterface $translator
  959.      *
  960.      * @return Response
  961.      */
  962.     public function thumbnailAdapterCheckAction(Request $requestTranslatorInterface $translator)
  963.     {
  964.         $content '';
  965.         $instance \Pimcore\Image::getInstance();
  966.         if ($instance instanceof \Pimcore\Image\Adapter\GD) {
  967.             $content '<span style="color: red; font-weight: bold;padding: 10px;margin:0 0 20px 0;border:1px solid red;display:block;">' .
  968.                 $translator->trans('important_use_imagick_pecl_extensions_for_best_results_gd_is_just_a_fallback_with_less_quality', [], 'admin') .
  969.                 '</span>';
  970.         }
  971.         return new Response($content);
  972.     }
  973.     /**
  974.      * @Route("/thumbnail-tree", name="pimcore_admin_settings_thumbnailtree", methods={"GET", "POST"})
  975.      *
  976.      * @return JsonResponse
  977.      */
  978.     public function thumbnailTreeAction()
  979.     {
  980.         $this->checkPermission('thumbnails');
  981.         $thumbnails = [];
  982.         $list = new Asset\Image\Thumbnail\Config\Listing();
  983.         $groups = [];
  984.         foreach ($list->getThumbnails() as $item) {
  985.             if ($item->getGroup()) {
  986.                 if (empty($groups[$item->getGroup()])) {
  987.                     $groups[$item->getGroup()] = [
  988.                         'id' => 'group_' $item->getName(),
  989.                         'text' => htmlspecialchars($item->getGroup()),
  990.                         'expandable' => true,
  991.                         'leaf' => false,
  992.                         'allowChildren' => true,
  993.                         'iconCls' => 'pimcore_icon_folder',
  994.                         'group' => $item->getGroup(),
  995.                         'children' => [],
  996.                     ];
  997.                 }
  998.                 $groups[$item->getGroup()]['children'][] =
  999.                     [
  1000.                         'id' => $item->getName(),
  1001.                         'text' => $item->getName(),
  1002.                         'leaf' => true,
  1003.                         'iconCls' => 'pimcore_icon_thumbnails',
  1004.                         'cls' => 'pimcore_treenode_disabled',
  1005.                         'writeable' => $item->isWriteable(),
  1006.                     ];
  1007.             } else {
  1008.                 $thumbnails[] = [
  1009.                     'id' => $item->getName(),
  1010.                     'text' => $item->getName(),
  1011.                     'leaf' => true,
  1012.                     'iconCls' => 'pimcore_icon_thumbnails',
  1013.                     'cls' => 'pimcore_treenode_disabled',
  1014.                     'writeable' => $item->isWriteable(),
  1015.                 ];
  1016.             }
  1017.         }
  1018.         foreach ($groups as $group) {
  1019.             $thumbnails[] = $group;
  1020.         }
  1021.         return $this->adminJson($thumbnails);
  1022.     }
  1023.     /**
  1024.      * @Route("/thumbnail-downloadable", name="pimcore_admin_settings_thumbnaildownloadable", methods={"GET"})
  1025.      *
  1026.      * @return JsonResponse
  1027.      */
  1028.     public function thumbnailDownloadableAction()
  1029.     {
  1030.         $thumbnails = [];
  1031.         $list = new Asset\Image\Thumbnail\Config\Listing();
  1032.         $list->setFilter(function (Asset\Image\Thumbnail\Config $config) {
  1033.             return $config->isDownloadable();
  1034.         });
  1035.         foreach ($list->getThumbnails() as $item) {
  1036.             $thumbnails[] = [
  1037.                 'id' => $item->getName(),
  1038.                 'text' => $item->getName(),
  1039.             ];
  1040.         }
  1041.         return $this->adminJson($thumbnails);
  1042.     }
  1043.     /**
  1044.      * @Route("/thumbnail-add", name="pimcore_admin_settings_thumbnailadd", methods={"POST"})
  1045.      *
  1046.      * @param Request $request
  1047.      *
  1048.      * @return JsonResponse
  1049.      */
  1050.     public function thumbnailAddAction(Request $request)
  1051.     {
  1052.         $this->checkPermission('thumbnails');
  1053.         $success false;
  1054.         $pipe Asset\Image\Thumbnail\Config::getByName($request->get('name'));
  1055.         if (!$pipe) {
  1056.             $pipe = new Asset\Image\Thumbnail\Config();
  1057.             if (!$pipe->isWriteable()) {
  1058.                 throw new ConfigWriteException();
  1059.             }
  1060.             $pipe->setName($request->get('name'));
  1061.             $pipe->save();
  1062.             $success true;
  1063.         } else {
  1064.             if (!$pipe->isWriteable()) {
  1065.                 throw new ConfigWriteException();
  1066.             }
  1067.         }
  1068.         return $this->adminJson(['success' => $success'id' => $pipe->getName()]);
  1069.     }
  1070.     /**
  1071.      * @Route("/thumbnail-delete", name="pimcore_admin_settings_thumbnaildelete", methods={"DELETE"})
  1072.      *
  1073.      * @param Request $request
  1074.      *
  1075.      * @return JsonResponse
  1076.      */
  1077.     public function thumbnailDeleteAction(Request $request)
  1078.     {
  1079.         $this->checkPermission('thumbnails');
  1080.         $pipe Asset\Image\Thumbnail\Config::getByName($request->get('name'));
  1081.         if (!$pipe->isWriteable()) {
  1082.             throw new ConfigWriteException();
  1083.         }
  1084.         $pipe->delete();
  1085.         return $this->adminJson(['success' => true]);
  1086.     }
  1087.     /**
  1088.      * @Route("/thumbnail-get", name="pimcore_admin_settings_thumbnailget", methods={"GET"})
  1089.      *
  1090.      * @param Request $request
  1091.      *
  1092.      * @return JsonResponse
  1093.      */
  1094.     public function thumbnailGetAction(Request $request)
  1095.     {
  1096.         $this->checkPermission('thumbnails');
  1097.         $pipe Asset\Image\Thumbnail\Config::getByName($request->get('name'));
  1098.         $data $pipe->getObjectVars();
  1099.         $data['writeable'] = $pipe->isWriteable();
  1100.         return $this->adminJson($data);
  1101.     }
  1102.     /**
  1103.      * @Route("/thumbnail-update", name="pimcore_admin_settings_thumbnailupdate", methods={"PUT"})
  1104.      *
  1105.      * @param Request $request
  1106.      *
  1107.      * @return JsonResponse
  1108.      */
  1109.     public function thumbnailUpdateAction(Request $request)
  1110.     {
  1111.         $this->checkPermission('thumbnails');
  1112.         $pipe Asset\Image\Thumbnail\Config::getByName($request->get('name'));
  1113.         if (!$pipe->isWriteable()) {
  1114.             throw new ConfigWriteException();
  1115.         }
  1116.         $settingsData $this->decodeJson($request->get('settings'));
  1117.         $mediaData $this->decodeJson($request->get('medias'));
  1118.         $mediaOrder $this->decodeJson($request->get('mediaOrder'));
  1119.         foreach ($settingsData as $key => $value) {
  1120.             $setter 'set' ucfirst($key);
  1121.             if (method_exists($pipe$setter)) {
  1122.                 $pipe->$setter($value);
  1123.             }
  1124.         }
  1125.         $pipe->resetItems();
  1126.         uksort($mediaData, function ($a$b) use ($mediaOrder) {
  1127.             if ($a === 'default') {
  1128.                 return -1;
  1129.             }
  1130.             return ($mediaOrder[$a] < $mediaOrder[$b]) ? -1;
  1131.         });
  1132.         foreach ($mediaData as $mediaName => $items) {
  1133.             if (preg_match('/["<>]/'$mediaName)) {
  1134.                 throw new \Exception('Invalid media query name');
  1135.             }
  1136.             foreach ($items as $item) {
  1137.                 $type $item['type'];
  1138.                 unset($item['type']);
  1139.                 $pipe->addItem($type$item$mediaName);
  1140.             }
  1141.         }
  1142.         $pipe->save();
  1143.         return $this->adminJson(['success' => true]);
  1144.     }
  1145.     /**
  1146.      * @Route("/video-thumbnail-adapter-check", name="pimcore_admin_settings_videothumbnailadaptercheck", methods={"GET"})
  1147.      *
  1148.      * @param Request $request
  1149.      * @param TranslatorInterface $translator
  1150.      *
  1151.      * @return Response
  1152.      */
  1153.     public function videoThumbnailAdapterCheckAction(Request $requestTranslatorInterface $translator)
  1154.     {
  1155.         $content '';
  1156.         if (!\Pimcore\Video::isAvailable()) {
  1157.             $content '<span style="color: red; font-weight: bold;padding: 10px;margin:0 0 20px 0;border:1px solid red;display:block;">' .
  1158.                 $translator->trans('php_cli_binary_and_or_ffmpeg_binary_setting_is_missing', [], 'admin') .
  1159.                 '</span>';
  1160.         }
  1161.         return new Response($content);
  1162.     }
  1163.     /**
  1164.      * @Route("/video-thumbnail-tree", name="pimcore_admin_settings_videothumbnailtree", methods={"GET", "POST"})
  1165.      *
  1166.      * @return JsonResponse
  1167.      */
  1168.     public function videoThumbnailTreeAction()
  1169.     {
  1170.         $this->checkPermission('thumbnails');
  1171.         $thumbnails = [];
  1172.         $list = new Asset\Video\Thumbnail\Config\Listing();
  1173.         $groups = [];
  1174.         foreach ($list->getThumbnails() as $item) {
  1175.             if ($item->getGroup()) {
  1176.                 if (empty($groups[$item->getGroup()])) {
  1177.                     $groups[$item->getGroup()] = [
  1178.                         'id' => 'group_' $item->getName(),
  1179.                         'text' => htmlspecialchars($item->getGroup()),
  1180.                         'expandable' => true,
  1181.                         'leaf' => false,
  1182.                         'allowChildren' => true,
  1183.                         'iconCls' => 'pimcore_icon_folder',
  1184.                         'group' => $item->getGroup(),
  1185.                         'children' => [],
  1186.                     ];
  1187.                 }
  1188.                 $groups[$item->getGroup()]['children'][] =
  1189.                     [
  1190.                         'id' => $item->getName(),
  1191.                         'text' => $item->getName(),
  1192.                         'leaf' => true,
  1193.                         'iconCls' => 'pimcore_icon_videothumbnails',
  1194.                         'cls' => 'pimcore_treenode_disabled',
  1195.                         'writeable' => $item->isWriteable(),
  1196.                     ];
  1197.             } else {
  1198.                 $thumbnails[] = [
  1199.                     'id' => $item->getName(),
  1200.                     'text' => $item->getName(),
  1201.                     'leaf' => true,
  1202.                     'iconCls' => 'pimcore_icon_videothumbnails',
  1203.                     'cls' => 'pimcore_treenode_disabled',
  1204.                     'writeable' => $item->isWriteable(),
  1205.                 ];
  1206.             }
  1207.         }
  1208.         foreach ($groups as $group) {
  1209.             $thumbnails[] = $group;
  1210.         }
  1211.         return $this->adminJson($thumbnails);
  1212.     }
  1213.     /**
  1214.      * @Route("/video-thumbnail-add", name="pimcore_admin_settings_videothumbnailadd", methods={"POST"})
  1215.      *
  1216.      * @param Request $request
  1217.      *
  1218.      * @return JsonResponse
  1219.      */
  1220.     public function videoThumbnailAddAction(Request $request)
  1221.     {
  1222.         $this->checkPermission('thumbnails');
  1223.         $success false;
  1224.         $pipe Asset\Video\Thumbnail\Config::getByName($request->get('name'));
  1225.         if (!$pipe) {
  1226.             $pipe = new Asset\Video\Thumbnail\Config();
  1227.             if (!$pipe->isWriteable()) {
  1228.                 throw new ConfigWriteException();
  1229.             }
  1230.             $pipe->setName($request->get('name'));
  1231.             $pipe->save();
  1232.             $success true;
  1233.         } else {
  1234.             if (!$pipe->isWriteable()) {
  1235.                 throw new ConfigWriteException();
  1236.             }
  1237.         }
  1238.         return $this->adminJson(['success' => $success'id' => $pipe->getName()]);
  1239.     }
  1240.     /**
  1241.      * @Route("/video-thumbnail-delete", name="pimcore_admin_settings_videothumbnaildelete", methods={"DELETE"})
  1242.      *
  1243.      * @param Request $request
  1244.      *
  1245.      * @return JsonResponse
  1246.      */
  1247.     public function videoThumbnailDeleteAction(Request $request)
  1248.     {
  1249.         $this->checkPermission('thumbnails');
  1250.         $pipe Asset\Video\Thumbnail\Config::getByName($request->get('name'));
  1251.         if (!$pipe->isWriteable()) {
  1252.             throw new ConfigWriteException();
  1253.         }
  1254.         $pipe->delete();
  1255.         return $this->adminJson(['success' => true]);
  1256.     }
  1257.     /**
  1258.      * @Route("/video-thumbnail-get", name="pimcore_admin_settings_videothumbnailget", methods={"GET"})
  1259.      *
  1260.      * @param Request $request
  1261.      *
  1262.      * @return JsonResponse
  1263.      */
  1264.     public function videoThumbnailGetAction(Request $request)
  1265.     {
  1266.         $this->checkPermission('thumbnails');
  1267.         $pipe Asset\Video\Thumbnail\Config::getByName($request->get('name'));
  1268.         $data $pipe->getObjectVars();
  1269.         $data['writeable'] = $pipe->isWriteable();
  1270.         return $this->adminJson($data);
  1271.     }
  1272.     /**
  1273.      * @Route("/video-thumbnail-update", name="pimcore_admin_settings_videothumbnailupdate", methods={"PUT"})
  1274.      *
  1275.      * @param Request $request
  1276.      *
  1277.      * @return JsonResponse
  1278.      */
  1279.     public function videoThumbnailUpdateAction(Request $request)
  1280.     {
  1281.         $this->checkPermission('thumbnails');
  1282.         $pipe Asset\Video\Thumbnail\Config::getByName($request->get('name'));
  1283.         if (!$pipe->isWriteable()) {
  1284.             throw new ConfigWriteException();
  1285.         }
  1286.         $settingsData $this->decodeJson($request->get('settings'));
  1287.         $mediaData $this->decodeJson($request->get('medias'));
  1288.         $mediaOrder $this->decodeJson($request->get('mediaOrder'));
  1289.         foreach ($settingsData as $key => $value) {
  1290.             $setter 'set' ucfirst($key);
  1291.             if (method_exists($pipe$setter)) {
  1292.                 $pipe->$setter($value);
  1293.             }
  1294.         }
  1295.         $pipe->resetItems();
  1296.         uksort($mediaData, function ($a$b) use ($mediaOrder) {
  1297.             if ($a === 'default') {
  1298.                 return -1;
  1299.             }
  1300.             return ($mediaOrder[$a] < $mediaOrder[$b]) ? -1;
  1301.         });
  1302.         foreach ($mediaData as $mediaName => $items) {
  1303.             foreach ($items as $item) {
  1304.                 $type $item['type'];
  1305.                 unset($item['type']);
  1306.                 $pipe->addItem($type$itemhtmlspecialchars($mediaName));
  1307.             }
  1308.         }
  1309.         $pipe->save();
  1310.         return $this->adminJson(['success' => true]);
  1311.     }
  1312.     /**
  1313.      * @Route("/robots-txt", name="pimcore_admin_settings_robotstxtget", methods={"GET"})
  1314.      *
  1315.      * @return JsonResponse
  1316.      */
  1317.     public function robotsTxtGetAction()
  1318.     {
  1319.         $this->checkPermission('robots.txt');
  1320.         $config Config::getRobotsConfig();
  1321.         $config $config->toArray();
  1322.         return $this->adminJson([
  1323.             'success' => true,
  1324.             'data' => $config,
  1325.             'onFileSystem' => file_exists(PIMCORE_WEB_ROOT '/robots.txt'),
  1326.         ]);
  1327.     }
  1328.     /**
  1329.      * @Route("/robots-txt", name="pimcore_admin_settings_robotstxtput", methods={"PUT"})
  1330.      *
  1331.      * @param Request $request
  1332.      *
  1333.      * @return JsonResponse
  1334.      */
  1335.     public function robotsTxtPutAction(Request $request)
  1336.     {
  1337.         $this->checkPermission('robots.txt');
  1338.         $values $request->get('data');
  1339.         if (!is_array($values)) {
  1340.             $values = [];
  1341.         }
  1342.         foreach ($values as $siteId => $robotsContent) {
  1343.             SettingsStore::set('robots.txt-' $siteId$robotsContent'string''robots.txt');
  1344.         }
  1345.         return $this->adminJson([
  1346.             'success' => true,
  1347.         ]);
  1348.     }
  1349.     /**
  1350.      * @Route("/website-settings", name="pimcore_admin_settings_websitesettings", methods={"POST"})
  1351.      *
  1352.      * @param Request $request
  1353.      *
  1354.      * @return JsonResponse
  1355.      *
  1356.      * @throws \Exception
  1357.      */
  1358.     public function websiteSettingsAction(Request $request)
  1359.     {
  1360.         $this->checkPermission('website_settings');
  1361.         if ($request->get('data')) {
  1362.             $data $this->decodeJson($request->get('data'));
  1363.             if (is_array($data)) {
  1364.                 foreach ($data as &$value) {
  1365.                     $value trim($value);
  1366.                 }
  1367.             }
  1368.             if ($request->get('xaction') == 'destroy') {
  1369.                 $id $data['id'];
  1370.                 $setting WebsiteSetting::getById($id);
  1371.                 if ($setting instanceof WebsiteSetting) {
  1372.                     $setting->delete();
  1373.                     return $this->adminJson(['success' => true'data' => []]);
  1374.                 }
  1375.             } elseif ($request->get('xaction') == 'update') {
  1376.                 // save routes
  1377.                 $setting WebsiteSetting::getById($data['id']);
  1378.                 if ($setting instanceof WebsiteSetting) {
  1379.                     switch ($setting->getType()) {
  1380.                         case 'document':
  1381.                         case 'asset':
  1382.                         case 'object':
  1383.                             if (isset($data['data'])) {
  1384.                                 $element Element\Service::getElementByPath($setting->getType(), $data['data']);
  1385.                                 $data['data'] = $element;
  1386.                             }
  1387.                             break;
  1388.                     }
  1389.                     $setting->setValues($data);
  1390.                     $setting->save();
  1391.                     $data $this->getWebsiteSettingForEditMode($setting);
  1392.                     return $this->adminJson(['data' => $data'success' => true]);
  1393.                 }
  1394.             } elseif ($request->get('xaction') == 'create') {
  1395.                 unset($data['id']);
  1396.                 // save route
  1397.                 $setting = new WebsiteSetting();
  1398.                 $setting->setValues($data);
  1399.                 $setting->save();
  1400.                 return $this->adminJson(['data' => $setting->getObjectVars(), 'success' => true]);
  1401.             }
  1402.         } else {
  1403.             $list = new WebsiteSetting\Listing();
  1404.             $list->setLimit($request->get('limit'));
  1405.             $list->setOffset($request->get('start'));
  1406.             $sortingSettings \Pimcore\Bundle\AdminBundle\Helper\QueryParams::extractSortingSettings(array_merge($request->request->all(), $request->query->all()));
  1407.             if ($sortingSettings['orderKey']) {
  1408.                 $list->setOrderKey($sortingSettings['orderKey']);
  1409.                 $list->setOrder($sortingSettings['order']);
  1410.             } else {
  1411.                 $list->setOrderKey('name');
  1412.                 $list->setOrder('asc');
  1413.             }
  1414.             if ($request->get('filter')) {
  1415.                 $list->setCondition('`name` LIKE ' $list->quote('%'.$request->get('filter').'%'));
  1416.             }
  1417.             $totalCount $list->getTotalCount();
  1418.             $list $list->load();
  1419.             $settings = [];
  1420.             foreach ($list as $item) {
  1421.                 $resultItem $this->getWebsiteSettingForEditMode($item);
  1422.                 $settings[] = $resultItem;
  1423.             }
  1424.             return $this->adminJson(['data' => $settings'success' => true'total' => $totalCount]);
  1425.         }
  1426.         return $this->adminJson(['success' => false]);
  1427.     }
  1428.     /**
  1429.      * @param WebsiteSetting $item
  1430.      *
  1431.      * @return array
  1432.      */
  1433.     private function getWebsiteSettingForEditMode($item)
  1434.     {
  1435.         $resultItem = [
  1436.             'id' => $item->getId(),
  1437.             'name' => $item->getName(),
  1438.             'language' => $item->getLanguage(),
  1439.             'type' => $item->getType(),
  1440.             'data' => null,
  1441.             'siteId' => $item->getSiteId(),
  1442.             'creationDate' => $item->getCreationDate(),
  1443.             'modificationDate' => $item->getModificationDate(),
  1444.         ];
  1445.         switch ($item->getType()) {
  1446.             case 'document':
  1447.             case 'asset':
  1448.             case 'object':
  1449.                 $element $item->getData();
  1450.                 if ($element) {
  1451.                     $resultItem['data'] = $element->getRealFullPath();
  1452.                 }
  1453.                 break;
  1454.             default:
  1455.                 $resultItem['data'] = $item->getData();
  1456.                 break;
  1457.         }
  1458.         return $resultItem;
  1459.     }
  1460.     /**
  1461.      * @Route("/get-available-algorithms", name="pimcore_admin_settings_getavailablealgorithms", methods={"GET"})
  1462.      *
  1463.      * @param Request $request
  1464.      *
  1465.      * @return JsonResponse
  1466.      */
  1467.     public function getAvailableAlgorithmsAction(Request $request)
  1468.     {
  1469.         $options = [
  1470.             [
  1471.                 'key' => 'password_hash',
  1472.                 'value' => 'password_hash',
  1473.             ],
  1474.         ];
  1475.         $algorithms hash_algos();
  1476.         foreach ($algorithms as $algorithm) {
  1477.             $options[] = [
  1478.                 'key' => $algorithm,
  1479.                 'value' => $algorithm,
  1480.             ];
  1481.         }
  1482.         $result = ['data' => $options'success' => true'total' => count($options)];
  1483.         return $this->adminJson($result);
  1484.     }
  1485.     /**
  1486.      * deleteViews
  1487.      * delete views for localized fields when languages are removed to
  1488.      * prevent mysql errors
  1489.      *
  1490.      * @param string $language
  1491.      * @param string $dbName
  1492.      */
  1493.     protected function deleteViews($language$dbName)
  1494.     {
  1495.         $db \Pimcore\Db::get();
  1496.         $views $db->fetchAllAssociative('SHOW FULL TABLES IN ' $db->quoteIdentifier($dbName) . " WHERE TABLE_TYPE LIKE 'VIEW'");
  1497.         foreach ($views as $view) {
  1498.             if (preg_match('/^object_localized_[0-9]+_' $language '$/'$view['Tables_in_' $dbName])) {
  1499.                 $sql 'DROP VIEW ' $db->quoteIdentifier($view['Tables_in_' $dbName]);
  1500.                 $db->executeQuery($sql);
  1501.             }
  1502.         }
  1503.     }
  1504.     /**
  1505.      * @Route("/test-web2print", name="pimcore_admin_settings_testweb2print", methods={"GET"})
  1506.      *
  1507.      * @param Request $request
  1508.      *
  1509.      * @return Response
  1510.      */
  1511.     public function testWeb2printAction(Request $request)
  1512.     {
  1513.         $this->checkPermission('web2print_settings');
  1514.         $response $this->render('@PimcoreAdmin/Admin/Settings/testWeb2print.html.twig');
  1515.         $html $response->getContent();
  1516.         $adapter \Pimcore\Web2Print\Processor::getInstance();
  1517.         $params = [];
  1518.         if ($adapter instanceof \Pimcore\Web2Print\Processor\WkHtmlToPdf) {
  1519.             $params['adapterConfig'] = '-O landscape';
  1520.         } elseif ($adapter instanceof \Pimcore\Web2Print\Processor\PdfReactor) {
  1521.             $params['adapterConfig'] = [
  1522.                 'javaScriptMode' => 0,
  1523.                 'addLinks' => true,
  1524.                 'appendLog' => true,
  1525.                 'enableDebugMode' => true,
  1526.             ];
  1527.         } elseif ($adapter instanceof \Pimcore\Web2Print\Processor\HeadlessChrome) {
  1528.             $params Config::getWeb2PrintConfig();
  1529.             $params $params->get('headlessChromeSettings');
  1530.             $params json_decode($paramstrue);
  1531.         }
  1532.         $responseOptions = [
  1533.             'Content-Type' => 'application/pdf',
  1534.         ];
  1535.         $pdfData $adapter->getPdfFromString($html$params);
  1536.         return new \Symfony\Component\HttpFoundation\Response(
  1537.             $pdfData,
  1538.             200,
  1539.             $responseOptions
  1540.         );
  1541.     }
  1542. }