summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Functions/FunctionsPrintLists.php64
-rw-r--r--app/Http/Controllers/Admin/ControlPanelController.php2
-rw-r--r--app/Http/Controllers/Admin/ModuleController.php30
-rw-r--r--app/Http/Controllers/BranchesController.php6
-rw-r--r--app/Http/Controllers/ListController.php81
-rw-r--r--app/Http/Controllers/PlaceHierarchyController.php16
-rw-r--r--app/Module/BranchesListModule.php97
-rw-r--r--app/Module/FamilyListModule.php81
-rw-r--r--app/Module/FamilyTreeStatisticsModule.php10
-rw-r--r--app/Module/IndividualListModule.php81
-rw-r--r--app/Module/ListsMenuModule.php64
-rw-r--r--app/Module/MediaListModule.php89
-rw-r--r--app/Module/ModuleListInterface.php70
-rw-r--r--app/Module/ModuleListTrait.php100
-rw-r--r--app/Module/NoteListModule.php89
-rw-r--r--app/Module/PlaceHierarchyListModule.php81
-rw-r--r--app/Module/RepositoryListModule.php89
-rw-r--r--app/Module/SourceListModule.php88
-rw-r--r--app/Module/TopSurnamesModule.php19
-rw-r--r--app/Place.php22
-rw-r--r--app/Schema/Migration42.php62
-rw-r--r--app/Services/ModuleService.php18
-rw-r--r--app/Statistics/Repository/IndividualRepository.php10
-rw-r--r--app/Webtrees.php2
-rw-r--r--resources/views/admin/control-panel-tree-list.phtml87
-rw-r--r--resources/views/admin/control-panel.phtml7
-rw-r--r--resources/views/admin/modules.phtml13
-rw-r--r--resources/views/branches-page.phtml6
-rw-r--r--resources/views/icons/list.phtml1
-rw-r--r--resources/views/lists/surnames-table.phtml31
-rw-r--r--resources/views/media-list-page.phtml26
-rw-r--r--resources/views/places-page.phtml6
-rw-r--r--routes/web.php20
-rw-r--r--tests/feature/IndividualListTest.php10
34 files changed, 1315 insertions, 163 deletions
diff --git a/app/Functions/FunctionsPrintLists.php b/app/Functions/FunctionsPrintLists.php
index 2c6456b1da..79fd1a082a 100644
--- a/app/Functions/FunctionsPrintLists.php
+++ b/app/Functions/FunctionsPrintLists.php
@@ -19,6 +19,7 @@ namespace Fisharebest\Webtrees\Functions;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\Module\ModuleListInterface;
/**
* Class FunctionsPrintLists - create sortable lists using datatables.net
@@ -28,14 +29,15 @@ class FunctionsPrintLists
/**
* Print a tagcloud of surnames.
*
- * @param int[][] $surnames array (of SURN, of array of SPFX_SURN, of counts)
- * @param string $route individual-list or family-listlist
- * @param bool $totals show totals after each name
- * @param Tree $tree generate links to this tree
+ * @param int[][] $surnames array (of SURN, of array of SPFX_SURN, of counts)
+ * @param Tree $tree
+ * @param ?ModuleListInterface $module
+ * @param bool $totals show totals after each name
+ * @param Tree $tree generate links to this tree
*
* @return string
*/
- public static function surnameTagCloud(array $surnames, string $route, bool $totals, Tree $tree): string
+ public static function surnameTagCloud(array $surnames, ?ModuleListInterface $module, bool $totals, Tree $tree): string
{
$minimum = PHP_INT_MAX;
$maximum = 1;
@@ -55,17 +57,20 @@ class FunctionsPrintLists
} else {
$size = 75.0 + 125.0 * ($count - $minimum) / ($maximum - $minimum);
}
- $url = route($route, [
- 'surname' => $surn,
- 'ged' => $tree->name(),
- ]);
- $html .= '<a style="font-size:' . $size . '%" href="' . e($url) . '">';
+
+ $tag = ($module instanceof ModuleListInterface)?'a':'span';
+ $html .= '<'.$tag.' style="font-size:' . $size . '%"';
+ if ($module instanceof ModuleListInterface) {
+ $url = $module->listUrl($tree, ['surname' => $surn]);
+ $html .= ' href="' . e($url) . '"';
+ }
+ $html .= '>';
if ($totals) {
$html .= I18N::translate('%1$s (%2$s)', '<span dir="auto">' . $spfxsurn . '</span>', I18N::number($count));
} else {
$html .= $spfxsurn;
}
- $html .= '</a> ';
+ $html .= '</'.$tag.'> ';
}
}
@@ -75,34 +80,35 @@ class FunctionsPrintLists
/**
* Print a list of surnames.
*
- * @param string[][][] $surnames array (of SURN, of array of SPFX_SURN, of array of XREF)
- * @param int $style 1=bullet list, 2=semicolon-separated list, 3=tabulated list with up to 4 columns
- * @param bool $totals show totals after each name
- * @param string $route individual-list or family-list
- * @param Tree $tree Link back to the individual list in this tree
+ * @param string[][][] $surnames array (of SURN, of array of SPFX_SURN, of array of XREF)
+ * @param int $style 1=bullet list, 2=semicolon-separated list, 3=tabulated list with up to 4 columns
+ * @param bool $totals show totals after each name
+ * @param ?ModuleListInterface $module
+ * @param Tree $tree Link back to the individual list in this tree
*
* @return string
*/
- public static function surnameList($surnames, $style, $totals, $route, Tree $tree)
+ public static function surnameList($surnames, $style, $totals, ?ModuleListInterface $module, Tree $tree)
{
$html = [];
foreach ($surnames as $surn => $surns) {
// Each surname links back to the indilist
- if ($surn) {
- $url = route($route, [
- 'surname' => $surn,
- 'ged' => $tree->name(),
- ]);
- } else {
- $url = route($route, [
- 'alpha' => ',',
- 'ged' => $tree->name(),
- ]);
+ if ($module instanceof ModuleListInterface) {
+ if ($surn) {
+ $url = $module->listUrl($tree, ['surname' => $surn]);
+ } else {
+ $url = $module->listUrl($tree, ['alpha' => ',']);
+ }
}
// If all the surnames are just case variants, then merge them into one
// Comment out this block if you want SMITH listed separately from Smith
- $subhtml = '<a href="' . e($url) . '" dir="auto">' . e(implode(I18N::$list_separator, array_keys($surns))) . '</a>';
-
+ $tag = ($module instanceof ModuleListInterface)?'a':'span';
+ $subhtml = '<'.$tag;
+ if ($url !== null) {
+ $subhtml .= ' href="' . e($url) . '"';
+ }
+ $subhtml .= ' dir="auto">' . e(implode(I18N::$list_separator, array_keys($surns))) . '</'.$tag.'>';
+
if ($totals) {
$subtotal = 0;
foreach ($surns as $count) {
diff --git a/app/Http/Controllers/Admin/ControlPanelController.php b/app/Http/Controllers/Admin/ControlPanelController.php
index 4ae1dc9d65..d804140d2e 100644
--- a/app/Http/Controllers/Admin/ControlPanelController.php
+++ b/app/Http/Controllers/Admin/ControlPanelController.php
@@ -25,6 +25,7 @@ use Fisharebest\Webtrees\Module\ModuleChartInterface;
use Fisharebest\Webtrees\Module\ModuleFooterInterface;
use Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface;
use Fisharebest\Webtrees\Module\ModuleLanguageInterface;
+use Fisharebest\Webtrees\Module\ModuleListInterface;
use Fisharebest\Webtrees\Module\ModuleMenuInterface;
use Fisharebest\Webtrees\Module\ModuleReportInterface;
use Fisharebest\Webtrees\Module\ModuleSidebarInterface;
@@ -94,6 +95,7 @@ class ControlPanelController extends AbstractAdminController
'footer_modules' => $module_service->findByInterface(ModuleFooterInterface::class, true),
'history_modules' => $module_service->findByInterface(ModuleHistoricEventsInterface::class, true),
'language_modules' => $module_service->findByInterface(ModuleLanguageInterface::class, true),
+ 'list_modules' => $module_service->findByInterface(ModuleListInterface::class, true),
'menu_modules' => $module_service->findByInterface(ModuleMenuInterface::class, true),
'report_modules' => $module_service->findByInterface(ModuleReportInterface::class, true),
'sidebar_modules' => $module_service->findByInterface(ModuleSidebarInterface::class, true),
diff --git a/app/Http/Controllers/Admin/ModuleController.php b/app/Http/Controllers/Admin/ModuleController.php
index 767665de91..68fe692677 100644
--- a/app/Http/Controllers/Admin/ModuleController.php
+++ b/app/Http/Controllers/Admin/ModuleController.php
@@ -25,6 +25,7 @@ use Fisharebest\Webtrees\Module\ModuleChartInterface;
use Fisharebest\Webtrees\Module\ModuleFooterInterface;
use Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface;
use Fisharebest\Webtrees\Module\ModuleLanguageInterface;
+use Fisharebest\Webtrees\Module\ModuleListInterface;
use Fisharebest\Webtrees\Module\ModuleMenuInterface;
use Fisharebest\Webtrees\Module\ModuleReportInterface;
use Fisharebest\Webtrees\Module\ModuleSidebarInterface;
@@ -46,6 +47,7 @@ class ModuleController extends AbstractAdminController
private const COMPONENTS_WITH_ACCESS = [
'block',
'chart',
+ 'list',
'menu',
'report',
'sidebar',
@@ -165,6 +167,19 @@ class ModuleController extends AbstractAdminController
''
);
}
+
+ /**
+ * @return Response
+ */
+ public function listLists(): Response
+ {
+ return $this->listComponents(
+ ModuleListInterface::class,
+ 'list',
+ I18N::translate('Lists'),
+ ''
+ );
+ }
/**
* @return Response
@@ -372,7 +387,22 @@ class ModuleController extends AbstractAdminController
return new RedirectResponse(route('language'));
}
+
+ /**
+ * @param Request $request
+ *
+ * @return RedirectResponse
+ */
+ public function updateLists(Request $request): RedirectResponse
+ {
+ $modules = $this->module_service->findByInterface(ModuleListInterface::class, true);
+
+ $this->updateStatus($modules, $request);
+ $this->updateAccessLevel($modules, 'list', $request);
+ return new RedirectResponse(route('lists'));
+ }
+
/**
* @param Request $request
*
diff --git a/app/Http/Controllers/BranchesController.php b/app/Http/Controllers/BranchesController.php
index 50b72474f1..e93bd61f11 100644
--- a/app/Http/Controllers/BranchesController.php
+++ b/app/Http/Controllers/BranchesController.php
@@ -62,6 +62,10 @@ class BranchesController extends AbstractBaseController
*/
public function page(Request $request): Response
{
+ //route is assumed to be 'module'
+ $module = $request->get('module');
+ $action = $request->get('action');
+
$surname = $request->get('surname', '');
$soundex_std = (bool) $request->get('soundex_std');
$soundex_dm = (bool) $request->get('soundex_dm');
@@ -79,6 +83,8 @@ class BranchesController extends AbstractBaseController
'soundex_std' => $soundex_std,
'surname' => $surname,
'title' => $title,
+ 'module' => $module,
+ 'action' => $action,
]);
}
diff --git a/app/Http/Controllers/ListController.php b/app/Http/Controllers/ListController.php
index d0eeebeff9..676efc9232 100644
--- a/app/Http/Controllers/ListController.php
+++ b/app/Http/Controllers/ListController.php
@@ -25,6 +25,7 @@ use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Media;
use Fisharebest\Webtrees\Note;
use Fisharebest\Webtrees\Repository;
+use Fisharebest\Webtrees\Module\ModuleListInterface;
use Fisharebest\Webtrees\Services\IndividualListService;
use Fisharebest\Webtrees\Services\LocalizationService;
use Fisharebest\Webtrees\Session;
@@ -47,7 +48,8 @@ class ListController extends AbstractBaseController
/** @var LocalizationService */
private $localization_service;
-
+
+
/**
* ListController constructor.
*
@@ -63,32 +65,40 @@ class ListController extends AbstractBaseController
/**
* Show a list of all individual or family records.
*
- * @param Request $request
- * @param Tree $tree
- * @param UserInterface $user
+ * @param ?ModuleListInterface $module
+ * @param Request $request
+ * @param Tree $tree
+ * @param UserInterface $user
*
* @return Response
*/
- public function familyList(Request $request, Tree $tree, UserInterface $user): Response
+ public function familyList(?ModuleListInterface $moduleListInterface, Request $request, Tree $tree, UserInterface $user): Response
{
- return $this->individualList($request, $tree, $user);
+ return $this->individualOrFamilyList($moduleListInterface, true, $request, $tree, $user);
}
/**
* Show a list of all individual or family records.
- *
- * @param Request $request
- * @param Tree $tree
- * @param UserInterface $user
+ *
+ * @param ?ModuleListInterface $module
+ * @param Request $request
+ * @param Tree $tree
+ * @param UserInterface $user
*
* @return Response
*/
- public function individualList(Request $request, Tree $tree, UserInterface $user): Response
+ public function individualList(?ModuleListInterface $moduleListInterface, Request $request, Tree $tree, UserInterface $user): Response
+ {
+ return $this->individualOrFamilyList($moduleListInterface, false, $request, $tree, $user);
+ }
+
+ public function individualOrFamilyList(?ModuleListInterface $moduleListInterface, bool $families, Request $request, Tree $tree, UserInterface $user): Response
{
// This action can show lists of both families and individuals.
- $route = $request->get('route');
- $families = $route === 'family-list';
-
+ //route is assumed to be 'module'
+ $module = $request->get('module');
+ $action = $request->get('action');
+
ob_start();
// We show three different lists: initials, surnames and individuals
@@ -114,10 +124,10 @@ class ListController extends AbstractBaseController
switch ($show_marnm) {
case 'no':
case 'yes':
- $user->setPreference($route . '-marnm', $show_marnm);
+ $user->setPreference($families?'family-list-marnm':'individual-list-marnm', $show_marnm);
break;
default:
- $show_marnm = $user->getPreference($route . '-marnm');
+ $show_marnm = $user->getPreference($families?'family-list-marnm':'individual-list-marnm');
}
// Make sure selections are consistent.
@@ -222,7 +232,7 @@ class ListController extends AbstractBaseController
<?php foreach ($this->individual_list_service->surnameAlpha($show_marnm === 'yes', $families, WT_LOCALE, I18N::collation()) as $letter => $count) : ?>
<li class="wt-initials-list-item d-flex">
<?php if ($count > 0) : ?>
- <a href="<?= e(route($route, ['alpha' => $letter, 'ged' => $tree->name()])) ?>" class="wt-initial px-1<?= $letter === $alpha ? ' active' : '' ?> '" title="<?= I18N::number($count) ?>"><?= $this->surnameInitial((string) $letter) ?></a>
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'alpha' => $letter, 'ged' => $tree->name()])) ?>" class="wt-initial px-1<?= $letter === $alpha ? ' active' : '' ?> '" title="<?= I18N::number($count) ?>"><?= $this->surnameInitial((string) $letter) ?></a>
<?php else : ?>
<span class="wt-initial px-1 text-muted"><?= $this->surnameInitial((string) $letter) ?></span>
@@ -233,7 +243,7 @@ class ListController extends AbstractBaseController
<?php if (Session::has('initiated')) : ?>
<!-- Search spiders don't get the "show all" option as the other links give them everything. -->
<li class="wt-initials-list-item d-flex">
- <a class="wt-initial px-1<?= $show_all === 'yes' ? ' active' : '' ?>" href="<?= e(route($route, ['show_all' => 'yes'] + $params)) ?>"><?= I18N::translate('All') ?></a>
+ <a class="wt-initial px-1<?= $show_all === 'yes' ? ' active' : '' ?>" href="<?= e(route('module', ['module' => $module, 'action' => $action, 'show_all' => 'yes'] + $params)) ?>"><?= I18N::translate('All') ?></a>
</li>
<?php endif ?>
</ul>
@@ -242,13 +252,13 @@ class ListController extends AbstractBaseController
<?php if (Session::has('initiated') && $show !== 'none') : ?>
<?php if ($show_marnm === 'yes') : ?>
<p>
- <a href="<?= e(route($route, ['show' => $show, 'show_marnm' => 'no'] + $params)) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'show' => $show, 'show_marnm' => 'no'] + $params)) ?>">
<?= I18N::translate('Exclude individuals with “%s” as a married name', $legend) ?>
</a>
</p>
<?php else : ?>
<p>
- <a href="<?= e(route($route, ['show' => $show, 'show_marnm' => 'yes'] + $params)) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'show' => $show, 'show_marnm' => 'yes'] + $params)) ?>">
<?= I18N::translate('Include individuals with “%s” as a married name', $legend) ?>
</a>
</p>
@@ -257,13 +267,13 @@ class ListController extends AbstractBaseController
<?php if ($alpha !== '@' && $alpha !== ',' && !$surname) : ?>
<?php if ($show === 'surn') : ?>
<p>
- <a href="<?= e(route($route, ['show' => 'indi', 'show_marnm' => 'no'] + $params)) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'show' => 'indi', 'show_marnm' => 'no'] + $params)) ?>">
<?= I18N::translate('Show the list of individuals') ?>
</a>
</p>
<?php else : ?>
<p>
- <a href="<?= e(route($route, ['show' => 'surn', 'show_marnm' => 'no'] + $params)) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'show' => 'surn', 'show_marnm' => 'no'] + $params)) ?>">
<?= I18N::translate('Show the list of surnames') ?>
</a>
</p>
@@ -281,16 +291,17 @@ class ListController extends AbstractBaseController
// Show the surname list
switch ($tree->getPreference('SURNAME_LIST_STYLE')) {
case 'style1':
- echo FunctionsPrintLists::surnameList($surns, 3, true, $route, $tree);
+ echo FunctionsPrintLists::surnameList($surns, 3, true, $moduleListInterface, $tree);
break;
- case 'style3':
- echo FunctionsPrintLists::surnameTagCloud($surns, $route, true, $tree);
+ case 'style3':
+ echo FunctionsPrintLists::surnameTagCloud($surns, $moduleListInterface, true, $tree);
break;
case 'style2':
default:
echo view('lists/surnames-table', [
'surnames' => $surns,
- 'route' => $route,
+ 'families' => $families,
+ 'router' => $moduleListInterface,
]);
break;
}
@@ -320,9 +331,9 @@ class ListController extends AbstractBaseController
echo '<li class="wt-initials-list-item d-flex">';
if ($count > 0) {
if ($show === 'indi' && $givn_initial === $falpha && $show_all_firstnames === 'no') {
- echo '<a class="wt-initial px-1 active" href="' . e(route($route, ['falpha' => $givn_initial] + $params)) . '" title="' . I18N::number($count) . '">' . $this->givenNameInitial((string) $givn_initial) . '</a>';
+ echo '<a class="wt-initial px-1 active" href="' . e(route('module', ['module' => $module, 'action' => $action, 'falpha' => $givn_initial] + $params)) . '" title="' . I18N::number($count) . '">' . $this->givenNameInitial((string) $givn_initial) . '</a>';
} else {
- echo '<a class="wt-initial px-1" href="' . e(route($route, ['falpha' => $givn_initial] + $params)) . '" title="' . I18N::number($count) . '">' . $this->givenNameInitial((string) $givn_initial) . '</a>';
+ echo '<a class="wt-initial px-1" href="' . e(route('module', ['module' => $module, 'action' => $action, 'falpha' => $givn_initial] + $params)) . '" title="' . I18N::number($count) . '">' . $this->givenNameInitial((string) $givn_initial) . '</a>';
}
} else {
echo '<span class="wt-initial px-1 text-muted">' . $this->givenNameInitial((string) $givn_initial) . '</span>';
@@ -335,7 +346,7 @@ class ListController extends AbstractBaseController
if ($show_all_firstnames === 'yes') {
echo '<span class="wt-initial px-1 warning">' . I18N::translate('All') . '</span>';
} else {
- echo '<a class="wt-initial px-1" href="' . e(route($route, ['show_all_firstnames' => 'yes'] + $params)) . '" title="' . I18N::number($count) . '">' . I18N::translate('All') . '</a>';
+ echo '<a class="wt-initial px-1" href="' . e(route('module', ['module' => $module, 'action' => $action, 'show_all_firstnames' => 'yes'] + $params)) . '" title="' . I18N::number($count) . '">' . I18N::translate('All') . '</a>';
}
echo '</li>';
}
@@ -344,7 +355,7 @@ class ListController extends AbstractBaseController
}
}
if ($show === 'indi') {
- if ($route === 'individual-list') {
+ if (!$families) {
echo view('lists/individuals-table', [
'individuals' => $this->individual_list_service->individuals($surname, $alpha, $falpha, $show_marnm === 'yes', false, I18N::collation()),
'sosa' => false,
@@ -381,9 +392,13 @@ class ListController extends AbstractBaseController
*/
public function mediaList(Request $request, Tree $tree): Response
{
+ //route is assumed to be 'module'
+ $module = $request->get('module');
+ $action = $request->get('action');
+
$formats = GedcomTag::getFileFormTypes();
- $action = $request->get('action');
+ $action2 = $request->get('action2');
$page = (int) $request->get('page');
$max = (int) $request->get('max', 20);
$folder = $request->get('folder', '');
@@ -393,7 +408,7 @@ class ListController extends AbstractBaseController
$folders = $this->allFolders($tree);
- if ($action === '1') {
+ if ($action2 === '1') {
$media_objects = $this->allMedia(
$tree,
$folder,
@@ -426,6 +441,8 @@ class ListController extends AbstractBaseController
'pages' => $pages,
'subdirs' => $subdirs,
'title' => I18N::translate('Media'),
+ 'module' => $module,
+ 'action' => $action,
]);
}
diff --git a/app/Http/Controllers/PlaceHierarchyController.php b/app/Http/Controllers/PlaceHierarchyController.php
index 91b647efaf..e8ca99daec 100644
--- a/app/Http/Controllers/PlaceHierarchyController.php
+++ b/app/Http/Controllers/PlaceHierarchyController.php
@@ -66,8 +66,8 @@ class PlaceHierarchyController extends AbstractBaseController
* @return Response
*/
public function show(Request $request, Tree $tree, SearchService $search_service): Response
- {
- $action = $request->query->get('action', 'hierarchy');
+ {
+ $action2 = $request->query->get('action2', 'hierarchy');
$parent = $request->query->get('parent', []);
$fqpn = implode(Gedcom::PLACE_SEPARATOR, array_reverse($parent));
$place = new Place($fqpn, $tree);
@@ -86,7 +86,7 @@ class PlaceHierarchyController extends AbstractBaseController
]);
}
- switch ($action) {
+ switch ($action2) {
case 'list':
$nextaction = ['hierarchy' => I18N::translate('Show place hierarchy')];
$content .= view('place-list', $this->getList($tree, $search_service));
@@ -96,7 +96,7 @@ class PlaceHierarchyController extends AbstractBaseController
$nextaction = ['list' => I18N::translate('Show all places in a list')];
$data = $this->getHierarchy($tree, $place, $parent);
$content .= (null === $data || $showmap) ? '' : view('place-hierarchy', $data);
- if (null === $data || $action === 'hierarchy-e') {
+ if (null === $data || $action2 === 'hierarchy-e') {
$content .= view('place-events', $this->getEvents($tree, $place));
}
break;
@@ -106,6 +106,10 @@ class PlaceHierarchyController extends AbstractBaseController
$breadcrumbs = $this->breadcrumbs($place);
+ //route is assumed to be 'module'
+ $module = $request->get('module');
+ $action = $request->get('action');
+
return $this->viewResponse(
'places-page',
[
@@ -117,8 +121,10 @@ class PlaceHierarchyController extends AbstractBaseController
'parent' => $parent,
'place' => $fqpn,
'content' => $content,
- 'showeventslink' => null !== $data && $place->gedcomName() !== '' && $action !== 'hierarchy-e',
+ 'showeventslink' => null !== $data && $place->gedcomName() !== '' && $action2 !== 'hierarchy-e',
'nextaction' => $nextaction,
+ 'module' => $module,
+ 'action' => $action,
]
);
}
diff --git a/app/Module/BranchesListModule.php b/app/Module/BranchesListModule.php
new file mode 100644
index 0000000000..76245532ae
--- /dev/null
+++ b/app/Module/BranchesListModule.php
@@ -0,0 +1,97 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\UserInterface;
+use Fisharebest\Webtrees\Http\Controllers\BranchesController;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\ModuleService;
+use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\Auth;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Class BranchesListModule
+ */
+class BranchesListModule extends AbstractModule implements ModuleListInterface
+{
+ use ModuleListTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ /* I18N: Name of a module/list */
+ return I18N::translate('Branches');
+ }
+
+ /**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ /* I18N: Description of the “BranchesListModule” module */
+ return I18N::translate('A list of branches of a family.');
+ }
+
+ /**
+ * CSS class for the URL.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string
+ {
+ return 'menu-branches';
+ }
+
+ public function listUrl(Tree $tree, array $parameters = []): string
+ {
+ return route('module', [
+ 'module' => $this->name(),
+ 'action' => 'Page',
+ 'ged' => $tree->name(),
+ ] + $parameters);
+ }
+
+ public function getPageAction(Request $request, Tree $tree, UserInterface $user): Response
+ {
+ Auth::checkComponentAccess($this, 'list', $tree, $user);
+
+ $listController = new BranchesController(app(ModuleService::class));
+ return $listController->page($request);
+ }
+
+ public function getListAction(Request $request, Tree $tree, UserInterface $user): Response
+ {
+ Auth::checkComponentAccess($this, 'list', $tree, $user);
+
+ $listController = new BranchesController(app(ModuleService::class));
+ return $listController->list($request, $tree, $user);
+ }
+
+ public function listUrlAttributes(): array
+ {
+ return [];
+ }
+}
diff --git a/app/Module/FamilyListModule.php b/app/Module/FamilyListModule.php
new file mode 100644
index 0000000000..06d9e309b8
--- /dev/null
+++ b/app/Module/FamilyListModule.php
@@ -0,0 +1,81 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\UserInterface;
+use Fisharebest\Webtrees\Http\Controllers\ListController;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\IndividualListService;
+use Fisharebest\Webtrees\Services\LocalizationService;
+use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\Auth;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Class FamilyListModule
+ */
+class FamilyListModule extends AbstractModule implements ModuleListInterface
+{
+ use ModuleListTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ /* I18N: Name of a module/list */
+ return I18N::translate('Families');
+ }
+
+ /**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ /* I18N: Description of the “FamilyListModule” module */
+ return I18N::translate('A list of families.');
+ }
+
+ /**
+ * CSS class for the URL.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string
+ {
+ return 'menu-list-fam';
+ }
+
+ public function getListAction(Request $request, Tree $tree, UserInterface $user): Response
+ {
+ Auth::checkComponentAccess($this, 'list', $tree, $user);
+
+ $listController = new ListController(app(IndividualListService::class), app(LocalizationService::class));
+ return $listController->familyList($this, $request, $tree, $user);
+ }
+
+ public function listUrlAttributes(): array
+ {
+ return [];
+ }
+}
diff --git a/app/Module/FamilyTreeStatisticsModule.php b/app/Module/FamilyTreeStatisticsModule.php
index 47af90942d..4e02f15fb1 100644
--- a/app/Module/FamilyTreeStatisticsModule.php
+++ b/app/Module/FamilyTreeStatisticsModule.php
@@ -20,6 +20,9 @@ namespace Fisharebest\Webtrees\Module;
use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\Functions\FunctionsPrintLists;
use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Module\IndividualListModule;
+use Fisharebest\Webtrees\Module\ModuleInterface;
+use Fisharebest\Webtrees\Services\ModuleService;
use Fisharebest\Webtrees\Statistics;
use Fisharebest\Webtrees\Tree;
use Illuminate\Database\Capsule\Manager as DB;
@@ -120,7 +123,12 @@ class FamilyTreeStatisticsModule extends AbstractModule implements ModuleBlockIn
uksort($all_surnames, [I18N::class, 'strcasecmp']);
- $surnames = FunctionsPrintLists::surnameList($all_surnames, 2, false, 'individual-list', $tree);
+ //find a module providing individual lists
+ $module = app(ModuleService::class)->findByComponent('list', $tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof IndividualListModule;
+ });
+
+ $surnames = FunctionsPrintLists::surnameList($all_surnames, 2, false, $module, $tree);
} else {
$surnames = '';
}
diff --git a/app/Module/IndividualListModule.php b/app/Module/IndividualListModule.php
new file mode 100644
index 0000000000..3ee79c6296
--- /dev/null
+++ b/app/Module/IndividualListModule.php
@@ -0,0 +1,81 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\UserInterface;
+use Fisharebest\Webtrees\Http\Controllers\ListController;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\IndividualListService;
+use Fisharebest\Webtrees\Services\LocalizationService;
+use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\Auth;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Class IndividualListModule
+ */
+class IndividualListModule extends AbstractModule implements ModuleListInterface
+{
+ use ModuleListTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ /* I18N: Name of a module/list */
+ return I18N::translate('Individuals');
+ }
+
+ /**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ /* I18N: Description of the “IndividualListModule” module */
+ return I18N::translate('A list of individuals.');
+ }
+
+ /**
+ * CSS class for the URL.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string
+ {
+ return 'menu-list-indi';
+ }
+
+ public function getListAction(Request $request, Tree $tree, UserInterface $user): Response
+ {
+ Auth::checkComponentAccess($this, 'list', $tree, $user);
+
+ $listController = new ListController(app(IndividualListService::class), app(LocalizationService::class));
+ return $listController->individualList($this, $request, $tree, $user);
+ }
+
+ public function listUrlAttributes(): array
+ {
+ return [];
+ }
+}
diff --git a/app/Module/ListsMenuModule.php b/app/Module/ListsMenuModule.php
index 8ea66673e1..683c1d4456 100644
--- a/app/Module/ListsMenuModule.php
+++ b/app/Module/ListsMenuModule.php
@@ -17,10 +17,11 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees\Module;
+use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Menu;
+use Fisharebest\Webtrees\Services\ModuleService;
use Fisharebest\Webtrees\Tree;
-use Illuminate\Database\Capsule\Manager as DB;
/**
* Class ListsMenuModule - provide a menu option for the lists
@@ -30,6 +31,21 @@ class ListsMenuModule extends AbstractModule implements ModuleMenuInterface
use ModuleMenuTrait;
/**
+ * @var ModuleService
+ */
+ private $module_service;
+
+ /**
+ * ListsMenuModule constructor.
+ *
+ * @param ModuleService $module_service
+ */
+ public function __construct(ModuleService $module_service)
+ {
+ $this->module_service = $module_service;
+ }
+
+ /**
* How should this module be labelled on tabs, menus, etc.?
*
* @return string
@@ -70,45 +86,19 @@ class ListsMenuModule extends AbstractModule implements ModuleMenuInterface
*/
public function getMenu(Tree $tree): ?Menu
{
- // Do not show empty lists
- $sources_exist = DB::table('sources')
- ->where('s_file', '=', $tree->id())
- ->exists();
-
- $repositories_exist = DB::table('other')
- ->where('o_file', '=', $tree->id())
- ->where('o_type', '=', 'REPO')
- ->exists();
-
- $notes_exist = DB::table('other')
- ->where('o_file', '=', $tree->id())
- ->where('o_type', '=', 'NOTE')
- ->exists();
-
- $media_exist = DB::table('media')
- ->where('m_file', '=', $tree->id())
- ->exists();
+ $submenusCollection = $this->module_service->findByComponent('list', $tree, Auth::user())
+ ->map(function (ModuleListInterface $module) use ($tree): Menu {
+ return $module->listMenu($tree);
+ })
+ ->filter(function (Menu $menu): bool {
+ return ($menu !== null);
+ });
- $submenus = [
- new Menu(I18N::translate('Individuals'), route('individual-list', ['ged' => $tree->name()]), 'menu-list-indi'),
- new Menu(I18N::translate('Families'), route('family-list', ['ged' => $tree->name()]), 'menu-list-fam'),
- new Menu(I18N::translate('Branches'), route('branches', ['ged' => $tree->name()]), 'menu-branches', ['rel' => 'nofollow']),
- new Menu(I18N::translate('Place hierarchy'), route('place-hierarchy', ['ged' => $tree->name()]), 'menu-list-plac', ['rel' => 'nofollow']),
- ];
-
- if ($media_exist) {
- $submenus[] = new Menu(I18N::translate('Media objects'), route('media-list', ['ged' => $tree->name()]), 'menu-list-obje', ['rel' => 'nofollow']);
- }
- if ($repositories_exist) {
- $submenus[] = new Menu(I18N::translate('Repositories'), route('repository-list', ['ged' => $tree->name()]), 'menu-list-repo', ['rel' => 'nofollow']);
- }
- if ($sources_exist) {
- $submenus[] = new Menu(I18N::translate('Sources'), route('source-list', ['ged' => $tree->name()]), 'menu-list-sour', ['rel' => 'nofollow']);
- }
- if ($notes_exist) {
- $submenus[] = new Menu(I18N::translate('Shared notes'), route('note-list', ['ged' => $tree->name()]), 'menu-list-note', ['rel' => 'nofollow']);
+ if ($submenusCollection->isEmpty()) {
+ return null;
}
+ $submenus = $submenusCollection->toArray();
uasort($submenus, function (Menu $x, Menu $y) {
return I18N::strcasecmp($x->getLabel(), $y->getLabel());
});
diff --git a/app/Module/MediaListModule.php b/app/Module/MediaListModule.php
new file mode 100644
index 0000000000..acb11fd00d
--- /dev/null
+++ b/app/Module/MediaListModule.php
@@ -0,0 +1,89 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\UserInterface;
+use Fisharebest\Webtrees\Http\Controllers\ListController;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\IndividualListService;
+use Fisharebest\Webtrees\Services\LocalizationService;
+use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\Auth;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Illuminate\Database\Capsule\Manager as DB;
+
+/**
+ * Class MediaListModule
+ */
+class MediaListModule extends AbstractModule implements ModuleListInterface
+{
+ use ModuleListTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ /* I18N: Name of a module/list */
+ return I18N::translate('Media objects');
+ }
+
+ /**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ /* I18N: Description of the “MediaListModule” module */
+ return I18N::translate('A list of media objects.');
+ }
+
+ /**
+ * CSS class for the URL.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string
+ {
+ return 'menu-list-obje';
+ }
+
+ public function getListAction(Request $request, Tree $tree, UserInterface $user): Response
+ {
+ Auth::checkComponentAccess($this, 'list', $tree, $user);
+
+ $listController = new ListController(app(IndividualListService::class), app(LocalizationService::class));
+ return $listController->mediaList($request, $tree);
+ }
+
+ public function listUrlAttributes(): array
+ {
+ return [];
+ }
+
+ public function listIsEmpty(Tree $tree): bool
+ {
+ return !DB::table('media')
+ ->where('m_file', '=', $tree->id())
+ ->exists();
+ }
+}
diff --git a/app/Module/ModuleListInterface.php b/app/Module/ModuleListInterface.php
new file mode 100644
index 0000000000..19feb8e84b
--- /dev/null
+++ b/app/Module/ModuleListInterface.php
@@ -0,0 +1,70 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Menu;
+use Fisharebest\Webtrees\Tree;
+
+/**
+ * Interface ModuleListInterface - Classes and libraries for module system
+ */
+interface ModuleListInterface extends ModuleInterface
+{
+
+ /**
+ * A main menu item for this list, or null if the list is empty.
+ *
+ * @param Tree $tree
+ *
+ * @return Menu|null
+ */
+ public function listMenu(Tree $tree): ?Menu;
+
+ /**
+ * CSS class for the menu.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string;
+
+ /**
+ * The title for a specific instance of this list.
+ *
+ * @return string
+ */
+ public function listTitle(): string;
+
+ /**
+ * The URL for a page showing list options.
+ *
+ * @param Tree $tree
+ * @param string[] $parameters
+ *
+ * @return string
+ */
+ public function listUrl(Tree $tree, array $parameters = []): string;
+
+ /**
+ * Attributes for the URL.
+ *
+ * @return string[]
+ */
+ public function listUrlAttributes(): array;
+
+ public function listIsEmpty(Tree $tree): bool;
+}
diff --git a/app/Module/ModuleListTrait.php b/app/Module/ModuleListTrait.php
new file mode 100644
index 0000000000..9ee2f3c1fc
--- /dev/null
+++ b/app/Module/ModuleListTrait.php
@@ -0,0 +1,100 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Menu;
+use Fisharebest\Webtrees\Tree;
+
+/**
+ * Trait ModuleListTrait - default implementation of ModuleListInterface
+ */
+trait ModuleListTrait
+{
+ /**
+ * A main menu item for this list, or null if the list is empty.
+ *
+ * @param Tree $tree
+ *
+ * @return Menu|null
+ */
+ public function listMenu(Tree $tree): ?Menu
+ {
+ if ($this->listIsEmpty($tree)) {
+ return null;
+ }
+
+ return new Menu(
+ $this->title(),
+ $this->listUrl($tree),
+ $this->listMenuClass(),
+ $this->listUrlAttributes()
+ );
+ }
+
+ /**
+ * CSS class for the menu.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string
+ {
+ return '';
+ }
+
+ /**
+ * The title for a specific instance of this list.
+ *
+ * @return string
+ */
+ public function listTitle(): string
+ {
+ return $this->title();
+ }
+
+ /**
+ * The URL for a page showing list options.
+ *
+ * @param Tree $tree
+ * @param string[] $parameters
+ *
+ * @return string
+ */
+ public function listUrl(Tree $tree, array $parameters = []): string
+ {
+ return route('module', [
+ 'module' => $this->name(),
+ 'action' => 'List',
+ 'ged' => $tree->name(),
+ ] + $parameters);
+ }
+
+ /**
+ * Attributes for the URL.
+ *
+ * @return string[]
+ */
+ public function listUrlAttributes(): array
+ {
+ return ['rel' => 'nofollow'];
+ }
+
+ public function listIsEmpty(Tree $tree): bool
+ {
+ return false;
+ }
+}
diff --git a/app/Module/NoteListModule.php b/app/Module/NoteListModule.php
new file mode 100644
index 0000000000..32a5450449
--- /dev/null
+++ b/app/Module/NoteListModule.php
@@ -0,0 +1,89 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\UserInterface;
+use Fisharebest\Webtrees\Http\Controllers\ListController;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\IndividualListService;
+use Fisharebest\Webtrees\Services\LocalizationService;
+use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\Auth;
+use Symfony\Component\HttpFoundation\Response;
+use Illuminate\Database\Capsule\Manager as DB;
+
+/**
+ * Class IndividualListModule
+ */
+class NoteListModule extends AbstractModule implements ModuleListInterface
+{
+ use ModuleListTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ /* I18N: Name of a module/list */
+ return I18N::translate('Shared notes');
+ }
+
+ /**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ /* I18N: Description of the “NoteListModule” module */
+ return I18N::translate('A list of shared notes.');
+ }
+
+ /**
+ * CSS class for the URL.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string
+ {
+ return 'menu-list-note';
+ }
+
+ public function getListAction(Tree $tree, UserInterface $user): Response
+ {
+ Auth::checkComponentAccess($this, 'list', $tree, $user);
+
+ $listController = new ListController(app(IndividualListService::class), app(LocalizationService::class));
+ return $listController->noteList($tree);
+ }
+
+ public function listUrlAttributes(): array
+ {
+ return [];
+ }
+
+ public function listIsEmpty(Tree $tree): bool
+ {
+ return !DB::table('other')
+ ->where('o_file', '=', $tree->id())
+ ->where('o_type', '=', 'NOTE')
+ ->exists();
+ }
+}
diff --git a/app/Module/PlaceHierarchyListModule.php b/app/Module/PlaceHierarchyListModule.php
new file mode 100644
index 0000000000..82789464ad
--- /dev/null
+++ b/app/Module/PlaceHierarchyListModule.php
@@ -0,0 +1,81 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Contracts\UserInterface;
+use Fisharebest\Webtrees\Http\Controllers\PlaceHierarchyController;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\SearchService;
+use Fisharebest\Webtrees\Statistics;
+use Fisharebest\Webtrees\Tree;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Class IndividualListModule
+ */
+class PlaceHierarchyListModule extends AbstractModule implements ModuleListInterface
+{
+ use ModuleListTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ /* I18N: Name of a module/list */
+ return I18N::translate('Place hierarchy');
+ }
+
+ /**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ /* I18N: Description of the “PlaceHierarchyListModule” module */
+ return I18N::translate('The place hierarchy.');
+ }
+
+ /**
+ * CSS class for the URL.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string
+ {
+ return 'menu-list-plac';
+ }
+
+ public function getListAction(Request $request, Tree $tree, UserInterface $user): Response
+ {
+ Auth::checkComponentAccess($this, 'list', $tree, $user);
+
+ $listController = new PlaceHierarchyController(app(Statistics::class));
+ return $listController->show($request, $tree, app(SearchService::class));
+ }
+
+ public function listUrlAttributes(): array
+ {
+ return [];
+ }
+}
diff --git a/app/Module/RepositoryListModule.php b/app/Module/RepositoryListModule.php
new file mode 100644
index 0000000000..cfaa8b72cf
--- /dev/null
+++ b/app/Module/RepositoryListModule.php
@@ -0,0 +1,89 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\UserInterface;
+use Fisharebest\Webtrees\Http\Controllers\ListController;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\IndividualListService;
+use Fisharebest\Webtrees\Services\LocalizationService;
+use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\Auth;
+use Symfony\Component\HttpFoundation\Response;
+use Illuminate\Database\Capsule\Manager as DB;
+
+/**
+ * Class RepositoryListModule
+ */
+class RepositoryListModule extends AbstractModule implements ModuleListInterface
+{
+ use ModuleListTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ /* I18N: Name of a module/list */
+ return I18N::translate('Repositories');
+ }
+
+ /**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ /* I18N: Description of the “RepositoryListModule” module */
+ return I18N::translate('A list of repositories.');
+ }
+
+ /**
+ * CSS class for the URL.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string
+ {
+ return 'menu-list-repo';
+ }
+
+ public function getListAction(Tree $tree, UserInterface $user): Response
+ {
+ Auth::checkComponentAccess($this, 'list', $tree, $user);
+
+ $listController = new ListController(app(IndividualListService::class), app(LocalizationService::class));
+ return $listController->repositoryList($tree);
+ }
+
+ public function listUrlAttributes(): array
+ {
+ return [];
+ }
+
+ public function listIsEmpty(Tree $tree): bool
+ {
+ return !DB::table('other')
+ ->where('o_file', '=', $tree->id())
+ ->where('o_type', '=', 'REPO')
+ ->exists();
+ }
+}
diff --git a/app/Module/SourceListModule.php b/app/Module/SourceListModule.php
new file mode 100644
index 0000000000..f5750b857e
--- /dev/null
+++ b/app/Module/SourceListModule.php
@@ -0,0 +1,88 @@
+<?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\Module;
+
+use Fisharebest\Webtrees\Contracts\UserInterface;
+use Fisharebest\Webtrees\Http\Controllers\ListController;
+use Fisharebest\Webtrees\I18N;
+use Fisharebest\Webtrees\Services\IndividualListService;
+use Fisharebest\Webtrees\Services\LocalizationService;
+use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\Auth;
+use Symfony\Component\HttpFoundation\Response;
+use Illuminate\Database\Capsule\Manager as DB;
+
+/**
+ * Class RepositoryListModule
+ */
+class SourceListModule extends AbstractModule implements ModuleListInterface
+{
+ use ModuleListTrait;
+
+ /**
+ * How should this module be labelled on tabs, menus, etc.?
+ *
+ * @return string
+ */
+ public function title(): string
+ {
+ /* I18N: Name of a module/list */
+ return I18N::translate('Sources');
+ }
+
+ /**
+ * A sentence describing what this module does.
+ *
+ * @return string
+ */
+ public function description(): string
+ {
+ /* I18N: Description of the “SourceListModule” module */
+ return I18N::translate('A list of sources.');
+ }
+
+ /**
+ * CSS class for the URL.
+ *
+ * @return string
+ */
+ public function listMenuClass(): string
+ {
+ return 'menu-list-sour';
+ }
+
+ public function getListAction(Tree $tree, UserInterface $user): Response
+ {
+ Auth::checkComponentAccess($this, 'list', $tree, $user);
+
+ $listController = new ListController(app(IndividualListService::class), app(LocalizationService::class));
+ return $listController->sourceList($tree);
+ }
+
+ public function listUrlAttributes(): array
+ {
+ return [];
+ }
+
+ public function listIsEmpty(Tree $tree): bool
+ {
+ return !DB::table('sources')
+ ->where('s_file', '=', $tree->id())
+ ->exists();
+ }
+}
diff --git a/app/Module/TopSurnamesModule.php b/app/Module/TopSurnamesModule.php
index 1b52c65913..8f8d34f07e 100644
--- a/app/Module/TopSurnamesModule.php
+++ b/app/Module/TopSurnamesModule.php
@@ -21,6 +21,9 @@ use Fisharebest\Webtrees\Auth;
use Fisharebest\Webtrees\Functions\FunctionsPrintLists;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Tree;
+use Fisharebest\Webtrees\Module\ModuleInterface;
+use Fisharebest\Webtrees\Module\IndividualListModule;
+use Fisharebest\Webtrees\Services\ModuleService;
use Illuminate\Database\Capsule\Manager as DB;
use Symfony\Component\HttpFoundation\Request;
@@ -97,25 +100,31 @@ class TopSurnamesModule extends AbstractModule implements ModuleBlockInterface
$all_surnames[$top_surname] = $variants;
}
-
+
+ //find a module providing individual lists
+ $module = app(ModuleService::class)->findByComponent('list', $tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof IndividualListModule;
+ });
+
switch ($infoStyle) {
case 'tagcloud':
uksort($all_surnames, [I18N::class, 'strcasecmp']);
- $content = FunctionsPrintLists::surnameTagCloud($all_surnames, 'individual-list', true, $tree);
+ $content = FunctionsPrintLists::surnameTagCloud($all_surnames, $module, true, $tree);
break;
case 'list':
uasort($all_surnames, [$this, 'surnameCountSort']);
- $content = FunctionsPrintLists::surnameList($all_surnames, 1, true, 'individual-list', $tree);
+ $content = FunctionsPrintLists::surnameList($all_surnames, 1, true, $module, $tree);
break;
case 'array':
uasort($all_surnames, [$this, 'surnameCountSort']);
- $content = FunctionsPrintLists::surnameList($all_surnames, 2, true, 'individual-list', $tree);
+ $content = FunctionsPrintLists::surnameList($all_surnames, 2, true, $module, $tree);
break;
case 'table':
default:
$content = view('lists/surnames-table', [
'surnames' => $all_surnames,
- 'route' => 'individual-list',
+ 'module' => $module,
+ 'families' => false,
'tree' => $tree,
]);
break;
diff --git a/app/Place.php b/app/Place.php
index 70e2b42e13..a6dcba2073 100644
--- a/app/Place.php
+++ b/app/Place.php
@@ -17,6 +17,10 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees;
+use Fisharebest\Webtrees\Auth;
+use Fisharebest\Webtrees\Module\ModuleInterface;
+use Fisharebest\Webtrees\Module\PlaceHierarchyListModule;
+use Fisharebest\Webtrees\Services\ModuleService;
use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Support\Collection;
@@ -157,10 +161,20 @@ class Place
*/
public function url(): string
{
- return route('place-hierarchy', [
- 'parent' => $this->parts->reverse()->all(),
- 'ged' => $this->tree->name(),
- ]);
+ //find a module providing the place hierarchy
+ $module = app(ModuleService::class)->findByComponent('list', $this->tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof PlaceHierarchyListModule;
+ });
+
+ if ($module instanceof PlaceHierarchyListModule) {
+ return $module->listUrl($this->tree, [
+ 'parent' => $this->parts->reverse()->all(),
+ 'ged' => $this->tree->name(),
+ ]);
+ } else {
+ //TODO: should we be allowed to return null here?
+ return \Fisharebest\Webtrees\Html::url('index.php', []);
+ }
}
/**
diff --git a/app/Schema/Migration42.php b/app/Schema/Migration42.php
new file mode 100644
index 0000000000..7635a75880
--- /dev/null
+++ b/app/Schema/Migration42.php
@@ -0,0 +1,62 @@
+<?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\Schema;
+
+use Illuminate\Database\Capsule\Manager as DB;
+use Illuminate\Database\Schema\Blueprint;
+
+/**
+ * Upgrade the database schema from version 42 to version 43.
+ */
+class Migration42 implements MigrationInterface
+{
+ /**
+ * Upgrade to to the next version
+ *
+ * @return void
+ */
+ public function upgrade(): void
+ {
+ //apparently not possible to change enum column directly via laravel
+ $data = DB::table('module_privacy')->get();
+ DB::schema()->drop('module_privacy');
+
+ DB::schema()->create('module_privacy', function (Blueprint $table): void {
+ $table->string('module_name', 32);
+ $table->integer('gedcom_id');
+ $table->enum('component', ['block', 'chart', 'list', 'menu', 'report', 'sidebar', 'tab', 'theme']);
+ $table->tinyInteger('access_level');
+
+ $table->primary(['module_name', 'gedcom_id', 'component']);
+ $table->unique(['gedcom_id', 'module_name', 'component']);
+
+ $table->foreign('module_name')->references('module_name')->on('module');
+ $table->foreign('gedcom_id')->references('gedcom_id')->on('gedcom');
+ });
+
+ $rows = $data->toArray();
+ foreach ($rows as $row) {
+ DB::table('module_privacy')->insert([
+ 'module_name' => $row->module_name,
+ 'gedcom_id' => $row->gedcom_id,
+ 'component' => $row->component,
+ 'access_level' => $row->access_level,
+ ]);
+ }
+ }
+}
diff --git a/app/Services/ModuleService.php b/app/Services/ModuleService.php
index 7e71841dd5..49baf50e5d 100644
--- a/app/Services/ModuleService.php
+++ b/app/Services/ModuleService.php
@@ -29,6 +29,7 @@ use Fisharebest\Webtrees\Module\BatchUpdateModule;
use Fisharebest\Webtrees\Module\BingWebmasterToolsModule;
use Fisharebest\Webtrees\Module\BirthDeathMarriageReportModule;
use Fisharebest\Webtrees\Module\BirthReportModule;
+use Fisharebest\Webtrees\Module\BranchesListModule;
use Fisharebest\Webtrees\Module\CalendarMenuModule;
use Fisharebest\Webtrees\Module\CemeteryReportModule;
use Fisharebest\Webtrees\Module\CensusAssistantModule;
@@ -51,6 +52,7 @@ use Fisharebest\Webtrees\Module\FabTheme;
use Fisharebest\Webtrees\Module\FactSourcesReportModule;
use Fisharebest\Webtrees\Module\FamilyBookChartModule;
use Fisharebest\Webtrees\Module\FamilyGroupReportModule;
+use Fisharebest\Webtrees\Module\FamilyListModule;
use Fisharebest\Webtrees\Module\FamilyNavigatorModule;
use Fisharebest\Webtrees\Module\FamilyTreeFavoritesModule;
use Fisharebest\Webtrees\Module\FamilyTreeNewsModule;
@@ -64,6 +66,7 @@ use Fisharebest\Webtrees\Module\HourglassChartModule;
use Fisharebest\Webtrees\Module\HtmlBlockModule;
use Fisharebest\Webtrees\Module\IndividualFactsTabModule;
use Fisharebest\Webtrees\Module\IndividualFamiliesReportModule;
+use Fisharebest\Webtrees\Module\IndividualListModule;
use Fisharebest\Webtrees\Module\IndividualReportModule;
use Fisharebest\Webtrees\Module\InteractiveTreeModule;
use Fisharebest\Webtrees\Module\LifespansChartModule;
@@ -72,6 +75,7 @@ use Fisharebest\Webtrees\Module\LoggedInUsersModule;
use Fisharebest\Webtrees\Module\LoginBlockModule;
use Fisharebest\Webtrees\Module\MarriageReportModule;
use Fisharebest\Webtrees\Module\MatomoAnalyticsModule;
+use Fisharebest\Webtrees\Module\MediaListModule;
use Fisharebest\Webtrees\Module\MediaTabModule;
use Fisharebest\Webtrees\Module\MinimalTheme;
use Fisharebest\Webtrees\Module\MissingFactsReportModule;
@@ -84,17 +88,20 @@ use Fisharebest\Webtrees\Module\ModuleFooterInterface;
use Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface;
use Fisharebest\Webtrees\Module\ModuleInterface;
use Fisharebest\Webtrees\Module\ModuleLanguageInterface;
+use Fisharebest\Webtrees\Module\ModuleListInterface;
use Fisharebest\Webtrees\Module\ModuleMenuInterface;
use Fisharebest\Webtrees\Module\ModuleReportInterface;
use Fisharebest\Webtrees\Module\ModuleSidebarInterface;
use Fisharebest\Webtrees\Module\ModuleTabInterface;
use Fisharebest\Webtrees\Module\ModuleThemeInterface;
+use Fisharebest\Webtrees\Module\NoteListModule;
use Fisharebest\Webtrees\Module\NotesTabModule;
use Fisharebest\Webtrees\Module\OccupationReportModule;
use Fisharebest\Webtrees\Module\OnThisDayModule;
use Fisharebest\Webtrees\Module\PedigreeChartModule;
use Fisharebest\Webtrees\Module\PedigreeMapModule;
use Fisharebest\Webtrees\Module\PedigreeReportModule;
+use Fisharebest\Webtrees\Module\PlaceHierarchyListModule;
use Fisharebest\Webtrees\Module\PlacesModule;
use Fisharebest\Webtrees\Module\PoweredByWebtreesModule;
use Fisharebest\Webtrees\Module\RecentChangesModule;
@@ -102,11 +109,13 @@ use Fisharebest\Webtrees\Module\RelatedIndividualsReportModule;
use Fisharebest\Webtrees\Module\RelationshipsChartModule;
use Fisharebest\Webtrees\Module\RelativesTabModule;
use Fisharebest\Webtrees\Module\ReportsMenuModule;
+use Fisharebest\Webtrees\Module\RepositoryListModule;
use Fisharebest\Webtrees\Module\ResearchTaskModule;
use Fisharebest\Webtrees\Module\ReviewChangesModule;
use Fisharebest\Webtrees\Module\SearchMenuModule;
use Fisharebest\Webtrees\Module\SiteMapModule;
use Fisharebest\Webtrees\Module\SlideShowModule;
+use Fisharebest\Webtrees\Module\SourceListModule;
use Fisharebest\Webtrees\Module\SourcesTabModule;
use Fisharebest\Webtrees\Module\StatcounterModule;
use Fisharebest\Webtrees\Module\StatisticsChartModule;
@@ -147,6 +156,7 @@ class ModuleService
'footer' => ModuleFooterInterface::class,
'history' => ModuleHistoricEventsInterface::class,
'language' => ModuleLanguageInterface::class,
+ 'list' => ModuleListInterface::class,
'menu' => ModuleMenuInterface::class,
'report' => ModuleReportInterface::class,
'sidebar' => ModuleSidebarInterface::class,
@@ -163,6 +173,7 @@ class ModuleService
'bdm_report' => BirthDeathMarriageReportModule::class,
'bing-webmaster-tools' => BingWebmasterToolsModule::class,
'birth_report' => BirthReportModule::class,
+ 'branches_list' => BranchesListModule::class,
'calendar-menu' => CalendarMenuModule::class,
'cemetery_report' => CemeteryReportModule::class,
'change_report' => ChangeReportModule::class,
@@ -184,6 +195,7 @@ class ModuleService
'fact_sources' => FactSourcesReportModule::class,
'family_book_chart' => FamilyBookChartModule::class,
'family_group_report' => FamilyGroupReportModule::class,
+ 'family_list' => FamilyListModule::class,
'family_nav' => FamilyNavigatorModule::class,
'fan_chart' => FanChartModule::class,
'faq' => FrequentlyAskedQuestionsModule::class,
@@ -197,6 +209,7 @@ class ModuleService
'hourglass_chart' => HourglassChartModule::class,
'html' => HtmlBlockModule::class,
'individual_ext_report' => IndividualFamiliesReportModule::class,
+ 'individual_list' => IndividualListModule::class,
'individual_report' => IndividualReportModule::class,
'lifespans_chart' => LifespansChartModule::class,
'lightbox' => AlbumModule::class,
@@ -206,15 +219,18 @@ class ModuleService
'marriage_report' => MarriageReportModule::class,
'matomo-analytics' => MatomoAnalyticsModule::class,
'media' => MediaTabModule::class,
+ 'media_list' => MediaListModule::class,
'minimal' => MinimalTheme::class,
'missing_facts_report' => MissingFactsReportModule::class,
'notes' => NotesTabModule::class,
+ 'note_list' => NoteListModule::class,
'occupation_report' => OccupationReportModule::class,
'pedigree-map' => PedigreeMapModule::class,
'pedigree_chart' => PedigreeChartModule::class,
'pedigree_report' => PedigreeReportModule::class,
'personal_facts' => IndividualFactsTabModule::class,
'places' => PlacesModule::class,
+ 'places_list' => PlaceHierarchyListModule::class,
'powered-by-webtrees' => PoweredByWebtreesModule::class,
'random_media' => SlideShowModule::class,
'recent_changes' => RecentChangesModule::class,
@@ -222,9 +238,11 @@ class ModuleService
'relative_ext_report' => RelatedIndividualsReportModule::class,
'relatives' => RelativesTabModule::class,
'reports-menu' => ReportsMenuModule::class,
+ 'repository_list' => RepositoryListModule::class,
'review_changes' => ReviewChangesModule::class,
'search-menu' => SearchMenuModule::class,
'sitemap' => SiteMapModule::class,
+ 'source_list' => SourceListModule::class,
'sources_tab' => SourcesTabModule::class,
'statcounter' => StatcounterModule::class,
'statistics_chart' => StatisticsChartModule::class,
diff --git a/app/Statistics/Repository/IndividualRepository.php b/app/Statistics/Repository/IndividualRepository.php
index 1c366e4945..1433a55b6c 100644
--- a/app/Statistics/Repository/IndividualRepository.php
+++ b/app/Statistics/Repository/IndividualRepository.php
@@ -24,6 +24,9 @@ use Fisharebest\Webtrees\Gedcom;
use Fisharebest\Webtrees\GedcomRecord;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Individual;
+use Fisharebest\Webtrees\Module\IndividualListModule;
+use Fisharebest\Webtrees\Module\ModuleInterface;
+use Fisharebest\Webtrees\Services\ModuleService;
use Fisharebest\Webtrees\Statistics\Google\ChartAge;
use Fisharebest\Webtrees\Statistics\Google\ChartBirth;
use Fisharebest\Webtrees\Statistics\Google\ChartCommonGiven;
@@ -554,11 +557,16 @@ class IndividualRepository implements IndividualRepositoryInterface
break;
}
+ //find a module providing individual lists
+ $module = app(ModuleService::class)->findByComponent('list', $tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof IndividualListModule;
+ });
+
return FunctionsPrintLists::surnameList(
$surnames,
($type === 'list' ? 1 : 2),
$show_tot,
- 'individual-list',
+ $module,
$this->tree
);
}
diff --git a/app/Webtrees.php b/app/Webtrees.php
index ef47e5a076..b9079aeb81 100644
--- a/app/Webtrees.php
+++ b/app/Webtrees.php
@@ -40,7 +40,7 @@ class Webtrees
public const NAME = 'webtrees';
// Required version of database tables/columns/indexes/etc.
- public const SCHEMA_VERSION = 42;
+ public const SCHEMA_VERSION = 43;
// e.g. "dev", "alpha", "beta.3", etc.
public const STABILITY = 'alpha.5';
diff --git a/resources/views/admin/control-panel-tree-list.phtml b/resources/views/admin/control-panel-tree-list.phtml
index 25b73a4144..6d684affda 100644
--- a/resources/views/admin/control-panel-tree-list.phtml
+++ b/resources/views/admin/control-panel-tree-list.phtml
@@ -1,4 +1,13 @@
<?php use Fisharebest\Webtrees\I18N; ?>
+<?php use Fisharebest\Webtrees\Auth; ?>
+<?php use Fisharebest\Webtrees\Module\IndividualListModule; ?>
+<?php use Fisharebest\Webtrees\Module\FamilyListModule; ?>
+<?php use Fisharebest\Webtrees\Module\MediaListModule; ?>
+<?php use Fisharebest\Webtrees\Module\ModuleInterface; ?>
+<?php use Fisharebest\Webtrees\Module\NoteListModule; ?>
+<?php use Fisharebest\Webtrees\Module\RepositoryListModule; ?>
+<?php use Fisharebest\Webtrees\Module\SourceListModule; ?>
+<?php use Fisharebest\Webtrees\Services\ModuleService; ?>
<?php foreach ($all_trees as $tree) : ?>
<tr class="<?= $changes[$tree->id()] ? 'danger' : '' ?>">
@@ -26,7 +35,18 @@
</td>
<td class="d-none d-sm-table-cell text-right">
<?php if ($individuals[$tree->id()]) : ?>
- <a href="<?= e(route('individual-list', ['ged' => $tree->name()])) ?>">
+ <?php
+ //find a module providing individual lists
+ $module = app(ModuleService::class)->findByComponent('list', $tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof IndividualListModule;
+ });
+
+ if ($module instanceof IndividualListModule) {
+ echo '<a href="'.e($module->listUrl($tree)).'">';
+ } else {
+ echo '<a>';
+ }
+ ?>
<?= I18N::number($individuals[$tree->id()]) ?>
</a>
<?php else : ?>
@@ -35,7 +55,18 @@
</td>
<td class="d-none d-lg-table-cell text-right">
<?php if ($families[$tree->id()]) : ?>
- <a href="<?= e(route('family-list', ['ged' => $tree->name()])) ?>">
+ <?php
+ //find a module providing family lists
+ $module = app(ModuleService::class)->findByComponent('list', $tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof FamilyListModule;
+ });
+
+ if ($module instanceof FamilyListModule) {
+ echo '<a href="'.e($module->listUrl($tree)).'">';
+ } else {
+ echo '<a>';
+ }
+ ?>
<?= I18N::number($families[$tree->id()]) ?>
</a>
<?php else : ?>
@@ -44,7 +75,18 @@
</td>
<td class="d-none d-sm-table-cell text-right">
<?php if ($sources[$tree->id()]) : ?>
- <a href="<?= e(route('source-list', ['ged' => $tree->name()])) ?>">
+ <?php
+ //find a module providing source lists
+ $module = app(ModuleService::class)->findByComponent('list', $tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof SourceListModule;
+ });
+
+ if ($module instanceof SourceListModule) {
+ echo '<a href="'.e($module->listUrl($tree)).'">';
+ } else {
+ echo '<a>';
+ }
+ ?>
<?= I18N::number($sources[$tree->id()]) ?>
</a>
<?php else : ?>
@@ -53,7 +95,18 @@
</td>
<td class="d-none d-lg-table-cell text-right">
<?php if ($repositories[$tree->id()]) : ?>
- <a href="<?= e(route('repository-list', ['ged' => $tree->name()])) ?>">
+ <?php
+ //find a module providing repository lists
+ $module = app(ModuleService::class)->findByComponent('list', $tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof RepositoryListModule;
+ });
+
+ if ($module instanceof RepositoryListModule) {
+ echo '<a href="'.e($module->listUrl($tree)).'">';
+ } else {
+ echo '<a>';
+ }
+ ?>
<?= I18N::number($repositories[$tree->id()]) ?>
</a>
<?php else : ?>
@@ -62,7 +115,18 @@
</td>
<td class="d-none d-sm-table-cell text-right">
<?php if ($media[$tree->id()]) : ?>
- <a href="<?= e(route('media-list', ['ged' => $tree->name()])) ?>">
+ <?php
+ //find a module providing media lists
+ $module = app(ModuleService::class)->findByComponent('list', $tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof MediaListModule;
+ });
+
+ if ($module instanceof MediaListModule) {
+ echo '<a href="'.e($module->listUrl($tree)).'">';
+ } else {
+ echo '<a>';
+ }
+ ?>
<?= I18N::number($media[$tree->id()]) ?>
</a>
<?php else : ?>
@@ -71,7 +135,18 @@
</td>
<td class="d-none d-lg-table-cell text-right">
<?php if ($notes[$tree->id()]) : ?>
- <a href="<?= e(route('note-list', ['ged' => $tree->name()])) ?>">
+ <?php
+ //find a module providing note lists
+ $module = app(ModuleService::class)->findByComponent('list', $tree, Auth::user())->first(function (ModuleInterface $module) {
+ return $module instanceof NoteListModule;
+ });
+
+ if ($module instanceof NoteListModule) {
+ echo '<a href="'.e($module->listUrl($tree)).'">';
+ } else {
+ echo '<a>';
+ }
+ ?>
<?= I18N::number($media[$tree->id()]) ?>
</a>
<?php else : ?>
diff --git a/resources/views/admin/control-panel.phtml b/resources/views/admin/control-panel.phtml
index 9f9c09e6ab..20bc5d4103 100644
--- a/resources/views/admin/control-panel.phtml
+++ b/resources/views/admin/control-panel.phtml
@@ -331,6 +331,13 @@
<?= view('components/badge', ['count' => $chart_modules->count(), 'context' => 'primary']) ?>
</li>
<li>
+ <span class="fa-li"><?= view('icons/list') ?></span>
+ <a href="<?= e(route('lists')) ?>">
+ <?= I18N::translate('Lists') ?>
+ </a>
+ <?= view('components/badge', ['count' => $list_modules->count(), 'context' => 'primary']) ?>
+ </li>
+ <li>
<span class="fa-li"><?= view('icons/report') ?></span>
<a href="<?= e(route('reports')) ?>">
<?= I18N::translate('Reports') ?>
diff --git a/resources/views/admin/modules.phtml b/resources/views/admin/modules.phtml
index 0f168bc6e8..b969fe4948 100644
--- a/resources/views/admin/modules.phtml
+++ b/resources/views/admin/modules.phtml
@@ -9,6 +9,7 @@
<?php use Fisharebest\Webtrees\Module\ModuleFooterInterface; ?>
<?php use Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface; ?>
<?php use Fisharebest\Webtrees\Module\ModuleLanguageInterface; ?>
+<?php use Fisharebest\Webtrees\Module\ModuleListInterface; ?>
<?php use Fisharebest\Webtrees\Module\ModuleMenuInterface; ?>
<?php use Fisharebest\Webtrees\Module\ModuleReportInterface; ?>
<?php use Fisharebest\Webtrees\Module\ModuleSidebarInterface; ?>
@@ -75,6 +76,10 @@
<?= view('icons/chart') ?>
<span class="sr-only"><?= I18N::translate('Charts') ?></span>
</th>
+ <th title="<?= I18N::translate('Lists') ?>">
+ <?= view('icons/list') ?>
+ <span class="sr-only"><?= I18N::translate('Lists') ?></span>
+ </th>
<th title="<?= I18N::translate('Reports') ?>">
<?= view('icons/report') ?>
<span class="sr-only"><?= I18N::translate('Reports') ?></span>
@@ -198,6 +203,14 @@
-
<?php endif ?>
</td>
+ <td class="text-center text-muted" title="<?= I18N::translate('List') ?>">
+ <?php if ($module instanceof ModuleListInterface) : ?>
+ <?= view('icons/list') ?>
+ <span class="sr-only"><?= I18N::translate('List') ?></span>
+ <?php else : ?>
+ -
+ <?php endif ?>
+ </td>
<td class="text-center text-muted" title="<?= I18N::translate('Report') ?>">
<?php if ($module instanceof ModuleReportInterface) : ?>
<?= view('icons/report') ?>
diff --git a/resources/views/branches-page.phtml b/resources/views/branches-page.phtml
index da0f6e5c83..2235d19b2c 100644
--- a/resources/views/branches-page.phtml
+++ b/resources/views/branches-page.phtml
@@ -6,7 +6,9 @@
</h2>
<form class="wt-page-options wt-page-options-branches d-print-none">
- <input type="hidden" name="route" value="branches">
+ <input type="hidden" name="route" value="module">
+ <input type="hidden" name="module" value="<?= e($module) ?>">
+ <input type="hidden" name="action" value="<?= e($action) ?>">
<input type="hidden" name="ged" value="<?= e($tree->name()) ?>">
<div class="form-group row">
@@ -42,5 +44,5 @@
</form>
<?php if ($surname !== '') : ?>
- <div class="wt-ajax-load wt-page-content wt-chart wt-branches" data-ajax-url="<?= e(route('branches-list', ['surname' => $surname, 'soundex_std' => $soundex_std, 'soundex_dm' => $soundex_dm, 'ged' => $tree->name()])) ?>"></div>
+ <div class="wt-ajax-load wt-page-content wt-chart wt-branches" data-ajax-url="<?= e(route('module', ['module' => $module, 'action' => 'List', 'surname' => $surname, 'soundex_std' => $soundex_std, 'soundex_dm' => $soundex_dm, 'ged' => $tree->name()])) ?>"></div>
<?php endif ?>
diff --git a/resources/views/icons/list.phtml b/resources/views/icons/list.phtml
new file mode 100644
index 0000000000..2d0344f114
--- /dev/null
+++ b/resources/views/icons/list.phtml
@@ -0,0 +1 @@
+<i class="fas fa-list fa-fw wt-icon-list" aria-hidden="true"></i>
diff --git a/resources/views/lists/surnames-table.phtml b/resources/views/lists/surnames-table.phtml
index 19bac44961..8e6a30c654 100644
--- a/resources/views/lists/surnames-table.phtml
+++ b/resources/views/lists/surnames-table.phtml
@@ -10,7 +10,7 @@
<?= I18N::translate('Surname') ?>
</th>
<th>
- <?php if ($route == 'family-list') :?>
+ <?php if ($families) :?>
<?= I18N::translate('Spouses') ?>
<?php else : ?>
<?= I18N::translate('Individuals') ?>
@@ -25,19 +25,28 @@
<td data-sort="<?= e($surn) ?>">
<!-- Multiple surname variants, e.g. von Groot, van Groot, van der Groot, etc. -->
<?php foreach ($surns as $spfxsurn => $indis) : ?>
- <?php if ($spfxsurn) : ?>
- <?php if ($surn !== '') : ?>
- <a href="<?= route($route, ['surname' => $surn, 'ged' => $tree->name()]) ?>" dir="auto">
- <?= e($spfxsurn) ?>
- </a>
+ <?php if ($module instanceof IndividualListModule) : ?>
+ <?php if ($spfxsurn) : ?>
+ <?php if ($surn !== '') : ?>
+ <a href="<?= $module->listUrl($tree, ['surname' => $surn]) ?>" dir="auto">
+ <?= e($spfxsurn) ?>
+ </a>
+ <?php else : ?>
+ <a href="<?= $module->listUrl($tree, ['alpha' => ',']) ?>" dir="auto">
+ <?= e($spfxsurn) ?>
+ </a>
+ <?php endif ?>
<?php else : ?>
- <a href="<?= route($route, ['alpha' => ',', 'ged' => $tree->name()]) ?>" dir="auto">
- <?= e($spfxsurn) ?>
- </a>
+ <!-- No surname, but a value from "2 SURN"? A common workaround for toponyms, etc. -->
+ <a href="<?= $module->listUrl($tree, ['surname' => $surn]) ?>" dir="auto"><?= e($surn) ?></a>
<?php endif ?>
<?php else : ?>
- <!-- No surname, but a value from "2 SURN"? A common workaround for toponyms, etc. -->
- <a href="<?= route($route, ['surname' => $surn, 'ged' => $tree->name()]) ?>" dir="auto"><?= e($surn) ?></a>
+ <?php if ($spfxsurn) : ?>
+ <span dir="auto"><?= e($spfxsurn) ?></span>
+ <?php else : ?>
+ <!-- No surname, but a value from "2 SURN"? A common workaround for toponyms, etc. -->
+ <span dir="auto"><?= e($surn) ?></span>
+ <?php endif ?>
<?php endif ?>
<br>
<?php endforeach ?>
diff --git a/resources/views/media-list-page.phtml b/resources/views/media-list-page.phtml
index 9a216e78ff..8090ed4a9c 100644
--- a/resources/views/media-list-page.phtml
+++ b/resources/views/media-list-page.phtml
@@ -12,8 +12,10 @@
<form class="wt-page-options wt-page-options-media-list d-print-none">
<input type="hidden" name="ged" value="<?= e($tree->name()) ?>">
- <input type="hidden" name="route" value="media-list">
- <input type="hidden" name="action" value="1">
+ <input type="hidden" name="route" value="module">
+ <input type="hidden" name="module" value="<?= e($module) ?>">
+ <input type="hidden" name="action" value="<?= e($action) ?>">
+ <input type="hidden" name="action2" value="1">
<input type="hidden" name="search" value="yes">
<div class="row form-group">
@@ -58,10 +60,10 @@
<div class="col-sm-3 col-form-label wt-page-options-label">
</div>
<div class="col-sm-3 wt-page-options-value">
- <button type="submit" name="action" value="1" class="btn btn-primary">
+ <button type="submit" name="action2" value="1" class="btn btn-primary">
<?= /* I18N: A button label. */ I18N::translate('search') ?>
</button>
- <a class="btn btn-secondary" href="<?= e(route('media-list', ['ged' => $tree->name()])) ?>">
+ <a class="btn btn-secondary" href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name()])) ?>">
<?= /* I18N: A button label. */ I18N::translate('reset') ?>
</a>
</div>
@@ -75,14 +77,14 @@
<div class="row text-center">
<div class="col">
<?php if ($page > 1) : ?>
- <a href="<?= e(route('media-list', ['ged' => $tree->name(), 'action' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => 1])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'action2' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => 1])) ?>">
<?= I18N::translate('first') ?>
</a>
<?php endif ?>
</div>
<div class="col">
<?php if ($page > 1) : ?>
- <a href="<?= e(route('media-list', ['ged' => $tree->name(), 'action' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $page - 1])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'action2' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $page - 1])) ?>">
<?= I18N::translate('previous') ?>
</a>
<?php endif ?>
@@ -92,14 +94,14 @@
</div>
<div class="col">
<?php if ($page < $pages) : ?>
- <a href="<?= e(route('media-list', ['ged' => $tree->name(), 'action' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $page + 1])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'action2' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $page + 1])) ?>">
<?= I18N::translate('next') ?>
</a>
<?php endif ?>
</div>
<div class="col">
<?php if ($page < $pages) : ?>
- <a href="<?= e(route('media-list', ['ged' => $tree->name(), 'action' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $pages])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'action2' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $pages])) ?>">
<?= I18N::translate('last') ?>
</a>
<?php endif ?>
@@ -181,14 +183,14 @@
<div class="row text-center">
<div class="col">
<?php if ($page > 1) : ?>
- <a href="<?= e(route('media-list', ['ged' => $tree->name(), 'action' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => 1])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'action2' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => 1])) ?>">
<?= I18N::translate('first') ?>
</a>
<?php endif ?>
</div>
<div class="col">
<?php if ($page > 1) : ?>
- <a href="<?= e(route('media-list', ['ged' => $tree->name(), 'action' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $page - 1])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'action2' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $page - 1])) ?>">
<?= I18N::translate('previous') ?>
</a>
<?php endif ?>
@@ -198,14 +200,14 @@
</div>
<div class="col">
<?php if ($page < $pages) : ?>
- <a href="<?= e(route('media-list', ['ged' => $tree->name(), 'action' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $page + 1])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'action2' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $page + 1])) ?>">
<?= I18N::translate('next') ?>
</a>
<?php endif ?>
</div>
<div class="col">
<?php if ($page < $pages) : ?>
- <a href="<?= e(route('media-list', ['ged' => $tree->name(), 'action' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $pages])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'action2' => '1', 'folder' => $folder, 'subdirs' => $subdirs, 'filter' => $filter, 'form_type' => $form_type, 'max' => $max, 'page' => $pages])) ?>">
<?= I18N::translate('last') ?>
</a>
<?php endif ?>
diff --git a/resources/views/places-page.phtml b/resources/views/places-page.phtml
index 0f5a26ff18..2648f77fe5 100644
--- a/resources/views/places-page.phtml
+++ b/resources/views/places-page.phtml
@@ -5,7 +5,7 @@
<h4><?= $title ?></h4>
<h5 class="text-center">
<?php if ($current) : ?>
- <a href="<?= e(route('place-hierarchy', ['ged' => $tree->name()])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name()])) ?>">
<?= I18N::translate('World') ?>
</a>
<?php else : ?>
@@ -29,13 +29,13 @@
<?= $content ?>
<div class="text-center">
<?php if ($showeventslink) : ?>
- <a class="formField" href= <?= e(route('place-hierarchy', ['ged' => $tree->name(), 'parent' => $parent, 'action' => 'hierarchy-e'])) ?>>
+ <a class="formField" href= <?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'parent' => $parent, 'action2' => 'hierarchy-e'])) ?>>
<?= I18N::translate('View table of events occurring in %s', $place) ?>
</a>
|
<?php endif ?>
- <a href="<?= e(route('place-hierarchy', ['ged' => $tree->name(), 'action' => key($nextaction)])) ?>">
+ <a href="<?= e(route('module', ['module' => $module, 'action' => $action, 'ged' => $tree->name(), 'action2' => key($nextaction)])) ?>">
<?= current($nextaction) ?>
</a>
</div>
diff --git a/routes/web.php b/routes/web.php
index 6bee6539a5..4e862f7031 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -41,6 +41,8 @@ if (Auth::isAdmin()) {
'POST:blocks' => 'Admin\\ModuleController@updateBlocks',
'GET:charts' => 'Admin\\ModuleController@listCharts',
'POST:charts' => 'Admin\\ModuleController@updateCharts',
+ 'GET:lists' => 'Admin\\ModuleController@listLists',
+ 'POST:lists' => 'Admin\\ModuleController@updateLists',
'GET:footers' => 'Admin\\ModuleController@listFooters',
'POST:footers' => 'Admin\\ModuleController@updateFooters',
'GET:history' => 'Admin\\ModuleController@listHistory',
@@ -255,8 +257,8 @@ if ($tree instanceof Tree && $tree->getPreference('imported') === '1') {
'GET:autocomplete-folder' => 'AutocompleteController@folder',
'GET:autocomplete-page' => 'AutocompleteController@page',
'GET:autocomplete-place' => 'AutocompleteController@place',
- 'GET:branches' => 'BranchesController@page',
- 'GET:branches-list' => 'BranchesController@list',
+ //'GET:branches' => 'BranchesController@page',
+ //'GET:branches-list' => 'BranchesController@list',
'GET:calendar' => 'CalendarController@page',
'GET:calendar-events' => 'CalendarController@calendar',
'GET:help-text' => 'HelpTextController@helpText',
@@ -279,13 +281,13 @@ if ($tree instanceof Tree && $tree->getPreference('imported') === '1') {
'GET:report-list' => 'ReportEngineController@reportList',
'GET:report-setup' => 'ReportEngineController@reportSetup',
'GET:report-run' => 'ReportEngineController@reportRun',
- 'GET:family-list' => 'ListController@familyList',
- 'GET:individual-list' => 'ListController@individualList',
- 'GET:media-list' => 'ListController@mediaList',
- 'GET:note-list' => 'ListController@noteList',
- 'GET:place-hierarchy' => 'PlaceHierarchyController@show',
- 'GET:repository-list' => 'ListController@repositoryList',
- 'GET:source-list' => 'ListController@sourceList',
+ //'GET:family-list' => 'ListController@familyList',
+ //'GET:individual-list' => 'ListController@individualList',
+ //'GET:media-list' => 'ListController@mediaList',
+ //'GET:note-list' => 'ListController@noteList',
+ //'GET:place-hierarchy' => 'PlaceHierarchyController@show',
+ //'GET:repository-list' => 'ListController@repositoryList',
+ //'GET:source-list' => 'ListController@sourceList',
'POST:accept-changes' => 'PendingChangesController@acceptChanges',
'POST:reject-changes' => 'PendingChangesController@rejectChanges',
'POST:accept-all-changes' => 'PendingChangesController@acceptAllChanges',
diff --git a/tests/feature/IndividualListTest.php b/tests/feature/IndividualListTest.php
index d454034cf7..ac17907d6d 100644
--- a/tests/feature/IndividualListTest.php
+++ b/tests/feature/IndividualListTest.php
@@ -51,23 +51,23 @@ class IndividualListTest extends TestCase
$individual_list_service = new IndividualListService($localization_service, $tree);
$controller = new ListController($individual_list_service, $localization_service);
- $request = new Request(['route' => 'individual-list']);
+ $request = new Request(['route' => 'module', 'module' => 'individual_list', 'action' => 'List']);
$response = $controller->individualList($request, $tree, $user);
$this->assertSame(Response::HTTP_OK, $response->getStatusCode());
- $request = new Request(['route' => 'individual-list', 'alpha' => 'B']);
+ $request = new Request(['route' => 'module', 'module' => 'individual_list', 'action' => 'List', 'alpha' => 'B']);
$response = $controller->individualList($request, $tree, $user);
$this->assertSame(Response::HTTP_OK, $response->getStatusCode());
- $request = new Request(['route' => 'individual-list', 'alpha' => ',']);
+ $request = new Request(['route' => 'module', 'module' => 'individual_list', 'action' => 'List', 'alpha' => ',']);
$response = $controller->individualList($request, $tree, $user);
$this->assertSame(Response::HTTP_OK, $response->getStatusCode());
- $request = new Request(['route' => 'individual-list', 'alpha' => '@']);
+ $request = new Request(['route' => 'module', 'module' => 'individual_list', 'action' => 'List', 'alpha' => '@']);
$response = $controller->individualList($request, $tree, $user);
$this->assertSame(Response::HTTP_OK, $response->getStatusCode());
- $request = new Request(['route' => 'individual-list', 'surname' => 'BRAUN']);
+ $request = new Request(['route' => 'module', 'module' => 'individual_list', 'action' => 'List', 'surname' => 'BRAUN']);
$response = $controller->individualList($request, $tree, $user);
$this->assertSame(Response::HTTP_OK, $response->getStatusCode());
}