diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/base.php | 9 | ||||
-rw-r--r-- | lib/helper.php | 15 | ||||
-rwxr-xr-x | lib/preview.php | 797 | ||||
-rw-r--r-- | lib/preview/images.php | 33 | ||||
-rw-r--r-- | lib/preview/libreoffice-cl.php | 127 | ||||
-rw-r--r-- | lib/preview/movies.php | 43 | ||||
-rw-r--r-- | lib/preview/mp3.php | 46 | ||||
-rw-r--r-- | lib/preview/msoffice.php | 142 | ||||
-rw-r--r-- | lib/preview/office.php | 17 | ||||
-rw-r--r-- | lib/preview/pdf.php | 40 | ||||
-rw-r--r-- | lib/preview/provider.php | 19 | ||||
-rw-r--r-- | lib/preview/svg.php | 44 | ||||
-rw-r--r-- | lib/preview/txt.php | 69 | ||||
-rw-r--r-- | lib/preview/unknown.php | 43 | ||||
-rw-r--r-- | lib/public/preview.php | 34 | ||||
-rw-r--r-- | lib/public/template.php | 19 | ||||
-rw-r--r-- | lib/template.php | 3 | ||||
-rw-r--r-- | lib/template/functions.php | 16 |
18 files changed, 1513 insertions, 3 deletions
diff --git a/lib/base.php b/lib/base.php index eaee8424651..b51becc4d93 100644 --- a/lib/base.php +++ b/lib/base.php @@ -487,6 +487,7 @@ class OC { self::registerCacheHooks(); self::registerFilesystemHooks(); + self::registerPreviewHooks(); self::registerShareHooks(); //make sure temporary files are cleaned up @@ -559,6 +560,14 @@ class OC { } /** + * register hooks for previews + */ + public static function registerPreviewHooks() { + OC_Hook::connect('OC_Filesystem', 'post_write', 'OC\Preview', 'post_write'); + OC_Hook::connect('OC_Filesystem', 'delete', 'OC\Preview', 'post_delete'); + } + + /** * register hooks for sharing */ public static function registerShareHooks() { diff --git a/lib/helper.php b/lib/helper.php index ca508e1d933..460e5679b02 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -223,6 +223,21 @@ class OC_Helper { } /** + * @brief get path to preview of file + * @param string $path path + * @return string the url + * + * Returns the path to the preview of the file. + */ + public static function previewIcon($path) { + return self::linkToRoute( 'core_ajax_preview', array('x' => 36, 'y' => 36, 'file' => urlencode($path) )); + } + + public static function publicPreview_icon( $path, $token ) { + return self::linkToRoute( 'core_ajax_public_preview', array('x' => 36, 'y' => 36, 'file' => urlencode($path), 't' => $token)); + } + + /** * @brief Make a human file size * @param int $bytes file size in bytes * @return string a human readable file size diff --git a/lib/preview.php b/lib/preview.php new file mode 100755 index 00000000000..245ad64014e --- /dev/null +++ b/lib/preview.php @@ -0,0 +1,797 @@ +<?php +/** + * Copyright (c) 2013 Frank Karlitschek frank@owncloud.org + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + * Thumbnails: + * structure of filename: + * /data/user/thumbnails/pathhash/x-y.png + * + */ +namespace OC; + +require_once('preview/images.php'); +require_once('preview/movies.php'); +require_once('preview/mp3.php'); +require_once('preview/pdf.php'); +require_once('preview/svg.php'); +require_once('preview/txt.php'); +require_once('preview/unknown.php'); +require_once('preview/office.php'); + +class Preview { + //the thumbnail folder + const THUMBNAILS_FOLDER = 'thumbnails'; + + //config + private $maxScaleFactor; + private $configMaxX; + private $configMaxY; + + //fileview object + private $fileview = null; + private $userview = null; + + //vars + private $file; + private $maxX; + private $maxY; + private $scalingup; + + //preview images object + private $preview; + + //preview providers + static private $providers = array(); + static private $registeredProviders = array(); + + /** + * @brief check if thumbnail or bigger version of thumbnail of file is cached + * @param string $user userid - if no user is given, OC_User::getUser will be used + * @param string $root path of root + * @param string $file The path to the file where you want a thumbnail from + * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image + * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image + * @param bool $scalingup Disable/Enable upscaling of previews + * @return mixed (bool / string) + * false if thumbnail does not exist + * path to thumbnail if thumbnail exists + */ + public function __construct($user='', $root='/', $file='', $maxX=1, $maxY=1, $scalingup=true) { + //set config + $this->configMaxX = \OC_Config::getValue('preview_max_x', null); + $this->configMaxY = \OC_Config::getValue('preview_max_y', null); + $this->maxScaleFactor = \OC_Config::getValue('preview_max_scale_factor', 2); + + //save parameters + $this->setFile($file); + $this->setMaxX($maxX); + $this->setMaxY($maxY); + $this->setScalingUp($scalingup); + + //init fileviews + if($user === ''){ + $user = OC_User::getUser(); + } + $this->fileview = new \OC\Files\View('/' . $user . '/' . $root); + $this->userview = new \OC\Files\View('/' . $user); + + $this->preview = null; + + //check if there are preview backends + if(empty(self::$providers)) { + self::initProviders(); + } + + if(empty(self::$providers)) { + \OC_Log::write('core', 'No preview providers exist', \OC_Log::ERROR); + throw new \Exception('No preview providers'); + } + } + + /** + * @brief returns the path of the file you want a thumbnail from + * @return string + */ + public function getFile() { + return $this->file; + } + + /** + * @brief returns the max width of the preview + * @return integer + */ + public function getMaxX() { + return $this->maxX; + } + + /** + * @brief returns the max height of the preview + * @return integer + */ + public function getMaxY() { + return $this->maxY; + } + + /** + * @brief returns whether or not scalingup is enabled + * @return bool + */ + public function getScalingup() { + return $this->scalingup; + } + + /** + * @brief returns the name of the thumbnailfolder + * @return string + */ + public function getThumbnailsFolder() { + return self::THUMBNAILS_FOLDER; + } + + /** + * @brief returns the max scale factor + * @return integer + */ + public function getMaxScaleFactor() { + return $this->maxScaleFactor; + } + + /** + * @brief returns the max width set in ownCloud's config + * @return integer + */ + public function getConfigMaxX() { + return $this->configMaxX; + } + + /** + * @brief returns the max height set in ownCloud's config + * @return integer + */ + public function getConfigMaxY() { + return $this->configMaxY; + } + + /** + * @brief set the path of the file you want a thumbnail from + * @param string $file + * @return $this + */ + public function setFile($file) { + $this->file = $file; + return $this; + } + + /** + * @brief set the the max width of the preview + * @param int $maxX + * @return $this + */ + public function setMaxX($maxX=1) { + if($maxX === 0) { + throw new \Exception('Cannot set width of 0!'); + } + $configMaxX = $this->getConfigMaxX(); + if(!is_null($configMaxX)) { + if($maxX > $configMaxX) { + \OC_Log::write('core', 'maxX reduced from ' . $maxX . ' to ' . $configMaxX, \OC_Log::DEBUG); + $maxX = $configMaxX; + } + } + $this->maxX = $maxX; + return $this; + } + + /** + * @brief set the the max height of the preview + * @param int $maxY + * @return $this + */ + public function setMaxY($maxY=1) { + if($maxY === 0) { + throw new \Exception('Cannot set height of 0!'); + } + $configMaxY = $this->getConfigMaxY(); + if(!is_null($configMaxY)) { + if($maxY > $configMaxY) { + \OC_Log::write('core', 'maxX reduced from ' . $maxY . ' to ' . $configMaxY, \OC_Log::DEBUG); + $maxY = $configMaxY; + } + } + $this->maxY = $maxY; + return $this; + } + + /** + * @brief set whether or not scalingup is enabled + * @param bool $scalingup + * @return $this + */ + public function setScalingup($scalingup) { + if($this->getMaxScaleFactor() === 1) { + $scalingup = false; + } + $this->scalingup = $scalingup; + return $this; + } + + /** + * @brief check if all parameters are valid + * @return bool + */ + public function isFileValid() { + $file = $this->getFile(); + if($file === '') { + \OC_Log::write('core', 'No filename passed', \OC_Log::ERROR); + return false; + } + + if(!$this->fileview->file_exists($file)) { + \OC_Log::write('core', 'File:"' . $file . '" not found', \OC_Log::ERROR); + return false; + } + + return true; + } + + /** + * @brief deletes previews of a file with specific x and y + * @return bool + */ + public function deletePreview() { + $file = $this->getFile(); + + $fileinfo = $this->fileview->getFileInfo($file); + $fileid = $fileinfo['fileid']; + + $previewpath = $this->getThumbnailsFolder() . '/' . $fileid . '/' . $this->getMaxX() . '-' . $this->getMaxY() . '.png'; + $this->userview->unlink($previewpath); + return !$this->userview->file_exists($previewpath); + } + + /** + * @brief deletes all previews of a file + * @return bool + */ + public function deleteAllPreviews() { + $file = $this->getFile(); + + $fileinfo = $this->fileview->getFileInfo($file); + $fileid = $fileinfo['fileid']; + + $previewpath = $this->getThumbnailsFolder() . '/' . $fileid . '/'; + $this->userview->deleteAll($previewpath); + $this->userview->rmdir($previewpath); + return !$this->userview->is_dir($previewpath); + } + + /** + * @brief check if thumbnail or bigger version of thumbnail of file is cached + * @return mixed (bool / string) + * false if thumbnail does not exist + * path to thumbnail if thumbnail exists + */ + private function isCached() { + $file = $this->getFile(); + $maxX = $this->getMaxX(); + $maxY = $this->getMaxY(); + $scalingup = $this->getScalingup(); + $maxscalefactor = $this->getMaxScaleFactor(); + + $fileinfo = $this->fileview->getFileInfo($file); + $fileid = $fileinfo['fileid']; + + if(is_null($fileid)) { + return false; + } + + $previewpath = $this->getThumbnailsFolder() . '/' . $fileid . '/'; + if(!$this->userview->is_dir($previewpath)) { + return false; + } + + //does a preview with the wanted height and width already exist? + if($this->userview->file_exists($previewpath . $maxX . '-' . $maxY . '.png')) { + return $previewpath . $maxX . '-' . $maxY . '.png'; + } + + $wantedaspectratio = (float) ($maxX / $maxY); + + //array for usable cached thumbnails + $possiblethumbnails = array(); + + $allthumbnails = $this->userview->getDirectoryContent($previewpath); + foreach($allthumbnails as $thumbnail) { + $name = rtrim($thumbnail['name'], '.png'); + $size = explode('-', $name); + $x = (int) $size[0]; + $y = (int) $size[1]; + + $aspectratio = (float) ($x / $y); + if($aspectratio !== $wantedaspectratio) { + continue; + } + + if($x < $maxX || $y < $maxY) { + if($scalingup) { + $scalefactor = $maxX / $x; + if($scalefactor > $maxscalefactor) { + continue; + } + }else{ + continue; + } + } + $possiblethumbnails[$x] = $thumbnail['path']; + } + + if(count($possiblethumbnails) === 0) { + return false; + } + + if(count($possiblethumbnails) === 1) { + return current($possiblethumbnails); + } + + ksort($possiblethumbnails); + + if(key(reset($possiblethumbnails)) > $maxX) { + return current(reset($possiblethumbnails)); + } + + if(key(end($possiblethumbnails)) < $maxX) { + return current(end($possiblethumbnails)); + } + + foreach($possiblethumbnails as $width => $path) { + if($width < $maxX) { + continue; + }else{ + return $path; + } + } + } + + /** + * @brief return a preview of a file + * @return image + */ + public function getPreview() { + if(!is_null($this->preview) && $this->preview->valid()){ + return $this->preview; + } + + $this->preview = null; + $file = $this->getFile(); + $maxX = $this->getMaxX(); + $maxY = $this->getMaxY(); + $scalingup = $this->getScalingup(); + + $fileinfo = $this->fileview->getFileInfo($file); + $fileid = $fileinfo['fileid']; + + $cached = $this->isCached(); + + if($cached) { + $image = new \OC_Image($this->userview->file_get_contents($cached, 'r')); + $this->preview = $image->valid() ? $image : null; + $this->resizeAndCrop(); + } + + if(is_null($this->preview)) { + $mimetype = $this->fileview->getMimeType($file); + $preview = null; + + foreach(self::$providers as $supportedmimetype => $provider) { + if(!preg_match($supportedmimetype, $mimetype)) { + continue; + } + + $preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingup, $this->fileview); + + if(!($preview instanceof \OC_Image)) { + continue; + } + + $this->preview = $preview; + $this->resizeAndCrop(); + + $previewpath = $this->getThumbnailsFolder() . '/' . $fileid . '/'; + $cachepath = $previewpath . $maxX . '-' . $maxY . '.png'; + + if($this->userview->is_dir($this->getThumbnailsFolder() . '/') === false) { + $this->userview->mkdir($this->getThumbnailsFolder() . '/'); + } + + if($this->userview->is_dir($previewpath) === false) { + $this->userview->mkdir($previewpath); + } + + $this->userview->file_put_contents($cachepath, $preview->data()); + + break; + } + } + + if(is_null($this->preview)) { + $this->preview = new \OC_Image(); + } + + return $this->preview; + } + + /** + * @brief show preview + * @return void + */ + public function showPreview() { + \OCP\Response::enableCaching(3600 * 24); // 24 hours + if(is_null($this->preview)) { + $this->getPreview(); + } + $this->preview->show(); + return; + } + + /** + * @brief show preview + * @return void + */ + public function show() { + return $this->showPreview(); + } + + /** + * @brief resize, crop and fix orientation + * @return image + */ + private function resizeAndCrop() { + $image = $this->preview; + $x = $this->getMaxX(); + $y = $this->getMaxY(); + $scalingup = $this->getScalingup(); + $maxscalefactor = $this->getMaxScaleFactor(); + + if(!($image instanceof \OC_Image)) { + \OC_Log::write('core', '$this->preview is not an instance of OC_Image', \OC_Log::DEBUG); + return; + } + + $image->fixOrientation(); + + $realx = (int) $image->width(); + $realy = (int) $image->height(); + + if($x === $realx && $y === $realy) { + $this->preview = $image; + return true; + } + + $factorX = $x / $realx; + $factorY = $y / $realy; + + if($factorX >= $factorY) { + $factor = $factorX; + }else{ + $factor = $factorY; + } + + if($scalingup === false) { + if($factor > 1) { + $factor = 1; + } + } + + if(!is_null($maxscalefactor)) { + if($factor > $maxscalefactor) { + \OC_Log::write('core', 'scalefactor reduced from ' . $factor . ' to ' . $maxscalefactor, \OC_Log::DEBUG); + $factor = $maxscalefactor; + } + } + + $newXsize = (int) ($realx * $factor); + $newYsize = (int) ($realy * $factor); + + $image->preciseResize($newXsize, $newYsize); + + if($newXsize === $x && $newYsize === $y) { + $this->preview = $image; + return; + } + + if($newXsize >= $x && $newYsize >= $y) { + $cropX = floor(abs($x - $newXsize) * 0.5); + //don't crop previews on the Y axis, this sucks if it's a document. + //$cropY = floor(abs($y - $newYsize) * 0.5); + $cropY = 0; + + $image->crop($cropX, $cropY, $x, $y); + + $this->preview = $image; + return; + } + + if($newXsize < $x || $newYsize < $y) { + if($newXsize > $x) { + $cropX = floor(($newXsize - $x) * 0.5); + $image->crop($cropX, 0, $x, $newYsize); + } + + if($newYsize > $y) { + $cropY = floor(($newYsize - $y) * 0.5); + $image->crop(0, $cropY, $newXsize, $y); + } + + $newXsize = (int) $image->width(); + $newYsize = (int) $image->height(); + + //create transparent background layer + $backgroundlayer = imagecreatetruecolor($x, $y); + $white = imagecolorallocate($backgroundlayer, 255, 255, 255); + imagefill($backgroundlayer, 0, 0, $white); + + $image = $image->resource(); + + $mergeX = floor(abs($x - $newXsize) * 0.5); + $mergeY = floor(abs($y - $newYsize) * 0.5); + + imagecopy($backgroundlayer, $image, $mergeX, $mergeY, 0, 0, $newXsize, $newYsize); + + //$black = imagecolorallocate(0,0,0); + //imagecolortransparent($transparentlayer, $black); + + $image = new \OC_Image($backgroundlayer); + + $this->preview = $image; + return; + } + } + + /** + * @brief register a new preview provider to be used + * @param string $provider class name of a Preview_Provider + * @param array $options + * @return void + */ + public static function registerProvider($class, $options=array()) { + self::$registeredProviders[]=array('class'=>$class, 'options'=>$options); + } + + /** + * @brief create instances of all the registered preview providers + * @return void + */ + private static function initProviders() { + if(count(self::$providers)>0) { + return; + } + + foreach(self::$registeredProviders as $provider) { + $class=$provider['class']; + $options=$provider['options']; + + $object = new $class($options); + + self::$providers[$object->getMimeType()] = $object; + } + + $keys = array_map('strlen', array_keys(self::$providers)); + array_multisort($keys, SORT_DESC, self::$providers); + } + + /** + * @brief method that handles preview requests from users that are logged in + * @return void + */ + public static function previewRouter() { + \OC_Util::checkLoggedIn(); + + $file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : ''; + $maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '36'; + $maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '36'; + $scalingup = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; + + if($file === '') { + \OC_Response::setStatus(400); //400 Bad Request + \OC_Log::write('core-preview', 'No file parameter was passed', \OC_Log::DEBUG); + self::showErrorPreview(); + exit; + } + + if($maxX === 0 || $maxY === 0) { + \OC_Response::setStatus(400); //400 Bad Request + \OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG); + self::showErrorPreview(); + exit; + } + + try{ + $preview = new Preview(\OC_User::getUser(), 'files'); + $preview->setFile($file); + $preview->setMaxX($maxX); + $preview->setMaxY($maxY); + $preview->setScalingUp($scalingup); + + $preview->show(); + }catch(\Exception $e) { + \OC_Response::setStatus(500); + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + self::showErrorPreview(); + exit; + } + } + + /** + * @brief method that handles preview requests from users that are not logged in / view shared folders that are public + * @return void + */ + public static function publicPreviewRouter() { + if(!\OC_App::isEnabled('files_sharing')){ + exit; + } + + $file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : ''; + $maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '36'; + $maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '36'; + $scalingup = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; + $token = array_key_exists('t', $_GET) ? (string) $_GET['t'] : ''; + + if($token === ''){ + \OC_Response::setStatus(400); //400 Bad Request + \OC_Log::write('core-preview', 'No token parameter was passed', \OC_Log::DEBUG); + self::showErrorPreview(); + exit; + } + + $linkedItem = \OCP\Share::getShareByToken($token); + if($linkedItem === false || ($linkedItem['item_type'] !== 'file' && $linkedItem['item_type'] !== 'folder')) { + \OC_Response::setStatus(404); + \OC_Log::write('core-preview', 'Passed token parameter is not valid', \OC_Log::DEBUG); + self::showErrorPreview(); + exit; + } + + if(!isset($linkedItem['uid_owner']) || !isset($linkedItem['file_source'])) { + \OC_Response::setStatus(500); + \OC_Log::write('core-preview', 'Passed token seems to be valid, but it does not contain all necessary information . ("' . $token . '")'); + self::showErrorPreview(); + exit; + } + + $userid = $linkedItem['uid_owner']; + \OC_Util::setupFS($userid); + + $pathid = $linkedItem['file_source']; + $path = \OC\Files\Filesystem::getPath($pathid); + $pathinfo = \OC\Files\Filesystem::getFileInfo($path); + $sharedfile = null; + + if($linkedItem['item_type'] === 'folder') { + $isvalid = \OC\Files\Filesystem::isValidPath($file); + if(!$isvalid) { + \OC_Response::setStatus(400); //400 Bad Request + \OC_Log::write('core-preview', 'Passed filename is not valid, might be malicious (file:"' . $file . '";ip:"' . $_SERVER['REMOTE_ADDR'] . '")', \OC_Log::WARN); + self::showErrorPreview(); + exit; + } + $sharedfile = \OC\Files\Filesystem::normalizePath($file); + } + + if($linkedItem['item_type'] === 'file') { + $parent = $pathinfo['parent']; + $path = \OC\Files\Filesystem::getPath($parent); + $sharedfile = $pathinfo['name']; + } + + $path = \OC\Files\Filesystem::normalizePath($path, false); + if(substr($path, 0, 1) === '/') { + $path = substr($path, 1); + } + + if($maxX === 0 || $maxY === 0) { + \OC_Response::setStatus(400); //400 Bad Request + \OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG); + self::showErrorPreview(); + exit; + } + + $root = 'files/' . $path; + + try{ + $preview = new Preview($userid, $root); + $preview->setFile($file); + $preview->setMaxX($maxX); + $preview->setMaxY($maxY); + $preview->setScalingUp($scalingup); + + $preview->show(); + }catch(\Exception $e) { + \OC_Response::setStatus(500); + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + self::showErrorPreview(); + exit; + } + } + + public static function trashbinPreviewRouter() { + \OC_Util::checkLoggedIn(); + + if(!\OC_App::isEnabled('files_trashbin')){ + exit; + } + + $file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : ''; + $maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '44'; + $maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '44'; + $scalingup = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; + + if($file === '') { + \OC_Response::setStatus(400); //400 Bad Request + \OC_Log::write('core-preview', 'No file parameter was passed', \OC_Log::DEBUG); + self::showErrorPreview(); + exit; + } + + if($maxX === 0 || $maxY === 0) { + \OC_Response::setStatus(400); //400 Bad Request + \OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG); + self::showErrorPreview(); + exit; + } + + try{ + $preview = new Preview(\OC_User::getUser(), 'files_trashbin/files'); + $preview->setFile($file); + $preview->setMaxX($maxX); + $preview->setMaxY($maxY); + $preview->setScalingUp($scalingup); + + $preview->showPreview(); + }catch(\Exception $e) { + \OC_Response::setStatus(500); + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + self::showErrorPreview(); + exit; + } + } + + public static function post_write($args) { + self::post_delete($args); + } + + public static function post_delete($args) { + $path = $args['path']; + if(substr($path, 0, 1) === '/') { + $path = substr($path, 1); + } + $preview = new Preview(\OC_User::getUser(), 'files/', $path, 0, 0, false, true); + $preview->deleteAllPreviews(); + } + + public static function isMimeSupported($mimetype) { + //check if there are preview backends + if(empty(self::$providers)) { + self::initProviders(); + } + + //remove last element because it has the mimetype * + $providers = array_slice(self::$providers, 0, -1); + foreach($providers as $supportedmimetype => $provider) { + if(preg_match($supportedmimetype, $mimetype)) { + return true; + } + } + return false; + } + + private static function showErrorPreview() { + $path = \OC::$SERVERROOT . '/core/img/actions/delete.png'; + $preview = new \OC_Image($path); + $preview->preciseResize(36, 36); + $preview->show(); + } +}
\ No newline at end of file diff --git a/lib/preview/images.php b/lib/preview/images.php new file mode 100644 index 00000000000..987aa9aef0a --- /dev/null +++ b/lib/preview/images.php @@ -0,0 +1,33 @@ +<?php +/** + * Copyright (c) 2013 Frank Karlitschek frank@owncloud.org + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Preview; + +class Image extends Provider { + + public function getMimeType() { + return '/image\/.*/'; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + //get fileinfo + $fileinfo = $fileview->getFileInfo($path); + + //check if file is encrypted + if($fileinfo['encrypted'] === true) { + $image = new \OC_Image(stream_get_contents($fileview->fopen($path, 'r'))); + }else{ + $image = new \OC_Image(); + $image->loadFromFile($fileview->getLocalFile($path)); + } + + return $image->valid() ? $image : false; + } +} + +\OC\Preview::registerProvider('OC\Preview\Image');
\ No newline at end of file diff --git a/lib/preview/libreoffice-cl.php b/lib/preview/libreoffice-cl.php new file mode 100644 index 00000000000..2749c4867e9 --- /dev/null +++ b/lib/preview/libreoffice-cl.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Preview; + +//we need imagick to convert +class Office extends Provider { + + private $cmd; + + public function getMimeType() { + return null; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $this->initCmd(); + if(is_null($this->cmd)) { + return false; + } + + $abspath = $fileview->toTmpFile($path); + + $tmpdir = get_temp_dir(); + + $exec = $this->cmd . ' --headless --nologo --nofirststartwizard --invisible --norestore -convert-to pdf -outdir ' . escapeshellarg($tmpdir) . ' ' . escapeshellarg($abspath); + $export = 'export HOME=/' . $tmpdir; + + shell_exec($export . "\n" . $exec); + + //create imagick object from pdf + try{ + $pdf = new \imagick($abspath . '.pdf' . '[0]'); + $pdf->setImageFormat('jpg'); + }catch(\Exception $e){ + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; + } + + $image = new \OC_Image($pdf); + + unlink($abspath); + unlink($abspath . '.pdf'); + + return $image->valid() ? $image : false; + } + + private function initCmd() { + $cmd = ''; + + if(is_string(\OC_Config::getValue('preview_libreoffice_path', null))) { + $cmd = \OC_Config::getValue('preview_libreoffice_path', null); + } + + if($cmd === '' && shell_exec('libreoffice --headless --version')) { + $cmd = 'libreoffice'; + } + + if($cmd === '' && shell_exec('openoffice --headless --version')) { + $cmd = 'openoffice'; + } + + if($cmd === '') { + $cmd = null; + } + + $this->cmd = $cmd; + } +} + +//.doc, .dot +class MSOfficeDoc extends Office { + + public function getMimeType() { + return '/application\/msword/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\MSOfficeDoc'); + +//.docm, .dotm, .xls(m), .xlt(m), .xla(m), .ppt(m), .pot(m), .pps(m), .ppa(m) +class MSOffice2003 extends Office { + + public function getMimeType() { + return '/application\/vnd.ms-.*/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\MSOffice2003'); + +//.docx, .dotx, .xlsx, .xltx, .pptx, .potx, .ppsx +class MSOffice2007 extends Office { + + public function getMimeType() { + return '/application\/vnd.openxmlformats-officedocument.*/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\MSOffice2007'); + +//.odt, .ott, .oth, .odm, .odg, .otg, .odp, .otp, .ods, .ots, .odc, .odf, .odb, .odi, .oxt +class OpenDocument extends Office { + + public function getMimeType() { + return '/application\/vnd.oasis.opendocument.*/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\OpenDocument'); + +//.sxw, .stw, .sxc, .stc, .sxd, .std, .sxi, .sti, .sxg, .sxm +class StarOffice extends Office { + + public function getMimeType() { + return '/application\/vnd.sun.xml.*/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\StarOffice');
\ No newline at end of file diff --git a/lib/preview/movies.php b/lib/preview/movies.php new file mode 100644 index 00000000000..8531050d112 --- /dev/null +++ b/lib/preview/movies.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright (c) 2013 Frank Karlitschek frank@owncloud.org + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Preview; + +if(!is_null(shell_exec('ffmpeg -version'))) { + + class Movie extends Provider { + + public function getMimeType() { + return '/video\/.*/'; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $abspath = \OC_Helper::tmpFile(); + $tmppath = \OC_Helper::tmpFile(); + + $handle = $fileview->fopen($path, 'rb'); + + $firstmb = stream_get_contents($handle, 1048576); //1024 * 1024 = 1048576 + file_put_contents($abspath, $firstmb); + + //$cmd = 'ffmpeg -y -i ' . escapeshellarg($abspath) . ' -f mjpeg -vframes 1 -ss 1 -s ' . escapeshellarg($maxX) . 'x' . escapeshellarg($maxY) . ' ' . $tmppath; + $cmd = 'ffmpeg -an -y -i ' . escapeshellarg($abspath) . ' -f mjpeg -vframes 1 -ss 1 ' . escapeshellarg($tmppath); + + shell_exec($cmd); + + $image = new \OC_Image($tmppath); + + unlink($abspath); + unlink($tmppath); + + return $image->valid() ? $image : false; + } + } + + \OC\Preview::registerProvider('OC\Preview\Movie'); +}
\ No newline at end of file diff --git a/lib/preview/mp3.php b/lib/preview/mp3.php new file mode 100644 index 00000000000..835ff529000 --- /dev/null +++ b/lib/preview/mp3.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Preview; + +class MP3 extends Provider { + + public function getMimeType() { + return '/audio\/mpeg/'; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + require_once('getid3/getid3.php'); + + $getID3 = new \getID3(); + + $tmppath = $fileview->toTmpFile($path); + + $tags = $getID3->analyze($tmppath); + \getid3_lib::CopyTagsToComments($tags); + $picture = @$tags['id3v2']['APIC'][0]['data']; + + unlink($tmppath); + + $image = new \OC_Image($picture); + return $image->valid() ? $image : $this->getNoCoverThumbnail($maxX, $maxY); + } + + public function getNoCoverThumbnail($maxX, $maxY) { + $icon = \OC::$SERVERROOT . '/core/img/filetypes/audio.png'; + + if(!file_exists($icon)) { + return false; + } + + $image = new \OC_Image($icon); + return $image->valid() ? $image : false; + } + +} + +\OC\Preview::registerProvider('OC\Preview\MP3');
\ No newline at end of file diff --git a/lib/preview/msoffice.php b/lib/preview/msoffice.php new file mode 100644 index 00000000000..ccf1d674c7a --- /dev/null +++ b/lib/preview/msoffice.php @@ -0,0 +1,142 @@ +<?php +/** + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Preview; + +/* //There is no (good) php-only solution for converting 2003 word documents to pdfs / pngs ... +class DOC extends Provider { + + public function getMimeType() { + return '/application\/msword/'; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + require_once(''); + } + +} + +\OC\Preview::registerProvider('OC\Preview\DOC'); +*/ + +class DOCX extends Provider { + + public function getMimeType() { + return '/application\/vnd.openxmlformats-officedocument.wordprocessingml.document/'; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + require_once('phpdocx/classes/TransformDoc.inc'); + + $tmpdoc = $fileview->toTmpFile($path); + + $transformdoc = new \TransformDoc(); + $transformdoc->setStrFile($tmpdoc); + $transformdoc->generatePDF($tmpdoc); + + $pdf = new \imagick($tmpdoc . '[0]'); + $pdf->setImageFormat('jpg'); + + unlink($tmpdoc); + + $image = new \OC_Image($pdf); + + return $image->valid() ? $image : false; + } + +} + +\OC\Preview::registerProvider('OC\Preview\DOCX'); + +class MSOfficeExcel extends Provider { + + public function getMimeType() { + return null; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + require_once('PHPExcel/Classes/PHPExcel.php'); + require_once('PHPExcel/Classes/PHPExcel/IOFactory.php'); + + $abspath = $fileview->toTmpFile($path); + $tmppath = \OC_Helper::tmpFile(); + + $rendererName = \PHPExcel_Settings::PDF_RENDERER_DOMPDF; + $rendererLibraryPath = \OC::$THIRDPARTYROOT . '/3rdparty/dompdf'; + + \PHPExcel_Settings::setPdfRenderer($rendererName, $rendererLibraryPath); + + $phpexcel = new \PHPExcel($abspath); + $excel = \PHPExcel_IOFactory::createWriter($phpexcel, 'PDF'); + $excel->save($tmppath); + + $pdf = new \imagick($tmppath . '[0]'); + $pdf->setImageFormat('jpg'); + + unlink($abspath); + unlink($tmppath); + + $image = new \OC_Image($pdf); + + return $image->valid() ? $image : false; + } + +} + +class XLS extends MSOfficeExcel { + + public function getMimeType() { + return '/application\/vnd.ms-excel/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\XLS'); + +class XLSX extends MSOfficeExcel { + + public function getMimeType() { + return '/application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\XLSX'); + +/* //There is no (good) php-only solution for converting powerpoint documents to pdfs / pngs ... +class MSOfficePowerPoint extends Provider { + + public function getMimeType() { + return null; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + return false; + } + +} + +class PPT extends MSOfficePowerPoint { + + public function getMimeType() { + return '/application\/vnd.ms-powerpoint/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\PPT'); + +class PPTX extends MSOfficePowerPoint { + + public function getMimeType() { + return '/application\/vnd.openxmlformats-officedocument.presentationml.presentation/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\PPTX'); +*/
\ No newline at end of file diff --git a/lib/preview/office.php b/lib/preview/office.php new file mode 100644 index 00000000000..b6783bc5798 --- /dev/null +++ b/lib/preview/office.php @@ -0,0 +1,17 @@ +<?php +/** + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +//both, libreoffice backend and php fallback, need imagick +if (extension_loaded('imagick')) { + //let's see if there is libreoffice or openoffice on this machine + if(shell_exec('libreoffice --headless --version') || shell_exec('openoffice --headless --version') || is_string(\OC_Config::getValue('preview_libreoffice_path', null))) { + require_once('libreoffice-cl.php'); + }else{ + //in case there isn't, use our fallback + require_once('msoffice.php'); + } +}
\ No newline at end of file diff --git a/lib/preview/pdf.php b/lib/preview/pdf.php new file mode 100644 index 00000000000..3eabd201156 --- /dev/null +++ b/lib/preview/pdf.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Preview; + +if (extension_loaded('imagick')) { + + class PDF extends Provider { + + public function getMimeType() { + return '/application\/pdf/'; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $tmppath = $fileview->toTmpFile($path); + + //create imagick object from pdf + try{ + $pdf = new \imagick($tmppath . '[0]'); + $pdf->setImageFormat('jpg'); + }catch(\Exception $e){ + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; + } + + unlink($tmppath); + + //new image object + $image = new \OC_Image($pdf); + //check if image object is valid + return $image->valid() ? $image : false; + } + } + + \OC\Preview::registerProvider('OC\Preview\PDF'); +} diff --git a/lib/preview/provider.php b/lib/preview/provider.php new file mode 100644 index 00000000000..e4a730bafc8 --- /dev/null +++ b/lib/preview/provider.php @@ -0,0 +1,19 @@ +<?php +namespace OC\Preview; + +abstract class Provider { + private $options; + + public function __construct($options) { + $this->options=$options; + } + + abstract public function getMimeType(); + + /** + * search for $query + * @param string $query + * @return + */ + abstract public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview); +} diff --git a/lib/preview/svg.php b/lib/preview/svg.php new file mode 100644 index 00000000000..bafaf71b15a --- /dev/null +++ b/lib/preview/svg.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Preview; + +if (extension_loaded('imagick')) { + + class SVG extends Provider { + + public function getMimeType() { + return '/image\/svg\+xml/'; + } + + public function getThumbnail($path,$maxX,$maxY,$scalingup,$fileview) { + try{ + $svg = new \Imagick(); + $svg->setResolution($maxX, $maxY); + + $content = stream_get_contents($fileview->fopen($path, 'r')); + if(substr($content, 0, 5) !== '<?xml') { + $content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content; + } + + $svg->readImageBlob($content); + $svg->setImageFormat('jpg'); + }catch(\Exception $e){ + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + return false; + } + + //new image object + $image = new \OC_Image($svg); + //check if image object is valid + return $image->valid() ? $image : false; + } + } + + \OC\Preview::registerProvider('OC\Preview\SVG'); + +}
\ No newline at end of file diff --git a/lib/preview/txt.php b/lib/preview/txt.php new file mode 100644 index 00000000000..c7b8fabc6b0 --- /dev/null +++ b/lib/preview/txt.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Preview; + +class TXT extends Provider { + + public function getMimeType() { + return '/text\/.*/'; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $content = $fileview->fopen($path, 'r'); + $content = stream_get_contents($content); + + $lines = preg_split("/\r\n|\n|\r/", $content); + $numoflines = count($lines); + + $fontsize = 5; //5px + $linesize = ceil($fontsize * 1.25); + + $image = imagecreate($maxX, $maxY); + $imagecolor = imagecolorallocate($image, 255, 255, 255); + $textcolor = imagecolorallocate($image, 0, 0, 0); + + foreach($lines as $index => $line) { + $index = $index + 1; + + $x = (int) 1; + $y = (int) ($index * $linesize) - $fontsize; + + imagestring($image, 1, $x, $y, $line, $textcolor); + + if(($index * $linesize) >= $maxY) { + break; + } + } + + $image = new \OC_Image($image); + + return $image->valid() ? $image : false; + } +} + +\OC\Preview::registerProvider('OC\Preview\TXT'); + +class PHP extends TXT { + + public function getMimeType() { + return '/application\/x-php/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\PHP'); + +class JavaScript extends TXT { + + public function getMimeType() { + return '/application\/javascript/'; + } + +} + +\OC\Preview::registerProvider('OC\Preview\JavaScript');
\ No newline at end of file diff --git a/lib/preview/unknown.php b/lib/preview/unknown.php new file mode 100644 index 00000000000..a31b365722e --- /dev/null +++ b/lib/preview/unknown.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright (c) 2013 Frank Karlitschek frank@owncloud.org + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OC\Preview; + +class Unknown extends Provider { + + public function getMimeType() { + return '/.*/'; + } + + public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { + $mimetype = $fileview->getMimeType($path); + if(substr_count($mimetype, '/')) { + list($type, $subtype) = explode('/', $mimetype); + } + + $iconsroot = \OC::$SERVERROOT . '/core/img/filetypes/'; + + if(isset($type)){ + $icons = array($mimetype, $type, 'text'); + }else{ + $icons = array($mimetype, 'text'); + } + foreach($icons as $icon) { + $icon = str_replace('/', '-', $icon); + + $iconpath = $iconsroot . $icon . '.png'; + + if(file_exists($iconpath)) { + return new \OC_Image($iconpath); + } + } + return false; + } +} + +\OC\Preview::registerProvider('OC\Preview\Unknown');
\ No newline at end of file diff --git a/lib/public/preview.php b/lib/public/preview.php new file mode 100644 index 00000000000..e488eade4da --- /dev/null +++ b/lib/public/preview.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright (c) 2013 Frank Karlitschek frank@owncloud.org + * Copyright (c) 2013 Georg Ehrke georg@ownCloud.com + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +namespace OCP; + +/** + * This class provides functions to render and show thumbnails and previews of files + */ +class Preview { + + /** + * @brief return a preview of a file + * @param $file The path to the file where you want a thumbnail from + * @param $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image + * @param $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image + * @param $scaleup Scale smaller images up to the thumbnail size or not. Might look ugly + * @return image + */ + public static function show($file,$maxX=100,$maxY=75,$scaleup=false) { + return(\OC_Preview::show($file,$maxX,$maxY,$scaleup)); + } + + + + public static function isMimeSupported($mimetype='*') { + return \OC\Preview::isMimeSupported($mimetype); + } + +} diff --git a/lib/public/template.php b/lib/public/template.php index ab1089c332d..3b1a4ed4906 100644 --- a/lib/public/template.php +++ b/lib/public/template.php @@ -54,6 +54,25 @@ function mimetype_icon( $mimetype ) { return(\mimetype_icon( $mimetype )); } +/** + * @brief make preview_icon available as a simple function + * Returns the path to the preview of the image. + * @param $path path of file + * @returns link to the preview + */ +function preview_icon( $path ) { + return(\preview_icon( $path )); +} + +/** + * @brief make publicpreview_icon available as a simple function + * Returns the path to the preview of the image. + * @param $path path of file + * @returns link to the preview + */ +function publicPreview_icon ( $path, $token ) { + return(\publicPreview_icon( $path, $token )); +} /** * @brief make OC_Helper::humanFileSize available as a simple function diff --git a/lib/template.php b/lib/template.php index 9b2c1211e61..caa1e667c61 100644 --- a/lib/template.php +++ b/lib/template.php @@ -23,9 +23,6 @@ require_once __DIR__.'/template/functions.php'; -/** - * This class provides the templates for ownCloud. - */ class OC_Template extends \OC\Template\Base { private $renderas; // Create a full page? private $path; // The path to the template diff --git a/lib/template/functions.php b/lib/template/functions.php index 2d43cae1c0c..a864614c9a1 100644 --- a/lib/template/functions.php +++ b/lib/template/functions.php @@ -59,6 +59,22 @@ function mimetype_icon( $mimetype ) { } /** + * @brief make preview_icon available as a simple function + * Returns the path to the preview of the image. + * @param $path path of file + * @returns link to the preview + * + * For further information have a look at OC_Helper::previewIcon + */ +function preview_icon( $path ) { + return OC_Helper::previewIcon( $path ); +} + +function publicPreview_icon ( $path, $token ) { + return OC_Helper::publicPreview_icon( $path, $token ); +} + +/** * @brief make OC_Helper::humanFileSize available as a simple function * @param int $bytes size in bytes * @return string size as string |