summaryrefslogtreecommitdiff
path: root/app/Http
diff options
context:
space:
mode:
Diffstat (limited to 'app/Http')
-rw-r--r--app/Http/Controllers/Admin/ChangesLogController.php319
-rw-r--r--app/Http/Controllers/Admin/ImportThumbnailsController.php14
-rw-r--r--app/Http/Controllers/EditMediaController.php23
-rw-r--r--app/Http/Controllers/PendingChangesController.php326
-rw-r--r--app/Http/RequestHandlers/PendingChanges.php146
-rw-r--r--app/Http/RequestHandlers/PendingChangesAcceptChange.php69
-rw-r--r--app/Http/RequestHandlers/PendingChangesAcceptRecord.php78
-rw-r--r--app/Http/RequestHandlers/PendingChangesAcceptTree.php66
-rw-r--r--app/Http/RequestHandlers/PendingChangesLogAction.php66
-rw-r--r--app/Http/RequestHandlers/PendingChangesLogData.php135
-rw-r--r--app/Http/RequestHandlers/PendingChangesLogDelete.php58
-rw-r--r--app/Http/RequestHandlers/PendingChangesLogDownload.php75
-rw-r--r--app/Http/RequestHandlers/PendingChangesLogPage.php133
-rw-r--r--app/Http/RequestHandlers/PendingChangesRejectChange.php69
-rw-r--r--app/Http/RequestHandlers/PendingChangesRejectRecord.php73
-rw-r--r--app/Http/RequestHandlers/PendingChangesRejectTree.php67
16 files changed, 1063 insertions, 654 deletions
diff --git a/app/Http/Controllers/Admin/ChangesLogController.php b/app/Http/Controllers/Admin/ChangesLogController.php
deleted file mode 100644
index d1e8d388fb..0000000000
--- a/app/Http/Controllers/Admin/ChangesLogController.php
+++ /dev/null
@@ -1,319 +0,0 @@
-<?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\Controllers\Admin;
-
-use Fig\Http\Message\StatusCodeInterface;
-use Fisharebest\Algorithm\MyersDiff;
-use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Carbon;
-use Fisharebest\Webtrees\Gedcom;
-use Fisharebest\Webtrees\GedcomRecord;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Services\DatatablesService;
-use Fisharebest\Webtrees\Services\TreeService;
-use Fisharebest\Webtrees\Services\UserService;
-use Fisharebest\Webtrees\Tree;
-use Illuminate\Database\Capsule\Manager as DB;
-use Illuminate\Database\Query\Builder;
-use Illuminate\Database\Query\Expression;
-use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\ServerRequestInterface;
-use stdClass;
-
-use function explode;
-use function implode;
-use function preg_replace_callback;
-
-/**
- * Controller for the changes log.
- */
-class ChangesLogController extends AbstractAdminController
-{
- /** @var DatatablesService */
- private $datatables_service;
-
- /** @var MyersDiff */
- private $myers_diff;
-
- /** @var TreeService */
- private $tree_service;
-
- /** @var UserService */
- private $user_service;
-
- /**
- * ChangesLogController constructor.
- *
- * @param DatatablesService $datatables_service
- * @param MyersDiff $myers_diff
- * @param TreeService $tree_service
- * @param UserService $user_service
- */
- public function __construct(DatatablesService $datatables_service, MyersDiff $myers_diff, TreeService $tree_service, UserService $user_service)
- {
- $this->datatables_service = $datatables_service;
- $this->myers_diff = $myers_diff;
- $this->tree_service = $tree_service;
- $this->user_service = $user_service;
- }
-
- /**
- * Show the edit history for a tree.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function changesLog(ServerRequestInterface $request): ResponseInterface
- {
- $tree_list = [];
- foreach ($this->tree_service->all() as $tree) {
- if (Auth::isManager($tree)) {
- $tree_list[$tree->name()] = $tree->title();
- }
- }
-
- $user_list = ['' => ''];
- foreach ($this->user_service->all() as $tmp_user) {
- $user_list[$tmp_user->userName()] = $tmp_user->userName();
- }
-
- $action = $request->getQueryParams()['action'] ?? '';
-
- // @TODO This ought to be a POST action
- if ($action === 'delete') {
- $this->changesQuery($request)->delete();
- }
-
- // First and last change in the database.
- $earliest = DB::table('change')->min('change_time');
- $latest = DB::table('change')->max('change_time');
-
- $earliest = $earliest !== null ? Carbon::make($earliest) : Carbon::now();
- $latest = $latest !== null ? Carbon::make($latest) : Carbon::now();
-
- $earliest = $earliest->toDateString();
- $latest = $latest->toDateString();
-
- $ged = $request->getQueryParams()['ged'] ?? '';
- $from = $request->getQueryParams()['from'] ?? $earliest;
- $to = $request->getQueryParams()['to'] ?? $latest;
- $type = $request->getQueryParams()['type'] ?? '';
- $oldged = $request->getQueryParams()['oldged'] ?? '';
- $newged = $request->getQueryParams()['newged'] ?? '';
- $xref = $request->getQueryParams()['xref'] ?? '';
- $username = $request->getQueryParams()['username'] ?? '';
- $search = $request->getQueryParams()['search'] ?? [];
- $search = $search['value'] ?? null;
-
- if (!array_key_exists($ged, $tree_list)) {
- $ged = reset($tree_list);
- }
-
- $statuses = [
- '' => '',
- /* I18N: the status of an edit accepted/rejected/pending */
- 'accepted' => I18N::translate('accepted'),
- /* I18N: the status of an edit accepted/rejected/pending */
- 'rejected' => I18N::translate('rejected'),
- /* I18N: the status of an edit accepted/rejected/pending */
- 'pending' => I18N::translate('pending'),
- ];
-
- return $this->viewResponse('admin/changes-log', [
- 'action' => $action,
- 'earliest' => $earliest,
- 'from' => $from,
- 'ged' => $ged,
- 'latest' => $latest,
- 'newged' => $newged,
- 'oldged' => $oldged,
- 'search' => $search,
- 'statuses' => $statuses,
- 'title' => I18N::translate('Changes log'),
- 'to' => $to,
- 'tree_list' => $tree_list,
- 'type' => $type,
- 'username' => $username,
- 'user_list' => $user_list,
- 'xref' => $xref,
- ]);
- }
-
- /**
- * Show the edit history for a tree.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function changesLogData(ServerRequestInterface $request): ResponseInterface
- {
- $query = $this->changesQuery($request);
-
- $callback = function (stdClass $row): array {
- $old_lines = explode("\n", $row->old_gedcom);
- $new_lines = explode("\n", $row->new_gedcom);
-
- $differences = $this->myers_diff->calculate($old_lines, $new_lines);
- $diff_lines = [];
-
- foreach ($differences as $difference) {
- switch ($difference[1]) {
- case MyersDiff::DELETE:
- $diff_lines[] = '<del>' . $difference[0] . '</del>';
- break;
- case MyersDiff::INSERT:
- $diff_lines[] = '<ins>' . $difference[0] . '</ins>';
- break;
- default:
- $diff_lines[] = $difference[0];
- }
- }
-
- // Only convert valid xrefs to links
- $tree = $this->tree_service->all()->get($row->gedcom_name);
- $record = GedcomRecord::getInstance($row->xref, $tree);
-
- return [
- $row->change_id,
- Carbon::make($row->change_time)->local()->format('Y-m-d H:i:s'),
- I18N::translate($row->status),
- $record ? '<a href="' . e($record->url()) . '">' . $record->xref() . '</a>' : $row->xref,
- '<div class="gedcom-data" dir="ltr">' .
- preg_replace_callback(
- '/@(' . Gedcom::REGEX_XREF . ')@/',
- static function (array $match) use ($tree): string {
- $record = GedcomRecord::getInstance($match[1], $tree);
-
- return $record ? '<a href="' . e($record->url()) . '">' . $match[0] . '</a>' : $match[0];
- },
- implode("\n", $diff_lines)
- ) .
- '</div>',
- $row->user_name,
- $row->gedcom_name,
- ];
- };
-
- return $this->datatables_service->handle($request, $query, [], [], $callback);
- }
-
- /**
- * Show the edit history for a tree.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function changesLogDownload(ServerRequestInterface $request): ResponseInterface
- {
- $content = $this->changesQuery($request)
- ->get()
- ->map(static function (stdClass $row): string {
- // Convert to CSV
- return implode(',', [
- '"' . $row->change_time . '"',
- '"' . $row->status . '"',
- '"' . $row->xref . '"',
- '"' . str_replace('"', '""', $row->old_gedcom) . '"',
- '"' . str_replace('"', '""', $row->new_gedcom) . '"',
- '"' . str_replace('"', '""', $row->user_name) . '"',
- '"' . str_replace('"', '""', $row->gedcom_name) . '"',
- ]);
- })
- ->implode("\n");
-
- return response($content, StatusCodeInterface::STATUS_OK, [
- 'Content-Type' => 'text/csv; charset=utf-8',
- 'Content-Length' => strlen($content),
- 'Content-Disposition' => 'attachment; filename="changes.csv"',
- ]);
- }
-
- /**
- * Generate a query for filtering the changes log.
- *
- * @param ServerRequestInterface $request
- *
- * @return Builder
- */
- private function changesQuery(ServerRequestInterface $request): Builder
- {
- $from = $request->getQueryParams()['from'] ?? '';
- $to = $request->getQueryParams()['to'] ?? '';
- $type = $request->getQueryParams()['type'] ?? '';
- $oldged = $request->getQueryParams()['oldged'] ?? '';
- $newged = $request->getQueryParams()['newged'] ?? '';
- $xref = $request->getQueryParams()['xref'] ?? '';
- $username = $request->getQueryParams()['username'] ?? '';
- $ged = $request->getQueryParams()['ged'] ?? '';
- $search = $request->getQueryParams()['search'] ?? [];
- $search = $search['value'] ?? '';
-
-
- $query = DB::table('change')
- ->leftJoin('user', 'user.user_id', '=', 'change.user_id')
- ->join('gedcom', 'gedcom.gedcom_id', '=', 'change.gedcom_id')
- ->select(['change.*', new Expression("COALESCE(user_name, '<none>') AS user_name"), 'gedcom_name']);
-
- if ($search !== '') {
- $query->where(static function (Builder $query) use ($search): void {
- $query
- ->whereContains('old_gedcom', $search)
- ->whereContains('new_gedcom', $search, 'or');
- });
- }
-
- if ($from !== '') {
- $query->where('change_time', '>=', $from);
- }
-
- if ($to !== '') {
- // before end of the day
- $query->where('change_time', '<', Carbon::make($to)->addDay());
- }
-
- if ($type !== '') {
- $query->where('status', '=', $type);
- }
-
- if ($oldged !== '') {
- $query->whereContains('old_gedcom', $oldged);
- }
- if ($newged !== '') {
- $query->whereContains('new_gedcom', $oldged);
- }
-
- if ($xref !== '') {
- $query->where('xref', '=', $xref);
- }
-
- if ($username !== '') {
- $query->whereContains('user_name', $username);
- }
-
- if ($ged !== '') {
- $query->whereContains('gedcom_name', $ged);
- }
-
- return $query;
- }
-}
diff --git a/app/Http/Controllers/Admin/ImportThumbnailsController.php b/app/Http/Controllers/Admin/ImportThumbnailsController.php
index 012479f7ab..07340a71c8 100644
--- a/app/Http/Controllers/Admin/ImportThumbnailsController.php
+++ b/app/Http/Controllers/Admin/ImportThumbnailsController.php
@@ -23,6 +23,7 @@ use FilesystemIterator;
use Fisharebest\Webtrees\Functions\FunctionsImport;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Media;
+use Fisharebest\Webtrees\Services\PendingChangesService;
use Fisharebest\Webtrees\Services\TreeService;
use Fisharebest\Webtrees\Webtrees;
use Illuminate\Database\Capsule\Manager as DB;
@@ -43,14 +44,19 @@ class ImportThumbnailsController extends AbstractAdminController
/** @var TreeService */
private $tree_service;
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
/**
* ImportThumbnailsController constructor.
*
- * @param TreeService $tree_service
+ * @param PendingChangesService $pending_changes_service
+ * @param TreeService $tree_service
*/
- public function __construct(TreeService $tree_service)
+ public function __construct(PendingChangesService $pending_changes_service, TreeService $tree_service)
{
- $this->tree_service = $tree_service;
+ $this->pending_changes_service = $pending_changes_service;
+ $this->tree_service = $tree_service;
}
/**
@@ -120,7 +126,7 @@ class ImportThumbnailsController extends AbstractAdminController
}
// Accept the changes, to keep the filesystem in sync with the GEDCOM data.
- FunctionsImport::acceptAllChanges($media_object->xref(), $media_object->tree());
+ $this->pending_changes_service->acceptRecord($media_object);
}
break;
}
diff --git a/app/Http/Controllers/EditMediaController.php b/app/Http/Controllers/EditMediaController.php
index 84325d5d66..ef7d589ee7 100644
--- a/app/Http/Controllers/EditMediaController.php
+++ b/app/Http/Controllers/EditMediaController.php
@@ -23,12 +23,12 @@ use Exception;
use Fig\Http\Message\StatusCodeInterface;
use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\FlashMessages;
-use Fisharebest\Webtrees\Functions\FunctionsImport;
use Fisharebest\Webtrees\GedcomRecord;
use Fisharebest\Webtrees\GedcomTag;
use Fisharebest\Webtrees\Html;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Media;
+use Fisharebest\Webtrees\Services\PendingChangesService;
use Fisharebest\Webtrees\Tree;
use Illuminate\Database\Capsule\Manager as DB;
use InvalidArgumentException;
@@ -63,6 +63,19 @@ class EditMediaController extends AbstractEditController
'confidential',
];
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * EditMediaController constructor.
+ *
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
/**
* Add a media file to an existing media object.
*
@@ -135,7 +148,7 @@ class EditMediaController extends AbstractEditController
$media->createFact($gedcom, true);
// Accept the changes, to keep the filesystem in sync with the GEDCOM data.
- FunctionsImport::acceptAllChanges($media->xref(), $tree);
+ $this->pending_changes_service->acceptRecord($media);
return redirect($media->url());
}
@@ -266,7 +279,7 @@ class EditMediaController extends AbstractEditController
// Accept the changes, to keep the filesystem in sync with the GEDCOM data.
if ($old !== $new && !$media_file->isExternal()) {
- FunctionsImport::acceptAllChanges($media->xref(), $tree);
+ $this->pending_changes_service->acceptRecord($media);
}
return redirect($media->url());
@@ -327,7 +340,7 @@ class EditMediaController extends AbstractEditController
$media_object = $tree->createRecord($gedcom);
// Accept the new record. Rejecting it would leave the filesystem out-of-sync with the genealogy
- FunctionsImport::acceptAllChanges($media_object->xref(), $tree);
+ $this->pending_changes_service->acceptRecord($media_object);
return redirect($media_object->url());
}
@@ -383,7 +396,7 @@ class EditMediaController extends AbstractEditController
$record = $tree->createMediaObject($gedcom);
// Accept the new record to keep the filesystem synchronized with the genealogy.
- FunctionsImport::acceptAllChanges($record->xref(), $record->tree());
+ $this->pending_changes_service->acceptRecord($record);
return response([
'id' => $record->xref(),
diff --git a/app/Http/Controllers/PendingChangesController.php b/app/Http/Controllers/PendingChangesController.php
deleted file mode 100644
index 91fb1c59e8..0000000000
--- a/app/Http/Controllers/PendingChangesController.php
+++ /dev/null
@@ -1,326 +0,0 @@
-<?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\Controllers;
-
-use Fisharebest\Webtrees\Auth;
-use Fisharebest\Webtrees\Carbon;
-use Fisharebest\Webtrees\Family;
-use Fisharebest\Webtrees\FlashMessages;
-use Fisharebest\Webtrees\Functions\FunctionsImport;
-use Fisharebest\Webtrees\Gedcom;
-use Fisharebest\Webtrees\GedcomRecord;
-use Fisharebest\Webtrees\I18N;
-use Fisharebest\Webtrees\Individual;
-use Fisharebest\Webtrees\Log;
-use Fisharebest\Webtrees\Media;
-use Fisharebest\Webtrees\Note;
-use Fisharebest\Webtrees\Repository;
-use Fisharebest\Webtrees\Source;
-use Fisharebest\Webtrees\Tree;
-use Illuminate\Database\Capsule\Manager as DB;
-use InvalidArgumentException;
-use Psr\Http\Message\ResponseInterface;
-use Psr\Http\Message\ServerRequestInterface;
-
-use function assert;
-use function route;
-
-/**
- * Show, accept and reject pending changes.
- */
-class PendingChangesController extends AbstractBaseController
-{
- /**
- * Accept all changes to a tree.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function acceptAllChanges(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- assert($tree instanceof Tree, new InvalidArgumentException());
-
- $url = $request->getQueryParams()['url'];
-
- $changes = DB::table('change')
- ->where('gedcom_id', '=', $tree->id())
- ->where('status', '=', 'pending')
- ->orderBy('change_id')
- ->get();
-
- foreach ($changes as $change) {
- if ($change->new_gedcom === '') {
- // delete
- FunctionsImport::updateRecord($change->old_gedcom, $tree, true);
- } else {
- // add/update
- FunctionsImport::updateRecord($change->new_gedcom, $tree, false);
- }
-
- DB::table('change')
- ->where('change_id', '=', $change->change_id)
- ->update(['status' => 'accepted']);
-
- Log::addEditLog('Accepted change ' . $change->change_id . ' for ' . $change->xref . ' / ' . $tree->name(), $tree);
- }
-
- return redirect(route('show-pending', [
- 'tree' => $tree->name(),
- 'url' => $url,
- ]));
- }
-
- /**
- * Accept a change (and all previous changes) to a single record.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function acceptChange(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- $params = $request->getQueryParams();
- $url = $params['url'];
- $xref = $params['xref'];
- $change_id = $params['change_id'];
-
- $changes = DB::table('change')
- ->where('gedcom_id', '=', $tree->id())
- ->where('xref', '=', $xref)
- ->where('change_id', '<=', $change_id)
- ->where('status', '=', 'pending')
- ->orderBy('change_id')
- ->get();
-
- foreach ($changes as $change) {
- if ($change->new_gedcom === '') {
- // delete
- FunctionsImport::updateRecord($change->old_gedcom, $tree, true);
- } else {
- // add/update
- FunctionsImport::updateRecord($change->new_gedcom, $tree, false);
- }
-
- DB::table('change')
- ->where('change_id', '=', $change->change_id)
- ->update(['status' => 'accepted']);
-
- Log::addEditLog('Accepted change ' . $change->change_id . ' for ' . $change->xref . ' / ' . $tree->name(), $tree);
- }
-
- return redirect(route('show-pending', [
- 'tree' => $tree->name(),
- 'url' => $url,
- ]));
- }
-
- /**
- * Accept all changes to a single record.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function acceptChanges(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- $xref = $request->getAttribute('xref');
- $record = GedcomRecord::getInstance($xref, $tree);
-
- Auth::checkRecordAccess($record, false);
-
- if ($record && Auth::isModerator($tree)) {
- if ($record->isPendingDeletion()) {
- /* I18N: %s is the name of a genealogy record */
- FlashMessages::addMessage(I18N::translate('“%s” has been deleted.', $record->fullName()));
- } else {
- /* I18N: %s is the name of a genealogy record */
- FlashMessages::addMessage(I18N::translate('The changes to “%s” have been accepted.', $record->fullName()));
- }
- FunctionsImport::acceptAllChanges($record->xref(), $record->tree());
- }
-
- return response();
- }
-
- /**
- * Reject all changes to a tree.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function rejectAllChanges(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- assert($tree instanceof Tree, new InvalidArgumentException());
-
- $url = $request->getQueryParams()['url'];
-
- DB::table('change')
- ->where('gedcom_id', '=', $tree->id())
- ->where('status', '=', 'pending')
- ->update(['status' => 'rejected']);
-
- return redirect(route('show-pending', [
- 'tree' => $tree->name(),
- 'url' => $url,
- ]));
- }
-
- /**
- * Reject a change (and all subsequent changes) to a single record.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function rejectChange(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- $params = $request->getQueryParams();
- $url = $params['url'];
- $xref = $params['xref'];
- $change_id = $params['change_id'];
-
- // Reject a change, and subsequent changes to the same record
- DB::table('change')
- ->where('gedcom_id', '=', $tree->id())
- ->where('xref', '=', $xref)
- ->where('change_id', '>=', $change_id)
- ->where('status', '=', 'pending')
- ->update(['status' => 'rejected']);
-
- return redirect(route('show-pending', [
- 'tree' => $tree->name(),
- 'url' => $url,
- ]));
- }
-
- /**
- * Accept all changes to a single record.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function rejectChanges(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- $xref = $request->getAttribute('xref');
- $record = GedcomRecord::getInstance($xref, $tree);
-
- Auth::checkRecordAccess($record);
-
- if (Auth::isModerator($tree)) {
- DB::table('change')
- ->where('gedcom_id', '=', $record->tree()->id())
- ->where('xref', '=', $record->xref())
- ->where('status', '=', 'pending')
- ->update(['status' => 'rejected']);
-
- /* I18N: %s is the name of an individual, source or other record */
- FlashMessages::addMessage(I18N::translate('The changes to “%s” have been rejected.', $record->fullName()));
- }
-
- return response();
- }
-
- /**
- * Show the pending changes for the current tree.
- *
- * @param ServerRequestInterface $request
- *
- * @return ResponseInterface
- */
- public function showChanges(ServerRequestInterface $request): ResponseInterface
- {
- $tree = $request->getAttribute('tree');
- $default_url = route('tree-page', ['tree' => $tree->name()]);
-
- $url = $request->getQueryParams()['url'] ?? $default_url;
-
- $rows = DB::table('change')
- ->join('user', 'user.user_id', '=', 'change.user_id')
- ->join('gedcom', 'gedcom.gedcom_id', '=', 'change.gedcom_id')
- ->where('status', '=', 'pending')
- ->orderBy('change.gedcom_id')
- ->orderBy('change.xref')
- ->orderBy('change.change_id')
- ->select(['change.*', 'user.user_name', 'user.real_name', 'gedcom_name'])
- ->get();
-
- $changes = [];
- foreach ($rows as $row) {
- $row->change_time = Carbon::make($row->change_time);
-
- $change_tree = Tree::findById((int) $row->gedcom_id);
-
- preg_match('/^0 (?:@' . Gedcom::REGEX_XREF . '@ )?(' . Gedcom::REGEX_TAG . ')/', $row->old_gedcom . $row->new_gedcom, $match);
-
- switch ($match[1]) {
- case 'INDI':
- $row->record = new Individual($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
- break;
- case 'FAM':
- $row->record = new Family($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
- break;
- case 'SOUR':
- $row->record = new Source($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
- break;
- case 'REPO':
- $row->record = new Repository($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
- break;
- case 'OBJE':
- $row->record = new Media($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
- break;
- case 'NOTE':
- $row->record = new Note($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
- break;
- default:
- $row->record = new GedcomRecord($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
- break;
- }
-
- $changes[$row->gedcom_id][$row->xref][] = $row;
- }
-
- $title = I18N::translate('Pending changes');
-
- // If the current tree has changes, activate that tab. Otherwise activate the first tab.
- if (($changes[$tree->id()] ?? []) === []) {
- reset($changes);
- $active_tree_id = key($changes);
- } else {
- $active_tree_id = $tree->id();
- }
-
- return $this->viewResponse('pending-changes-page', [
- 'active_tree_id' => $active_tree_id,
- 'changes' => $changes,
- 'title' => $title,
- 'tree' => $tree,
- 'url' => $url,
- ]);
- }
-}
diff --git a/app/Http/RequestHandlers/PendingChanges.php b/app/Http/RequestHandlers/PendingChanges.php
new file mode 100644
index 0000000000..94bfec4349
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChanges.php
@@ -0,0 +1,146 @@
+<?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\Carbon;
+use Fisharebest\Webtrees\Family;
+use Fisharebest\Webtrees\Gedcom;
+use Fisharebest\Webtrees\GedcomRecord;
+use Fisharebest\Webtrees\Http\ViewResponseTrait;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Individual;
+use Fisharebest\Webtrees\Media;
+use Fisharebest\Webtrees\Note;
+use Fisharebest\Webtrees\Repository;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Fisharebest\Webtrees\Services\TreeService;
+use Fisharebest\Webtrees\Source;
+use Fisharebest\Webtrees\Tree;
+use Illuminate\Database\Capsule\Manager as DB;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function key;
+use function preg_match;
+use function reset;
+use function route;
+
+/**
+ * Show all pending changes.
+ */
+class PendingChanges implements RequestHandlerInterface
+{
+ use ViewResponseTrait;
+
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /** @var TreeService */
+ private $tree_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ * @param TreeService $tree_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service, TreeService $tree_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ $this->tree_service = $tree_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree, new InvalidArgumentException());
+
+ $url = $request->getQueryParams()['url'] ?? route('tree-page', ['tree' => $tree->name()]);
+
+ $rows = DB::table('change')
+ ->join('user', 'user.user_id', '=', 'change.user_id')
+ ->join('gedcom', 'gedcom.gedcom_id', '=', 'change.gedcom_id')
+ ->where('status', '=', 'pending')
+ ->orderBy('change.gedcom_id')
+ ->orderBy('change.xref')
+ ->orderBy('change.change_id')
+ ->select(['change.*', 'user.user_name', 'user.real_name', 'gedcom_name'])
+ ->get();
+
+ $changes = [];
+ foreach ($rows as $row) {
+ $row->change_time = Carbon::make($row->change_time);
+
+ $change_tree = $this->tree_service->all()->get($row->gedcom_name);
+
+ preg_match('/^0 (?:@' . Gedcom::REGEX_XREF . '@ )?(' . Gedcom::REGEX_TAG . ')/', $row->old_gedcom . $row->new_gedcom, $match);
+
+ switch ($match[1]) {
+ case 'INDI':
+ $row->record = new Individual($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
+ break;
+ case 'FAM':
+ $row->record = new Family($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
+ break;
+ case 'SOUR':
+ $row->record = new Source($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
+ break;
+ case 'REPO':
+ $row->record = new Repository($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
+ break;
+ case 'OBJE':
+ $row->record = new Media($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
+ break;
+ case 'NOTE':
+ $row->record = new Note($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
+ break;
+ default:
+ $row->record = new GedcomRecord($row->xref, $row->old_gedcom, $row->new_gedcom, $change_tree);
+ break;
+ }
+
+ $changes[$row->gedcom_id][$row->xref][] = $row;
+ }
+
+ $title = I18N::translate('Pending changes');
+
+ // If the current tree has changes, activate that tab. Otherwise activate the first tab.
+ if (($changes[$tree->id()] ?? []) === []) {
+ reset($changes);
+ $active_tree_id = key($changes);
+ } else {
+ $active_tree_id = $tree->id();
+ }
+
+ return $this->viewResponse('pending-changes-page', [
+ 'active_tree_id' => $active_tree_id,
+ 'changes' => $changes,
+ 'title' => $title,
+ 'tree' => $tree,
+ 'url' => $url,
+ ]);
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesAcceptChange.php b/app/Http/RequestHandlers/PendingChangesAcceptChange.php
new file mode 100644
index 0000000000..4130482850
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesAcceptChange.php
@@ -0,0 +1,69 @@
+<?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\GedcomRecord;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Fisharebest\Webtrees\Tree;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function response;
+
+/**
+ * Accept pending changes for a record.
+ */
+class PendingChangesAcceptChange implements RequestHandlerInterface
+{
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree, new InvalidArgumentException());
+
+ $xref = $request->getAttribute('xref');
+ $record = GedcomRecord::getInstance($xref, $tree);
+ $change = $request->getAttribute('change');
+
+ if ($record instanceof GedcomRecord) {
+ $this->pending_changes_service->acceptChange($record, $change);
+ }
+
+ return response();
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesAcceptRecord.php b/app/Http/RequestHandlers/PendingChangesAcceptRecord.php
new file mode 100644
index 0000000000..645e08cecb
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesAcceptRecord.php
@@ -0,0 +1,78 @@
+<?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\FlashMessages;
+use Fisharebest\Webtrees\GedcomRecord;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Fisharebest\Webtrees\Tree;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function response;
+
+/**
+ * Accept pending changes for a record.
+ */
+class PendingChangesAcceptRecord implements RequestHandlerInterface
+{
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree, new InvalidArgumentException());
+
+ $xref = $request->getAttribute('xref') ?? '';
+ $record = GedcomRecord::getInstance($xref, $tree);
+
+ if ($record) {
+ if ($record->isPendingDeletion()) {
+ /* I18N: %s is the name of a genealogy record */
+ FlashMessages::addMessage(I18N::translate('“%s” has been deleted.', $record->fullName()));
+ } else {
+ /* I18N: %s is the name of a genealogy record */
+ FlashMessages::addMessage(I18N::translate('The changes to “%s” have been accepted.', $record->fullName()));
+ }
+
+ $this->pending_changes_service->acceptRecord($record);
+ }
+
+ return response();
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesAcceptTree.php b/app/Http/RequestHandlers/PendingChangesAcceptTree.php
new file mode 100644
index 0000000000..18d505941e
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesAcceptTree.php
@@ -0,0 +1,66 @@
+<?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\FlashMessages;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Fisharebest\Webtrees\Tree;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function e;
+use function response;
+
+/**
+ * Accept pending changes for a tree.
+ */
+class PendingChangesAcceptTree implements RequestHandlerInterface
+{
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree, new InvalidArgumentException());
+
+ $this->pending_changes_service->acceptTree($tree);
+
+ FlashMessages::addMessage(I18N::translate('The changes to “%s” have been accepted.', e($tree->title())));
+ return response();
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesLogAction.php b/app/Http/RequestHandlers/PendingChangesLogAction.php
new file mode 100644
index 0000000000..c2ddd37389
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesLogAction.php
@@ -0,0 +1,66 @@
+<?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\PendingChangesService;
+use Fisharebest\Webtrees\Tree;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function response;
+
+/**
+ * Show pending changes.
+ */
+class PendingChangesLogAction implements RequestHandlerInterface
+{
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ return redirect(route(PendingChangesLogPage::class, [
+ 'tree' => $request->getParsedBody()['tree'],
+ 'from' => $request->getParsedBody()['from'] ?? '',
+ 'to' => $request->getParsedBody()['to'] ?? '',
+ 'type' => $request->getParsedBody()['type'] ?? '',
+ 'oldged' => $request->getParsedBody()['oldged'] ?? '',
+ 'newged' => $request->getParsedBody()['newged'] ?? '',
+ 'xref' => $request->getParsedBody()['xref'] ?? '',
+ 'username' => $request->getParsedBody()['username'] ?? '',
+ ]));
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesLogData.php b/app/Http/RequestHandlers/PendingChangesLogData.php
new file mode 100644
index 0000000000..a4af95c3b3
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesLogData.php
@@ -0,0 +1,135 @@
+<?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\Algorithm\MyersDiff;
+use Fisharebest\Webtrees\Carbon;
+use Fisharebest\Webtrees\Gedcom;
+use Fisharebest\Webtrees\GedcomRecord;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\DatatablesService;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Fisharebest\Webtrees\Services\TreeService;
+use Fisharebest\Webtrees\Tree;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+use stdClass;
+
+use function e;
+use function explode;
+use function implode;
+use function preg_replace_callback;
+
+/**
+ * Find pending changes.
+ */
+class PendingChangesLogData implements RequestHandlerInterface
+{
+ /** @var DatatablesService */
+ private $datatables_service;
+
+ /** @var MyersDiff */
+ private $myers_diff;
+
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /** @var TreeService */
+ private $tree_service;
+
+ /**
+ * @param DatatablesService $datatables_service
+ * @param MyersDiff $myers_diff
+ * @param PendingChangesService $pending_changes_service
+ * @param TreeService $tree_service
+ */
+ public function __construct(
+ DatatablesService $datatables_service,
+ MyersDiff $myers_diff,
+ PendingChangesService $pending_changes_service,
+ TreeService $tree_service
+ ) {
+ $this->datatables_service = $datatables_service;
+ $this->myers_diff = $myers_diff;
+ $this->pending_changes_service = $pending_changes_service;
+ $this->tree_service = $tree_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree, new InvalidArgumentException());
+
+ $query = $this->pending_changes_service->changesQuery($request);
+
+ $callback = function (stdClass $row) use ($tree): array {
+ $old_lines = explode("\n", $row->old_gedcom);
+ $new_lines = explode("\n", $row->new_gedcom);
+
+ $differences = $this->myers_diff->calculate($old_lines, $new_lines);
+ $diff_lines = [];
+
+ foreach ($differences as $difference) {
+ switch ($difference[1]) {
+ case MyersDiff::DELETE:
+ $diff_lines[] = '<del>' . $difference[0] . '</del>';
+ break;
+ case MyersDiff::INSERT:
+ $diff_lines[] = '<ins>' . $difference[0] . '</ins>';
+ break;
+ default:
+ $diff_lines[] = $difference[0];
+ }
+ }
+
+ // Only convert valid xrefs to links
+ $record = GedcomRecord::getInstance($row->xref, $tree);
+
+ return [
+ $row->change_id,
+ Carbon::make($row->change_time)->local()->format('Y-m-d H:i:s'),
+ I18N::translate($row->status),
+ $record ? '<a href="' . e($record->url()) . '">' . $record->xref() . '</a>' : $row->xref,
+ '<div class="gedcom-data" dir="ltr">' .
+ preg_replace_callback(
+ '/@(' . Gedcom::REGEX_XREF . ')@/',
+ static function (array $match) use ($tree): string {
+ $record = GedcomRecord::getInstance($match[1], $tree);
+
+ return $record ? '<a href="' . e($record->url()) . '">' . $match[0] . '</a>' : $match[0];
+ },
+ implode("\n", $diff_lines)
+ ) .
+ '</div>',
+ $row->user_name,
+ $row->gedcom_name,
+ ];
+ };
+
+ return $this->datatables_service->handle($request, $query, [], [], $callback);
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesLogDelete.php b/app/Http/RequestHandlers/PendingChangesLogDelete.php
new file mode 100644
index 0000000000..d8c72fe91c
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesLogDelete.php
@@ -0,0 +1,58 @@
+<?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\PendingChangesService;
+use Fisharebest\Webtrees\Tree;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function response;
+
+/**
+ * Delete pending changes.
+ */
+class PendingChangesLogDelete implements RequestHandlerInterface
+{
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $this->pending_changes_service->changesQuery($request)->delete();
+
+ return response();
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesLogDownload.php b/app/Http/RequestHandlers/PendingChangesLogDownload.php
new file mode 100644
index 0000000000..de5a3afb6b
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesLogDownload.php
@@ -0,0 +1,75 @@
+<?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 Fig\Http\Message\StatusCodeInterface;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+use stdClass;
+
+use function response;
+
+/**
+ * Download pending changes.
+ */
+class PendingChangesLogDownload implements RequestHandlerInterface
+{
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $content = $this->pending_changes_service->changesQuery($request)
+ ->get()
+ ->map(static function (stdClass $row): string {
+ // Convert to CSV
+ return implode(',', [
+ '"' . $row->change_time . '"',
+ '"' . $row->status . '"',
+ '"' . $row->xref . '"',
+ '"' . str_replace('"', '""', $row->old_gedcom) . '"',
+ '"' . str_replace('"', '""', $row->new_gedcom) . '"',
+ '"' . str_replace('"', '""', $row->user_name) . '"',
+ '"' . str_replace('"', '""', $row->gedcom_name) . '"',
+ ]);
+ })
+ ->implode("\n");
+
+ return response($content, StatusCodeInterface::STATUS_OK, [
+ 'Content-Type' => 'text/csv; charset=utf-8',
+ 'Content-Disposition' => 'attachment; filename="changes.csv"',
+ ]);
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesLogPage.php b/app/Http/RequestHandlers/PendingChangesLogPage.php
new file mode 100644
index 0000000000..76c887f289
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesLogPage.php
@@ -0,0 +1,133 @@
+<?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\Carbon;
+use Fisharebest\Webtrees\Http\ViewResponseTrait;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\TreeService;
+use Fisharebest\Webtrees\Services\UserService;
+use Fisharebest\Webtrees\Tree;
+use Illuminate\Database\Capsule\Manager as DB;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function array_key_exists;
+use function reset;
+
+/**
+ * Show pending changes.
+ */
+class PendingChangesLogPage implements RequestHandlerInterface
+{
+ use ViewResponseTrait;
+
+ /** @var TreeService */
+ private $tree_service;
+
+ /** @var UserService */
+ private $user_service;
+
+ /**
+ * @param TreeService $tree_service
+ * @param UserService $user_service
+ */
+ public function __construct(TreeService $tree_service, UserService $user_service)
+ {
+ $this->tree_service = $tree_service;
+ $this->user_service = $user_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $this->layout = 'layouts/administration';
+
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree, new InvalidArgumentException());
+
+ $trees = $this->tree_service->titles();
+
+ $users = ['' => ''];
+ foreach ($this->user_service->all() as $user) {
+ $user_name = $user->userName();
+ $users[$user_name] = $user_name;
+ }
+
+ // First and last change in the database.
+ $earliest = DB::table('change')->min('change_time');
+ $latest = DB::table('change')->max('change_time');
+
+ $earliest = $earliest !== null ? Carbon::make($earliest) : Carbon::now();
+ $latest = $latest !== null ? Carbon::make($latest) : Carbon::now();
+
+ $earliest = $earliest->toDateString();
+ $latest = $latest->toDateString();
+
+ $from = $request->getQueryParams()['from'] ?? $earliest;
+ $to = $request->getQueryParams()['to'] ?? $latest;
+ $type = $request->getQueryParams()['type'] ?? '';
+ $oldged = $request->getQueryParams()['oldged'] ?? '';
+ $newged = $request->getQueryParams()['newged'] ?? '';
+ $xref = $request->getQueryParams()['xref'] ?? '';
+ $username = $request->getQueryParams()['username'] ?? '';
+
+ return $this->viewResponse('admin/changes-log', [
+ 'earliest' => $earliest,
+ 'from' => $from,
+ 'latest' => $latest,
+ 'newged' => $newged,
+ 'oldged' => $oldged,
+ 'statuses' => $this->changeStatuses(),
+ 'title' => I18N::translate('Changes log'),
+ 'to' => $to,
+ 'tree' => $tree,
+ 'trees' => $trees,
+ 'type' => $type,
+ 'username' => $username,
+ 'users' => $users,
+ 'xref' => $xref,
+ ]);
+ }
+
+ /**
+ * Labels for the various statuses.
+ *
+ * @return array
+ */
+ private function changeStatuses(): array
+ {
+ return [
+ '' => '',
+ /* I18N: the status of an edit accepted/rejected/pending */
+ 'accepted' => I18N::translate('accepted'),
+ /* I18N: the status of an edit accepted/rejected/pending */
+ 'rejected' => I18N::translate('rejected'),
+ /* I18N: the status of an edit accepted/rejected/pending */
+ 'pending' => I18N::translate('pending'),
+ ];
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesRejectChange.php b/app/Http/RequestHandlers/PendingChangesRejectChange.php
new file mode 100644
index 0000000000..07a146016c
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesRejectChange.php
@@ -0,0 +1,69 @@
+<?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\GedcomRecord;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Fisharebest\Webtrees\Tree;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function response;
+
+/**
+ * Reject a pending change for a record.
+ */
+class PendingChangesRejectChange implements RequestHandlerInterface
+{
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree, new InvalidArgumentException());
+
+ $xref = $request->getAttribute('xref');
+ $record = GedcomRecord::getInstance($xref, $tree);
+ $change = $request->getAttribute('change');
+
+ if ($record instanceof GedcomRecord) {
+ $this->pending_changes_service->rejectChange($record, $change);
+ }
+
+ return response();
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesRejectRecord.php b/app/Http/RequestHandlers/PendingChangesRejectRecord.php
new file mode 100644
index 0000000000..0f4f6e1ac4
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesRejectRecord.php
@@ -0,0 +1,73 @@
+<?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\FlashMessages;
+use Fisharebest\Webtrees\GedcomRecord;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Fisharebest\Webtrees\Tree;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function response;
+
+/**
+ * Reject pending changes for a record.
+ */
+class PendingChangesRejectRecord implements RequestHandlerInterface
+{
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree, new InvalidArgumentException());
+
+ $xref = $request->getAttribute('xref') ?? '';
+ $record = GedcomRecord::getInstance($xref, $tree);
+
+ if ($record instanceof GedcomRecord) {
+ $this->pending_changes_service->rejectRecord($record);
+
+ /* I18N: %s is the name of a genealogy record */
+ FlashMessages::addMessage(I18N::translate('The changes to “%s” have been rejected.', $record->fullName()));
+ }
+
+ return response();
+ }
+}
diff --git a/app/Http/RequestHandlers/PendingChangesRejectTree.php b/app/Http/RequestHandlers/PendingChangesRejectTree.php
new file mode 100644
index 0000000000..4089661a0b
--- /dev/null
+++ b/app/Http/RequestHandlers/PendingChangesRejectTree.php
@@ -0,0 +1,67 @@
+<?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\FlashMessages;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\PendingChangesService;
+use Fisharebest\Webtrees\Tree;
+use InvalidArgumentException;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\RequestHandlerInterface;
+
+use function assert;
+use function e;
+use function response;
+
+/**
+ * Reject pending changes for a tree.
+ */
+class PendingChangesRejectTree implements RequestHandlerInterface
+{
+ /** @var PendingChangesService */
+ private $pending_changes_service;
+
+ /**
+ * @param PendingChangesService $pending_changes_service
+ */
+ public function __construct(PendingChangesService $pending_changes_service)
+ {
+ $this->pending_changes_service = $pending_changes_service;
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ *
+ * @return ResponseInterface
+ */
+ public function handle(ServerRequestInterface $request): ResponseInterface
+ {
+ $tree = $request->getAttribute('tree');
+ assert($tree instanceof Tree, new InvalidArgumentException());
+
+ $this->pending_changes_service->rejectTree($tree);
+
+ FlashMessages::addMessage(I18N::translate('The changes to “%s” have been rejected.', e($tree->title())));
+
+ return response();
+ }
+}