/** * Returns the configuration for the image thumbnail with the given ID. */ public function imageThumbnailAction() { $this->checkUserPermission("thumbnails"); try { $id = $this->getParam("id"); if ($id) { $config = Asset\Image\Thumbnail\Config::getByName($id); if (!$config instanceof Asset\Image\Thumbnail\Config) { throw new \Exception("Thumbnail '" . $id . "' file doesn't exists"); } $this->encoder->encode(["success" => true, "data" => $config->getForWebserviceExport()]); return; } } catch (\Exception $e) { \Logger::error($e); $this->encoder->encode(["success" => false, "msg" => (string) $e]); } $this->encoder->encode(["success" => false]); }
/** * Loads a list of predefined properties for the specicifies parameters, returns an array of Property\Predefined elements * * @return array */ public function load() { $properties = array(); $propertiesData = $this->db->fetchAll($this->model->getFilter(), $this->model->getOrder()); foreach ($propertiesData as $propertyData) { $properties[] = Config::getByName($propertyData["id"]); } $this->model->setThumbnails($properties); return $properties; }
/** * @param \Zend_Controller_Request_Abstract $request */ public function routeStartup(\Zend_Controller_Request_Abstract $request) { // this is a filter which checks for common used files (by browser, crawlers, ...) and prevent the default // error page, because this is more resource-intensive than exiting right here if (preg_match("@^/website/var/tmp/image-thumbnails(.*)?/([0-9]+)/thumb__([a-zA-Z0-9_\\-]+)([^\\@]+)(\\@[0-9.]+x)?\\.([a-zA-Z]{2,5})@", $request->getPathInfo(), $matches)) { $assetId = $matches[2]; $thumbnailName = $matches[3]; $format = $matches[6]; if ($asset = Asset::getById($assetId)) { try { $page = 1; $thumbnailFile = null; $thumbnailConfig = null; $deferredConfigId = "thumb_" . $assetId . "__" . md5($request->getPathInfo()); if ($thumbnailConfigItem = TmpStore::get($deferredConfigId)) { $thumbnailConfig = $thumbnailConfigItem->getData(); TmpStore::delete($deferredConfigId); if (!$thumbnailConfig instanceof Asset\Image\Thumbnail\Config) { throw new \Exception("Deferred thumbnail config file doesn't contain a valid \\Asset\\Image\\Thumbnail\\Config object"); } $tmpPage = array_pop(explode("-", $thumbnailName)); if (is_numeric($tmpPage)) { $page = $tmpPage; } } else { //get thumbnail for e.g. pdf page thumb__document_pdfPage-5 if (preg_match("|document_(.*)\\-(\\d+)\$|", $thumbnailName, $matchesThumbs)) { $thumbnailName = $matchesThumbs[1]; $page = (int) $matchesThumbs[2]; } // just check if the thumbnail exists -> throws exception otherwise $thumbnailConfig = Asset\Image\Thumbnail\Config::getByName($thumbnailName); } if ($asset instanceof Asset\Document) { $thumbnailConfig->setName(preg_replace("/\\-[\\d]+/", "", $thumbnailConfig->getName())); $thumbnailConfig->setName(str_replace("document_", "", $thumbnailConfig->getName())); $thumbnailFile = PIMCORE_DOCUMENT_ROOT . $asset->getImageThumbnail($thumbnailConfig, $page); } else { if ($asset instanceof Asset\Image) { //check if high res image is called if (array_key_exists(5, $matches)) { $highResFactor = (double) str_replace(array("@", "x"), "", $matches[5]); $thumbnailConfig->setHighResolution($highResFactor); } $thumbnailFile = PIMCORE_DOCUMENT_ROOT . $asset->getThumbnail($thumbnailConfig); } } if ($thumbnailFile && file_exists($thumbnailFile)) { $fileExtension = \Pimcore\File::getFileExtension($thumbnailFile); if (in_array($fileExtension, array("gif", "jpeg", "jpeg", "png", "pjpeg"))) { header("Content-Type: image/" . $fileExtension, true); } else { header("Content-Type: " . $asset->getMimetype(), true); } header("Content-Length: " . filesize($thumbnailFile), true); while (@ob_end_flush()) { } flush(); readfile($thumbnailFile); exit; } } catch (\Exception $e) { // nothing to do \Logger::error("Thumbnail with name '" . $thumbnailName . "' doesn't exist"); } } } }
/** * Returns the configuration for the image thumbnail with the given ID. */ public function imageThumbnailAction() { $this->checkUserPermission("thumbnails"); try { $id = $this->getParam("id"); if ($id) { $config = Asset\Image\Thumbnail\Config::getByName($id); $this->encoder->encode(array("success" => true, "data" => $config->getForWebserviceExport())); return; } } catch (\Exception $e) { \Logger::error($e); $this->encoder->encode(array("success" => false, "msg" => (string) $e)); } $this->encoder->encode(array("success" => false)); }
public function thumbnailUpdateAction() { $this->checkPermission("thumbnails"); $pipe = Asset\Image\Thumbnail\Config::getByName($this->getParam("name")); $settingsData = \Zend_Json::decode($this->getParam("settings")); $mediaData = \Zend_Json::decode($this->getParam("medias")); foreach ($settingsData as $key => $value) { $setter = "set" . ucfirst($key); if (method_exists($pipe, $setter)) { $pipe->{$setter}($value); } } $pipe->resetItems(); foreach ($mediaData as $mediaName => $items) { foreach ($items as $item) { $type = $item["type"]; unset($item["type"]); $pipe->addItem($type, $item, $mediaName); } } $pipe->save(); $this->deleteThumbnailTmpFiles($pipe); $this->_helper->json(array("success" => true)); }
/** * @param \Zend_Controller_Request_Abstract $request */ public function routeStartup(\Zend_Controller_Request_Abstract $request) { // this is a filter which checks for common used files (by browser, crawlers, ...) and prevent the default // error page, because this is more resource-intensive than exiting right here if (preg_match("@/image-thumbnails(.*)?/([0-9]+)/thumb__([a-zA-Z0-9_\\-]+)([^\\@]+)(\\@[0-9.]+x)?\\.([a-zA-Z]{2,5})@", rawurldecode($request->getPathInfo()), $matches)) { $assetId = $matches[2]; $thumbnailName = $matches[3]; if ($asset = Asset::getById($assetId)) { try { $page = 1; // default $thumbnailFile = null; $thumbnailConfig = null; //get thumbnail for e.g. pdf page thumb__document_pdfPage-5 if (preg_match("|document_(.*)\\-(\\d+)\$|", $thumbnailName, $matchesThumbs)) { $thumbnailName = $matchesThumbs[1]; $page = (int) $matchesThumbs[2]; } // just check if the thumbnail exists -> throws exception otherwise $thumbnailConfig = Asset\Image\Thumbnail\Config::getByName($thumbnailName); if (!$thumbnailConfig) { // check if there's an item in the TmpStore $deferredConfigId = "thumb_" . $assetId . "__" . md5($matches[0]); if ($thumbnailConfigItem = TmpStore::get($deferredConfigId)) { $thumbnailConfig = $thumbnailConfigItem->getData(); TmpStore::delete($deferredConfigId); if (!$thumbnailConfig instanceof Asset\Image\Thumbnail\Config) { throw new \Exception("Deferred thumbnail config file doesn't contain a valid \\Asset\\Image\\Thumbnail\\Config object"); } } } if (!$thumbnailConfig) { throw new \Exception("Thumbnail '" . $thumbnailName . "' file doesn't exists"); } if ($asset instanceof Asset\Document) { $thumbnailConfig->setName(preg_replace("/\\-[\\d]+/", "", $thumbnailConfig->getName())); $thumbnailConfig->setName(str_replace("document_", "", $thumbnailConfig->getName())); $thumbnailFile = $asset->getImageThumbnail($thumbnailConfig, $page)->getFileSystemPath(); } elseif ($asset instanceof Asset\Image) { //check if high res image is called if (array_key_exists(5, $matches)) { $highResFactor = (double) str_replace(["@", "x"], "", $matches[5]); $thumbnailConfig->setHighResolution($highResFactor); } // check if a media query thumbnail was requested if (preg_match("#~\\-~([\\d]+w)#", $matches[4], $mediaQueryResult)) { $thumbnailConfig->selectMedia($mediaQueryResult[1]); } $thumbnailFile = $asset->getThumbnail($thumbnailConfig)->getFileSystemPath(); } if ($thumbnailFile && file_exists($thumbnailFile)) { // set appropriate caching headers // see also: https://github.com/pimcore/pimcore/blob/1931860f0aea27de57e79313b2eb212dcf69ef13/.htaccess#L86-L86 $lifetime = 86400 * 7; // 1 week lifetime, same as direct delivery in .htaccess header("Cache-Control: public, max-age=" . $lifetime); header("Expires: " . date("D, d M Y H:i:s T", time() + $lifetime)); $fileExtension = \Pimcore\File::getFileExtension($thumbnailFile); if (in_array($fileExtension, ["gif", "jpeg", "jpeg", "png", "pjpeg"])) { header("Content-Type: image/" . $fileExtension, true); } else { header("Content-Type: " . $asset->getMimetype(), true); } header("Content-Length: " . filesize($thumbnailFile), true); while (@ob_end_flush()) { } flush(); readfile($thumbnailFile); exit; } } catch (\Exception $e) { // nothing to do Logger::error("Thumbnail with name '" . $thumbnailName . "' doesn't exist"); } } } }
/** * @param $asset * @param Config $config * @param null $fileSystemPath * @param bool $deferred deferred means that the image will be generated on-the-fly (details see below) * @param bool $returnAbsolutePath * @param bool $generated * @return mixed|string */ public static function process($asset, Config $config, $fileSystemPath = null, $deferred = false, $returnAbsolutePath = false, &$generated = false) { $generated = false; $errorImage = PIMCORE_PATH . "/static6/img/filetype-not-supported.png"; $format = strtolower($config->getFormat()); $contentOptimizedFormat = false; if (!$fileSystemPath && $asset instanceof Asset) { $fileSystemPath = $asset->getFileSystemPath(); } if ($asset instanceof Asset) { $id = $asset->getId(); } else { $id = "dyn~" . crc32($fileSystemPath); } $fileExt = File::getFileExtension(basename($fileSystemPath)); // simple detection for source type if SOURCE is selected if ($format == "source" || empty($format)) { $format = self::getAllowedFormat($fileExt, ["jpeg", "gif", "png"], "png"); $contentOptimizedFormat = true; // format can change depending of the content (alpha-channel, ...) } if ($format == "print") { $format = self::getAllowedFormat($fileExt, ["svg", "jpeg", "png", "tiff"], "png"); if (($format == "tiff" || $format == "svg") && \Pimcore\Tool::isFrontentRequestByAdmin()) { // return a webformat in admin -> tiff cannot be displayed in browser $format = "png"; } elseif ($format == "tiff") { $transformations = $config->getItems(); if (is_array($transformations) && count($transformations) > 0) { foreach ($transformations as $transformation) { if (!empty($transformation)) { if ($transformation["method"] == "tifforiginal") { return self::returnPath($fileSystemPath, $returnAbsolutePath); } } } } } elseif ($format == "svg") { return self::returnPath($fileSystemPath, $returnAbsolutePath); } } $thumbDir = $asset->getImageThumbnailSavePath() . "/thumb__" . $config->getName(); $filename = preg_replace("/\\." . preg_quote(File::getFileExtension($asset->getFilename())) . "/", "", $asset->getFilename()); // add custom suffix if available if ($config->getFilenameSuffix()) { $filename .= "~-~" . $config->getFilenameSuffix(); } // add high-resolution modifier suffix to the filename if ($config->getHighResolution() > 1) { $filename .= "@" . $config->getHighResolution() . "x"; } $filename .= "." . $format; $fsPath = $thumbDir . "/" . $filename; // deferred means that the image will be generated on-the-fly (when requested by the browser) // the configuration is saved for later use in Pimcore\Controller\Plugin\Thumbnail::routeStartup() // so that it can be used also with dynamic configurations if ($deferred) { // only add the config to the TmpStore if necessary (the config is auto-generated) if (!Config::getByName($config->getName())) { $configId = "thumb_" . $id . "__" . md5(str_replace(PIMCORE_TEMPORARY_DIRECTORY, "", $fsPath)); TmpStore::add($configId, $config, "thumbnail_deferred"); } return self::returnPath($fsPath, $returnAbsolutePath); } // all checks on the file system should be below the deferred part for performance reasons (remote file systems) if (!file_exists($fileSystemPath)) { return self::returnPath($errorImage, $returnAbsolutePath); } if (!is_dir(dirname($fsPath))) { File::mkdir(dirname($fsPath)); } $path = self::returnPath($fsPath, false); // check for existing and still valid thumbnail if (is_file($fsPath) and filemtime($fsPath) >= filemtime($fileSystemPath)) { return self::returnPath($fsPath, $returnAbsolutePath); } // transform image $image = Asset\Image::getImageTransformInstance(); if (!$image->load($fileSystemPath)) { return self::returnPath($errorImage, $returnAbsolutePath); } $image->setUseContentOptimizedFormat($contentOptimizedFormat); $startTime = StopWatch::microtime_float(); $transformations = $config->getItems(); // check if the original image has an orientation exif flag // if so add a transformation at the beginning that rotates and/or mirrors the image if (function_exists("exif_read_data")) { $exif = @exif_read_data($fileSystemPath); if (is_array($exif)) { if (array_key_exists("Orientation", $exif)) { $orientation = intval($exif["Orientation"]); if ($orientation > 1) { $angleMappings = [2 => 180, 3 => 180, 4 => 180, 5 => 90, 6 => 90, 7 => 90, 8 => 270]; if (array_key_exists($orientation, $angleMappings)) { array_unshift($transformations, ["method" => "rotate", "arguments" => ["angle" => $angleMappings[$orientation]]]); } // values that have to be mirrored, this is not very common, but should be covered anyway $mirrorMappings = [2 => "vertical", 4 => "horizontal", 5 => "vertical", 7 => "horizontal"]; if (array_key_exists($orientation, $mirrorMappings)) { array_unshift($transformations, ["method" => "mirror", "arguments" => ["mode" => $mirrorMappings[$orientation]]]); } } } } } if (is_array($transformations) && count($transformations) > 0) { $sourceImageWidth = PHP_INT_MAX; $sourceImageHeight = PHP_INT_MAX; if ($asset instanceof Asset\Image) { $sourceImageWidth = $asset->getWidth(); $sourceImageHeight = $asset->getHeight(); } $highResFactor = $config->getHighResolution(); $calculateMaxFactor = function ($factor, $original, $new) { $newFactor = $factor * $original / $new; if ($newFactor < 1) { // don't go below factor 1 $newFactor = 1; } return $newFactor; }; // sorry for the goto/label - but in this case it makes life really easier and the code more readable prepareTransformations: foreach ($transformations as $transformation) { if (!empty($transformation)) { $arguments = []; $mapping = self::$argumentMapping[$transformation["method"]]; if (is_array($transformation["arguments"])) { foreach ($transformation["arguments"] as $key => $value) { $position = array_search($key, $mapping); if ($position !== false) { // high res calculations if enabled if (!in_array($transformation["method"], ["cropPercent"]) && in_array($key, ["width", "height", "x", "y"])) { if ($highResFactor && $highResFactor > 1) { $value *= $highResFactor; // check if source image is big enough otherwise adjust the high-res factor if (in_array($key, ["width", "x"])) { if ($sourceImageWidth < $value) { $highResFactor = $calculateMaxFactor($highResFactor, $sourceImageWidth, $value); goto prepareTransformations; } } elseif (in_array($key, ["height", "y"])) { if ($sourceImageHeight < $value) { $highResFactor = $calculateMaxFactor($highResFactor, $sourceImageHeight, $value); goto prepareTransformations; } } } } $arguments[$position] = $value; } } } ksort($arguments); call_user_func_array([$image, $transformation["method"]], $arguments); } } } $image->save($fsPath, $format, $config->getQuality()); $generated = true; if ($contentOptimizedFormat) { $tmpStoreKey = str_replace(PIMCORE_TEMPORARY_DIRECTORY . "/", "", $fsPath); TmpStore::add($tmpStoreKey, "-", "image-optimize-queue"); } clearstatcache(); \Logger::debug("Thumbnail " . $path . " generated in " . (StopWatch::microtime_float() - $startTime) . " seconds"); // set proper permissions @chmod($fsPath, File::getDefaultMode()); // quick bugfix / workaround, it seems that imagemagick / image optimizers creates sometimes empty PNG chunks (total size 33 bytes) // no clue why it does so as this is not continuous reproducible, and this is the only fix we can do for now // if the file is corrupted the file will be created on the fly when requested by the browser (because it's deleted here) if (is_file($fsPath) && filesize($fsPath) < 50) { unlink($fsPath); } return self::returnPath($fsPath, $returnAbsolutePath); }
// create legacy config folder $legacyFolder = PIMCORE_CONFIGURATION_DIRECTORY . "/LEGACY"; if (!is_dir($legacyFolder)) { mkdir($legacyFolder, 0777, true); } // IMAGE THUMBNAILS $dir = PIMCORE_CONFIGURATION_DIRECTORY . "/imagepipelines"; if (is_dir($dir)) { $file = Pimcore\Config::locateConfigFile("image-thumbnails"); $json = \Pimcore\Db\JsonFileTable::get($file); $json->truncate(); $files = scandir($dir); foreach ($files as $file) { if (strpos($file, ".xml")) { $name = str_replace(".xml", "", $file); $thumbnail = Asset\Image\Thumbnail\Config::getByName($name); $thumbnail = object2array($thumbnail); $thumbnail["id"] = $thumbnail["name"]; unset($thumbnail["name"]); $json->insertOrUpdate($thumbnail, $thumbnail["id"]); } } // move data rename($dir, $legacyFolder . "/imagepipelines"); } // VIDEO THUMBNAILS $dir = PIMCORE_CONFIGURATION_DIRECTORY . "/videopipelines"; if (is_dir($dir)) { $file = Pimcore\Config::locateConfigFile("video-thumbnails"); $json = \Pimcore\Db\JsonFileTable::get($file); $json->truncate();