diff --git a/filemanager/inc/class.filemanager_ui.inc.php b/filemanager/inc/class.filemanager_ui.inc.php index 932ad28bc8..9ba1310469 100644 --- a/filemanager/inc/class.filemanager_ui.inc.php +++ b/filemanager/inc/class.filemanager_ui.inc.php @@ -131,6 +131,13 @@ class filemanager_ui 'disableClass' => 'isDir', 'enabled' => 'javaScript:app.filemanager.is_multiple_allowed' ), + 'saveaszip' => array( + 'caption' => lang('Save as ZIP'), + 'group' => $group, + 'allowOnMultiple' => true, + 'icon' => 'filesave', + 'postSubmit' => true + ), 'edit' => array( 'caption' => lang('Edit settings'), 'group' => $group, @@ -606,6 +613,9 @@ class filemanager_ui } return lang("Error while creating directory."); + case 'saveaszip': + return egw_vfs::download_zip($selected); + default: list($action, $settings) = explode('_', $action, 2); switch($action) diff --git a/phpgwapi/inc/class.egw_vfs.inc.php b/phpgwapi/inc/class.egw_vfs.inc.php index b4003ba073..686e608c00 100644 --- a/phpgwapi/inc/class.egw_vfs.inc.php +++ b/phpgwapi/inc/class.egw_vfs.inc.php @@ -1320,6 +1320,120 @@ class egw_vfs extends vfs_stream_wrapper return '/webdav.php'.strtr($path,array('+' => '%2B',' ' => '%20','"' => '%22')).($force_download ? '?download' : ''); } + /** + * Download the given file list as a ZIP + * + * @param array $files + * + * @return undefined + */ + public static function download_zip(Array $files) + { + error_log(__METHOD__ . ': '.implode(',',$files)); + + // Create zip file + $zip_file = tempnam($GLOBALS['egw_info']['server']['temp_dir'], 'zip'); + + $zip = new ZipArchive(); + $result = $zip->open($zip_file, ZipArchive::OVERWRITE); + if($result !== TRUE) + { + throw new egw_exception("Cannot open zip file for writing."); + } + + // A nice name for the user + $filename = $GLOBALS['egw_info']['server']['site_title'] . (count($files) == 1 ? '_'. self::basename($files[0]) : '') . '.zip'; + + // Go into directories, find them all + $files = self::find($files); + + // We need to remove them _after_ we're done + $tempfiles = array(); + + // Give 1 second per file + set_time_limit(count($files)); + + // Add files to archive + foreach($files as $idx => &$addfile) + { + // Use relative paths inside zip, if we could + //$_name = self::concat($directory,self::basename($addfile)); + $_name = $addfile; + + // Don't go infinite with app entries + if(self::is_link($addfile)) + { + if(in_array($addfile, $links)) continue; + $links[] = $addfile; + } + // Add directory - if empty, client app might not show it though + if(self::is_dir($addfile)) + { + $zip->addEmptyDir($addfile); + } + else if(self::is_readable($addfile)) + { + // Copy to temp file, as ZipArchive fails to read VFS + $temp = tempnam($GLOBALS['egw_info']['server']['temp_dir'], 'zip_'); + $from = egw_vfs::fopen($addfile,'r'); + $to = fopen($temp,'w'); + if(!stream_copy_to_stream($from,$to) || !$zip->addFile($temp, $_name)) + { + unlink($temp); + trigger_error("Could not add $addfile to ZIP file", E_USER_ERROR); + continue; + } + // Keep temp file until _after_ zipping is done + $tempfiles[] = $temp; + + // Add comment in + $props = self::propfind($addfile); + if($props) + { + $comment = self::find_prop($props,'comment'); + if($comment) + { + $zip->setCommentName($_name, $prop['val']); + } + } + $props = null; + } + } + + // Set a comment to help tell them apart + $zip->setArchiveComment(lang('Created by %1', $GLOBALS['egw_info']['user']['account_lid']) . ' ' .egw_time::to()); + + // Record total for debug, not available after close() + $total_files = $zip->numFiles; + + $result = $zip->close(); + if(!$result || !filesize($zip_file)) + { + error_log('close() result: '.array2string($result)); + return 'Error creating zip file'; + } + + error_log("Total files: " . $total_files . " Peak memory to zip: " . self::hsize(memory_get_peak_usage(true))); + + // Stop any buffering + while(ob_get_level() > 0) + ob_end_clean(); + + // Stream the file to the client + header("Content-Type: application/zip"); + header("Content-Length: " . filesize($zip_file)); + header("Content-Disposition: attachment; filename=\"$filename\""); + readfile($zip_file); + + unlink($zip_file); + foreach($tempfiles as $temp_file) + { + unlink($temp_file); + } + + common::egw_exit(); + } + /** * We cache locks within a request, as HTTP_WebDAV_Server generates so many, that it can be a bottleneck *