Passed
Push — master ( 822bde...a3a671 )
by Greg
06:27
created

EditMediaController::linkMediaToSource()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 1
dl 0
loc 12
rs 10
c 0
b 0
f 0
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\Http\Controllers;
21
22
use Exception;
23
use Fig\Http\Message\StatusCodeInterface;
24
use Fisharebest\Webtrees\Auth;
25
use Fisharebest\Webtrees\FlashMessages;
26
use Fisharebest\Webtrees\GedcomRecord;
27
use Fisharebest\Webtrees\Html;
28
use Fisharebest\Webtrees\I18N;
29
use Fisharebest\Webtrees\Media;
30
use Fisharebest\Webtrees\Services\MediaFileService;
31
use Fisharebest\Webtrees\Services\PendingChangesService;
32
use Fisharebest\Webtrees\Tree;
33
use League\Flysystem\FileExistsException;
34
use League\Flysystem\FileNotFoundException;
35
use League\Flysystem\FilesystemInterface;
36
use League\Flysystem\Util;
37
use Psr\Http\Message\ResponseInterface;
38
use Psr\Http\Message\ServerRequestInterface;
39
40
use function assert;
41
use function is_string;
42
43
/**
44
 * Controller for edit forms and responses.
45
 */
46
class EditMediaController extends AbstractEditController
47
{
48
    /** @var MediaFileService */
49
    private $media_file_service;
50
51
    /** @var PendingChangesService */
52
    private $pending_changes_service;
53
54
    /**
55
     * EditMediaController constructor.
56
     *
57
     * @param MediaFileService      $media_file_service
58
     * @param PendingChangesService $pending_changes_service
59
     */
60
    public function __construct(MediaFileService $media_file_service, PendingChangesService $pending_changes_service)
61
    {
62
        $this->media_file_service      = $media_file_service;
63
        $this->pending_changes_service = $pending_changes_service;
64
    }
65
66
    /**
67
     * Add a media file to an existing media object.
68
     *
69
     * @param ServerRequestInterface $request
70
     *
71
     * @return ResponseInterface
72
     */
73
    public function addMediaFile(ServerRequestInterface $request): ResponseInterface
74
    {
75
        $tree = $request->getAttribute('tree');
76
        assert($tree instanceof Tree);
77
78
        $data_filesystem = $request->getAttribute('filesystem.data');
79
        assert($data_filesystem instanceof FilesystemInterface);
80
81
        $xref  = $request->getQueryParams()['xref'];
82
        $media = Media::getInstance($xref, $tree);
83
84
        try {
85
            $media = Auth::checkMediaAccess($media);
86
        } catch (Exception $ex) {
87
            return response(view('modals/error', [
88
                'title' => I18N::translate('Add a media file'),
89
                'error' => $ex->getMessage(),
90
            ]));
91
        }
92
93
        return response(view('modals/add-media-file', [
94
            'max_upload_size' => $this->media_file_service->maxUploadFilesize(),
95
            'media'           => $media,
96
            'media_types'     => $this->media_file_service->mediaTypes(),
97
            'tree'            => $tree,
98
            'unused_files'    => $this->media_file_service->unusedFiles($tree, $data_filesystem),
99
        ]));
100
    }
101
102
    /**
103
     * Add a media file to an existing media object.
104
     *
105
     * @param ServerRequestInterface $request
106
     *
107
     * @return ResponseInterface
108
     */
109
    public function addMediaFileAction(ServerRequestInterface $request): ResponseInterface
110
    {
111
        $tree = $request->getAttribute('tree');
112
        assert($tree instanceof Tree);
113
114
        $xref  = $request->getQueryParams()['xref'];
115
        $media = Media::getInstance($xref, $tree);
116
117
        $params = (array) $request->getParsedBody();
118
119
        $title = $params['title'];
120
        $type  = $params['type'];
121
122
        // Tidy whitespace
123
        $type  = trim(preg_replace('/\s+/', ' ', $type));
124
        $title = trim(preg_replace('/\s+/', ' ', $title));
125
126
        if ($media === null || $media->isPendingDeletion() || !$media->canEdit()) {
127
            return redirect(route('tree-page', ['tree' => $tree->name()]));
128
        }
129
130
        $file = $this->media_file_service->uploadFile($request);
131
132
        if ($file === '') {
133
            FlashMessages::addMessage(I18N::translate('There was an error uploading your file.'));
134
135
            return redirect($media->url());
136
        }
137
138
        $gedcom = '1 FILE ' . $file;
139
        if ($type !== '') {
140
            $gedcom .= "\n2 FORM\n3 TYPE " . $type;
141
        }
142
        if ($title !== '') {
143
            $gedcom .= "\n2 TITL " . $title;
144
        }
145
146
        $media->createFact($gedcom, true);
147
148
        // Accept the changes, to keep the filesystem in sync with the GEDCOM data.
149
        $this->pending_changes_service->acceptRecord($media);
150
151
        return redirect($media->url());
152
    }
153
154
    /**
155
     * Edit an existing media file.
156
     *
157
     * @param ServerRequestInterface $request
158
     *
159
     * @return ResponseInterface
160
     */
161
    public function editMediaFile(ServerRequestInterface $request): ResponseInterface
162
    {
163
        $tree = $request->getAttribute('tree');
164
        assert($tree instanceof Tree);
165
166
        $data_filesystem = $request->getAttribute('filesystem.data');
167
        assert($data_filesystem instanceof FilesystemInterface);
168
169
        $params  = $request->getQueryParams();
170
        $xref    = $params['xref'];
171
        $fact_id = $params['fact_id'];
172
        $media   = Media::getInstance($xref, $tree);
173
174
        try {
175
            $media = Auth::checkMediaAccess($media);
176
        } catch (Exception $ex) {
177
            return response(view('modals/error', [
178
                'title' => I18N::translate('Edit a media file'),
179
                'error' => $ex->getMessage(),
180
            ]), StatusCodeInterface::STATUS_FORBIDDEN);
181
        }
182
183
        foreach ($media->mediaFiles() as $media_file) {
184
            if ($media_file->factId() === $fact_id) {
185
                return response(view('modals/edit-media-file', [
186
                    'media_file'      => $media_file,
187
                    'max_upload_size' => $this->media_file_service->maxUploadFilesize(),
188
                    'media'           => $media,
189
                    'media_types'     => $this->media_file_service->mediaTypes(),
190
                    'unused_files'    => $this->media_file_service->unusedFiles($tree, $data_filesystem),
191
                    'tree'            => $tree,
192
                ]));
193
            }
194
        }
195
196
        return response('', StatusCodeInterface::STATUS_NOT_FOUND);
197
    }
198
199
    /**
200
     * Save an edited media file.
201
     *
202
     * @param ServerRequestInterface $request
203
     *
204
     * @return ResponseInterface
205
     */
206
    public function editMediaFileAction(ServerRequestInterface $request): ResponseInterface
207
    {
208
        $tree = $request->getAttribute('tree');
209
        assert($tree instanceof Tree);
210
211
        $data_filesystem = $request->getAttribute('filesystem.data');
212
        assert($data_filesystem instanceof FilesystemInterface);
213
214
        $xref    = $request->getQueryParams()['xref'];
215
        $fact_id = $request->getQueryParams()['fact_id'];
216
217
        $params = (array) $request->getParsedBody();
218
219
        $folder   = $params['folder'];
220
        $new_file = $params['new_file'];
221
        $remote   = $params['remote'];
222
        $title    = $params['title'];
223
        $type     = $params['type'];
224
        $media    = Media::getInstance($xref, $tree);
225
226
        // Tidy whitespace
227
        $type  = trim(preg_replace('/\s+/', ' ', $type));
228
        $title = trim(preg_replace('/\s+/', ' ', $title));
229
230
        // Media object oes not exist?  Media object is read-only?
231
        if ($media === null || $media->isPendingDeletion() || !$media->canEdit()) {
232
            return redirect(route('tree-page', ['tree' => $tree->name()]));
233
        }
234
235
        // Find the fact we are editing.
236
        $media_file = null;
237
        foreach ($media->mediaFiles() as $tmp) {
238
            if ($tmp->factId() === $fact_id) {
239
                $media_file = $tmp;
240
            }
241
        }
242
243
        // Media file does not exist?
244
        if ($media_file === null) {
0 ignored issues
show
introduced by
The condition $media_file === null is always true.
Loading history...
245
            return redirect(route('tree-page', ['tree' => $tree->name()]));
246
        }
247
248
        // We can edit the file as either a URL or a folder/file
249
        if ($remote !== '') {
250
            $file = $remote;
251
        } else {
252
            $new_file = str_replace('\\', '/', $new_file);
253
            $folder   = str_replace('\\', '/', $folder);
254
            $folder   = trim($folder, '/');
255
256
            if ($folder === '') {
257
                $file = $new_file;
258
            } else {
259
                $file = $folder . '/' . $new_file;
260
            }
261
        }
262
263
        // Invalid filename?  Do not change it.
264
        if ($new_file === '') {
265
            $file = $media_file->filename();
266
        }
267
268
        $filesystem = $media->tree()->mediaFilesystem($data_filesystem);
269
        $old        = $media_file->filename();
270
        $new        = $file;
271
272
        // Update the filesystem, if we can.
273
        if ($old !== $new && !$media_file->isExternal()) {
274
            try {
275
                $new = Util::normalizePath($new);
276
                $filesystem->rename($old, $new);
277
                FlashMessages::addMessage(I18N::translate('The media file %1$s has been renamed to %2$s.', Html::filename($media_file->filename()), Html::filename($file)), 'info');
278
            } catch (FileNotFoundException $ex) {
279
                // The "old" file may not exist.  For example, if the file was renamed on disk,
280
                // and we are now renaming the GEDCOM data to match.
281
            } catch (FileExistsException $ex) {
282
                // Don't overwrite existing file
283
                FlashMessages::addMessage(I18N::translate('The media file %1$s could not be renamed to %2$s.', Html::filename($media_file->filename()), Html::filename($file)), 'info');
284
                $file = $old;
285
            }
286
        }
287
288
        $gedcom = $this->media_file_service->createMediaFileGedcom($file, $type, $title);
289
290
        $media->updateFact($fact_id, $gedcom, true);
291
292
        // Accept the changes, to keep the filesystem in sync with the GEDCOM data.
293
        if ($old !== $new && !$media_file->isExternal()) {
294
            $this->pending_changes_service->acceptRecord($media);
295
        }
296
297
        return redirect($media->url());
298
    }
299
300
    /**
301
     * Show a form to create a new media object.
302
     *
303
     * @param ServerRequestInterface $request
304
     *
305
     * @return ResponseInterface
306
     */
307
    public function createMediaObject(ServerRequestInterface $request): ResponseInterface
308
    {
309
        $tree = $request->getAttribute('tree');
310
        assert($tree instanceof Tree);
311
312
        $data_filesystem = $request->getAttribute('filesystem.data');
313
        assert($data_filesystem instanceof FilesystemInterface);
314
315
        return response(view('modals/create-media-object', [
316
            'max_upload_size' => $this->media_file_service->maxUploadFilesize(),
317
            'media_types'     => $this->media_file_service->mediaTypes(),
318
            'unused_files'    => $this->media_file_service->unusedFiles($tree, $data_filesystem),
319
        ]));
320
    }
321
322
    /**
323
     * @param ServerRequestInterface $request
324
     *
325
     * @return ResponseInterface
326
     */
327
    public function createMediaObjectFromFileAction(ServerRequestInterface $request): ResponseInterface
328
    {
329
        $tree = $request->getAttribute('tree');
330
        assert($tree instanceof Tree);
331
332
        $params = (array) $request->getParsedBody();
333
        $file   = $params['file'];
334
        $type   = $params['type'];
335
        $title  = $params['title'];
336
        $note   = $params['note'];
337
338
        if (preg_match('/\.([a-zA-Z0-9]+)$/', $file, $match)) {
339
            $format = ' ' . $match[1];
340
        } else {
341
            $format = '';
342
        }
343
344
        $gedcom = "0 @@ OBJE\n1 FILE " . $file . "\n2 FORM " . $format;
345
346
        if ($type !== '') {
347
            $gedcom .= "\n3 TYPE " . $type;
348
        }
349
350
        if ($title !== '') {
351
            $gedcom .= "\n2 TITL " . $title;
352
        }
353
354
        // Convert HTML line endings to GEDCOM continuations
355
        $note = strtr($note, ["\r\n" => "\n2 CONT "]);
356
357
        if ($note !== '') {
358
            $gedcom .= "\n1 NOTE " . $note;
359
        }
360
361
        $media_object = $tree->createRecord($gedcom);
362
        // Accept the new record.  Rejecting it would leave the filesystem out-of-sync with the genealogy
363
        $this->pending_changes_service->acceptRecord($media_object);
364
365
        return redirect($media_object->url());
366
    }
367
368
    /**
369
     * @param ServerRequestInterface $request
370
     *
371
     * @return ResponseInterface
372
     */
373
    public function linkMediaToIndividual(ServerRequestInterface $request): ResponseInterface
374
    {
375
        $tree = $request->getAttribute('tree');
376
        assert($tree instanceof Tree);
377
378
        $xref = $request->getQueryParams()['xref'];
379
        $media = Media::getInstance($xref, $tree);
380
381
        return response(view('modals/link-media-to-individual', [
382
            'media' => $media,
383
            'tree'  => $tree,
384
        ]));
385
    }
386
387
    /**
388
     * @param ServerRequestInterface $request
389
     *
390
     * @return ResponseInterface
391
     */
392
    public function linkMediaToFamily(ServerRequestInterface $request): ResponseInterface
393
    {
394
        $tree = $request->getAttribute('tree');
395
        assert($tree instanceof Tree);
396
397
        $xref = $request->getQueryParams()['xref'];
398
399
        $media = Media::getInstance($xref, $tree);
400
401
        return response(view('modals/link-media-to-family', [
402
            'media' => $media,
403
            'tree'  => $tree,
404
        ]));
405
    }
406
407
    /**
408
     * @param ServerRequestInterface $request
409
     *
410
     * @return ResponseInterface
411
     */
412
    public function linkMediaToSource(ServerRequestInterface $request): ResponseInterface
413
    {
414
        $tree = $request->getAttribute('tree');
415
        assert($tree instanceof Tree);
416
417
        $xref = $request->getQueryParams()['xref'];
418
419
        $media = Media::getInstance($xref, $tree);
420
421
        return response(view('modals/link-media-to-source', [
422
            'media' => $media,
423
            'tree'  => $tree,
424
        ]));
425
    }
426
427
    /**
428
     * @param ServerRequestInterface $request
429
     *
430
     * @return ResponseInterface
431
     */
432
    public function linkMediaToRecordAction(ServerRequestInterface $request): ResponseInterface
433
    {
434
        $tree = $request->getAttribute('tree');
435
        assert($tree instanceof Tree);
436
437
        $xref = $request->getAttribute('xref');
438
        assert(is_string($xref));
439
440
        $params = (array) $request->getParsedBody();
441
442
        $link = $params['link'];
443
444
        $media  = Media::getInstance($xref, $tree);
445
        $record = GedcomRecord::getInstance($link, $tree);
446
447
        $record->createFact('1 OBJE @' . $xref . '@', true);
448
449
        return redirect($media->url());
450
    }
451
}
452