19declare(strict_types=1);
55 private const int MAX_SIZE = 100 * 1024 * 1024;
62 private readonly DataFactory
$data =
new DataFactory(),
68 return $this->data->text()->markdown()->simpleDocument(
69 "Packs the files of a container into a zip archive.\n\n"
70 .
"Only files the user may read end up in it, at most " . self::MAX_FILES .
" of them and "
71 . self::MAX_SIZE .
" bytes together. "
72 .
"Files whose content cannot be read are left out and listed in `skipped`. "
73 .
"The archive is returned base64 encoded; over REST `?raw=1` gives the zip itself."
81 'parent_ref_id' =>
$f->numeric(
'Container',
'Reference id of the container to pack.')
83 ->withDedicatedName(
'parent_ref_id'),
84 'recursive' =>
$f->checkbox(
86 'Also pack the files of the containers below this one.'
87 )->withDedicatedName(
'recursive'),
89 'Download a container as zip'
95 $text = $this->data->text()->markdown();
97 return $this->payloads->getDescription(
99 'The zip archive with the files of the container.',
101 'skipped' =>
$f->list(
102 $text->simpleDocument(
'Files that could not be packed, empty if the archive is complete.'),
104 $text->simpleDocument(
'A file left out of the archive.'),
106 'ref_id' =>
$f->int(
$text->simpleDocument(
'Reference id of the file.')),
107 'title' =>
$f->string(
$text->simpleDocument(
'Title of the file.')),
108 'reason' =>
$f->string(
$text->simpleDocument(
'Why the file was left out.')),
119 return $this->list_files->isAllowedToPerform($usr_id,
$parameters);
129 if (count(
$files) > self::MAX_FILES) {
130 throw new \DomainException(
131 'This container holds more than ' . self::MAX_FILES .
' files, refusing to pack it.',
136 $size = array_sum(array_map(
static fn(
FileEntry $file):
int => $file->size,
$files));
138 if ($size > self::MAX_SIZE) {
139 throw new \DomainException(
140 'The files of this container are larger than ' . self::MAX_SIZE .
' bytes, refusing to pack them.',
145 $name =
'container_' .
$parameters[
'parent_ref_id'] .
'.zip';
149 $path = tempnam(sys_get_temp_dir(),
'ilias_zip_');
151 if ($path ===
false) {
152 throw new \RuntimeException(
'The archive could not be created.');
159 $this->payloads->fromString($name,
'application/zip', (
string) file_get_contents($path)),
171 InputFactory $input_factory,
173 array $raw_parameters
179 return $this->data->error(
180 new \DomainException(
'You are not allowed to look into this container.', 403)
185 }
catch (\Throwable
$e) {
197 $zip = new \ZipArchive();
199 if ($zip->open($path, \ZipArchive::OVERWRITE) !==
true) {
200 throw new \RuntimeException(
'The archive could not be created.');
207 foreach (
$files as $file) {
214 $payload = $this->deliver_file->perform([
'ref_id' => $file->ref_id]);
215 $name = $this->entryName($payload->filename);
216 $content = base64_decode($payload->content,
true);
217 }
catch (\Throwable) {
224 || $content ===
false
225 || !$zip->addFromString($this->uniqueName($names, $name), $content)
230 'The content of this file could not be read.'
239 if (!$zip->close()) {
240 throw new \RuntimeException(
'The archive could not be created.');
246 file_put_contents($path,
"PK\x05\x06" .
str_repeat(
"\0", 18));
260 $parts = explode(
'/', str_replace(
'\\',
'/',
$filename));
261 $name = ltrim((
string) end($parts),
'.');
263 return trim($name) ===
'' ?
'file' : $name;
277 $extension = pathinfo(
$filename, PATHINFO_EXTENSION);
278 $base = pathinfo(
$filename, PATHINFO_FILENAME);
282 $name = $base .
' (' . ++$names[
$filename] .
')' . ($extension ===
'' ?
'' :
'.' . $extension);
283 }
while (isset($names[$name]));
297 $parent_ref_id = $this->parameter->id($raw_parameters,
'parent_ref_id',
'A container is required.');
300 'parent_ref_id' => $parent_ref_id,
301 'recursive' => $this->parameter->flag($raw_parameters,
'recursive',
false),
This describes some datastructure in terms of standard data structures such as primitives,...
Builds the payload of a delivering activity and describes its shape.
What the file activities hand to Data\Factory::error() when something failed: the result only takes e...
Reads the raw parameters of an activity strictly, with the Refinery.
The zip archive of a container: a file payload like every other delivery, so a consumer writes it the...
static of(FilePayload $payload, array $skipped)
Deliver the content of a file object of the repository.
Deliver the files of a container as one zip archive.
perform(mixed $parameters)
getOutputDescription(Description\Factory $f)
isAllowedToPerform(int $usr_id, mixed $parameters)
This shall check if the given user is allowed to perform the activity based on business rules of this...
toParameters(array $raw_parameters)
__construct(private readonly ListFiles $list_files, private readonly DeliverFileObject $deliver_file, private readonly FilePayloadFactory $payloads, private readonly Parameter $parameter, private readonly DataFactory $data=new DataFactory(),)
const int MAX_SIZE
The archive is built and handed out in memory, encoded on top of that, so the sum of what goes into i...
pack(string $path, array $files)
getInputDescription(FieldFactory $f)
uniqueName(array &$names, string $filename)
entryName(string $filename)
A name for an entry that stays in the folder it is unpacked into: the storage names its resources aft...
maybePerformAs(InputFactory $input_factory, int $usr_id, array $raw_parameters)
List the files in a container of the repository.
A file a packing activity has left out, and why - so the user learns that the archive is not complete...
A result encapsulates a value or an error and simplifies the handling of those.