Passed
Push — master ( 852ede...d4265d )
by Greg
06:08
created

MediaFileService::unusedFiles()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 18
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 28
rs 9.6666
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2019 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Services;
21
22
use Fisharebest\Webtrees\FlashMessages;
23
use Fisharebest\Webtrees\GedcomTag;
24
use Fisharebest\Webtrees\I18N;
25
use Fisharebest\Webtrees\Tree;
26
use Illuminate\Database\Capsule\Manager as DB;
27
use InvalidArgumentException;
28
use Psr\Http\Message\ServerRequestInterface;
29
use Psr\Http\Message\UploadedFileInterface;
30
use RuntimeException;
31
use Symfony\Component\HttpFoundation\File\UploadedFile;
32
33
use function array_combine;
34
use function array_diff;
35
use function array_filter;
36
use function array_map;
37
use function assert;
38
use function intdiv;
39
use function pathinfo;
40
use function preg_match;
41
use function sha1;
42
use function sort;
43
use function str_replace;
44
use function strpos;
45
use function strtolower;
46
use function trim;
47
48
use const PATHINFO_EXTENSION;
49
use const UPLOAD_ERR_OK;
50
51
/**
52
 * Managing media files.
53
 */
54
class MediaFileService
55
{
56
    public const EDIT_RESTRICTIONS = [
57
        'locked',
58
    ];
59
60
    public const PRIVACY_RESTRICTIONS = [
61
        'none',
62
        'privacy',
63
        'confidential',
64
    ];
65
66
    /**
67
     * What is the largest file a user may upload?
68
     */
69
    public function maxUploadFilesize(): string
70
    {
71
        $bytes = UploadedFile::getMaxFilesize();
72
        $kb    = intdiv($bytes + 1023, 1024);
73
74
        return I18N::translate('%s KB', I18N::number($kb));
75
    }
76
77
    /**
78
     * A list of key/value options for media types.
79
     *
80
     * @param string $current
81
     *
82
     * @return array
83
     */
84
    public function mediaTypes($current = ''): array
85
    {
86
        $media_types = GedcomTag::getFileFormTypes();
87
88
        $media_types = ['' => ''] + [$current => $current] + $media_types;
89
90
        return $media_types;
91
    }
92
93
    /**
94
     * A list of media files not already linked to a media object.
95
     *
96
     * @param Tree $tree
97
     *
98
     * @return array
99
     */
100
    public function unusedFiles(Tree $tree): array
101
    {
102
        $used_files = DB::table('media_file')
103
            ->where('m_file', '=', $tree->id())
104
            ->where('multimedia_file_refn', 'NOT LIKE', 'http://%')
105
            ->where('multimedia_file_refn', 'NOT LIKE', 'https://%')
106
            ->pluck('multimedia_file_refn')
107
            ->all();
108
109
        $disk_files = $tree->mediaFilesystem()->listContents('', true);
110
111
        $disk_files = array_filter($disk_files, static function (array $item) {
112
            // Older versions of webtrees used a couple of special folders.
113
            return
114
                $item['type'] === 'file' &&
115
                strpos($item['path'], '/thumbs/') === false &&
116
                strpos($item['path'], '/watermarks/') === false;
117
        });
118
119
        $disk_files = array_map(static function (array $item): string {
120
            return $item['path'];
121
        }, $disk_files);
122
123
        $unused_files = array_diff($disk_files, $used_files);
124
125
        sort($unused_files);
126
127
        return array_combine($unused_files, $unused_files);
128
    }
129
130
    /**
131
     * Store an uploaded file (or URL), either to be added to a media object
132
     * or to create a media object.
133
     *
134
     * @param ServerRequestInterface $request
135
     *
136
     * @return string The value to be stored in the 'FILE' field of the media object.
137
     */
138
    public function uploadFile(ServerRequestInterface $request): string
139
    {
140
        $tree = $request->getAttribute('tree');
141
        assert($tree instanceof Tree);
142
143
        $params        = $request->getParsedBody();
144
        $file_location = $params['file_location'];
145
146
        switch ($file_location) {
147
            case 'url':
148
                $remote = $params['remote'];
149
150
                if (strpos($remote, '://') !== false) {
151
                    return $remote;
152
                }
153
154
                return '';
155
156
            case 'unused':
157
                $unused = $params['unused'];
158
159
                if ($tree->mediaFilesystem()->has($unused)) {
160
                    return $unused;
161
                }
162
163
                return '';
164
165
            case 'upload':
166
            default:
167
                $folder   = $params['folder'];
168
                $auto     = $params['auto'];
169
                $new_file = $params['new_file'];
170
171
                /** @var UploadedFileInterface|null $uploaded_file */
172
                $uploaded_file = $request->getUploadedFiles()['file'];
173
                if ($uploaded_file === null || $uploaded_file->getError() !== UPLOAD_ERR_OK) {
174
                    return '';
175
                }
176
177
                // The filename
178
                $new_file = str_replace('\\', '/', $new_file);
179
                if ($new_file !== '' && strpos($new_file, '/') === false) {
180
                    $file = $new_file;
181
                } else {
182
                    $file = $uploaded_file->getClientFilename();
183
                }
184
185
                // The folder
186
                $folder = str_replace('\\', '/', $folder);
187
                $folder = trim($folder, '/');
188
                if ($folder !== '') {
189
                    $folder .= '/';
190
                }
191
192
                // Generate a unique name for the file?
193
                if ($auto === '1' || $tree->mediaFilesystem()->has($folder . $file)) {
194
                    $folder    = '';
195
                    $extension = pathinfo($uploaded_file->getClientFilename(), PATHINFO_EXTENSION);
196
                    $file      = sha1((string) $uploaded_file->getStream()) . '.' . $extension;
197
                }
198
199
                try {
200
                    $tree->mediaFilesystem()->writeStream($folder . $file, $uploaded_file->getStream()->detach());
201
202
                    return $folder . $file;
203
                } catch (RuntimeException | InvalidArgumentException $ex) {
204
                    FlashMessages::addMessage(I18N::translate('There was an error uploading your file.'));
205
206
                    return '';
207
                }
208
        }
209
    }
210
211
    /**
212
     * Convert the media file attributes into GEDCOM format.
213
     *
214
     * @param string $file
215
     * @param string $type
216
     * @param string $title
217
     *
218
     * @return string
219
     */
220
    public function createMediaFileGedcom(string $file, string $type, string $title): string
221
    {
222
        if (preg_match('/\.([a-z0-9]+)/i', $file, $match)) {
223
            $extension = strtolower($match[1]);
224
            $extension = str_replace('jpg', 'jpeg', $extension);
225
            $extension = ' ' . $extension;
226
        } else {
227
            $extension = '';
228
        }
229
230
        $gedcom = '1 FILE ' . $file;
231
        if ($type !== '') {
232
            $gedcom .= "\n2 FORM" . $extension . "\n3 TYPE " . $type;
233
        }
234
        if ($title !== '') {
235
            $gedcom .= "\n2 TITL " . $title;
236
        }
237
238
        return $gedcom;
239
    }
240
}
241