vendor/pimcore/pimcore/bundles/CoreBundle/Controller/PublicServicesController.php line 213

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\CoreBundle\Controller;
  15. use function date;
  16. use Pimcore\Config;
  17. use Pimcore\Controller\Controller;
  18. use Pimcore\File;
  19. use Pimcore\Logger;
  20. use Pimcore\Model\Asset;
  21. use Pimcore\Model\Site;
  22. use Pimcore\Model\Tool\TmpStore;
  23. use Pimcore\Tool\Storage;
  24. use Symfony\Component\HttpFoundation\Cookie;
  25. use Symfony\Component\HttpFoundation\RedirectResponse;
  26. use Symfony\Component\HttpFoundation\Request;
  27. use Symfony\Component\HttpFoundation\Response;
  28. use Symfony\Component\HttpFoundation\StreamedResponse;
  29. use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener;
  30. use function time;
  31. /**
  32. * @internal
  33. */
  34. class PublicServicesController extends Controller
  35. {
  36. /**
  37. * @param Request $request
  38. *
  39. * @return RedirectResponse|StreamedResponse
  40. */
  41. public function thumbnailAction(Request $request)
  42. {
  43. $storage = Storage::get('thumbnail');
  44. $assetId = (int) $request->get('assetId');
  45. $thumbnailName = $request->get('thumbnailName');
  46. $thumbnailType = $request->get('type');
  47. $filename = $request->get('filename');
  48. $requestedFileExtension = strtolower(File::getFileExtension($filename));
  49. $asset = Asset::getById($assetId);
  50. if ($asset) {
  51. $prefix = preg_replace('@^cache-buster\-[\d]+\/@', '', $request->get('prefix'));
  52. $prefix = preg_replace('@' . $asset->getId() . '/$@', '', $prefix);
  53. if ($asset->getPath() === ('/' . $prefix)) {
  54. // we need to check the path as well, this is important in the case you have restricted the public access to
  55. // assets via rewrite rules
  56. try {
  57. $thumbnail = null;
  58. $thumbnailStream = null;
  59. // just check if the thumbnail exists -> throws exception otherwise
  60. $thumbnailConfigClass = 'Pimcore\\Model\\Asset\\' . ucfirst($thumbnailType) . '\\Thumbnail\Config';
  61. $thumbnailConfig = $thumbnailConfigClass::getByName($thumbnailName);
  62. if (!$thumbnailConfig) {
  63. // check if there's an item in the TmpStore
  64. // remove an eventually existing cache-buster prefix first (eg. when using with a CDN)
  65. $pathInfo = preg_replace('@^/cache-buster\-[\d]+@', '', $request->getPathInfo());
  66. $deferredConfigId = 'thumb_' . $assetId . '__' . md5(urldecode($pathInfo));
  67. if ($thumbnailConfigItem = TmpStore::get($deferredConfigId)) {
  68. $thumbnailConfig = $thumbnailConfigItem->getData();
  69. TmpStore::delete($deferredConfigId);
  70. if (!$thumbnailConfig instanceof $thumbnailConfigClass) {
  71. throw new \Exception('Deferred thumbnail config file doesn\'t contain a valid '.$thumbnailConfigClass.' object');
  72. }
  73. } elseif ($this->getParameter('pimcore.config')['assets'][$thumbnailType]['thumbnails']['status_cache']) {
  74. // Delete Thumbnail Name from Cache so the next call can generate a new TmpStore entry
  75. $asset->getDao()->deleteFromThumbnailCache($thumbnailName);
  76. }
  77. }
  78. if (!$thumbnailConfig) {
  79. throw $this->createNotFoundException("Thumbnail '" . $thumbnailName . "' file doesn't exist");
  80. }
  81. if ($thumbnailType == 'image' && strcasecmp($thumbnailConfig->getFormat(), 'SOURCE') === 0) {
  82. $formatOverride = $requestedFileExtension;
  83. if (in_array($requestedFileExtension, ['jpg', 'jpeg'])) {
  84. $formatOverride = 'pjpeg';
  85. }
  86. $thumbnailConfig->setFormat($formatOverride);
  87. }
  88. if ($asset instanceof Asset\Video) {
  89. if ($thumbnailType == 'video') {
  90. $thumbnail = $asset->getThumbnail($thumbnailName, [$requestedFileExtension]);
  91. $storagePath = urldecode($thumbnail['formats'][$requestedFileExtension]);
  92. if ($storage->fileExists($storagePath)) {
  93. $thumbnailStream = $storage->readStream($storagePath);
  94. }
  95. } else {
  96. $time = 1;
  97. if (preg_match("|~\-~time\-(\d+)\.|", $filename, $matchesThumbs)) {
  98. $time = (int)$matchesThumbs[1];
  99. }
  100. $thumbnail = $asset->getImageThumbnail($thumbnailConfig, $time);
  101. $thumbnailStream = $thumbnail->getStream();
  102. }
  103. } elseif ($asset instanceof Asset\Document) {
  104. $page = 1;
  105. if (preg_match("|~\-~page\-(\d+)(@[0-9.]+x)?\.|", $filename, $matchesThumbs)) {
  106. $page = (int)$matchesThumbs[1];
  107. }
  108. $thumbnailConfig->setName(preg_replace("/\-[\d]+/", '', $thumbnailConfig->getName()));
  109. $thumbnailConfig->setName(str_replace('document_', '', $thumbnailConfig->getName()));
  110. $thumbnail = $asset->getImageThumbnail($thumbnailConfig, $page);
  111. $thumbnailStream = $thumbnail->getStream();
  112. } elseif ($asset instanceof Asset\Image) {
  113. //check if high res image is called
  114. preg_match("@([^\@]+)(\@[0-9.]+x)?\.([a-zA-Z]{2,5})@", $filename, $matches);
  115. if (empty($matches) || !isset($matches[1])) {
  116. throw $this->createNotFoundException('Requested asset does not exist');
  117. }
  118. if (array_key_exists(2, $matches) && $matches[2]) {
  119. $highResFactor = (float)str_replace(['@', 'x'], '', $matches[2]);
  120. $thumbnailConfig->setHighResolution($highResFactor);
  121. }
  122. // check if a media query thumbnail was requested
  123. if (preg_match("#~\-~media\-\-(.*)\-\-query#", $matches[1], $mediaQueryResult)) {
  124. $thumbnailConfig->selectMedia($mediaQueryResult[1]);
  125. }
  126. $thumbnail = $asset->getThumbnail($thumbnailConfig);
  127. $thumbnailStream = $thumbnail->getStream();
  128. }
  129. if ($thumbnail && $thumbnailStream) {
  130. if ($thumbnailType == 'image') {
  131. $mime = $thumbnail->getMimeType();
  132. $fileSize = $thumbnail->getFileSize();
  133. $pathReference = $thumbnail->getPathReference();
  134. $actualFileExtension = File::getFileExtension($pathReference['src']);
  135. if ($actualFileExtension !== $requestedFileExtension) {
  136. // create a copy/symlink to the file with the original file extension
  137. // this can be e.g. the case when the thumbnail is called as foo.png but the thumbnail config
  138. // is set to auto-optimized format so the resulting thumbnail can be jpeg
  139. $requestedFile = preg_replace('/\.' . $actualFileExtension . '$/', '.' . $requestedFileExtension, $pathReference['src']);
  140. //Only copy the file if not exists yet
  141. if (!$storage->fileExists($requestedFile)) {
  142. $storage->writeStream($requestedFile, $thumbnailStream);
  143. }
  144. //Stream can be closed by writeStream and needs to be reloaded.
  145. $thumbnailStream = $storage->readStream($requestedFile);
  146. }
  147. } elseif ($thumbnailType =='video' && isset($storagePath)) {
  148. $mime = $storage->mimeType($storagePath);
  149. $fileSize = $storage->fileSize($storagePath);
  150. } else {
  151. throw new \Exception('Cannot determine mime type and file size of '.$thumbnailType.' thumbnail, see logs for details.');
  152. }
  153. // set appropriate caching headers
  154. // see also: https://github.com/pimcore/pimcore/blob/1931860f0aea27de57e79313b2eb212dcf69ef13/.htaccess#L86-L86
  155. $lifetime = 86400 * 7; // 1 week lifetime, same as direct delivery in .htaccess
  156. $headers = [
  157. 'Cache-Control' => 'public, max-age=' . $lifetime,
  158. 'Expires' => date('D, d M Y H:i:s T', time() + $lifetime),
  159. 'Content-Type' => $mime,
  160. 'Content-Length' => $fileSize,
  161. ];
  162. $headers[AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER] = true;
  163. return new StreamedResponse(function () use ($thumbnailStream) {
  164. fpassthru($thumbnailStream);
  165. }, 200, $headers);
  166. }
  167. throw new \Exception('Unable to generate '.$thumbnailType.' thumbnail, see logs for details.');
  168. } catch (\Exception $e) {
  169. Logger::error($e->getMessage());
  170. return new RedirectResponse('/bundles/pimcoreadmin/img/filetype-not-supported.svg');
  171. }
  172. }
  173. }
  174. throw $this->createNotFoundException('Asset not found');
  175. }
  176. /**
  177. * @param Request $request
  178. *
  179. * @return Response
  180. */
  181. public function robotsTxtAction(Request $request)
  182. {
  183. // check for site
  184. $domain = \Pimcore\Tool::getHostname();
  185. $site = Site::getByDomain($domain);
  186. $config = Config::getRobotsConfig()->toArray();
  187. $siteId = 'default';
  188. if ($site instanceof Site) {
  189. $siteId = $site->getId();
  190. }
  191. // send correct headers
  192. header('Content-Type: text/plain; charset=utf8');
  193. while (@ob_end_flush()) ;
  194. // check for configured robots.txt in pimcore
  195. $content = '';
  196. if (array_key_exists($siteId, $config)) {
  197. $content = $config[$siteId];
  198. }
  199. if (empty($content)) {
  200. // default behavior, allow robots to index everything
  201. $content = "User-agent: *\nDisallow:";
  202. }
  203. return new Response($content, Response::HTTP_OK, [
  204. 'Content-Type' => 'text/plain',
  205. ]);
  206. }
  207. /**
  208. * @param Request $request
  209. *
  210. * @return Response
  211. */
  212. public function commonFilesAction(Request $request)
  213. {
  214. return new Response("HTTP/1.1 404 Not Found\nFiltered by common files filter", 404);
  215. }
  216. /**
  217. * @param Request $request
  218. *
  219. * @return RedirectResponse
  220. */
  221. public function customAdminEntryPointAction(Request $request)
  222. {
  223. $params = $request->query->all();
  224. $url = match (true) {
  225. isset($params['token']) => $this->generateUrl('pimcore_admin_login_check', $params),
  226. isset($params['deeplink']) => $this->generateUrl('pimcore_admin_login_deeplink', $params),
  227. default => $this->generateUrl('pimcore_admin_login', $params)
  228. };
  229. $redirect = new RedirectResponse($url);
  230. $customAdminPathIdentifier = $this->getParameter('pimcore_admin.custom_admin_path_identifier');
  231. if (!empty($customAdminPathIdentifier) && $request->cookies->get('pimcore_custom_admin') != $customAdminPathIdentifier) {
  232. $redirect->headers->setCookie(new Cookie('pimcore_custom_admin', $customAdminPathIdentifier, strtotime('+1 year')));
  233. }
  234. return $redirect;
  235. }
  236. }