Total Complexity | 40 |
Total Lines | 334 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like MediaFileService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MediaFileService, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
61 | class MediaFileService |
||
62 | { |
||
63 | public const EDIT_RESTRICTIONS = [ |
||
64 | 'locked', |
||
65 | ]; |
||
66 | |||
67 | public const PRIVACY_RESTRICTIONS = [ |
||
68 | 'none', |
||
69 | 'privacy', |
||
70 | 'confidential', |
||
71 | ]; |
||
72 | |||
73 | public const EXTENSION_TO_FORM = [ |
||
74 | 'jpg' => 'jpeg', |
||
75 | 'tif' => 'tiff', |
||
76 | ]; |
||
77 | |||
78 | /** |
||
79 | * What is the largest file a user may upload? |
||
80 | */ |
||
81 | public function maxUploadFilesize(): string |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * Returns the given size from an ini value in bytes. |
||
94 | * |
||
95 | * @param string $size |
||
96 | * |
||
97 | * @return int |
||
98 | */ |
||
99 | private function parseIniFileSize(string $size): int |
||
115 | } |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * A list of key/value options for media types. |
||
120 | * |
||
121 | * @param string $current |
||
122 | * |
||
123 | * @return array<int|string,string> |
||
124 | * |
||
125 | * @deprecated - Will be removed in 2.1.0 - use Registry::elementFactory()->make('OBJE:FILE:FORM:TYPE')->values() |
||
126 | */ |
||
127 | public function mediaTypes($current = ''): array |
||
130 | } |
||
131 | |||
132 | /** |
||
133 | * A list of media files not already linked to a media object. |
||
134 | * |
||
135 | * @param Tree $tree |
||
136 | * @param FilesystemInterface $data_filesystem |
||
137 | * |
||
138 | * @return array<string> |
||
139 | */ |
||
140 | public function unusedFiles(Tree $tree, FilesystemInterface $data_filesystem): array |
||
141 | { |
||
142 | $used_files = DB::table('media_file') |
||
143 | ->where('m_file', '=', $tree->id()) |
||
144 | ->where('multimedia_file_refn', 'NOT LIKE', 'http://%') |
||
145 | ->where('multimedia_file_refn', 'NOT LIKE', 'https://%') |
||
146 | ->pluck('multimedia_file_refn') |
||
147 | ->all(); |
||
148 | |||
149 | $disk_files = $tree->mediaFilesystem($data_filesystem)->listContents('', true); |
||
150 | |||
151 | $disk_files = array_filter($disk_files, static function (array $item) { |
||
152 | // Older versions of webtrees used a couple of special folders. |
||
153 | return |
||
154 | $item['type'] === 'file' && |
||
155 | !str_contains($item['path'], '/thumbs/') && |
||
156 | !str_contains($item['path'], '/watermarks/'); |
||
157 | }); |
||
158 | |||
159 | $disk_files = array_map(static function (array $item): string { |
||
160 | return $item['path']; |
||
161 | }, $disk_files); |
||
162 | |||
163 | $unused_files = array_diff($disk_files, $used_files); |
||
164 | |||
165 | sort($unused_files); |
||
166 | |||
167 | return array_combine($unused_files, $unused_files); |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Store an uploaded file (or URL), either to be added to a media object |
||
172 | * or to create a media object. |
||
173 | * |
||
174 | * @param ServerRequestInterface $request |
||
175 | * |
||
176 | * @return string The value to be stored in the 'FILE' field of the media object. |
||
177 | */ |
||
178 | public function uploadFile(ServerRequestInterface $request): string |
||
179 | { |
||
180 | $tree = $request->getAttribute('tree'); |
||
181 | assert($tree instanceof Tree); |
||
182 | |||
183 | $data_filesystem = Registry::filesystem()->data(); |
||
184 | |||
185 | $params = (array) $request->getParsedBody(); |
||
186 | $file_location = $params['file_location']; |
||
187 | |||
188 | switch ($file_location) { |
||
189 | case 'url': |
||
190 | $remote = $params['remote']; |
||
191 | |||
192 | if (str_contains($remote, '://')) { |
||
193 | return $remote; |
||
194 | } |
||
195 | |||
196 | return ''; |
||
197 | |||
198 | case 'unused': |
||
199 | $unused = $params['unused']; |
||
200 | |||
201 | if ($tree->mediaFilesystem($data_filesystem)->has($unused)) { |
||
202 | return $unused; |
||
203 | } |
||
204 | |||
205 | return ''; |
||
206 | |||
207 | case 'upload': |
||
208 | default: |
||
209 | $folder = $params['folder']; |
||
210 | $auto = $params['auto']; |
||
211 | $new_file = $params['new_file']; |
||
212 | |||
213 | /** @var UploadedFileInterface|null $uploaded_file */ |
||
214 | $uploaded_file = $request->getUploadedFiles()['file']; |
||
215 | if ($uploaded_file === null || $uploaded_file->getError() !== UPLOAD_ERR_OK) { |
||
216 | return ''; |
||
217 | } |
||
218 | |||
219 | // The filename |
||
220 | $new_file = strtr($new_file, ['\\' => '/']); |
||
221 | if ($new_file !== '' && !str_contains($new_file, '/')) { |
||
222 | $file = $new_file; |
||
223 | } else { |
||
224 | $file = $uploaded_file->getClientFilename(); |
||
225 | } |
||
226 | |||
227 | // The folder |
||
228 | $folder = strtr($folder, ['\\' => '/']); |
||
229 | $folder = trim($folder, '/'); |
||
230 | if ($folder !== '') { |
||
231 | $folder .= '/'; |
||
232 | } |
||
233 | |||
234 | // Generate a unique name for the file? |
||
235 | if ($auto === '1' || $tree->mediaFilesystem($data_filesystem)->has($folder . $file)) { |
||
236 | $folder = ''; |
||
237 | $extension = pathinfo($uploaded_file->getClientFilename(), PATHINFO_EXTENSION); |
||
238 | $file = sha1((string) $uploaded_file->getStream()) . '.' . $extension; |
||
1 ignored issue
–
show
|
|||
239 | } |
||
240 | |||
241 | try { |
||
242 | $tree->mediaFilesystem($data_filesystem)->putStream($folder . $file, $uploaded_file->getStream()->detach()); |
||
243 | |||
244 | return $folder . $file; |
||
245 | } catch (RuntimeException | InvalidArgumentException $ex) { |
||
246 | FlashMessages::addMessage(I18N::translate('There was an error uploading your file.')); |
||
247 | |||
248 | return ''; |
||
249 | } |
||
250 | } |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Convert the media file attributes into GEDCOM format. |
||
255 | * |
||
256 | * @param string $file |
||
257 | * @param string $type |
||
258 | * @param string $title |
||
259 | * @param string $note |
||
260 | * |
||
261 | * @return string |
||
262 | */ |
||
263 | public function createMediaFileGedcom(string $file, string $type, string $title, string $note): string |
||
264 | { |
||
265 | // Tidy non-printing characters |
||
266 | $type = trim(preg_replace('/\s+/', ' ', $type)); |
||
267 | $title = trim(preg_replace('/\s+/', ' ', $title)); |
||
268 | |||
269 | $gedcom = '1 FILE ' . $file; |
||
270 | |||
271 | $format = strtolower(pathinfo($file, PATHINFO_EXTENSION)); |
||
1 ignored issue
–
show
|
|||
272 | $format = self::EXTENSION_TO_FORM[$format] ?? $format; |
||
273 | |||
274 | if ($format !== '') { |
||
275 | $gedcom .= "\n2 FORM " . $format; |
||
276 | } elseif ($type !== '') { |
||
277 | $gedcom .= "\n2 FORM"; |
||
278 | } |
||
279 | |||
280 | if ($type !== '') { |
||
281 | $gedcom .= "\n3 TYPE " . $type; |
||
282 | } |
||
283 | |||
284 | if ($title !== '') { |
||
285 | $gedcom .= "\n2 TITL " . $title; |
||
286 | } |
||
287 | |||
288 | if ($note !== '') { |
||
289 | // Convert HTML line endings to GEDCOM continuations |
||
290 | $gedcom .= "\n1 NOTE " . strtr($note, ["\r\n" => "\n2 CONT "]); |
||
291 | } |
||
292 | |||
293 | return $gedcom; |
||
294 | } |
||
295 | |||
296 | /** |
||
297 | * Fetch a list of all files on disk (in folders used by any tree). |
||
298 | * |
||
299 | * @param FilesystemInterface $data_filesystem Fileystem to search |
||
300 | * @param string $media_folder Root folder |
||
301 | * @param bool $subfolders Include subfolders |
||
302 | * |
||
303 | * @return Collection<string> |
||
304 | */ |
||
305 | public function allFilesOnDisk(FilesystemInterface $data_filesystem, string $media_folder, bool $subfolders): Collection |
||
306 | { |
||
307 | $array = $data_filesystem->listContents($media_folder, $subfolders); |
||
308 | |||
309 | return Collection::make($array) |
||
310 | ->filter(static function (array $metadata): bool { |
||
311 | return |
||
312 | $metadata['type'] === 'file' && |
||
313 | !str_contains($metadata['path'], '/thumbs/') && |
||
314 | !str_contains($metadata['path'], '/watermark/'); |
||
315 | }) |
||
316 | ->map(static function (array $metadata): string { |
||
317 | return $metadata['path']; |
||
318 | }); |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Fetch a list of all files on in the database. |
||
323 | * |
||
324 | * @param string $media_folder Root folder |
||
325 | * @param bool $subfolders Include subfolders |
||
326 | * |
||
327 | * @return Collection<string> |
||
328 | */ |
||
329 | public function allFilesInDatabase(string $media_folder, bool $subfolders): Collection |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Generate a list of all folders in either the database or the filesystem. |
||
350 | * |
||
351 | * @param FilesystemInterface $data_filesystem |
||
352 | * |
||
353 | * @return Collection<string,string> |
||
354 | */ |
||
355 | public function allMediaFolders(FilesystemInterface $data_filesystem): Collection |
||
395 | }); |
||
396 | } |
||
397 | } |
||
398 |
This check looks for parameters that have been defined for a function or method, but which are not used in the method body.