1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2023 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 <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Http\RequestHandlers; 21 22use Fisharebest\Webtrees\Auth; 23use Fisharebest\Webtrees\FlashMessages; 24use Fisharebest\Webtrees\Html; 25use Fisharebest\Webtrees\I18N; 26use Fisharebest\Webtrees\MediaFile; 27use Fisharebest\Webtrees\Registry; 28use Fisharebest\Webtrees\Services\MediaFileService; 29use Fisharebest\Webtrees\Services\PendingChangesService; 30use Fisharebest\Webtrees\Validator; 31use League\Flysystem\FilesystemException; 32use League\Flysystem\UnableToMoveFile; 33use League\Flysystem\UnableToRetrieveMetadata; 34use Psr\Http\Message\ResponseInterface; 35use Psr\Http\Message\ServerRequestInterface; 36use Psr\Http\Server\RequestHandlerInterface; 37 38use function preg_replace; 39use function redirect; 40use function route; 41use function str_replace; 42use function trim; 43 44/** 45 * Edit a media file. 46 */ 47class EditMediaFileAction implements RequestHandlerInterface 48{ 49 private MediaFileService $media_file_service; 50 51 private PendingChangesService $pending_changes_service; 52 53 /** 54 * EditMediaFileAction constructor. 55 * 56 * @param MediaFileService $media_file_service 57 * @param PendingChangesService $pending_changes_service 58 */ 59 public function __construct(MediaFileService $media_file_service, PendingChangesService $pending_changes_service) 60 { 61 $this->media_file_service = $media_file_service; 62 $this->pending_changes_service = $pending_changes_service; 63 } 64 65 /** 66 * Save an edited media file. 67 * 68 * @param ServerRequestInterface $request 69 * 70 * @return ResponseInterface 71 */ 72 public function handle(ServerRequestInterface $request): ResponseInterface 73 { 74 $tree = Validator::attributes($request)->tree(); 75 $xref = Validator::attributes($request)->isXref()->string('xref'); 76 $fact_id = Validator::attributes($request)->string('fact_id'); 77 $folder = Validator::parsedBody($request)->string('folder'); 78 $new_file = Validator::parsedBody($request)->string('new_file'); 79 $remote = Validator::parsedBody($request)->string('remote'); 80 $title = Validator::parsedBody($request)->string('title'); 81 $type = Validator::parsedBody($request)->string('type'); 82 $media = Registry::mediaFactory()->make($xref, $tree); 83 $media = Auth::checkMediaAccess($media, true); 84 85 $type = Registry::elementFactory()->make('OBJE:FILE:FORM:TYPE')->canonical($type); 86 $title = Registry::elementFactory()->make('OBJE:FILE:TITL')->canonical($title); 87 88 // Find the fact to edit 89 $media_file = $media->mediaFiles() 90 ->first(static function (MediaFile $media_file) use ($fact_id): bool { 91 return $media_file->factId() === $fact_id; 92 }); 93 94 // Media file does not exist? 95 if ($media_file === null) { 96 return redirect(route(TreePage::class, ['tree' => $tree->name()])); 97 } 98 99 // We can edit the file as either a URL or a folder/file 100 if ($remote !== '') { 101 $file = $remote; 102 } else { 103 $new_file = str_replace('\\', '/', $new_file); 104 $folder = str_replace('\\', '/', $folder); 105 $folder = trim($folder, '/'); 106 107 if ($folder === '') { 108 $file = $new_file; 109 } else { 110 $file = $folder . '/' . $new_file; 111 } 112 } 113 114 // Invalid filename? Do not change it. 115 if ($new_file === '') { 116 $file = $media_file->filename(); 117 } 118 119 $filesystem = $media->tree()->mediaFilesystem(); 120 $old = $media_file->filename(); 121 $new = $file; 122 123 // Update the filesystem, if we can. 124 if ($old !== $new && !$media_file->isExternal() && $filesystem->fileExists($old)) { 125 try { 126 $file_exists = $filesystem->fileExists($old); 127 128 if ($file_exists) { 129 try { 130 $filesystem->move($old, $new); 131 FlashMessages::addMessage(I18N::translate('The media file %1$s has been renamed to %2$s.', Html::filename($media_file->filename()), Html::filename($file)), 'info'); 132 } catch (FilesystemException | UnableToMoveFile) { 133 // Don't overwrite existing file 134 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'); 135 $file = $old; 136 } 137 } 138 } catch (FilesystemException | UnableToRetrieveMetadata) { 139 // File does not exist? 140 } 141 } 142 143 $gedcom = $this->media_file_service->createMediaFileGedcom($file, $type, $title, ''); 144 145 $media->updateFact($fact_id, $gedcom, true); 146 147 // Accept the changes, to keep the filesystem in sync with the GEDCOM data. 148 if ($old !== $new && !$media_file->isExternal()) { 149 $this->pending_changes_service->acceptRecord($media); 150 } 151 152 return redirect($media->url()); 153 } 154} 155