diff --git a/lib/foldermanagement/compressionArchiveUtil.inc.php b/lib/foldermanagement/compressionArchiveUtil.inc.php new file mode 100644 index 0000000..6fa5cf0 --- /dev/null +++ b/lib/foldermanagement/compressionArchiveUtil.inc.php @@ -0,0 +1,238 @@ +oKTConfig =& KTConfig::getSingleton(); + $this->oStorage =& KTStorageManagerUtil::getSingleton(); + + $sBasedir = $this->oKTConfig->get("urls/tmpDirectory"); + $sTmpPath = tempnam($sBasedir, 'kt_compress_zip'); + + unlink($sTmpPath); + mkdir($sTmpPath, 0700); + + $this->sTmpPath = $sTmpPath; + $this->sZipFileName = $sZipFileName; + $this->aPaths = array(); + + $aReplace = array( + "[" => "[[]", + " " => "[ ]", + "*" => "[*]", + "?" => "[?]", + ); + + $this->aReplaceKeys = array_keys($aReplace); + $this->aReplaceValues = array_values($aReplace); + } + + /** + * Add a document to the zip file + */ + function addDocumentToZip($oDocument) { + $sParentFolder = str_replace('<', '', str_replace('', '', str_replace('>', '', sprintf('%s/%s', $this->sTmpPath, $oDocument->getFullPath())))); + $newDir = $this->sTmpPath; + $sFullPath = str_replace('<', '', str_replace('', '', str_replace('>', '', $this->_convertEncoding($oDocument->getFullPath(), true)))); + foreach (split('/', $sFullPath) as $dirPart) { + $newDir = sprintf("%s/%s", $newDir, $dirPart); + if (!file_exists($newDir)) { + mkdir($newDir, 0700); + } + } + + $sOrigFile = str_replace('<', '', str_replace('', '', str_replace('>', '', $this->oStorage->temporaryFile($oDocument)))); + $sFilename = sprintf("%s/%s", $sParentFolder, str_replace('<', '', str_replace('', '', str_replace('>', '', $oDocument->getFileName())))); + $sFilename = $this->_convertEncoding($sFilename, true); + copy($sOrigFile, $sFilename); + + $sPath = str_replace('<', '', str_replace('', '', str_replace('>', '', sprintf("%s/%s", $oDocument->getFullPath(), $oDocument->getFileName())))); + $sPath = str_replace($this->aReplaceKeys, $this->aReplaceValues, $sPath); + $sPath = $this->_convertEncoding($sPath, true); + + $this->aPaths[] = $sPath; + return true; + } + + /** + * Add a folder to the zip file + */ + function addFolderToZip($oFolder) { + $sFolderPath = $oFolder->getFullPath().'/'.$oFolder->getName().'/'; + $sParentFolder = str_replace('<', '', str_replace('', '', str_replace('>', '', sprintf('%s/%s', $this->sTmpPath, $sFolderPath)))); + $newDir = $this->sTmpPath; + $sFullPath = str_replace('<', '', str_replace('', '', str_replace('>', '', $this->_convertEncoding($sFolderPath, true)))); + foreach (split('/', $sFullPath) as $dirPart) { + $newDir = sprintf("%s/%s", $newDir, $dirPart); + if (!file_exists($newDir)) { + mkdir($newDir, 0700); + } + } + + $sPath = str_replace('<', '', str_replace('', '', str_replace('>', '', sprintf("%s", $sFolderPath)))); + $sPath = str_replace($this->aReplaceKeys, $this->aReplaceValues, $sPath); + $sPath = $this->_convertEncoding($sPath, true); + + $this->aPaths[] = $sPath; + return true; + } + + /** + * Zip the temp folder + */ + function createZipFile($bEchoStatus = FALSE) { + if(empty($this->aPaths)){ + return PEAR::raiseError(_kt("No folders or documents found to compress")); + //$this->addErrorMessage(_kt("No folders or documents found to compress")); + //return false; + } + + $sManifest = sprintf("%s/%s", $this->sTmpPath, "MANIFEST"); + file_put_contents($sManifest, join("\n", $this->aPaths)); + $sZipFile = sprintf("%s/%s.zip", $this->sTmpPath, $this->sZipFileName); + $sZipFile = str_replace('<', '', str_replace('', '', str_replace('>', '', $sZipFile))); + $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); + + // Save the zip file and path into session + $_SESSION['zipcompression'] = KTUtil::arrayGet($_SESSION, 'zipcompression', array()); + $sExportCode = KTUtil::randomString(); + $_SESSION['zipcompression'][$sExportCode] = array( + 'file' => $sZipFile, + 'dir' => $this->oZip->sTmpPath, + ); + $_SESSION['zipcompression']['exportcode'] = $sExportCode; + + $this->sZipFile = $sZipFile; + return $sExportCode; + } + + /** + * Download the zip file + */ + 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 can only be downloaded once - if you cancel the download, you will need to reload the page.')); + } + + header("Content-Type: application/zip"); + header("Content-Length: ". filesize($sZipFile)); + header("Content-Disposition: attachment; filename=\"" . $this->sZipFileName . ".zip" . "\""); + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header("Cache-Control: must-revalidate"); + readfile($sZipFile); + $sTmpDir = $sTmpPath; + KTUtil::deleteDirectory($sTmpDir); + return true; + } + + /** + * Check that iconv exists and that the selected encoding is supported. + */ + 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); + } + } +} +?> \ No newline at end of file diff --git a/plugins/ktcore/KTBulkActions.php b/plugins/ktcore/KTBulkActions.php index 60a9a3a..513dc38 100644 --- a/plugins/ktcore/KTBulkActions.php +++ b/plugins/ktcore/KTBulkActions.php @@ -31,6 +31,7 @@ require_once(KT_LIB_DIR . '/actions/bulkaction.php'); require_once(KT_LIB_DIR . '/widgets/forms.inc.php'); +require_once(KT_LIB_DIR . '/foldermanagement/compressionArchiveUtil.inc.php'); class KTBulkDeleteAction extends KTBulkAction { @@ -486,33 +487,33 @@ class KTBulkArchiveAction extends KTBulkAction { DBUtil::rollback(); return false; } - + $oDocumentTransaction = & new DocumentTransaction($document, sprintf(_kt('Document archived: %s'), $this->sReason), 'ktcore.transactions.update'); $oDocumentTransaction->create(); - + DBUtil::commit(); return true; }else if(is_a($oEntity, 'Folder')) { DBUtil::startTransaction(); - + $aDocuments = array(); $aChildFolders = array(); $oFolder = $oEntity; - + // Get folder id $sFolderId = $oFolder->getID(); - + // Get documents in folder $sDocuments = $oFolder->getDocumentIDs($sFolderId); $aDocuments = explode(',', $sDocuments); - + // Get all the folders within the folder $sWhereClause = "parent_folder_ids = '{$sFolderId}' OR parent_folder_ids LIKE '{$sFolderId},%' OR parent_folder_ids LIKE '%,{$sFolderId},%' OR parent_folder_ids LIKE '%,{$sFolderId}'"; $aChildFolders = $this->oFolder->getList($sWhereClause); - + // Loop through folders and get documents if(!empty($aChildFolders)){ foreach($aChildFolders as $oChild){ @@ -522,26 +523,26 @@ class KTBulkArchiveAction extends KTBulkAction { DBUtil::rollback(); return false; } - + if(!empty($sChildDocs)){ $aChildDocs = explode(',', $sChildDocs); $aDocuments = array_merge($aDocuments, $aChildDocs); } } } - + // Archive all documents if(!empty($aDocuments)){ foreach($aDocuments as $sDocumentId){ $oDocument = Document::get($sDocumentId); - + $oDocument->setStatusID(ARCHIVED); $res = $oDocument->update(); if (($res === false) || PEAR::isError($res)) { DBUtil::rollback(); return false; } - + $oDocumentTransaction = & new DocumentTransaction($oDocument, sprintf(_kt('Document archived: %s'), $this->sReason), 'ktcore.transactions.update'); $oDocumentTransaction->create(); } @@ -573,47 +574,29 @@ class KTBrowseBulkExportAction extends KTBulkAction { function do_performaction() { - $oKTConfig =& KTConfig::getSingleton(); - $sBasedir = $oKTConfig->get("urls/tmpDirectory"); - $this->sTmpPath= $sTmpPath = tempnam($sBasedir, 'kt_export'); + $folderName = $this->oFolder->getName(); + $this->oZip = new ZipFolder($folderName); + $res = $this->oZip->checkConvertEncoding(); - $this->oStorage =& KTStorageManagerUtil::getSingleton(); + $folderurl = KTBrowseUtil::getUrlForFolder($this->oFolder); + $sReturn = sprintf('
' . _kt('Return to the original folder') . "
\n", $folderurl); - unlink($sTmpPath); - mkdir($sTmpPath, 0700); + if(PEAR::isError($res)){ + $this->addErrorMessage($res->getMessage()); + return $sReturn; + } $this->startTransaction(); + $oKTConfig =& KTConfig::getSingleton(); $this->bNoisy = $oKTConfig->get("tweaks/noisyBulkOperations"); - $this->sTmpPath = $sTmpPath; - $this->aPaths = array(); - $result = parent::do_performaction(); + $sExportCode = $this->oZip->createZipFile(); - - $sManifest = sprintf("%s/%s", $this->sTmpPath, "MANIFEST"); - file_put_contents($sManifest, join("\n", $this->aPaths)); - $sZipFile = sprintf("%s/%s.zip", $this->sTmpPath, $this->oFolder->getName()); - $sZipFile = str_replace('<', '', str_replace('', '', str_replace('>', '', $sZipFile))); - $_SESSION['bulkexport'] = KTUtil::arrayGet($_SESSION, 'bulkexport', array()); - $sExportCode = KTUtil::randomString(); - $_SESSION['bulkexport'][$sExportCode] = array( - 'file' => $sZipFile, - 'dir' => $this->sTmpPath, - ); - $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 - - $fh = KTUtil::pexec($aCmd); + if(PEAR::isError($sExportCode)){ + $this->addErrorMessage($sExportCode->getMessage()); + return $sReturn; + } $oTransaction = KTFolderTransaction::createFromArray(array( 'folderid' => $this->oFolder->getId(), @@ -624,32 +607,25 @@ class KTBrowseBulkExportAction extends KTBulkAction { )); $this->commitTransaction(); - - - header("Content-Type: application/zip"); - header("Content-Length: ". filesize($sZipFile)); - header("Content-Disposition: attachment; filename=\"" . $this->oFolder->getName() . ".zip" . "\""); - header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); - header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); - header("Cache-Control: must-revalidate"); - readfile($sZipFile); - $sTmpDir = $aData['dir']; - KTUtil::deleteDirectory($sTmpDir); - - return $result; + + $url = KTUtil::addQueryStringSelf(sprintf('action=downloadZipFile&fFolderId=%d&exportcode=%s', $this->oFolder->getId(), $sExportCode)); + $str = sprintf('' . _kt('Go here to download the zip file if you are not automatically redirected there') . "
\n", $url); + $folderurl = KTBrowseUtil::getUrlForFolder($this->oFolder); + $str .= sprintf('' . _kt('Once downloaded, return to the original folder') . "
\n", $folderurl); + //$str .= sprintf("