Forked from
Stud.IP / Stud.IP
3383 commits behind the upstream repository.
-
Closes #1235 Merge request studip/studip!806
Closes #1235 Merge request studip/studip!806
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
FileArchiveManager.class.php 38.29 KiB
<?php
/**
* FileArchiveManager.class.php
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* @author Moritz Strohm <strohm@data-quest.de>
* @copyright 2016 data-quest
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
* @category Stud.IP
*/
/**
* The FileArchiveManager class gives programmers a simple way to handle
* file archives by providing different methods for packing and unpacking
* file archives in a simple manner.
*/
class FileArchiveManager
{
//ARCHIVE HELPER METHODS
/**
* Adds a file to an archive using its FileType object.
*
* @param ZipArchive $archive The Zip archive where the FileRef shall be added to.
* @param FileType $file_type The FileType which shall be added to the zip archive.
* @param string $user_id The user who wishes to add the FileRef to the archive.
* @param string $archive_fs_path The path of the file inside the archive's file system.
* @param bool $do_user_permission_checks Set to true if reading/downloading permissions
* shall be checked. False otherwise. Default is true.
* @param bool $ignore_user Set to true, if a file
* which has no download restrictions shall be included
* and the user-specific download condition check shall be ignored.
* If this parameter is set to true, the user_id parameter is irrelevant.
* The default for this parameter is false.
* @return bool True on success, false on failure.
*
* @throws Exception|FileArchiveManagerException If an error occurs a general exception or a more
* special exception is thrown.
*/
public static function addFileTypeToArchive(
ZipArchive $archive,
FileType $file_type,
$user_id = null,
$archive_fs_path = '',
$do_user_permission_checks = true,
$ignore_user = false,
&$file_list = null
)
{
$archive_max_size = Config::get()->ZIP_DOWNLOAD_MAX_SIZE * 1024 * 1024;
//For FileRef objects we first have to do permission checks
//using the FileRef's folder object.
$adding_allowed = false;
if ($do_user_permission_checks) {
$folder = $file_type->getFolderType();
if (!$folder) {
return false;
}
if ($folder->isReadable($user_id) && $file_type->isDownloadable($user_id)) {
//FileRef is readable and downloadable for the user (identified by $user_id).
$adding_allowed = true;
}
} elseif ($ignore_user) {
//we have to check the download condition by looking at the
//terms of use object of the FileType:
$terms_of_use = $file_type->getTermsOfUse();
if ($terms_of_use && $terms_of_use->download_condition == 0) {
$adding_allowed = true;
}
} else {
//Totally skip permission checks:
$adding_allowed = true;
}
if ($adding_allowed) {
//Adding the FileType is allowed:
$file_contains_link = false;
if ($file_type instanceof LibraryFile) {
$file_contains_link = !$file_type->hasFileAttached();
} else {
$file_contains_link = $file_type instanceof URLFile;
}
// Increase download counter
if ($file_type instanceof StandardFile) {
$file_ref = $file_type->getFileRef();
$file_ref->incrementDownloadCounter();
}
if ($file_contains_link) {
//The FileType references a link:
//Put the URL into a file ending with .url:
$url = $file_type->getDownloadURL();
if ($url) {
//The URL has been fetched and we can put it
//in a file in the archive:
$archive->addFromString(
$archive_fs_path . $file_type->getFilename() . '.url',
"[InternetShortcut]\nURL={$url}\n"
);
//Check the file size of the archive:
if (file_exists($archive->filename) && filesize($archive->filename) > $archive_max_size) {
throw new FileArchiveManagerException(
sprintf(
_('Das ZIP-Archiv ist zu groß! Die maximal erlaubte Größe ist %d bytes!'),
$archive_max_size
)
);
}
if (is_array($file_list)) {
$user = $file_type->getUser();
$file_list[] = [
'name' => $file_type->getFilename(),
'size' => $file_type->getSize(),
'first_name' => ($user instanceof User) ? $user->vorname : '',
'last_name' => ($user instanceof User) ? $user->nachname : '',
'downloads' => $file_type->getDownloads(),
'mkdate' => date('d.m.Y H:i', $file_type->getMakeDate()),
'path' => ($archive_fs_path . $file_type->getFilename())
];
}
return true;
}
} else {
//Get the file's path (if the file exists) and add the file to the archive:
$path = $file_type->getPath();
if ($path) {
//It is a file in the file system:
if (file_exists($path)) {
$archive->addFile($path, $archive_fs_path . $file_type->getFilename());
//Check the file size of the archive:
if (file_exists($archive->filename) && filesize($archive->filename) > $archive_max_size) {
throw new FileArchiveManagerException(
sprintf(
_('Das ZIP-Archiv ist zu groß! Die maximal erlaubte Größe ist %d bytes!'),
$archive_max_size
)
);
}
//Add the file to the file list (if available):
if (is_array($file_list)) {
$archive_max_num_files = Config::get()->ZIP_DOWNLOAD_MAX_FILES;
$archive_max_size = Config::get()->ZIP_DOWNLOAD_MAX_SIZE * 1024 * 1024; //1048576 bytes = 1 Mebibyte
$user = $file_type->getUser();
$file_list[] = [
'name' => $file_type->getFilename(),
'size' => $file_type->getSize(),
'first_name' => ($user instanceof User) ? $user->vorname : '',
'last_name' => ($user instanceof User) ? $user->nachname : '',
'downloads' => $file_type->getDownloads(),
'mkdate' => date('d.m.Y H:i', $file_type->getMakeDate()),
'path' => ($archive_fs_path . $file_type->getFilename())
];
if (count($file_list) > $archive_max_num_files) {
$archive->unchangeAll();
unlink($archive->filename);
throw new FileArchiveManagerException(
sprintf(
_('Das Archiv beinhaltet zu viele Dateibereich-Objekte! Die Obergrenze liegt bei %d Objekten!'),
$archive_max_num_files
)
);
}
if (array_sum(array_column($file_list, 'size')) > $archive_max_size) {
$archive->unchangeAll();
unlink($archive->filename);
throw new FileArchiveManagerException(
sprintf(
_('Das ZIP-Archiv ist zu groß! Die maximal erlaubte Größe ist %d bytes!'),
$archive_max_size
)
);
}
}
}
}
}
}
//Something must have gone wrong:
return false;
}
/**
* Adds a FileRef to a Zip archive.
* This is only a wrapper to addFileTypeToArchive that exists only
* for compatibility reasons.
*
* @see addFileTypeToArchive
*/
public static function addFileRefToArchive(
ZipArchive $archive,
FileRef $file_ref,
$user_id = null,
$archive_fs_path = '',
$do_user_permission_checks = true,
$ignore_user = false,
&$file_list = null
)
{
$file_type = $file_ref->getFileType();
if ($file_type instanceof FileType) {
return self::addFileTypeToArchive(
$archive,
$file_type,
$user_id,
$archive_fs_path,
$do_user_permission_checks,
$ignore_user,
$file_list
);
}
//The file type variable does not contain a FileType object.
return false;
}
/**
* Adds a FolderType instance to a Zip archive.
*
* @param ZipArchive $archive The Zip archive where the FileRef shall be added to.
* @param FileRef $file_ref The FileRef which shall be added to the zip archive.
* @param string $user_id The user who wishes to add the FileRef to the archive.
* @param string $archive_fs_path The path of the folder inside the archive's file system.
* @param bool $do_user_permission_checks Set to true if reading/downloading permissions
* shall be checked. False otherwise. Default is true.
* @param bool $keep_hierarchy True, if the folder hierarchy shall be kept.
* False, if the folder hierarchy shall be flattened.
* @param bool $ignore_user Set to true, if a folder
* of type StandardFolder shall be included without checking
* if the user (identified by user_id) can read it.
* @return bool True on success, false on failure.
*
* @throws Exception|FileArchiveManagerException If an error occurs a general exception or a more
* special exception is thrown.
*/
public static function addFolderToArchive(
ZipArchive $archive,
FolderType $folder,
$user_id = null,
$archive_fs_path = '',
$do_user_permission_checks = true,
$keep_hierarchy = true,
$ignore_user = false,
&$file_list = null
) {
if ($do_user_permission_checks) {
//Check if the folder is readable for the user (identified by $user_id):
if (!$folder->isReadable($user_id)) {
//Folder is not readable:
return false;
}
} elseif ($ignore_user
&& !($folder instanceof StandardFolder)
&& in_array($folder->range_type, ['course', 'institute']))
{
//If user permissions shall be skipped the folder must be
//an instance of StandardFolder and the folder's range type
//must be course or institute since we can only be sure
//that StandardFolder instances in courses or institutes
//are readable by everyone.
return false;
}
$folder_zip_path = $archive_fs_path;
if ($keep_hierarchy) {
$folder_zip_path .= $folder->name;
$archive->addEmptyDir($folder_zip_path);
}
foreach ($folder->getFiles() as $file) {
/*if (!$file_ref instanceof FileRef) { TODO: OwnCloudPlugin is this ready?
$plugin = PluginManager::getInstance()->getPlugin($folder->range_id);
if (!$plugin) {
$plugin = PluginManager::getInstance()->getPlugin($folder->range_type);;
}
if ($plugin) {
$file_ref = $plugin->getPreparedFile($file_ref->id, true);
}
}*/
self::addFileTypeToArchive(
$archive,
$file,
$user_id,
//keep hierarchy in zip file (files and subdirectories)
$keep_hierarchy ? $folder_zip_path . '/' : '',
$do_user_permission_checks,
$ignore_user,
$file_list
);
}
foreach ($folder->getSubfolders() as $subfolder) {
self::addFolderToArchive(
$archive,
$subfolder,
$user_id,
//keep hierarchy in zip file (files and subdirectories)
$keep_hierarchy ? $folder_zip_path . '/' : '',
$do_user_permission_checks,
$keep_hierarchy,
$ignore_user,
$file_list
);
}
return true;
}
//ARCHIVE CREATION METHODS
/**
* General method for creating file archives.
*
* This method is a generalisation for all archive creation methods.
* For easier archive creation you may use the other archive creation
* methods which work with less arguments.
*
* @param Array $file_area_objects Array of FileRef, FileURL, Folder or FolderType objects.
* $file_area_objects may contain a mix between those object types.
* @param string $user_id The user who wishes to pack files.
* @param string $archive_file_path The path for the archive file.
* @param bool $do_user_permission_checks Set to true if individual
* reading/downloading permissions shall be checked. False otherwise.
* Default is true.
* @param bool $keep_hierarchy True, if the folder hierarchy shall be kept.
* False, if the folder hierarchy shall be flattened. Default is true.
* @param bool $ignore_user Set to true, if all files
* which have no download restrictions and all folders which are of type
* StandardFolder shall be included and the user-specific
* download condition check shall be ignored.
* If this parameter is set to true, the user_id parameter is irrelevant.
* The default for this parameter is false.
* @param string $zip_encoding encoding for filenames in zip
* @param bool $add_filelist_to_archive If this is set to true a file list
* in the CSV format will be added to the archive. Its name is hardcoded
* to archive_filelist.csv. The default value of $add_filelist_to_archive
* is false which means no file list is added.
*
* @return bool True, if the archive file was created and saved successfully
* at $archive_file_path, false otherwise.
*
* @throws Exception|FileArchiveManagerException If an error occurs a general exception or a more
* special exception is thrown.
*/
public static function createArchive(
$file_area_objects = [],
$user_id = null,
$archive_file_path = '',
$do_user_permission_checks = true,
$keep_hierarchy = true,
$ignore_user = false,
$zip_encoding = 'UTF-8',
$add_filelist_to_archive = false
)
{
// check if archive path is set:
if (!$archive_file_path) {
throw new FileArchiveManagerException(
_('Der Zielpfad für das Archiv wurde nicht angegeben!')
);
}
// $file_area_objects must be a non-empty array!
// Otherwise we would return an empty Zip archive.
if (!is_array($file_area_objects) || empty($file_area_objects)) {
throw new FileArchiveManagerException(
_('Es wurden keine Dateien ausgewählt!')
);
}
// We can create the Zip archive now since its path exists in the file system
// and furthermore there are file area objects available.
$archive = new Studip\ZipArchive();
if (!$archive->open($archive_file_path, ZipArchive::CREATE | ZipArchive::OVERWRITE)) {
throw new FileArchiveManagerException('Error opening new ZIP archive!');
}
$archive->setOutputEncoding($zip_encoding);
//If $file_list is not an array
//then no files are added to the file list.
$file_list = null;
if ($add_filelist_to_archive) {
$file_list = [];
}
foreach ($file_area_objects as $file_area_object) {
if ($file_area_object instanceof FileRef) {
self::addFileRefToArchive(
$archive,
$file_area_object,
$user_id,
'',
$do_user_permission_checks,
$ignore_user,
$file_list
);
} elseif ($file_area_object instanceof FileType) {
self::addFileTypeToArchive(
$archive,
$file_area_object,
$user_id,
'',
$do_user_permission_checks,
$ignore_user,
$file_list
);
} elseif ($file_area_object instanceof Folder || $file_area_object instanceof FolderType) {
$folder = $file_area_object;
if ($folder instanceof Folder) {
//We use FolderType instances here.
$folder = $folder->getTypedFolder();
}
self::addFolderToArchive(
$archive,
$folder,
$user_id,
'',
$do_user_permission_checks,
$keep_hierarchy,
$ignore_user,
$file_list
);
}
}
if ($archive->numFiles > 0) {
//At least one file is in the archive.
if ($add_filelist_to_archive) {
//If a file list shall be included in the ZIP archive
//we must now make a CSV file out of file_list:
$csv_data = array_merge(
[
[
_('Name'),
_('Größe'),
_('Vorname'),
_('Nachname'),
_('Downloads'),
_('Datum'),
_('Pfad')
]
],
$file_list
);
//The CSV file has been generated.
//Now we must add it to the archive:
$archive->addFromString('archive_filelist.csv', array_to_csv($csv_data));
}
//Now the ZIP file is really finished:
return $archive->close();
}
//empty archive
throw new FileArchiveManagerException(
_('Das ZIP Archiv enthält keine Dateien!')
);
}
/**
* Puts files (identified by their file refs) into one file archive.
*
* @param FileRef[] $file_refs Array of FileRef objects.
* @param User $user The user who wishes to pack files.
* @param string $archive_file_path The path for the archive file.
* @param bool $do_user_permission_checks Set to true if reading/downloading
* permissions shall be checked. False otherwise. Default is true.
*
* @return bool True, if the archive file was created and saved successfully
* at $archive_file_path, false otherwise.
*
* @throws Exception|FileArchiveManagerException If an error occurs
* a general exception or a more special exception is thrown.
*/
public static function createArchiveFromFileRefs(
$file_refs,
User $user,
$archive_file_path = '',
$do_user_permission_checks = true
)
{
if (!$archive_file_path) {
throw new FileArchiveManagerException(
_('Der Zielpfad für das Archiv wurde nicht angegeben!')
);
}
//We must now collect all the files from these FileRefs and copy them
//into the new archive file.
return self::createArchive(
$file_refs,
$user->id,
$archive_file_path,
$do_user_permission_checks,
false //do not keep the file hierarchy
);
}
/**
* Returns all children of a folder type.
*
* @param FolderType $folder
* @return array
*/
private static function getFolderChildren(FolderType $folder)
{
$children = [];
foreach ($folder->subfolders as $folder) {
$children[] = $folder;
}
foreach ($folder->file_refs as $ref) {
$children[] = $ref;
}
return $children;
}
/**
* Creates an archive that contains all files of a course the given user
* is allowed to download.
*
* @param FolderType $folder The folder whose files shall be put inside an archive.
* @param string $user_id The ID of the user who wishes to put the course's files into an archive
* @param string $archive_file_path The path for the archive file.
* @param bool $do_user_permission_checks Set to true if reading/downloading permissions
* shall be checked. False otherwise. Default is true.
* @param bool $keep_hierarchy True, if the file hierarchy shall be kept inside the archive.
* If $keep_hierarchy is set to false you will get an archive that contains only files
* and no subdirectories.
*
* @return bool True, if the archive file was created and saved successfully
* at $archive_file_path, false otherwise.
*
* @throws Exception|FileArchiveManagerException If an error occurs
* a general exception or a more special exception is thrown.
*/
public static function createArchiveFromFolder(
FolderType $folder,
$user_id = null,
$archive_file_path = '',
$do_user_permission_checks = true,
$keep_hierarchy = true
)
{
return self::createArchive(
self::getFolderChildren($folder),
$user_id,
$archive_file_path,
$do_user_permission_checks,
$keep_hierarchy
);
}
/**
* Creates an archive that contains all files of a course the given user
* is allowed to download.
*
* @param string $course_id The ID of the course whose files shall be put inside an archive.
* @param string $user_id The ID of the user who wishes to put the course's files into an archive
* @param string $archive_file_path The path for the archive file.
* @param bool $do_user_permission_checks Set to true if reading/downloading permissions
* shall be checked. False otherwise. Default is true.
* @param bool $keep_hierarchy True, if the file hierarchy shall be kept inside the archive.
* If $keep_hierarchy is set to false you will get an archive that contains only files
* and no subdirectories.
*
* @return bool True, if the archive file was created and saved successfully
* at $archive_file_path, false otherwise.
*
* @throws Exception|FileArchiveManagerException If an error occurs
* a general exception or a more special exception is thrown.
*/
public static function createArchiveFromCourse(
$course_id,
$user_id = null,
$archive_file_path = '',
$do_user_permission_checks = true,
$keep_hierarchy = true
)
{
$folder = Folder::findTopFolder($course_id);
if (!$folder) {
return null;
}
$folder = $folder->getTypedFolder();
if (!$folder) {
return null;
}
return self::createArchive(
self::getFolderChildren($folder),
$user_id,
$archive_file_path,
$do_user_permission_checks,
$keep_hierarchy
);
}
/**
* Creates an archive that contains all files of an institute the given user
* is allowed to download.
*
* @param string $institute_id The ID of the institute whose files shall be put inside an archive.
* @param string $user_id The ID of the user who wishes to put the institute's files into an archive
* @param string $archive_file_path The path for the archive file.
* @param bool $do_user_permission_checks Set to true if reading/downloading permissions
* shall be checked. False otherwise. Default is true.
* @param bool $keep_hierarchy True, if the file hierarchy shall be kept inside the archive.
* If $keep_hierarchy is set to false you will get an archive that contains only files
* and no subdirectories.
*
* @return bool True, if the archive file was created and saved successfully
* at $archive_file_path, false otherwise.
*
* @throws Exception|FileArchiveManagerException If an error occurs
* a general exception or a more special exception is thrown.
*/
public static function createArchiveFromInstitute(
$institute_id,
$user_id = null,
$archive_file_path = '',
$do_user_permission_checks = true,
$keep_hierarchy = true)
{
$folder = Folder::findTopFolder($institute_id);
if(!$folder) {
return null;
}
$folder = $folder->getTypedFolder();
if(!$folder) {
return null;
}
return self::createArchive(
self::getFolderChildren($folder),
$archive_file_path,
$do_user_permission_checks,
$keep_hierarchy
);
}
/**
* Creates an archive that contains all files of a user, if the current
* user has root permissions to do this.
*
* @param string $user_id The ID of the user whose files shall be put inside an archive.
* @param string $archive_file_path The path for the archive file.
* @param bool $do_user_permission_checks Set to true if reading/downloading permissions
* shall be checked. False otherwise. Default is true.
* @param bool $keep_hierarchy True, if the file hierarchy shall be kept inside the archive.
* If $keep_hierarchy is set to false you will get an archive that contains only files
* and no subdirectories.
*
* @return bool True, if the archive file was created and saved successfully
* at $archive_file_path, false otherwise.
*
* @throws Exception|FileArchiveManagerException If an error occurs
* a general exception or a more special exception is thrown.
*/
public static function createArchiveFromUser(
$user_id,
$archive_file_path = '',
$do_user_permission_checks = true,
$keep_hierarchy = true
)
{
$folder = Folder::findTopFolder($user_id);
if (!$folder) {
return null;
}
$folder = $folder->getTypedFolder();
if (!$folder) {
return null;
}
return self::createArchive(
self::getFolderChildren($folder),
$archive_file_path,
$do_user_permission_checks,
$keep_hierarchy
);
}
/**
* This method creates an archive with the content of a physical folder
* (A folder inside the operating system's file system).
*
* @param string $folder_path The path to the physical folder
* which content shall be added to a file archive.
* @param string $archive_file_path The path to the archive file which
* shall be created.
*
* @return True, if all files were added successfully, false otherwise.
*
* @throws Exception|FileArchiveManagerException If an error occurs
* a general exception or a more special exception is thrown.
*/
public static function createArchiveFromPhysicalFolder($folder_path, $archive_file_path)
{
if (!$folder_path || !$archive_file_path) {
//we can't work with empty paths!
return false;
}
if (!file_exists($folder_path)) {
//path to physical folder does not exist!
throw new FileArchiveManagerException(
_('Der Ordner wurde im Dateisystem des Servers nicht gefunden!')
);
}
//Put all the content of the folder inside an archive:
$archive = Studip\ZipArchive::create($archive_file_path, true);
$result = $archive->addFromPath($folder_path);
$archive->close();
return $result;
}
//ARCHIVE EXTRACTION METHODS
/**
* This is a helper method that builds a subfolder hierarchy inside
* a folder by looking at a string representing a file system path.
*
* The variable $path contains a hierarchy of subfolders that shall be created
* inside the given folder. If $path contains "folder1/folder2/folder3" then
* the given folder will get a subfolder named "folder1". The folder
* "folder1" itself will get a subfolder named "folder2" and so on.
*
* @param FolderType $folder The folder where a subfolder path shall be created.
* @param User $user The user who wishes to create the path.
* @param string $path The path which shall be created inside $folder.
*
* @return FolderType[] An array with FolderType objects representing
* each element of $path.
*/
public static function createFolderPath(FolderType $folder, User $user, $path = '')
{
if (!$path) {
return [];
}
// now we strip leading and trailing slashes, whitespaces and other characters:
// then we convert path into an array of strings:
$path = trim($path, ' /');
$path = explode('/', $path);
//now we loop through path and build subfolders:
$folder_path = [];
$current_folder = $folder;
foreach ($path as $new_folder_name) {
//first we check if the folder already exists:
foreach ($current_folder->getSubfolders() as $subfolder) {
if ($subfolder->name === $new_folder_name) {
//We have found a folder that has the name $new_folder_name:
//No need to create a new folder, we can use that folder
//and continue with it:
$current_folder = $subfolder;
$folder_path[] = $subfolder;
//start next iteration of the outer foreach loop:
continue 2;
}
}
//If code execution has reached this point we have looped
//throug all subfolders of the current folder and couldn't find
//any subfolder that matches the name given in $new_folder_name.
//Therefore we must create a new folder here, if possible:
//Check the user's permissions first:
if ($current_folder->isSubfolderAllowed($user->id)) {
//Create a subfolder:
$result = FileManager::createSubFolder(
$current_folder,
$user,
get_class($current_folder) === RootFolder::class ? StandardFolder::class : get_class($current_folder),
$new_folder_name
);
if ($result instanceof FolderType) {
$folder_path[] = $result;
}
}
}
return $folder_path;
}
/**
* Extracts one file from an opened archive and stores it in a folder.
*
* @param ZipArchive $archive The archive from which a file shall be extracted.
* @param string $archive_path The path of the file in the archive.
* @param FolderType $target_folder The folder where the file shall be stored.
* @param User $user The user who wishes to extract the file from the archive.
*
* @return FileRef|null FileRef instance on success, null otherwise.
*/
public static function extractFileFromArchive(
Studip\ZipArchive $archive,
$archive_path,
FolderType $target_folder,
User $user
)
{
$file_resource = $archive->getStream($archive_path);
$file_info = $archive->statName($archive_path);
if (!$file_resource) {
return null;
}
$file = new File();
$file->user_id = $user->id;
$file->name = $archive->convertArchiveFilename(basename($archive_path));
$file->mime_type = get_mime_type($file->name);
$file->size = $file_info['size'];
$file->store();
// Ok, we have a file object in the database. Now we must connect
// it with the data file by extracting the data file into
// the place, where the file's content has to be placed.
$file_path = pathinfo($file->getPath(), PATHINFO_DIRNAME);
// Create the directory for the file, if necessary:
if (!is_dir($file_path)) {
mkdir($file_path);
}
// Ok, now we read all data from $file_resource and put it into
// the file's path:
if (file_put_contents($file->getPath(), $file_resource) === false) {
//Something went wrong: abort and clean up!
$file->delete();
return null;
}
// Ok, we now must create a FileRef:
$file_ref = new FileRef();
$file_ref->file_id = $file->id;
$file_ref->folder_id = $target_folder->getId();
$file_ref->user_id = $user->id;
$file_ref->name = $file->name;
if ($file_ref->store()) {
return $file_ref;
}
//Something went wrong: abort and clean up!
$file_ref->delete();
return null;
}
/**
* Extracts an archive into a folder inside the Stud.IP file area.
*
* @param FileRef $archive_file_ref The archive file which shall be extracted.
* @param FolderType $folder The folder where the archive shall be extracted.
* @param string $user_id The ID of the user who wants to extract the archive.
*
* @return FileRef[] Array with extracted files, represented as FileRef objects.
*/
public static function extractArchiveFileToFolder(
FileRef $archive_file_ref,
FolderType $folder,
$user_id = null
)
{
$user = $user_id ? User::find($user_id) : User::findCurrent();
if (!$user) {
return [];
}
// Determine, if the folder is writable for the user identified by $user_id:
if (!$folder->isWritable($user->id)) {
return [];
}
// Determine if we can keep the zip archive's folder hierarchy:
$keep_hierarchy = $folder->isSubfolderAllowed($user->id);
$archive = new Studip\ZipArchive();
$archive->open($archive_file_ref->file->getPath());
// loop over all entries in the zip archive and put each entry
// in the current folder or one of its subfolders:
$file_refs = [];
for ($i = 0; $i < $archive->numFiles; $i++) {
$entry_info = $archive->statIndex($i);
$entry_info_name = $archive->convertArchiveFilename($entry_info['name']);
// split the entry's path into its path and its name component:
$entry_path = ltrim(pathinfo($entry_info_name, PATHINFO_DIRNAME), '.');
$entry_name = pathinfo($entry_info_name, PATHINFO_BASENAME);
// check if $entry_info['name'] ends with a slash:
// In that case it is a directory entry:
$entry_is_directory = preg_match('/\/$/', $entry_info_name);
//The folder where the extracted file/folder shall be inserted:
$extracted_entry_destination_folder = $folder;
if ($keep_hierarchy) {
//Keep the archive's folder hierarchy:
//We may have to create subfolders.
if (basename($entry_path)) {
//The file/folder doesn't lie in the "top folder" of the archive:
//Pass the path to createFolderPath and let it generate
//a folder path before extracting the file:
$folder_path = self::createFolderPath(
$folder,
$user,
$entry_path
);
//Get the last element of $folder_path:
$last_folder_path_element = array_pop($folder_path);
//Compare $extracted_entry_destination_folder's name with the name of the
//last path item in $file_archive_path. Only if they are equal
//we can use that folder to store the file. Otherwise
//we must continue with the next file entry in the archive:
if ($last_folder_path_element
&& $last_folder_path_element->name === basename($entry_path))
{
$extracted_entry_destination_folder = $last_folder_path_element;
}
}
}
if ($entry_is_directory) {
//We have to create a subfolder if it doesn't exist yet:
self::createFolderPath(
$extracted_entry_destination_folder,
$user,
$entry_name
);
} else {
//we extract one file:
//$entry_info['name'] is necessary because we need the full path
//to the entry inside the archive.
$file_ref = self::extractFileFromArchive(
$archive,
$entry_info['name'],
$extracted_entry_destination_folder,
$user
);
if ($file_ref instanceof FileRef) {
$file_refs[] = $file_ref;
}
}
}
return $file_refs;
}
}