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

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