vendor/pimcore/pimcore/bundles/AdminBundle/Controller/Admin/Asset/AssetController.php line 1278

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\Asset;
  15. use Pimcore\Bundle\AdminBundle\Controller\Admin\ElementControllerBase;
  16. use Pimcore\Bundle\AdminBundle\Controller\Traits\AdminStyleTrait;
  17. use Pimcore\Bundle\AdminBundle\Controller\Traits\ApplySchedulerDataTrait;
  18. use Pimcore\Bundle\AdminBundle\Helper\GridHelperService;
  19. use Pimcore\Bundle\AdminBundle\Security\CsrfProtectionHandler;
  20. use Pimcore\Config;
  21. use Pimcore\Controller\KernelControllerEventInterface;
  22. use Pimcore\Controller\Traits\ElementEditLockHelperTrait;
  23. use Pimcore\Event\Admin\ElementAdminStyleEvent;
  24. use Pimcore\Event\AdminEvents;
  25. use Pimcore\Event\AssetEvents;
  26. use Pimcore\File;
  27. use Pimcore\Loader\ImplementationLoader\Exception\UnsupportedException;
  28. use Pimcore\Logger;
  29. use Pimcore\Model;
  30. use Pimcore\Model\Asset;
  31. use Pimcore\Model\Element;
  32. use Pimcore\Model\Metadata;
  33. use Pimcore\Model\Schedule\Task;
  34. use Pimcore\Tool;
  35. use Symfony\Component\EventDispatcher\GenericEvent;
  36. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  37. use Symfony\Component\HttpFoundation\JsonResponse;
  38. use Symfony\Component\HttpFoundation\Request;
  39. use Symfony\Component\HttpFoundation\Response;
  40. use Symfony\Component\HttpFoundation\ResponseHeaderBag;
  41. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
  42. use Symfony\Component\HttpFoundation\StreamedResponse;
  43. use Symfony\Component\HttpKernel\Event\ControllerEvent;
  44. use Symfony\Component\Mime\MimeTypes;
  45. use Symfony\Component\Process\Process;
  46. use Symfony\Component\Routing\Annotation\Route;
  47. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  48. /**
  49. * @Route("/asset")
  50. *
  51. * @internal
  52. */
  53. class AssetController extends ElementControllerBase implements KernelControllerEventInterface
  54. {
  55. use AdminStyleTrait;
  56. use ElementEditLockHelperTrait;
  57. use ApplySchedulerDataTrait;
  58. /**
  59. * @var Asset\Service
  60. */
  61. protected $_assetService;
  62. /**
  63. * @Route("/tree-get-root", name="pimcore_admin_asset_treegetroot", methods={"GET"})
  64. *
  65. * @param Request $request
  66. *
  67. * @return JsonResponse
  68. */
  69. public function treeGetRootAction(Request $request)
  70. {
  71. return parent::treeGetRootAction($request);
  72. }
  73. /**
  74. * @Route("/delete-info", name="pimcore_admin_asset_deleteinfo", methods={"GET"})
  75. *
  76. * @param Request $request
  77. * @param EventDispatcherInterface $eventDispatcher
  78. *
  79. * @return JsonResponse
  80. */
  81. public function deleteInfoAction(Request $request, EventDispatcherInterface $eventDispatcher)
  82. {
  83. return parent::deleteInfoAction($request, $eventDispatcher);
  84. }
  85. /**
  86. * @Route("/get-data-by-id", name="pimcore_admin_asset_getdatabyid", methods={"GET"})
  87. *
  88. * @param Request $request
  89. *
  90. * @return JsonResponse
  91. */
  92. public function getDataByIdAction(Request $request, EventDispatcherInterface $eventDispatcher)
  93. {
  94. $asset = Asset::getById((int)$request->get('id'));
  95. if (!$asset instanceof Asset) {
  96. return $this->adminJson(['success' => false, 'message' => "asset doesn't exist"]);
  97. }
  98. // check for lock
  99. if ($asset->isAllowed('publish') || $asset->isAllowed('delete')) {
  100. if (Element\Editlock::isLocked($request->get('id'), 'asset')) {
  101. return $this->getEditLockResponse($request->get('id'), 'asset');
  102. }
  103. Element\Editlock::lock($request->get('id'), 'asset');
  104. }
  105. $asset = clone $asset;
  106. $asset->setLocked($asset->isLocked());
  107. $asset->setParent(null);
  108. $asset->setStream(null);
  109. $data = $asset->getObjectVars();
  110. if ($asset instanceof Asset\Text) {
  111. if ($asset->getFileSize() < 2000000) {
  112. // it doesn't make sense to show a preview for files bigger than 2MB
  113. $data['data'] = \ForceUTF8\Encoding::toUTF8($asset->getData());
  114. } else {
  115. $data['data'] = false;
  116. }
  117. } elseif ($asset instanceof Asset\Document) {
  118. $data['pdfPreviewAvailable'] = (bool)$this->getDocumentPreviewPdf($asset);
  119. } elseif ($asset instanceof Asset\Video) {
  120. $videoInfo = [];
  121. if (\Pimcore\Video::isAvailable()) {
  122. $config = Asset\Video\Thumbnail\Config::getPreviewConfig();
  123. $thumbnail = $asset->getThumbnail($config, ['mp4']);
  124. if ($thumbnail) {
  125. if ($thumbnail['status'] == 'finished') {
  126. $videoInfo['previewUrl'] = $thumbnail['formats']['mp4'];
  127. $videoInfo['width'] = $asset->getWidth();
  128. $videoInfo['height'] = $asset->getHeight();
  129. $metaData = $asset->getSphericalMetaData();
  130. if (isset($metaData['ProjectionType']) && strtolower($metaData['ProjectionType']) == 'equirectangular') {
  131. $videoInfo['isVrVideo'] = true;
  132. }
  133. }
  134. }
  135. }
  136. $data['videoInfo'] = $videoInfo;
  137. } elseif ($asset instanceof Asset\Image) {
  138. $imageInfo = [];
  139. $previewUrl = $this->generateUrl('pimcore_admin_asset_getimagethumbnail', [
  140. 'id' => $asset->getId(),
  141. 'treepreview' => true,
  142. 'hdpi' => true,
  143. '_dc' => time(),
  144. ]);
  145. if ($asset->isAnimated()) {
  146. $previewUrl = $this->generateUrl('pimcore_admin_asset_getasset', [
  147. 'id' => $asset->getId(),
  148. '_dc' => time(),
  149. ]);
  150. }
  151. $imageInfo['previewUrl'] = $previewUrl;
  152. if ($asset->getWidth() && $asset->getHeight()) {
  153. $imageInfo['dimensions'] = [];
  154. $imageInfo['dimensions']['width'] = $asset->getWidth();
  155. $imageInfo['dimensions']['height'] = $asset->getHeight();
  156. }
  157. $imageInfo['exiftoolAvailable'] = (bool)\Pimcore\Tool\Console::getExecutable('exiftool');
  158. if (!$asset->getEmbeddedMetaData(false)) {
  159. $asset->getEmbeddedMetaData(true, false); // read Exif, IPTC and XPM like in the old days ...
  160. }
  161. $data['imageInfo'] = $imageInfo;
  162. }
  163. $predefinedMetaData = Metadata\Predefined\Listing::getByTargetType('asset', [$asset->getType()]);
  164. $predefinedMetaDataGroups = [];
  165. /** @var Metadata\Predefined $item */
  166. foreach ($predefinedMetaData as $item) {
  167. if ($item->getGroup()) {
  168. $predefinedMetaDataGroups[$item->getGroup()] = true;
  169. }
  170. }
  171. $data['predefinedMetaDataGroups'] = array_keys($predefinedMetaDataGroups);
  172. $data['properties'] = Element\Service::minimizePropertiesForEditmode($asset->getProperties());
  173. $data['metadata'] = Asset\Service::expandMetadataForEditmode($asset->getMetadata());
  174. $data['versionDate'] = $asset->getModificationDate();
  175. $data['filesizeFormatted'] = $asset->getFileSize(true);
  176. $data['filesize'] = $asset->getFileSize();
  177. $data['fileExtension'] = File::getFileExtension($asset->getFilename());
  178. $data['idPath'] = Element\Service::getIdPath($asset);
  179. $data['userPermissions'] = $asset->getUserPermissions();
  180. $frontendPath = $asset->getFrontendFullPath();
  181. $data['url'] = preg_match('/^http(s)?:\\/\\/.+/', $frontendPath) ?
  182. $frontendPath :
  183. $request->getSchemeAndHttpHost() . $frontendPath;
  184. $data['scheduledTasks'] = array_map(
  185. static function (Task $task) {
  186. return $task->getObjectVars();
  187. },
  188. $asset->getScheduledTasks()
  189. );
  190. $this->addAdminStyle($asset, ElementAdminStyleEvent::CONTEXT_EDITOR, $data);
  191. $data['php'] = [
  192. 'classes' => array_merge([get_class($asset)], array_values(class_parents($asset))),
  193. 'interfaces' => array_values(class_implements($asset)),
  194. ];
  195. $event = new GenericEvent($this, [
  196. 'data' => $data,
  197. 'asset' => $asset,
  198. ]);
  199. $eventDispatcher->dispatch($event, AdminEvents::ASSET_GET_PRE_SEND_DATA);
  200. $data = $event->getArgument('data');
  201. if ($asset->isAllowed('view')) {
  202. return $this->adminJson($data);
  203. }
  204. throw $this->createAccessDeniedHttpException();
  205. }
  206. /**
  207. * @Route("/tree-get-childs-by-id", name="pimcore_admin_asset_treegetchildsbyid", methods={"GET"})
  208. *
  209. * @param Request $request
  210. *
  211. * @return JsonResponse
  212. */
  213. public function treeGetChildsByIdAction(Request $request, EventDispatcherInterface $eventDispatcher)
  214. {
  215. $allParams = array_merge($request->request->all(), $request->query->all());
  216. $assets = [];
  217. $cv = false;
  218. $asset = Asset::getById($allParams['node']);
  219. $filter = $request->get('filter');
  220. $limit = (int)$allParams['limit'];
  221. if (!is_null($filter)) {
  222. if (substr($filter, -1) != '*') {
  223. $filter .= '*';
  224. }
  225. $filter = str_replace('*', '%', $filter);
  226. $limit = 100;
  227. $offset = 0;
  228. } elseif (!$allParams['limit']) {
  229. $limit = 100000000;
  230. }
  231. $offset = isset($allParams['start']) ? (int)$allParams['start'] : 0;
  232. $filteredTotalCount = 0;
  233. if ($asset->hasChildren()) {
  234. if ($allParams['view']) {
  235. $cv = \Pimcore\Model\Element\Service::getCustomViewById($allParams['view']);
  236. }
  237. // get assets
  238. $childsList = new Asset\Listing();
  239. $childsList->addConditionParam('parentId = ?', [$asset->getId()]);
  240. $childsList->filterAccessibleByUser($this->getAdminUser());
  241. if (!is_null($filter)) {
  242. $childsList->addConditionParam('CAST(assets.filename AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci LIKE ?', [$filter]);
  243. }
  244. $childsList->setLimit($limit);
  245. $childsList->setOffset($offset);
  246. $childsList->setOrderKey("FIELD(assets.type, 'folder') DESC, CAST(assets.filename AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci ASC", false);
  247. \Pimcore\Model\Element\Service::addTreeFilterJoins($cv, $childsList);
  248. $beforeListLoadEvent = new GenericEvent($this, [
  249. 'list' => $childsList,
  250. 'context' => $allParams,
  251. ]);
  252. $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD);
  253. /** @var Asset\Listing $childsList */
  254. $childsList = $beforeListLoadEvent->getArgument('list');
  255. $childs = $childsList->load();
  256. $filteredTotalCount = $childsList->getTotalCount();
  257. foreach ($childs as $childAsset) {
  258. if ($childAsset->isAllowed('list')) {
  259. $assets[] = $this->getTreeNodeConfig($childAsset);
  260. }
  261. }
  262. }
  263. //Hook for modifying return value - e.g. for changing permissions based on asset data
  264. $event = new GenericEvent($this, [
  265. 'assets' => $assets,
  266. ]);
  267. $eventDispatcher->dispatch($event, AdminEvents::ASSET_TREE_GET_CHILDREN_BY_ID_PRE_SEND_DATA);
  268. $assets = $event->getArgument('assets');
  269. if ($allParams['limit']) {
  270. return $this->adminJson([
  271. 'offset' => $offset,
  272. 'limit' => $limit,
  273. 'total' => $asset->getChildAmount($this->getAdminUser()),
  274. 'overflow' => !is_null($filter) && ($filteredTotalCount > $limit),
  275. 'nodes' => $assets,
  276. 'filter' => $request->get('filter') ? $request->get('filter') : '',
  277. 'inSearch' => (int)$request->get('inSearch'),
  278. ]);
  279. } else {
  280. return $this->adminJson($assets);
  281. }
  282. }
  283. /**
  284. * @Route("/add-asset", name="pimcore_admin_asset_addasset", methods={"POST"})
  285. *
  286. * @param Request $request
  287. * @param Config $config
  288. *
  289. * @return JsonResponse
  290. */
  291. public function addAssetAction(Request $request, Config $config)
  292. {
  293. try {
  294. $res = $this->addAsset($request, $config);
  295. $response = [
  296. 'success' => $res['success'],
  297. ];
  298. if ($res['success']) {
  299. $response['asset'] = [
  300. 'id' => $res['asset']->getId(),
  301. 'path' => $res['asset']->getFullPath(),
  302. 'type' => $res['asset']->getType(),
  303. ];
  304. }
  305. return $this->adminJson($response);
  306. } catch (\Exception $e) {
  307. return $this->adminJson([
  308. 'success' => false,
  309. 'message' => $e->getMessage(),
  310. ]);
  311. }
  312. }
  313. /**
  314. * @Route("/add-asset-compatibility", name="pimcore_admin_asset_addassetcompatibility", methods={"POST"})
  315. *
  316. * @param Request $request
  317. * @param Config $config
  318. *
  319. * @return JsonResponse
  320. */
  321. public function addAssetCompatibilityAction(Request $request, Config $config)
  322. {
  323. try {
  324. // this is a special action for the compatibility mode upload (without flash)
  325. $res = $this->addAsset($request, $config);
  326. $response = $this->adminJson([
  327. 'success' => $res['success'],
  328. 'msg' => $res['success'] ? 'Success' : 'Error',
  329. 'id' => $res['asset'] ? $res['asset']->getId() : null,
  330. 'fullpath' => $res['asset'] ? $res['asset']->getRealFullPath() : null,
  331. 'type' => $res['asset'] ? $res['asset']->getType() : null,
  332. ]);
  333. $response->headers->set('Content-Type', 'text/html');
  334. return $response;
  335. } catch (\Exception $e) {
  336. return $this->adminJson([
  337. 'success' => false,
  338. 'message' => $e->getMessage(),
  339. ]);
  340. }
  341. }
  342. /**
  343. * @param Request $request
  344. * @param Config $config
  345. *
  346. * @return array
  347. *
  348. * @throws \Exception
  349. */
  350. protected function addAsset(Request $request, Config $config)
  351. {
  352. $defaultUploadPath = $config['assets']['default_upload_path'] ?? '/';
  353. if (array_key_exists('Filedata', $_FILES)) {
  354. $filename = $_FILES['Filedata']['name'];
  355. $sourcePath = $_FILES['Filedata']['tmp_name'];
  356. } elseif ($request->get('type') == 'base64') {
  357. $filename = $request->get('filename');
  358. $sourcePath = PIMCORE_SYSTEM_TEMP_DIRECTORY . '/upload-base64' . uniqid() . '.tmp';
  359. $data = preg_replace('@^data:[^,]+;base64,@', '', $request->get('data'));
  360. File::put($sourcePath, base64_decode($data));
  361. } else {
  362. throw new \Exception('The filename of the asset is empty');
  363. }
  364. $parentId = $request->get('parentId');
  365. $parentPath = $request->get('parentPath');
  366. if ($request->get('dir') && $request->get('parentId')) {
  367. // this is for uploading folders with Drag&Drop
  368. // param "dir" contains the relative path of the file
  369. $parent = Asset::getById($request->get('parentId'));
  370. $dir = $request->get('dir');
  371. if (strpos($dir, '..') !== false) {
  372. throw new \Exception('not allowed');
  373. }
  374. $newPath = $parent->getRealFullPath() . '/' . trim($dir, '/ ');
  375. $maxRetries = 5;
  376. $newParent = null;
  377. for ($retries = 0; $retries < $maxRetries; $retries++) {
  378. try {
  379. $newParent = Asset\Service::createFolderByPath($newPath);
  380. break;
  381. } catch (\Exception $e) {
  382. if ($retries < ($maxRetries - 1)) {
  383. $waitTime = rand(100000, 900000); // microseconds
  384. usleep($waitTime); // wait specified time until we restart the transaction
  385. } else {
  386. // if the transaction still fail after $maxRetries retries, we throw out the exception
  387. throw $e;
  388. }
  389. }
  390. }
  391. if ($newParent) {
  392. $parentId = $newParent->getId();
  393. }
  394. } elseif (!$request->get('parentId') && $parentPath) {
  395. $parent = Asset::getByPath($parentPath);
  396. if ($parent instanceof Asset\Folder) {
  397. $parentId = $parent->getId();
  398. }
  399. }
  400. $filename = Element\Service::getValidKey($filename, 'asset');
  401. if (empty($filename)) {
  402. throw new \Exception('The filename of the asset is empty');
  403. }
  404. $context = $request->get('context');
  405. if ($context) {
  406. $context = json_decode($context, true);
  407. $context = $context ? $context : [];
  408. $event = new \Pimcore\Event\Model\Asset\ResolveUploadTargetEvent($parentId, $filename, $context);
  409. \Pimcore::getEventDispatcher()->dispatch($event, AssetEvents::RESOLVE_UPLOAD_TARGET);
  410. $filename = Element\Service::getValidKey($event->getFilename(), 'asset');
  411. $parentId = $event->getParentId();
  412. }
  413. if (!$parentId) {
  414. $parentId = Asset\Service::createFolderByPath($defaultUploadPath)->getId();
  415. }
  416. $parentAsset = Asset::getById((int)$parentId);
  417. // check for duplicate filename
  418. $filename = $this->getSafeFilename($parentAsset->getRealFullPath(), $filename);
  419. $asset = null;
  420. if (!$parentAsset->isAllowed('create')) {
  421. throw $this->createAccessDeniedHttpException(
  422. 'Missing the permission to create new assets in the folder: ' . $parentAsset->getRealFullPath()
  423. );
  424. }
  425. if (is_file($sourcePath) && filesize($sourcePath) < 1) {
  426. throw new \Exception('File is empty!');
  427. } elseif (!is_file($sourcePath)) {
  428. throw new \Exception('Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions of your temporary directories.');
  429. }
  430. $asset = Asset::create($parentId, [
  431. 'filename' => $filename,
  432. 'sourcePath' => $sourcePath,
  433. 'userOwner' => $this->getAdminUser()->getId(),
  434. 'userModification' => $this->getAdminUser()->getId(),
  435. ]);
  436. @unlink($sourcePath);
  437. return [
  438. 'success' => true,
  439. 'asset' => $asset,
  440. ];
  441. }
  442. /**
  443. * @param string $targetPath
  444. * @param string $filename
  445. *
  446. * @return string
  447. */
  448. protected function getSafeFilename($targetPath, $filename)
  449. {
  450. $pathinfo = pathinfo($filename);
  451. $originalFilename = $pathinfo['filename'];
  452. $originalFileextension = empty($pathinfo['extension']) ? '' : '.' . $pathinfo['extension'];
  453. $count = 1;
  454. if ($targetPath == '/') {
  455. $targetPath = '';
  456. }
  457. while (true) {
  458. if (Asset\Service::pathExists($targetPath . '/' . $filename)) {
  459. $filename = $originalFilename . '_' . $count . $originalFileextension;
  460. $count++;
  461. } else {
  462. return $filename;
  463. }
  464. }
  465. }
  466. /**
  467. * @Route("/replace-asset", name="pimcore_admin_asset_replaceasset", methods={"POST", "PUT"})
  468. *
  469. * @param Request $request
  470. *
  471. * @return JsonResponse
  472. *
  473. * @throws \Exception
  474. */
  475. public function replaceAssetAction(Request $request)
  476. {
  477. $asset = Asset::getById($request->get('id'));
  478. $newFilename = Element\Service::getValidKey($_FILES['Filedata']['name'], 'asset');
  479. $mimetype = MimeTypes::getDefault()->guessMimeType($_FILES['Filedata']['tmp_name']);
  480. $newType = Asset::getTypeFromMimeMapping($mimetype, $newFilename);
  481. if ($newType != $asset->getType()) {
  482. return $this->adminJson([
  483. 'success' => false,
  484. 'message' => sprintf($this->trans('asset_type_change_not_allowed', [], 'admin'), $asset->getType(), $newType),
  485. ]);
  486. }
  487. $stream = fopen($_FILES['Filedata']['tmp_name'], 'r+');
  488. $asset->setStream($stream);
  489. $asset->setCustomSetting('thumbnails', null);
  490. $asset->setUserModification($this->getAdminUser()->getId());
  491. $newFileExt = File::getFileExtension($newFilename);
  492. $currentFileExt = File::getFileExtension($asset->getFilename());
  493. if ($newFileExt != $currentFileExt) {
  494. $newFilename = preg_replace('/\.' . $currentFileExt . '$/i', '.' . $newFileExt, $asset->getFilename());
  495. $newFilename = Element\Service::getSafeCopyName($newFilename, $asset->getParent());
  496. $asset->setFilename($newFilename);
  497. }
  498. if ($asset->isAllowed('publish')) {
  499. $asset->save();
  500. $response = $this->adminJson([
  501. 'id' => $asset->getId(),
  502. 'path' => $asset->getRealFullPath(),
  503. 'success' => true,
  504. ]);
  505. // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in
  506. // Ext.form.Action.Submit and mark the submission as failed
  507. $response->headers->set('Content-Type', 'text/html');
  508. return $response;
  509. } else {
  510. throw new \Exception('missing permission');
  511. }
  512. }
  513. /**
  514. * @Route("/add-folder", name="pimcore_admin_asset_addfolder", methods={"POST"})
  515. *
  516. * @param Request $request
  517. *
  518. * @return JsonResponse
  519. */
  520. public function addFolderAction(Request $request)
  521. {
  522. $success = false;
  523. $parentAsset = Asset::getById((int)$request->get('parentId'));
  524. $equalAsset = Asset::getByPath($parentAsset->getRealFullPath() . '/' . $request->get('name'));
  525. if ($parentAsset->isAllowed('create')) {
  526. if (!$equalAsset) {
  527. $asset = Asset::create($request->get('parentId'), [
  528. 'filename' => $request->get('name'),
  529. 'type' => 'folder',
  530. 'userOwner' => $this->getAdminUser()->getId(),
  531. 'userModification' => $this->getAdminUser()->getId(),
  532. ]);
  533. $success = true;
  534. }
  535. } else {
  536. Logger::debug('prevented creating asset because of missing permissions');
  537. }
  538. return $this->adminJson(['success' => $success]);
  539. }
  540. /**
  541. * @Route("/delete", name="pimcore_admin_asset_delete", methods={"DELETE"})
  542. *
  543. * @param Request $request
  544. *
  545. * @return JsonResponse
  546. */
  547. public function deleteAction(Request $request)
  548. {
  549. if ($request->get('type') == 'childs') {
  550. $parentAsset = Asset::getById($request->get('id'));
  551. $list = new Asset\Listing();
  552. $list->setCondition('path LIKE ?', [$list->escapeLike($parentAsset->getRealFullPath()) . '/%']);
  553. $list->setLimit((int)$request->get('amount'));
  554. $list->setOrderKey('LENGTH(path)', false);
  555. $list->setOrder('DESC');
  556. $deletedItems = [];
  557. foreach ($list as $asset) {
  558. $deletedItems[$asset->getId()] = $asset->getRealFullPath();
  559. if ($asset->isAllowed('delete') && !$asset->isLocked()) {
  560. $asset->delete();
  561. }
  562. }
  563. return $this->adminJson(['success' => true, 'deleted' => $deletedItems]);
  564. } elseif ($request->get('id')) {
  565. $asset = Asset::getById($request->get('id'));
  566. if ($asset && $asset->isAllowed('delete')) {
  567. if ($asset->isLocked()) {
  568. return $this->adminJson([
  569. 'success' => false,
  570. 'message' => 'prevented deleting asset, because it is locked: ID: ' . $asset->getId(),
  571. ]);
  572. } else {
  573. $asset->delete();
  574. return $this->adminJson(['success' => true]);
  575. }
  576. }
  577. }
  578. throw $this->createAccessDeniedHttpException();
  579. }
  580. /**
  581. * @param Asset $element
  582. *
  583. * @return array
  584. */
  585. protected function getTreeNodeConfig($element)
  586. {
  587. $asset = $element;
  588. $tmpAsset = [
  589. 'id' => $asset->getId(),
  590. 'text' => htmlspecialchars($asset->getFilename()),
  591. 'type' => $asset->getType(),
  592. 'path' => $asset->getRealFullPath(),
  593. 'basePath' => $asset->getRealPath(),
  594. 'locked' => $asset->isLocked(),
  595. 'lockOwner' => $asset->getLocked() ? true : false,
  596. 'elementType' => 'asset',
  597. 'permissions' => [
  598. 'remove' => $asset->isAllowed('delete'),
  599. 'settings' => $asset->isAllowed('settings'),
  600. 'rename' => $asset->isAllowed('rename'),
  601. 'publish' => $asset->isAllowed('publish'),
  602. 'view' => $asset->isAllowed('view'),
  603. ],
  604. ];
  605. // set type specific settings
  606. if ($asset instanceof Asset\Folder) {
  607. $tmpAsset['leaf'] = false;
  608. $tmpAsset['expanded'] = !$asset->hasChildren();
  609. $tmpAsset['loaded'] = !$asset->hasChildren();
  610. $tmpAsset['permissions']['create'] = $asset->isAllowed('create');
  611. $tmpAsset['thumbnail'] = $this->getThumbnailUrl($asset);
  612. } else {
  613. $tmpAsset['leaf'] = true;
  614. $tmpAsset['expandable'] = false;
  615. $tmpAsset['expanded'] = false;
  616. }
  617. $this->addAdminStyle($asset, ElementAdminStyleEvent::CONTEXT_TREE, $tmpAsset);
  618. if ($asset->getType() == 'image') {
  619. try {
  620. $tmpAsset['thumbnail'] = $this->getThumbnailUrl($asset);
  621. $tmpAsset['thumbnailHdpi'] = $this->getThumbnailUrl($asset, true);
  622. // we need the dimensions for the wysiwyg editors, so that they can resize the image immediately
  623. if ($asset->getCustomSetting('imageWidth') && $asset->getCustomSetting('imageHeight')) {
  624. $tmpAsset['imageWidth'] = $asset->getCustomSetting('imageWidth');
  625. $tmpAsset['imageHeight'] = $asset->getCustomSetting('imageHeight');
  626. }
  627. } catch (\Exception $e) {
  628. Logger::debug('Cannot get dimensions of image, seems to be broken.');
  629. }
  630. } elseif ($asset->getType() == 'video') {
  631. try {
  632. if (\Pimcore\Video::isAvailable()) {
  633. $tmpAsset['thumbnail'] = $this->getThumbnailUrl($asset);
  634. $tmpAsset['thumbnailHdpi'] = $this->getThumbnailUrl($asset, true);
  635. }
  636. } catch (\Exception $e) {
  637. Logger::debug('Cannot get dimensions of video, seems to be broken.');
  638. }
  639. } elseif ($asset->getType() == 'document') {
  640. try {
  641. // add the PDF check here, otherwise the preview layer in admin is shown without content
  642. if (\Pimcore\Document::isAvailable() && \Pimcore\Document::isFileTypeSupported($asset->getFilename())) {
  643. $tmpAsset['thumbnail'] = $this->getThumbnailUrl($asset);
  644. $tmpAsset['thumbnailHdpi'] = $this->getThumbnailUrl($asset, true);
  645. }
  646. } catch (\Exception $e) {
  647. Logger::debug('Cannot get dimensions of video, seems to be broken.');
  648. }
  649. }
  650. $tmpAsset['cls'] = '';
  651. if ($asset->isLocked()) {
  652. $tmpAsset['cls'] .= 'pimcore_treenode_locked ';
  653. }
  654. if ($asset->getLocked()) {
  655. $tmpAsset['cls'] .= 'pimcore_treenode_lockOwner ';
  656. }
  657. return $tmpAsset;
  658. }
  659. /**
  660. * @param Asset $asset
  661. * @param bool $hdpi
  662. * @param bool $grid
  663. *
  664. * @return null|string
  665. */
  666. protected function getThumbnailUrl(Asset $asset, $hdpi = false, $grid = false)
  667. {
  668. $params = [
  669. 'id' => $asset->getId(),
  670. 'treepreview' => true,
  671. ];
  672. if ($hdpi) {
  673. $params['hdpi'] = true;
  674. }
  675. if ($grid) {
  676. $params['grid'] = true;
  677. }
  678. if ($asset instanceof Asset\Image) {
  679. return $this->generateUrl('pimcore_admin_asset_getimagethumbnail', $params);
  680. }
  681. if ($asset instanceof Asset\Folder) {
  682. return $this->generateUrl('pimcore_admin_asset_getfolderthumbnail', $params);
  683. }
  684. if ($asset instanceof Asset\Video && \Pimcore\Video::isAvailable()) {
  685. return $this->generateUrl('pimcore_admin_asset_getvideothumbnail', $params);
  686. }
  687. if ($asset instanceof Asset\Document && \Pimcore\Document::isAvailable() && $asset->getPageCount()) {
  688. return $this->generateUrl('pimcore_admin_asset_getdocumentthumbnail', $params);
  689. }
  690. return null;
  691. }
  692. /**
  693. * @Route("/update", name="pimcore_admin_asset_update", methods={"PUT"})
  694. *
  695. * @param Request $request
  696. *
  697. * @return JsonResponse
  698. *
  699. * @throws \Exception
  700. */
  701. public function updateAction(Request $request)
  702. {
  703. $success = false;
  704. $allowUpdate = true;
  705. $updateData = array_merge($request->request->all(), $request->query->all());
  706. $asset = Asset::getById($request->get('id'));
  707. if ($asset->isAllowed('settings')) {
  708. $asset->setUserModification($this->getAdminUser()->getId());
  709. // if the position is changed the path must be changed || also from the children
  710. if ($request->get('parentId')) {
  711. $parentAsset = Asset::getById($request->get('parentId'));
  712. //check if parent is changed i.e. asset is moved
  713. if ($asset->getParentId() != $parentAsset->getId()) {
  714. if (!$parentAsset->isAllowed('create')) {
  715. throw new \Exception('Prevented moving asset - no create permission on new parent ');
  716. }
  717. $intendedPath = $parentAsset->getRealPath();
  718. $pKey = $parentAsset->getKey();
  719. if (!empty($pKey)) {
  720. $intendedPath .= $parentAsset->getKey() . '/';
  721. }
  722. $assetWithSamePath = Asset::getByPath($intendedPath . $asset->getKey());
  723. if ($assetWithSamePath != null) {
  724. $allowUpdate = false;
  725. }
  726. if ($asset->isLocked()) {
  727. $allowUpdate = false;
  728. }
  729. }
  730. }
  731. if ($allowUpdate) {
  732. if ($request->get('filename') != $asset->getFilename() && !$asset->isAllowed('rename')) {
  733. unset($updateData['filename']);
  734. Logger::debug('prevented renaming asset because of missing permissions ');
  735. }
  736. $asset->setValues($updateData);
  737. try {
  738. $asset->save();
  739. $success = true;
  740. } catch (\Exception $e) {
  741. return $this->adminJson(['success' => false, 'message' => $e->getMessage()]);
  742. }
  743. } else {
  744. $msg = 'prevented moving asset, asset with same path+key already exists at target location or the asset is locked. ID: ' . $asset->getId();
  745. Logger::debug($msg);
  746. return $this->adminJson(['success' => $success, 'message' => $msg]);
  747. }
  748. } elseif ($asset->isAllowed('rename') && $request->get('filename')) {
  749. //just rename
  750. try {
  751. $asset->setFilename($request->get('filename'));
  752. $asset->save();
  753. $success = true;
  754. } catch (\Exception $e) {
  755. return $this->adminJson(['success' => false, 'message' => $e->getMessage()]);
  756. }
  757. } else {
  758. Logger::debug('prevented update asset because of missing permissions ');
  759. }
  760. return $this->adminJson(['success' => $success]);
  761. }
  762. /**
  763. * @Route("/webdav{path}", name="pimcore_admin_webdav", requirements={"path"=".*"})
  764. *
  765. * @param Request $request
  766. */
  767. public function webdavAction(Request $request)
  768. {
  769. $homeDir = Asset::getById(1);
  770. try {
  771. $publicDir = new Asset\WebDAV\Folder($homeDir);
  772. $objectTree = new Asset\WebDAV\Tree($publicDir);
  773. $server = new \Sabre\DAV\Server($objectTree);
  774. $server->setBaseUri($this->generateUrl('pimcore_admin_webdav', ['path' => '/']));
  775. // lock plugin
  776. $lockBackend = new \Sabre\DAV\Locks\Backend\PDO(\Pimcore\Db::get()->getWrappedConnection());
  777. $lockBackend->tableName = 'webdav_locks';
  778. $lockPlugin = new \Sabre\DAV\Locks\Plugin($lockBackend);
  779. $server->addPlugin($lockPlugin);
  780. // browser plugin
  781. $server->addPlugin(new \Sabre\DAV\Browser\Plugin());
  782. $server->exec();
  783. } catch (\Exception $e) {
  784. Logger::error($e);
  785. }
  786. exit;
  787. }
  788. /**
  789. * @Route("/save", name="pimcore_admin_asset_save", methods={"PUT","POST"})
  790. *
  791. * @param Request $request
  792. * @param EventDispatcherInterface $eventDispatcher
  793. *
  794. * @return JsonResponse
  795. *
  796. * @throws \Exception
  797. */
  798. public function saveAction(Request $request, EventDispatcherInterface $eventDispatcher)
  799. {
  800. $asset = Asset::getById($request->get('id'));
  801. if (!$asset) {
  802. throw $this->createNotFoundException('Asset not found');
  803. }
  804. if ($asset->isAllowed('publish')) {
  805. // metadata
  806. if ($request->get('metadata')) {
  807. $metadata = $this->decodeJson($request->get('metadata'));
  808. $metadataEvent = new GenericEvent($this, [
  809. 'id' => $asset->getId(),
  810. 'metadata' => $metadata,
  811. ]);
  812. $eventDispatcher->dispatch($metadataEvent, AdminEvents::ASSET_METADATA_PRE_SET);
  813. $metadata = $metadataEvent->getArgument('metadata');
  814. $metadataValues = $metadata['values'];
  815. $metadataValues = Asset\Service::minimizeMetadata($metadataValues, 'editor');
  816. $asset->setMetadataRaw($metadataValues);
  817. }
  818. // properties
  819. if ($request->get('properties')) {
  820. $properties = [];
  821. $propertiesData = $this->decodeJson($request->get('properties'));
  822. if (is_array($propertiesData)) {
  823. foreach ($propertiesData as $propertyName => $propertyData) {
  824. $value = $propertyData['data'];
  825. try {
  826. $property = new Model\Property();
  827. $property->setType($propertyData['type']);
  828. $property->setName($propertyName);
  829. $property->setCtype('asset');
  830. $property->setDataFromEditmode($value);
  831. $property->setInheritable($propertyData['inheritable']);
  832. $properties[$propertyName] = $property;
  833. } catch (\Exception $e) {
  834. Logger::err("Can't add " . $propertyName . ' to asset ' . $asset->getRealFullPath());
  835. }
  836. }
  837. $asset->setProperties($properties);
  838. }
  839. }
  840. $this->applySchedulerDataToElement($request, $asset);
  841. if ($request->get('data')) {
  842. $asset->setData($request->get('data'));
  843. }
  844. // image specific data
  845. if ($asset instanceof Asset\Image) {
  846. if ($request->get('image')) {
  847. $imageData = $this->decodeJson($request->get('image'));
  848. if (isset($imageData['focalPoint'])) {
  849. $asset->setCustomSetting('focalPointX', $imageData['focalPoint']['x']);
  850. $asset->setCustomSetting('focalPointY', $imageData['focalPoint']['y']);
  851. $asset->removeCustomSetting('disableFocalPointDetection');
  852. }
  853. } else {
  854. // wipe all data
  855. $asset->removeCustomSetting('focalPointX');
  856. $asset->removeCustomSetting('focalPointY');
  857. $asset->setCustomSetting('disableFocalPointDetection', true);
  858. }
  859. }
  860. $asset->setUserModification($this->getAdminUser()->getId());
  861. if ($request->get('task') === 'session') {
  862. // save to session only
  863. Asset\Service::saveElementToSession($asset);
  864. } else {
  865. $asset->save();
  866. }
  867. $treeData = $this->getTreeNodeConfig($asset);
  868. return $this->adminJson([
  869. 'success' => true,
  870. 'data' => [
  871. 'versionDate' => $asset->getModificationDate(),
  872. 'versionCount' => $asset->getVersionCount(),
  873. ],
  874. 'treeData' => $treeData,
  875. ]);
  876. } else {
  877. throw $this->createAccessDeniedHttpException();
  878. }
  879. }
  880. /**
  881. * @Route("/publish-version", name="pimcore_admin_asset_publishversion", methods={"POST"})
  882. *
  883. * @param Request $request
  884. *
  885. * @return JsonResponse
  886. */
  887. public function publishVersionAction(Request $request)
  888. {
  889. $version = Model\Version::getById($request->get('id'));
  890. $asset = $version->loadData();
  891. $currentAsset = Asset::getById($asset->getId());
  892. if ($currentAsset->isAllowed('publish')) {
  893. try {
  894. $asset->setUserModification($this->getAdminUser()->getId());
  895. $asset->save();
  896. $treeData = $this->getTreeNodeConfig($asset);
  897. return $this->adminJson(['success' => true, 'treeData' => $treeData]);
  898. } catch (\Exception $e) {
  899. return $this->adminJson(['success' => false, 'message' => $e->getMessage()]);
  900. }
  901. }
  902. throw $this->createAccessDeniedHttpException();
  903. }
  904. /**
  905. * @Route("/show-version", name="pimcore_admin_asset_showversion", methods={"GET"})
  906. *
  907. * @param Request $request
  908. *
  909. * @return Response
  910. */
  911. public function showVersionAction(Request $request)
  912. {
  913. $id = (int)$request->get('id');
  914. $version = Model\Version::getById($id);
  915. if (!$version) {
  916. throw $this->createNotFoundException('Version not found');
  917. }
  918. $asset = $version->loadData();
  919. if (!$asset->isAllowed('versions')) {
  920. throw $this->createAccessDeniedHttpException('Permission denied, version id [' . $id . ']');
  921. }
  922. $loader = \Pimcore::getContainer()->get('pimcore.implementation_loader.asset.metadata.data');
  923. return $this->render(
  924. '@PimcoreAdmin/Admin/Asset/showVersion' . ucfirst($asset->getType()) . '.html.twig',
  925. [
  926. 'asset' => $asset,
  927. 'loader' => $loader,
  928. ]
  929. );
  930. }
  931. /**
  932. * @Route("/download", name="pimcore_admin_asset_download", methods={"GET"})
  933. *
  934. * @param Request $request
  935. *
  936. * @return StreamedResponse
  937. */
  938. public function downloadAction(Request $request)
  939. {
  940. $asset = Asset::getById($request->get('id'));
  941. if (!$asset) {
  942. throw $this->createNotFoundException('Asset not found');
  943. }
  944. if (!$asset->isAllowed('view')) {
  945. throw $this->createAccessDeniedException('not allowed to view asset');
  946. }
  947. $stream = $asset->getStream();
  948. return new StreamedResponse(function () use ($stream) {
  949. fpassthru($stream);
  950. }, 200, [
  951. 'Content-Type' => $asset->getMimeType(),
  952. 'Content-Disposition' => sprintf('attachment; filename="%s"', $asset->getFilename()),
  953. 'Content-Length' => $asset->getFileSize(),
  954. ]);
  955. }
  956. /**
  957. * @Route("/download-image-thumbnail", name="pimcore_admin_asset_downloadimagethumbnail", methods={"GET"})
  958. *
  959. * @param Request $request
  960. *
  961. * @return BinaryFileResponse
  962. */
  963. public function downloadImageThumbnailAction(Request $request)
  964. {
  965. $image = Asset\Image::getById($request->get('id'));
  966. if (!$image) {
  967. throw $this->createNotFoundException('Asset not found');
  968. }
  969. if (!$image->isAllowed('view')) {
  970. throw $this->createAccessDeniedException('not allowed to view thumbnail');
  971. }
  972. $config = null;
  973. $thumbnail = null;
  974. $thumbnailName = $request->get('thumbnail');
  975. $thumbnailFile = null;
  976. $deleteThumbnail = true;
  977. if ($request->get('config')) {
  978. $config = $this->decodeJson($request->get('config'));
  979. } elseif ($request->get('type')) {
  980. $predefined = [
  981. 'web' => [
  982. 'resize_mode' => 'scaleByWidth',
  983. 'width' => 3500,
  984. 'dpi' => 72,
  985. 'format' => 'JPEG',
  986. 'quality' => 85,
  987. ],
  988. 'print' => [
  989. 'resize_mode' => 'scaleByWidth',
  990. 'width' => 6000,
  991. 'dpi' => 300,
  992. 'format' => 'JPEG',
  993. 'quality' => 95,
  994. ],
  995. 'office' => [
  996. 'resize_mode' => 'scaleByWidth',
  997. 'width' => 1190,
  998. 'dpi' => 144,
  999. 'format' => 'JPEG',
  1000. 'quality' => 90,
  1001. ],
  1002. ];
  1003. $config = $predefined[$request->get('type')];
  1004. } elseif ($thumbnailName) {
  1005. $thumbnail = $image->getThumbnail($thumbnailName);
  1006. $deleteThumbnail = false;
  1007. }
  1008. if ($config) {
  1009. $thumbnailConfig = new Asset\Image\Thumbnail\Config();
  1010. $thumbnailConfig->setName('pimcore-download-' . $image->getId() . '-' . md5($request->get('config')));
  1011. if ($config['resize_mode'] == 'scaleByWidth') {
  1012. $thumbnailConfig->addItem('scaleByWidth', [
  1013. 'width' => $config['width'],
  1014. ]);
  1015. } elseif ($config['resize_mode'] == 'scaleByHeight') {
  1016. $thumbnailConfig->addItem('scaleByHeight', [
  1017. 'height' => $config['height'],
  1018. ]);
  1019. } else {
  1020. $thumbnailConfig->addItem('resize', [
  1021. 'width' => $config['width'],
  1022. 'height' => $config['height'],
  1023. ]);
  1024. }
  1025. $thumbnailConfig->setQuality($config['quality']);
  1026. $thumbnailConfig->setFormat($config['format']);
  1027. $thumbnailConfig->setRasterizeSVG(true);
  1028. if ($thumbnailConfig->getFormat() == 'JPEG') {
  1029. $thumbnailConfig->setPreserveMetaData(true);
  1030. if (empty($config['quality'])) {
  1031. $thumbnailConfig->setPreserveColor(true);
  1032. }
  1033. }
  1034. $thumbnail = $image->getThumbnail($thumbnailConfig);
  1035. $thumbnailFile = $thumbnail->getLocalFile();
  1036. $exiftool = \Pimcore\Tool\Console::getExecutable('exiftool');
  1037. if ($thumbnailConfig->getFormat() == 'JPEG' && $exiftool && isset($config['dpi']) && $config['dpi']) {
  1038. $process = new Process([$exiftool, '-overwrite_original', '-xresolution=' . (int)$config['dpi'], '-yresolution=' . (int)$config['dpi'], '-resolutionunit=inches', $thumbnailFile]);
  1039. $process->run();
  1040. }
  1041. }
  1042. if ($thumbnail) {
  1043. $thumbnailFile = $thumbnailFile ?: $thumbnail->getLocalFile();
  1044. $downloadFilename = preg_replace(
  1045. '/\.' . preg_quote(File::getFileExtension($image->getFilename())) . '$/i',
  1046. '.' . $thumbnail->getFileExtension(),
  1047. $image->getFilename()
  1048. );
  1049. $downloadFilename = strtolower($downloadFilename);
  1050. clearstatcache();
  1051. $response = new BinaryFileResponse($thumbnailFile);
  1052. $response->headers->set('Content-Type', $thumbnail->getMimeType());
  1053. $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $downloadFilename);
  1054. $this->addThumbnailCacheHeaders($response);
  1055. $response->deleteFileAfterSend($deleteThumbnail);
  1056. return $response;
  1057. }
  1058. throw $this->createNotFoundException('Thumbnail not found');
  1059. }
  1060. /**
  1061. * @Route("/get-asset", name="pimcore_admin_asset_getasset", methods={"GET"})
  1062. *
  1063. * @param Request $request
  1064. *
  1065. * @return StreamedResponse
  1066. */
  1067. public function getAssetAction(Request $request)
  1068. {
  1069. $image = Asset::getById((int)$request->get('id'));
  1070. if (!$image) {
  1071. throw $this->createNotFoundException('Asset not found');
  1072. }
  1073. if (!$image->isAllowed('view')) {
  1074. throw $this->createAccessDeniedException('not allowed to view asset');
  1075. }
  1076. $stream = $image->getStream();
  1077. $response = new StreamedResponse(function () use ($stream) {
  1078. fpassthru($stream);
  1079. }, 200, [
  1080. 'Content-Type' => $image->getMimeType(),
  1081. 'Access-Control-Allow-Origin' => '*',
  1082. ]);
  1083. $this->addThumbnailCacheHeaders($response);
  1084. return $response;
  1085. }
  1086. /**
  1087. * @Route("/get-image-thumbnail", name="pimcore_admin_asset_getimagethumbnail", methods={"GET"})
  1088. *
  1089. * @param Request $request
  1090. *
  1091. * @return StreamedResponse|JsonResponse
  1092. */
  1093. public function getImageThumbnailAction(Request $request)
  1094. {
  1095. $fileinfo = $request->get('fileinfo');
  1096. $image = Asset\Image::getById((int)$request->get('id'));
  1097. if (!$image) {
  1098. throw $this->createNotFoundException('Asset not found');
  1099. }
  1100. if (!$image->isAllowed('view')) {
  1101. throw $this->createAccessDeniedException('not allowed to view thumbnail');
  1102. }
  1103. $thumbnailConfig = null;
  1104. if ($request->get('thumbnail')) {
  1105. $thumbnailConfig = $image->getThumbnailConfig($request->get('thumbnail'));
  1106. }
  1107. if (!$thumbnailConfig) {
  1108. if ($request->get('config')) {
  1109. $thumbnailConfig = $image->getThumbnailConfig($this->decodeJson($request->get('config')));
  1110. } else {
  1111. $thumbnailConfig = $image->getThumbnailConfig(array_merge($request->request->all(), $request->query->all()));
  1112. }
  1113. } else {
  1114. // no high-res images in admin mode (editmode)
  1115. // this is mostly because of the document's image editable, which doesn't know anything about the thumbnail
  1116. // configuration, so the dimensions would be incorrect (double the size)
  1117. $thumbnailConfig->setHighResolution(1);
  1118. }
  1119. $format = strtolower($thumbnailConfig->getFormat());
  1120. if ($format == 'source' || $format == 'print') {
  1121. $thumbnailConfig->setFormat('PNG');
  1122. $thumbnailConfig->setRasterizeSVG(true);
  1123. }
  1124. if ($request->get('treepreview')) {
  1125. $thumbnailConfig = Asset\Image\Thumbnail\Config::getPreviewConfig((bool)$request->get('hdpi'));
  1126. }
  1127. $cropPercent = $request->get('cropPercent');
  1128. if ($cropPercent && filter_var($cropPercent, FILTER_VALIDATE_BOOLEAN)) {
  1129. $thumbnailConfig->addItemAt(0, 'cropPercent', [
  1130. 'width' => $request->get('cropWidth'),
  1131. 'height' => $request->get('cropHeight'),
  1132. 'y' => $request->get('cropTop'),
  1133. 'x' => $request->get('cropLeft'),
  1134. ]);
  1135. $hash = md5(Tool\Serialize::serialize(array_merge($request->request->all(), $request->query->all())));
  1136. $thumbnailConfig->setName($thumbnailConfig->getName() . '_auto_' . $hash);
  1137. }
  1138. $thumbnail = $image->getThumbnail($thumbnailConfig);
  1139. if ($fileinfo) {
  1140. return $this->adminJson([
  1141. 'width' => $thumbnail->getWidth(),
  1142. 'height' => $thumbnail->getHeight(), ]);
  1143. }
  1144. $stream = $thumbnail->getStream();
  1145. $response = new StreamedResponse(function () use ($stream) {
  1146. fpassthru($stream);
  1147. }, 200, [
  1148. 'Content-Type' => $thumbnail->getMimeType(),
  1149. 'Access-Control-Allow-Origin', '*',
  1150. ]);
  1151. $this->addThumbnailCacheHeaders($response);
  1152. return $response;
  1153. }
  1154. /**
  1155. * @Route("/get-folder-thumbnail", name="pimcore_admin_asset_getfolderthumbnail", methods={"GET"})
  1156. *
  1157. * @param Request $request
  1158. *
  1159. * @return BinaryFileResponse|StreamedResponse
  1160. */
  1161. public function getFolderThumbnailAction(Request $request)
  1162. {
  1163. $folder = null;
  1164. if ($request->get('id')) {
  1165. $folder = Asset\Folder::getById((int)$request->get('id'));
  1166. if ($folder instanceof Asset\Folder) {
  1167. if (!$folder->isAllowed('view')) {
  1168. throw $this->createAccessDeniedException('not allowed to view thumbnail');
  1169. }
  1170. $stream = $folder->getPreviewImage((bool)$request->get('hdpi'));
  1171. if (!$stream) {
  1172. $response = new BinaryFileResponse(PIMCORE_PATH . '/bundles/AdminBundle/Resources/public/img/blank.png');
  1173. } else {
  1174. $response = new StreamedResponse(function () use ($stream) {
  1175. fpassthru($stream);
  1176. }, 200, [
  1177. 'Content-Type' => 'image/jpg',
  1178. ]);
  1179. }
  1180. $this->addThumbnailCacheHeaders($response);
  1181. return $response;
  1182. }
  1183. }
  1184. throw $this->createNotFoundException('could not load asset folder');
  1185. }
  1186. /**
  1187. * @Route("/get-video-thumbnail", name="pimcore_admin_asset_getvideothumbnail", methods={"GET"})
  1188. *
  1189. * @param Request $request
  1190. *
  1191. * @return StreamedResponse
  1192. */
  1193. public function getVideoThumbnailAction(Request $request)
  1194. {
  1195. $video = null;
  1196. if ($request->get('id')) {
  1197. $video = Asset\Video::getById((int)$request->get('id'));
  1198. } elseif ($request->get('path')) {
  1199. $video = Asset\Video::getByPath($request->get('path'));
  1200. }
  1201. if (!$video) {
  1202. throw $this->createNotFoundException('could not load video asset');
  1203. }
  1204. if (!$video->isAllowed('view')) {
  1205. throw $this->createAccessDeniedException('not allowed to view thumbnail');
  1206. }
  1207. $thumbnail = array_merge($request->request->all(), $request->query->all());
  1208. if ($request->get('treepreview')) {
  1209. $thumbnail = Asset\Image\Thumbnail\Config::getPreviewConfig((bool)$request->get('hdpi'));
  1210. }
  1211. $time = null;
  1212. if ($request->get('time')) {
  1213. $time = (int)$request->get('time');
  1214. }
  1215. if ($request->get('settime')) {
  1216. $video->removeCustomSetting('image_thumbnail_asset');
  1217. $video->setCustomSetting('image_thumbnail_time', $time);
  1218. $video->save();
  1219. }
  1220. $image = null;
  1221. if ($request->get('image')) {
  1222. $image = Asset\Image::getById((int)$request->get('image'));
  1223. }
  1224. if ($request->get('setimage') && $image) {
  1225. $video->removeCustomSetting('image_thumbnail_time');
  1226. $video->setCustomSetting('image_thumbnail_asset', $image->getId());
  1227. $video->save();
  1228. }
  1229. $thumb = $video->getImageThumbnail($thumbnail, $time, $image);
  1230. $stream = $thumb->getStream();
  1231. $response = new StreamedResponse(function () use ($stream) {
  1232. fpassthru($stream);
  1233. }, 200, [
  1234. 'Content-Type' => 'image/' . $thumb->getFileExtension(),
  1235. ]);
  1236. $this->addThumbnailCacheHeaders($response);
  1237. return $response;
  1238. }
  1239. /**
  1240. * @Route("/get-document-thumbnail", name="pimcore_admin_asset_getdocumentthumbnail", methods={"GET"})
  1241. *
  1242. * @param Request $request
  1243. *
  1244. * @return StreamedResponse|BinaryFileResponse
  1245. */
  1246. public function getDocumentThumbnailAction(Request $request)
  1247. {
  1248. $document = Asset\Document::getById((int)$request->get('id'));
  1249. if (!$document) {
  1250. throw $this->createNotFoundException('could not load document asset');
  1251. }
  1252. if (!$document->isAllowed('view')) {
  1253. throw $this->createAccessDeniedException('not allowed to view thumbnail');
  1254. }
  1255. $thumbnail = Asset\Image\Thumbnail\Config::getByAutoDetect(array_merge($request->request->all(), $request->query->all()));
  1256. $format = strtolower($thumbnail->getFormat());
  1257. if ($format == 'source') {
  1258. $thumbnail->setFormat('jpeg'); // default format for documents is JPEG not PNG (=too big)
  1259. }
  1260. if ($request->get('treepreview')) {
  1261. $thumbnail = Asset\Image\Thumbnail\Config::getPreviewConfig((bool)$request->get('hdpi'));
  1262. }
  1263. $page = 1;
  1264. if (is_numeric($request->get('page'))) {
  1265. $page = (int)$request->get('page');
  1266. }
  1267. $thumb = $document->getImageThumbnail($thumbnail, $page);
  1268. $stream = $thumb->getStream();
  1269. if ($stream) {
  1270. $response = new StreamedResponse(function () use ($stream) {
  1271. fpassthru($stream);
  1272. }, 200, [
  1273. 'Content-Type' => 'image/' . $thumb->getFileExtension(),
  1274. ]);
  1275. } else {
  1276. $response = new BinaryFileResponse(PIMCORE_PATH . '/bundles/AdminBundle/Resources/public/img/filetype-not-supported.svg');
  1277. }
  1278. $this->addThumbnailCacheHeaders($response);
  1279. return $response;
  1280. }
  1281. /**
  1282. * @param Response $response
  1283. */
  1284. protected function addThumbnailCacheHeaders(Response $response)
  1285. {
  1286. $lifetime = 300;
  1287. $date = new \DateTime('now');
  1288. $date->add(new \DateInterval('PT' . $lifetime . 'S'));
  1289. $response->setMaxAge($lifetime);
  1290. $response->setPublic();
  1291. $response->setExpires($date);
  1292. $response->headers->set('Pragma', '');
  1293. }
  1294. /**
  1295. * @Route("/get-preview-document", name="pimcore_admin_asset_getpreviewdocument", methods={"GET"})
  1296. *
  1297. * @param Request $request
  1298. *
  1299. * @return StreamedResponse
  1300. */
  1301. public function getPreviewDocumentAction(Request $request)
  1302. {
  1303. $asset = Asset\Document::getById($request->get('id'));
  1304. if (!$asset) {
  1305. throw $this->createNotFoundException('could not load document asset');
  1306. }
  1307. if ($asset->isAllowed('view')) {
  1308. $stream = $this->getDocumentPreviewPdf($asset);
  1309. if ($stream) {
  1310. return new StreamedResponse(function () use ($stream) {
  1311. fpassthru($stream);
  1312. }, 200, [
  1313. 'Content-Type' => 'application/pdf',
  1314. ]);
  1315. } else {
  1316. throw $this->createNotFoundException('Unable to get preview for asset ' . $asset->getId());
  1317. }
  1318. } else {
  1319. throw $this->createAccessDeniedException('Access to asset ' . $asset->getId() . ' denied');
  1320. }
  1321. }
  1322. /**
  1323. * @param Asset\Document $asset
  1324. *
  1325. * @return resource|null
  1326. */
  1327. protected function getDocumentPreviewPdf(Asset\Document $asset)
  1328. {
  1329. $stream = null;
  1330. if ($asset->getMimeType() == 'application/pdf') {
  1331. $stream = $asset->getStream();
  1332. }
  1333. if (!$stream && $asset->getPageCount() && \Pimcore\Document::isAvailable() && \Pimcore\Document::isFileTypeSupported($asset->getFilename())) {
  1334. try {
  1335. $document = \Pimcore\Document::getInstance();
  1336. $stream = $document->getPdf($asset);
  1337. } catch (\Exception $e) {
  1338. // nothing to do
  1339. }
  1340. }
  1341. return $stream;
  1342. }
  1343. /**
  1344. * @Route("/get-preview-video", name="pimcore_admin_asset_getpreviewvideo", methods={"GET"})
  1345. *
  1346. * @param Request $request
  1347. *
  1348. * @return Response
  1349. */
  1350. public function getPreviewVideoAction(Request $request)
  1351. {
  1352. $asset = Asset\Video::getById($request->get('id'));
  1353. if (!$asset) {
  1354. throw $this->createNotFoundException('could not load video asset');
  1355. }
  1356. if (!$asset->isAllowed('view')) {
  1357. throw $this->createAccessDeniedException('not allowed to preview');
  1358. }
  1359. $previewData = ['asset' => $asset];
  1360. $config = Asset\Video\Thumbnail\Config::getPreviewConfig();
  1361. $thumbnail = $asset->getThumbnail($config, ['mp4']);
  1362. if ($thumbnail) {
  1363. $previewData['asset'] = $asset;
  1364. $previewData['thumbnail'] = $thumbnail;
  1365. if ($thumbnail['status'] == 'finished') {
  1366. return $this->render(
  1367. '@PimcoreAdmin/Admin/Asset/getPreviewVideoDisplay.html.twig',
  1368. $previewData
  1369. );
  1370. } else {
  1371. return $this->render(
  1372. '@PimcoreAdmin/Admin/Asset/getPreviewVideoError.html.twig',
  1373. $previewData
  1374. );
  1375. }
  1376. } else {
  1377. return $this->render(
  1378. '@PimcoreAdmin/Admin/Asset/getPreviewVideoError.html.twig',
  1379. $previewData
  1380. );
  1381. }
  1382. }
  1383. /**
  1384. * @Route("/serve-video-preview", name="pimcore_admin_asset_servevideopreview", methods={"GET"})
  1385. *
  1386. * @param Request $request
  1387. *
  1388. * @return StreamedResponse
  1389. */
  1390. public function serveVideoPreviewAction(Request $request)
  1391. {
  1392. $asset = Asset\Video::getById($request->get('id'));
  1393. if (!$asset) {
  1394. throw $this->createNotFoundException('could not load video asset');
  1395. }
  1396. if (!$asset->isAllowed('view')) {
  1397. throw $this->createAccessDeniedException('not allowed to preview');
  1398. }
  1399. $config = Asset\Video\Thumbnail\Config::getPreviewConfig();
  1400. $thumbnail = $asset->getThumbnail($config, ['mp4']);
  1401. $storagePath = $asset->getRealPath() . '/' . preg_replace('@^' . preg_quote($asset->getPath(), '@') . '@', '', urldecode($thumbnail['formats']['mp4']));
  1402. $storage = Tool\Storage::get('thumbnail');
  1403. if ($storage->fileExists($storagePath)) {
  1404. $fs = $storage->fileSize($storagePath);
  1405. $stream = $storage->readStream($storagePath);
  1406. return new StreamedResponse(function () use ($stream) {
  1407. fpassthru($stream);
  1408. }, 200, [
  1409. 'Content-Type' => 'video/mp4',
  1410. 'Content-Length' => $fs,
  1411. 'Accept-Ranges' => 'bytes',
  1412. ]);
  1413. } else {
  1414. throw $this->createNotFoundException('Video thumbnail not found');
  1415. }
  1416. }
  1417. /**
  1418. * @Route("/image-editor", name="pimcore_admin_asset_imageeditor", methods={"GET"})
  1419. *
  1420. * @param Request $request
  1421. *
  1422. * @return Response
  1423. */
  1424. public function imageEditorAction(Request $request)
  1425. {
  1426. $asset = Asset::getById($request->get('id'));
  1427. if (!$asset->isAllowed('view')) {
  1428. throw new \Exception('not allowed to preview');
  1429. }
  1430. return $this->render(
  1431. '@PimcoreAdmin/Admin/Asset/imageEditor.html.twig',
  1432. ['asset' => $asset]
  1433. );
  1434. }
  1435. /**
  1436. * @Route("/image-editor-save", name="pimcore_admin_asset_imageeditorsave", methods={"PUT"})
  1437. *
  1438. * @param Request $request
  1439. *
  1440. * @return JsonResponse
  1441. */
  1442. public function imageEditorSaveAction(Request $request)
  1443. {
  1444. $asset = Asset::getById($request->get('id'));
  1445. if (!$asset) {
  1446. throw $this->createNotFoundException('Asset not found');
  1447. }
  1448. if (!$asset->isAllowed('publish')) {
  1449. throw $this->createAccessDeniedException('not allowed to publish');
  1450. }
  1451. $data = $request->get('dataUri');
  1452. $data = substr($data, strpos($data, ','));
  1453. $data = base64_decode($data);
  1454. $asset->setData($data);
  1455. $asset->setUserModification($this->getAdminUser()->getId());
  1456. $asset->save();
  1457. return $this->adminJson(['success' => true]);
  1458. }
  1459. /**
  1460. * @Route("/get-folder-content-preview", name="pimcore_admin_asset_getfoldercontentpreview", methods={"GET"})
  1461. *
  1462. * @param Request $request
  1463. *
  1464. * @return JsonResponse
  1465. */
  1466. public function getFolderContentPreviewAction(Request $request, EventDispatcherInterface $eventDispatcher)
  1467. {
  1468. $allParams = array_merge($request->request->all(), $request->query->all());
  1469. $filterPrepareEvent = new GenericEvent($this, [
  1470. 'requestParams' => $allParams,
  1471. ]);
  1472. $eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::ASSET_LIST_BEFORE_FILTER_PREPARE);
  1473. $allParams = $filterPrepareEvent->getArgument('requestParams');
  1474. $folder = Asset::getById($allParams['id']);
  1475. $start = 0;
  1476. $limit = 10;
  1477. if ($allParams['limit']) {
  1478. $limit = $allParams['limit'];
  1479. }
  1480. if ($allParams['start']) {
  1481. $start = $allParams['start'];
  1482. }
  1483. $conditionFilters = [];
  1484. $list = new Asset\Listing();
  1485. $conditionFilters[] = 'path LIKE ' . ($folder->getRealFullPath() == '/' ? "'/%'" : $list->quote($list->escapeLike($folder->getRealFullPath()) . '/%')) . " AND type != 'folder'";
  1486. if (!$this->getAdminUser()->isAdmin()) {
  1487. $userIds = $this->getAdminUser()->getRoles();
  1488. $userIds[] = $this->getAdminUser()->getId();
  1489. $conditionFilters[] = ' (
  1490. (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(CONCAT(path, filename),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1
  1491. OR
  1492. (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(path, filename))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1
  1493. )';
  1494. }
  1495. $condition = implode(' AND ', $conditionFilters);
  1496. $list->setCondition($condition);
  1497. $list->setLimit($limit);
  1498. $list->setOffset($start);
  1499. $list->setOrderKey('CAST(filename AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci ASC', false);
  1500. $beforeListLoadEvent = new GenericEvent($this, [
  1501. 'list' => $list,
  1502. 'context' => $allParams,
  1503. ]);
  1504. $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD);
  1505. /** @var Asset\Listing $list */
  1506. $list = $beforeListLoadEvent->getArgument('list');
  1507. $list->load();
  1508. $assets = [];
  1509. foreach ($list as $asset) {
  1510. $thumbnailMethod = Asset\Service::getPreviewThumbnail($asset, [], true);
  1511. if (!empty($thumbnailMethod)) {
  1512. $filenameDisplay = $asset->getFilename();
  1513. if (strlen($filenameDisplay) > 32) {
  1514. $filenameDisplay = substr($filenameDisplay, 0, 25) . '...' . \Pimcore\File::getFileExtension($filenameDisplay);
  1515. }
  1516. // Like for treeGetChildsByIdAction, so we respect isAllowed method which can be extended (object DI) for custom permissions, so relying only users_workspaces_asset is insufficient and could lead security breach
  1517. if ($asset->isAllowed('list')) {
  1518. $assets[] = [
  1519. 'id' => $asset->getId(),
  1520. 'type' => $asset->getType(),
  1521. 'filename' => $asset->getFilename(),
  1522. 'filenameDisplay' => htmlspecialchars($filenameDisplay),
  1523. 'url' => $this->getThumbnailUrl($asset, true, true),
  1524. 'idPath' => $data['idPath'] = Element\Service::getIdPath($asset),
  1525. ];
  1526. }
  1527. }
  1528. }
  1529. // We need to temporary use data key to be compatible with the ASSET_LIST_AFTER_LIST_LOAD global event
  1530. $result = ['data' => $assets, 'success' => true, 'total' => $list->getTotalCount()];
  1531. $afterListLoadEvent = new GenericEvent($this, [
  1532. 'list' => $result,
  1533. 'context' => $allParams,
  1534. ]);
  1535. $eventDispatcher->dispatch($afterListLoadEvent, AdminEvents::ASSET_LIST_AFTER_LIST_LOAD);
  1536. $result = $afterListLoadEvent->getArgument('list');
  1537. // Here we revert to assets key
  1538. return $this->adminJson(['assets' => $result['data'], 'success' => $result['success'], 'total' => $result['total']]);
  1539. }
  1540. /**
  1541. * @Route("/copy-info", name="pimcore_admin_asset_copyinfo", methods={"GET"})
  1542. *
  1543. * @param Request $request
  1544. *
  1545. * @return JsonResponse
  1546. */
  1547. public function copyInfoAction(Request $request)
  1548. {
  1549. $transactionId = time();
  1550. $pasteJobs = [];
  1551. Tool\Session::useSession(function (AttributeBagInterface $session) use ($transactionId) {
  1552. $session->set($transactionId, []);
  1553. }, 'pimcore_copy');
  1554. if ($request->get('type') == 'recursive') {
  1555. $asset = Asset::getById($request->get('sourceId'));
  1556. if (!$asset) {
  1557. throw $this->createNotFoundException('Source not found');
  1558. }
  1559. // first of all the new parent
  1560. $pasteJobs[] = [[
  1561. 'url' => $this->generateUrl('pimcore_admin_asset_copy'),
  1562. 'method' => 'POST',
  1563. 'params' => [
  1564. 'sourceId' => $request->get('sourceId'),
  1565. 'targetId' => $request->get('targetId'),
  1566. 'type' => 'child',
  1567. 'transactionId' => $transactionId,
  1568. 'saveParentId' => true,
  1569. ],
  1570. ]];
  1571. if ($asset->hasChildren()) {
  1572. // get amount of children
  1573. $list = new Asset\Listing();
  1574. $list->setCondition('path LIKE ?', [$list->escapeLike($asset->getRealFullPath()) . '/%']);
  1575. $list->setOrderKey('LENGTH(path)', false);
  1576. $list->setOrder('ASC');
  1577. $childIds = $list->loadIdList();
  1578. if (count($childIds) > 0) {
  1579. foreach ($childIds as $id) {
  1580. $pasteJobs[] = [[
  1581. 'url' => $this->generateUrl('pimcore_admin_asset_copy'),
  1582. 'method' => 'POST',
  1583. 'params' => [
  1584. 'sourceId' => $id,
  1585. 'targetParentId' => $request->get('targetId'),
  1586. 'sourceParentId' => $request->get('sourceId'),
  1587. 'type' => 'child',
  1588. 'transactionId' => $transactionId,
  1589. ],
  1590. ]];
  1591. }
  1592. }
  1593. }
  1594. } elseif ($request->get('type') == 'child' || $request->get('type') == 'replace') {
  1595. // the object itself is the last one
  1596. $pasteJobs[] = [[
  1597. 'url' => $this->generateUrl('pimcore_admin_asset_copy'),
  1598. 'method' => 'POST',
  1599. 'params' => [
  1600. 'sourceId' => $request->get('sourceId'),
  1601. 'targetId' => $request->get('targetId'),
  1602. 'type' => $request->get('type'),
  1603. 'transactionId' => $transactionId,
  1604. ],
  1605. ]];
  1606. }
  1607. return $this->adminJson([
  1608. 'pastejobs' => $pasteJobs,
  1609. ]);
  1610. }
  1611. /**
  1612. * @Route("/copy", name="pimcore_admin_asset_copy", methods={"POST"})
  1613. *
  1614. * @param Request $request
  1615. *
  1616. * @return JsonResponse
  1617. */
  1618. public function copyAction(Request $request)
  1619. {
  1620. $success = false;
  1621. $sourceId = (int)$request->get('sourceId');
  1622. $source = Asset::getById($sourceId);
  1623. $session = Tool\Session::get('pimcore_copy');
  1624. $sessionBag = $session->get($request->get('transactionId'));
  1625. $targetId = (int)$request->get('targetId');
  1626. if ($request->get('targetParentId')) {
  1627. $sourceParent = Asset::getById($request->get('sourceParentId'));
  1628. // this is because the key can get the prefix "_copy" if the target does already exists
  1629. if ($sessionBag['parentId']) {
  1630. $targetParent = Asset::getById($sessionBag['parentId']);
  1631. } else {
  1632. $targetParent = Asset::getById($request->get('targetParentId'));
  1633. }
  1634. $targetPath = preg_replace('@^' . $sourceParent->getRealFullPath() . '@', $targetParent . '/', $source->getRealPath());
  1635. $target = Asset::getByPath($targetPath);
  1636. } else {
  1637. $target = Asset::getById($targetId);
  1638. }
  1639. if (!$target) {
  1640. throw $this->createNotFoundException('Target not found');
  1641. }
  1642. if ($target->isAllowed('create')) {
  1643. $source = Asset::getById($sourceId);
  1644. if ($source != null) {
  1645. if ($request->get('type') == 'child') {
  1646. $newAsset = $this->_assetService->copyAsChild($target, $source);
  1647. // this is because the key can get the prefix "_copy" if the target does already exists
  1648. if ($request->get('saveParentId')) {
  1649. $sessionBag['parentId'] = $newAsset->getId();
  1650. }
  1651. } elseif ($request->get('type') == 'replace') {
  1652. $this->_assetService->copyContents($target, $source);
  1653. }
  1654. $session->set($request->get('transactionId'), $sessionBag);
  1655. Tool\Session::writeClose();
  1656. $success = true;
  1657. } else {
  1658. Logger::debug('prevended copy/paste because asset with same path+key already exists in this location');
  1659. }
  1660. } else {
  1661. Logger::error('could not execute copy/paste because of missing permissions on target [ ' . $targetId . ' ]');
  1662. throw $this->createAccessDeniedHttpException();
  1663. }
  1664. Tool\Session::writeClose();
  1665. return $this->adminJson(['success' => $success]);
  1666. }
  1667. /**
  1668. * @Route("/download-as-zip-jobs", name="pimcore_admin_asset_downloadaszipjobs", methods={"GET"})
  1669. *
  1670. * @param Request $request
  1671. *
  1672. * @return JsonResponse
  1673. */
  1674. public function downloadAsZipJobsAction(Request $request)
  1675. {
  1676. $jobId = uniqid();
  1677. $filesPerJob = 5;
  1678. $jobs = [];
  1679. $asset = Asset::getById($request->get('id'));
  1680. if (!$asset) {
  1681. throw $this->createNotFoundException('Asset not found');
  1682. }
  1683. if ($asset->isAllowed('view')) {
  1684. $parentPath = $asset->getRealFullPath();
  1685. if ($asset->getId() == 1) {
  1686. $parentPath = '';
  1687. }
  1688. $db = \Pimcore\Db::get();
  1689. $conditionFilters = [];
  1690. $selectedIds = explode(',', $request->get('selectedIds', ''));
  1691. $quotedSelectedIds = [];
  1692. foreach ($selectedIds as $selectedId) {
  1693. if ($selectedId) {
  1694. $quotedSelectedIds[] = $db->quote($selectedId);
  1695. }
  1696. }
  1697. if (!empty($quotedSelectedIds)) {
  1698. //add a condition if id numbers are specified
  1699. $conditionFilters[] = 'id IN (' . implode(',', $quotedSelectedIds) . ')';
  1700. }
  1701. $conditionFilters[] = 'path LIKE ' . $db->quote($db->escapeLike($parentPath) . '/%') . ' AND type != ' . $db->quote('folder');
  1702. if (!$this->getAdminUser()->isAdmin()) {
  1703. $userIds = $this->getAdminUser()->getRoles();
  1704. $userIds[] = $this->getAdminUser()->getId();
  1705. $conditionFilters[] = ' (
  1706. (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(CONCAT(path, filename),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1
  1707. OR
  1708. (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(path, filename))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1
  1709. )';
  1710. }
  1711. $condition = implode(' AND ', $conditionFilters);
  1712. $assetList = new Asset\Listing();
  1713. $assetList->setCondition($condition);
  1714. $assetList->setOrderKey('LENGTH(path)', false);
  1715. $assetList->setOrder('ASC');
  1716. for ($i = 0; $i < ceil($assetList->getTotalCount() / $filesPerJob); $i++) {
  1717. $jobs[] = [[
  1718. 'url' => $this->generateUrl('pimcore_admin_asset_downloadaszipaddfiles'),
  1719. 'method' => 'GET',
  1720. 'params' => [
  1721. 'id' => $asset->getId(),
  1722. 'selectedIds' => implode(',', $selectedIds),
  1723. 'offset' => $i * $filesPerJob,
  1724. 'limit' => $filesPerJob,
  1725. 'jobId' => $jobId,
  1726. ],
  1727. ]];
  1728. }
  1729. }
  1730. return $this->adminJson([
  1731. 'success' => true,
  1732. 'jobs' => $jobs,
  1733. 'jobId' => $jobId,
  1734. ]);
  1735. }
  1736. /**
  1737. * @Route("/download-as-zip-add-files", name="pimcore_admin_asset_downloadaszipaddfiles", methods={"GET"})
  1738. *
  1739. * @param Request $request
  1740. *
  1741. * @return JsonResponse
  1742. */
  1743. public function downloadAsZipAddFilesAction(Request $request)
  1744. {
  1745. $zipFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . '/download-zip-' . $request->get('jobId') . '.zip';
  1746. $asset = Asset::getById($request->get('id'));
  1747. $success = false;
  1748. if (!$asset) {
  1749. throw $this->createNotFoundException('Asset not found');
  1750. }
  1751. if ($asset->isAllowed('view')) {
  1752. $zip = new \ZipArchive();
  1753. if (!is_file($zipFile)) {
  1754. $zipState = $zip->open($zipFile, \ZipArchive::CREATE);
  1755. } else {
  1756. $zipState = $zip->open($zipFile);
  1757. }
  1758. if ($zipState === true) {
  1759. $parentPath = $asset->getRealFullPath();
  1760. if ($asset->getId() == 1) {
  1761. $parentPath = '';
  1762. }
  1763. $db = \Pimcore\Db::get();
  1764. $conditionFilters = [];
  1765. $selectedIds = $request->get('selectedIds', []);
  1766. if (!empty($selectedIds)) {
  1767. $selectedIds = explode(',', $selectedIds);
  1768. //add a condition if id numbers are specified
  1769. $conditionFilters[] = 'id IN (' . implode(',', $selectedIds) . ')';
  1770. }
  1771. $conditionFilters[] = "type != 'folder' AND path LIKE " . $db->quote($db->escapeLike($parentPath) . '/%');
  1772. if (!$this->getAdminUser()->isAdmin()) {
  1773. $userIds = $this->getAdminUser()->getRoles();
  1774. $userIds[] = $this->getAdminUser()->getId();
  1775. $conditionFilters[] = ' (
  1776. (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(CONCAT(path, filename),cpath)=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1
  1777. OR
  1778. (select list from users_workspaces_asset where userId in (' . implode(',', $userIds) . ') and LOCATE(cpath,CONCAT(path, filename))=1 ORDER BY LENGTH(cpath) DESC LIMIT 1)=1
  1779. )';
  1780. }
  1781. $condition = implode(' AND ', $conditionFilters);
  1782. $assetList = new Asset\Listing();
  1783. $assetList->setCondition($condition);
  1784. $assetList->setOrderKey('LENGTH(path) ASC, id ASC', false);
  1785. $assetList->setOffset((int)$request->get('offset'));
  1786. $assetList->setLimit((int)$request->get('limit'));
  1787. foreach ($assetList as $a) {
  1788. if ($a->isAllowed('view')) {
  1789. if (!$a instanceof Asset\Folder) {
  1790. // add the file with the relative path to the parent directory
  1791. $zip->addFile($a->getLocalFile(), preg_replace('@^' . preg_quote($asset->getRealPath(), '@') . '@i', '', $a->getRealFullPath()));
  1792. }
  1793. }
  1794. }
  1795. $zip->close();
  1796. $success = true;
  1797. }
  1798. }
  1799. return $this->adminJson([
  1800. 'success' => $success,
  1801. ]);
  1802. }
  1803. /**
  1804. * @Route("/download-as-zip", name="pimcore_admin_asset_downloadaszip", methods={"GET"})
  1805. *
  1806. * @param Request $request
  1807. *
  1808. * @return BinaryFileResponse
  1809. * Download all assets contained in the folder with parameter id as ZIP file.
  1810. * The suggested filename is either [folder name].zip or assets.zip for the root folder.
  1811. */
  1812. public function downloadAsZipAction(Request $request)
  1813. {
  1814. $asset = Asset::getById($request->get('id'));
  1815. if (!$asset) {
  1816. throw $this->createNotFoundException('Asset not found');
  1817. }
  1818. $zipFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . '/download-zip-' . $request->get('jobId') . '.zip';
  1819. $suggestedFilename = $asset->getFilename();
  1820. if (empty($suggestedFilename)) {
  1821. $suggestedFilename = 'assets';
  1822. }
  1823. $response = new BinaryFileResponse($zipFile);
  1824. $response->headers->set('Content-Type', 'application/zip');
  1825. $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $suggestedFilename . '.zip');
  1826. $response->deleteFileAfterSend(true);
  1827. return $response;
  1828. }
  1829. /**
  1830. * @Route("/import-zip", name="pimcore_admin_asset_importzip", methods={"POST"})
  1831. *
  1832. * @param Request $request
  1833. *
  1834. * @return Response
  1835. */
  1836. public function importZipAction(Request $request)
  1837. {
  1838. $jobId = uniqid();
  1839. $filesPerJob = 5;
  1840. $jobs = [];
  1841. $asset = Asset::getById($request->get('parentId'));
  1842. if (!is_file($_FILES['Filedata']['tmp_name'])) {
  1843. return $this->adminJson([
  1844. 'success' => false,
  1845. 'message' => 'Something went wrong, please check upload_max_filesize and post_max_size in your php.ini as well as the write permissions on the file system',
  1846. ]);
  1847. }
  1848. if (!$asset) {
  1849. throw $this->createNotFoundException('Parent asset not found');
  1850. }
  1851. if (!$asset->isAllowed('create')) {
  1852. throw $this->createAccessDeniedException('not allowed to create');
  1853. }
  1854. $zipFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . '/' . $jobId . '.zip';
  1855. copy($_FILES['Filedata']['tmp_name'], $zipFile);
  1856. $zip = new \ZipArchive;
  1857. if ($zip->open($zipFile) === true) {
  1858. $jobAmount = ceil($zip->numFiles / $filesPerJob);
  1859. for ($i = 0; $i < $jobAmount; $i++) {
  1860. $jobs[] = [[
  1861. 'url' => $this->generateUrl('pimcore_admin_asset_importzipfiles'),
  1862. 'method' => 'POST',
  1863. 'params' => [
  1864. 'parentId' => $asset->getId(),
  1865. 'offset' => $i * $filesPerJob,
  1866. 'limit' => $filesPerJob,
  1867. 'jobId' => $jobId,
  1868. 'last' => (($i + 1) >= $jobAmount) ? 'true' : '',
  1869. ],
  1870. ]];
  1871. }
  1872. $zip->close();
  1873. }
  1874. // here we have to use this method and not the JSON action helper ($this->_helper->json()) because this will add
  1875. // Content-Type: application/json which fires a download window in most browsers, because this is a normal POST
  1876. // request and not XHR where the content-type doesn't matter
  1877. $responseJson = $this->encodeJson([
  1878. 'success' => true,
  1879. 'jobs' => $jobs,
  1880. 'jobId' => $jobId,
  1881. ]);
  1882. return new Response($responseJson);
  1883. }
  1884. /**
  1885. * @Route("/import-zip-files", name="pimcore_admin_asset_importzipfiles", methods={"POST"})
  1886. *
  1887. * @param Request $request
  1888. *
  1889. * @return JsonResponse
  1890. */
  1891. public function importZipFilesAction(Request $request)
  1892. {
  1893. $jobId = $request->get('jobId');
  1894. $limit = (int)$request->get('limit');
  1895. $offset = (int)$request->get('offset');
  1896. $importAsset = Asset::getById($request->get('parentId'));
  1897. $zipFile = PIMCORE_SYSTEM_TEMP_DIRECTORY . '/' . $jobId . '.zip';
  1898. $tmpDir = PIMCORE_SYSTEM_TEMP_DIRECTORY . '/zip-import';
  1899. if (!is_dir($tmpDir)) {
  1900. File::mkdir($tmpDir, 0777, true);
  1901. }
  1902. $zip = new \ZipArchive;
  1903. if ($zip->open($zipFile) === true) {
  1904. for ($i = $offset; $i < ($offset + $limit); $i++) {
  1905. $path = $zip->getNameIndex($i);
  1906. if ($path !== false) {
  1907. if ($zip->extractTo($tmpDir . '/', $path)) {
  1908. $tmpFile = $tmpDir . '/' . preg_replace('@^/@', '', $path);
  1909. $filename = Element\Service::getValidKey(basename($path), 'asset');
  1910. $relativePath = '';
  1911. if (dirname($path) != '.') {
  1912. $relativePath = dirname($path);
  1913. }
  1914. $parentPath = $importAsset->getRealFullPath() . '/' . preg_replace('@^/@', '', $relativePath);
  1915. $parent = Asset\Service::createFolderByPath($parentPath);
  1916. // check for duplicate filename
  1917. $filename = $this->getSafeFilename($parent->getRealFullPath(), $filename);
  1918. if ($parent->isAllowed('create')) {
  1919. $asset = Asset::create($parent->getId(), [
  1920. 'filename' => $filename,
  1921. 'sourcePath' => $tmpFile,
  1922. 'userOwner' => $this->getAdminUser()->getId(),
  1923. 'userModification' => $this->getAdminUser()->getId(),
  1924. ]);
  1925. @unlink($tmpFile);
  1926. } else {
  1927. Logger::debug('prevented creating asset because of missing permissions');
  1928. }
  1929. }
  1930. }
  1931. }
  1932. $zip->close();
  1933. }
  1934. if ($request->get('last')) {
  1935. unlink($zipFile);
  1936. }
  1937. return $this->adminJson([
  1938. 'success' => true,
  1939. ]);
  1940. }
  1941. /**
  1942. * @Route("/import-server", name="pimcore_admin_asset_importserver", methods={"POST"})
  1943. *
  1944. * @param Request $request
  1945. *
  1946. * @return JsonResponse
  1947. */
  1948. public function importServerAction(Request $request)
  1949. {
  1950. $success = true;
  1951. $filesPerJob = 5;
  1952. $jobs = [];
  1953. $importDirectory = str_replace('/fileexplorer', PIMCORE_PROJECT_ROOT, $request->get('serverPath'));
  1954. if (preg_match('@^' . preg_quote(PIMCORE_PROJECT_ROOT, '@') . '@', $importDirectory) && is_dir($importDirectory)) {
  1955. $this->checkForPharStreamWrapper($importDirectory);
  1956. $files = rscandir($importDirectory . '/');
  1957. $count = count($files);
  1958. $jobFiles = [];
  1959. for ($i = 0; $i < $count; $i++) {
  1960. if (is_dir($files[$i])) {
  1961. continue;
  1962. }
  1963. $jobFiles[] = preg_replace('@^' . preg_quote($importDirectory, '@') . '@', '', $files[$i]);
  1964. if (count($jobFiles) >= $filesPerJob || $i >= ($count - 1)) {
  1965. $relativeImportDirectory = preg_replace('@^' . preg_quote(PIMCORE_PROJECT_ROOT, '@') . '@', '', $importDirectory);
  1966. $jobs[] = [[
  1967. 'url' => $this->generateUrl('pimcore_admin_asset_importserverfiles'),
  1968. 'method' => 'POST',
  1969. 'params' => [
  1970. 'parentId' => $request->get('parentId'),
  1971. 'serverPath' => $relativeImportDirectory,
  1972. 'files' => implode('::', $jobFiles),
  1973. ],
  1974. ]];
  1975. $jobFiles = [];
  1976. }
  1977. }
  1978. }
  1979. return $this->adminJson([
  1980. 'success' => $success,
  1981. 'jobs' => $jobs,
  1982. ]);
  1983. }
  1984. /**
  1985. * @Route("/import-server-files", name="pimcore_admin_asset_importserverfiles", methods={"POST"})
  1986. *
  1987. * @param Request $request
  1988. *
  1989. * @return JsonResponse
  1990. */
  1991. public function importServerFilesAction(Request $request)
  1992. {
  1993. $assetFolder = Asset::getById($request->get('parentId'));
  1994. if (!$assetFolder) {
  1995. throw $this->createNotFoundException('Parent asset not found');
  1996. }
  1997. $serverPath = PIMCORE_PROJECT_ROOT . $request->get('serverPath');
  1998. $files = explode('::', $request->get('files'));
  1999. foreach ($files as $file) {
  2000. $absolutePath = $serverPath . $file;
  2001. $this->checkForPharStreamWrapper($absolutePath);
  2002. if (is_file($absolutePath)) {
  2003. $relFolderPath = str_replace('\\', '/', dirname($file));
  2004. $folder = Asset\Service::createFolderByPath($assetFolder->getRealFullPath() . $relFolderPath);
  2005. $filename = basename($file);
  2006. // check for duplicate filename
  2007. $filename = Element\Service::getValidKey($filename, 'asset');
  2008. $filename = $this->getSafeFilename($folder->getRealFullPath(), $filename);
  2009. if ($assetFolder->isAllowed('create')) {
  2010. $asset = Asset::create($folder->getId(), [
  2011. 'filename' => $filename,
  2012. 'sourcePath' => $absolutePath,
  2013. 'userOwner' => $this->getAdminUser()->getId(),
  2014. 'userModification' => $this->getAdminUser()->getId(),
  2015. ]);
  2016. } else {
  2017. Logger::debug('prevented creating asset because of missing permissions ');
  2018. }
  2019. }
  2020. }
  2021. return $this->adminJson([
  2022. 'success' => true,
  2023. ]);
  2024. }
  2025. protected function checkForPharStreamWrapper($path)
  2026. {
  2027. if (stripos($path, 'phar://') !== false) {
  2028. throw $this->createAccessDeniedException('Using PHAR files is not allowed!');
  2029. }
  2030. }
  2031. /**
  2032. * @Route("/import-url", name="pimcore_admin_asset_importurl", methods={"POST"})
  2033. *
  2034. * @param Request $request
  2035. *
  2036. * @return JsonResponse
  2037. *
  2038. * @throws \Exception
  2039. */
  2040. public function importUrlAction(Request $request)
  2041. {
  2042. $success = true;
  2043. $data = Tool::getHttpData($request->get('url'));
  2044. $filename = basename($request->get('url'));
  2045. $parentId = $request->get('id');
  2046. $parentAsset = Asset::getById((int)$parentId);
  2047. if (!$parentAsset) {
  2048. throw $this->createNotFoundException('Parent asset not found');
  2049. }
  2050. $filename = Element\Service::getValidKey($filename, 'asset');
  2051. $filename = $this->getSafeFilename($parentAsset->getRealFullPath(), $filename);
  2052. if (empty($filename)) {
  2053. throw new \Exception('The filename of the asset is empty');
  2054. }
  2055. // check for duplicate filename
  2056. $filename = $this->getSafeFilename($parentAsset->getRealFullPath(), $filename);
  2057. if ($parentAsset->isAllowed('create')) {
  2058. $asset = Asset::create($parentId, [
  2059. 'filename' => $filename,
  2060. 'data' => $data,
  2061. 'userOwner' => $this->getAdminUser()->getId(),
  2062. 'userModification' => $this->getAdminUser()->getId(),
  2063. ]);
  2064. $success = true;
  2065. } else {
  2066. Logger::debug('prevented creating asset because of missing permissions');
  2067. }
  2068. return $this->adminJson(['success' => $success]);
  2069. }
  2070. /**
  2071. * @Route("/clear-thumbnail", name="pimcore_admin_asset_clearthumbnail", methods={"POST"})
  2072. *
  2073. * @param Request $request
  2074. *
  2075. * @return JsonResponse
  2076. */
  2077. public function clearThumbnailAction(Request $request)
  2078. {
  2079. $success = false;
  2080. if ($asset = Asset::getById($request->get('id'))) {
  2081. if (method_exists($asset, 'clearThumbnails')) {
  2082. if (!$asset->isAllowed('publish')) {
  2083. throw $this->createAccessDeniedException('not allowed to publish');
  2084. }
  2085. $asset->clearThumbnails(true); // force clear
  2086. $asset->save();
  2087. $success = true;
  2088. }
  2089. }
  2090. return $this->adminJson(['success' => $success]);
  2091. }
  2092. /**
  2093. * @Route("/grid-proxy", name="pimcore_admin_asset_gridproxy", methods={"GET", "POST", "PUT"})
  2094. *
  2095. * @param Request $request
  2096. * @param EventDispatcherInterface $eventDispatcher
  2097. * @param GridHelperService $gridHelperService
  2098. * @param CsrfProtectionHandler $csrfProtection
  2099. *
  2100. * @return JsonResponse
  2101. */
  2102. public function gridProxyAction(Request $request, EventDispatcherInterface $eventDispatcher, GridHelperService $gridHelperService, CsrfProtectionHandler $csrfProtection)
  2103. {
  2104. $allParams = array_merge($request->request->all(), $request->query->all());
  2105. $filterPrepareEvent = new GenericEvent($this, [
  2106. 'requestParams' => $allParams,
  2107. ]);
  2108. $language = $request->get('language') != 'default' ? $request->get('language') : null;
  2109. $eventDispatcher->dispatch($filterPrepareEvent, AdminEvents::ASSET_LIST_BEFORE_FILTER_PREPARE);
  2110. $allParams = $filterPrepareEvent->getArgument('requestParams');
  2111. $loader = \Pimcore::getContainer()->get('pimcore.implementation_loader.asset.metadata.data');
  2112. if (isset($allParams['data']) && $allParams['data']) {
  2113. $csrfProtection->checkCsrfToken($request);
  2114. if ($allParams['xaction'] == 'update') {
  2115. try {
  2116. $data = $this->decodeJson($allParams['data']);
  2117. $updateEvent = new GenericEvent($this, [
  2118. 'data' => $data,
  2119. 'processed' => false,
  2120. ]);
  2121. $eventDispatcher->dispatch($updateEvent, AdminEvents::ASSET_LIST_BEFORE_UPDATE);
  2122. $processed = $updateEvent->getArgument('processed');
  2123. if ($processed) {
  2124. // update already processed by event handler
  2125. return $this->adminJson(['success' => true]);
  2126. }
  2127. $data = $updateEvent->getArgument('data');
  2128. // save
  2129. $asset = Asset::getById($data['id']);
  2130. if (!$asset) {
  2131. throw $this->createNotFoundException('Asset not found');
  2132. }
  2133. if (!$asset->isAllowed('publish')) {
  2134. throw $this->createAccessDeniedException("Permission denied. You don't have the rights to save this asset.");
  2135. }
  2136. $metadata = $asset->getMetadata(null, null, false, true);
  2137. $dirty = false;
  2138. unset($data['id']);
  2139. foreach ($data as $key => $value) {
  2140. $fieldDef = explode('~', $key);
  2141. $key = $fieldDef[0];
  2142. if (isset($fieldDef[1])) {
  2143. $language = ($fieldDef[1] == 'none' ? '' : $fieldDef[1]);
  2144. }
  2145. foreach ($metadata as $idx => &$em) {
  2146. if ($em['name'] == $key && $em['language'] == $language) {
  2147. try {
  2148. $dataImpl = $loader->build($em['type']);
  2149. $value = $dataImpl->getDataFromListfolderGrid($value, $em);
  2150. } catch (UnsupportedException $le) {
  2151. Logger::error('could not resolve metadata implementation for ' . $em['type']);
  2152. }
  2153. $em['data'] = $value;
  2154. $dirty = true;
  2155. break;
  2156. }
  2157. }
  2158. if (!$dirty) {
  2159. $defaulMetadata = ['title', 'alt', 'copyright'];
  2160. if (in_array($key, $defaulMetadata)) {
  2161. $newEm = [
  2162. 'name' => $key,
  2163. 'language' => $language,
  2164. 'type' => 'input',
  2165. 'data' => $value,
  2166. ];
  2167. try {
  2168. $dataImpl = $loader->build($newEm['type']);
  2169. $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm);
  2170. } catch (UnsupportedException $le) {
  2171. Logger::error('could not resolve metadata implementation for ' . $newEm['type']);
  2172. }
  2173. $metadata[] = $newEm;
  2174. $dirty = true;
  2175. } else {
  2176. $predefined = Model\Metadata\Predefined::getByName($key);
  2177. if ($predefined && (empty($predefined->getTargetSubtype())
  2178. || $predefined->getTargetSubtype() == $asset->getType())) {
  2179. $newEm = [
  2180. 'name' => $key,
  2181. 'language' => $language,
  2182. 'type' => $predefined->getType(),
  2183. 'data' => $value,
  2184. ];
  2185. try {
  2186. $dataImpl = $loader->build($newEm['type']);
  2187. $newEm['data'] = $dataImpl->getDataFromListfolderGrid($value, $newEm);
  2188. } catch (UnsupportedException $le) {
  2189. Logger::error('could not resolve metadata implementation for ' . $newEm['type']);
  2190. }
  2191. $metadata[] = $newEm;
  2192. $dirty = true;
  2193. }
  2194. }
  2195. }
  2196. }
  2197. if ($dirty) {
  2198. // $metadata = Asset\Service::minimizeMetadata($metadata, "grid");
  2199. $asset->setMetadataRaw($metadata);
  2200. $asset->save();
  2201. return $this->adminJson(['success' => true]);
  2202. }
  2203. return $this->adminJson(['success' => false, 'message' => 'something went wrong.']);
  2204. } catch (\Exception $e) {
  2205. return $this->adminJson(['success' => false, 'message' => $e->getMessage()]);
  2206. }
  2207. }
  2208. } else {
  2209. $list = $gridHelperService->prepareAssetListingForGrid($allParams, $this->getAdminUser());
  2210. $beforeListLoadEvent = new GenericEvent($this, [
  2211. 'list' => $list,
  2212. 'context' => $allParams,
  2213. ]);
  2214. $eventDispatcher->dispatch($beforeListLoadEvent, AdminEvents::ASSET_LIST_BEFORE_LIST_LOAD);
  2215. /** @var Asset\Listing $list */
  2216. $list = $beforeListLoadEvent->getArgument('list');
  2217. $list->load();
  2218. $assets = [];
  2219. foreach ($list->getAssets() as $index => $asset) {
  2220. // Like for treeGetChildsByIdAction, so we respect isAllowed method which can be extended (object DI) for custom permissions, so relying only users_workspaces_asset is insufficient and could lead security breach
  2221. if ($asset->isAllowed('list')) {
  2222. $a = Asset\Service::gridAssetData($asset, $allParams['fields'], $allParams['language'] ?? '');
  2223. $assets[] = $a;
  2224. }
  2225. }
  2226. $result = ['data' => $assets, 'success' => true, 'total' => $list->getTotalCount()];
  2227. $afterListLoadEvent = new GenericEvent($this, [
  2228. 'list' => $result,
  2229. 'context' => $allParams,
  2230. ]);
  2231. $eventDispatcher->dispatch($afterListLoadEvent, AdminEvents::ASSET_LIST_AFTER_LIST_LOAD);
  2232. $result = $afterListLoadEvent->getArgument('list');
  2233. return $this->adminJson($result);
  2234. }
  2235. return $this->adminJson(['success' => false]);
  2236. }
  2237. /**
  2238. * @Route("/get-text", name="pimcore_admin_asset_gettext", methods={"GET"})
  2239. *
  2240. * @param Request $request
  2241. *
  2242. * @return JsonResponse
  2243. */
  2244. public function getTextAction(Request $request)
  2245. {
  2246. $asset = Asset::getById($request->get('id'));
  2247. if (!$asset) {
  2248. throw $this->createNotFoundException('Asset not found');
  2249. }
  2250. if (!$asset->isAllowed('view')) {
  2251. throw $this->createAccessDeniedException('not allowed to view');
  2252. }
  2253. $page = $request->get('page');
  2254. $text = null;
  2255. if ($asset instanceof Asset\Document) {
  2256. $text = $asset->getText($page);
  2257. }
  2258. return $this->adminJson(['success' => 'true', 'text' => $text]);
  2259. }
  2260. /**
  2261. * @Route("/detect-image-features", name="pimcore_admin_asset_detectimagefeatures", methods={"GET"})
  2262. *
  2263. * @param Request $request
  2264. *
  2265. * @return JsonResponse
  2266. */
  2267. public function detectImageFeaturesAction(Request $request)
  2268. {
  2269. $asset = Asset\Image::getById((int)$request->get('id'));
  2270. if (!$asset instanceof Asset) {
  2271. return $this->adminJson(['success' => false, 'message' => "asset doesn't exist"]);
  2272. }
  2273. if ($asset->isAllowed('publish')) {
  2274. $asset->detectFaces();
  2275. $asset->removeCustomSetting('disableImageFeatureAutoDetection');
  2276. $asset->save();
  2277. return $this->adminJson(['success' => true]);
  2278. }
  2279. throw $this->createAccessDeniedHttpException();
  2280. }
  2281. /**
  2282. * @Route("/delete-image-features", name="pimcore_admin_asset_deleteimagefeatures", methods={"GET"})
  2283. *
  2284. * @param Request $request
  2285. *
  2286. * @return JsonResponse
  2287. */
  2288. public function deleteImageFeaturesAction(Request $request)
  2289. {
  2290. $asset = Asset::getById((int)$request->get('id'));
  2291. if (!$asset instanceof Asset) {
  2292. return $this->adminJson(['success' => false, 'message' => "asset doesn't exist"]);
  2293. }
  2294. if ($asset->isAllowed('publish')) {
  2295. $asset->removeCustomSetting('faceCoordinates');
  2296. $asset->setCustomSetting('disableImageFeatureAutoDetection', true);
  2297. $asset->save();
  2298. return $this->adminJson(['success' => true]);
  2299. }
  2300. throw $this->createAccessDeniedHttpException();
  2301. }
  2302. /**
  2303. * @param ControllerEvent $event
  2304. */
  2305. public function onKernelControllerEvent(ControllerEvent $event)
  2306. {
  2307. $isMasterRequest = $event->isMasterRequest();
  2308. if (!$isMasterRequest) {
  2309. return;
  2310. }
  2311. $this->checkActionPermission($event, 'assets', [
  2312. 'getImageThumbnailAction', 'getVideoThumbnailAction', 'getDocumentThumbnailAction',
  2313. ]);
  2314. $this->_assetService = new Asset\Service($this->getAdminUser());
  2315. }
  2316. }