diff --git a/SeedDMS_Preview/Preview.php b/SeedDMS_Preview/Preview.php index fb59f3067..d19d82943 100644 --- a/SeedDMS_Preview/Preview.php +++ b/SeedDMS_Preview/Preview.php @@ -16,9 +16,19 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +/** + * @uses Preview/Base.php + */ +require_once('Preview/Base.php'); + /** * @uses Preview/Previewer.php */ require_once('Preview/Previewer.php'); +/** + * @uses Preview/PdfPreviewer.php + */ +require_once('Preview/PdfPreviewer.php'); + ?> diff --git a/SeedDMS_Preview/Preview/Base.php b/SeedDMS_Preview/Preview/Base.php new file mode 100644 index 000000000..2e46f593a --- /dev/null +++ b/SeedDMS_Preview/Preview/Base.php @@ -0,0 +1,120 @@ + + * @copyright Copyright (C) 2010, Uwe Steinmann + * @version Release: @package_version@ + */ + + +/** + * Class for managing creation of preview images for documents. + * + * @category DMS + * @package SeedDMS_Preview + * @version @version@ + * @author Uwe Steinmann + * @copyright Copyright (C) 2011, Uwe Steinmann + * @version Release: @package_version@ + */ +class SeedDMS_Preview_Base { + /** + * @var string $cacheDir location in the file system where all the + * cached data like thumbnails are located. This should be an + * absolute path. + * @access public + */ + public $previewDir; + + /** + * @var array $converters list of mimetypes and commands for converting + * file into preview image + * @access protected + */ + protected $converters; + + /** + * @var integer $timeout maximum time for execution of external commands + * @access protected + */ + protected $timeout; + + function __construct($previewDir, $timeout=5) { /* {{{ */ + if(!is_dir($previewDir)) { + if (!SeedDMS_Core_File::makeDir($previewDir)) { + $this->previewDir = ''; + } else { + $this->previewDir = $previewDir; + } + } else { + $this->previewDir = $previewDir; + } + $this->timeout = intval($timeout); + } /* }}} */ + + static function execWithTimeout($cmd, $timeout=5) { /* {{{ */ + $descriptorspec = array( + 0 => array("pipe", "r"), + 1 => array("pipe", "w"), + 2 => array("pipe", "w") + ); + $pipes = array(); + + $timeout += time(); + $process = proc_open($cmd, $descriptorspec, $pipes); + if (!is_resource($process)) { + throw new Exception("proc_open failed on: " . $cmd); + } + + $output = ''; + $timeleft = $timeout - time(); + $read = array($pipes[1]); + $write = NULL; + $exeptions = NULL; + do { + stream_select($read, $write, $exeptions, $timeleft, 200000); + + if (!empty($read)) { + $output .= fread($pipes[1], 8192); + } + $timeleft = $timeout - time(); + } while (!feof($pipes[1]) && $timeleft > 0); + + if ($timeleft <= 0) { + proc_terminate($process); + throw new Exception("command timeout on: " . $cmd); + } else { + return $output; + } + } /* }}} */ + + /** + * Set a list of converters + * + * Merges the list of passed converters with the already existing ones. + * Existing converters will be overwritten. + * + * @param array list of converters. The key of the array contains the mimetype + * and the value is the command to be called for creating the preview + */ + function setConverters($arr) { /* {{{ */ + $this->converters = array_merge($arr, $this->converters); + } /* }}} */ + + /** + * Check if converter for a given mimetype is set + * + * @param string $mimetype + * @return boolean true if converter exists, otherwise false + */ + function hasConverter($mimetype) { /* {{{ */ + return array_key_exists($mimetype, $this->converters); + } /* }}} */ + +} + diff --git a/SeedDMS_Preview/Preview/PdfPreviewer.php b/SeedDMS_Preview/Preview/PdfPreviewer.php new file mode 100644 index 000000000..10005afc7 --- /dev/null +++ b/SeedDMS_Preview/Preview/PdfPreviewer.php @@ -0,0 +1,276 @@ + + * @copyright Copyright (C) 2010, Uwe Steinmann + * @version Release: @package_version@ + */ + + +/** + * Class for managing creation of pdf preview for documents. + * + * @category DMS + * @package SeedDMS_Preview + * @version @version@ + * @author Uwe Steinmann + * @copyright Copyright (C) 2011, Uwe Steinmann + * @version Release: @package_version@ + */ +class SeedDMS_Preview_PdfPreviewer extends SeedDMS_Preview_Base { + + function __construct($previewDir, $timeout=5) { /* {{{ */ + parent::__construct($previewDir, $timeout); + $this->converters = array( + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'", + 'application/vnd.oasis.opendocument.text' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'", + 'text/rtf' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'", + 'application/msword' => "unoconv -d document -f pdf --stdout -v '%f' > '%o'", + ); + } /* }}} */ + + /** + * Return the physical filename of the preview image on disk + * + * @param object $object document content or document file + * @return string file name of preview image + */ + protected function getFileName($object) { /* {{{ */ + if(!$object) + return false; + + $document = $object->getDocument(); + $dir = $this->previewDir.'/'.$document->getDir(); + switch(get_class($object)) { + case "SeedDMS_Core_DocumentContent": + $target = $dir.'p'.$object->getVersion(); + break; + case "SeedDMS_Core_DocumentFile": + $target = $dir.'f'.$object->getID(); + break; + default: + return false; + } + return $target; + } /* }}} */ + + /** + * Create a pdf preview for a given file + * + * This method creates a preview in pdf format for a regular file + * in the file system and stores the result in the directory $dir relative + * to the configured preview directory. The filename of the resulting preview + * image is either $target.pdf (if set) or md5($infile).pdf. + * The $mimetype is used to select the propper conversion programm. + * An already existing pdf preview is replaced. + * + * @param string $infile name of input file including full path + * @param string $dir directory relative to $this->previewDir + * @param string $mimetype MimeType of input file + * @param string $target optional name of preview image (without extension) + * @return boolean true on success, false on failure + */ + public function createRawPreview($infile, $dir, $mimetype, $target='') { /* {{{ */ + if(!$this->previewDir) + return false; + if(!is_dir($this->previewDir.'/'.$dir)) { + if (!SeedDMS_Core_File::makeDir($this->previewDir.'/'.$dir)) { + return false; + } + } + if(!file_exists($infile)) + return false; + if(!$target) + $target = $this->previewDir.$dir.md5($infile); + if($target != '' && (!file_exists($target.'.pdf') || filectime($target.'.pdf') < filectime($infile))) { + $cmd = ''; + if(isset($this->converters[$mimetype])) { + $cmd = str_replace(array('%f', '%o'), array($infile, $target.'.pdf'), $this->converters[$mimetype]); + } + if($cmd) { + try { + self::execWithTimeout($cmd, $this->timeout); + } catch(Exception $e) { + } + } + return true; + } + return true; + + } /* }}} */ + + /** + * Create preview image + * + * This function creates a preview image for the given document + * content or document file. It internally uses + * {@link SeedDMS_Preview::createRawPreview()}. The filename of the + * preview image is created by {@link SeedDMS_Preview_Previewer::getFileName()} + * + * @param object $object instance of SeedDMS_Core_DocumentContent + * or SeedDMS_Core_DocumentFile + * @return boolean true on success, false on failure + */ + public function createPreview($object) { /* {{{ */ + if(!$object) + return false; + + $document = $object->getDocument(); + $file = $document->_dms->contentDir.$object->getPath(); + $target = $this->getFileName($object); + return $this->createRawPreview($file, $document->getDir(), $object->getMimeType(), $target); + } /* }}} */ + + /** + * Check if a preview image already exists. + * + * This function is a companion to {@link SeedDMS_Preview_Previewer::createRawPreview()}. + * + * @param string $infile name of input file including full path + * @param string $dir directory relative to $this->previewDir + * @return boolean true if preview exists, otherwise false + */ + public function hasRawPreview($infile, $dir) { /* {{{ */ + if(!$this->previewDir) + return false; + $target = $this->previewDir.$dir.md5($infile); + if($target !== false && file_exists($target.'.pdf') && filectime($target.'.pdf') >= filectime($infile)) { + return true; + } + return false; + } /* }}} */ + + /** + * Check if a preview image already exists. + * + * This function is a companion to {@link SeedDMS_Preview_Previewer::createPreview()}. + * + * @param object $object instance of SeedDMS_Core_DocumentContent + * or SeedDMS_Core_DocumentFile + * @return boolean true if preview exists, otherwise false + */ + public function hasPreview($object) { /* {{{ */ + if(!$object) + return false; + + if(!$this->previewDir) + return false; + $target = $this->getFileName($object); + if($target !== false && file_exists($target.'.pdf') && filectime($target.'.pdf') >= $object->getDate()) { + return true; + } + return false; + } /* }}} */ + + /** + * Return a preview image. + * + * This function returns the content of a preview image if it exists.. + * + * @param string $infile name of input file including full path + * @param string $dir directory relative to $this->previewDir + * @return boolean/string image content if preview exists, otherwise false + */ + public function getRawPreview($infile, $dir) { /* {{{ */ + if(!$this->previewDir) + return false; + + $target = $this->previewDir.$dir.md5($infile); + if($target && file_exists($target.'.pdf')) { + readfile($target.'.pdf'); + } + } /* }}} */ + + /** + * Return a preview image. + * + * This function returns the content of a preview image if it exists.. + * + * @param object $object instance of SeedDMS_Core_DocumentContent + * or SeedDMS_Core_DocumentFile + * @return boolean/string image content if preview exists, otherwise false + */ + public function getPreview($object) { /* {{{ */ + if(!$this->previewDir) + return false; + + $target = $this->getFileName($object); + if($target && file_exists($target.'.pdf')) { + readfile($target.'.pdf'); + } + } /* }}} */ + + /** + * Return file size preview image. + * + * @param object $object instance of SeedDMS_Core_DocumentContent + * or SeedDMS_Core_DocumentFile + * @return boolean/integer size of preview image or false if image + * does not exist + */ + public function getFilesize($object) { /* {{{ */ + $target = $this->getFileName($object); + if($target && file_exists($target.'.pdf')) { + return(filesize($target.'.pdf')); + } else { + return false; + } + + } /* }}} */ + + /** + * Delete preview image. + * + * @param object $object instance of SeedDMS_Core_DocumentContent + * or SeedDMS_Core_DocumentFile + * @return boolean true if deletion succeded or false if file does not exist + */ + public function deletePreview($object) { /* {{{ */ + if(!$this->previewDir) + return false; + + $target = $this->getFileName($object); + if($target && file_exists($target.'.pdf')) { + return(unlink($target.'.pdf')); + } else { + return false; + } + } /* }}} */ + + static function recurseRmdir($dir) { + $files = array_diff(scandir($dir), array('.','..')); + foreach ($files as $file) { + (is_dir("$dir/$file")) ? SeedDMS_Preview_Previewer::recurseRmdir("$dir/$file") : unlink("$dir/$file"); + } + return rmdir($dir); + } + + /** + * Delete all preview images belonging to a document + * + * This function removes the preview images of all versions and + * files of a document including the directory. It actually just + * removes the directory for the document in the cache. + * + * @param object $document instance of SeedDMS_Core_Document + * @return boolean true if deletion succeded or false if file does not exist + */ + public function deleteDocumentPreviews($document) { /* {{{ */ + if(!$this->previewDir) + return false; + + $dir = $this->previewDir.'/'.$document->getDir(); + if(file_exists($dir) && is_dir($dir)) { + return SeedDMS_Preview_Previewer::recurseRmdir($dir); + } else { + return false; + } + + } /* }}} */ +} +?> diff --git a/SeedDMS_Preview/Preview/Previewer.php b/SeedDMS_Preview/Preview/Previewer.php index bbc32a004..8c9b67713 100644 --- a/SeedDMS_Preview/Preview/Previewer.php +++ b/SeedDMS_Preview/Preview/Previewer.php @@ -22,45 +22,15 @@ * @copyright Copyright (C) 2011, Uwe Steinmann * @version Release: @package_version@ */ -class SeedDMS_Preview_Previewer { - /** - * @var string $cacheDir location in the file system where all the - * cached data like thumbnails are located. This should be an - * absolute path. - * @access public - */ - public $previewDir; - +class SeedDMS_Preview_Previewer extends SeedDMS_Preview_Base { /** * @var integer $width maximum width/height of resized image * @access protected */ protected $width; - /** - * @var array $converters list of mimetypes and commands for converting - * file into preview image - * @access protected - */ - protected $converters; - - /** - * @var integer $timeout maximum time for execution of external commands - * @access protected - */ - protected $timeout; - function __construct($previewDir, $width=40, $timeout=5) { /* {{{ */ - if(!is_dir($previewDir)) { - if (!SeedDMS_Core_File::makeDir($previewDir)) { - $this->previewDir = ''; - } else { - $this->previewDir = $previewDir; - } - } else { - $this->previewDir = $previewDir; - } - $this->width = intval($width); + parent::__construct($previewDir, $timeout); $this->converters = array( 'image/png' => "convert -resize %wx '%f' '%o'", 'image/gif' => "convert -resize %wx '%f' '%o'", @@ -72,56 +42,7 @@ class SeedDMS_Preview_Previewer { 'application/postscript' => "convert -density 100 -resize %wx '%f[0]' '%o'", 'application/x-compressed-tar' => "tar tzvf '%f' | convert -density 100 -resize %wx text:-[0] '%o", ); - $this->timeout = intval($timeout); - } /* }}} */ - - static function execWithTimeout($cmd, $timeout=5) { /* {{{ */ - $descriptorspec = array( - 0 => array("pipe", "r"), - 1 => array("pipe", "w"), - 2 => array("pipe", "w") - ); - $pipes = array(); - - $timeout += time(); - $process = proc_open($cmd, $descriptorspec, $pipes); - if (!is_resource($process)) { - throw new Exception("proc_open failed on: " . $cmd); - } - - $output = ''; - $timeleft = $timeout - time(); - $read = array($pipes[1]); - $write = NULL; - $exeptions = NULL; - do { - stream_select($read, $write, $exeptions, $timeleft, 200000); - - if (!empty($read)) { - $output .= fread($pipes[1], 8192); - } - $timeleft = $timeout - time(); - } while (!feof($pipes[1]) && $timeleft > 0); - - if ($timeleft <= 0) { - proc_terminate($process); - throw new Exception("command timeout on: " . $cmd); - } else { - return $output; - } - } /* }}} */ - - /** - * Set a list of converters - * - * Merges the list of passed converters with the already existing ones. - * Existing converters will be overwritten. - * - * @param array list of converters. The key of the array contains the mimetype - * and the value is the command to be called for creating the preview - */ - function setConverters($arr) { /* {{{ */ - $this->converters = array_merge($arr, $this->converters); + $this->width = intval($width); } /* }}} */ /** diff --git a/SeedDMS_Preview/package.xml b/SeedDMS_Preview/package.xml index 681e952bf..265990820 100644 --- a/SeedDMS_Preview/package.xml +++ b/SeedDMS_Preview/package.xml @@ -11,11 +11,11 @@ uwe@steinmann.cx yes - 2016-04-26 + 2016-11-07 - 1.1.9 - 1.1.9 + 1.2.0 + 1.2.0 stable @@ -23,11 +23,7 @@ GPL License -add more documentation -finish deletePreview() -add new method deleteDocumentPreviews() -fix calculation of timeout (Bug #269) -check if cache dir exists before deleting it in deleteDocumentPreviews() +add new previewer which converts document to pdf instead of png @@ -216,5 +212,25 @@ set last parameter of stream_select() to 200000 micro sec. in case the timeout i pass variables to stream_select (required by php7) + + 2016-04-26 + + + 1.1.9 + 1.1.9 + + + stable + stable + + GPL License + +add more documentation +finish deletePreview() +add new method deleteDocumentPreviews() +fix calculation of timeout (Bug #269) +check if cache dir exists before deleting it in deleteDocumentPreviews() + +