diff --git a/lib/foldermanagement/compressionArchiveUtil.inc.php b/lib/foldermanagement/compressionArchiveUtil.inc.php index 2732d20..fcec614 100644 --- a/lib/foldermanagement/compressionArchiveUtil.inc.php +++ b/lib/foldermanagement/compressionArchiveUtil.inc.php @@ -37,6 +37,7 @@ */ require_once('File/Archive.php'); +require_once(KT_LIB_DIR . '/util/ktpclzip.inc.php'); /** * Class to create and download a zip file @@ -207,6 +208,13 @@ class ZipFolder { return PEAR::raiseError(_kt("No folders or documents found to compress")); } + $sZipFile = sprintf("%s/%s.".$this->extension, $this->sTmpPath, $this->sZipFileName); + $sZipFile = str_replace('<', '', str_replace('', '', $sZipFile))); + + $archive = new KTPclZip($sZipFile); + $archive->createZipFile($this->sTmpPath.'/Root Folder'); + + /* $config = KTConfig::getSingleton(); $useBinary = true; //$config->get('export/useBinary', false); @@ -259,7 +267,8 @@ class ZipFolder { File_Archive::toArchive($this->sZipFileName.'.'.$this->extension, File_Archive::toFiles($this->sTmpPath), $this->extension) ); } - + */ + // Save the zip file and path into session $_SESSION['zipcompression'] = KTUtil::arrayGet($_SESSION, 'zipcompression', array()); $sExportCode = $this->exportCode; diff --git a/lib/import/zipimportstorage.inc.php b/lib/import/zipimportstorage.inc.php index 362507f..96ff25a 100644 --- a/lib/import/zipimportstorage.inc.php +++ b/lib/import/zipimportstorage.inc.php @@ -40,8 +40,8 @@ require_once(KT_LIB_DIR . '/filelike/fsfilelike.inc.php'); require_once(KT_LIB_DIR . '/import/fsimportstorage.inc.php'); -require_once(KT_DIR.'/thirdparty/peclzip/pclzip.lib.php'); require_once('File/Archive.php'); +require_once(KT_LIB_DIR . '/util/ktpclzip.inc.php'); class KTZipImportStorage extends KTFSImportStorage { @@ -137,11 +137,8 @@ class KTZipImportStorage extends KTFSImportStorage { // todo: replace file archive for tar, etc if($this->sExtension == 'zip'){ - $archive = new PclZip($this->sZipPath); - - if ($archive->extract(PCLZIP_OPT_PATH, $sTmpPath) == 0){ - die("Error : Unable to unzip archive"); - } + $archive = new KTPclZip($this->sZipPath); + $archive->extractZipFile($sTmpPath); /* ** Original zip functionality using the unzip binary ** * $sUnzipCommand = KTUtil::findCommand("import/unzip", "unzip"); diff --git a/lib/util/ktpclzip.inc.php b/lib/util/ktpclzip.inc.php new file mode 100644 index 0000000..9a60d98 --- /dev/null +++ b/lib/util/ktpclzip.inc.php @@ -0,0 +1,502 @@ +. + * + * You can contact KnowledgeTree Inc., PO Box 7775 #87847, San Francisco, + * California 94120-7775, or email info@knowledgetree.com. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "Powered by + * KnowledgeTree" logo and retain the original copyright notice. If the display of the + * logo is not reasonably feasible for technical reasons, the Appropriate Legal Notices + * must display the words "Powered by KnowledgeTree" and retain the original + * copyright notice. + * Contributor( s): ______________________________________ + */ + +require_once ('config/dmsDefaults.php'); +require_once (KT_DIR . '/thirdparty/peclzip/pclzip.lib.php'); + +/** + * Class to create, extract and download a zip file using the pclzip library + * TODO: Class base was borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + * and currently only supports extractintg a zip archive. The other logic needs to be + * woven to work with pclzip and tested for relevancy. + */ + +class KTPclZip { + + var $sTmpPath = ''; + var $sZipFileName = ''; + var $sZipFile = ''; + var $sPattern = ''; + var $sFolderPattern = ''; + var $aPaths = array (); + var $aReplaceKeys = array (); + var $aReplaceValues = array (); + var $sOutputEncoding = 'UTF-8'; + var $extension = 'zip'; + var $exportCode = null; + var $_pclZip = null; + + /** + * Constructor + * + * @param string $sZipFileName The name of the zip file. + * @param string $exportCode The code to use if a zip file has already been created. + */ + function KTPclZip($sZipFileName = 'kt_pclzip', $exportCode = null, $extension = 'zip') { + + //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + $this->oKTConfig = & KTConfig::getSingleton (); + $this->oStorage = & KTStorageManagerUtil::getSingleton (); + + $this->sOutputEncoding = $this->oKTConfig->get ( 'export/encoding', 'UTF-8' ); + $this->extension = $extension; + + $this->sPattern = "[\*|\%|\\\|\/|\<|\>|\+|\:|\?|\||\'|\"]"; + $this->sFolderPattern = "[\*|\%|\<|\>|\+|\:|\?|\||\'|\"]"; + + if (! empty ( $exportCode )) { + $this->exportCode = $exportCode; + } else { + $this->exportCode = KTUtil::randomString (); + } + + // Check if the temp directory has been created and stored in session + $aData = KTUtil::arrayGet ( $_SESSION ['zipcompression'], $exportCode ); + if (! empty ( $aData ) && isset ( $aData ['dir'] )) { + $sTmpPath = $aData ['dir']; + } else { + $sBasedir = $this->oKTConfig->get ( "urls/tmpDirectory" ); + $sTmpPath = tempnam ( $sBasedir, 'kt_compress_zip' ); + + unlink ( $sTmpPath ); + mkdir ( $sTmpPath, 0755 ); + } + + $this->sTmpPath = $sTmpPath; + $this->sZipFileName = $sZipFileName; + $this->aPaths = array (); + + $this->_pclZip = new PclZip($sZipFileName); + + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + + $aReplace = array ("[" => "[[]", " " => "[ ]", "*" => "[*]", "?" => "[?]" ); + + $this->aReplaceKeys = array_keys ( $aReplace ); + $this->aReplaceValues = array_values ( $aReplace ); + */ + } + + /** + * Creates a zip archive using the sFolder contents + */ + function createZipFile($sFolder) { + //Overriding $this->aPaths with specified + if (!is_null($sFolder)) { + $this->aPaths = $sFolder; + } + + $excludePath = $this->getExcludePath($sFolder, DIRECTORY_SEPARATOR); + + // Create the zip archive using the PclZip Wrapper + if ($this->_pclZip->create ( $sFolder , PCLZIP_OPT_REMOVE_PATH, $excludePath) == 0) { + //( File_Archive::read ( $this->sTmpPath . '/Root Folder' ), File_Archive::toArchive ( $this->sZipFileName . '.' . $this->extension, File_Archive::toFiles ( $this->sTmpPath ), $this->extension ) ); + return PEAR::raiseError ( _kt ( "Error compressing files" ) ); + } + + // Save the zip file and path into session + $_SESSION ['zipcompression'] = KTUtil::arrayGet ( $_SESSION, 'zipcompression', array () ); + $sExportCode = $this->exportCode; + $_SESSION ['zipcompression'] [$sExportCode] = array ('file' => $sZipFile, 'dir' => $this->sTmpPath ); + $_SESSION ['zipcompression'] ['exportcode'] = $sExportCode; + + $this->sZipFile = $sZipFile; + return $sExportCode; + } + + /* + * Returns the part of the folder to be excluded from the zip content structure + * + * @params: $sPath folder to start from. + * @params: $ds directory separator + */ + function getExcludePath($sPath, $ds = '/') { + //Will grab the part of the full path to exclude from the zip contents + if ($sPath[strlen($sPath)] == $ds) { + //Chopping the last ds to make the keepPath contain an actual folder name + $sPath = substr($sPath, 0, strlen($sPath) - 1); + } + $keepPath = end(explode($ds, $sPath)); + $excludePath = str_replace($keepPath, '', $sPath); + return $excludePath; + } + + /** + * Extract/Unzip the temp folder + */ + function extractZipFile($tmpPath = null) { + + //Overriding $this->tmpPath if specified + if (!is_null($tmpPath)) { + $this->sTmpPath = $tmpPath; + } + + //Further checking that $tmpPath isn't empty + /* + if (empty ( $this->tmpPath )) { + return PEAR::raiseError ( _kt ( "No temporary path specified to extract to" ) ); + } + */ + + if ($this->_pclZip->extract ( PCLZIP_OPT_PATH, $this->sTmpPath ) == 0) { + return PEAR::raiseError ( _kt ( "Error : Unable to unzip archive" ) ); + } + + //Returning the sExportCode for uniformity. + return $this->sExportCode; + + // Save the zip file and path into session + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + + $_SESSION ['zipcompression'] = KTUtil::arrayGet ( $_SESSION, 'zipcompression', array () ); + $sExportCode = $this->exportCode; + $_SESSION ['zipcompression'] [$sExportCode] = array ('file' => $sZipFile, 'dir' => $this->sTmpPath ); + $_SESSION ['zipcompression'] ['exportcode'] = $sExportCode; + + $this->sZipFile = $sZipFile; + return $sExportCode; + */ + } + + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + static public function get($exportCode) { + static $zipFolder = null; + if (is_null ( $zipFolder )) { + $zipFolder = new KTPclZip ( 'kt_pclzip', $exportCode ); + } + return $zipFolder; + } + */ + + /** + * Return the full path + * + * @param mixed $oFolderOrDocument May be a Folder or Document + */ + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + + function getFullFolderPath($oFolder) { + static $sRootFolder = null; + + if (is_null ( $sRootFolder )) { + $oRootFolder = Folder::get ( 1 ); + $sRootFolder = $oRootFolder->getName (); + } + + $sFullPath = $sRootFolder . '/'; + $sFullPath .= $oFolder->getFullPath (); + + if (substr ( $sFullPath, - 1 ) == '/') + $sFullPath = substr ( $sFullPath, 0, - 1 ); + return $sFullPath; + } + */ + + /** + * Add a document to the zip file + */ + function addDocumentToZip($oDocument, $oFolder = null) { + + if (empty ( $oFolder )) { + $oFolder = Folder::get ( $oDocument->getFolderID () ); + } + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + if (empty ( $oFolder )) { + $oFolder = Folder::get ( $oDocument->getFolderID () ); + } + + $sDocPath = $this->getFullFolderPath ( $oFolder ); + $sDocPath = preg_replace ( $this->sFolderPattern, '-', $sDocPath ); + $sDocPath = $this->_convertEncoding ( $sDocPath, true ); + + $sDocName = $oDocument->getFileName (); + $sDocName = preg_replace ( $this->sPattern, '-', $sDocName ); + $sDocName = $this->_convertEncoding ( $sDocName, true ); + + $sParentFolder = $this->sTmpPath . '/' . $sDocPath; + $newDir = $this->sTmpPath; + + $aFullPath = split ( '/', $sDocPath ); + foreach ( $aFullPath as $dirPart ) { + $newDir = sprintf ( "%s/%s", $newDir, $dirPart ); + if (! file_exists ( $newDir )) { + mkdir ( $newDir, 0700 ); + } + } + + $sOrigFile = $this->oStorage->temporaryFile ( $oDocument ); + $sFilename = $sParentFolder . '/' . $sDocName; + @copy ( $sOrigFile, $sFilename ); + + $this->aPaths [] = $sDocPath . '/' . $sDocName; + return true; + */ + } + + + + /** + * Add a folder to the zip file + */ + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + + function addFolderToZip($oFolder) { + $sFolderPath = $this->getFullFolderPath ( $oFolder ) . '/'; + $sFolderPath = preg_replace ( $this->sFolderPattern, '-', $sFolderPath ); + $sFolderPath = $this->_convertEncoding ( $sFolderPath, true ); + + $newDir = $this->sTmpPath; + + $aFullPath = split ( '/', $sFolderPath ); + foreach ( $aFullPath as $dirPart ) { + $newDir = sprintf ( "%s/%s", $newDir, $dirPart ); + if (! file_exists ( $newDir )) { + mkdir ( $newDir, 0700 ); + } + } + + $this->aPaths [] = $sFolderPath; + return true; + } + */ + + + /** + * Zip the temp folder + */ + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + + function createZipFile($bEchoStatus = FALSE) { + if (empty ( $this->aPaths )) { + return PEAR::raiseError ( _kt ( "No folders or documents found to compress" ) ); + } + + $config = KTConfig::getSingleton (); + $useBinary = false; //$config->get('export/useBinary', false); + + + // Set environment language to output character encoding + $loc = $this->sOutputEncoding; + putenv ( "LANG=$loc" ); + putenv ( "LANGUAGE=$loc" ); + $loc = setlocale ( LC_ALL, $loc ); + + if ($useBinary) { + $sManifest = sprintf ( "%s/%s", $this->sTmpPath, "MANIFEST" ); + file_put_contents ( $sManifest, join ( "\n", $this->aPaths ) ); + } + + $sZipFile = sprintf ( "%s/%s." . $this->extension, $this->sTmpPath, $this->sZipFileName ); + $sZipFile = str_replace ( '<', '', str_replace ( '', '', $sZipFile ) ) ); + + if ($useBinary) { + $sZipCommand = KTUtil::findCommand ( "export/zip", "zip" ); + $aCmd = array ($sZipCommand, "-r", $sZipFile, ".", "-i@MANIFEST" ); + $sOldPath = getcwd (); + chdir ( $this->sTmpPath ); + + // Note that the popen means that pexec will return a file descriptor + $aOptions = array ('popen' => 'r' ); + $fh = KTUtil::pexec ( $aCmd, $aOptions ); + + if ($bEchoStatus) { + $last_beat = time (); + while ( ! feof ( $fh ) ) { + if ($i % 1000 == 0) { + $this_beat = time (); + if ($last_beat + 1 < $this_beat) { + $last_beat = $this_beat; + print " "; + } + } + $contents = fread ( $fh, 4096 ); + if ($contents) { + print nl2br ( $this->_convertEncoding ( $contents, false ) ); + } + $i ++; + } + } + pclose ( $fh ); + } else { + // Create the zip archive using the PEAR File_Archive + File_Archive::extract ( File_Archive::read ( $this->sTmpPath . '/Root Folder' ), File_Archive::toArchive ( $this->sZipFileName . '.' . $this->extension, File_Archive::toFiles ( $this->sTmpPath ), $this->extension ) ); + } + + // Save the zip file and path into session + $_SESSION ['zipcompression'] = KTUtil::arrayGet ( $_SESSION, 'zipcompression', array () ); + $sExportCode = $this->exportCode; + $_SESSION ['zipcompression'] [$sExportCode] = array ('file' => $sZipFile, 'dir' => $this->sTmpPath ); + $_SESSION ['zipcompression'] ['exportcode'] = $sExportCode; + + $this->sZipFile = $sZipFile; + return $sExportCode; + } + */ + + + /** + * Download the zip file + */ + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + + function downloadZipFile($exportCode = NULL) { + if (! (isset ( $exportCode ) && ! empty ( $exportCode ))) { + $exportCode = KTUtil::arrayGet ( $_SESSION ['zipcompression'], 'exportcode' ); + } + + $aData = KTUtil::arrayGet ( $_SESSION ['zipcompression'], $exportCode ); + + if (! empty ( $aData )) { + $sZipFile = $aData ['file']; + $sTmpPath = $aData ['dir']; + } else { + $sZipFile = $this->sZipFile; + $sTmpPath = $this->sTmpPath; + } + + if (! file_exists ( $sZipFile )) { + return PEAR::raiseError ( _kt ( 'The zip file has not been created, if you are downloading a large number of documents + or a large document then it may take a few minutes to finish.' ) ); + } + + $mimeType = 'application/zip; charset=utf-8;'; + $fileSize = filesize ( $sZipFile ); + $fileName = $this->sZipFileName . '.' . $this->extension; + + KTUtil::download ( $sZipFile, $mimeType, $fileSize, $fileName ); + KTUtil::deleteDirectory ( $sTmpPath ); + return true; + } + */ + + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + + function checkArchiveExists($exportCode = null) { + if (! (isset ( $exportCode ) && ! empty ( $exportCode ))) { + $exportCode = KTUtil::arrayGet ( $_SESSION ['zipcompression'], 'exportcode' ); + } + + $aData = KTUtil::arrayGet ( $_SESSION ['zipcompression'], $exportCode ); + + if (! empty ( $aData )) { + $sZipFile = $aData ['file']; + $sTmpPath = $aData ['dir']; + } else { + $sZipFile = $this->sZipFile; + $sTmpPath = $this->sTmpPath; + } + + if (! file_exists ( $sZipFile )) { + return false; + } + return true; + } + */ + + /** + * Check that iconv exists and that the selected encoding is supported. + */ + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + + function checkConvertEncoding() { + if (! function_exists ( "iconv" )) { + return PEAR::raiseError ( _kt ( 'IConv PHP extension not installed. The zip file compression could not handle output filename encoding conversion !' ) ); + } + $oKTConfig = $this->oKTConfig; + $this->sOutputEncoding = $oKTConfig->get ( 'export/encoding', 'UTF-8' ); + + // Test the specified encoding + if (iconv ( "UTF-8", $this->sOutputEncoding, "" ) === FALSE) { + return PEAR::raiseError ( _kt ( 'Specified output encoding for the zip files compression does not exists !' ) ); + } + return true; + } + + function _convertEncoding($sMystring, $bEncode) { + if (strcasecmp ( $this->sOutputEncoding, "UTF-8" ) === 0) { + return $sMystring; + } + if ($bEncode) { + return iconv ( "UTF-8", $this->sOutputEncoding, $sMystring ); + } else { + return iconv ( $this->sOutputEncoding, "UTF-8", $sMystring ); + } + } + */ + + /* //TODO: Cherry pick some of this logic borrowed from lib/foldremanagement/compressionArchiveUtil.inc.php + + static public function checkDownloadSize($object) { + return true; + + if ($object instanceof Document || $object instanceof DocumentProxy) { + } + + if ($object instanceof Folder || $object instanceof FolderProxy) { + $id = $object->iId; + + // If we're working with the root folder + if ($id = 1) { + $sql = 'SELECT count(*) as cnt FROM documents where folder_id = 1'; + } else { + $sql [] = "SELECT count(*) as cnt FROM documents where parent_folder_ids like '%,?' OR parent_folder_ids like '%,?,%' OR folder_id = ?"; + $sql [] = array ($id, $id, $id ); + } + + + //SELECT count(*) FROM documents d + //INNER JOIN document_metadata_version m ON d.metadata_version_id = m.id + //INNER JOIN document_content_version c ON m.content_version_id = c.id + //where (d.parent_folder_ids like '%,12' OR d.parent_folder_ids like '%,12,%' OR d.folder_id = 12) AND d.status_id < 3 AND size > 100000 + + + $result = DBUtil::getOneResult ( $sql ); + + if ($result ['cnt'] > 10) { + return true; + } + } + + return false; + } + */ + + +} +?>