diff options
| -rw-r--r-- | app/Http/RequestHandlers/MapDataDelete.php | 2 | ||||
| -rw-r--r-- | app/Http/RequestHandlers/MapDataDeleteUnused.php | 61 | ||||
| -rw-r--r-- | app/Http/RequestHandlers/MapDataImportAction.php | 11 | ||||
| -rw-r--r-- | app/Http/Routes/WebRoutes.php | 2 | ||||
| -rw-r--r-- | app/Services/MapDataService.php | 107 | ||||
| -rw-r--r-- | phpstan-baseline.neon | 22 | ||||
| -rw-r--r-- | resources/views/admin/locations.phtml | 14 | ||||
| -rw-r--r-- | resources/views/admin/map-import-form.phtml | 25 |
8 files changed, 170 insertions, 74 deletions
diff --git a/app/Http/RequestHandlers/MapDataDelete.php b/app/Http/RequestHandlers/MapDataDelete.php index ba7c7b3949..ddeb4c67b6 100644 --- a/app/Http/RequestHandlers/MapDataDelete.php +++ b/app/Http/RequestHandlers/MapDataDelete.php @@ -28,7 +28,7 @@ use function redirect; use function route; /** - * Delete a place location from the control panel. + * Delete a location from the control panel. */ class MapDataDelete implements RequestHandlerInterface { diff --git a/app/Http/RequestHandlers/MapDataDeleteUnused.php b/app/Http/RequestHandlers/MapDataDeleteUnused.php new file mode 100644 index 0000000000..1b57b012e6 --- /dev/null +++ b/app/Http/RequestHandlers/MapDataDeleteUnused.php @@ -0,0 +1,61 @@ +<?php + +/** + * webtrees: online genealogy + * Copyright (C) 2021 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 <https://www.gnu.org/licenses/>. + */ + +declare(strict_types=1); + +namespace Fisharebest\Webtrees\Http\RequestHandlers; + +use Fisharebest\Webtrees\PlaceLocation; +use Fisharebest\Webtrees\Services\MapDataService; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Server\RequestHandlerInterface; + +use function redirect; +use function route; + +/** + * Delete unused locations from the control panel. + */ +class MapDataDeleteUnused implements RequestHandlerInterface +{ + private MapDataService $map_data_service; + + /** + * Dependency injection. + * + * @param MapDataService $map_data_service + */ + public function __construct(MapDataService $map_data_service) + { + $this->map_data_service = $map_data_service; + } + + /** + * @param ServerRequestInterface $request + * + * @return ResponseInterface + */ + public function handle(ServerRequestInterface $request): ResponseInterface + { + $this->map_data_service->deleteUnusedLocations(null, [0]); + + $url = route(MapDataList::class); + + return redirect($url); + } +} diff --git a/app/Http/RequestHandlers/MapDataImportAction.php b/app/Http/RequestHandlers/MapDataImportAction.php index 52cd2ebbc5..51b358ea9b 100644 --- a/app/Http/RequestHandlers/MapDataImportAction.php +++ b/app/Http/RequestHandlers/MapDataImportAction.php @@ -88,7 +88,6 @@ class MapDataImportAction implements RequestHandlerInterface $serverfile = $params['serverfile'] ?? ''; $options = $params['import-options'] ?? ''; - $clear_database = (bool) ($params['cleardatabase'] ?? false); $local_file = $request->getUploadedFiles()['localfile'] ?? null; $places = []; @@ -155,16 +154,6 @@ class MapDataImportAction implements RequestHandlerInterface fclose($fp); - if ($clear_database) { - // Child places are deleted via on-delete-cascade... - DB::table('place_location') - ->whereNull('parent_id') - ->delete(); - - // Automatically import any new/missing places. - $this->map_data_service->importMissingLocations(); - } - $added = 0; $updated = 0; diff --git a/app/Http/Routes/WebRoutes.php b/app/Http/Routes/WebRoutes.php index 0d168907a8..1ce7fb721b 100644 --- a/app/Http/Routes/WebRoutes.php +++ b/app/Http/Routes/WebRoutes.php @@ -147,6 +147,7 @@ use Fisharebest\Webtrees\Http\RequestHandlers\ManageMediaPage; use Fisharebest\Webtrees\Http\RequestHandlers\ManageTrees; use Fisharebest\Webtrees\Http\RequestHandlers\MapDataAdd; use Fisharebest\Webtrees\Http\RequestHandlers\MapDataDelete; +use Fisharebest\Webtrees\Http\RequestHandlers\MapDataDeleteUnused; use Fisharebest\Webtrees\Http\RequestHandlers\MapDataEdit; use Fisharebest\Webtrees\Http\RequestHandlers\MapDataExportCSV; use Fisharebest\Webtrees\Http\RequestHandlers\MapDataExportGeoJson; @@ -380,6 +381,7 @@ class WebRoutes $router->post(UsersCleanupAction::class, '/users-cleanup'); $router->get(MapDataAdd::class, '/map-data-add{/parent_id}'); $router->post(MapDataDelete::class, '/map-data-delete/{place_id}'); + $router->post(MapDataDeleteUnused::class, '/map-data-delete-unused'); $router->get(MapDataEdit::class, '/map-data-edit/{place_id}'); $router->get(MapDataExportCSV::class, '/map-data-csv{/parent_id}'); $router->get(MapDataExportGeoJson::class, '/map-data-geojson{/parent_id}'); diff --git a/app/Services/MapDataService.php b/app/Services/MapDataService.php index 174d1efd0a..747a0d6a6a 100644 --- a/app/Services/MapDataService.php +++ b/app/Services/MapDataService.php @@ -19,13 +19,13 @@ declare(strict_types=1); namespace Fisharebest\Webtrees\Services; +use Fisharebest\Webtrees\FlashMessages; use Fisharebest\Webtrees\Gedcom; use Fisharebest\Webtrees\I18N; use Fisharebest\Webtrees\PlaceLocation; use Illuminate\Database\Capsule\Manager as DB; use Illuminate\Database\Query\Expression; use Illuminate\Support\Collection; -use stdClass; use function abs; use function array_filter; @@ -74,7 +74,7 @@ class MapDataService * * @param PlaceLocation $location * - * @return array<string,array<stdClass>> + * @return array<string,array<object>> */ public function activePlaces(PlaceLocation $location): array { @@ -124,7 +124,7 @@ class MapDataService 'p8.p_place AS part_8', ]) ->get() - ->map(static function (stdClass $row): string { + ->map(static function (object $row): string { return implode(Gedcom::PLACE_SEPARATOR, array_filter((array) $row)); }); @@ -149,7 +149,7 @@ class MapDataService 'p8.place AS part_8', ]) ->get() - ->map(static function (stdClass $row): string { + ->map(static function (object $row): string { return implode(Gedcom::PLACE_SEPARATOR, array_filter((array) $row)); }); @@ -161,37 +161,9 @@ class MapDataService } /** - * Find all active places that match a location - * - * @param PlaceLocation $location - * - * @return array<string> - */ - private function placeIdsForLocation(PlaceLocation $location): array - { - $hierarchy = []; - - while ($location->id() !== null) { - array_unshift($hierarchy, $location->locationName()); - $location = $location->parent(); - } - - $place_ids = ['0']; - - foreach ($hierarchy as $place_name) { - $place_ids = DB::table('places') - ->whereIn('p_parent_id', $place_ids) - ->where('p_place', '=', $place_name) - ->groupBy(['p_id']) - ->pluck('p_id') - ->all(); - } - - return $place_ids; - } - - /** * @param int $id + * + * @return void */ public function deleteRecursively(int $id): void { @@ -202,6 +174,41 @@ class MapDataService } /** + * @param int|null $parent_location_id + * @param array<int> $parent_place_ids + * + * @return void + */ + public function deleteUnusedLocations(?int $parent_location_id, array $parent_place_ids): void + { + if ($parent_location_id === null) { + $location_query = DB::table('place_location') + ->whereNull('parent_id'); + } else { + $location_query = DB::table('place_location') + ->where('parent_id', '=', $parent_location_id); + } + + foreach ($location_query->get() as $location) { + $places = DB::table('places') + ->whereIn('p_parent_id', $parent_place_ids) + ->where('p_place', '=', $location->place) + ->get(); + + if ($places->isEmpty()) { + FlashMessages::addMessage(I18N::translate('ā%sā has been deleted.', e($location->place))); + + DB::table('place_location') + ->where('id', '=', $location->id) + ->delete(); + } else { + $place_ids = $places->map(static fn(object $place): int => (int) $place->p_id)->all(); + $this->deleteUnusedLocations((int) $location->id, $place_ids); + } + } + } + + /** * Find a list of child places. * How many children does each child place have? How many have co-ordinates? * @@ -252,7 +259,7 @@ class MapDataService new Expression('SUM(' . $expression . ') AS no_coord'), ]) ->get() - ->map(static function (stdClass $row): stdClass { + ->map(static function (object $row): object { $row->child_count = (int) $row->child_count; $row->no_coord = (int) $row->no_coord; $row->key = mb_strtolower($row->place); @@ -282,6 +289,36 @@ class MapDataService } /** + * Find all active places that match a location + * + * @param PlaceLocation $location + * + * @return array<string> + */ + private function placeIdsForLocation(PlaceLocation $location): array + { + $hierarchy = []; + + while ($location->id() !== null) { + array_unshift($hierarchy, $location->locationName()); + $location = $location->parent(); + } + + $place_ids = ['0']; + + foreach ($hierarchy as $place_name) { + $place_ids = DB::table('places') + ->whereIn('p_parent_id', $place_ids) + ->where('p_place', '=', $place_name) + ->groupBy(['p_id']) + ->pluck('p_id') + ->all(); + } + + return $place_ids; + } + + /** * @param float $degrees * @param string $positive * @param string $negative diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3fa20b6f85..bbcacb2878 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1430,13 +1430,33 @@ parameters: path: app/Services/HomePageService.php - + message: "#^Access to an undefined property object\\:\\:\\$child_count\\.$#" + count: 2 + path: app/Services/MapDataService.php + + - + message: "#^Access to an undefined property object\\:\\:\\$key\\.$#" + count: 1 + path: app/Services/MapDataService.php + + - + message: "#^Access to an undefined property object\\:\\:\\$no_coord\\.$#" + count: 2 + path: app/Services/MapDataService.php + + - + message: "#^Access to an undefined property object\\:\\:\\$p_id\\.$#" + count: 1 + path: app/Services/MapDataService.php + + - message: "#^Access to an undefined property object\\:\\:\\$parent_id\\.$#" count: 1 path: app/Services/MapDataService.php - message: "#^Access to an undefined property object\\:\\:\\$place\\.$#" - count: 1 + count: 2 path: app/Services/MapDataService.php - diff --git a/resources/views/admin/locations.phtml b/resources/views/admin/locations.phtml index fb6fa30c41..3c5dcd4beb 100644 --- a/resources/views/admin/locations.phtml +++ b/resources/views/admin/locations.phtml @@ -2,6 +2,7 @@ use Fisharebest\Webtrees\Http\RequestHandlers\MapDataAdd; use Fisharebest\Webtrees\Http\RequestHandlers\MapDataDelete; +use Fisharebest\Webtrees\Http\RequestHandlers\MapDataDeleteUnused; use Fisharebest\Webtrees\Http\RequestHandlers\MapDataEdit; use Fisharebest\Webtrees\Http\RequestHandlers\MapDataExportCSV; use Fisharebest\Webtrees\Http\RequestHandlers\MapDataExportGeoJson; @@ -137,7 +138,7 @@ use Illuminate\Support\Collection; <tfoot> <tr> - <td colspan="7"> + <td colspan="4"> <a class="btn btn-primary" href="<?= e(route(MapDataAdd::class, ['parent_id' => $parent_id])) ?>"> <?= view('icons/add') ?> <?= /* I18N: A button label. */ @@ -163,6 +164,17 @@ use Illuminate\Support\Collection; I18N::translate('import file') ?> </a> </td> + <td colspan="2"> + <?php if (count($active) !== count($placelist)) : ?> + <form class="form-inline d-inline" method="post" action="<?= route(MapDataDeleteUnused::class) ?>"> + <?= csrf_field() ?> + <button type="submit" class="btn btn-danger"> + <?= view('icons/delete') ?> + <?= I18N::translate('Delete unused locations') ?> + </button> + </form> + <?php endif ?> + </td> </tr> </tfoot> </table> diff --git a/resources/views/admin/map-import-form.phtml b/resources/views/admin/map-import-form.phtml index 4498275224..042d34864c 100644 --- a/resources/views/admin/map-import-form.phtml +++ b/resources/views/admin/map-import-form.phtml @@ -54,16 +54,6 @@ use Fisharebest\Webtrees\View; </div> </div> - <!-- CLEAR DATABASE --> - <fieldset class="row form-group"> - <legend class="col-form-label col-sm-4"> - <?= I18N::translate('Delete all existing geographic data before importing the file.') ?> - </legend> - <div class="col-sm-8"> - <?= view('components/radios-inline', ['name' => 'cleardatabase', 'options' => [I18N::translate('no'), I18N::translate('yes')], 'selected' => 0]) ?> - </div> - </fieldset> - <!-- Import options --> <fieldset class="row form-group"> <legend class="col-form-label col-sm-4"> @@ -88,18 +78,3 @@ use Fisharebest\Webtrees\View; </div> </div> </form> - -<?php View::push('javascript') ?> - <script> - $('#upload_form').on('submit', function(e) { - let self = this; - e.preventDefault(); - if($('input[name="cleardatabase"]:checked').val() === '1') { - if (!confirm('<?= I18N::translate('Really delete all geographic data?') ?> ')) { - return false; - } - } - self.submit(); - }); - </script> -<?php View::endpush() ?> |
