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

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