ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
ContainerWrapper.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
28
34{
38 private const BASE = '/';
39 private array $ignored = ['.', '..', '__MACOSX', '.info', '.DS_Store'];
40
41 private string $current_level;
42 private array $data;
43 private Services $irss;
44 private bool $use_flavour = true;
46 private \ILIAS\FileDelivery\Services $file_delivery;
47
48 public function __construct(
49 private ResourceIdentification $rid,
50 ?string $current_level = self::BASE ?? self::BASE
51 ) {
52 global $DIC;
53 // dependencies
54 $this->irss = $DIC->resourceStorage();
55 $this->file_delivery = $DIC->fileDelivery();
56 $this->reader = new ZipReader(
57 $this->irss->consume()->stream($rid)->getStream()
58 );
59
60 // Init Data
61 $this->initData($rid);
62
63 $this->initCurrentLevel($current_level);
64 }
65
66 protected function initData(
68 ): void {
69 // init ZIP
70
71 if ($this->use_flavour) {
72 $flavour_definition = new \ZipStructureDefinition();
73 $flavour = $this->irss->flavours()->get(
74 $rid,
75 $flavour_definition
76 );
77 $flavour_stream = $flavour->getStreamResolvers()[0] ?? null;
78 if ($flavour_stream !== null) {
79 $this->data = $flavour_definition->wake((string) $flavour_stream->getStream());
80 }
81 }
82
83 if (empty($this->data)) {
84 $this->data = $this->reader->getStructure();
85 }
86
87 // remove items from array with key . or /
88 $this->data = array_filter(
89 $this->data,
90 static fn($key): bool => !in_array($key, ['.', '/', './', '..'], true),
91 ARRAY_FILTER_USE_KEY
92 );
93 }
94
95 public function download(string $path_inside_zip): never
96 {
97 [$stream, $info] = $this->reader->getItem($path_inside_zip, $this->data);
98
99 $supported_for_inline = [
100 'image/*',
101 'application/pdf',
102 ];
103
104 $regex = array_map(
105 static fn(string $mime_type): string => str_replace('*', '.*', preg_quote($mime_type, '/')),
106 $supported_for_inline
107 );
108 $regex = implode('|', $regex);
109
110 $disposition = preg_match("/($regex)/", (string) $info['mime_type']) ? Disposition::INLINE : Disposition::ATTACHMENT;
111
112 $this->file_delivery->delivery()->deliver(
113 $stream,
114 $info['basename'],
115 $info['mime_type'],
116 $disposition
117 );
118 }
119
120 public function unzip(string $path_inside_zip): bool
121 {
122 [$stream, $info] = $this->reader->getItem($path_inside_zip, $this->data);
123 if ($info['mime_type'] !== 'application/zip' && $info['mime_type'] !== 'application/x-zip-compressed') {
124 return false;
125 }
126
127 // save stream to temporary file
128 $tmp_directory = defined('CLIENT_DATA_DIR') ? \CLIENT_DATA_DIR . '/temp' : sys_get_temp_dir();
129 $tmp_file = tempnam($tmp_directory, 'ilias_zip_');
130
132 $return = file_put_contents($tmp_file, $stream->detach());
133
134 $zip_reader = new ZipReader(
135 Streams::ofResource(fopen($tmp_file, 'rb'))
136 );
137
138 foreach ($zip_reader->getStructure() as $append_path_inside_zip => $item) {
139 if ($item['is_dir']) {
140 continue;
141 }
142 [$stream, $info] = $zip_reader->getItem($append_path_inside_zip, $this->data);
143 $this->irss->manageContainer()->addStreamToContainer(
144 $this->rid,
145 $stream,
146 $this->current_level . '/' . ltrim($append_path_inside_zip, './')
147 );
148 }
149
150 unlink($tmp_file);
151 return true;
152 }
153
154 public function getEntries(): \Generator
155 {
156 // sort by basename, but directories after files
157 uasort($this->data, static function (array $a, array $b): int {
158 if ($a['is_dir'] === $b['is_dir']) {
159 return strnatcasecmp((string) $a['basename'], (string) $b['basename']);
160 }
161 return $a['is_dir'] ? 1 : -1;
162 });
163
164 foreach ($this->data as $path => $path_data) {
165 $dirname = $path_data['dirname'] ?? './';
166 if ($dirname !== $this->current_level) {
167 continue;
168 }
169 $basename = $path_data['basename'] ?? '';
170 if (in_array($basename, $this->ignored, true)) {
171 continue;
172 }
173
174 if ($path_data['is_dir'] ?? false) {
175 // directory
176 yield $this->directory($path, $path_data);
177 } else {
178 // file
179 $file = $this->file($path, $path_data);
180 if ($file !== null) {
181 yield $file;
182 }
183 }
184 }
185 }
186
187 public function getData(): array
188 {
189 return $this->data;
190 }
191
192 private function basename(string $full_path): string
193 {
194 return basename($full_path);
195 // return preg_replace('/^' . preg_quote($this->current_level, '/') . '/', '', $full_path);
196 }
197
198 public function initCurrentLevel(string $current_level): void
199 {
200 // init current level
201 $current_level = '/' . ltrim($current_level, './');
202 $current_level = rtrim($current_level, '/');
203 $this->current_level = $current_level === '' ? self::BASE : $current_level;
204 }
205
206 protected function directory(string $path_inside_zip, array $data): ?Dir
207 {
208 return new Dir(
209 $path_inside_zip,
210 $this->basename($path_inside_zip),
211 new \DateTimeImmutable('@' . ($data['modified'] ?? 0))
212 );
213 }
214
215 protected function file(string $path_inside_zip, array $data): ?File
216 {
217 return new File(
218 $path_inside_zip,
219 $this->basename($path_inside_zip),
220 $data['mime_type'] ?? 'application/octet-stream',
221 $data['size'] ?? 0,
222 new \DateTimeImmutable('@' . ($data['modified'] ?? 0))
223 );
224 }
225
226}
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
static ofResource($resource)
Wraps an already created resource with the stream abstraction.
Definition: Streams.php:64
__construct(private ResourceIdentification $rid, ?string $current_level=self::BASE ?? self::BASE)
const CLIENT_DATA_DIR
Definition: constants.php:46
$info
Definition: entry_point.php:21
$path
Definition: ltiservices.php:30
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
if(!file_exists('../ilias.ini.php'))
global $DIC
Definition: shib_login.php:26