Commit 3329c23b authored by Richard Mansfield's avatar Richard Mansfield
Browse files

Support for extracting files from .tar(.gz/.bz2) archives


Signed-off-by: default avatarRichard Mansfield <richardm@catalyst.net.nz>
parent 8bff8673
......@@ -76,14 +76,15 @@ function unzip_iframe_progress_handler($artefacts) {
}
$file = artefact_instance_from_id($unzip['file']);
$status = $file->unzip('unzip_iframe_progress_handler');
$file->set_archive_info($unzip['zipinfo']);
$status = $file->extract('unzip_iframe_progress_handler');
$next = $unzip['from'];
$next .= (strpos($next, '?') === false ? '?' : '&') . 'folder=' . $status->basefolderid;
$next .= (strpos($next, '?') === false ? '?' : '&') . 'folder=' . $status['basefolderid'];
$SESSION->set('unzip', false);
$message = get_string('extractfilessuccess', 'artefact.file', $status->folders, $status->files);
$message = get_string('extractfilessuccess', 'artefact.file', $status['folderscreated'], $status['filescreated']);
?>
<div class="progress-bar" style="width: 100%;">
<p><?php echo $message; ?> <a href="<?php echo $next; ?>" target="_top"><?php echo get_string('Continue', 'artefact.file'); ?></a></p>
......
......@@ -65,15 +65,15 @@ if ($fileid) {
throw new AccessDeniedException();
}
$zip = zip_open($file->get_path());
if (!is_resource($zip)) {
throw new NotFoundException();
try {
$zipinfo = $file->read_archive();
}
catch (SystemException $e) {
$message = get_string('invalidarchive', 'artefact.file');
}
$zipinfo = ArtefactTypeArchive::zip_file_info($zip);
zip_close($zip);
if (!$file->get('owner') || $USER->quota_allowed($zipinfo->totalsize)) {
$name = ArtefactTypeArchive::get_unzip_directory_name($file);
if ($zipinfo && !$file->get('owner') || $USER->quota_allowed($zipinfo->totalsize)) {
$name = $file->unzip_directory_name();
$message = get_string('fileswillbeextractedintofolder', 'artefact.file', $name['fullname']);
$goto = files_page($file);
......@@ -129,21 +129,23 @@ function files_page($file) {
}
function unzip_artefact_submit(Pieform $form, $values) {
global $file, $zipinfo, $SESSION;
global $file, $SESSION;
$zipinfo = $file->read_archive();
$from = files_page($file);
if (count($zipinfo->names) > 10) {
$SESSION->set('unzip', array('file' => $file->get('id'), 'from' => $from, 'artefacts' => count($zipinfo->names)));
$SESSION->set('unzip', array('file' => $file->get('id'), 'from' => $from, 'artefacts' => count($zipinfo->names), 'zipinfo' => $zipinfo));
$smarty = smarty();
$smarty->display('artefact:file:extract-progress.tpl');
exit;
}
$created = $file->unzip();
$status = $file->extract();
$SESSION->add_ok_msg(get_string('extractfilessuccess', 'artefact.file', $created->folders, $created->files));
$redirect = $from . (strpos($from, '?') === false ? '?' : '&') . 'folder=' . $created->basefolderid;
$SESSION->add_ok_msg(get_string('extractfilessuccess', 'artefact.file', $status['folderscreated'], $status['filescreated']));
$redirect = $from . (strpos($from, '?') === false ? '?' : '&') . 'folder=' . $status['basefolderid'];
redirect($redirect);
}
......
......@@ -57,12 +57,20 @@
<description>gz</description>
<mimetypes>
<mimetype>application/g-zip</mimetype>
<mimetype>application/x-gzip</mimetype>
</mimetypes>
</filetype>
<filetype>
<description>tgz</description>
<mimetypes>
<mimetype>application/x-compressed-tar</mimetype>
</mimetypes>
</filetype>
<filetype>
<description>bz2</description>
<mimetypes>
<mimetype>application/x-bzip2</mimetype>
<mimetype>application/x-bzip</mimetype>
</mimetypes>
</filetype>
......
......@@ -215,9 +215,11 @@ $string['wrongfiletypeforblock'] = 'The file you uploaded was not the correct ty
$string['Contents'] = 'Contents';
$string['Continue'] = 'Continue';
$string['extractfilessuccess'] = 'Created %s folders and %s files.';
$string['filesextractedfromarchive'] = 'Files extracted from archive';
$string['filesextractedfromziparchive'] = 'Files extracted from Zip archive';
$string['fileswillbeextractedintofolder'] = 'Files will be extracted into %s';
$string['insufficientquotaforunzip'] = 'Your remaining file quota is too small to unzip this file.';
$string['invalidarchive'] = 'Error reading archive file.';
$string['pleasewaitwhileyourfilesarebeingunzipped'] = 'Please wait while your files are being unzipped.';
$string['spacerequired'] = 'Space Required';
$string['Unzip'] = 'Unzip';
......
......@@ -756,16 +756,8 @@ class ArtefactTypeFile extends ArtefactTypeFileBase {
$data->height = $imageinfo[1];
return new ArtefactTypeImage(0, $data);
}
if ((!isset($data->filetype) || in_array($data->filetype, ArtefactTypeArchive::zip_mime_types()))
&& function_exists('zip_open')) {
$zip = zip_open($path);
if (is_resource($zip)) {
zip_close($zip);
if (!isset($data->filetype)) {
$data->filetype = 'application/zip';
}
return new ArtefactTypeArchive(0, $data);
}
if ($archive = ArtefactTypeArchive::new_archive($path, $data)) {
return $archive;
}
return new ArtefactTypeFile(0, $data);
}
......@@ -1580,60 +1572,210 @@ class ArtefactTypeProfileIcon extends ArtefactTypeImage {
class ArtefactTypeArchive extends ArtefactTypeFile {
public static function get_icon($options=null) {
global $THEME;
return $THEME->get_url('images/archive.gif');
private $archivetype;
private $handle;
private $info;
private $data = array();
public function __construct($id = 0, $data = null) {
parent::__construct($id, $data);
if ($this->id) {
$descriptions = self::archive_file_descriptions();
$validtypes = self::archive_mime_types();
$this->archivetype = $descriptions[$validtypes[$this->filetype]->description];
}
}
public static function zip_mime_types() {
public static function new_archive($path, $data) {
if (!isset($data->filetype)) {
return self::archive_from_file($path, $data);
}
$descriptions = self::archive_file_descriptions();
$validtypes = self::archive_mime_types();
if (isset($validtypes[$data->filetype])) {
return self::archive_from_file($path, $data, $descriptions[$validtypes[$data->filetype]->description]);
}
return false;
}
public static function is_zip($path) {
if (function_exists('zip_read')) {
$zip = zip_open($path);
if (is_resource($zip)) {
zip_close($zip);
return true;
}
}
return false;
}
public static function is_tar($path) {
require_once('Archive/Tar.php');
if (!$tar = new Archive_Tar($path)) {
return false;
}
$list = $tar->listContent();
if (empty($list)) {
return false;
}
switch ($tar->_compress_type) {
case 'gz': return 'application/x-gzip';
case 'bz2': return 'application/x-bzip2';
case 'none': return 'application/x-tar';
}
return false;
}
public static function archive_from_file($path, $data, $type=null) {
if (is_null($type)) {
if (self::is_zip($path)) {
$data->filetype = 'application/zip';
$data->archivetype = 'zip';
return new ArtefactTypeArchive(0, $data);
}
if ($data->filetype = self::is_tar($path)) {
$data->archivetype = 'tar';
// @todo:: TEST!
return new ArtefactTypeArchive(0, $data);
}
}
else if ($type == 'zip' && self::is_zip($path) || $type == 'tar' && self::is_tar($path)) {
$data->archivetype = $type;
return new ArtefactTypeArchive(0, $data);
}
return false;
}
public static function archive_file_descriptions() {
static $descriptions = null;
if (is_null($descriptions)) {
$descriptions = array('tar' => 'tar', 'gz' => 'tar', 'tgz' => 'tar', 'bz2' => 'tar');
if (function_exists('zip_open')) {
$descriptions['zip'] = 'zip';
}
}
return $descriptions;
}
public static function archive_mime_types() {
static $mimetypes = null;
if (is_null($mimetypes)) {
$mimetypes = get_column('artefact_file_mime_types', 'mimetype', 'description', 'zip');
$descriptions = self::archive_file_descriptions();
$mimetypes = get_records_select_assoc('artefact_file_mime_types', 'description IN (' . join(',', array_map('db_quote', array_keys($descriptions))) . ')');
}
return $mimetypes;
}
public static function zip_file_info($zip) {
$info = (object) array(
public static function get_icon($options=null) {
global $THEME;
return $THEME->get_url('images/archive.gif');
}
public function open_archive() {
if ($this->archivetype == 'zip') {
$this->handle = zip_open($this->get_path());
if (!is_resource($this->handle)) {
$this->handle = null;
throw new NotFoundException();
}
}
else if ($this->archivetype == 'tar') {
require_once('Archive/Tar.php');
if (!$this->handle = new Archive_Tar($this->get_path())) {
throw new NotFoundException();
}
}
}
public function set_archive_info($zipinfo) {
$this->info = $zipinfo;
}
public function read_archive() {
if (!$this->handle) {
$this->open_archive();
}
if ($this->info) {
return $this->info;
}
$this->info = (object) array(
'files' => 0,
'folders' => 0,
'totalsize' => 0,
'names' => array(),
);
while ($entry = zip_read($zip)) {
$name = zip_entry_name($entry);
$info->names[] = $name;
if (substr($name, -1) == '/') {
$info->folders++;
if ($this->archivetype == 'zip') {
while ($entry = zip_read($this->handle)) {
$name = zip_entry_name($entry);
$this->info->names[] = $name;
if (substr($name, -1) == '/') {
$this->info->folders++;
}
else {
$this->info->files++;
if ($size = zip_entry_filesize($entry)) {
$this->info->totalsize += $size;
}
}
}
else {
$info->files++;
if ($size = zip_entry_filesize($entry)) {
$info->totalsize += $size;
}
else if ($this->archivetype == 'tar') {
$foldernames = array();
$list = $this->handle->listContent();
if (empty($list)) {
throw new SystemException("Unknown archive type");
}
foreach ($list as $entry) {
$path = split('/', $entry['filename']);
if ($isfolder = substr($entry['filename'], -1) == '/') {
array_pop($path);
}
$folder = '';
for ($i = 0; $i < count($path) - 1; $i++) {
$folder .= $path[$i] . '/';
if (!isset($foldernames[$folder])) {
$foldernames[$folder] = 1;
$this->info->names[] = $folder;
$this->info->folders++;
}
}
if (!$isfolder) {
$this->info->names[] = $entry['filename'];
$this->info->files++;
$this->info->totalsize += $entry['size'];
}
}
}
$info->displaysize = ArtefactTypeFile::short_size($info->totalsize);
return $info;
else {
throw new SystemException("Unknown archive type");
}
$this->info->displaysize = ArtefactTypeFile::short_size($this->info->totalsize);
return $this->info;
}
public static function get_unzip_directory_name($file) {
$folderdata = ArtefactTypeFileBase::artefactchooser_folder_data($file);
$parent = $file->get('parent');
public function unzip_directory_name() {
if (isset($this->data['unzipdir'])) {
return $this->data['unzipdir'];
}
$folderdata = ArtefactTypeFileBase::artefactchooser_folder_data($this);
$parent = $this->get('parent');
$strpath = ArtefactTypeFileBase::get_full_path($parent, $folderdata->data);
$extn = $file->get('oldextension');
$name = $file->get('title');
$extn = $this->get('oldextension');
$name = $this->get('title');
if (substr($name, -1-strlen($extn)) == '.' . $extn) {
$name = substr($name, 0, strlen($name)-1-strlen($extn));
}
$name = ArtefactTypeFileBase::get_new_file_title($name, $parent, $file->get('owner'), $file->get('group'), $file->get('institution'));
return array('basename' => $name, 'fullname' => $strpath . $name);
$name = ArtefactTypeFileBase::get_new_file_title($name, $parent, $this->get('owner'), $this->get('group'), $this->get('institution'));
$this->data['unzipdir'] = array('basename' => $name, 'fullname' => $strpath . $name);
return $this->data['unzipdir'];
}
public function unzip($progresscallback=null) {
global $USER;
$foldername = ArtefactTypeArchive::get_unzip_directory_name($this);
public function create_base_folder() {
$foldername = $this->unzip_directory_name();
$foldername = $foldername['basename'];
$data = (object) array(
......@@ -1641,58 +1783,98 @@ class ArtefactTypeArchive extends ArtefactTypeFile {
'group' => $this->get('group'),
'institution' => $this->get('institution'),
'title' => $foldername,
'description' => get_string('filesextractedfromziparchive', 'artefact.file'),
'description' => get_string('filesextractedfromarchive', 'artefact.file'),
'parent' => $this->get('parent'),
);
$user = $data->owner ? $USER : null;
$basefolder = new ArtefactTypeFolder(0, $data);
$basefolder->commit();
$folders = array('.' => $basefolder->get('id'));
$status = (object) array('folders' => 1, 'files' => 0, 'basefolderid' => $basefolder->get('id'));
return $basefolder->get('id');
}
public function create_folder($folder) {
$newfolder = new ArtefactTypeFolder(0, $this->data['template']);
$newfolder->commit();
$folderindex = ($folder == '.' ? '' : ($folder . '/')) . $this->data['template']->title;
$this->data['folderids'][$folderindex] = $newfolder->get('id');
$this->data['folderscreated']++;
}
public function extract($progresscallback=null) {
global $USER;
unset($data->description);
$quotauser = $this->owner ? $USER : null;
$this->data['basefolderid'] = $this->create_base_folder();
$this->data['folderids'] = array('.' => $this->data['basefolderid']);
$this->data['folderscreated'] = 1;
$this->data['filescreated'] = 0;
$this->data['template'] = (object) array(
'owner' => $this->get('owner'),
'group' => $this->get('group'),
'institution' => $this->get('institution'),
);
$tempdir = get_config('dataroot') . 'artefact/file/temp';
check_dir_exists($tempdir);
$zip = zip_open($this->get_path());
$tempfile = tempnam($tempdir, '');
$this->read_archive();
$i = 0;
if ($this->archivetype == 'tar') {
while ($entry = zip_read($zip)) {
$name = zip_entry_name($entry);
$folder = dirname($name);
$data->title = basename($name);
$data->parent = $folders[$folder];
if (substr($name, -1) == '/') {
$newfolder = new ArtefactTypeFolder(0, $data);
$newfolder->commit();
$status->folders++;
$folderindex = ($folder == '.' ? '' : ($folder . '/')) . $data->title;
$folders[$folderindex] = $newfolder->get('id');
}
else {
$h = fopen($tempfile, 'w');
$size = zip_entry_filesize($entry);
$contents = zip_entry_read($entry, $size);
fwrite($h, $contents);
fclose($h);
ArtefactTypeFile::save_file($tempfile, $data, $user, true);
$status->files++;
// Untar everything into a temp directory first
$tempsubdir = tempnam($tempdir, '');
unlink($tempsubdir);
mkdir($tempsubdir);
if (!$this->handle->extract($tempsubdir)) {
throw new SystemException("Unable to extract archive into $tempsubdir");
}
if ($progresscallback) {
$i++;
if ($i % 5 == 0) {
$i = 0;
foreach ($this->info->names as $name) {
$folder = dirname($name);
$this->data['template']->parent = $this->data['folderids'][$folder];
$this->data['template']->title = basename($name);
if (substr($name, -1) == '/') {
$this->create_folder($folder);
}
else {
ArtefactTypeFile::save_file($tempsubdir . '/' . $name, $this->data['template'], $quotauser, true);
$this->data['filescreated']++;
}
if ($progresscallback && ++$i % 5 == 0) {
call_user_func_array($progresscallback, $i);
}
}
} else if ($this->archivetype == 'zip') {
$tempfile = tempnam($tempdir, '');
$i = 0;
while ($entry = zip_read($this->handle)) {
$name = zip_entry_name($entry);
$folder = dirname($name);
$this->data['template']->parent = $this->data['folderids'][$folder];
$this->data['template']->title = basename($name);
if (substr($name, -1) == '/') {
$this->create_folder($folder);
}
else {
$h = fopen($tempfile, 'w');
$size = zip_entry_filesize($entry);
$contents = zip_entry_read($entry, $size);
fwrite($h, $contents);
fclose($h);
ArtefactTypeFile::save_file($tempfile, $this->data['template'], $quotauser, true);
$this->data['filescreated']++;
}
if ($progresscallback && ++$i % 5 == 0) {
call_user_func_array($progresscallback, $i);
}
}
}
return $status;
return $this->data;
}
}
......
......@@ -2,12 +2,15 @@
{if $file}
<h5>{$file->get('title')|escape}</h5>
{if $zipinfo}
<p>
<span><label>{str tag=Files section=artefact.file}:</label> {$zipinfo->files}&nbsp;</span>
<span><label>{str tag=Folders section=artefact.file}:</label> {$zipinfo->folders}</span>
<span><label>{str tag=spacerequired section=artefact.file}:</label> {$zipinfo->displaysize}</span>
</p>
{/if}
<p>{$message|escape}</p>
{if $zipinfo}
{$form}
<p>
<div><label>{str tag=Contents section=artefact.file}:</label></div>
......@@ -15,6 +18,7 @@
<div>{$name|escape}</div>
{/foreach}
</p>
{/if}
{/if}
{include file="footer.tpl"}
......@@ -28,7 +28,7 @@
defined('INTERNAL') || die();
$config = new StdClass;
$config->version = 2009092300;
$config->version = 2009101200;
$config->release = '1.1.0';
?>
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment