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

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