summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Functions/FunctionsEdit.php3
-rw-r--r--app/Http/Controllers/EditMediaController.php232
-rw-r--r--app/Http/Controllers/EditNoteController.php57
-rw-r--r--app/Http/RequestHandlers/CreateMediaObjectAction.php125
-rw-r--r--app/Http/RequestHandlers/CreateMediaObjectModal.php65
-rw-r--r--app/Http/RequestHandlers/CreateNoteAction.php79
-rw-r--r--app/Http/RequestHandlers/CreateNoteModal.php52
-rw-r--r--app/Http/RequestHandlers/CreateRepositoryModal.php2
-rw-r--r--app/Http/RequestHandlers/CreateSourceModal.php90
-rw-r--r--app/Services/MediaFileService.php240
10 files changed, 588 insertions, 357 deletions
diff --git a/app/Functions/FunctionsEdit.php b/app/Functions/FunctionsEdit.php
index 68e65076dd..1b50dae045 100644
--- a/app/Functions/FunctionsEdit.php
+++ b/app/Functions/FunctionsEdit.php
@@ -37,6 +37,7 @@ use Fisharebest\Webtrees\GedcomCode\GedcomCodeTemp;
use Fisharebest\Webtrees\GedcomRecord;
use Fisharebest\Webtrees\GedcomTag;
use Fisharebest\Webtrees\Html;
+use Fisharebest\Webtrees\Http\RequestHandlers\CreateMediaObjectModal;
use Fisharebest\Webtrees\Http\RequestHandlers\CreateRepositoryModal;
use Fisharebest\Webtrees\Http\RequestHandlers\CreateSourceModal;
use Fisharebest\Webtrees\Http\RequestHandlers\CreateSubmitterModal;
@@ -488,7 +489,7 @@ class FunctionsEdit
} elseif ($fact === 'OBJE') {
$html .=
'<div class="input-group">' .
- '<div class="input-group-prepend"><button class="btn btn-secondary" type="button" data-toggle="modal" data-href="' . e(route('create-media-object', ['tree' => $tree->name()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a media object') . '">' . view('icons/add') . '</button></div>' .
+ '<div class="input-group-prepend"><button class="btn btn-secondary" type="button" data-toggle="modal" data-href="' . e(route(CreateMediaObjectModal::class, ['tree' => $tree->name()])) . '" data-target="#wt-ajax-modal" data-select-id="' . $id . '" title="' . I18N::translate('Create a media object') . '">' . view('icons/add') . '</button></div>' .
view('components/select-media', ['id' => $id, 'name' => $name, 'media' => Media::getInstance($value, $tree), 'tree' => $tree]) .
'</div>';
} elseif ($fact === 'PAGE') {
diff --git a/app/Http/Controllers/EditMediaController.php b/app/Http/Controllers/EditMediaController.php
index 2310f4e325..a087b4a8c6 100644
--- a/app/Http/Controllers/EditMediaController.php
+++ b/app/Http/Controllers/EditMediaController.php
@@ -24,44 +24,27 @@ use Fig\Http\Message\StatusCodeInterface;
use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\FlashMessages;
use Fisharebest\Webtrees\GedcomRecord;
-use Fisharebest\Webtrees\GedcomTag;
use Fisharebest\Webtrees\Html;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Media;
+use Fisharebest\Webtrees\Services\MediaFileService;
use Fisharebest\Webtrees\Services\PendingChangesService;
use Fisharebest\Webtrees\Tree;
-use Illuminate\Database\Capsule\Manager as DB;
-use InvalidArgumentException;
use League\Flysystem\FileExistsException;
use League\Flysystem\FileNotFoundException;
use League\Flysystem\Util;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Message\UploadedFileInterface;
-use RuntimeException;
-use Symfony\Component\HttpFoundation\File\UploadedFile;
use function assert;
-use function pathinfo;
-use function strpos;
-
-use const PATHINFO_EXTENSION;
-use const UPLOAD_ERR_OK;
/**
* Controller for edit forms and responses.
*/
class EditMediaController extends AbstractEditController
{
- private const EDIT_RESTRICTIONS = [
- 'locked',
- ];
-
- private const PRIVACY_RESTRICTIONS = [
- 'none',
- 'privacy',
- 'confidential',
- ];
+ /** @var MediaFileService */
+ private $media_file_service;
/** @var PendingChangesService */
private $pending_changes_service;
@@ -69,10 +52,12 @@ class EditMediaController extends AbstractEditController
/**
* EditMediaController constructor.
*
+ * @param MediaFileService $media_file_service
* @param PendingChangesService $pending_changes_service
*/
- public function __construct(PendingChangesService $pending_changes_service)
+ public function __construct(MediaFileService $media_file_service, PendingChangesService $pending_changes_service)
{
+ $this->media_file_service = $media_file_service;
$this->pending_changes_service = $pending_changes_service;
}
@@ -101,10 +86,10 @@ class EditMediaController extends AbstractEditController
}
return response(view('modals/add-media-file', [
- 'max_upload_size' => $this->maxUploadFilesize(),
+ 'max_upload_size' => $this->media_file_service->maxUploadFilesize(),
'media' => $media,
- 'media_types' => $this->mediaTypes(),
- 'unused_files' => $this->unusedFiles($tree),
+ 'media_types' => $this->media_file_service->mediaTypes(),
+ 'unused_files' => $this->media_file_service->unusedFiles($tree),
]));
}
@@ -133,7 +118,7 @@ class EditMediaController extends AbstractEditController
return redirect(route('tree-page', ['tree' => $tree->name()]));
}
- $file = $this->uploadFile($request);
+ $file = $this->media_file_service->uploadFile($request);
if ($file === '') {
FlashMessages::addMessage(I18N::translate('There was an error uploading your file.'));
@@ -187,10 +172,10 @@ class EditMediaController extends AbstractEditController
if ($media_file->factId() === $fact_id) {
return response(view('modals/edit-media-file', [
'media_file' => $media_file,
- 'max_upload_size' => $this->maxUploadFilesize(),
+ 'max_upload_size' => $this->media_file_service->maxUploadFilesize(),
'media' => $media,
- 'media_types' => $this->mediaTypes(),
- 'unused_files' => $this->unusedFiles($tree),
+ 'media_types' => $this->media_file_service->mediaTypes(),
+ 'unused_files' => $this->media_file_service->unusedFiles($tree),
]));
}
}
@@ -281,7 +266,7 @@ class EditMediaController extends AbstractEditController
}
}
- $gedcom = $this->createMediaFileGedcom($file, $type, $title);
+ $gedcom = $this->media_file_service->createMediaFileGedcom($file, $type, $title);
$media->updateFact($fact_id, $gedcom, true);
@@ -306,9 +291,9 @@ class EditMediaController extends AbstractEditController
assert($tree instanceof Tree);
return response(view('modals/create-media-object', [
- 'max_upload_size' => $this->maxUploadFilesize(),
- 'media_types' => $this->mediaTypes(),
- 'unused_files' => $this->unusedFiles($tree),
+ 'max_upload_size' => $this->media_file_service->maxUploadFilesize(),
+ 'media_types' => $this->media_file_service->mediaTypes(),
+ 'unused_files' => $this->media_file_service->unusedFiles($tree),
]));
}
@@ -385,23 +370,23 @@ class EditMediaController extends AbstractEditController
"\n",
], "\n1 CONT ", $note);
- $file = $this->uploadFile($request);
+ $file = $this->media_file_service->uploadFile($request);
if ($file === '') {
return response(['error_message' => I18N::translate('There was an error uploading your file.')], 406);
}
- $gedcom = "0 @@ OBJE\n" . $this->createMediaFileGedcom($file, $type, $title);
+ $gedcom = "0 @@ OBJE\n" . $this->media_file_service->createMediaFileGedcom($file, $type, $title);
if ($note !== '') {
$gedcom .= "\n1 NOTE " . preg_replace('/\r?\n/', "\n2 CONT ", $note);
}
- if (in_array($privacy_restriction, self::PRIVACY_RESTRICTIONS, true)) {
+ if (in_array($privacy_restriction, $this->media_file_service::PRIVACY_RESTRICTIONS, true)) {
$gedcom .= "\n1 RESN " . $privacy_restriction;
}
- if (in_array($edit_restriction, self::EDIT_RESTRICTIONS, true)) {
+ if (in_array($edit_restriction, $this->media_file_service::EDIT_RESTRICTIONS, true)) {
$gedcom .= "\n1 RESN " . $edit_restriction;
}
@@ -504,179 +489,4 @@ class EditMediaController extends AbstractEditController
return redirect($media->url());
}
-
- /**
- * Convert the media file attributes into GEDCOM format.
- *
- * @param string $file
- * @param string $type
- * @param string $title
- *
- * @return string
- */
- private function createMediaFileGedcom(string $file, string $type, string $title): string
- {
- if (preg_match('/\.([a-z0-9]+)/i', $file, $match)) {
- $extension = strtolower($match[1]);
- $extension = str_replace('jpg', 'jpeg', $extension);
- $extension = ' ' . $extension;
- } else {
- $extension = '';
- }
-
- $gedcom = '1 FILE ' . $file;
- if ($type !== '') {
- $gedcom .= "\n2 FORM" . $extension . "\n3 TYPE " . $type;
- }
- if ($title !== '') {
- $gedcom .= "\n2 TITL " . $title;
- }
-
- return $gedcom;
- }
-
- /**
- * What is the largest file a user may upload?
- */
- private function maxUploadFilesize(): string
- {
- $bytes = UploadedFile::getMaxFilesize();
- $kb = intdiv($bytes + 1023, 1024);
-
- return I18N::translate('%s KB', I18N::number($kb));
- }
-
- /**
- * A list of key/value options for media types.
- *
- * @param string $current
- *
- * @return array
- */
- private function mediaTypes($current = ''): array
- {
- $media_types = GedcomTag::getFileFormTypes();
-
- $media_types = ['' => ''] + [$current => $current] + $media_types;
-
- return $media_types;
- }
-
- /**
- * Store an uploaded file (or URL), either to be added to a media object
- * or to create a media object.
- *
- * @param ServerRequestInterface $request
- *
- * @return string The value to be stored in the 'FILE' field of the media object.
- */
- private function uploadFile(ServerRequestInterface $request): string
- {
- $tree = $request->getAttribute('tree');
- assert($tree instanceof Tree);
-
- $params = $request->getParsedBody();
- $file_location = $params['file_location'];
-
- switch ($file_location) {
- case 'url':
- $remote = $params['remote'];
-
- if (strpos($remote, '://') !== false) {
- return $remote;
- }
-
- return '';
-
- case 'unused':
- $unused = $params['unused'];
-
- if ($tree->mediaFilesystem()->has($unused)) {
- return $unused;
- }
-
- return '';
-
- case 'upload':
- default:
- $folder = $params['folder'];
- $auto = $params['auto'];
- $new_file = $params['new_file'];
-
- /** @var UploadedFileInterface|null $uploaded_file */
- $uploaded_file = $request->getUploadedFiles()['file'];
- if ($uploaded_file === null || $uploaded_file->getError() !== UPLOAD_ERR_OK) {
- return '';
- }
-
- // The filename
- $new_file = str_replace('\\', '/', $new_file);
- if ($new_file !== '' && strpos($new_file, '/') === false) {
- $file = $new_file;
- } else {
- $file = $uploaded_file->getClientFilename();
- }
-
- // The folder
- $folder = str_replace('\\', '/', $folder);
- $folder = trim($folder, '/');
- if ($folder !== '') {
- $folder .= '/';
- }
-
- // Generate a unique name for the file?
- if ($auto === '1' || $tree->mediaFilesystem()->has($folder . $file)) {
- $folder = '';
- $extension = pathinfo($uploaded_file->getClientFilename(), PATHINFO_EXTENSION);
- $file = sha1((string) $uploaded_file->getStream()) . '.' . $extension;
- }
-
- try {
- $tree->mediaFilesystem()->writeStream($folder . $file, $uploaded_file->getStream()->detach());
-
- return $folder . $file;
- } catch (RuntimeException | InvalidArgumentException $ex) {
- FlashMessages::addMessage(I18N::translate('There was an error uploading your file.'));
-
- return '';
- }
- }
- }
-
- /**
- * A list of media files not already linked to a media object.
- *
- * @param Tree $tree
- *
- * @return array
- */
- private function unusedFiles(Tree $tree): array
- {
- $used_files = DB::table('media_file')
- ->where('m_file', '=', $tree->id())
- ->where('multimedia_file_refn', 'NOT LIKE', 'http://%')
- ->where('multimedia_file_refn', 'NOT LIKE', 'https://%')
- ->pluck('multimedia_file_refn')
- ->all();
-
- $disk_files = $tree->mediaFilesystem()->listContents('', true);
-
- $disk_files = array_filter($disk_files, static function (array $item) {
- // Older versions of webtrees used a couple of special folders.
- return
- $item['type'] === 'file' &&
- strpos($item['path'], '/thumbs/') === false &&
- strpos($item['path'], '/watermarks/') === false;
- });
-
- $disk_files = array_map(static function (array $item): string {
- return $item['path'];
- }, $disk_files);
-
- $unused_files = array_diff($disk_files, $used_files);
-
- sort($unused_files);
-
- return array_combine($unused_files, $unused_files);
- }
}
diff --git a/app/Http/Controllers/EditNoteController.php b/app/Http/Controllers/EditNoteController.php
index 8b3622f074..6fe0cb48cb 100644
--- a/app/Http/Controllers/EditNoteController.php
+++ b/app/Http/Controllers/EditNoteController.php
@@ -40,18 +40,6 @@ class EditNoteController extends AbstractEditController
*
* @return ResponseInterface
*/
- public function createNoteObject(ServerRequestInterface $request): ResponseInterface
- {
- return response(view('modals/create-note-object'));
- }
-
- /**
- * Show a form to create a new note object.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
public function editNoteObject(ServerRequestInterface $request): ResponseInterface
{
$tree = $request->getAttribute('tree');
@@ -107,49 +95,4 @@ class EditNoteController extends AbstractEditController
return redirect($note->url());
}
-
- /**
- * Process a form to create a new note object.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function createNoteObjectAction(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- assert($tree instanceof Tree);
-
- $params = $request->getParsedBody();
- $note = $params['note'];
- $privacy_restriction = $params['privacy-restriction'];
- $edit_restriction = $params['edit-restriction'];
-
- // Convert line endings to GEDDCOM continuations
- $note = preg_replace('/\r|\r\n|\n/', "\n1 CONT ", $note);
-
- $gedcom = '0 @@ NOTE ' . $note;
-
- if (in_array($privacy_restriction, ['none', 'privacy', 'confidential'], true)) {
- $gedcom .= "\n1 RESN " . $privacy_restriction;
- }
-
- if ($edit_restriction === 'locked') {
- $gedcom .= "\n1 RESN " . $edit_restriction;
- }
-
- $record = $tree->createRecord($gedcom);
-
- return response([
- 'id' => $record->xref(),
- 'text' => view('selects/note', [
- 'note' => $record,
- ]),
- 'html' => view('modals/record-created', [
- 'title' => I18N::translate('The note has been created'),
- 'name' => $record->fullName(),
- 'url' => $record->url(),
- ]),
- ]);
- }
}
diff --git a/app/Http/RequestHandlers/CreateMediaObjectAction.php b/app/Http/RequestHandlers/CreateMediaObjectAction.php
new file mode 100644
index 0000000000..20f8e8d17e
--- /dev/null
+++ b/app/Http/RequestHandlers/CreateMediaObjectAction.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Http\RequestHandlers;
+
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\MediaFileService;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Fisharebest\Webtrees\Tree;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function in_array;
+
+/**
+ * Process a form to create a new media object.
+ */
+class CreateMediaObjectAction implements RequestHandlerInterface
+{
+ /** @var MediaFileService */
+ private $media_file_service;
+
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * CreateMediaObjectAction constructor.
+ *
+ * @param MediaFileService $media_file_service
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(MediaFileService $media_file_service, PendingChangesService $pending_changes_service)
+ {
+ $this->media_file_service = $media_file_service;
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * Process a form to create a new media object.
+ *
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree);
+
+ $params = $request->getParsedBody();
+ $note = $params['media-note'];
+ $title = $params['title'];
+ $type = $params['type'];
+ $privacy_restriction = $params['privacy-restriction'];
+ $edit_restriction = $params['edit-restriction'];
+
+ // Tidy whitespace
+ $type = trim(preg_replace('/\s+/', ' ', $type));
+ $title = trim(preg_replace('/\s+/', ' ', $title));
+
+ // Convert line endings to GEDDCOM continuations
+ $note = str_replace([
+ "\r\n",
+ "\r",
+ "\n",
+ ], "\n1 CONT ", $note);
+
+ $file = $this->media_file_service->uploadFile($request);
+
+ if ($file === '') {
+ return response(['error_message' => I18N::translate('There was an error uploading your file.')], 406);
+ }
+
+ $gedcom = "0 @@ OBJE\n" . $this->media_file_service->createMediaFileGedcom($file, $type, $title);
+
+ if ($note !== '') {
+ $gedcom .= "\n1 NOTE " . preg_replace('/\r?\n/', "\n2 CONT ", $note);
+ }
+
+ if (in_array($privacy_restriction, $this->media_file_service::PRIVACY_RESTRICTIONS, true)) {
+ $gedcom .= "\n1 RESN " . $privacy_restriction;
+ }
+
+ if (in_array($edit_restriction, $this->media_file_service::EDIT_RESTRICTIONS, true)) {
+ $gedcom .= "\n1 RESN " . $edit_restriction;
+ }
+
+ $record = $tree->createMediaObject($gedcom);
+
+ // Accept the new record to keep the filesystem synchronized with the genealogy.
+ $this->pending_changes_service->acceptRecord($record);
+
+ // id and text are for select2 / autocomplete
+ // html is for interactive modals
+ return response([
+ 'id' => $record->xref(),
+ 'text' => view('selects/media', [
+ 'media' => $record,
+ ]),
+ 'html' => view('modals/record-created', [
+ 'title' => I18N::translate('The media object has been created'),
+ 'name' => $record->fullName(),
+ 'url' => $record->url(),
+ ]),
+ ]);
+ }
+}
diff --git a/app/Http/RequestHandlers/CreateMediaObjectModal.php b/app/Http/RequestHandlers/CreateMediaObjectModal.php
new file mode 100644
index 0000000000..d598b00844
--- /dev/null
+++ b/app/Http/RequestHandlers/CreateMediaObjectModal.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Http\RequestHandlers;
+
+use Fisharebest\Webtrees\Services\MediaFileService;
+use Fisharebest\Webtrees\Tree;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+
+/**
+ * Show a form to create a new media object.
+ */
+class CreateMediaObjectModal implements RequestHandlerInterface
+{
+ /** @var MediaFileService */
+ private $media_file_service;
+
+ /**
+ * CreateMediaObjectModal constructor.
+ *
+ * @param MediaFileService $media_file_service
+ */
+ public function __construct(MediaFileService $media_file_service)
+ {
+ $this->media_file_service = $media_file_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree);
+
+ return response(view('modals/create-media-object', [
+ 'max_upload_size' => $this->media_file_service->maxUploadFilesize(),
+ 'media_types' => $this->media_file_service->mediaTypes(),
+ 'unused_files' => $this->media_file_service->unusedFiles($tree),
+ 'tree' => $tree,
+ ]));
+ }
+}
diff --git a/app/Http/RequestHandlers/CreateNoteAction.php b/app/Http/RequestHandlers/CreateNoteAction.php
new file mode 100644
index 0000000000..018b1dfd50
--- /dev/null
+++ b/app/Http/RequestHandlers/CreateNoteAction.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Http\RequestHandlers;
+
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Tree;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+
+/**
+ * Process a form to create a new note object.
+ */
+class CreateNoteAction implements RequestHandlerInterface
+{
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree);
+
+ $params = $request->getParsedBody();
+ $note = $params['note'];
+ $privacy_restriction = $params['privacy-restriction'];
+ $edit_restriction = $params['edit-restriction'];
+
+ // Convert line endings to GEDDCOM continuations
+ $note = preg_replace('/\r|\r\n|\n/', "\n1 CONT ", $note);
+
+ $gedcom = '0 @@ NOTE ' . $note;
+
+ if (in_array($privacy_restriction, ['none', 'privacy', 'confidential'], true)) {
+ $gedcom .= "\n1 RESN " . $privacy_restriction;
+ }
+
+ if ($edit_restriction === 'locked') {
+ $gedcom .= "\n1 RESN " . $edit_restriction;
+ }
+
+ $record = $tree->createRecord($gedcom);
+
+ // id and text are for select2 / autocomplete
+ // html is for interactive modals
+ return response([
+ 'id' => $record->xref(),
+ 'text' => view('selects/note', [
+ 'note' => $record,
+ ]),
+ 'html' => view('modals/record-created', [
+ 'title' => I18N::translate('The note has been created'),
+ 'name' => $record->fullName(),
+ 'url' => $record->url(),
+ ]),
+ ]);
+ }
+}
diff --git a/app/Http/RequestHandlers/CreateNoteModal.php b/app/Http/RequestHandlers/CreateNoteModal.php
new file mode 100644
index 0000000000..37624045ae
--- /dev/null
+++ b/app/Http/RequestHandlers/CreateNoteModal.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Http\RequestHandlers;
+
+use Fisharebest\Webtrees\Tree;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function response;
+use function view;
+
+/**
+ * Show a form to create a new note object.
+ */
+class CreateNoteModal implements RequestHandlerInterface
+{
+ /**
+ * Show a form to create a new note object.
+ *
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree);
+
+ return response(view('modals/create-note-object', [
+ 'tree' => $tree,
+ ]));
+ }
+}
diff --git a/app/Http/RequestHandlers/CreateRepositoryModal.php b/app/Http/RequestHandlers/CreateRepositoryModal.php
index def6c0b3f3..cffa7497e9 100644
--- a/app/Http/RequestHandlers/CreateRepositoryModal.php
+++ b/app/Http/RequestHandlers/CreateRepositoryModal.php
@@ -26,6 +26,8 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function response;
+use function view;
/**
* Show a form to create a new repository.
diff --git a/app/Http/RequestHandlers/CreateSourceModal.php b/app/Http/RequestHandlers/CreateSourceModal.php
index 2e29b15431..41cf0019e6 100644
--- a/app/Http/RequestHandlers/CreateSourceModal.php
+++ b/app/Http/RequestHandlers/CreateSourceModal.php
@@ -19,13 +19,14 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Http\RequestHandlers;
-use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Tree;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function assert;
+use function response;
+use function view;
/**
* Process a form to create a new source.
@@ -46,91 +47,4 @@ class CreateSourceModal implements RequestHandlerInterface
'tree' => $tree,
]));
}
-
- /**
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function createSourceAction(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- assert($tree instanceof Tree);
-
- $params = $request->getParsedBody();
- $title = $params['source-title'];
- $abbreviation = $params['source-abbreviation'];
- $author = $params['source-author'];
- $publication = $params['source-publication'];
- $repository = $params['source-repository'];
- $call_number = $params['source-call-number'];
- $text = $params['source-text'];
- $privacy_restriction = $params['privacy-restriction'];
- $edit_restriction = $params['edit-restriction'];
-
- // Fix whitespace
- $title = trim(preg_replace('/\s+/', ' ', $title));
- $abbreviation = trim(preg_replace('/\s+/', ' ', $abbreviation));
- $author = trim(preg_replace('/\s+/', ' ', $author));
- $publication = trim(preg_replace('/\s+/', ' ', $publication));
- $repository = trim(preg_replace('/\s+/', ' ', $repository));
- $call_number = trim(preg_replace('/\s+/', ' ', $call_number));
-
- // Convert line endings to GEDDCOM continuations
- $text = str_replace([
- "\r\n",
- "\r",
- "\n",
- ], "\n1 CONT ", $text);
-
- $gedcom = "0 @@ SOUR\n\n1 TITL " . $title;
-
- if ($abbreviation !== '') {
- $gedcom .= "\n1 ABBR " . $abbreviation;
- }
-
- if ($author !== '') {
- $gedcom .= "\n1 AUTH " . $author;
- }
-
- if ($publication !== '') {
- $gedcom .= "\n1 PUBL " . $publication;
- }
-
- if ($text !== '') {
- $gedcom .= "\n1 TEXT " . $text;
- }
-
- if ($repository !== '') {
- $gedcom .= "\n1 REPO @" . $repository . '@';
-
- if ($call_number !== '') {
- $gedcom .= "\n2 CALN " . $call_number;
- }
- }
-
- if (in_array($privacy_restriction, ['none', 'privacy', 'confidential'], true)) {
- $gedcom .= "\n1 RESN " . $privacy_restriction;
- }
-
- if ($edit_restriction === 'locked') {
- $gedcom .= "\n1 RESN " . $edit_restriction;
- }
-
- $record = $tree->createRecord($gedcom);
-
- // id and text are for select2 / autocomplete
- // html is for interactive modals
- return response([
- 'id' => $record->xref(),
- 'text' => view('selects/source', [
- 'source' => $record,
- ]),
- 'html' => view('modals/record-created', [
- 'title' => I18N::translate('The source has been created'),
- 'name' => $record->fullName(),
- 'url' => $record->url(),
- ]),
- ]);
- }
}
diff --git a/app/Services/MediaFileService.php b/app/Services/MediaFileService.php
new file mode 100644
index 0000000000..9532562713
--- /dev/null
+++ b/app/Services/MediaFileService.php
@@ -0,0 +1,240 @@
+<?php
+
+/**
+ * webtrees: online genealogy
+ * Copyright (C) 2019 webtrees development team
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+declare(strict_types=1);
+
+namespace Fisharebest\Webtrees\Services;
+
+use Fisharebest\Webtrees\FlashMessages;
+use Fisharebest\Webtrees\GedcomTag;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Tree;
+use Illuminate\Database\Capsule\Manager as DB;
+use InvalidArgumentException;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use RuntimeException;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+use function array_combine;
+use function array_diff;
+use function array_filter;
+use function array_map;
+use function assert;
+use function intdiv;
+use function pathinfo;
+use function preg_match;
+use function sha1;
+use function sort;
+use function str_replace;
+use function strpos;
+use function strtolower;
+use function trim;
+
+use const PATHINFO_EXTENSION;
+use const UPLOAD_ERR_OK;
+
+/**
+ * Managing media files.
+ */
+class MediaFileService
+{
+ public const EDIT_RESTRICTIONS = [
+ 'locked',
+ ];
+
+ public const PRIVACY_RESTRICTIONS = [
+ 'none',
+ 'privacy',
+ 'confidential',
+ ];
+
+ /**
+ * What is the largest file a user may upload?
+ */
+ public function maxUploadFilesize(): string
+ {
+ $bytes = UploadedFile::getMaxFilesize();
+ $kb = intdiv($bytes + 1023, 1024);
+
+ return I18N::translate('%s KB', I18N::number($kb));
+ }
+
+ /**
+ * A list of key/value options for media types.
+ *
+ * @param string $current
+ *
+ * @return array
+ */
+ public function mediaTypes($current = ''): array
+ {
+ $media_types = GedcomTag::getFileFormTypes();
+
+ $media_types = ['' => ''] + [$current => $current] + $media_types;
+
+ return $media_types;
+ }
+
+ /**
+ * A list of media files not already linked to a media object.
+ *
+ * @param Tree $tree
+ *
+ * @return array
+ */
+ public function unusedFiles(Tree $tree): array
+ {
+ $used_files = DB::table('media_file')
+ ->where('m_file', '=', $tree->id())
+ ->where('multimedia_file_refn', 'NOT LIKE', 'http://%')
+ ->where('multimedia_file_refn', 'NOT LIKE', 'https://%')
+ ->pluck('multimedia_file_refn')
+ ->all();
+
+ $disk_files = $tree->mediaFilesystem()->listContents('', true);
+
+ $disk_files = array_filter($disk_files, static function (array $item) {
+ // Older versions of webtrees used a couple of special folders.
+ return
+ $item['type'] === 'file' &&
+ strpos($item['path'], '/thumbs/') === false &&
+ strpos($item['path'], '/watermarks/') === false;
+ });
+
+ $disk_files = array_map(static function (array $item): string {
+ return $item['path'];
+ }, $disk_files);
+
+ $unused_files = array_diff($disk_files, $used_files);
+
+ sort($unused_files);
+
+ return array_combine($unused_files, $unused_files);
+ }
+
+ /**
+ * Store an uploaded file (or URL), either to be added to a media object
+ * or to create a media object.
+ *
+ * @param ServerRequestInterface $request
+ *
+ * @return string The value to be stored in the 'FILE' field of the media object.
+ */
+ public function uploadFile(ServerRequestInterface $request): string
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree);
+
+ $params = $request->getParsedBody();
+ $file_location = $params['file_location'];
+
+ switch ($file_location) {
+ case 'url':
+ $remote = $params['remote'];
+
+ if (strpos($remote, '://') !== false) {
+ return $remote;
+ }
+
+ return '';
+
+ case 'unused':
+ $unused = $params['unused'];
+
+ if ($tree->mediaFilesystem()->has($unused)) {
+ return $unused;
+ }
+
+ return '';
+
+ case 'upload':
+ default:
+ $folder = $params['folder'];
+ $auto = $params['auto'];
+ $new_file = $params['new_file'];
+
+ /** @var UploadedFileInterface|null $uploaded_file */
+ $uploaded_file = $request->getUploadedFiles()['file'];
+ if ($uploaded_file === null || $uploaded_file->getError() !== UPLOAD_ERR_OK) {
+ return '';
+ }
+
+ // The filename
+ $new_file = str_replace('\\', '/', $new_file);
+ if ($new_file !== '' && strpos($new_file, '/') === false) {
+ $file = $new_file;
+ } else {
+ $file = $uploaded_file->getClientFilename();
+ }
+
+ // The folder
+ $folder = str_replace('\\', '/', $folder);
+ $folder = trim($folder, '/');
+ if ($folder !== '') {
+ $folder .= '/';
+ }
+
+ // Generate a unique name for the file?
+ if ($auto === '1' || $tree->mediaFilesystem()->has($folder . $file)) {
+ $folder = '';
+ $extension = pathinfo($uploaded_file->getClientFilename(), PATHINFO_EXTENSION);
+ $file = sha1((string) $uploaded_file->getStream()) . '.' . $extension;
+ }
+
+ try {
+ $tree->mediaFilesystem()->writeStream($folder . $file, $uploaded_file->getStream()->detach());
+
+ return $folder . $file;
+ } catch (RuntimeException | InvalidArgumentException $ex) {
+ FlashMessages::addMessage(I18N::translate('There was an error uploading your file.'));
+
+ return '';
+ }
+ }
+ }
+
+ /**
+ * Convert the media file attributes into GEDCOM format.
+ *
+ * @param string $file
+ * @param string $type
+ * @param string $title
+ *
+ * @return string
+ */
+ public function createMediaFileGedcom(string $file, string $type, string $title): string
+ {
+ if (preg_match('/\.([a-z0-9]+)/i', $file, $match)) {
+ $extension = strtolower($match[1]);
+ $extension = str_replace('jpg', 'jpeg', $extension);
+ $extension = ' ' . $extension;
+ } else {
+ $extension = '';
+ }
+
+ $gedcom = '1 FILE ' . $file;
+ if ($type !== '') {
+ $gedcom .= "\n2 FORM" . $extension . "\n3 TYPE " . $type;
+ }
+ if ($title !== '') {
+ $gedcom .= "\n2 TITL " . $title;
+ }
+
+ return $gedcom;
+ }
+}